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