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