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