##// END OF EJS Templates
largefiles: simplify revert - use getstandinsstate like other commands do
Mads Kiilerich -
r21094:4643bfec default
parent child Browse files
Show More
@@ -1,1166 +1,1137
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 '''installmatchfn with a matchfn that ignores all largefiles'''
27 '''installmatchfn with a matchfn that ignores all largefiles'''
28 def overridematch(ctx, pats=[], opts={}, globbed=False,
28 def overridematch(ctx, pats=[], opts={}, globbed=False,
29 default='relpath'):
29 default='relpath'):
30 match = oldmatch(ctx, pats, opts, globbed, default)
30 match = oldmatch(ctx, pats, opts, globbed, default)
31 m = copy.copy(match)
31 m = copy.copy(match)
32 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
32 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
33 manifest)
33 manifest)
34 m._files = filter(notlfile, m._files)
34 m._files = filter(notlfile, m._files)
35 m._fmap = set(m._files)
35 m._fmap = set(m._files)
36 m._always = False
36 m._always = False
37 origmatchfn = m.matchfn
37 origmatchfn = m.matchfn
38 m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None
38 m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None
39 return m
39 return m
40 oldmatch = installmatchfn(overridematch)
40 oldmatch = installmatchfn(overridematch)
41
41
42 def installmatchfn(f):
42 def installmatchfn(f):
43 '''monkey patch the scmutil module with a custom match function.
43 '''monkey patch the scmutil module with a custom match function.
44 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
44 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
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 installmatchfn
51 '''restores scmutil.match to what it was before installmatchfn
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 installmatchfn will require n calls to
54 Note that n calls to installmatchfn will require n calls to
55 restore matchfn to reverse'''
55 restore matchfn to reverse'''
56 scmutil.match = getattr(scmutil.match, 'oldmatch')
56 scmutil.match = getattr(scmutil.match, 'oldmatch')
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 # Need to lock between the standins getting updated and their
300 # Need to lock between the standins getting updated and their
301 # largefiles getting updated
301 # largefiles getting updated
302 wlock = repo.wlock()
302 wlock = repo.wlock()
303 try:
303 try:
304 lfdirstate = lfutil.openlfdirstate(ui, repo)
304 lfdirstate = lfutil.openlfdirstate(ui, repo)
305 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()),
305 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()),
306 [], False, False, False)
306 [], False, False, False)
307 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
307 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
308
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 return orig(ui, repo, *pats, **opts)
326 finally:
326 finally:
327 wlock.release()
327 wlock.release()
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 installnormalfilesmatchfn(repo[None].manifest())
467 installnormalfilesmatchfn(repo[None].manifest())
468 try:
468 try:
469 try:
469 try:
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 def overridematch(ctx, pats=[], opts={}, globbed=False,
494 def overridematch(ctx, pats=[], opts={}, globbed=False,
495 default='relpath'):
495 default='relpath'):
496 newpats = []
496 newpats = []
497 # The patterns were previously mangled to add the standin
497 # The patterns were previously mangled to add the standin
498 # directory; we need to remove that now
498 # directory; we need to remove that now
499 for pat in pats:
499 for pat in pats:
500 if match_.patkind(pat) is None and lfutil.shortname in pat:
500 if match_.patkind(pat) is None and lfutil.shortname in pat:
501 newpats.append(pat.replace(lfutil.shortname, ''))
501 newpats.append(pat.replace(lfutil.shortname, ''))
502 else:
502 else:
503 newpats.append(pat)
503 newpats.append(pat)
504 match = oldmatch(ctx, newpats, opts, globbed, default)
504 match = oldmatch(ctx, newpats, opts, globbed, default)
505 m = copy.copy(match)
505 m = copy.copy(match)
506 lfile = lambda f: lfutil.standin(f) in manifest
506 lfile = lambda f: lfutil.standin(f) in manifest
507 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
507 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
508 m._fmap = set(m._files)
508 m._fmap = set(m._files)
509 m._always = False
509 m._always = False
510 origmatchfn = m.matchfn
510 origmatchfn = m.matchfn
511 m.matchfn = lambda f: (lfutil.isstandin(f) and
511 m.matchfn = lambda f: (lfutil.isstandin(f) and
512 (f in manifest) and
512 (f in manifest) and
513 origmatchfn(lfutil.splitstandin(f)) or
513 origmatchfn(lfutil.splitstandin(f)) or
514 None)
514 None)
515 return m
515 return m
516 oldmatch = installmatchfn(overridematch)
516 oldmatch = installmatchfn(overridematch)
517 listpats = []
517 listpats = []
518 for pat in pats:
518 for pat in pats:
519 if match_.patkind(pat) is not None:
519 if match_.patkind(pat) is not None:
520 listpats.append(pat)
520 listpats.append(pat)
521 else:
521 else:
522 listpats.append(makestandin(pat))
522 listpats.append(makestandin(pat))
523
523
524 try:
524 try:
525 origcopyfile = util.copyfile
525 origcopyfile = util.copyfile
526 copiedfiles = []
526 copiedfiles = []
527 def overridecopyfile(src, dest):
527 def overridecopyfile(src, dest):
528 if (lfutil.shortname in src and
528 if (lfutil.shortname in src and
529 dest.startswith(repo.wjoin(lfutil.shortname))):
529 dest.startswith(repo.wjoin(lfutil.shortname))):
530 destlfile = dest.replace(lfutil.shortname, '')
530 destlfile = dest.replace(lfutil.shortname, '')
531 if not opts['force'] and os.path.exists(destlfile):
531 if not opts['force'] and os.path.exists(destlfile):
532 raise IOError('',
532 raise IOError('',
533 _('destination largefile already exists'))
533 _('destination largefile already exists'))
534 copiedfiles.append((src, dest))
534 copiedfiles.append((src, dest))
535 origcopyfile(src, dest)
535 origcopyfile(src, dest)
536
536
537 util.copyfile = overridecopyfile
537 util.copyfile = overridecopyfile
538 result += orig(ui, repo, listpats, opts, rename)
538 result += orig(ui, repo, listpats, opts, rename)
539 finally:
539 finally:
540 util.copyfile = origcopyfile
540 util.copyfile = origcopyfile
541
541
542 lfdirstate = lfutil.openlfdirstate(ui, repo)
542 lfdirstate = lfutil.openlfdirstate(ui, repo)
543 for (src, dest) in copiedfiles:
543 for (src, dest) in copiedfiles:
544 if (lfutil.shortname in src and
544 if (lfutil.shortname in src and
545 dest.startswith(repo.wjoin(lfutil.shortname))):
545 dest.startswith(repo.wjoin(lfutil.shortname))):
546 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
546 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
547 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
547 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
548 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
548 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
549 if not os.path.isdir(destlfiledir):
549 if not os.path.isdir(destlfiledir):
550 os.makedirs(destlfiledir)
550 os.makedirs(destlfiledir)
551 if rename:
551 if rename:
552 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
552 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
553 lfdirstate.remove(srclfile)
553 lfdirstate.remove(srclfile)
554 else:
554 else:
555 util.copyfile(repo.wjoin(srclfile),
555 util.copyfile(repo.wjoin(srclfile),
556 repo.wjoin(destlfile))
556 repo.wjoin(destlfile))
557
557
558 lfdirstate.add(destlfile)
558 lfdirstate.add(destlfile)
559 lfdirstate.write()
559 lfdirstate.write()
560 except util.Abort, e:
560 except util.Abort, e:
561 if str(e) != _('no files to copy'):
561 if str(e) != _('no files to copy'):
562 raise e
562 raise e
563 else:
563 else:
564 nolfiles = True
564 nolfiles = True
565 finally:
565 finally:
566 restorematchfn()
566 restorematchfn()
567 wlock.release()
567 wlock.release()
568
568
569 if nolfiles and nonormalfiles:
569 if nolfiles and nonormalfiles:
570 raise util.Abort(_('no files to copy'))
570 raise util.Abort(_('no files to copy'))
571
571
572 return result
572 return result
573
573
574 # When the user calls revert, we have to be careful to not revert any
574 # When the user calls revert, we have to be careful to not revert any
575 # changes to other largefiles accidentally. This means we have to keep
575 # changes to other largefiles accidentally. This means we have to keep
576 # track of the largefiles that are being reverted so we only pull down
576 # track of the largefiles that are being reverted so we only pull down
577 # the necessary largefiles.
577 # the necessary largefiles.
578 #
578 #
579 # Standins are only updated (to match the hash of largefiles) before
579 # Standins are only updated (to match the hash of largefiles) before
580 # commits. Update the standins then run the original revert, changing
580 # commits. Update the standins then run the original revert, changing
581 # the matcher to hit standins instead of largefiles. Based on the
581 # the matcher to hit standins instead of largefiles. Based on the
582 # resulting standins update the largefiles. Then return the standins
582 # resulting standins update the largefiles.
583 # to their proper state
584 def overriderevert(orig, ui, repo, *pats, **opts):
583 def overriderevert(orig, ui, repo, *pats, **opts):
585 # Because we put the standins in a bad state (by updating them)
584 # Because we put the standins in a bad state (by updating them)
586 # and then return them to a correct state we need to lock to
585 # and then return them to a correct state we need to lock to
587 # prevent others from changing them in their incorrect state.
586 # prevent others from changing them in their incorrect state.
588 wlock = repo.wlock()
587 wlock = repo.wlock()
589 try:
588 try:
590 lfdirstate = lfutil.openlfdirstate(ui, repo)
589 lfdirstate = lfutil.openlfdirstate(ui, repo)
591 (modified, added, removed, missing, unknown, ignored, clean) = \
590 (modified, added, removed, missing, unknown, ignored, clean) = \
592 lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
591 lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
593 lfdirstate.write()
592 lfdirstate.write()
594 for lfile in modified:
593 for lfile in modified:
595 lfutil.updatestandin(repo, lfutil.standin(lfile))
594 lfutil.updatestandin(repo, lfutil.standin(lfile))
596 for lfile in missing:
595 for lfile in missing:
597 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
596 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
598 os.unlink(repo.wjoin(lfutil.standin(lfile)))
597 os.unlink(repo.wjoin(lfutil.standin(lfile)))
599
598
599 oldstandins = lfutil.getstandinsstate(repo)
600
600 try:
601 try:
601 ctx = scmutil.revsingle(repo, opts.get('rev'))
602 def overridematch(ctx, pats=[], opts={}, globbed=False,
602 def overridematch(ctx, pats=[], opts={}, globbed=False,
603 default='relpath'):
603 default='relpath'):
604 match = oldmatch(ctx, pats, opts, globbed, default)
604 match = oldmatch(ctx, pats, opts, globbed, default)
605 m = copy.copy(match)
605 m = copy.copy(match)
606 def tostandin(f):
606 def tostandin(f):
607 if lfutil.standin(f) in ctx:
607 if lfutil.standin(f) in ctx:
608 return lfutil.standin(f)
608 return lfutil.standin(f)
609 elif lfutil.standin(f) in repo[None]:
609 elif lfutil.standin(f) in repo[None]:
610 return None
610 return None
611 return f
611 return f
612 m._files = [tostandin(f) for f in m._files]
612 m._files = [tostandin(f) for f in m._files]
613 m._files = [f for f in m._files if f is not None]
613 m._files = [f for f in m._files if f is not None]
614 m._fmap = set(m._files)
614 m._fmap = set(m._files)
615 m._always = False
615 m._always = False
616 origmatchfn = m.matchfn
616 origmatchfn = m.matchfn
617 def matchfn(f):
617 def matchfn(f):
618 if lfutil.isstandin(f):
618 if lfutil.isstandin(f):
619 # We need to keep track of what largefiles are being
619 return (origmatchfn(lfutil.splitstandin(f)) and
620 # matched so we know which ones to update later --
620 (f in repo[None] or f in ctx))
621 # otherwise we accidentally revert changes to other
622 # largefiles. This is repo-specific, so duckpunch the
623 # repo object to keep the list of largefiles for us
624 # later.
625 if origmatchfn(lfutil.splitstandin(f)) and \
626 (f in repo[None] or f in ctx):
627 lfileslist = getattr(repo, '_lfilestoupdate', [])
628 lfileslist.append(lfutil.splitstandin(f))
629 repo._lfilestoupdate = lfileslist
630 return True
631 else:
632 return False
633 return origmatchfn(f)
621 return origmatchfn(f)
634 m.matchfn = matchfn
622 m.matchfn = matchfn
635 return m
623 return m
636 oldmatch = installmatchfn(overridematch)
624 oldmatch = installmatchfn(overridematch)
637 scmutil.match
625 overridematch(repo[None], pats, opts)
638 matches = overridematch(repo[None], pats, opts)
639 orig(ui, repo, *pats, **opts)
626 orig(ui, repo, *pats, **opts)
640 finally:
627 finally:
641 restorematchfn()
628 restorematchfn()
642 lfileslist = getattr(repo, '_lfilestoupdate', [])
643 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
644 printmessage=False)
645
629
646 # empty out the largefiles list so we start fresh next time
630 newstandins = lfutil.getstandinsstate(repo)
647 repo._lfilestoupdate = []
631 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
648 for lfile in modified:
632 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False)
649 if lfile in lfileslist:
633
650 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
651 in repo['.']:
652 lfutil.writestandin(repo, lfutil.standin(lfile),
653 repo['.'][lfile].data().strip(),
654 'x' in repo['.'][lfile].flags())
655 lfdirstate = lfutil.openlfdirstate(ui, repo)
656 for lfile in added:
657 standin = lfutil.standin(lfile)
658 if standin not in ctx and (standin in matches or opts.get('all')):
659 if lfile in lfdirstate:
660 lfdirstate.drop(lfile)
661 util.unlinkpath(repo.wjoin(standin))
662 lfdirstate.write()
663 finally:
634 finally:
664 wlock.release()
635 wlock.release()
665
636
666 def hgupdaterepo(orig, repo, node, overwrite):
637 def hgupdaterepo(orig, repo, node, overwrite):
667 if not overwrite:
638 if not overwrite:
668 # Only call updatelfiles on the standins that have changed to save time
639 # Only call updatelfiles on the standins that have changed to save time
669 oldstandins = lfutil.getstandinsstate(repo)
640 oldstandins = lfutil.getstandinsstate(repo)
670
641
671 result = orig(repo, node, overwrite)
642 result = orig(repo, node, overwrite)
672
643
673 filelist = None
644 filelist = None
674 if not overwrite:
645 if not overwrite:
675 newstandins = lfutil.getstandinsstate(repo)
646 newstandins = lfutil.getstandinsstate(repo)
676 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
647 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
677 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist)
648 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist)
678 return result
649 return result
679
650
680 def hgmerge(orig, repo, node, force=None, remind=True):
651 def hgmerge(orig, repo, node, force=None, remind=True):
681 result = orig(repo, node, force, remind)
652 result = orig(repo, node, force, remind)
682 lfcommands.updatelfiles(repo.ui, repo)
653 lfcommands.updatelfiles(repo.ui, repo)
683 return result
654 return result
684
655
685 # When we rebase a repository with remotely changed largefiles, we need to
656 # When we rebase a repository with remotely changed largefiles, we need to
686 # take some extra care so that the largefiles are correctly updated in the
657 # take some extra care so that the largefiles are correctly updated in the
687 # working copy
658 # working copy
688 def overridepull(orig, ui, repo, source=None, **opts):
659 def overridepull(orig, ui, repo, source=None, **opts):
689 revsprepull = len(repo)
660 revsprepull = len(repo)
690 if not source:
661 if not source:
691 source = 'default'
662 source = 'default'
692 repo.lfpullsource = source
663 repo.lfpullsource = source
693 if opts.get('rebase', False):
664 if opts.get('rebase', False):
694 repo._isrebasing = True
665 repo._isrebasing = True
695 try:
666 try:
696 if opts.get('update'):
667 if opts.get('update'):
697 del opts['update']
668 del opts['update']
698 ui.debug('--update and --rebase are not compatible, ignoring '
669 ui.debug('--update and --rebase are not compatible, ignoring '
699 'the update flag\n')
670 'the update flag\n')
700 del opts['rebase']
671 del opts['rebase']
701 origpostincoming = commands.postincoming
672 origpostincoming = commands.postincoming
702 def _dummy(*args, **kwargs):
673 def _dummy(*args, **kwargs):
703 pass
674 pass
704 commands.postincoming = _dummy
675 commands.postincoming = _dummy
705 try:
676 try:
706 result = commands.pull(ui, repo, source, **opts)
677 result = commands.pull(ui, repo, source, **opts)
707 finally:
678 finally:
708 commands.postincoming = origpostincoming
679 commands.postincoming = origpostincoming
709 revspostpull = len(repo)
680 revspostpull = len(repo)
710 if revspostpull > revsprepull:
681 if revspostpull > revsprepull:
711 result = result or rebase.rebase(ui, repo)
682 result = result or rebase.rebase(ui, repo)
712 finally:
683 finally:
713 repo._isrebasing = False
684 repo._isrebasing = False
714 else:
685 else:
715 result = orig(ui, repo, source, **opts)
686 result = orig(ui, repo, source, **opts)
716 revspostpull = len(repo)
687 revspostpull = len(repo)
717 lfrevs = opts.get('lfrev', [])
688 lfrevs = opts.get('lfrev', [])
718 if opts.get('all_largefiles'):
689 if opts.get('all_largefiles'):
719 lfrevs.append('pulled()')
690 lfrevs.append('pulled()')
720 if lfrevs and revspostpull > revsprepull:
691 if lfrevs and revspostpull > revsprepull:
721 numcached = 0
692 numcached = 0
722 repo.firstpulled = revsprepull # for pulled() revset expression
693 repo.firstpulled = revsprepull # for pulled() revset expression
723 try:
694 try:
724 for rev in scmutil.revrange(repo, lfrevs):
695 for rev in scmutil.revrange(repo, lfrevs):
725 ui.note(_('pulling largefiles for revision %s\n') % rev)
696 ui.note(_('pulling largefiles for revision %s\n') % rev)
726 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
697 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
727 numcached += len(cached)
698 numcached += len(cached)
728 finally:
699 finally:
729 del repo.firstpulled
700 del repo.firstpulled
730 ui.status(_("%d largefiles cached\n") % numcached)
701 ui.status(_("%d largefiles cached\n") % numcached)
731 return result
702 return result
732
703
733 def pulledrevsetsymbol(repo, subset, x):
704 def pulledrevsetsymbol(repo, subset, x):
734 """``pulled()``
705 """``pulled()``
735 Changesets that just has been pulled.
706 Changesets that just has been pulled.
736
707
737 Only available with largefiles from pull --lfrev expressions.
708 Only available with largefiles from pull --lfrev expressions.
738
709
739 .. container:: verbose
710 .. container:: verbose
740
711
741 Some examples:
712 Some examples:
742
713
743 - pull largefiles for all new changesets::
714 - pull largefiles for all new changesets::
744
715
745 hg pull -lfrev "pulled()"
716 hg pull -lfrev "pulled()"
746
717
747 - pull largefiles for all new branch heads::
718 - pull largefiles for all new branch heads::
748
719
749 hg pull -lfrev "head(pulled()) and not closed()"
720 hg pull -lfrev "head(pulled()) and not closed()"
750
721
751 """
722 """
752
723
753 try:
724 try:
754 firstpulled = repo.firstpulled
725 firstpulled = repo.firstpulled
755 except AttributeError:
726 except AttributeError:
756 raise util.Abort(_("pulled() only available in --lfrev"))
727 raise util.Abort(_("pulled() only available in --lfrev"))
757 return revset.baseset([r for r in subset if r >= firstpulled])
728 return revset.baseset([r for r in subset if r >= firstpulled])
758
729
759 def overrideclone(orig, ui, source, dest=None, **opts):
730 def overrideclone(orig, ui, source, dest=None, **opts):
760 d = dest
731 d = dest
761 if d is None:
732 if d is None:
762 d = hg.defaultdest(source)
733 d = hg.defaultdest(source)
763 if opts.get('all_largefiles') and not hg.islocal(d):
734 if opts.get('all_largefiles') and not hg.islocal(d):
764 raise util.Abort(_(
735 raise util.Abort(_(
765 '--all-largefiles is incompatible with non-local destination %s' %
736 '--all-largefiles is incompatible with non-local destination %s' %
766 d))
737 d))
767
738
768 return orig(ui, source, dest, **opts)
739 return orig(ui, source, dest, **opts)
769
740
770 def hgclone(orig, ui, opts, *args, **kwargs):
741 def hgclone(orig, ui, opts, *args, **kwargs):
771 result = orig(ui, opts, *args, **kwargs)
742 result = orig(ui, opts, *args, **kwargs)
772
743
773 if result is not None:
744 if result is not None:
774 sourcerepo, destrepo = result
745 sourcerepo, destrepo = result
775 repo = destrepo.local()
746 repo = destrepo.local()
776
747
777 # Caching is implicitly limited to 'rev' option, since the dest repo was
748 # Caching is implicitly limited to 'rev' option, since the dest repo was
778 # truncated at that point. The user may expect a download count with
749 # truncated at that point. The user may expect a download count with
779 # this option, so attempt whether or not this is a largefile repo.
750 # this option, so attempt whether or not this is a largefile repo.
780 if opts.get('all_largefiles'):
751 if opts.get('all_largefiles'):
781 success, missing = lfcommands.downloadlfiles(ui, repo, None)
752 success, missing = lfcommands.downloadlfiles(ui, repo, None)
782
753
783 if missing != 0:
754 if missing != 0:
784 return None
755 return None
785
756
786 return result
757 return result
787
758
788 def overriderebase(orig, ui, repo, **opts):
759 def overriderebase(orig, ui, repo, **opts):
789 repo._isrebasing = True
760 repo._isrebasing = True
790 try:
761 try:
791 return orig(ui, repo, **opts)
762 return orig(ui, repo, **opts)
792 finally:
763 finally:
793 repo._isrebasing = False
764 repo._isrebasing = False
794
765
795 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
766 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
796 prefix=None, mtime=None, subrepos=None):
767 prefix=None, mtime=None, subrepos=None):
797 # No need to lock because we are only reading history and
768 # No need to lock because we are only reading history and
798 # largefile caches, neither of which are modified.
769 # largefile caches, neither of which are modified.
799 lfcommands.cachelfiles(repo.ui, repo, node)
770 lfcommands.cachelfiles(repo.ui, repo, node)
800
771
801 if kind not in archival.archivers:
772 if kind not in archival.archivers:
802 raise util.Abort(_("unknown archive type '%s'") % kind)
773 raise util.Abort(_("unknown archive type '%s'") % kind)
803
774
804 ctx = repo[node]
775 ctx = repo[node]
805
776
806 if kind == 'files':
777 if kind == 'files':
807 if prefix:
778 if prefix:
808 raise util.Abort(
779 raise util.Abort(
809 _('cannot give prefix when archiving to files'))
780 _('cannot give prefix when archiving to files'))
810 else:
781 else:
811 prefix = archival.tidyprefix(dest, kind, prefix)
782 prefix = archival.tidyprefix(dest, kind, prefix)
812
783
813 def write(name, mode, islink, getdata):
784 def write(name, mode, islink, getdata):
814 if matchfn and not matchfn(name):
785 if matchfn and not matchfn(name):
815 return
786 return
816 data = getdata()
787 data = getdata()
817 if decode:
788 if decode:
818 data = repo.wwritedata(name, data)
789 data = repo.wwritedata(name, data)
819 archiver.addfile(prefix + name, mode, islink, data)
790 archiver.addfile(prefix + name, mode, islink, data)
820
791
821 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
792 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
822
793
823 if repo.ui.configbool("ui", "archivemeta", True):
794 if repo.ui.configbool("ui", "archivemeta", True):
824 def metadata():
795 def metadata():
825 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
796 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
826 hex(repo.changelog.node(0)), hex(node), ctx.branch())
797 hex(repo.changelog.node(0)), hex(node), ctx.branch())
827
798
828 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
799 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
829 if repo.tagtype(t) == 'global')
800 if repo.tagtype(t) == 'global')
830 if not tags:
801 if not tags:
831 repo.ui.pushbuffer()
802 repo.ui.pushbuffer()
832 opts = {'template': '{latesttag}\n{latesttagdistance}',
803 opts = {'template': '{latesttag}\n{latesttagdistance}',
833 'style': '', 'patch': None, 'git': None}
804 'style': '', 'patch': None, 'git': None}
834 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
805 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
835 ltags, dist = repo.ui.popbuffer().split('\n')
806 ltags, dist = repo.ui.popbuffer().split('\n')
836 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
807 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
837 tags += 'latesttagdistance: %s\n' % dist
808 tags += 'latesttagdistance: %s\n' % dist
838
809
839 return base + tags
810 return base + tags
840
811
841 write('.hg_archival.txt', 0644, False, metadata)
812 write('.hg_archival.txt', 0644, False, metadata)
842
813
843 for f in ctx:
814 for f in ctx:
844 ff = ctx.flags(f)
815 ff = ctx.flags(f)
845 getdata = ctx[f].data
816 getdata = ctx[f].data
846 if lfutil.isstandin(f):
817 if lfutil.isstandin(f):
847 path = lfutil.findfile(repo, getdata().strip())
818 path = lfutil.findfile(repo, getdata().strip())
848 if path is None:
819 if path is None:
849 raise util.Abort(
820 raise util.Abort(
850 _('largefile %s not found in repo store or system cache')
821 _('largefile %s not found in repo store or system cache')
851 % lfutil.splitstandin(f))
822 % lfutil.splitstandin(f))
852 f = lfutil.splitstandin(f)
823 f = lfutil.splitstandin(f)
853
824
854 def getdatafn():
825 def getdatafn():
855 fd = None
826 fd = None
856 try:
827 try:
857 fd = open(path, 'rb')
828 fd = open(path, 'rb')
858 return fd.read()
829 return fd.read()
859 finally:
830 finally:
860 if fd:
831 if fd:
861 fd.close()
832 fd.close()
862
833
863 getdata = getdatafn
834 getdata = getdatafn
864 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
835 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
865
836
866 if subrepos:
837 if subrepos:
867 for subpath in sorted(ctx.substate):
838 for subpath in sorted(ctx.substate):
868 sub = ctx.sub(subpath)
839 sub = ctx.sub(subpath)
869 submatch = match_.narrowmatcher(subpath, matchfn)
840 submatch = match_.narrowmatcher(subpath, matchfn)
870 sub.archive(repo.ui, archiver, prefix, submatch)
841 sub.archive(repo.ui, archiver, prefix, submatch)
871
842
872 archiver.done()
843 archiver.done()
873
844
874 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
845 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
875 repo._get(repo._state + ('hg',))
846 repo._get(repo._state + ('hg',))
876 rev = repo._state[1]
847 rev = repo._state[1]
877 ctx = repo._repo[rev]
848 ctx = repo._repo[rev]
878
849
879 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
850 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
880
851
881 def write(name, mode, islink, getdata):
852 def write(name, mode, islink, getdata):
882 # At this point, the standin has been replaced with the largefile name,
853 # At this point, the standin has been replaced with the largefile name,
883 # so the normal matcher works here without the lfutil variants.
854 # so the normal matcher works here without the lfutil variants.
884 if match and not match(f):
855 if match and not match(f):
885 return
856 return
886 data = getdata()
857 data = getdata()
887
858
888 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
859 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
889
860
890 for f in ctx:
861 for f in ctx:
891 ff = ctx.flags(f)
862 ff = ctx.flags(f)
892 getdata = ctx[f].data
863 getdata = ctx[f].data
893 if lfutil.isstandin(f):
864 if lfutil.isstandin(f):
894 path = lfutil.findfile(repo._repo, getdata().strip())
865 path = lfutil.findfile(repo._repo, getdata().strip())
895 if path is None:
866 if path is None:
896 raise util.Abort(
867 raise util.Abort(
897 _('largefile %s not found in repo store or system cache')
868 _('largefile %s not found in repo store or system cache')
898 % lfutil.splitstandin(f))
869 % lfutil.splitstandin(f))
899 f = lfutil.splitstandin(f)
870 f = lfutil.splitstandin(f)
900
871
901 def getdatafn():
872 def getdatafn():
902 fd = None
873 fd = None
903 try:
874 try:
904 fd = open(os.path.join(prefix, path), 'rb')
875 fd = open(os.path.join(prefix, path), 'rb')
905 return fd.read()
876 return fd.read()
906 finally:
877 finally:
907 if fd:
878 if fd:
908 fd.close()
879 fd.close()
909
880
910 getdata = getdatafn
881 getdata = getdatafn
911
882
912 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
883 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
913
884
914 for subpath in sorted(ctx.substate):
885 for subpath in sorted(ctx.substate):
915 sub = ctx.sub(subpath)
886 sub = ctx.sub(subpath)
916 submatch = match_.narrowmatcher(subpath, match)
887 submatch = match_.narrowmatcher(subpath, match)
917 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
888 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
918 submatch)
889 submatch)
919
890
920 # If a largefile is modified, the change is not reflected in its
891 # If a largefile is modified, the change is not reflected in its
921 # standin until a commit. cmdutil.bailifchanged() raises an exception
892 # standin until a commit. cmdutil.bailifchanged() raises an exception
922 # if the repo has uncommitted changes. Wrap it to also check if
893 # if the repo has uncommitted changes. Wrap it to also check if
923 # largefiles were changed. This is used by bisect and backout.
894 # largefiles were changed. This is used by bisect and backout.
924 def overridebailifchanged(orig, repo):
895 def overridebailifchanged(orig, repo):
925 orig(repo)
896 orig(repo)
926 repo.lfstatus = True
897 repo.lfstatus = True
927 modified, added, removed, deleted = repo.status()[:4]
898 modified, added, removed, deleted = repo.status()[:4]
928 repo.lfstatus = False
899 repo.lfstatus = False
929 if modified or added or removed or deleted:
900 if modified or added or removed or deleted:
930 raise util.Abort(_('uncommitted changes'))
901 raise util.Abort(_('uncommitted changes'))
931
902
932 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
903 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
933 def overridefetch(orig, ui, repo, *pats, **opts):
904 def overridefetch(orig, ui, repo, *pats, **opts):
934 repo.lfstatus = True
905 repo.lfstatus = True
935 modified, added, removed, deleted = repo.status()[:4]
906 modified, added, removed, deleted = repo.status()[:4]
936 repo.lfstatus = False
907 repo.lfstatus = False
937 if modified or added or removed or deleted:
908 if modified or added or removed or deleted:
938 raise util.Abort(_('uncommitted changes'))
909 raise util.Abort(_('uncommitted changes'))
939 return orig(ui, repo, *pats, **opts)
910 return orig(ui, repo, *pats, **opts)
940
911
941 def overrideforget(orig, ui, repo, *pats, **opts):
912 def overrideforget(orig, ui, repo, *pats, **opts):
942 installnormalfilesmatchfn(repo[None].manifest())
913 installnormalfilesmatchfn(repo[None].manifest())
943 result = orig(ui, repo, *pats, **opts)
914 result = orig(ui, repo, *pats, **opts)
944 restorematchfn()
915 restorematchfn()
945 m = scmutil.match(repo[None], pats, opts)
916 m = scmutil.match(repo[None], pats, opts)
946
917
947 try:
918 try:
948 repo.lfstatus = True
919 repo.lfstatus = True
949 s = repo.status(match=m, clean=True)
920 s = repo.status(match=m, clean=True)
950 finally:
921 finally:
951 repo.lfstatus = False
922 repo.lfstatus = False
952 forget = sorted(s[0] + s[1] + s[3] + s[6])
923 forget = sorted(s[0] + s[1] + s[3] + s[6])
953 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
924 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
954
925
955 for f in forget:
926 for f in forget:
956 if lfutil.standin(f) not in repo.dirstate and not \
927 if lfutil.standin(f) not in repo.dirstate and not \
957 os.path.isdir(m.rel(lfutil.standin(f))):
928 os.path.isdir(m.rel(lfutil.standin(f))):
958 ui.warn(_('not removing %s: file is already untracked\n')
929 ui.warn(_('not removing %s: file is already untracked\n')
959 % m.rel(f))
930 % m.rel(f))
960 result = 1
931 result = 1
961
932
962 for f in forget:
933 for f in forget:
963 if ui.verbose or not m.exact(f):
934 if ui.verbose or not m.exact(f):
964 ui.status(_('removing %s\n') % m.rel(f))
935 ui.status(_('removing %s\n') % m.rel(f))
965
936
966 # Need to lock because standin files are deleted then removed from the
937 # Need to lock because standin files are deleted then removed from the
967 # repository and we could race in-between.
938 # repository and we could race in-between.
968 wlock = repo.wlock()
939 wlock = repo.wlock()
969 try:
940 try:
970 lfdirstate = lfutil.openlfdirstate(ui, repo)
941 lfdirstate = lfutil.openlfdirstate(ui, repo)
971 for f in forget:
942 for f in forget:
972 if lfdirstate[f] == 'a':
943 if lfdirstate[f] == 'a':
973 lfdirstate.drop(f)
944 lfdirstate.drop(f)
974 else:
945 else:
975 lfdirstate.remove(f)
946 lfdirstate.remove(f)
976 lfdirstate.write()
947 lfdirstate.write()
977 standins = [lfutil.standin(f) for f in forget]
948 standins = [lfutil.standin(f) for f in forget]
978 for f in standins:
949 for f in standins:
979 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
950 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
980 repo[None].forget(standins)
951 repo[None].forget(standins)
981 finally:
952 finally:
982 wlock.release()
953 wlock.release()
983
954
984 return result
955 return result
985
956
986 def outgoinghook(ui, repo, other, opts, missing):
957 def outgoinghook(ui, repo, other, opts, missing):
987 if opts.pop('large', None):
958 if opts.pop('large', None):
988 toupload = set()
959 toupload = set()
989 lfutil.getlfilestoupload(repo, missing,
960 lfutil.getlfilestoupload(repo, missing,
990 lambda fn, lfhash: toupload.add(fn))
961 lambda fn, lfhash: toupload.add(fn))
991 if not toupload:
962 if not toupload:
992 ui.status(_('largefiles: no files to upload\n'))
963 ui.status(_('largefiles: no files to upload\n'))
993 else:
964 else:
994 ui.status(_('largefiles to upload:\n'))
965 ui.status(_('largefiles to upload:\n'))
995 for file in sorted(toupload):
966 for file in sorted(toupload):
996 ui.status(lfutil.splitstandin(file) + '\n')
967 ui.status(lfutil.splitstandin(file) + '\n')
997 ui.status('\n')
968 ui.status('\n')
998
969
999 def summaryremotehook(ui, repo, opts, changes):
970 def summaryremotehook(ui, repo, opts, changes):
1000 largeopt = opts.get('large', False)
971 largeopt = opts.get('large', False)
1001 if changes is None:
972 if changes is None:
1002 if largeopt:
973 if largeopt:
1003 return (False, True) # only outgoing check is needed
974 return (False, True) # only outgoing check is needed
1004 else:
975 else:
1005 return (False, False)
976 return (False, False)
1006 elif largeopt:
977 elif largeopt:
1007 url, branch, peer, outgoing = changes[1]
978 url, branch, peer, outgoing = changes[1]
1008 if peer is None:
979 if peer is None:
1009 # i18n: column positioning for "hg summary"
980 # i18n: column positioning for "hg summary"
1010 ui.status(_('largefiles: (no remote repo)\n'))
981 ui.status(_('largefiles: (no remote repo)\n'))
1011 return
982 return
1012
983
1013 toupload = set()
984 toupload = set()
1014 lfutil.getlfilestoupload(repo, outgoing.missing,
985 lfutil.getlfilestoupload(repo, outgoing.missing,
1015 lambda fn, lfhash: toupload.add(fn))
986 lambda fn, lfhash: toupload.add(fn))
1016 if not toupload:
987 if not toupload:
1017 # i18n: column positioning for "hg summary"
988 # i18n: column positioning for "hg summary"
1018 ui.status(_('largefiles: (no files to upload)\n'))
989 ui.status(_('largefiles: (no files to upload)\n'))
1019 else:
990 else:
1020 # i18n: column positioning for "hg summary"
991 # i18n: column positioning for "hg summary"
1021 ui.status(_('largefiles: %d to upload\n') % len(toupload))
992 ui.status(_('largefiles: %d to upload\n') % len(toupload))
1022
993
1023 def overridesummary(orig, ui, repo, *pats, **opts):
994 def overridesummary(orig, ui, repo, *pats, **opts):
1024 try:
995 try:
1025 repo.lfstatus = True
996 repo.lfstatus = True
1026 orig(ui, repo, *pats, **opts)
997 orig(ui, repo, *pats, **opts)
1027 finally:
998 finally:
1028 repo.lfstatus = False
999 repo.lfstatus = False
1029
1000
1030 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1001 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1031 similarity=None):
1002 similarity=None):
1032 if not lfutil.islfilesrepo(repo):
1003 if not lfutil.islfilesrepo(repo):
1033 return orig(repo, pats, opts, dry_run, similarity)
1004 return orig(repo, pats, opts, dry_run, similarity)
1034 # Get the list of missing largefiles so we can remove them
1005 # Get the list of missing largefiles so we can remove them
1035 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1006 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1036 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
1007 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
1037 False, False)
1008 False, False)
1038 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
1009 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
1039
1010
1040 # Call into the normal remove code, but the removing of the standin, we want
1011 # Call into the normal remove code, but the removing of the standin, we want
1041 # to have handled by original addremove. Monkey patching here makes sure
1012 # to have handled by original addremove. Monkey patching here makes sure
1042 # we don't remove the standin in the largefiles code, preventing a very
1013 # we don't remove the standin in the largefiles code, preventing a very
1043 # confused state later.
1014 # confused state later.
1044 if missing:
1015 if missing:
1045 m = [repo.wjoin(f) for f in missing]
1016 m = [repo.wjoin(f) for f in missing]
1046 repo._isaddremove = True
1017 repo._isaddremove = True
1047 removelargefiles(repo.ui, repo, *m, **opts)
1018 removelargefiles(repo.ui, repo, *m, **opts)
1048 repo._isaddremove = False
1019 repo._isaddremove = False
1049 # Call into the normal add code, and any files that *should* be added as
1020 # Call into the normal add code, and any files that *should* be added as
1050 # largefiles will be
1021 # largefiles will be
1051 addlargefiles(repo.ui, repo, *pats, **opts)
1022 addlargefiles(repo.ui, repo, *pats, **opts)
1052 # Now that we've handled largefiles, hand off to the original addremove
1023 # Now that we've handled largefiles, hand off to the original addremove
1053 # function to take care of the rest. Make sure it doesn't do anything with
1024 # function to take care of the rest. Make sure it doesn't do anything with
1054 # largefiles by installing a matcher that will ignore them.
1025 # largefiles by installing a matcher that will ignore them.
1055 installnormalfilesmatchfn(repo[None].manifest())
1026 installnormalfilesmatchfn(repo[None].manifest())
1056 result = orig(repo, pats, opts, dry_run, similarity)
1027 result = orig(repo, pats, opts, dry_run, similarity)
1057 restorematchfn()
1028 restorematchfn()
1058 return result
1029 return result
1059
1030
1060 # Calling purge with --all will cause the largefiles to be deleted.
1031 # Calling purge with --all will cause the largefiles to be deleted.
1061 # Override repo.status to prevent this from happening.
1032 # Override repo.status to prevent this from happening.
1062 def overridepurge(orig, ui, repo, *dirs, **opts):
1033 def overridepurge(orig, ui, repo, *dirs, **opts):
1063 # XXX large file status is buggy when used on repo proxy.
1034 # XXX large file status is buggy when used on repo proxy.
1064 # XXX this needs to be investigate.
1035 # XXX this needs to be investigate.
1065 repo = repo.unfiltered()
1036 repo = repo.unfiltered()
1066 oldstatus = repo.status
1037 oldstatus = repo.status
1067 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1038 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1068 clean=False, unknown=False, listsubrepos=False):
1039 clean=False, unknown=False, listsubrepos=False):
1069 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1040 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1070 listsubrepos)
1041 listsubrepos)
1071 lfdirstate = lfutil.openlfdirstate(ui, repo)
1042 lfdirstate = lfutil.openlfdirstate(ui, repo)
1072 modified, added, removed, deleted, unknown, ignored, clean = r
1043 modified, added, removed, deleted, unknown, ignored, clean = r
1073 unknown = [f for f in unknown if lfdirstate[f] == '?']
1044 unknown = [f for f in unknown if lfdirstate[f] == '?']
1074 ignored = [f for f in ignored if lfdirstate[f] == '?']
1045 ignored = [f for f in ignored if lfdirstate[f] == '?']
1075 return modified, added, removed, deleted, unknown, ignored, clean
1046 return modified, added, removed, deleted, unknown, ignored, clean
1076 repo.status = overridestatus
1047 repo.status = overridestatus
1077 orig(ui, repo, *dirs, **opts)
1048 orig(ui, repo, *dirs, **opts)
1078 repo.status = oldstatus
1049 repo.status = oldstatus
1079
1050
1080 def overriderollback(orig, ui, repo, **opts):
1051 def overriderollback(orig, ui, repo, **opts):
1081 result = orig(ui, repo, **opts)
1052 result = orig(ui, repo, **opts)
1082 merge.update(repo, node=None, branchmerge=False, force=True,
1053 merge.update(repo, node=None, branchmerge=False, force=True,
1083 partial=lfutil.isstandin)
1054 partial=lfutil.isstandin)
1084 wlock = repo.wlock()
1055 wlock = repo.wlock()
1085 try:
1056 try:
1086 lfdirstate = lfutil.openlfdirstate(ui, repo)
1057 lfdirstate = lfutil.openlfdirstate(ui, repo)
1087 lfiles = lfutil.listlfiles(repo)
1058 lfiles = lfutil.listlfiles(repo)
1088 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
1059 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
1089 for file in lfiles:
1060 for file in lfiles:
1090 if file in oldlfiles:
1061 if file in oldlfiles:
1091 lfdirstate.normallookup(file)
1062 lfdirstate.normallookup(file)
1092 else:
1063 else:
1093 lfdirstate.add(file)
1064 lfdirstate.add(file)
1094 lfdirstate.write()
1065 lfdirstate.write()
1095 finally:
1066 finally:
1096 wlock.release()
1067 wlock.release()
1097 return result
1068 return result
1098
1069
1099 def overridetransplant(orig, ui, repo, *revs, **opts):
1070 def overridetransplant(orig, ui, repo, *revs, **opts):
1100 try:
1071 try:
1101 oldstandins = lfutil.getstandinsstate(repo)
1072 oldstandins = lfutil.getstandinsstate(repo)
1102 repo._istransplanting = True
1073 repo._istransplanting = True
1103 result = orig(ui, repo, *revs, **opts)
1074 result = orig(ui, repo, *revs, **opts)
1104 newstandins = lfutil.getstandinsstate(repo)
1075 newstandins = lfutil.getstandinsstate(repo)
1105 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1076 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1106 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1077 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1107 printmessage=True)
1078 printmessage=True)
1108 finally:
1079 finally:
1109 repo._istransplanting = False
1080 repo._istransplanting = False
1110 return result
1081 return result
1111
1082
1112 def overridecat(orig, ui, repo, file1, *pats, **opts):
1083 def overridecat(orig, ui, repo, file1, *pats, **opts):
1113 ctx = scmutil.revsingle(repo, opts.get('rev'))
1084 ctx = scmutil.revsingle(repo, opts.get('rev'))
1114 err = 1
1085 err = 1
1115 notbad = set()
1086 notbad = set()
1116 m = scmutil.match(ctx, (file1,) + pats, opts)
1087 m = scmutil.match(ctx, (file1,) + pats, opts)
1117 origmatchfn = m.matchfn
1088 origmatchfn = m.matchfn
1118 def lfmatchfn(f):
1089 def lfmatchfn(f):
1119 if origmatchfn(f):
1090 if origmatchfn(f):
1120 return True
1091 return True
1121 lf = lfutil.splitstandin(f)
1092 lf = lfutil.splitstandin(f)
1122 if lf is None:
1093 if lf is None:
1123 return False
1094 return False
1124 notbad.add(lf)
1095 notbad.add(lf)
1125 return origmatchfn(lf)
1096 return origmatchfn(lf)
1126 m.matchfn = lfmatchfn
1097 m.matchfn = lfmatchfn
1127 origbadfn = m.bad
1098 origbadfn = m.bad
1128 def lfbadfn(f, msg):
1099 def lfbadfn(f, msg):
1129 if not f in notbad:
1100 if not f in notbad:
1130 origbadfn(f, msg)
1101 origbadfn(f, msg)
1131 m.bad = lfbadfn
1102 m.bad = lfbadfn
1132 for f in ctx.walk(m):
1103 for f in ctx.walk(m):
1133 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1104 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1134 pathname=f)
1105 pathname=f)
1135 lf = lfutil.splitstandin(f)
1106 lf = lfutil.splitstandin(f)
1136 if lf is None or origmatchfn(f):
1107 if lf is None or origmatchfn(f):
1137 # duplicating unreachable code from commands.cat
1108 # duplicating unreachable code from commands.cat
1138 data = ctx[f].data()
1109 data = ctx[f].data()
1139 if opts.get('decode'):
1110 if opts.get('decode'):
1140 data = repo.wwritedata(f, data)
1111 data = repo.wwritedata(f, data)
1141 fp.write(data)
1112 fp.write(data)
1142 else:
1113 else:
1143 hash = lfutil.readstandin(repo, lf, ctx.rev())
1114 hash = lfutil.readstandin(repo, lf, ctx.rev())
1144 if not lfutil.inusercache(repo.ui, hash):
1115 if not lfutil.inusercache(repo.ui, hash):
1145 store = basestore._openstore(repo)
1116 store = basestore._openstore(repo)
1146 success, missing = store.get([(lf, hash)])
1117 success, missing = store.get([(lf, hash)])
1147 if len(success) != 1:
1118 if len(success) != 1:
1148 raise util.Abort(
1119 raise util.Abort(
1149 _('largefile %s is not in cache and could not be '
1120 _('largefile %s is not in cache and could not be '
1150 'downloaded') % lf)
1121 'downloaded') % lf)
1151 path = lfutil.usercachepath(repo.ui, hash)
1122 path = lfutil.usercachepath(repo.ui, hash)
1152 fpin = open(path, "rb")
1123 fpin = open(path, "rb")
1153 for chunk in util.filechunkiter(fpin, 128 * 1024):
1124 for chunk in util.filechunkiter(fpin, 128 * 1024):
1154 fp.write(chunk)
1125 fp.write(chunk)
1155 fpin.close()
1126 fpin.close()
1156 fp.close()
1127 fp.close()
1157 err = 0
1128 err = 0
1158 return err
1129 return err
1159
1130
1160 def mercurialsinkbefore(orig, sink):
1131 def mercurialsinkbefore(orig, sink):
1161 sink.repo._isconverting = True
1132 sink.repo._isconverting = True
1162 orig(sink)
1133 orig(sink)
1163
1134
1164 def mercurialsinkafter(orig, sink):
1135 def mercurialsinkafter(orig, sink):
1165 sink.repo._isconverting = False
1136 sink.repo._isconverting = False
1166 orig(sink)
1137 orig(sink)
General Comments 0
You need to be logged in to leave comments. Login now