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