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