##// END OF EJS Templates
largefiles: fix cat for largefiles (issue3352)...
Na'Tosha Bard -
r16439:290850e7 default
parent child Browse files
Show More
@@ -1,500 +1,517
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 '''High-level command function for lfconvert, plus the cmdtable.'''
9 '''High-level command function for lfconvert, plus the cmdtable.'''
10
10
11 import os
11 import os
12 import shutil
12 import shutil
13
13
14 from mercurial import util, match as match_, hg, node, context, error
14 from mercurial import util, match as match_, hg, node, context, error, cmdutil
15 from mercurial.i18n import _
15 from mercurial.i18n import _
16
16
17 import lfutil
17 import lfutil
18 import basestore
18 import basestore
19
19
20 # -- Commands ----------------------------------------------------------
20 # -- Commands ----------------------------------------------------------
21
21
22 def lfconvert(ui, src, dest, *pats, **opts):
22 def lfconvert(ui, src, dest, *pats, **opts):
23 '''convert a normal repository to a largefiles repository
23 '''convert a normal repository to a largefiles repository
24
24
25 Convert repository SOURCE to a new repository DEST, identical to
25 Convert repository SOURCE to a new repository DEST, identical to
26 SOURCE except that certain files will be converted as largefiles:
26 SOURCE except that certain files will be converted as largefiles:
27 specifically, any file that matches any PATTERN *or* whose size is
27 specifically, any file that matches any PATTERN *or* whose size is
28 above the minimum size threshold is converted as a largefile. The
28 above the minimum size threshold is converted as a largefile. The
29 size used to determine whether or not to track a file as a
29 size used to determine whether or not to track a file as a
30 largefile is the size of the first version of the file. The
30 largefile is the size of the first version of the file. The
31 minimum size can be specified either with --size or in
31 minimum size can be specified either with --size or in
32 configuration as ``largefiles.size``.
32 configuration as ``largefiles.size``.
33
33
34 After running this command you will need to make sure that
34 After running this command you will need to make sure that
35 largefiles is enabled anywhere you intend to push the new
35 largefiles is enabled anywhere you intend to push the new
36 repository.
36 repository.
37
37
38 Use --to-normal to convert largefiles back to normal files; after
38 Use --to-normal to convert largefiles back to normal files; after
39 this, the DEST repository can be used without largefiles at all.'''
39 this, the DEST repository can be used without largefiles at all.'''
40
40
41 if opts['to_normal']:
41 if opts['to_normal']:
42 tolfile = False
42 tolfile = False
43 else:
43 else:
44 tolfile = True
44 tolfile = True
45 size = lfutil.getminsize(ui, True, opts.get('size'), default=None)
45 size = lfutil.getminsize(ui, True, opts.get('size'), default=None)
46
46
47 if not hg.islocal(src):
47 if not hg.islocal(src):
48 raise util.Abort(_('%s is not a local Mercurial repo') % src)
48 raise util.Abort(_('%s is not a local Mercurial repo') % src)
49 if not hg.islocal(dest):
49 if not hg.islocal(dest):
50 raise util.Abort(_('%s is not a local Mercurial repo') % dest)
50 raise util.Abort(_('%s is not a local Mercurial repo') % dest)
51
51
52 rsrc = hg.repository(ui, src)
52 rsrc = hg.repository(ui, src)
53 ui.status(_('initializing destination %s\n') % dest)
53 ui.status(_('initializing destination %s\n') % dest)
54 rdst = hg.repository(ui, dest, create=True)
54 rdst = hg.repository(ui, dest, create=True)
55
55
56 success = False
56 success = False
57 try:
57 try:
58 # Lock destination to prevent modification while it is converted to.
58 # Lock destination to prevent modification while it is converted to.
59 # Don't need to lock src because we are just reading from its history
59 # Don't need to lock src because we are just reading from its history
60 # which can't change.
60 # which can't change.
61 dstlock = rdst.lock()
61 dstlock = rdst.lock()
62
62
63 # Get a list of all changesets in the source. The easy way to do this
63 # Get a list of all changesets in the source. The easy way to do this
64 # is to simply walk the changelog, using changelog.nodesbewteen().
64 # is to simply walk the changelog, using changelog.nodesbewteen().
65 # Take a look at mercurial/revlog.py:639 for more details.
65 # Take a look at mercurial/revlog.py:639 for more details.
66 # Use a generator instead of a list to decrease memory usage
66 # Use a generator instead of a list to decrease memory usage
67 ctxs = (rsrc[ctx] for ctx in rsrc.changelog.nodesbetween(None,
67 ctxs = (rsrc[ctx] for ctx in rsrc.changelog.nodesbetween(None,
68 rsrc.heads())[0])
68 rsrc.heads())[0])
69 revmap = {node.nullid: node.nullid}
69 revmap = {node.nullid: node.nullid}
70 if tolfile:
70 if tolfile:
71 lfiles = set()
71 lfiles = set()
72 normalfiles = set()
72 normalfiles = set()
73 if not pats:
73 if not pats:
74 pats = ui.configlist(lfutil.longname, 'patterns', default=[])
74 pats = ui.configlist(lfutil.longname, 'patterns', default=[])
75 if pats:
75 if pats:
76 matcher = match_.match(rsrc.root, '', list(pats))
76 matcher = match_.match(rsrc.root, '', list(pats))
77 else:
77 else:
78 matcher = None
78 matcher = None
79
79
80 lfiletohash = {}
80 lfiletohash = {}
81 for ctx in ctxs:
81 for ctx in ctxs:
82 ui.progress(_('converting revisions'), ctx.rev(),
82 ui.progress(_('converting revisions'), ctx.rev(),
83 unit=_('revision'), total=rsrc['tip'].rev())
83 unit=_('revision'), total=rsrc['tip'].rev())
84 _lfconvert_addchangeset(rsrc, rdst, ctx, revmap,
84 _lfconvert_addchangeset(rsrc, rdst, ctx, revmap,
85 lfiles, normalfiles, matcher, size, lfiletohash)
85 lfiles, normalfiles, matcher, size, lfiletohash)
86 ui.progress(_('converting revisions'), None)
86 ui.progress(_('converting revisions'), None)
87
87
88 if os.path.exists(rdst.wjoin(lfutil.shortname)):
88 if os.path.exists(rdst.wjoin(lfutil.shortname)):
89 shutil.rmtree(rdst.wjoin(lfutil.shortname))
89 shutil.rmtree(rdst.wjoin(lfutil.shortname))
90
90
91 for f in lfiletohash.keys():
91 for f in lfiletohash.keys():
92 if os.path.isfile(rdst.wjoin(f)):
92 if os.path.isfile(rdst.wjoin(f)):
93 os.unlink(rdst.wjoin(f))
93 os.unlink(rdst.wjoin(f))
94 try:
94 try:
95 os.removedirs(os.path.dirname(rdst.wjoin(f)))
95 os.removedirs(os.path.dirname(rdst.wjoin(f)))
96 except OSError:
96 except OSError:
97 pass
97 pass
98
98
99 # If there were any files converted to largefiles, add largefiles
99 # If there were any files converted to largefiles, add largefiles
100 # to the destination repository's requirements.
100 # to the destination repository's requirements.
101 if lfiles:
101 if lfiles:
102 rdst.requirements.add('largefiles')
102 rdst.requirements.add('largefiles')
103 rdst._writerequirements()
103 rdst._writerequirements()
104 else:
104 else:
105 for ctx in ctxs:
105 for ctx in ctxs:
106 ui.progress(_('converting revisions'), ctx.rev(),
106 ui.progress(_('converting revisions'), ctx.rev(),
107 unit=_('revision'), total=rsrc['tip'].rev())
107 unit=_('revision'), total=rsrc['tip'].rev())
108 _addchangeset(ui, rsrc, rdst, ctx, revmap)
108 _addchangeset(ui, rsrc, rdst, ctx, revmap)
109
109
110 ui.progress(_('converting revisions'), None)
110 ui.progress(_('converting revisions'), None)
111 success = True
111 success = True
112 finally:
112 finally:
113 if not success:
113 if not success:
114 # we failed, remove the new directory
114 # we failed, remove the new directory
115 shutil.rmtree(rdst.root)
115 shutil.rmtree(rdst.root)
116 dstlock.release()
116 dstlock.release()
117
117
118 def _addchangeset(ui, rsrc, rdst, ctx, revmap):
118 def _addchangeset(ui, rsrc, rdst, ctx, revmap):
119 # Convert src parents to dst parents
119 # Convert src parents to dst parents
120 parents = _convertparents(ctx, revmap)
120 parents = _convertparents(ctx, revmap)
121
121
122 # Generate list of changed files
122 # Generate list of changed files
123 files = _getchangedfiles(ctx, parents)
123 files = _getchangedfiles(ctx, parents)
124
124
125 def getfilectx(repo, memctx, f):
125 def getfilectx(repo, memctx, f):
126 if lfutil.standin(f) in files:
126 if lfutil.standin(f) in files:
127 # if the file isn't in the manifest then it was removed
127 # if the file isn't in the manifest then it was removed
128 # or renamed, raise IOError to indicate this
128 # or renamed, raise IOError to indicate this
129 try:
129 try:
130 fctx = ctx.filectx(lfutil.standin(f))
130 fctx = ctx.filectx(lfutil.standin(f))
131 except error.LookupError:
131 except error.LookupError:
132 raise IOError()
132 raise IOError()
133 renamed = fctx.renamed()
133 renamed = fctx.renamed()
134 if renamed:
134 if renamed:
135 renamed = lfutil.splitstandin(renamed[0])
135 renamed = lfutil.splitstandin(renamed[0])
136
136
137 hash = fctx.data().strip()
137 hash = fctx.data().strip()
138 path = lfutil.findfile(rsrc, hash)
138 path = lfutil.findfile(rsrc, hash)
139 ### TODO: What if the file is not cached?
139 ### TODO: What if the file is not cached?
140 data = ''
140 data = ''
141 fd = None
141 fd = None
142 try:
142 try:
143 fd = open(path, 'rb')
143 fd = open(path, 'rb')
144 data = fd.read()
144 data = fd.read()
145 finally:
145 finally:
146 if fd:
146 if fd:
147 fd.close()
147 fd.close()
148 return context.memfilectx(f, data, 'l' in fctx.flags(),
148 return context.memfilectx(f, data, 'l' in fctx.flags(),
149 'x' in fctx.flags(), renamed)
149 'x' in fctx.flags(), renamed)
150 else:
150 else:
151 return _getnormalcontext(repo.ui, ctx, f, revmap)
151 return _getnormalcontext(repo.ui, ctx, f, revmap)
152
152
153 dstfiles = []
153 dstfiles = []
154 for file in files:
154 for file in files:
155 if lfutil.isstandin(file):
155 if lfutil.isstandin(file):
156 dstfiles.append(lfutil.splitstandin(file))
156 dstfiles.append(lfutil.splitstandin(file))
157 else:
157 else:
158 dstfiles.append(file)
158 dstfiles.append(file)
159 # Commit
159 # Commit
160 _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap)
160 _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap)
161
161
162 def _lfconvert_addchangeset(rsrc, rdst, ctx, revmap, lfiles, normalfiles,
162 def _lfconvert_addchangeset(rsrc, rdst, ctx, revmap, lfiles, normalfiles,
163 matcher, size, lfiletohash):
163 matcher, size, lfiletohash):
164 # Convert src parents to dst parents
164 # Convert src parents to dst parents
165 parents = _convertparents(ctx, revmap)
165 parents = _convertparents(ctx, revmap)
166
166
167 # Generate list of changed files
167 # Generate list of changed files
168 files = _getchangedfiles(ctx, parents)
168 files = _getchangedfiles(ctx, parents)
169
169
170 dstfiles = []
170 dstfiles = []
171 for f in files:
171 for f in files:
172 if f not in lfiles and f not in normalfiles:
172 if f not in lfiles and f not in normalfiles:
173 islfile = _islfile(f, ctx, matcher, size)
173 islfile = _islfile(f, ctx, matcher, size)
174 # If this file was renamed or copied then copy
174 # If this file was renamed or copied then copy
175 # the lfileness of its predecessor
175 # the lfileness of its predecessor
176 if f in ctx.manifest():
176 if f in ctx.manifest():
177 fctx = ctx.filectx(f)
177 fctx = ctx.filectx(f)
178 renamed = fctx.renamed()
178 renamed = fctx.renamed()
179 renamedlfile = renamed and renamed[0] in lfiles
179 renamedlfile = renamed and renamed[0] in lfiles
180 islfile |= renamedlfile
180 islfile |= renamedlfile
181 if 'l' in fctx.flags():
181 if 'l' in fctx.flags():
182 if renamedlfile:
182 if renamedlfile:
183 raise util.Abort(
183 raise util.Abort(
184 _('renamed/copied largefile %s becomes symlink')
184 _('renamed/copied largefile %s becomes symlink')
185 % f)
185 % f)
186 islfile = False
186 islfile = False
187 if islfile:
187 if islfile:
188 lfiles.add(f)
188 lfiles.add(f)
189 else:
189 else:
190 normalfiles.add(f)
190 normalfiles.add(f)
191
191
192 if f in lfiles:
192 if f in lfiles:
193 dstfiles.append(lfutil.standin(f))
193 dstfiles.append(lfutil.standin(f))
194 # largefile in manifest if it has not been removed/renamed
194 # largefile in manifest if it has not been removed/renamed
195 if f in ctx.manifest():
195 if f in ctx.manifest():
196 fctx = ctx.filectx(f)
196 fctx = ctx.filectx(f)
197 if 'l' in fctx.flags():
197 if 'l' in fctx.flags():
198 renamed = fctx.renamed()
198 renamed = fctx.renamed()
199 if renamed and renamed[0] in lfiles:
199 if renamed and renamed[0] in lfiles:
200 raise util.Abort(_('largefile %s becomes symlink') % f)
200 raise util.Abort(_('largefile %s becomes symlink') % f)
201
201
202 # largefile was modified, update standins
202 # largefile was modified, update standins
203 fullpath = rdst.wjoin(f)
203 fullpath = rdst.wjoin(f)
204 util.makedirs(os.path.dirname(fullpath))
204 util.makedirs(os.path.dirname(fullpath))
205 m = util.sha1('')
205 m = util.sha1('')
206 m.update(ctx[f].data())
206 m.update(ctx[f].data())
207 hash = m.hexdigest()
207 hash = m.hexdigest()
208 if f not in lfiletohash or lfiletohash[f] != hash:
208 if f not in lfiletohash or lfiletohash[f] != hash:
209 try:
209 try:
210 fd = open(fullpath, 'wb')
210 fd = open(fullpath, 'wb')
211 fd.write(ctx[f].data())
211 fd.write(ctx[f].data())
212 finally:
212 finally:
213 if fd:
213 if fd:
214 fd.close()
214 fd.close()
215 executable = 'x' in ctx[f].flags()
215 executable = 'x' in ctx[f].flags()
216 os.chmod(fullpath, lfutil.getmode(executable))
216 os.chmod(fullpath, lfutil.getmode(executable))
217 lfutil.writestandin(rdst, lfutil.standin(f), hash,
217 lfutil.writestandin(rdst, lfutil.standin(f), hash,
218 executable)
218 executable)
219 lfiletohash[f] = hash
219 lfiletohash[f] = hash
220 else:
220 else:
221 # normal file
221 # normal file
222 dstfiles.append(f)
222 dstfiles.append(f)
223
223
224 def getfilectx(repo, memctx, f):
224 def getfilectx(repo, memctx, f):
225 if lfutil.isstandin(f):
225 if lfutil.isstandin(f):
226 # if the file isn't in the manifest then it was removed
226 # if the file isn't in the manifest then it was removed
227 # or renamed, raise IOError to indicate this
227 # or renamed, raise IOError to indicate this
228 srcfname = lfutil.splitstandin(f)
228 srcfname = lfutil.splitstandin(f)
229 try:
229 try:
230 fctx = ctx.filectx(srcfname)
230 fctx = ctx.filectx(srcfname)
231 except error.LookupError:
231 except error.LookupError:
232 raise IOError()
232 raise IOError()
233 renamed = fctx.renamed()
233 renamed = fctx.renamed()
234 if renamed:
234 if renamed:
235 # standin is always a largefile because largefile-ness
235 # standin is always a largefile because largefile-ness
236 # doesn't change after rename or copy
236 # doesn't change after rename or copy
237 renamed = lfutil.standin(renamed[0])
237 renamed = lfutil.standin(renamed[0])
238
238
239 return context.memfilectx(f, lfiletohash[srcfname] + '\n', 'l' in
239 return context.memfilectx(f, lfiletohash[srcfname] + '\n', 'l' in
240 fctx.flags(), 'x' in fctx.flags(), renamed)
240 fctx.flags(), 'x' in fctx.flags(), renamed)
241 else:
241 else:
242 return _getnormalcontext(repo.ui, ctx, f, revmap)
242 return _getnormalcontext(repo.ui, ctx, f, revmap)
243
243
244 # Commit
244 # Commit
245 _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap)
245 _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap)
246
246
247 def _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap):
247 def _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap):
248 mctx = context.memctx(rdst, parents, ctx.description(), dstfiles,
248 mctx = context.memctx(rdst, parents, ctx.description(), dstfiles,
249 getfilectx, ctx.user(), ctx.date(), ctx.extra())
249 getfilectx, ctx.user(), ctx.date(), ctx.extra())
250 ret = rdst.commitctx(mctx)
250 ret = rdst.commitctx(mctx)
251 rdst.dirstate.setparents(ret)
251 rdst.dirstate.setparents(ret)
252 revmap[ctx.node()] = rdst.changelog.tip()
252 revmap[ctx.node()] = rdst.changelog.tip()
253
253
254 # Generate list of changed files
254 # Generate list of changed files
255 def _getchangedfiles(ctx, parents):
255 def _getchangedfiles(ctx, parents):
256 files = set(ctx.files())
256 files = set(ctx.files())
257 if node.nullid not in parents:
257 if node.nullid not in parents:
258 mc = ctx.manifest()
258 mc = ctx.manifest()
259 mp1 = ctx.parents()[0].manifest()
259 mp1 = ctx.parents()[0].manifest()
260 mp2 = ctx.parents()[1].manifest()
260 mp2 = ctx.parents()[1].manifest()
261 files |= (set(mp1) | set(mp2)) - set(mc)
261 files |= (set(mp1) | set(mp2)) - set(mc)
262 for f in mc:
262 for f in mc:
263 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
263 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
264 files.add(f)
264 files.add(f)
265 return files
265 return files
266
266
267 # Convert src parents to dst parents
267 # Convert src parents to dst parents
268 def _convertparents(ctx, revmap):
268 def _convertparents(ctx, revmap):
269 parents = []
269 parents = []
270 for p in ctx.parents():
270 for p in ctx.parents():
271 parents.append(revmap[p.node()])
271 parents.append(revmap[p.node()])
272 while len(parents) < 2:
272 while len(parents) < 2:
273 parents.append(node.nullid)
273 parents.append(node.nullid)
274 return parents
274 return parents
275
275
276 # Get memfilectx for a normal file
276 # Get memfilectx for a normal file
277 def _getnormalcontext(ui, ctx, f, revmap):
277 def _getnormalcontext(ui, ctx, f, revmap):
278 try:
278 try:
279 fctx = ctx.filectx(f)
279 fctx = ctx.filectx(f)
280 except error.LookupError:
280 except error.LookupError:
281 raise IOError()
281 raise IOError()
282 renamed = fctx.renamed()
282 renamed = fctx.renamed()
283 if renamed:
283 if renamed:
284 renamed = renamed[0]
284 renamed = renamed[0]
285
285
286 data = fctx.data()
286 data = fctx.data()
287 if f == '.hgtags':
287 if f == '.hgtags':
288 data = _converttags (ui, revmap, data)
288 data = _converttags (ui, revmap, data)
289 return context.memfilectx(f, data, 'l' in fctx.flags(),
289 return context.memfilectx(f, data, 'l' in fctx.flags(),
290 'x' in fctx.flags(), renamed)
290 'x' in fctx.flags(), renamed)
291
291
292 # Remap tag data using a revision map
292 # Remap tag data using a revision map
293 def _converttags(ui, revmap, data):
293 def _converttags(ui, revmap, data):
294 newdata = []
294 newdata = []
295 for line in data.splitlines():
295 for line in data.splitlines():
296 try:
296 try:
297 id, name = line.split(' ', 1)
297 id, name = line.split(' ', 1)
298 except ValueError:
298 except ValueError:
299 ui.warn(_('skipping incorrectly formatted tag %s\n'
299 ui.warn(_('skipping incorrectly formatted tag %s\n'
300 % line))
300 % line))
301 continue
301 continue
302 try:
302 try:
303 newid = node.bin(id)
303 newid = node.bin(id)
304 except TypeError:
304 except TypeError:
305 ui.warn(_('skipping incorrectly formatted id %s\n'
305 ui.warn(_('skipping incorrectly formatted id %s\n'
306 % id))
306 % id))
307 continue
307 continue
308 try:
308 try:
309 newdata.append('%s %s\n' % (node.hex(revmap[newid]),
309 newdata.append('%s %s\n' % (node.hex(revmap[newid]),
310 name))
310 name))
311 except KeyError:
311 except KeyError:
312 ui.warn(_('no mapping for id %s\n') % id)
312 ui.warn(_('no mapping for id %s\n') % id)
313 continue
313 continue
314 return ''.join(newdata)
314 return ''.join(newdata)
315
315
316 def _islfile(file, ctx, matcher, size):
316 def _islfile(file, ctx, matcher, size):
317 '''Return true if file should be considered a largefile, i.e.
317 '''Return true if file should be considered a largefile, i.e.
318 matcher matches it or it is larger than size.'''
318 matcher matches it or it is larger than size.'''
319 # never store special .hg* files as largefiles
319 # never store special .hg* files as largefiles
320 if file == '.hgtags' or file == '.hgignore' or file == '.hgsigs':
320 if file == '.hgtags' or file == '.hgignore' or file == '.hgsigs':
321 return False
321 return False
322 if matcher and matcher(file):
322 if matcher and matcher(file):
323 return True
323 return True
324 try:
324 try:
325 return ctx.filectx(file).size() >= size * 1024 * 1024
325 return ctx.filectx(file).size() >= size * 1024 * 1024
326 except error.LookupError:
326 except error.LookupError:
327 return False
327 return False
328
328
329 def uploadlfiles(ui, rsrc, rdst, files):
329 def uploadlfiles(ui, rsrc, rdst, files):
330 '''upload largefiles to the central store'''
330 '''upload largefiles to the central store'''
331
331
332 if not files:
332 if not files:
333 return
333 return
334
334
335 store = basestore._openstore(rsrc, rdst, put=True)
335 store = basestore._openstore(rsrc, rdst, put=True)
336
336
337 at = 0
337 at = 0
338 files = filter(lambda h: not store.exists(h), files)
338 files = filter(lambda h: not store.exists(h), files)
339 for hash in files:
339 for hash in files:
340 ui.progress(_('uploading largefiles'), at, unit='largefile',
340 ui.progress(_('uploading largefiles'), at, unit='largefile',
341 total=len(files))
341 total=len(files))
342 source = lfutil.findfile(rsrc, hash)
342 source = lfutil.findfile(rsrc, hash)
343 if not source:
343 if not source:
344 raise util.Abort(_('largefile %s missing from store'
344 raise util.Abort(_('largefile %s missing from store'
345 ' (needs to be uploaded)') % hash)
345 ' (needs to be uploaded)') % hash)
346 # XXX check for errors here
346 # XXX check for errors here
347 store.put(source, hash)
347 store.put(source, hash)
348 at += 1
348 at += 1
349 ui.progress(_('uploading largefiles'), None)
349 ui.progress(_('uploading largefiles'), None)
350
350
351 def verifylfiles(ui, repo, all=False, contents=False):
351 def verifylfiles(ui, repo, all=False, contents=False):
352 '''Verify that every big file revision in the current changeset
352 '''Verify that every big file revision in the current changeset
353 exists in the central store. With --contents, also verify that
353 exists in the central store. With --contents, also verify that
354 the contents of each big file revision are correct (SHA-1 hash
354 the contents of each big file revision are correct (SHA-1 hash
355 matches the revision ID). With --all, check every changeset in
355 matches the revision ID). With --all, check every changeset in
356 this repository.'''
356 this repository.'''
357 if all:
357 if all:
358 # Pass a list to the function rather than an iterator because we know a
358 # Pass a list to the function rather than an iterator because we know a
359 # list will work.
359 # list will work.
360 revs = range(len(repo))
360 revs = range(len(repo))
361 else:
361 else:
362 revs = ['.']
362 revs = ['.']
363
363
364 store = basestore._openstore(repo)
364 store = basestore._openstore(repo)
365 return store.verify(revs, contents=contents)
365 return store.verify(revs, contents=contents)
366
366
367 def cachelfiles(ui, repo, node):
367 def cachelfiles(ui, repo, node):
368 '''cachelfiles ensures that all largefiles needed by the specified revision
368 '''cachelfiles ensures that all largefiles needed by the specified revision
369 are present in the repository's largefile cache.
369 are present in the repository's largefile cache.
370
370
371 returns a tuple (cached, missing). cached is the list of files downloaded
371 returns a tuple (cached, missing). cached is the list of files downloaded
372 by this operation; missing is the list of files that were needed but could
372 by this operation; missing is the list of files that were needed but could
373 not be found.'''
373 not be found.'''
374 lfiles = lfutil.listlfiles(repo, node)
374 lfiles = lfutil.listlfiles(repo, node)
375 toget = []
375 toget = []
376
376
377 for lfile in lfiles:
377 for lfile in lfiles:
378 # If we are mid-merge, then we have to trust the standin that is in the
378 # If we are mid-merge, then we have to trust the standin that is in the
379 # working copy to have the correct hashvalue. This is because the
379 # working copy to have the correct hashvalue. This is because the
380 # original hg.merge() already updated the standin as part of the normal
380 # original hg.merge() already updated the standin as part of the normal
381 # merge process -- we just have to udpate the largefile to match.
381 # merge process -- we just have to udpate the largefile to match.
382 if (getattr(repo, "_ismerging", False) and
382 if (getattr(repo, "_ismerging", False) and
383 os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
383 os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
384 expectedhash = lfutil.readstandin(repo, lfile)
384 expectedhash = lfutil.readstandin(repo, lfile)
385 else:
385 else:
386 expectedhash = repo[node][lfutil.standin(lfile)].data().strip()
386 expectedhash = repo[node][lfutil.standin(lfile)].data().strip()
387
387
388 # if it exists and its hash matches, it might have been locally
388 # if it exists and its hash matches, it might have been locally
389 # modified before updating and the user chose 'local'. in this case,
389 # modified before updating and the user chose 'local'. in this case,
390 # it will not be in any store, so don't look for it.
390 # it will not be in any store, so don't look for it.
391 if ((not os.path.exists(repo.wjoin(lfile)) or
391 if ((not os.path.exists(repo.wjoin(lfile)) or
392 expectedhash != lfutil.hashfile(repo.wjoin(lfile))) and
392 expectedhash != lfutil.hashfile(repo.wjoin(lfile))) and
393 not lfutil.findfile(repo, expectedhash)):
393 not lfutil.findfile(repo, expectedhash)):
394 toget.append((lfile, expectedhash))
394 toget.append((lfile, expectedhash))
395
395
396 if toget:
396 if toget:
397 store = basestore._openstore(repo)
397 store = basestore._openstore(repo)
398 ret = store.get(toget)
398 ret = store.get(toget)
399 return ret
399 return ret
400
400
401 return ([], [])
401 return ([], [])
402
402
403 def updatelfiles(ui, repo, filelist=None, printmessage=True):
403 def updatelfiles(ui, repo, filelist=None, printmessage=True):
404 wlock = repo.wlock()
404 wlock = repo.wlock()
405 try:
405 try:
406 lfdirstate = lfutil.openlfdirstate(ui, repo)
406 lfdirstate = lfutil.openlfdirstate(ui, repo)
407 lfiles = set(lfutil.listlfiles(repo)) | set(lfdirstate)
407 lfiles = set(lfutil.listlfiles(repo)) | set(lfdirstate)
408
408
409 if filelist is not None:
409 if filelist is not None:
410 lfiles = [f for f in lfiles if f in filelist]
410 lfiles = [f for f in lfiles if f in filelist]
411
411
412 printed = False
412 printed = False
413 if printmessage and lfiles:
413 if printmessage and lfiles:
414 ui.status(_('getting changed largefiles\n'))
414 ui.status(_('getting changed largefiles\n'))
415 printed = True
415 printed = True
416 cachelfiles(ui, repo, '.')
416 cachelfiles(ui, repo, '.')
417
417
418 updated, removed = 0, 0
418 updated, removed = 0, 0
419 for i in map(lambda f: _updatelfile(repo, lfdirstate, f), lfiles):
419 for i in map(lambda f: _updatelfile(repo, lfdirstate, f), lfiles):
420 # increment the appropriate counter according to _updatelfile's
420 # increment the appropriate counter according to _updatelfile's
421 # return value
421 # return value
422 updated += i > 0 and i or 0
422 updated += i > 0 and i or 0
423 removed -= i < 0 and i or 0
423 removed -= i < 0 and i or 0
424 if printmessage and (removed or updated) and not printed:
424 if printmessage and (removed or updated) and not printed:
425 ui.status(_('getting changed largefiles\n'))
425 ui.status(_('getting changed largefiles\n'))
426 printed = True
426 printed = True
427
427
428 lfdirstate.write()
428 lfdirstate.write()
429 if printed and printmessage:
429 if printed and printmessage:
430 ui.status(_('%d largefiles updated, %d removed\n') % (updated,
430 ui.status(_('%d largefiles updated, %d removed\n') % (updated,
431 removed))
431 removed))
432 finally:
432 finally:
433 wlock.release()
433 wlock.release()
434
434
435 def _updatelfile(repo, lfdirstate, lfile):
435 def _updatelfile(repo, lfdirstate, lfile):
436 '''updates a single largefile and copies the state of its standin from
436 '''updates a single largefile and copies the state of its standin from
437 the repository's dirstate to its state in the lfdirstate.
437 the repository's dirstate to its state in the lfdirstate.
438
438
439 returns 1 if the file was modified, -1 if the file was removed, 0 if the
439 returns 1 if the file was modified, -1 if the file was removed, 0 if the
440 file was unchanged, and None if the needed largefile was missing from the
440 file was unchanged, and None if the needed largefile was missing from the
441 cache.'''
441 cache.'''
442 ret = 0
442 ret = 0
443 abslfile = repo.wjoin(lfile)
443 abslfile = repo.wjoin(lfile)
444 absstandin = repo.wjoin(lfutil.standin(lfile))
444 absstandin = repo.wjoin(lfutil.standin(lfile))
445 if os.path.exists(absstandin):
445 if os.path.exists(absstandin):
446 if os.path.exists(absstandin+'.orig'):
446 if os.path.exists(absstandin+'.orig'):
447 shutil.copyfile(abslfile, abslfile+'.orig')
447 shutil.copyfile(abslfile, abslfile+'.orig')
448 expecthash = lfutil.readstandin(repo, lfile)
448 expecthash = lfutil.readstandin(repo, lfile)
449 if (expecthash != '' and
449 if (expecthash != '' and
450 (not os.path.exists(abslfile) or
450 (not os.path.exists(abslfile) or
451 expecthash != lfutil.hashfile(abslfile))):
451 expecthash != lfutil.hashfile(abslfile))):
452 if not lfutil.copyfromcache(repo, expecthash, lfile):
452 if not lfutil.copyfromcache(repo, expecthash, lfile):
453 # use normallookup() to allocate entry in largefiles dirstate,
453 # use normallookup() to allocate entry in largefiles dirstate,
454 # because lack of it misleads lfilesrepo.status() into
454 # because lack of it misleads lfilesrepo.status() into
455 # recognition that such cache missing files are REMOVED.
455 # recognition that such cache missing files are REMOVED.
456 lfdirstate.normallookup(lfile)
456 lfdirstate.normallookup(lfile)
457 return None # don't try to set the mode
457 return None # don't try to set the mode
458 ret = 1
458 ret = 1
459 mode = os.stat(absstandin).st_mode
459 mode = os.stat(absstandin).st_mode
460 if mode != os.stat(abslfile).st_mode:
460 if mode != os.stat(abslfile).st_mode:
461 os.chmod(abslfile, mode)
461 os.chmod(abslfile, mode)
462 ret = 1
462 ret = 1
463 else:
463 else:
464 # Remove lfiles for which the standin is deleted, unless the
464 # Remove lfiles for which the standin is deleted, unless the
465 # lfile is added to the repository again. This happens when a
465 # lfile is added to the repository again. This happens when a
466 # largefile is converted back to a normal file: the standin
466 # largefile is converted back to a normal file: the standin
467 # disappears, but a new (normal) file appears as the lfile.
467 # disappears, but a new (normal) file appears as the lfile.
468 if os.path.exists(abslfile) and lfile not in repo[None]:
468 if os.path.exists(abslfile) and lfile not in repo[None]:
469 util.unlinkpath(abslfile)
469 util.unlinkpath(abslfile)
470 ret = -1
470 ret = -1
471 state = repo.dirstate[lfutil.standin(lfile)]
471 state = repo.dirstate[lfutil.standin(lfile)]
472 if state == 'n':
472 if state == 'n':
473 # When rebasing, we need to synchronize the standin and the largefile,
473 # When rebasing, we need to synchronize the standin and the largefile,
474 # because otherwise the largefile will get reverted. But for commit's
474 # because otherwise the largefile will get reverted. But for commit's
475 # sake, we have to mark the file as unclean.
475 # sake, we have to mark the file as unclean.
476 if getattr(repo, "_isrebasing", False):
476 if getattr(repo, "_isrebasing", False):
477 lfdirstate.normallookup(lfile)
477 lfdirstate.normallookup(lfile)
478 else:
478 else:
479 lfdirstate.normal(lfile)
479 lfdirstate.normal(lfile)
480 elif state == 'r':
480 elif state == 'r':
481 lfdirstate.remove(lfile)
481 lfdirstate.remove(lfile)
482 elif state == 'a':
482 elif state == 'a':
483 lfdirstate.add(lfile)
483 lfdirstate.add(lfile)
484 elif state == '?':
484 elif state == '?':
485 lfdirstate.drop(lfile)
485 lfdirstate.drop(lfile)
486 return ret
486 return ret
487
487
488 def catlfile(repo, lfile, rev, filename):
489 hash = lfutil.readstandin(repo, lfile, rev)
490 if not lfutil.inusercache(repo.ui, hash):
491 store = basestore._openstore(repo)
492 success, missing = store.get([(lfile, hash)])
493 if len(success) != 1:
494 raise util.Abort(
495 _('largefile %s is not in cache and could not be downloaded')
496 % lfile)
497 path = lfutil.usercachepath(repo.ui, hash)
498 fpout = cmdutil.makefileobj(repo, filename)
499 fpin = open(path, "rb")
500 fpout.write(fpin.read())
501 fpout.close()
502 fpin.close()
503 return 0
504
488 # -- hg commands declarations ------------------------------------------------
505 # -- hg commands declarations ------------------------------------------------
489
506
490 cmdtable = {
507 cmdtable = {
491 'lfconvert': (lfconvert,
508 'lfconvert': (lfconvert,
492 [('s', 'size', '',
509 [('s', 'size', '',
493 _('minimum size (MB) for files to be converted '
510 _('minimum size (MB) for files to be converted '
494 'as largefiles'),
511 'as largefiles'),
495 'SIZE'),
512 'SIZE'),
496 ('', 'to-normal', False,
513 ('', 'to-normal', False,
497 _('convert from a largefiles repo to a normal repo')),
514 _('convert from a largefiles repo to a normal repo')),
498 ],
515 ],
499 _('hg lfconvert SOURCE DEST [FILE ...]')),
516 _('hg lfconvert SOURCE DEST [FILE ...]')),
500 }
517 }
@@ -1,968 +1,975
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
10
10
11 import os
11 import os
12 import copy
12 import copy
13
13
14 from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
14 from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
15 node, archival, error, merge
15 node, archival, error, merge
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17 from mercurial.node import hex
17 from mercurial.node import hex
18 from hgext import rebase
18 from hgext import rebase
19
19
20 import lfutil
20 import lfutil
21 import lfcommands
21 import lfcommands
22
22
23 # -- Utility functions: commonly/repeatedly needed functionality ---------------
23 # -- Utility functions: commonly/repeatedly needed functionality ---------------
24
24
25 def installnormalfilesmatchfn(manifest):
25 def installnormalfilesmatchfn(manifest):
26 '''overrides scmutil.match so that the matcher it returns will ignore all
26 '''overrides scmutil.match so that the matcher it returns will ignore all
27 largefiles'''
27 largefiles'''
28 oldmatch = None # for the closure
28 oldmatch = None # for the closure
29 def overridematch(ctx, pats=[], opts={}, globbed=False,
29 def overridematch(ctx, pats=[], opts={}, globbed=False,
30 default='relpath'):
30 default='relpath'):
31 match = oldmatch(ctx, pats, opts, globbed, default)
31 match = oldmatch(ctx, pats, opts, globbed, default)
32 m = copy.copy(match)
32 m = copy.copy(match)
33 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
33 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
34 manifest)
34 manifest)
35 m._files = filter(notlfile, m._files)
35 m._files = filter(notlfile, m._files)
36 m._fmap = set(m._files)
36 m._fmap = set(m._files)
37 origmatchfn = m.matchfn
37 origmatchfn = m.matchfn
38 m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None
38 m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None
39 return m
39 return m
40 oldmatch = installmatchfn(overridematch)
40 oldmatch = installmatchfn(overridematch)
41
41
42 def installmatchfn(f):
42 def installmatchfn(f):
43 oldmatch = scmutil.match
43 oldmatch = scmutil.match
44 setattr(f, 'oldmatch', oldmatch)
44 setattr(f, 'oldmatch', oldmatch)
45 scmutil.match = f
45 scmutil.match = f
46 return oldmatch
46 return oldmatch
47
47
48 def restorematchfn():
48 def restorematchfn():
49 '''restores scmutil.match to what it was before installnormalfilesmatchfn
49 '''restores scmutil.match to what it was before installnormalfilesmatchfn
50 was called. no-op if scmutil.match is its original function.
50 was called. no-op if scmutil.match is its original function.
51
51
52 Note that n calls to installnormalfilesmatchfn will require n calls to
52 Note that n calls to installnormalfilesmatchfn will require n calls to
53 restore matchfn to reverse'''
53 restore matchfn to reverse'''
54 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
54 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
55
55
56 def addlargefiles(ui, repo, *pats, **opts):
56 def addlargefiles(ui, repo, *pats, **opts):
57 large = opts.pop('large', None)
57 large = opts.pop('large', None)
58 lfsize = lfutil.getminsize(
58 lfsize = lfutil.getminsize(
59 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
59 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
60
60
61 lfmatcher = None
61 lfmatcher = None
62 if lfutil.islfilesrepo(repo):
62 if lfutil.islfilesrepo(repo):
63 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
63 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
64 if lfpats:
64 if lfpats:
65 lfmatcher = match_.match(repo.root, '', list(lfpats))
65 lfmatcher = match_.match(repo.root, '', list(lfpats))
66
66
67 lfnames = []
67 lfnames = []
68 m = scmutil.match(repo[None], pats, opts)
68 m = scmutil.match(repo[None], pats, opts)
69 m.bad = lambda x, y: None
69 m.bad = lambda x, y: None
70 wctx = repo[None]
70 wctx = repo[None]
71 for f in repo.walk(m):
71 for f in repo.walk(m):
72 exact = m.exact(f)
72 exact = m.exact(f)
73 lfile = lfutil.standin(f) in wctx
73 lfile = lfutil.standin(f) in wctx
74 nfile = f in wctx
74 nfile = f in wctx
75 exists = lfile or nfile
75 exists = lfile or nfile
76
76
77 # Don't warn the user when they attempt to add a normal tracked file.
77 # Don't warn the user when they attempt to add a normal tracked file.
78 # The normal add code will do that for us.
78 # The normal add code will do that for us.
79 if exact and exists:
79 if exact and exists:
80 if lfile:
80 if lfile:
81 ui.warn(_('%s already a largefile\n') % f)
81 ui.warn(_('%s already a largefile\n') % f)
82 continue
82 continue
83
83
84 if exact or not exists:
84 if exact or not exists:
85 abovemin = (lfsize and
85 abovemin = (lfsize and
86 os.lstat(repo.wjoin(f)).st_size >= lfsize * 1024 * 1024)
86 os.lstat(repo.wjoin(f)).st_size >= lfsize * 1024 * 1024)
87 if large or abovemin or (lfmatcher and lfmatcher(f)):
87 if large or abovemin or (lfmatcher and lfmatcher(f)):
88 lfnames.append(f)
88 lfnames.append(f)
89 if ui.verbose or not exact:
89 if ui.verbose or not exact:
90 ui.status(_('adding %s as a largefile\n') % m.rel(f))
90 ui.status(_('adding %s as a largefile\n') % m.rel(f))
91
91
92 bad = []
92 bad = []
93 standins = []
93 standins = []
94
94
95 # Need to lock, otherwise there could be a race condition between
95 # Need to lock, otherwise there could be a race condition between
96 # when standins are created and added to the repo.
96 # when standins are created and added to the repo.
97 wlock = repo.wlock()
97 wlock = repo.wlock()
98 try:
98 try:
99 if not opts.get('dry_run'):
99 if not opts.get('dry_run'):
100 lfdirstate = lfutil.openlfdirstate(ui, repo)
100 lfdirstate = lfutil.openlfdirstate(ui, repo)
101 for f in lfnames:
101 for f in lfnames:
102 standinname = lfutil.standin(f)
102 standinname = lfutil.standin(f)
103 lfutil.writestandin(repo, standinname, hash='',
103 lfutil.writestandin(repo, standinname, hash='',
104 executable=lfutil.getexecutable(repo.wjoin(f)))
104 executable=lfutil.getexecutable(repo.wjoin(f)))
105 standins.append(standinname)
105 standins.append(standinname)
106 if lfdirstate[f] == 'r':
106 if lfdirstate[f] == 'r':
107 lfdirstate.normallookup(f)
107 lfdirstate.normallookup(f)
108 else:
108 else:
109 lfdirstate.add(f)
109 lfdirstate.add(f)
110 lfdirstate.write()
110 lfdirstate.write()
111 bad += [lfutil.splitstandin(f)
111 bad += [lfutil.splitstandin(f)
112 for f in lfutil.repoadd(repo, standins)
112 for f in lfutil.repoadd(repo, standins)
113 if f in m.files()]
113 if f in m.files()]
114 finally:
114 finally:
115 wlock.release()
115 wlock.release()
116 return bad
116 return bad
117
117
118 def removelargefiles(ui, repo, *pats, **opts):
118 def removelargefiles(ui, repo, *pats, **opts):
119 after = opts.get('after')
119 after = opts.get('after')
120 if not pats and not after:
120 if not pats and not after:
121 raise util.Abort(_('no files specified'))
121 raise util.Abort(_('no files specified'))
122 m = scmutil.match(repo[None], pats, opts)
122 m = scmutil.match(repo[None], pats, opts)
123 try:
123 try:
124 repo.lfstatus = True
124 repo.lfstatus = True
125 s = repo.status(match=m, clean=True)
125 s = repo.status(match=m, clean=True)
126 finally:
126 finally:
127 repo.lfstatus = False
127 repo.lfstatus = False
128 manifest = repo[None].manifest()
128 manifest = repo[None].manifest()
129 modified, added, deleted, clean = [[f for f in list
129 modified, added, deleted, clean = [[f for f in list
130 if lfutil.standin(f) in manifest]
130 if lfutil.standin(f) in manifest]
131 for list in [s[0], s[1], s[3], s[6]]]
131 for list in [s[0], s[1], s[3], s[6]]]
132
132
133 def warn(files, reason):
133 def warn(files, reason):
134 for f in files:
134 for f in files:
135 ui.warn(_('not removing %s: %s (use forget to undo)\n')
135 ui.warn(_('not removing %s: %s (use forget to undo)\n')
136 % (m.rel(f), reason))
136 % (m.rel(f), reason))
137
137
138 if after:
138 if after:
139 remove, forget = deleted, []
139 remove, forget = deleted, []
140 warn(modified + added + clean, _('file still exists'))
140 warn(modified + added + clean, _('file still exists'))
141 else:
141 else:
142 remove, forget = deleted + clean, []
142 remove, forget = deleted + clean, []
143 warn(modified, _('file is modified'))
143 warn(modified, _('file is modified'))
144 warn(added, _('file has been marked for add'))
144 warn(added, _('file has been marked for add'))
145
145
146 for f in sorted(remove + forget):
146 for f in sorted(remove + forget):
147 if ui.verbose or not m.exact(f):
147 if ui.verbose or not m.exact(f):
148 ui.status(_('removing %s\n') % m.rel(f))
148 ui.status(_('removing %s\n') % m.rel(f))
149
149
150 # Need to lock because standin files are deleted then removed from the
150 # Need to lock because standin files are deleted then removed from the
151 # repository and we could race inbetween.
151 # repository and we could race inbetween.
152 wlock = repo.wlock()
152 wlock = repo.wlock()
153 try:
153 try:
154 lfdirstate = lfutil.openlfdirstate(ui, repo)
154 lfdirstate = lfutil.openlfdirstate(ui, repo)
155 for f in remove:
155 for f in remove:
156 if not after:
156 if not after:
157 # If this is being called by addremove, notify the user that we
157 # If this is being called by addremove, notify the user that we
158 # are removing the file.
158 # are removing the file.
159 if getattr(repo, "_isaddremove", False):
159 if getattr(repo, "_isaddremove", False):
160 ui.status(_('removing %s\n') % f)
160 ui.status(_('removing %s\n') % f)
161 if os.path.exists(repo.wjoin(f)):
161 if os.path.exists(repo.wjoin(f)):
162 util.unlinkpath(repo.wjoin(f))
162 util.unlinkpath(repo.wjoin(f))
163 lfdirstate.remove(f)
163 lfdirstate.remove(f)
164 lfdirstate.write()
164 lfdirstate.write()
165 forget = [lfutil.standin(f) for f in forget]
165 forget = [lfutil.standin(f) for f in forget]
166 remove = [lfutil.standin(f) for f in remove]
166 remove = [lfutil.standin(f) for f in remove]
167 lfutil.repoforget(repo, forget)
167 lfutil.repoforget(repo, forget)
168 # If this is being called by addremove, let the original addremove
168 # If this is being called by addremove, let the original addremove
169 # function handle this.
169 # function handle this.
170 if not getattr(repo, "_isaddremove", False):
170 if not getattr(repo, "_isaddremove", False):
171 lfutil.reporemove(repo, remove, unlink=True)
171 lfutil.reporemove(repo, remove, unlink=True)
172 finally:
172 finally:
173 wlock.release()
173 wlock.release()
174
174
175 # -- Wrappers: modify existing commands --------------------------------
175 # -- Wrappers: modify existing commands --------------------------------
176
176
177 # Add works by going through the files that the user wanted to add and
177 # Add works by going through the files that the user wanted to add and
178 # checking if they should be added as largefiles. Then it makes a new
178 # checking if they should be added as largefiles. Then it makes a new
179 # matcher which matches only the normal files and runs the original
179 # matcher which matches only the normal files and runs the original
180 # version of add.
180 # version of add.
181 def overrideadd(orig, ui, repo, *pats, **opts):
181 def overrideadd(orig, ui, repo, *pats, **opts):
182 normal = opts.pop('normal')
182 normal = opts.pop('normal')
183 if normal:
183 if normal:
184 if opts.get('large'):
184 if opts.get('large'):
185 raise util.Abort(_('--normal cannot be used with --large'))
185 raise util.Abort(_('--normal cannot be used with --large'))
186 return orig(ui, repo, *pats, **opts)
186 return orig(ui, repo, *pats, **opts)
187 bad = addlargefiles(ui, repo, *pats, **opts)
187 bad = addlargefiles(ui, repo, *pats, **opts)
188 installnormalfilesmatchfn(repo[None].manifest())
188 installnormalfilesmatchfn(repo[None].manifest())
189 result = orig(ui, repo, *pats, **opts)
189 result = orig(ui, repo, *pats, **opts)
190 restorematchfn()
190 restorematchfn()
191
191
192 return (result == 1 or bad) and 1 or 0
192 return (result == 1 or bad) and 1 or 0
193
193
194 def overrideremove(orig, ui, repo, *pats, **opts):
194 def overrideremove(orig, ui, repo, *pats, **opts):
195 installnormalfilesmatchfn(repo[None].manifest())
195 installnormalfilesmatchfn(repo[None].manifest())
196 orig(ui, repo, *pats, **opts)
196 orig(ui, repo, *pats, **opts)
197 restorematchfn()
197 restorematchfn()
198 removelargefiles(ui, repo, *pats, **opts)
198 removelargefiles(ui, repo, *pats, **opts)
199
199
200 def overridestatus(orig, ui, repo, *pats, **opts):
200 def overridestatus(orig, ui, repo, *pats, **opts):
201 try:
201 try:
202 repo.lfstatus = True
202 repo.lfstatus = True
203 return orig(ui, repo, *pats, **opts)
203 return orig(ui, repo, *pats, **opts)
204 finally:
204 finally:
205 repo.lfstatus = False
205 repo.lfstatus = False
206
206
207 def overridelog(orig, ui, repo, *pats, **opts):
207 def overridelog(orig, ui, repo, *pats, **opts):
208 try:
208 try:
209 repo.lfstatus = True
209 repo.lfstatus = True
210 orig(ui, repo, *pats, **opts)
210 orig(ui, repo, *pats, **opts)
211 finally:
211 finally:
212 repo.lfstatus = False
212 repo.lfstatus = False
213
213
214 def overrideverify(orig, ui, repo, *pats, **opts):
214 def overrideverify(orig, ui, repo, *pats, **opts):
215 large = opts.pop('large', False)
215 large = opts.pop('large', False)
216 all = opts.pop('lfa', False)
216 all = opts.pop('lfa', False)
217 contents = opts.pop('lfc', False)
217 contents = opts.pop('lfc', False)
218
218
219 result = orig(ui, repo, *pats, **opts)
219 result = orig(ui, repo, *pats, **opts)
220 if large:
220 if large:
221 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
221 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
222 return result
222 return result
223
223
224 # Override needs to refresh standins so that update's normal merge
224 # Override needs to refresh standins so that update's normal merge
225 # will go through properly. Then the other update hook (overriding repo.update)
225 # will go through properly. Then the other update hook (overriding repo.update)
226 # will get the new files. Filemerge is also overriden so that the merge
226 # will get the new files. Filemerge is also overriden so that the merge
227 # will merge standins correctly.
227 # will merge standins correctly.
228 def overrideupdate(orig, ui, repo, *pats, **opts):
228 def overrideupdate(orig, ui, repo, *pats, **opts):
229 lfdirstate = lfutil.openlfdirstate(ui, repo)
229 lfdirstate = lfutil.openlfdirstate(ui, repo)
230 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
230 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
231 False, False)
231 False, False)
232 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
232 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
233
233
234 # Need to lock between the standins getting updated and their
234 # Need to lock between the standins getting updated and their
235 # largefiles getting updated
235 # largefiles getting updated
236 wlock = repo.wlock()
236 wlock = repo.wlock()
237 try:
237 try:
238 if opts['check']:
238 if opts['check']:
239 mod = len(modified) > 0
239 mod = len(modified) > 0
240 for lfile in unsure:
240 for lfile in unsure:
241 standin = lfutil.standin(lfile)
241 standin = lfutil.standin(lfile)
242 if repo['.'][standin].data().strip() != \
242 if repo['.'][standin].data().strip() != \
243 lfutil.hashfile(repo.wjoin(lfile)):
243 lfutil.hashfile(repo.wjoin(lfile)):
244 mod = True
244 mod = True
245 else:
245 else:
246 lfdirstate.normal(lfile)
246 lfdirstate.normal(lfile)
247 lfdirstate.write()
247 lfdirstate.write()
248 if mod:
248 if mod:
249 raise util.Abort(_('uncommitted local changes'))
249 raise util.Abort(_('uncommitted local changes'))
250 # XXX handle removed differently
250 # XXX handle removed differently
251 if not opts['clean']:
251 if not opts['clean']:
252 for lfile in unsure + modified + added:
252 for lfile in unsure + modified + added:
253 lfutil.updatestandin(repo, lfutil.standin(lfile))
253 lfutil.updatestandin(repo, lfutil.standin(lfile))
254 finally:
254 finally:
255 wlock.release()
255 wlock.release()
256 return orig(ui, repo, *pats, **opts)
256 return orig(ui, repo, *pats, **opts)
257
257
258 # Before starting the manifest merge, merge.updates will call
258 # Before starting the manifest merge, merge.updates will call
259 # _checkunknown to check if there are any files in the merged-in
259 # _checkunknown to check if there are any files in the merged-in
260 # changeset that collide with unknown files in the working copy.
260 # changeset that collide with unknown files in the working copy.
261 #
261 #
262 # The largefiles are seen as unknown, so this prevents us from merging
262 # The largefiles are seen as unknown, so this prevents us from merging
263 # in a file 'foo' if we already have a largefile with the same name.
263 # in a file 'foo' if we already have a largefile with the same name.
264 #
264 #
265 # The overridden function filters the unknown files by removing any
265 # The overridden function filters the unknown files by removing any
266 # largefiles. This makes the merge proceed and we can then handle this
266 # largefiles. This makes the merge proceed and we can then handle this
267 # case further in the overridden manifestmerge function below.
267 # case further in the overridden manifestmerge function below.
268 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
268 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
269 if lfutil.standin(f) in wctx:
269 if lfutil.standin(f) in wctx:
270 return False
270 return False
271 return origfn(repo, wctx, mctx, f)
271 return origfn(repo, wctx, mctx, f)
272
272
273 # The manifest merge handles conflicts on the manifest level. We want
273 # The manifest merge handles conflicts on the manifest level. We want
274 # to handle changes in largefile-ness of files at this level too.
274 # to handle changes in largefile-ness of files at this level too.
275 #
275 #
276 # The strategy is to run the original manifestmerge and then process
276 # The strategy is to run the original manifestmerge and then process
277 # the action list it outputs. There are two cases we need to deal with:
277 # the action list it outputs. There are two cases we need to deal with:
278 #
278 #
279 # 1. Normal file in p1, largefile in p2. Here the largefile is
279 # 1. Normal file in p1, largefile in p2. Here the largefile is
280 # detected via its standin file, which will enter the working copy
280 # detected via its standin file, which will enter the working copy
281 # with a "get" action. It is not "merge" since the standin is all
281 # with a "get" action. It is not "merge" since the standin is all
282 # Mercurial is concerned with at this level -- the link to the
282 # Mercurial is concerned with at this level -- the link to the
283 # existing normal file is not relevant here.
283 # existing normal file is not relevant here.
284 #
284 #
285 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
285 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
286 # since the largefile will be present in the working copy and
286 # since the largefile will be present in the working copy and
287 # different from the normal file in p2. Mercurial therefore
287 # different from the normal file in p2. Mercurial therefore
288 # triggers a merge action.
288 # triggers a merge action.
289 #
289 #
290 # In both cases, we prompt the user and emit new actions to either
290 # In both cases, we prompt the user and emit new actions to either
291 # remove the standin (if the normal file was kept) or to remove the
291 # remove the standin (if the normal file was kept) or to remove the
292 # normal file and get the standin (if the largefile was kept). The
292 # normal file and get the standin (if the largefile was kept). The
293 # default prompt answer is to use the largefile version since it was
293 # default prompt answer is to use the largefile version since it was
294 # presumably changed on purpose.
294 # presumably changed on purpose.
295 #
295 #
296 # Finally, the merge.applyupdates function will then take care of
296 # Finally, the merge.applyupdates function will then take care of
297 # writing the files into the working copy and lfcommands.updatelfiles
297 # writing the files into the working copy and lfcommands.updatelfiles
298 # will update the largefiles.
298 # will update the largefiles.
299 def overridemanifestmerge(origfn, repo, p1, p2, pa, overwrite, partial):
299 def overridemanifestmerge(origfn, repo, p1, p2, pa, overwrite, partial):
300 actions = origfn(repo, p1, p2, pa, overwrite, partial)
300 actions = origfn(repo, p1, p2, pa, overwrite, partial)
301 processed = []
301 processed = []
302
302
303 for action in actions:
303 for action in actions:
304 if overwrite:
304 if overwrite:
305 processed.append(action)
305 processed.append(action)
306 continue
306 continue
307 f, m = action[:2]
307 f, m = action[:2]
308
308
309 choices = (_('&Largefile'), _('&Normal file'))
309 choices = (_('&Largefile'), _('&Normal file'))
310 if m == "g" and lfutil.splitstandin(f) in p1 and f in p2:
310 if m == "g" and lfutil.splitstandin(f) in p1 and f in p2:
311 # Case 1: normal file in the working copy, largefile in
311 # Case 1: normal file in the working copy, largefile in
312 # the second parent
312 # the second parent
313 lfile = lfutil.splitstandin(f)
313 lfile = lfutil.splitstandin(f)
314 standin = f
314 standin = f
315 msg = _('%s has been turned into a largefile\n'
315 msg = _('%s has been turned into a largefile\n'
316 'use (l)argefile or keep as (n)ormal file?') % lfile
316 'use (l)argefile or keep as (n)ormal file?') % lfile
317 if repo.ui.promptchoice(msg, choices, 0) == 0:
317 if repo.ui.promptchoice(msg, choices, 0) == 0:
318 processed.append((lfile, "r"))
318 processed.append((lfile, "r"))
319 processed.append((standin, "g", p2.flags(standin)))
319 processed.append((standin, "g", p2.flags(standin)))
320 else:
320 else:
321 processed.append((standin, "r"))
321 processed.append((standin, "r"))
322 elif m == "g" and lfutil.standin(f) in p1 and f in p2:
322 elif m == "g" and lfutil.standin(f) in p1 and f in p2:
323 # Case 2: largefile in the working copy, normal file in
323 # Case 2: largefile in the working copy, normal file in
324 # the second parent
324 # the second parent
325 standin = lfutil.standin(f)
325 standin = lfutil.standin(f)
326 lfile = f
326 lfile = f
327 msg = _('%s has been turned into a normal file\n'
327 msg = _('%s has been turned into a normal file\n'
328 'keep as (l)argefile or use (n)ormal file?') % lfile
328 'keep as (l)argefile or use (n)ormal file?') % lfile
329 if repo.ui.promptchoice(msg, choices, 0) == 0:
329 if repo.ui.promptchoice(msg, choices, 0) == 0:
330 processed.append((lfile, "r"))
330 processed.append((lfile, "r"))
331 else:
331 else:
332 processed.append((standin, "r"))
332 processed.append((standin, "r"))
333 processed.append((lfile, "g", p2.flags(lfile)))
333 processed.append((lfile, "g", p2.flags(lfile)))
334 else:
334 else:
335 processed.append(action)
335 processed.append(action)
336
336
337 return processed
337 return processed
338
338
339 # Override filemerge to prompt the user about how they wish to merge
339 # Override filemerge to prompt the user about how they wish to merge
340 # largefiles. This will handle identical edits, and copy/rename +
340 # largefiles. This will handle identical edits, and copy/rename +
341 # edit without prompting the user.
341 # edit without prompting the user.
342 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca):
342 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca):
343 # Use better variable names here. Because this is a wrapper we cannot
343 # Use better variable names here. Because this is a wrapper we cannot
344 # change the variable names in the function declaration.
344 # change the variable names in the function declaration.
345 fcdest, fcother, fcancestor = fcd, fco, fca
345 fcdest, fcother, fcancestor = fcd, fco, fca
346 if not lfutil.isstandin(orig):
346 if not lfutil.isstandin(orig):
347 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
347 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
348 else:
348 else:
349 if not fcother.cmp(fcdest): # files identical?
349 if not fcother.cmp(fcdest): # files identical?
350 return None
350 return None
351
351
352 # backwards, use working dir parent as ancestor
352 # backwards, use working dir parent as ancestor
353 if fcancestor == fcother:
353 if fcancestor == fcother:
354 fcancestor = fcdest.parents()[0]
354 fcancestor = fcdest.parents()[0]
355
355
356 if orig != fcother.path():
356 if orig != fcother.path():
357 repo.ui.status(_('merging %s and %s to %s\n')
357 repo.ui.status(_('merging %s and %s to %s\n')
358 % (lfutil.splitstandin(orig),
358 % (lfutil.splitstandin(orig),
359 lfutil.splitstandin(fcother.path()),
359 lfutil.splitstandin(fcother.path()),
360 lfutil.splitstandin(fcdest.path())))
360 lfutil.splitstandin(fcdest.path())))
361 else:
361 else:
362 repo.ui.status(_('merging %s\n')
362 repo.ui.status(_('merging %s\n')
363 % lfutil.splitstandin(fcdest.path()))
363 % lfutil.splitstandin(fcdest.path()))
364
364
365 if fcancestor.path() != fcother.path() and fcother.data() == \
365 if fcancestor.path() != fcother.path() and fcother.data() == \
366 fcancestor.data():
366 fcancestor.data():
367 return 0
367 return 0
368 if fcancestor.path() != fcdest.path() and fcdest.data() == \
368 if fcancestor.path() != fcdest.path() and fcdest.data() == \
369 fcancestor.data():
369 fcancestor.data():
370 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
370 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
371 return 0
371 return 0
372
372
373 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
373 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
374 'keep (l)ocal or take (o)ther?') %
374 'keep (l)ocal or take (o)ther?') %
375 lfutil.splitstandin(orig),
375 lfutil.splitstandin(orig),
376 (_('&Local'), _('&Other')), 0) == 0:
376 (_('&Local'), _('&Other')), 0) == 0:
377 return 0
377 return 0
378 else:
378 else:
379 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
379 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
380 return 0
380 return 0
381
381
382 # Copy first changes the matchers to match standins instead of
382 # Copy first changes the matchers to match standins instead of
383 # largefiles. Then it overrides util.copyfile in that function it
383 # largefiles. Then it overrides util.copyfile in that function it
384 # checks if the destination largefile already exists. It also keeps a
384 # checks if the destination largefile already exists. It also keeps a
385 # list of copied files so that the largefiles can be copied and the
385 # list of copied files so that the largefiles can be copied and the
386 # dirstate updated.
386 # dirstate updated.
387 def overridecopy(orig, ui, repo, pats, opts, rename=False):
387 def overridecopy(orig, ui, repo, pats, opts, rename=False):
388 # doesn't remove largefile on rename
388 # doesn't remove largefile on rename
389 if len(pats) < 2:
389 if len(pats) < 2:
390 # this isn't legal, let the original function deal with it
390 # this isn't legal, let the original function deal with it
391 return orig(ui, repo, pats, opts, rename)
391 return orig(ui, repo, pats, opts, rename)
392
392
393 def makestandin(relpath):
393 def makestandin(relpath):
394 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
394 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
395 return os.path.join(repo.wjoin(lfutil.standin(path)))
395 return os.path.join(repo.wjoin(lfutil.standin(path)))
396
396
397 fullpats = scmutil.expandpats(pats)
397 fullpats = scmutil.expandpats(pats)
398 dest = fullpats[-1]
398 dest = fullpats[-1]
399
399
400 if os.path.isdir(dest):
400 if os.path.isdir(dest):
401 if not os.path.isdir(makestandin(dest)):
401 if not os.path.isdir(makestandin(dest)):
402 os.makedirs(makestandin(dest))
402 os.makedirs(makestandin(dest))
403 # This could copy both lfiles and normal files in one command,
403 # This could copy both lfiles and normal files in one command,
404 # but we don't want to do that. First replace their matcher to
404 # but we don't want to do that. First replace their matcher to
405 # only match normal files and run it, then replace it to just
405 # only match normal files and run it, then replace it to just
406 # match largefiles and run it again.
406 # match largefiles and run it again.
407 nonormalfiles = False
407 nonormalfiles = False
408 nolfiles = False
408 nolfiles = False
409 try:
409 try:
410 try:
410 try:
411 installnormalfilesmatchfn(repo[None].manifest())
411 installnormalfilesmatchfn(repo[None].manifest())
412 result = orig(ui, repo, pats, opts, rename)
412 result = orig(ui, repo, pats, opts, rename)
413 except util.Abort, e:
413 except util.Abort, e:
414 if str(e) != 'no files to copy':
414 if str(e) != 'no files to copy':
415 raise e
415 raise e
416 else:
416 else:
417 nonormalfiles = True
417 nonormalfiles = True
418 result = 0
418 result = 0
419 finally:
419 finally:
420 restorematchfn()
420 restorematchfn()
421
421
422 # The first rename can cause our current working directory to be removed.
422 # The first rename can cause our current working directory to be removed.
423 # In that case there is nothing left to copy/rename so just quit.
423 # In that case there is nothing left to copy/rename so just quit.
424 try:
424 try:
425 repo.getcwd()
425 repo.getcwd()
426 except OSError:
426 except OSError:
427 return result
427 return result
428
428
429 try:
429 try:
430 try:
430 try:
431 # When we call orig below it creates the standins but we don't add
431 # When we call orig below it creates the standins but we don't add
432 # them to the dir state until later so lock during that time.
432 # them to the dir state until later so lock during that time.
433 wlock = repo.wlock()
433 wlock = repo.wlock()
434
434
435 manifest = repo[None].manifest()
435 manifest = repo[None].manifest()
436 oldmatch = None # for the closure
436 oldmatch = None # for the closure
437 def overridematch(ctx, pats=[], opts={}, globbed=False,
437 def overridematch(ctx, pats=[], opts={}, globbed=False,
438 default='relpath'):
438 default='relpath'):
439 newpats = []
439 newpats = []
440 # The patterns were previously mangled to add the standin
440 # The patterns were previously mangled to add the standin
441 # directory; we need to remove that now
441 # directory; we need to remove that now
442 for pat in pats:
442 for pat in pats:
443 if match_.patkind(pat) is None and lfutil.shortname in pat:
443 if match_.patkind(pat) is None and lfutil.shortname in pat:
444 newpats.append(pat.replace(lfutil.shortname, ''))
444 newpats.append(pat.replace(lfutil.shortname, ''))
445 else:
445 else:
446 newpats.append(pat)
446 newpats.append(pat)
447 match = oldmatch(ctx, newpats, opts, globbed, default)
447 match = oldmatch(ctx, newpats, opts, globbed, default)
448 m = copy.copy(match)
448 m = copy.copy(match)
449 lfile = lambda f: lfutil.standin(f) in manifest
449 lfile = lambda f: lfutil.standin(f) in manifest
450 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
450 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
451 m._fmap = set(m._files)
451 m._fmap = set(m._files)
452 origmatchfn = m.matchfn
452 origmatchfn = m.matchfn
453 m.matchfn = lambda f: (lfutil.isstandin(f) and
453 m.matchfn = lambda f: (lfutil.isstandin(f) and
454 (f in manifest) and
454 (f in manifest) and
455 origmatchfn(lfutil.splitstandin(f)) or
455 origmatchfn(lfutil.splitstandin(f)) or
456 None)
456 None)
457 return m
457 return m
458 oldmatch = installmatchfn(overridematch)
458 oldmatch = installmatchfn(overridematch)
459 listpats = []
459 listpats = []
460 for pat in pats:
460 for pat in pats:
461 if match_.patkind(pat) is not None:
461 if match_.patkind(pat) is not None:
462 listpats.append(pat)
462 listpats.append(pat)
463 else:
463 else:
464 listpats.append(makestandin(pat))
464 listpats.append(makestandin(pat))
465
465
466 try:
466 try:
467 origcopyfile = util.copyfile
467 origcopyfile = util.copyfile
468 copiedfiles = []
468 copiedfiles = []
469 def overridecopyfile(src, dest):
469 def overridecopyfile(src, dest):
470 if (lfutil.shortname in src and
470 if (lfutil.shortname in src and
471 dest.startswith(repo.wjoin(lfutil.shortname))):
471 dest.startswith(repo.wjoin(lfutil.shortname))):
472 destlfile = dest.replace(lfutil.shortname, '')
472 destlfile = dest.replace(lfutil.shortname, '')
473 if not opts['force'] and os.path.exists(destlfile):
473 if not opts['force'] and os.path.exists(destlfile):
474 raise IOError('',
474 raise IOError('',
475 _('destination largefile already exists'))
475 _('destination largefile already exists'))
476 copiedfiles.append((src, dest))
476 copiedfiles.append((src, dest))
477 origcopyfile(src, dest)
477 origcopyfile(src, dest)
478
478
479 util.copyfile = overridecopyfile
479 util.copyfile = overridecopyfile
480 result += orig(ui, repo, listpats, opts, rename)
480 result += orig(ui, repo, listpats, opts, rename)
481 finally:
481 finally:
482 util.copyfile = origcopyfile
482 util.copyfile = origcopyfile
483
483
484 lfdirstate = lfutil.openlfdirstate(ui, repo)
484 lfdirstate = lfutil.openlfdirstate(ui, repo)
485 for (src, dest) in copiedfiles:
485 for (src, dest) in copiedfiles:
486 if (lfutil.shortname in src and
486 if (lfutil.shortname in src and
487 dest.startswith(repo.wjoin(lfutil.shortname))):
487 dest.startswith(repo.wjoin(lfutil.shortname))):
488 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
488 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
489 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
489 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
490 destlfiledir = os.path.dirname(destlfile) or '.'
490 destlfiledir = os.path.dirname(destlfile) or '.'
491 if not os.path.isdir(destlfiledir):
491 if not os.path.isdir(destlfiledir):
492 os.makedirs(destlfiledir)
492 os.makedirs(destlfiledir)
493 if rename:
493 if rename:
494 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
494 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
495 lfdirstate.remove(srclfile)
495 lfdirstate.remove(srclfile)
496 else:
496 else:
497 util.copyfile(srclfile, destlfile)
497 util.copyfile(srclfile, destlfile)
498 lfdirstate.add(destlfile)
498 lfdirstate.add(destlfile)
499 lfdirstate.write()
499 lfdirstate.write()
500 except util.Abort, e:
500 except util.Abort, e:
501 if str(e) != 'no files to copy':
501 if str(e) != 'no files to copy':
502 raise e
502 raise e
503 else:
503 else:
504 nolfiles = True
504 nolfiles = True
505 finally:
505 finally:
506 restorematchfn()
506 restorematchfn()
507 wlock.release()
507 wlock.release()
508
508
509 if nolfiles and nonormalfiles:
509 if nolfiles and nonormalfiles:
510 raise util.Abort(_('no files to copy'))
510 raise util.Abort(_('no files to copy'))
511
511
512 return result
512 return result
513
513
514 # When the user calls revert, we have to be careful to not revert any
514 # When the user calls revert, we have to be careful to not revert any
515 # changes to other largefiles accidentally. This means we have to keep
515 # changes to other largefiles accidentally. This means we have to keep
516 # track of the largefiles that are being reverted so we only pull down
516 # track of the largefiles that are being reverted so we only pull down
517 # the necessary largefiles.
517 # the necessary largefiles.
518 #
518 #
519 # Standins are only updated (to match the hash of largefiles) before
519 # Standins are only updated (to match the hash of largefiles) before
520 # commits. Update the standins then run the original revert, changing
520 # commits. Update the standins then run the original revert, changing
521 # the matcher to hit standins instead of largefiles. Based on the
521 # the matcher to hit standins instead of largefiles. Based on the
522 # resulting standins update the largefiles. Then return the standins
522 # resulting standins update the largefiles. Then return the standins
523 # to their proper state
523 # to their proper state
524 def overriderevert(orig, ui, repo, *pats, **opts):
524 def overriderevert(orig, ui, repo, *pats, **opts):
525 # Because we put the standins in a bad state (by updating them)
525 # Because we put the standins in a bad state (by updating them)
526 # and then return them to a correct state we need to lock to
526 # and then return them to a correct state we need to lock to
527 # prevent others from changing them in their incorrect state.
527 # prevent others from changing them in their incorrect state.
528 wlock = repo.wlock()
528 wlock = repo.wlock()
529 try:
529 try:
530 lfdirstate = lfutil.openlfdirstate(ui, repo)
530 lfdirstate = lfutil.openlfdirstate(ui, repo)
531 (modified, added, removed, missing, unknown, ignored, clean) = \
531 (modified, added, removed, missing, unknown, ignored, clean) = \
532 lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
532 lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
533 for lfile in modified:
533 for lfile in modified:
534 lfutil.updatestandin(repo, lfutil.standin(lfile))
534 lfutil.updatestandin(repo, lfutil.standin(lfile))
535 for lfile in missing:
535 for lfile in missing:
536 os.unlink(repo.wjoin(lfutil.standin(lfile)))
536 os.unlink(repo.wjoin(lfutil.standin(lfile)))
537
537
538 try:
538 try:
539 ctx = repo[opts.get('rev')]
539 ctx = repo[opts.get('rev')]
540 oldmatch = None # for the closure
540 oldmatch = None # for the closure
541 def overridematch(ctx, pats=[], opts={}, globbed=False,
541 def overridematch(ctx, pats=[], opts={}, globbed=False,
542 default='relpath'):
542 default='relpath'):
543 match = oldmatch(ctx, pats, opts, globbed, default)
543 match = oldmatch(ctx, pats, opts, globbed, default)
544 m = copy.copy(match)
544 m = copy.copy(match)
545 def tostandin(f):
545 def tostandin(f):
546 if lfutil.standin(f) in ctx:
546 if lfutil.standin(f) in ctx:
547 return lfutil.standin(f)
547 return lfutil.standin(f)
548 elif lfutil.standin(f) in repo[None]:
548 elif lfutil.standin(f) in repo[None]:
549 return None
549 return None
550 return f
550 return f
551 m._files = [tostandin(f) for f in m._files]
551 m._files = [tostandin(f) for f in m._files]
552 m._files = [f for f in m._files if f is not None]
552 m._files = [f for f in m._files if f is not None]
553 m._fmap = set(m._files)
553 m._fmap = set(m._files)
554 origmatchfn = m.matchfn
554 origmatchfn = m.matchfn
555 def matchfn(f):
555 def matchfn(f):
556 if lfutil.isstandin(f):
556 if lfutil.isstandin(f):
557 # We need to keep track of what largefiles are being
557 # We need to keep track of what largefiles are being
558 # matched so we know which ones to update later --
558 # matched so we know which ones to update later --
559 # otherwise we accidentally revert changes to other
559 # otherwise we accidentally revert changes to other
560 # largefiles. This is repo-specific, so duckpunch the
560 # largefiles. This is repo-specific, so duckpunch the
561 # repo object to keep the list of largefiles for us
561 # repo object to keep the list of largefiles for us
562 # later.
562 # later.
563 if origmatchfn(lfutil.splitstandin(f)) and \
563 if origmatchfn(lfutil.splitstandin(f)) and \
564 (f in repo[None] or f in ctx):
564 (f in repo[None] or f in ctx):
565 lfileslist = getattr(repo, '_lfilestoupdate', [])
565 lfileslist = getattr(repo, '_lfilestoupdate', [])
566 lfileslist.append(lfutil.splitstandin(f))
566 lfileslist.append(lfutil.splitstandin(f))
567 repo._lfilestoupdate = lfileslist
567 repo._lfilestoupdate = lfileslist
568 return True
568 return True
569 else:
569 else:
570 return False
570 return False
571 return origmatchfn(f)
571 return origmatchfn(f)
572 m.matchfn = matchfn
572 m.matchfn = matchfn
573 return m
573 return m
574 oldmatch = installmatchfn(overridematch)
574 oldmatch = installmatchfn(overridematch)
575 scmutil.match
575 scmutil.match
576 matches = overridematch(repo[None], pats, opts)
576 matches = overridematch(repo[None], pats, opts)
577 orig(ui, repo, *pats, **opts)
577 orig(ui, repo, *pats, **opts)
578 finally:
578 finally:
579 restorematchfn()
579 restorematchfn()
580 lfileslist = getattr(repo, '_lfilestoupdate', [])
580 lfileslist = getattr(repo, '_lfilestoupdate', [])
581 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
581 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
582 printmessage=False)
582 printmessage=False)
583
583
584 # empty out the largefiles list so we start fresh next time
584 # empty out the largefiles list so we start fresh next time
585 repo._lfilestoupdate = []
585 repo._lfilestoupdate = []
586 for lfile in modified:
586 for lfile in modified:
587 if lfile in lfileslist:
587 if lfile in lfileslist:
588 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
588 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
589 in repo['.']:
589 in repo['.']:
590 lfutil.writestandin(repo, lfutil.standin(lfile),
590 lfutil.writestandin(repo, lfutil.standin(lfile),
591 repo['.'][lfile].data().strip(),
591 repo['.'][lfile].data().strip(),
592 'x' in repo['.'][lfile].flags())
592 'x' in repo['.'][lfile].flags())
593 lfdirstate = lfutil.openlfdirstate(ui, repo)
593 lfdirstate = lfutil.openlfdirstate(ui, repo)
594 for lfile in added:
594 for lfile in added:
595 standin = lfutil.standin(lfile)
595 standin = lfutil.standin(lfile)
596 if standin not in ctx and (standin in matches or opts.get('all')):
596 if standin not in ctx and (standin in matches or opts.get('all')):
597 if lfile in lfdirstate:
597 if lfile in lfdirstate:
598 lfdirstate.drop(lfile)
598 lfdirstate.drop(lfile)
599 util.unlinkpath(repo.wjoin(standin))
599 util.unlinkpath(repo.wjoin(standin))
600 lfdirstate.write()
600 lfdirstate.write()
601 finally:
601 finally:
602 wlock.release()
602 wlock.release()
603
603
604 def hgupdate(orig, repo, node):
604 def hgupdate(orig, repo, node):
605 # Only call updatelfiles the standins that have changed to save time
605 # Only call updatelfiles the standins that have changed to save time
606 oldstandins = lfutil.getstandinsstate(repo)
606 oldstandins = lfutil.getstandinsstate(repo)
607 result = orig(repo, node)
607 result = orig(repo, node)
608 newstandins = lfutil.getstandinsstate(repo)
608 newstandins = lfutil.getstandinsstate(repo)
609 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
609 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
610 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist, printmessage=True)
610 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist, printmessage=True)
611 return result
611 return result
612
612
613 def hgclean(orig, repo, node, show_stats=True):
613 def hgclean(orig, repo, node, show_stats=True):
614 result = orig(repo, node, show_stats)
614 result = orig(repo, node, show_stats)
615 lfcommands.updatelfiles(repo.ui, repo)
615 lfcommands.updatelfiles(repo.ui, repo)
616 return result
616 return result
617
617
618 def hgmerge(orig, repo, node, force=None, remind=True):
618 def hgmerge(orig, repo, node, force=None, remind=True):
619 # Mark the repo as being in the middle of a merge, so that
619 # Mark the repo as being in the middle of a merge, so that
620 # updatelfiles() will know that it needs to trust the standins in
620 # updatelfiles() will know that it needs to trust the standins in
621 # the working copy, not in the standins in the current node
621 # the working copy, not in the standins in the current node
622 repo._ismerging = True
622 repo._ismerging = True
623 try:
623 try:
624 result = orig(repo, node, force, remind)
624 result = orig(repo, node, force, remind)
625 lfcommands.updatelfiles(repo.ui, repo)
625 lfcommands.updatelfiles(repo.ui, repo)
626 finally:
626 finally:
627 repo._ismerging = False
627 repo._ismerging = False
628 return result
628 return result
629
629
630 # When we rebase a repository with remotely changed largefiles, we need to
630 # When we rebase a repository with remotely changed largefiles, we need to
631 # take some extra care so that the largefiles are correctly updated in the
631 # take some extra care so that the largefiles are correctly updated in the
632 # working copy
632 # working copy
633 def overridepull(orig, ui, repo, source=None, **opts):
633 def overridepull(orig, ui, repo, source=None, **opts):
634 if opts.get('rebase', False):
634 if opts.get('rebase', False):
635 repo._isrebasing = True
635 repo._isrebasing = True
636 try:
636 try:
637 if opts.get('update'):
637 if opts.get('update'):
638 del opts['update']
638 del opts['update']
639 ui.debug('--update and --rebase are not compatible, ignoring '
639 ui.debug('--update and --rebase are not compatible, ignoring '
640 'the update flag\n')
640 'the update flag\n')
641 del opts['rebase']
641 del opts['rebase']
642 cmdutil.bailifchanged(repo)
642 cmdutil.bailifchanged(repo)
643 revsprepull = len(repo)
643 revsprepull = len(repo)
644 origpostincoming = commands.postincoming
644 origpostincoming = commands.postincoming
645 def _dummy(*args, **kwargs):
645 def _dummy(*args, **kwargs):
646 pass
646 pass
647 commands.postincoming = _dummy
647 commands.postincoming = _dummy
648 repo.lfpullsource = source
648 repo.lfpullsource = source
649 if not source:
649 if not source:
650 source = 'default'
650 source = 'default'
651 try:
651 try:
652 result = commands.pull(ui, repo, source, **opts)
652 result = commands.pull(ui, repo, source, **opts)
653 finally:
653 finally:
654 commands.postincoming = origpostincoming
654 commands.postincoming = origpostincoming
655 revspostpull = len(repo)
655 revspostpull = len(repo)
656 if revspostpull > revsprepull:
656 if revspostpull > revsprepull:
657 result = result or rebase.rebase(ui, repo)
657 result = result or rebase.rebase(ui, repo)
658 finally:
658 finally:
659 repo._isrebasing = False
659 repo._isrebasing = False
660 else:
660 else:
661 repo.lfpullsource = source
661 repo.lfpullsource = source
662 if not source:
662 if not source:
663 source = 'default'
663 source = 'default'
664 oldheads = lfutil.getcurrentheads(repo)
664 oldheads = lfutil.getcurrentheads(repo)
665 result = orig(ui, repo, source, **opts)
665 result = orig(ui, repo, source, **opts)
666 # If we do not have the new largefiles for any new heads we pulled, we
666 # If we do not have the new largefiles for any new heads we pulled, we
667 # will run into a problem later if we try to merge or rebase with one of
667 # will run into a problem later if we try to merge or rebase with one of
668 # these heads, so cache the largefiles now direclty into the system
668 # these heads, so cache the largefiles now direclty into the system
669 # cache.
669 # cache.
670 ui.status(_("caching new largefiles\n"))
670 ui.status(_("caching new largefiles\n"))
671 numcached = 0
671 numcached = 0
672 heads = lfutil.getcurrentheads(repo)
672 heads = lfutil.getcurrentheads(repo)
673 newheads = set(heads).difference(set(oldheads))
673 newheads = set(heads).difference(set(oldheads))
674 for head in newheads:
674 for head in newheads:
675 (cached, missing) = lfcommands.cachelfiles(ui, repo, head)
675 (cached, missing) = lfcommands.cachelfiles(ui, repo, head)
676 numcached += len(cached)
676 numcached += len(cached)
677 ui.status(_("%d largefiles cached\n") % numcached)
677 ui.status(_("%d largefiles cached\n") % numcached)
678 return result
678 return result
679
679
680 def overriderebase(orig, ui, repo, **opts):
680 def overriderebase(orig, ui, repo, **opts):
681 repo._isrebasing = True
681 repo._isrebasing = True
682 try:
682 try:
683 orig(ui, repo, **opts)
683 orig(ui, repo, **opts)
684 finally:
684 finally:
685 repo._isrebasing = False
685 repo._isrebasing = False
686
686
687 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
687 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
688 prefix=None, mtime=None, subrepos=None):
688 prefix=None, mtime=None, subrepos=None):
689 # No need to lock because we are only reading history and
689 # No need to lock because we are only reading history and
690 # largefile caches, neither of which are modified.
690 # largefile caches, neither of which are modified.
691 lfcommands.cachelfiles(repo.ui, repo, node)
691 lfcommands.cachelfiles(repo.ui, repo, node)
692
692
693 if kind not in archival.archivers:
693 if kind not in archival.archivers:
694 raise util.Abort(_("unknown archive type '%s'") % kind)
694 raise util.Abort(_("unknown archive type '%s'") % kind)
695
695
696 ctx = repo[node]
696 ctx = repo[node]
697
697
698 if kind == 'files':
698 if kind == 'files':
699 if prefix:
699 if prefix:
700 raise util.Abort(
700 raise util.Abort(
701 _('cannot give prefix when archiving to files'))
701 _('cannot give prefix when archiving to files'))
702 else:
702 else:
703 prefix = archival.tidyprefix(dest, kind, prefix)
703 prefix = archival.tidyprefix(dest, kind, prefix)
704
704
705 def write(name, mode, islink, getdata):
705 def write(name, mode, islink, getdata):
706 if matchfn and not matchfn(name):
706 if matchfn and not matchfn(name):
707 return
707 return
708 data = getdata()
708 data = getdata()
709 if decode:
709 if decode:
710 data = repo.wwritedata(name, data)
710 data = repo.wwritedata(name, data)
711 archiver.addfile(prefix + name, mode, islink, data)
711 archiver.addfile(prefix + name, mode, islink, data)
712
712
713 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
713 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
714
714
715 if repo.ui.configbool("ui", "archivemeta", True):
715 if repo.ui.configbool("ui", "archivemeta", True):
716 def metadata():
716 def metadata():
717 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
717 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
718 hex(repo.changelog.node(0)), hex(node), ctx.branch())
718 hex(repo.changelog.node(0)), hex(node), ctx.branch())
719
719
720 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
720 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
721 if repo.tagtype(t) == 'global')
721 if repo.tagtype(t) == 'global')
722 if not tags:
722 if not tags:
723 repo.ui.pushbuffer()
723 repo.ui.pushbuffer()
724 opts = {'template': '{latesttag}\n{latesttagdistance}',
724 opts = {'template': '{latesttag}\n{latesttagdistance}',
725 'style': '', 'patch': None, 'git': None}
725 'style': '', 'patch': None, 'git': None}
726 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
726 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
727 ltags, dist = repo.ui.popbuffer().split('\n')
727 ltags, dist = repo.ui.popbuffer().split('\n')
728 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
728 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
729 tags += 'latesttagdistance: %s\n' % dist
729 tags += 'latesttagdistance: %s\n' % dist
730
730
731 return base + tags
731 return base + tags
732
732
733 write('.hg_archival.txt', 0644, False, metadata)
733 write('.hg_archival.txt', 0644, False, metadata)
734
734
735 for f in ctx:
735 for f in ctx:
736 ff = ctx.flags(f)
736 ff = ctx.flags(f)
737 getdata = ctx[f].data
737 getdata = ctx[f].data
738 if lfutil.isstandin(f):
738 if lfutil.isstandin(f):
739 path = lfutil.findfile(repo, getdata().strip())
739 path = lfutil.findfile(repo, getdata().strip())
740 if path is None:
740 if path is None:
741 raise util.Abort(
741 raise util.Abort(
742 _('largefile %s not found in repo store or system cache')
742 _('largefile %s not found in repo store or system cache')
743 % lfutil.splitstandin(f))
743 % lfutil.splitstandin(f))
744 f = lfutil.splitstandin(f)
744 f = lfutil.splitstandin(f)
745
745
746 def getdatafn():
746 def getdatafn():
747 fd = None
747 fd = None
748 try:
748 try:
749 fd = open(path, 'rb')
749 fd = open(path, 'rb')
750 return fd.read()
750 return fd.read()
751 finally:
751 finally:
752 if fd:
752 if fd:
753 fd.close()
753 fd.close()
754
754
755 getdata = getdatafn
755 getdata = getdatafn
756 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
756 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
757
757
758 if subrepos:
758 if subrepos:
759 for subpath in ctx.substate:
759 for subpath in ctx.substate:
760 sub = ctx.sub(subpath)
760 sub = ctx.sub(subpath)
761 sub.archive(repo.ui, archiver, prefix)
761 sub.archive(repo.ui, archiver, prefix)
762
762
763 archiver.done()
763 archiver.done()
764
764
765 # If a largefile is modified, the change is not reflected in its
765 # If a largefile is modified, the change is not reflected in its
766 # standin until a commit. cmdutil.bailifchanged() raises an exception
766 # standin until a commit. cmdutil.bailifchanged() raises an exception
767 # if the repo has uncommitted changes. Wrap it to also check if
767 # if the repo has uncommitted changes. Wrap it to also check if
768 # largefiles were changed. This is used by bisect and backout.
768 # largefiles were changed. This is used by bisect and backout.
769 def overridebailifchanged(orig, repo):
769 def overridebailifchanged(orig, repo):
770 orig(repo)
770 orig(repo)
771 repo.lfstatus = True
771 repo.lfstatus = True
772 modified, added, removed, deleted = repo.status()[:4]
772 modified, added, removed, deleted = repo.status()[:4]
773 repo.lfstatus = False
773 repo.lfstatus = False
774 if modified or added or removed or deleted:
774 if modified or added or removed or deleted:
775 raise util.Abort(_('outstanding uncommitted changes'))
775 raise util.Abort(_('outstanding uncommitted changes'))
776
776
777 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
777 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
778 def overridefetch(orig, ui, repo, *pats, **opts):
778 def overridefetch(orig, ui, repo, *pats, **opts):
779 repo.lfstatus = True
779 repo.lfstatus = True
780 modified, added, removed, deleted = repo.status()[:4]
780 modified, added, removed, deleted = repo.status()[:4]
781 repo.lfstatus = False
781 repo.lfstatus = False
782 if modified or added or removed or deleted:
782 if modified or added or removed or deleted:
783 raise util.Abort(_('outstanding uncommitted changes'))
783 raise util.Abort(_('outstanding uncommitted changes'))
784 return orig(ui, repo, *pats, **opts)
784 return orig(ui, repo, *pats, **opts)
785
785
786 def overrideforget(orig, ui, repo, *pats, **opts):
786 def overrideforget(orig, ui, repo, *pats, **opts):
787 installnormalfilesmatchfn(repo[None].manifest())
787 installnormalfilesmatchfn(repo[None].manifest())
788 orig(ui, repo, *pats, **opts)
788 orig(ui, repo, *pats, **opts)
789 restorematchfn()
789 restorematchfn()
790 m = scmutil.match(repo[None], pats, opts)
790 m = scmutil.match(repo[None], pats, opts)
791
791
792 try:
792 try:
793 repo.lfstatus = True
793 repo.lfstatus = True
794 s = repo.status(match=m, clean=True)
794 s = repo.status(match=m, clean=True)
795 finally:
795 finally:
796 repo.lfstatus = False
796 repo.lfstatus = False
797 forget = sorted(s[0] + s[1] + s[3] + s[6])
797 forget = sorted(s[0] + s[1] + s[3] + s[6])
798 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
798 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
799
799
800 for f in forget:
800 for f in forget:
801 if lfutil.standin(f) not in repo.dirstate and not \
801 if lfutil.standin(f) not in repo.dirstate and not \
802 os.path.isdir(m.rel(lfutil.standin(f))):
802 os.path.isdir(m.rel(lfutil.standin(f))):
803 ui.warn(_('not removing %s: file is already untracked\n')
803 ui.warn(_('not removing %s: file is already untracked\n')
804 % m.rel(f))
804 % m.rel(f))
805
805
806 for f in forget:
806 for f in forget:
807 if ui.verbose or not m.exact(f):
807 if ui.verbose or not m.exact(f):
808 ui.status(_('removing %s\n') % m.rel(f))
808 ui.status(_('removing %s\n') % m.rel(f))
809
809
810 # Need to lock because standin files are deleted then removed from the
810 # Need to lock because standin files are deleted then removed from the
811 # repository and we could race inbetween.
811 # repository and we could race inbetween.
812 wlock = repo.wlock()
812 wlock = repo.wlock()
813 try:
813 try:
814 lfdirstate = lfutil.openlfdirstate(ui, repo)
814 lfdirstate = lfutil.openlfdirstate(ui, repo)
815 for f in forget:
815 for f in forget:
816 if lfdirstate[f] == 'a':
816 if lfdirstate[f] == 'a':
817 lfdirstate.drop(f)
817 lfdirstate.drop(f)
818 else:
818 else:
819 lfdirstate.remove(f)
819 lfdirstate.remove(f)
820 lfdirstate.write()
820 lfdirstate.write()
821 lfutil.reporemove(repo, [lfutil.standin(f) for f in forget],
821 lfutil.reporemove(repo, [lfutil.standin(f) for f in forget],
822 unlink=True)
822 unlink=True)
823 finally:
823 finally:
824 wlock.release()
824 wlock.release()
825
825
826 def getoutgoinglfiles(ui, repo, dest=None, **opts):
826 def getoutgoinglfiles(ui, repo, dest=None, **opts):
827 dest = ui.expandpath(dest or 'default-push', dest or 'default')
827 dest = ui.expandpath(dest or 'default-push', dest or 'default')
828 dest, branches = hg.parseurl(dest, opts.get('branch'))
828 dest, branches = hg.parseurl(dest, opts.get('branch'))
829 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
829 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
830 if revs:
830 if revs:
831 revs = [repo.lookup(rev) for rev in revs]
831 revs = [repo.lookup(rev) for rev in revs]
832
832
833 remoteui = hg.remoteui
833 remoteui = hg.remoteui
834
834
835 try:
835 try:
836 remote = hg.repository(remoteui(repo, opts), dest)
836 remote = hg.repository(remoteui(repo, opts), dest)
837 except error.RepoError:
837 except error.RepoError:
838 return None
838 return None
839 o = lfutil.findoutgoing(repo, remote, False)
839 o = lfutil.findoutgoing(repo, remote, False)
840 if not o:
840 if not o:
841 return None
841 return None
842 o = repo.changelog.nodesbetween(o, revs)[0]
842 o = repo.changelog.nodesbetween(o, revs)[0]
843 if opts.get('newest_first'):
843 if opts.get('newest_first'):
844 o.reverse()
844 o.reverse()
845
845
846 toupload = set()
846 toupload = set()
847 for n in o:
847 for n in o:
848 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
848 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
849 ctx = repo[n]
849 ctx = repo[n]
850 files = set(ctx.files())
850 files = set(ctx.files())
851 if len(parents) == 2:
851 if len(parents) == 2:
852 mc = ctx.manifest()
852 mc = ctx.manifest()
853 mp1 = ctx.parents()[0].manifest()
853 mp1 = ctx.parents()[0].manifest()
854 mp2 = ctx.parents()[1].manifest()
854 mp2 = ctx.parents()[1].manifest()
855 for f in mp1:
855 for f in mp1:
856 if f not in mc:
856 if f not in mc:
857 files.add(f)
857 files.add(f)
858 for f in mp2:
858 for f in mp2:
859 if f not in mc:
859 if f not in mc:
860 files.add(f)
860 files.add(f)
861 for f in mc:
861 for f in mc:
862 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
862 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
863 files.add(f)
863 files.add(f)
864 toupload = toupload.union(
864 toupload = toupload.union(
865 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
865 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
866 return toupload
866 return toupload
867
867
868 def overrideoutgoing(orig, ui, repo, dest=None, **opts):
868 def overrideoutgoing(orig, ui, repo, dest=None, **opts):
869 orig(ui, repo, dest, **opts)
869 orig(ui, repo, dest, **opts)
870
870
871 if opts.pop('large', None):
871 if opts.pop('large', None):
872 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
872 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
873 if toupload is None:
873 if toupload is None:
874 ui.status(_('largefiles: No remote repo\n'))
874 ui.status(_('largefiles: No remote repo\n'))
875 else:
875 else:
876 ui.status(_('largefiles to upload:\n'))
876 ui.status(_('largefiles to upload:\n'))
877 for file in toupload:
877 for file in toupload:
878 ui.status(lfutil.splitstandin(file) + '\n')
878 ui.status(lfutil.splitstandin(file) + '\n')
879 ui.status('\n')
879 ui.status('\n')
880
880
881 def overridesummary(orig, ui, repo, *pats, **opts):
881 def overridesummary(orig, ui, repo, *pats, **opts):
882 try:
882 try:
883 repo.lfstatus = True
883 repo.lfstatus = True
884 orig(ui, repo, *pats, **opts)
884 orig(ui, repo, *pats, **opts)
885 finally:
885 finally:
886 repo.lfstatus = False
886 repo.lfstatus = False
887
887
888 if opts.pop('large', None):
888 if opts.pop('large', None):
889 toupload = getoutgoinglfiles(ui, repo, None, **opts)
889 toupload = getoutgoinglfiles(ui, repo, None, **opts)
890 if toupload is None:
890 if toupload is None:
891 ui.status(_('largefiles: No remote repo\n'))
891 ui.status(_('largefiles: No remote repo\n'))
892 else:
892 else:
893 ui.status(_('largefiles: %d to upload\n') % len(toupload))
893 ui.status(_('largefiles: %d to upload\n') % len(toupload))
894
894
895 def overrideaddremove(orig, ui, repo, *pats, **opts):
895 def overrideaddremove(orig, ui, repo, *pats, **opts):
896 # Get the list of missing largefiles so we can remove them
896 # Get the list of missing largefiles so we can remove them
897 lfdirstate = lfutil.openlfdirstate(ui, repo)
897 lfdirstate = lfutil.openlfdirstate(ui, repo)
898 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
898 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
899 False, False)
899 False, False)
900 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
900 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
901
901
902 # Call into the normal remove code, but the removing of the standin, we want
902 # Call into the normal remove code, but the removing of the standin, we want
903 # to have handled by original addremove. Monkey patching here makes sure
903 # to have handled by original addremove. Monkey patching here makes sure
904 # we don't remove the standin in the largefiles code, preventing a very
904 # we don't remove the standin in the largefiles code, preventing a very
905 # confused state later.
905 # confused state later.
906 if missing:
906 if missing:
907 repo._isaddremove = True
907 repo._isaddremove = True
908 removelargefiles(ui, repo, *missing, **opts)
908 removelargefiles(ui, repo, *missing, **opts)
909 repo._isaddremove = False
909 repo._isaddremove = False
910 # Call into the normal add code, and any files that *should* be added as
910 # Call into the normal add code, and any files that *should* be added as
911 # largefiles will be
911 # largefiles will be
912 addlargefiles(ui, repo, *pats, **opts)
912 addlargefiles(ui, repo, *pats, **opts)
913 # Now that we've handled largefiles, hand off to the original addremove
913 # Now that we've handled largefiles, hand off to the original addremove
914 # function to take care of the rest. Make sure it doesn't do anything with
914 # function to take care of the rest. Make sure it doesn't do anything with
915 # largefiles by installing a matcher that will ignore them.
915 # largefiles by installing a matcher that will ignore them.
916 installnormalfilesmatchfn(repo[None].manifest())
916 installnormalfilesmatchfn(repo[None].manifest())
917 result = orig(ui, repo, *pats, **opts)
917 result = orig(ui, repo, *pats, **opts)
918 restorematchfn()
918 restorematchfn()
919 return result
919 return result
920
920
921 # Calling purge with --all will cause the largefiles to be deleted.
921 # Calling purge with --all will cause the largefiles to be deleted.
922 # Override repo.status to prevent this from happening.
922 # Override repo.status to prevent this from happening.
923 def overridepurge(orig, ui, repo, *dirs, **opts):
923 def overridepurge(orig, ui, repo, *dirs, **opts):
924 oldstatus = repo.status
924 oldstatus = repo.status
925 def overridestatus(node1='.', node2=None, match=None, ignored=False,
925 def overridestatus(node1='.', node2=None, match=None, ignored=False,
926 clean=False, unknown=False, listsubrepos=False):
926 clean=False, unknown=False, listsubrepos=False):
927 r = oldstatus(node1, node2, match, ignored, clean, unknown,
927 r = oldstatus(node1, node2, match, ignored, clean, unknown,
928 listsubrepos)
928 listsubrepos)
929 lfdirstate = lfutil.openlfdirstate(ui, repo)
929 lfdirstate = lfutil.openlfdirstate(ui, repo)
930 modified, added, removed, deleted, unknown, ignored, clean = r
930 modified, added, removed, deleted, unknown, ignored, clean = r
931 unknown = [f for f in unknown if lfdirstate[f] == '?']
931 unknown = [f for f in unknown if lfdirstate[f] == '?']
932 ignored = [f for f in ignored if lfdirstate[f] == '?']
932 ignored = [f for f in ignored if lfdirstate[f] == '?']
933 return modified, added, removed, deleted, unknown, ignored, clean
933 return modified, added, removed, deleted, unknown, ignored, clean
934 repo.status = overridestatus
934 repo.status = overridestatus
935 orig(ui, repo, *dirs, **opts)
935 orig(ui, repo, *dirs, **opts)
936 repo.status = oldstatus
936 repo.status = oldstatus
937
937
938 def overriderollback(orig, ui, repo, **opts):
938 def overriderollback(orig, ui, repo, **opts):
939 result = orig(ui, repo, **opts)
939 result = orig(ui, repo, **opts)
940 merge.update(repo, node=None, branchmerge=False, force=True,
940 merge.update(repo, node=None, branchmerge=False, force=True,
941 partial=lfutil.isstandin)
941 partial=lfutil.isstandin)
942 wlock = repo.wlock()
942 wlock = repo.wlock()
943 try:
943 try:
944 lfdirstate = lfutil.openlfdirstate(ui, repo)
944 lfdirstate = lfutil.openlfdirstate(ui, repo)
945 lfiles = lfutil.listlfiles(repo)
945 lfiles = lfutil.listlfiles(repo)
946 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
946 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
947 for file in lfiles:
947 for file in lfiles:
948 if file in oldlfiles:
948 if file in oldlfiles:
949 lfdirstate.normallookup(file)
949 lfdirstate.normallookup(file)
950 else:
950 else:
951 lfdirstate.add(file)
951 lfdirstate.add(file)
952 lfdirstate.write()
952 lfdirstate.write()
953 finally:
953 finally:
954 wlock.release()
954 wlock.release()
955 return result
955 return result
956
956
957 def overridetransplant(orig, ui, repo, *revs, **opts):
957 def overridetransplant(orig, ui, repo, *revs, **opts):
958 try:
958 try:
959 oldstandins = lfutil.getstandinsstate(repo)
959 oldstandins = lfutil.getstandinsstate(repo)
960 repo._istransplanting = True
960 repo._istransplanting = True
961 result = orig(ui, repo, *revs, **opts)
961 result = orig(ui, repo, *revs, **opts)
962 newstandins = lfutil.getstandinsstate(repo)
962 newstandins = lfutil.getstandinsstate(repo)
963 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
963 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
964 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
964 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
965 printmessage=True)
965 printmessage=True)
966 finally:
966 finally:
967 repo._istransplanting = False
967 repo._istransplanting = False
968 return result
968 return result
969
970 def overridecat(orig, ui, repo, file1, *pats, **opts):
971 rev = opts.get('rev')
972 if not lfutil.standin(file1) in repo[rev]:
973 result = orig(ui, repo, file1, *pats, **opts)
974 return result
975 return lfcommands.catlfile(repo, file1, opts.get('rev'), opts.get('output'))
@@ -1,143 +1,145
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 httprepo, localrepo, merge, sshrepo, sshserver, wireproto
12 httprepo, localrepo, merge, sshrepo, sshserver, wireproto
13 from mercurial.i18n import _
13 from mercurial.i18n import _
14 from mercurial.hgweb import hgweb_mod, protocol
14 from mercurial.hgweb import hgweb_mod, protocol
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 entry = extensions.wrapcommand(commands.table, 'addremove',
32 entry = extensions.wrapcommand(commands.table, 'addremove',
33 overrides.overrideaddremove)
33 overrides.overrideaddremove)
34 entry = extensions.wrapcommand(commands.table, 'remove',
34 entry = extensions.wrapcommand(commands.table, 'remove',
35 overrides.overrideremove)
35 overrides.overrideremove)
36 entry = extensions.wrapcommand(commands.table, 'forget',
36 entry = extensions.wrapcommand(commands.table, 'forget',
37 overrides.overrideforget)
37 overrides.overrideforget)
38 entry = extensions.wrapcommand(commands.table, 'status',
38 entry = extensions.wrapcommand(commands.table, 'status',
39 overrides.overridestatus)
39 overrides.overridestatus)
40 entry = extensions.wrapcommand(commands.table, 'log',
40 entry = extensions.wrapcommand(commands.table, 'log',
41 overrides.overridelog)
41 overrides.overridelog)
42 entry = extensions.wrapcommand(commands.table, 'rollback',
42 entry = extensions.wrapcommand(commands.table, 'rollback',
43 overrides.overriderollback)
43 overrides.overriderollback)
44 entry = extensions.wrapcommand(commands.table, 'verify',
44 entry = extensions.wrapcommand(commands.table, 'verify',
45 overrides.overrideverify)
45 overrides.overrideverify)
46
46
47 verifyopt = [('', 'large', None, _('verify largefiles')),
47 verifyopt = [('', 'large', None, _('verify largefiles')),
48 ('', 'lfa', None,
48 ('', 'lfa', None,
49 _('verify all revisions of largefiles not just current')),
49 _('verify all revisions of largefiles not just current')),
50 ('', 'lfc', None,
50 ('', 'lfc', None,
51 _('verify largefile contents not just existence'))]
51 _('verify largefile contents not just existence'))]
52 entry[1].extend(verifyopt)
52 entry[1].extend(verifyopt)
53
53
54 entry = extensions.wrapcommand(commands.table, 'outgoing',
54 entry = extensions.wrapcommand(commands.table, 'outgoing',
55 overrides.overrideoutgoing)
55 overrides.overrideoutgoing)
56 outgoingopt = [('', 'large', None, _('display outgoing largefiles'))]
56 outgoingopt = [('', 'large', None, _('display outgoing largefiles'))]
57 entry[1].extend(outgoingopt)
57 entry[1].extend(outgoingopt)
58 entry = extensions.wrapcommand(commands.table, 'summary',
58 entry = extensions.wrapcommand(commands.table, 'summary',
59 overrides.overridesummary)
59 overrides.overridesummary)
60 summaryopt = [('', 'large', None, _('display outgoing largefiles'))]
60 summaryopt = [('', 'large', None, _('display outgoing largefiles'))]
61 entry[1].extend(summaryopt)
61 entry[1].extend(summaryopt)
62
62
63 entry = extensions.wrapcommand(commands.table, 'update',
63 entry = extensions.wrapcommand(commands.table, 'update',
64 overrides.overrideupdate)
64 overrides.overrideupdate)
65 entry = extensions.wrapcommand(commands.table, 'pull',
65 entry = extensions.wrapcommand(commands.table, 'pull',
66 overrides.overridepull)
66 overrides.overridepull)
67 entry = extensions.wrapcommand(commands.table, 'cat',
68 overrides.overridecat)
67 entry = extensions.wrapfunction(merge, '_checkunknownfile',
69 entry = extensions.wrapfunction(merge, '_checkunknownfile',
68 overrides.overridecheckunknownfile)
70 overrides.overridecheckunknownfile)
69 entry = extensions.wrapfunction(merge, 'manifestmerge',
71 entry = extensions.wrapfunction(merge, 'manifestmerge',
70 overrides.overridemanifestmerge)
72 overrides.overridemanifestmerge)
71 entry = extensions.wrapfunction(filemerge, 'filemerge',
73 entry = extensions.wrapfunction(filemerge, 'filemerge',
72 overrides.overridefilemerge)
74 overrides.overridefilemerge)
73 entry = extensions.wrapfunction(cmdutil, 'copy',
75 entry = extensions.wrapfunction(cmdutil, 'copy',
74 overrides.overridecopy)
76 overrides.overridecopy)
75
77
76 # Backout calls revert so we need to override both the command and the
78 # Backout calls revert so we need to override both the command and the
77 # function
79 # function
78 entry = extensions.wrapcommand(commands.table, 'revert',
80 entry = extensions.wrapcommand(commands.table, 'revert',
79 overrides.overriderevert)
81 overrides.overriderevert)
80 entry = extensions.wrapfunction(commands, 'revert',
82 entry = extensions.wrapfunction(commands, 'revert',
81 overrides.overriderevert)
83 overrides.overriderevert)
82
84
83 # clone uses hg._update instead of hg.update even though they are the
85 # clone uses hg._update instead of hg.update even though they are the
84 # same function... so wrap both of them)
86 # same function... so wrap both of them)
85 extensions.wrapfunction(hg, 'update', overrides.hgupdate)
87 extensions.wrapfunction(hg, 'update', overrides.hgupdate)
86 extensions.wrapfunction(hg, '_update', overrides.hgupdate)
88 extensions.wrapfunction(hg, '_update', overrides.hgupdate)
87 extensions.wrapfunction(hg, 'clean', overrides.hgclean)
89 extensions.wrapfunction(hg, 'clean', overrides.hgclean)
88 extensions.wrapfunction(hg, 'merge', overrides.hgmerge)
90 extensions.wrapfunction(hg, 'merge', overrides.hgmerge)
89
91
90 extensions.wrapfunction(archival, 'archive', overrides.overridearchive)
92 extensions.wrapfunction(archival, 'archive', overrides.overridearchive)
91 extensions.wrapfunction(cmdutil, 'bailifchanged',
93 extensions.wrapfunction(cmdutil, 'bailifchanged',
92 overrides.overridebailifchanged)
94 overrides.overridebailifchanged)
93
95
94 # create the new wireproto commands ...
96 # create the new wireproto commands ...
95 wireproto.commands['putlfile'] = (proto.putlfile, 'sha')
97 wireproto.commands['putlfile'] = (proto.putlfile, 'sha')
96 wireproto.commands['getlfile'] = (proto.getlfile, 'sha')
98 wireproto.commands['getlfile'] = (proto.getlfile, 'sha')
97 wireproto.commands['statlfile'] = (proto.statlfile, 'sha')
99 wireproto.commands['statlfile'] = (proto.statlfile, 'sha')
98
100
99 # ... and wrap some existing ones
101 # ... and wrap some existing ones
100 wireproto.commands['capabilities'] = (proto.capabilities, '')
102 wireproto.commands['capabilities'] = (proto.capabilities, '')
101 wireproto.commands['heads'] = (proto.heads, '')
103 wireproto.commands['heads'] = (proto.heads, '')
102 wireproto.commands['lheads'] = (wireproto.heads, '')
104 wireproto.commands['lheads'] = (wireproto.heads, '')
103
105
104 # make putlfile behave the same as push and {get,stat}lfile behave
106 # make putlfile behave the same as push and {get,stat}lfile behave
105 # the same as pull w.r.t. permissions checks
107 # the same as pull w.r.t. permissions checks
106 hgweb_mod.perms['putlfile'] = 'push'
108 hgweb_mod.perms['putlfile'] = 'push'
107 hgweb_mod.perms['getlfile'] = 'pull'
109 hgweb_mod.perms['getlfile'] = 'pull'
108 hgweb_mod.perms['statlfile'] = 'pull'
110 hgweb_mod.perms['statlfile'] = 'pull'
109
111
110 # the hello wireproto command uses wireproto.capabilities, so it won't see
112 # the hello wireproto command uses wireproto.capabilities, so it won't see
111 # our largefiles capability unless we replace the actual function as well.
113 # our largefiles capability unless we replace the actual function as well.
112 proto.capabilitiesorig = wireproto.capabilities
114 proto.capabilitiesorig = wireproto.capabilities
113 wireproto.capabilities = proto.capabilities
115 wireproto.capabilities = proto.capabilities
114
116
115 # these let us reject non-largefiles clients and make them display
117 # these let us reject non-largefiles clients and make them display
116 # our error messages
118 # our error messages
117 protocol.webproto.refuseclient = proto.webprotorefuseclient
119 protocol.webproto.refuseclient = proto.webprotorefuseclient
118 sshserver.sshserver.refuseclient = proto.sshprotorefuseclient
120 sshserver.sshserver.refuseclient = proto.sshprotorefuseclient
119
121
120 # can't do this in reposetup because it needs to have happened before
122 # can't do this in reposetup because it needs to have happened before
121 # wirerepo.__init__ is called
123 # wirerepo.__init__ is called
122 proto.ssholdcallstream = sshrepo.sshrepository._callstream
124 proto.ssholdcallstream = sshrepo.sshrepository._callstream
123 proto.httpoldcallstream = httprepo.httprepository._callstream
125 proto.httpoldcallstream = httprepo.httprepository._callstream
124 sshrepo.sshrepository._callstream = proto.sshrepocallstream
126 sshrepo.sshrepository._callstream = proto.sshrepocallstream
125 httprepo.httprepository._callstream = proto.httprepocallstream
127 httprepo.httprepository._callstream = proto.httprepocallstream
126
128
127 # don't die on seeing a repo with the largefiles requirement
129 # don't die on seeing a repo with the largefiles requirement
128 localrepo.localrepository.supported |= set(['largefiles'])
130 localrepo.localrepository.supported |= set(['largefiles'])
129
131
130 # override some extensions' stuff as well
132 # override some extensions' stuff as well
131 for name, module in extensions.extensions():
133 for name, module in extensions.extensions():
132 if name == 'fetch':
134 if name == 'fetch':
133 extensions.wrapcommand(getattr(module, 'cmdtable'), 'fetch',
135 extensions.wrapcommand(getattr(module, 'cmdtable'), 'fetch',
134 overrides.overridefetch)
136 overrides.overridefetch)
135 if name == 'purge':
137 if name == 'purge':
136 extensions.wrapcommand(getattr(module, 'cmdtable'), 'purge',
138 extensions.wrapcommand(getattr(module, 'cmdtable'), 'purge',
137 overrides.overridepurge)
139 overrides.overridepurge)
138 if name == 'rebase':
140 if name == 'rebase':
139 extensions.wrapcommand(getattr(module, 'cmdtable'), 'rebase',
141 extensions.wrapcommand(getattr(module, 'cmdtable'), 'rebase',
140 overrides.overriderebase)
142 overrides.overriderebase)
141 if name == 'transplant':
143 if name == 'transplant':
142 extensions.wrapcommand(getattr(module, 'cmdtable'), 'transplant',
144 extensions.wrapcommand(getattr(module, 'cmdtable'), 'transplant',
143 overrides.overridetransplant)
145 overrides.overridetransplant)
@@ -1,1018 +1,1031
1 $ "$TESTDIR/hghave" symlink unix-permissions serve || exit 80
1 $ "$TESTDIR/hghave" symlink unix-permissions serve || exit 80
2 $ USERCACHE=`pwd`/cache; export USERCACHE
2 $ USERCACHE=`pwd`/cache; export USERCACHE
3 $ mkdir -p ${USERCACHE}
3 $ mkdir -p ${USERCACHE}
4 $ cat >> $HGRCPATH <<EOF
4 $ cat >> $HGRCPATH <<EOF
5 > [extensions]
5 > [extensions]
6 > largefiles=
6 > largefiles=
7 > purge=
7 > purge=
8 > rebase=
8 > rebase=
9 > transplant=
9 > transplant=
10 > [phases]
10 > [phases]
11 > publish=False
11 > publish=False
12 > [largefiles]
12 > [largefiles]
13 > minsize=2
13 > minsize=2
14 > patterns=glob:**.dat
14 > patterns=glob:**.dat
15 > usercache=${USERCACHE}
15 > usercache=${USERCACHE}
16 > [hooks]
16 > [hooks]
17 > precommit=echo "Invoking status precommit hook"; hg status
17 > precommit=echo "Invoking status precommit hook"; hg status
18 > EOF
18 > EOF
19
19
20 Create the repo with a couple of revisions of both large and normal
20 Create the repo with a couple of revisions of both large and normal
21 files, testing that status correctly shows largefiles and that summary output
21 files, testing that status correctly shows largefiles and that summary output
22 is correct.
22 is correct.
23
23
24 $ hg init a
24 $ hg init a
25 $ cd a
25 $ cd a
26 $ mkdir sub
26 $ mkdir sub
27 $ echo normal1 > normal1
27 $ echo normal1 > normal1
28 $ echo normal2 > sub/normal2
28 $ echo normal2 > sub/normal2
29 $ echo large1 > large1
29 $ echo large1 > large1
30 $ echo large2 > sub/large2
30 $ echo large2 > sub/large2
31 $ hg add normal1 sub/normal2
31 $ hg add normal1 sub/normal2
32 $ hg add --large large1 sub/large2
32 $ hg add --large large1 sub/large2
33 $ hg commit -m "add files"
33 $ hg commit -m "add files"
34 Invoking status precommit hook
34 Invoking status precommit hook
35 A large1
35 A large1
36 A normal1
36 A normal1
37 A sub/large2
37 A sub/large2
38 A sub/normal2
38 A sub/normal2
39 $ echo normal11 > normal1
39 $ echo normal11 > normal1
40 $ echo normal22 > sub/normal2
40 $ echo normal22 > sub/normal2
41 $ echo large11 > large1
41 $ echo large11 > large1
42 $ echo large22 > sub/large2
42 $ echo large22 > sub/large2
43 $ hg commit -m "edit files"
43 $ hg commit -m "edit files"
44 Invoking status precommit hook
44 Invoking status precommit hook
45 M large1
45 M large1
46 M normal1
46 M normal1
47 M sub/large2
47 M sub/large2
48 M sub/normal2
48 M sub/normal2
49 $ hg sum --large
49 $ hg sum --large
50 parent: 1:ce8896473775 tip
50 parent: 1:ce8896473775 tip
51 edit files
51 edit files
52 branch: default
52 branch: default
53 commit: (clean)
53 commit: (clean)
54 update: (current)
54 update: (current)
55 largefiles: No remote repo
55 largefiles: No remote repo
56
56
57 Commit preserved largefile contents.
57 Commit preserved largefile contents.
58
58
59 $ cat normal1
59 $ cat normal1
60 normal11
60 normal11
61 $ cat large1
61 $ cat large1
62 large11
62 large11
63 $ cat sub/normal2
63 $ cat sub/normal2
64 normal22
64 normal22
65 $ cat sub/large2
65 $ cat sub/large2
66 large22
66 large22
67
67
68 Remove both largefiles and normal files.
68 Remove both largefiles and normal files.
69
69
70 $ hg remove normal1 large1
70 $ hg remove normal1 large1
71 $ hg status large1
71 $ hg status large1
72 R large1
72 R large1
73 $ hg commit -m "remove files"
73 $ hg commit -m "remove files"
74 Invoking status precommit hook
74 Invoking status precommit hook
75 R large1
75 R large1
76 R normal1
76 R normal1
77 $ ls
77 $ ls
78 sub
78 sub
79 $ echo "testlargefile" > large1-test
79 $ echo "testlargefile" > large1-test
80 $ hg add --large large1-test
80 $ hg add --large large1-test
81 $ hg st
81 $ hg st
82 A large1-test
82 A large1-test
83 $ hg rm large1-test
83 $ hg rm large1-test
84 not removing large1-test: file has been marked for add (use forget to undo)
84 not removing large1-test: file has been marked for add (use forget to undo)
85 $ hg st
85 $ hg st
86 A large1-test
86 A large1-test
87 $ hg forget large1-test
87 $ hg forget large1-test
88 $ hg st
88 $ hg st
89 ? large1-test
89 ? large1-test
90 $ rm large1-test
90 $ rm large1-test
91
91
92 Copy both largefiles and normal files (testing that status output is correct).
92 Copy both largefiles and normal files (testing that status output is correct).
93
93
94 $ hg cp sub/normal2 normal1
94 $ hg cp sub/normal2 normal1
95 $ hg cp sub/large2 large1
95 $ hg cp sub/large2 large1
96 $ hg commit -m "copy files"
96 $ hg commit -m "copy files"
97 Invoking status precommit hook
97 Invoking status precommit hook
98 A large1
98 A large1
99 A normal1
99 A normal1
100 $ cat normal1
100 $ cat normal1
101 normal22
101 normal22
102 $ cat large1
102 $ cat large1
103 large22
103 large22
104
104
105 Test moving largefiles and verify that normal files are also unaffected.
105 Test moving largefiles and verify that normal files are also unaffected.
106
106
107 $ hg mv normal1 normal3
107 $ hg mv normal1 normal3
108 $ hg mv large1 large3
108 $ hg mv large1 large3
109 $ hg mv sub/normal2 sub/normal4
109 $ hg mv sub/normal2 sub/normal4
110 $ hg mv sub/large2 sub/large4
110 $ hg mv sub/large2 sub/large4
111 $ hg commit -m "move files"
111 $ hg commit -m "move files"
112 Invoking status precommit hook
112 Invoking status precommit hook
113 A large3
113 A large3
114 A normal3
114 A normal3
115 A sub/large4
115 A sub/large4
116 A sub/normal4
116 A sub/normal4
117 R large1
117 R large1
118 R normal1
118 R normal1
119 R sub/large2
119 R sub/large2
120 R sub/normal2
120 R sub/normal2
121 $ cat normal3
121 $ cat normal3
122 normal22
122 normal22
123 $ cat large3
123 $ cat large3
124 large22
124 large22
125 $ cat sub/normal4
125 $ cat sub/normal4
126 normal22
126 normal22
127 $ cat sub/large4
127 $ cat sub/large4
128 large22
128 large22
129
129
130 Test archiving the various revisions. These hit corner cases known with
130 Test archiving the various revisions. These hit corner cases known with
131 archiving.
131 archiving.
132
132
133 $ hg archive -r 0 ../archive0
133 $ hg archive -r 0 ../archive0
134 $ hg archive -r 1 ../archive1
134 $ hg archive -r 1 ../archive1
135 $ hg archive -r 2 ../archive2
135 $ hg archive -r 2 ../archive2
136 $ hg archive -r 3 ../archive3
136 $ hg archive -r 3 ../archive3
137 $ hg archive -r 4 ../archive4
137 $ hg archive -r 4 ../archive4
138 $ cd ../archive0
138 $ cd ../archive0
139 $ cat normal1
139 $ cat normal1
140 normal1
140 normal1
141 $ cat large1
141 $ cat large1
142 large1
142 large1
143 $ cat sub/normal2
143 $ cat sub/normal2
144 normal2
144 normal2
145 $ cat sub/large2
145 $ cat sub/large2
146 large2
146 large2
147 $ cd ../archive1
147 $ cd ../archive1
148 $ cat normal1
148 $ cat normal1
149 normal11
149 normal11
150 $ cat large1
150 $ cat large1
151 large11
151 large11
152 $ cat sub/normal2
152 $ cat sub/normal2
153 normal22
153 normal22
154 $ cat sub/large2
154 $ cat sub/large2
155 large22
155 large22
156 $ cd ../archive2
156 $ cd ../archive2
157 $ ls
157 $ ls
158 sub
158 sub
159 $ cat sub/normal2
159 $ cat sub/normal2
160 normal22
160 normal22
161 $ cat sub/large2
161 $ cat sub/large2
162 large22
162 large22
163 $ cd ../archive3
163 $ cd ../archive3
164 $ cat normal1
164 $ cat normal1
165 normal22
165 normal22
166 $ cat large1
166 $ cat large1
167 large22
167 large22
168 $ cat sub/normal2
168 $ cat sub/normal2
169 normal22
169 normal22
170 $ cat sub/large2
170 $ cat sub/large2
171 large22
171 large22
172 $ cd ../archive4
172 $ cd ../archive4
173 $ cat normal3
173 $ cat normal3
174 normal22
174 normal22
175 $ cat large3
175 $ cat large3
176 large22
176 large22
177 $ cat sub/normal4
177 $ cat sub/normal4
178 normal22
178 normal22
179 $ cat sub/large4
179 $ cat sub/large4
180 large22
180 large22
181
181
182 Commit corner case: specify files to commit.
182 Commit corner case: specify files to commit.
183
183
184 $ cd ../a
184 $ cd ../a
185 $ echo normal3 > normal3
185 $ echo normal3 > normal3
186 $ echo large3 > large3
186 $ echo large3 > large3
187 $ echo normal4 > sub/normal4
187 $ echo normal4 > sub/normal4
188 $ echo large4 > sub/large4
188 $ echo large4 > sub/large4
189 $ hg commit normal3 large3 sub/normal4 sub/large4 -m "edit files again"
189 $ hg commit normal3 large3 sub/normal4 sub/large4 -m "edit files again"
190 Invoking status precommit hook
190 Invoking status precommit hook
191 M large3
191 M large3
192 M normal3
192 M normal3
193 M sub/large4
193 M sub/large4
194 M sub/normal4
194 M sub/normal4
195 $ cat normal3
195 $ cat normal3
196 normal3
196 normal3
197 $ cat large3
197 $ cat large3
198 large3
198 large3
199 $ cat sub/normal4
199 $ cat sub/normal4
200 normal4
200 normal4
201 $ cat sub/large4
201 $ cat sub/large4
202 large4
202 large4
203
203
204 One more commit corner case: commit from a subdirectory.
204 One more commit corner case: commit from a subdirectory.
205
205
206 $ cd ../a
206 $ cd ../a
207 $ echo normal33 > normal3
207 $ echo normal33 > normal3
208 $ echo large33 > large3
208 $ echo large33 > large3
209 $ echo normal44 > sub/normal4
209 $ echo normal44 > sub/normal4
210 $ echo large44 > sub/large4
210 $ echo large44 > sub/large4
211 $ cd sub
211 $ cd sub
212 $ hg commit -m "edit files yet again"
212 $ hg commit -m "edit files yet again"
213 Invoking status precommit hook
213 Invoking status precommit hook
214 M large3
214 M large3
215 M normal3
215 M normal3
216 M sub/large4
216 M sub/large4
217 M sub/normal4
217 M sub/normal4
218 $ cat ../normal3
218 $ cat ../normal3
219 normal33
219 normal33
220 $ cat ../large3
220 $ cat ../large3
221 large33
221 large33
222 $ cat normal4
222 $ cat normal4
223 normal44
223 normal44
224 $ cat large4
224 $ cat large4
225 large44
225 large44
226
226
227 Committing standins is not allowed.
227 Committing standins is not allowed.
228
228
229 $ cd ..
229 $ cd ..
230 $ echo large3 > large3
230 $ echo large3 > large3
231 $ hg commit .hglf/large3 -m "try to commit standin"
231 $ hg commit .hglf/large3 -m "try to commit standin"
232 abort: file ".hglf/large3" is a largefile standin
232 abort: file ".hglf/large3" is a largefile standin
233 (commit the largefile itself instead)
233 (commit the largefile itself instead)
234 [255]
234 [255]
235
235
236 Corner cases for adding largefiles.
236 Corner cases for adding largefiles.
237
237
238 $ echo large5 > large5
238 $ echo large5 > large5
239 $ hg add --large large5
239 $ hg add --large large5
240 $ hg add --large large5
240 $ hg add --large large5
241 large5 already a largefile
241 large5 already a largefile
242 $ mkdir sub2
242 $ mkdir sub2
243 $ echo large6 > sub2/large6
243 $ echo large6 > sub2/large6
244 $ echo large7 > sub2/large7
244 $ echo large7 > sub2/large7
245 $ hg add --large sub2
245 $ hg add --large sub2
246 adding sub2/large6 as a largefile (glob)
246 adding sub2/large6 as a largefile (glob)
247 adding sub2/large7 as a largefile (glob)
247 adding sub2/large7 as a largefile (glob)
248 $ hg st
248 $ hg st
249 M large3
249 M large3
250 A large5
250 A large5
251 A sub2/large6
251 A sub2/large6
252 A sub2/large7
252 A sub2/large7
253
253
254 Test "hg status" with combination of 'file pattern' and 'directory
254 Test "hg status" with combination of 'file pattern' and 'directory
255 pattern' for largefiles:
255 pattern' for largefiles:
256
256
257 $ hg status sub2/large6 sub2
257 $ hg status sub2/large6 sub2
258 A sub2/large6
258 A sub2/large6
259 A sub2/large7
259 A sub2/large7
260
260
261 Config settings (pattern **.dat, minsize 2 MB) are respected.
261 Config settings (pattern **.dat, minsize 2 MB) are respected.
262
262
263 $ echo testdata > test.dat
263 $ echo testdata > test.dat
264 $ dd bs=1k count=2k if=/dev/zero of=reallylarge > /dev/null 2> /dev/null
264 $ dd bs=1k count=2k if=/dev/zero of=reallylarge > /dev/null 2> /dev/null
265 $ hg add
265 $ hg add
266 adding reallylarge as a largefile
266 adding reallylarge as a largefile
267 adding test.dat as a largefile
267 adding test.dat as a largefile
268
268
269 Test that minsize and --lfsize handle float values;
269 Test that minsize and --lfsize handle float values;
270 also tests that --lfsize overrides largefiles.minsize.
270 also tests that --lfsize overrides largefiles.minsize.
271 (0.250 MB = 256 kB = 262144 B)
271 (0.250 MB = 256 kB = 262144 B)
272
272
273 $ dd if=/dev/zero of=ratherlarge bs=1024 count=256 > /dev/null 2> /dev/null
273 $ dd if=/dev/zero of=ratherlarge bs=1024 count=256 > /dev/null 2> /dev/null
274 $ dd if=/dev/zero of=medium bs=1024 count=128 > /dev/null 2> /dev/null
274 $ dd if=/dev/zero of=medium bs=1024 count=128 > /dev/null 2> /dev/null
275 $ hg --config largefiles.minsize=.25 add
275 $ hg --config largefiles.minsize=.25 add
276 adding ratherlarge as a largefile
276 adding ratherlarge as a largefile
277 adding medium
277 adding medium
278 $ hg forget medium
278 $ hg forget medium
279 $ hg --config largefiles.minsize=.25 add --lfsize=.125
279 $ hg --config largefiles.minsize=.25 add --lfsize=.125
280 adding medium as a largefile
280 adding medium as a largefile
281 $ dd if=/dev/zero of=notlarge bs=1024 count=127 > /dev/null 2> /dev/null
281 $ dd if=/dev/zero of=notlarge bs=1024 count=127 > /dev/null 2> /dev/null
282 $ hg --config largefiles.minsize=.25 add --lfsize=.125
282 $ hg --config largefiles.minsize=.25 add --lfsize=.125
283 adding notlarge
283 adding notlarge
284 $ hg forget notlarge
284 $ hg forget notlarge
285
285
286 Test forget on largefiles.
286 Test forget on largefiles.
287
287
288 $ hg forget large3 large5 test.dat reallylarge ratherlarge medium
288 $ hg forget large3 large5 test.dat reallylarge ratherlarge medium
289 $ hg commit -m "add/edit more largefiles"
289 $ hg commit -m "add/edit more largefiles"
290 Invoking status precommit hook
290 Invoking status precommit hook
291 A sub2/large6
291 A sub2/large6
292 A sub2/large7
292 A sub2/large7
293 R large3
293 R large3
294 ? large5
294 ? large5
295 ? medium
295 ? medium
296 ? notlarge
296 ? notlarge
297 ? ratherlarge
297 ? ratherlarge
298 ? reallylarge
298 ? reallylarge
299 ? test.dat
299 ? test.dat
300 $ hg st
300 $ hg st
301 ? large3
301 ? large3
302 ? large5
302 ? large5
303 ? medium
303 ? medium
304 ? notlarge
304 ? notlarge
305 ? ratherlarge
305 ? ratherlarge
306 ? reallylarge
306 ? reallylarge
307 ? test.dat
307 ? test.dat
308
308
309 Purge with largefiles: verify that largefiles are still in the working
309 Purge with largefiles: verify that largefiles are still in the working
310 dir after a purge.
310 dir after a purge.
311
311
312 $ hg purge --all
312 $ hg purge --all
313 $ cat sub/large4
313 $ cat sub/large4
314 large44
314 large44
315 $ cat sub2/large6
315 $ cat sub2/large6
316 large6
316 large6
317 $ cat sub2/large7
317 $ cat sub2/large7
318 large7
318 large7
319
319
320 Test addremove: verify that files that should be added as largfiles are added as
320 Test addremove: verify that files that should be added as largfiles are added as
321 such and that already-existing largfiles are not added as normal files by
321 such and that already-existing largfiles are not added as normal files by
322 accident.
322 accident.
323
323
324 $ rm normal3
324 $ rm normal3
325 $ rm sub/large4
325 $ rm sub/large4
326 $ echo "testing addremove with patterns" > testaddremove.dat
326 $ echo "testing addremove with patterns" > testaddremove.dat
327 $ echo "normaladdremove" > normaladdremove
327 $ echo "normaladdremove" > normaladdremove
328 $ hg addremove
328 $ hg addremove
329 removing sub/large4
329 removing sub/large4
330 adding testaddremove.dat as a largefile
330 adding testaddremove.dat as a largefile
331 removing normal3
331 removing normal3
332 adding normaladdremove
332 adding normaladdremove
333
333
334 Clone a largefiles repo.
334 Clone a largefiles repo.
335
335
336 $ hg clone . ../b
336 $ hg clone . ../b
337 updating to branch default
337 updating to branch default
338 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
338 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
339 getting changed largefiles
339 getting changed largefiles
340 3 largefiles updated, 0 removed
340 3 largefiles updated, 0 removed
341 $ cd ../b
341 $ cd ../b
342 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
342 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
343 7:daea875e9014 add/edit more largefiles
343 7:daea875e9014 add/edit more largefiles
344 6:4355d653f84f edit files yet again
344 6:4355d653f84f edit files yet again
345 5:9d5af5072dbd edit files again
345 5:9d5af5072dbd edit files again
346 4:74c02385b94c move files
346 4:74c02385b94c move files
347 3:9e8fbc4bce62 copy files
347 3:9e8fbc4bce62 copy files
348 2:51a0ae4d5864 remove files
348 2:51a0ae4d5864 remove files
349 1:ce8896473775 edit files
349 1:ce8896473775 edit files
350 0:30d30fe6a5be add files
350 0:30d30fe6a5be add files
351 $ cat normal3
351 $ cat normal3
352 normal33
352 normal33
353 $ cat sub/normal4
353 $ cat sub/normal4
354 normal44
354 normal44
355 $ cat sub/large4
355 $ cat sub/large4
356 large44
356 large44
357 $ cat sub2/large6
357 $ cat sub2/large6
358 large6
358 large6
359 $ cat sub2/large7
359 $ cat sub2/large7
360 large7
360 large7
361 $ cd ..
361 $ cd ..
362 $ hg clone a -r 3 c
362 $ hg clone a -r 3 c
363 adding changesets
363 adding changesets
364 adding manifests
364 adding manifests
365 adding file changes
365 adding file changes
366 added 4 changesets with 10 changes to 4 files
366 added 4 changesets with 10 changes to 4 files
367 updating to branch default
367 updating to branch default
368 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
368 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
369 getting changed largefiles
369 getting changed largefiles
370 2 largefiles updated, 0 removed
370 2 largefiles updated, 0 removed
371 $ cd c
371 $ cd c
372 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
372 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
373 3:9e8fbc4bce62 copy files
373 3:9e8fbc4bce62 copy files
374 2:51a0ae4d5864 remove files
374 2:51a0ae4d5864 remove files
375 1:ce8896473775 edit files
375 1:ce8896473775 edit files
376 0:30d30fe6a5be add files
376 0:30d30fe6a5be add files
377 $ cat normal1
377 $ cat normal1
378 normal22
378 normal22
379 $ cat large1
379 $ cat large1
380 large22
380 large22
381 $ cat sub/normal2
381 $ cat sub/normal2
382 normal22
382 normal22
383 $ cat sub/large2
383 $ cat sub/large2
384 large22
384 large22
385
385
386 Old revisions of a clone have correct largefiles content (this also
386 Old revisions of a clone have correct largefiles content (this also
387 tests update).
387 tests update).
388
388
389 $ hg update -r 1
389 $ hg update -r 1
390 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
390 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
391 getting changed largefiles
391 getting changed largefiles
392 1 largefiles updated, 0 removed
392 1 largefiles updated, 0 removed
393 $ cat large1
393 $ cat large1
394 large11
394 large11
395 $ cat sub/large2
395 $ cat sub/large2
396 large22
396 large22
397
397
398 Rebasing between two repositories does not revert largefiles to old
398 Rebasing between two repositories does not revert largefiles to old
399 revisions (this was a very bad bug that took a lot of work to fix).
399 revisions (this was a very bad bug that took a lot of work to fix).
400
400
401 $ cd ..
401 $ cd ..
402 $ hg clone a d
402 $ hg clone a d
403 updating to branch default
403 updating to branch default
404 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
404 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
405 getting changed largefiles
405 getting changed largefiles
406 3 largefiles updated, 0 removed
406 3 largefiles updated, 0 removed
407 $ cd b
407 $ cd b
408 $ echo large4-modified > sub/large4
408 $ echo large4-modified > sub/large4
409 $ echo normal3-modified > normal3
409 $ echo normal3-modified > normal3
410 $ hg commit -m "modify normal file and largefile in repo b"
410 $ hg commit -m "modify normal file and largefile in repo b"
411 Invoking status precommit hook
411 Invoking status precommit hook
412 M normal3
412 M normal3
413 M sub/large4
413 M sub/large4
414 $ cd ../d
414 $ cd ../d
415 $ echo large6-modified > sub2/large6
415 $ echo large6-modified > sub2/large6
416 $ echo normal4-modified > sub/normal4
416 $ echo normal4-modified > sub/normal4
417 $ hg commit -m "modify normal file largefile in repo d"
417 $ hg commit -m "modify normal file largefile in repo d"
418 Invoking status precommit hook
418 Invoking status precommit hook
419 M sub/normal4
419 M sub/normal4
420 M sub2/large6
420 M sub2/large6
421 $ cd ..
421 $ cd ..
422 $ hg clone d e
422 $ hg clone d e
423 updating to branch default
423 updating to branch default
424 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
424 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
425 getting changed largefiles
425 getting changed largefiles
426 3 largefiles updated, 0 removed
426 3 largefiles updated, 0 removed
427 $ cd d
427 $ cd d
428 $ hg pull --rebase ../b
428 $ hg pull --rebase ../b
429 pulling from ../b
429 pulling from ../b
430 searching for changes
430 searching for changes
431 adding changesets
431 adding changesets
432 adding manifests
432 adding manifests
433 adding file changes
433 adding file changes
434 added 1 changesets with 2 changes to 2 files (+1 heads)
434 added 1 changesets with 2 changes to 2 files (+1 heads)
435 Invoking status precommit hook
435 Invoking status precommit hook
436 M sub/normal4
436 M sub/normal4
437 M sub2/large6
437 M sub2/large6
438 saved backup bundle to $TESTTMP/d/.hg/strip-backup/f574fb32bb45-backup.hg
438 saved backup bundle to $TESTTMP/d/.hg/strip-backup/f574fb32bb45-backup.hg
439 nothing to rebase
439 nothing to rebase
440 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
440 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
441 9:598410d3eb9a modify normal file largefile in repo d
441 9:598410d3eb9a modify normal file largefile in repo d
442 8:a381d2c8c80e modify normal file and largefile in repo b
442 8:a381d2c8c80e modify normal file and largefile in repo b
443 7:daea875e9014 add/edit more largefiles
443 7:daea875e9014 add/edit more largefiles
444 6:4355d653f84f edit files yet again
444 6:4355d653f84f edit files yet again
445 5:9d5af5072dbd edit files again
445 5:9d5af5072dbd edit files again
446 4:74c02385b94c move files
446 4:74c02385b94c move files
447 3:9e8fbc4bce62 copy files
447 3:9e8fbc4bce62 copy files
448 2:51a0ae4d5864 remove files
448 2:51a0ae4d5864 remove files
449 1:ce8896473775 edit files
449 1:ce8896473775 edit files
450 0:30d30fe6a5be add files
450 0:30d30fe6a5be add files
451 $ cat normal3
451 $ cat normal3
452 normal3-modified
452 normal3-modified
453 $ cat sub/normal4
453 $ cat sub/normal4
454 normal4-modified
454 normal4-modified
455 $ cat sub/large4
455 $ cat sub/large4
456 large4-modified
456 large4-modified
457 $ cat sub2/large6
457 $ cat sub2/large6
458 large6-modified
458 large6-modified
459 $ cat sub2/large7
459 $ cat sub2/large7
460 large7
460 large7
461 $ cd ../e
461 $ cd ../e
462 $ hg pull ../b
462 $ hg pull ../b
463 pulling from ../b
463 pulling from ../b
464 searching for changes
464 searching for changes
465 adding changesets
465 adding changesets
466 adding manifests
466 adding manifests
467 adding file changes
467 adding file changes
468 added 1 changesets with 2 changes to 2 files (+1 heads)
468 added 1 changesets with 2 changes to 2 files (+1 heads)
469 (run 'hg heads' to see heads, 'hg merge' to merge)
469 (run 'hg heads' to see heads, 'hg merge' to merge)
470 caching new largefiles
470 caching new largefiles
471 0 largefiles cached
471 0 largefiles cached
472 $ hg rebase
472 $ hg rebase
473 Invoking status precommit hook
473 Invoking status precommit hook
474 M sub/normal4
474 M sub/normal4
475 M sub2/large6
475 M sub2/large6
476 saved backup bundle to $TESTTMP/e/.hg/strip-backup/f574fb32bb45-backup.hg
476 saved backup bundle to $TESTTMP/e/.hg/strip-backup/f574fb32bb45-backup.hg
477 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
477 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
478 9:598410d3eb9a modify normal file largefile in repo d
478 9:598410d3eb9a modify normal file largefile in repo d
479 8:a381d2c8c80e modify normal file and largefile in repo b
479 8:a381d2c8c80e modify normal file and largefile in repo b
480 7:daea875e9014 add/edit more largefiles
480 7:daea875e9014 add/edit more largefiles
481 6:4355d653f84f edit files yet again
481 6:4355d653f84f edit files yet again
482 5:9d5af5072dbd edit files again
482 5:9d5af5072dbd edit files again
483 4:74c02385b94c move files
483 4:74c02385b94c move files
484 3:9e8fbc4bce62 copy files
484 3:9e8fbc4bce62 copy files
485 2:51a0ae4d5864 remove files
485 2:51a0ae4d5864 remove files
486 1:ce8896473775 edit files
486 1:ce8896473775 edit files
487 0:30d30fe6a5be add files
487 0:30d30fe6a5be add files
488 $ cat normal3
488 $ cat normal3
489 normal3-modified
489 normal3-modified
490 $ cat sub/normal4
490 $ cat sub/normal4
491 normal4-modified
491 normal4-modified
492 $ cat sub/large4
492 $ cat sub/large4
493 large4-modified
493 large4-modified
494 $ cat sub2/large6
494 $ cat sub2/large6
495 large6-modified
495 large6-modified
496 $ cat sub2/large7
496 $ cat sub2/large7
497 large7
497 large7
498
498
499 Rollback on largefiles.
499 Rollback on largefiles.
500
500
501 $ echo large4-modified-again > sub/large4
501 $ echo large4-modified-again > sub/large4
502 $ hg commit -m "Modify large4 again"
502 $ hg commit -m "Modify large4 again"
503 Invoking status precommit hook
503 Invoking status precommit hook
504 M sub/large4
504 M sub/large4
505 $ hg rollback
505 $ hg rollback
506 repository tip rolled back to revision 9 (undo commit)
506 repository tip rolled back to revision 9 (undo commit)
507 working directory now based on revision 9
507 working directory now based on revision 9
508 $ hg st
508 $ hg st
509 M sub/large4
509 M sub/large4
510 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
510 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
511 9:598410d3eb9a modify normal file largefile in repo d
511 9:598410d3eb9a modify normal file largefile in repo d
512 8:a381d2c8c80e modify normal file and largefile in repo b
512 8:a381d2c8c80e modify normal file and largefile in repo b
513 7:daea875e9014 add/edit more largefiles
513 7:daea875e9014 add/edit more largefiles
514 6:4355d653f84f edit files yet again
514 6:4355d653f84f edit files yet again
515 5:9d5af5072dbd edit files again
515 5:9d5af5072dbd edit files again
516 4:74c02385b94c move files
516 4:74c02385b94c move files
517 3:9e8fbc4bce62 copy files
517 3:9e8fbc4bce62 copy files
518 2:51a0ae4d5864 remove files
518 2:51a0ae4d5864 remove files
519 1:ce8896473775 edit files
519 1:ce8896473775 edit files
520 0:30d30fe6a5be add files
520 0:30d30fe6a5be add files
521 $ cat sub/large4
521 $ cat sub/large4
522 large4-modified-again
522 large4-modified-again
523
523
524 "update --check" refuses to update with uncommitted changes.
524 "update --check" refuses to update with uncommitted changes.
525 $ hg update --check 8
525 $ hg update --check 8
526 abort: uncommitted local changes
526 abort: uncommitted local changes
527 [255]
527 [255]
528
528
529 "update --clean" leaves correct largefiles in working copy.
529 "update --clean" leaves correct largefiles in working copy.
530
530
531 $ hg update --clean
531 $ hg update --clean
532 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
532 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
533 getting changed largefiles
533 getting changed largefiles
534 1 largefiles updated, 0 removed
534 1 largefiles updated, 0 removed
535 $ cat normal3
535 $ cat normal3
536 normal3-modified
536 normal3-modified
537 $ cat sub/normal4
537 $ cat sub/normal4
538 normal4-modified
538 normal4-modified
539 $ cat sub/large4
539 $ cat sub/large4
540 large4-modified
540 large4-modified
541 $ cat sub2/large6
541 $ cat sub2/large6
542 large6-modified
542 large6-modified
543 $ cat sub2/large7
543 $ cat sub2/large7
544 large7
544 large7
545
545
546 Now "update check" is happy.
546 Now "update check" is happy.
547 $ hg update --check 8
547 $ hg update --check 8
548 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
548 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
549 getting changed largefiles
549 getting changed largefiles
550 1 largefiles updated, 0 removed
550 1 largefiles updated, 0 removed
551 $ hg update --check
551 $ hg update --check
552 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
552 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
553 getting changed largefiles
553 getting changed largefiles
554 1 largefiles updated, 0 removed
554 1 largefiles updated, 0 removed
555
555
556 Test removing empty largefiles directories on update
556 Test removing empty largefiles directories on update
557 $ test -d sub2 && echo "sub2 exists"
557 $ test -d sub2 && echo "sub2 exists"
558 sub2 exists
558 sub2 exists
559 $ hg update -q null
559 $ hg update -q null
560 $ test -d sub2 && echo "error: sub2 should not exist anymore"
560 $ test -d sub2 && echo "error: sub2 should not exist anymore"
561 [1]
561 [1]
562 $ hg update -q
562 $ hg update -q
563
563
564 Test hg remove removes empty largefiles directories
564 Test hg remove removes empty largefiles directories
565 $ test -d sub2 && echo "sub2 exists"
565 $ test -d sub2 && echo "sub2 exists"
566 sub2 exists
566 sub2 exists
567 $ hg remove sub2/*
567 $ hg remove sub2/*
568 $ test -d sub2 && echo "error: sub2 should not exist anymore"
568 $ test -d sub2 && echo "error: sub2 should not exist anymore"
569 [1]
569 [1]
570 $ hg revert sub2/large6 sub2/large7
570 $ hg revert sub2/large6 sub2/large7
571
571
572 "revert" works on largefiles (and normal files too).
572 "revert" works on largefiles (and normal files too).
573 $ echo hack3 >> normal3
573 $ echo hack3 >> normal3
574 $ echo hack4 >> sub/normal4
574 $ echo hack4 >> sub/normal4
575 $ echo hack4 >> sub/large4
575 $ echo hack4 >> sub/large4
576 $ rm sub2/large6
576 $ rm sub2/large6
577 $ hg revert sub2/large6
577 $ hg revert sub2/large6
578 $ hg rm sub2/large6
578 $ hg rm sub2/large6
579 $ echo new >> sub2/large8
579 $ echo new >> sub2/large8
580 $ hg add --large sub2/large8
580 $ hg add --large sub2/large8
581 # XXX we don't really want to report that we're reverting the standin;
581 # XXX we don't really want to report that we're reverting the standin;
582 # that's just an implementation detail. But I don't see an obvious fix. ;-(
582 # that's just an implementation detail. But I don't see an obvious fix. ;-(
583 $ hg revert sub
583 $ hg revert sub
584 reverting .hglf/sub/large4 (glob)
584 reverting .hglf/sub/large4 (glob)
585 reverting sub/normal4 (glob)
585 reverting sub/normal4 (glob)
586 $ hg status
586 $ hg status
587 M normal3
587 M normal3
588 A sub2/large8
588 A sub2/large8
589 R sub2/large6
589 R sub2/large6
590 ? sub/large4.orig
590 ? sub/large4.orig
591 ? sub/normal4.orig
591 ? sub/normal4.orig
592 $ cat sub/normal4
592 $ cat sub/normal4
593 normal4-modified
593 normal4-modified
594 $ cat sub/large4
594 $ cat sub/large4
595 large4-modified
595 large4-modified
596 $ hg revert -a --no-backup
596 $ hg revert -a --no-backup
597 undeleting .hglf/sub2/large6 (glob)
597 undeleting .hglf/sub2/large6 (glob)
598 forgetting .hglf/sub2/large8 (glob)
598 forgetting .hglf/sub2/large8 (glob)
599 reverting normal3
599 reverting normal3
600 $ hg status
600 $ hg status
601 ? sub/large4.orig
601 ? sub/large4.orig
602 ? sub/normal4.orig
602 ? sub/normal4.orig
603 ? sub2/large8
603 ? sub2/large8
604 $ cat normal3
604 $ cat normal3
605 normal3-modified
605 normal3-modified
606 $ cat sub2/large6
606 $ cat sub2/large6
607 large6-modified
607 large6-modified
608 $ rm sub/*.orig sub2/large8
608 $ rm sub/*.orig sub2/large8
609
609
610 revert some files to an older revision
610 revert some files to an older revision
611 $ hg revert --no-backup -r 8 sub2
611 $ hg revert --no-backup -r 8 sub2
612 reverting .hglf/sub2/large6 (glob)
612 reverting .hglf/sub2/large6 (glob)
613 $ cat sub2/large6
613 $ cat sub2/large6
614 large6
614 large6
615 $ hg revert --no-backup sub2
615 $ hg revert --no-backup sub2
616 reverting .hglf/sub2/large6 (glob)
616 reverting .hglf/sub2/large6 (glob)
617 $ hg status
617 $ hg status
618
618
619 "verify --large" actually verifies largefiles
619 "verify --large" actually verifies largefiles
620
620
621 $ hg verify --large
621 $ hg verify --large
622 checking changesets
622 checking changesets
623 checking manifests
623 checking manifests
624 crosschecking files in changesets and manifests
624 crosschecking files in changesets and manifests
625 checking files
625 checking files
626 10 files, 10 changesets, 28 total revisions
626 10 files, 10 changesets, 28 total revisions
627 searching 1 changesets for largefiles
627 searching 1 changesets for largefiles
628 verified existence of 3 revisions of 3 largefiles
628 verified existence of 3 revisions of 3 largefiles
629
629
630 Merging does not revert to old versions of largefiles and also check
630 Merging does not revert to old versions of largefiles and also check
631 that merging after having pulled from a non-default remote works
631 that merging after having pulled from a non-default remote works
632 correctly.
632 correctly.
633
633
634 $ cd ..
634 $ cd ..
635 $ hg clone -r 7 e temp
635 $ hg clone -r 7 e temp
636 adding changesets
636 adding changesets
637 adding manifests
637 adding manifests
638 adding file changes
638 adding file changes
639 added 8 changesets with 24 changes to 10 files
639 added 8 changesets with 24 changes to 10 files
640 updating to branch default
640 updating to branch default
641 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
641 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
642 getting changed largefiles
642 getting changed largefiles
643 3 largefiles updated, 0 removed
643 3 largefiles updated, 0 removed
644 $ hg clone temp f
644 $ hg clone temp f
645 updating to branch default
645 updating to branch default
646 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
646 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
647 getting changed largefiles
647 getting changed largefiles
648 3 largefiles updated, 0 removed
648 3 largefiles updated, 0 removed
649 # Delete the largefiles in the largefiles system cache so that we have an
649 # Delete the largefiles in the largefiles system cache so that we have an
650 # opportunity to test that caching after a pull works.
650 # opportunity to test that caching after a pull works.
651 $ rm ${USERCACHE}/*
651 $ rm ${USERCACHE}/*
652 $ cd f
652 $ cd f
653 $ echo "large4-merge-test" > sub/large4
653 $ echo "large4-merge-test" > sub/large4
654 $ hg commit -m "Modify large4 to test merge"
654 $ hg commit -m "Modify large4 to test merge"
655 Invoking status precommit hook
655 Invoking status precommit hook
656 M sub/large4
656 M sub/large4
657 $ hg pull ../e
657 $ hg pull ../e
658 pulling from ../e
658 pulling from ../e
659 searching for changes
659 searching for changes
660 adding changesets
660 adding changesets
661 adding manifests
661 adding manifests
662 adding file changes
662 adding file changes
663 added 2 changesets with 4 changes to 4 files (+1 heads)
663 added 2 changesets with 4 changes to 4 files (+1 heads)
664 (run 'hg heads' to see heads, 'hg merge' to merge)
664 (run 'hg heads' to see heads, 'hg merge' to merge)
665 caching new largefiles
665 caching new largefiles
666 2 largefiles cached
666 2 largefiles cached
667 $ hg merge
667 $ hg merge
668 merging sub/large4
668 merging sub/large4
669 largefile sub/large4 has a merge conflict
669 largefile sub/large4 has a merge conflict
670 keep (l)ocal or take (o)ther? l
670 keep (l)ocal or take (o)ther? l
671 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
671 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
672 (branch merge, don't forget to commit)
672 (branch merge, don't forget to commit)
673 getting changed largefiles
673 getting changed largefiles
674 1 largefiles updated, 0 removed
674 1 largefiles updated, 0 removed
675 $ hg commit -m "Merge repos e and f"
675 $ hg commit -m "Merge repos e and f"
676 Invoking status precommit hook
676 Invoking status precommit hook
677 M normal3
677 M normal3
678 M sub/normal4
678 M sub/normal4
679 M sub2/large6
679 M sub2/large6
680 $ cat normal3
680 $ cat normal3
681 normal3-modified
681 normal3-modified
682 $ cat sub/normal4
682 $ cat sub/normal4
683 normal4-modified
683 normal4-modified
684 $ cat sub/large4
684 $ cat sub/large4
685 large4-merge-test
685 large4-merge-test
686 $ cat sub2/large6
686 $ cat sub2/large6
687 large6-modified
687 large6-modified
688 $ cat sub2/large7
688 $ cat sub2/large7
689 large7
689 large7
690
690
691 Test status after merging with a branch that introduces a new largefile:
691 Test status after merging with a branch that introduces a new largefile:
692
692
693 $ echo large > large
693 $ echo large > large
694 $ hg add --large large
694 $ hg add --large large
695 $ hg commit -m 'add largefile'
695 $ hg commit -m 'add largefile'
696 Invoking status precommit hook
696 Invoking status precommit hook
697 A large
697 A large
698 $ hg update -q ".^"
698 $ hg update -q ".^"
699 $ echo change >> normal3
699 $ echo change >> normal3
700 $ hg commit -m 'some change'
700 $ hg commit -m 'some change'
701 Invoking status precommit hook
701 Invoking status precommit hook
702 M normal3
702 M normal3
703 created new head
703 created new head
704 $ hg merge
704 $ hg merge
705 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
705 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
706 (branch merge, don't forget to commit)
706 (branch merge, don't forget to commit)
707 getting changed largefiles
707 getting changed largefiles
708 1 largefiles updated, 0 removed
708 1 largefiles updated, 0 removed
709 $ hg status
709 $ hg status
710 M large
710 M large
711
711
712 Test that a normal file and a largefile with the same name and path cannot
712 Test that a normal file and a largefile with the same name and path cannot
713 coexist.
713 coexist.
714
714
715 $ rm sub2/large7
715 $ rm sub2/large7
716 $ echo "largeasnormal" > sub2/large7
716 $ echo "largeasnormal" > sub2/large7
717 $ hg add sub2/large7
717 $ hg add sub2/large7
718 sub2/large7 already a largefile
718 sub2/large7 already a largefile
719
719
720 Test that transplanting a largefile change works correctly.
720 Test that transplanting a largefile change works correctly.
721
721
722 $ cd ..
722 $ cd ..
723 $ hg clone -r 8 d g
723 $ hg clone -r 8 d g
724 adding changesets
724 adding changesets
725 adding manifests
725 adding manifests
726 adding file changes
726 adding file changes
727 added 9 changesets with 26 changes to 10 files
727 added 9 changesets with 26 changes to 10 files
728 updating to branch default
728 updating to branch default
729 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
729 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
730 getting changed largefiles
730 getting changed largefiles
731 3 largefiles updated, 0 removed
731 3 largefiles updated, 0 removed
732 $ cd g
732 $ cd g
733 $ hg transplant -s ../d 598410d3eb9a
733 $ hg transplant -s ../d 598410d3eb9a
734 searching for changes
734 searching for changes
735 searching for changes
735 searching for changes
736 adding changesets
736 adding changesets
737 adding manifests
737 adding manifests
738 adding file changes
738 adding file changes
739 added 1 changesets with 2 changes to 2 files
739 added 1 changesets with 2 changes to 2 files
740 getting changed largefiles
740 getting changed largefiles
741 1 largefiles updated, 0 removed
741 1 largefiles updated, 0 removed
742 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
742 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
743 9:598410d3eb9a modify normal file largefile in repo d
743 9:598410d3eb9a modify normal file largefile in repo d
744 8:a381d2c8c80e modify normal file and largefile in repo b
744 8:a381d2c8c80e modify normal file and largefile in repo b
745 7:daea875e9014 add/edit more largefiles
745 7:daea875e9014 add/edit more largefiles
746 6:4355d653f84f edit files yet again
746 6:4355d653f84f edit files yet again
747 5:9d5af5072dbd edit files again
747 5:9d5af5072dbd edit files again
748 4:74c02385b94c move files
748 4:74c02385b94c move files
749 3:9e8fbc4bce62 copy files
749 3:9e8fbc4bce62 copy files
750 2:51a0ae4d5864 remove files
750 2:51a0ae4d5864 remove files
751 1:ce8896473775 edit files
751 1:ce8896473775 edit files
752 0:30d30fe6a5be add files
752 0:30d30fe6a5be add files
753 $ cat normal3
753 $ cat normal3
754 normal3-modified
754 normal3-modified
755 $ cat sub/normal4
755 $ cat sub/normal4
756 normal4-modified
756 normal4-modified
757 $ cat sub/large4
757 $ cat sub/large4
758 large4-modified
758 large4-modified
759 $ cat sub2/large6
759 $ cat sub2/large6
760 large6-modified
760 large6-modified
761 $ cat sub2/large7
761 $ cat sub2/large7
762 large7
762 large7
763
763
764 Cat a largefile
765 $ hg cat normal3
766 normal3-modified
767 $ hg cat sub/large4
768 large4-modified
769 $ rm ${USERCACHE}/*
770 $ hg cat -r a381d2c8c80e -o cat.out sub/large4
771 $ cat cat.out
772 large4-modified
773 $ rm cat.out
774 $ hg cat -r a381d2c8c80e normal3
775 normal3-modified
776
764 Test that renaming a largefile results in correct output for status
777 Test that renaming a largefile results in correct output for status
765
778
766 $ hg rename sub/large4 large4-renamed
779 $ hg rename sub/large4 large4-renamed
767 $ hg commit -m "test rename output"
780 $ hg commit -m "test rename output"
768 Invoking status precommit hook
781 Invoking status precommit hook
769 A large4-renamed
782 A large4-renamed
770 R sub/large4
783 R sub/large4
771 $ cat large4-renamed
784 $ cat large4-renamed
772 large4-modified
785 large4-modified
773 $ cd sub2
786 $ cd sub2
774 $ hg rename large6 large6-renamed
787 $ hg rename large6 large6-renamed
775 $ hg st
788 $ hg st
776 A sub2/large6-renamed
789 A sub2/large6-renamed
777 R sub2/large6
790 R sub2/large6
778 $ cd ..
791 $ cd ..
779
792
780 Test --normal flag
793 Test --normal flag
781
794
782 $ dd if=/dev/urandom bs=2k count=11k > new-largefile 2> /dev/null
795 $ dd if=/dev/urandom bs=2k count=11k > new-largefile 2> /dev/null
783 $ hg add --normal --large new-largefile
796 $ hg add --normal --large new-largefile
784 abort: --normal cannot be used with --large
797 abort: --normal cannot be used with --large
785 [255]
798 [255]
786 $ hg add --normal new-largefile
799 $ hg add --normal new-largefile
787 new-largefile: up to 69 MB of RAM may be required to manage this file
800 new-largefile: up to 69 MB of RAM may be required to manage this file
788 (use 'hg revert new-largefile' to cancel the pending addition)
801 (use 'hg revert new-largefile' to cancel the pending addition)
789 $ cd ..
802 $ cd ..
790
803
791 vanilla clients not locked out from largefiles servers on vanilla repos
804 vanilla clients not locked out from largefiles servers on vanilla repos
792 $ mkdir r1
805 $ mkdir r1
793 $ cd r1
806 $ cd r1
794 $ hg init
807 $ hg init
795 $ echo c1 > f1
808 $ echo c1 > f1
796 $ hg add f1
809 $ hg add f1
797 $ hg commit -m "m1"
810 $ hg commit -m "m1"
798 Invoking status precommit hook
811 Invoking status precommit hook
799 A f1
812 A f1
800 $ cd ..
813 $ cd ..
801 $ hg serve -R r1 -d -p $HGPORT --pid-file hg.pid
814 $ hg serve -R r1 -d -p $HGPORT --pid-file hg.pid
802 $ cat hg.pid >> $DAEMON_PIDS
815 $ cat hg.pid >> $DAEMON_PIDS
803 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT r2
816 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT r2
804 requesting all changes
817 requesting all changes
805 adding changesets
818 adding changesets
806 adding manifests
819 adding manifests
807 adding file changes
820 adding file changes
808 added 1 changesets with 1 changes to 1 files
821 added 1 changesets with 1 changes to 1 files
809 updating to branch default
822 updating to branch default
810 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
823 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
811
824
812 largefiles clients still work with vanilla servers
825 largefiles clients still work with vanilla servers
813 $ hg --config extensions.largefiles=! serve -R r1 -d -p $HGPORT1 --pid-file hg.pid
826 $ hg --config extensions.largefiles=! serve -R r1 -d -p $HGPORT1 --pid-file hg.pid
814 $ cat hg.pid >> $DAEMON_PIDS
827 $ cat hg.pid >> $DAEMON_PIDS
815 $ hg clone http://localhost:$HGPORT1 r3
828 $ hg clone http://localhost:$HGPORT1 r3
816 requesting all changes
829 requesting all changes
817 adding changesets
830 adding changesets
818 adding manifests
831 adding manifests
819 adding file changes
832 adding file changes
820 added 1 changesets with 1 changes to 1 files
833 added 1 changesets with 1 changes to 1 files
821 updating to branch default
834 updating to branch default
822 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
835 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
823
836
824 vanilla clients locked out from largefiles http repos
837 vanilla clients locked out from largefiles http repos
825 $ mkdir r4
838 $ mkdir r4
826 $ cd r4
839 $ cd r4
827 $ hg init
840 $ hg init
828 $ echo c1 > f1
841 $ echo c1 > f1
829 $ hg add --large f1
842 $ hg add --large f1
830 $ hg commit -m "m1"
843 $ hg commit -m "m1"
831 Invoking status precommit hook
844 Invoking status precommit hook
832 A f1
845 A f1
833 $ cd ..
846 $ cd ..
834 $ hg serve -R r4 -d -p $HGPORT2 --pid-file hg.pid
847 $ hg serve -R r4 -d -p $HGPORT2 --pid-file hg.pid
835 $ cat hg.pid >> $DAEMON_PIDS
848 $ cat hg.pid >> $DAEMON_PIDS
836 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT2 r5
849 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT2 r5
837 abort: remote error:
850 abort: remote error:
838
851
839 This repository uses the largefiles extension.
852 This repository uses the largefiles extension.
840
853
841 Please enable it in your Mercurial config file.
854 Please enable it in your Mercurial config file.
842 [255]
855 [255]
843
856
844 used all HGPORTs, kill all daemons
857 used all HGPORTs, kill all daemons
845 $ "$TESTDIR/killdaemons.py"
858 $ "$TESTDIR/killdaemons.py"
846
859
847 vanilla clients locked out from largefiles ssh repos
860 vanilla clients locked out from largefiles ssh repos
848 $ hg --config extensions.largefiles=! clone -e 'python "$TESTDIR/dummyssh"' ssh://user@dummy/r4 r5
861 $ hg --config extensions.largefiles=! clone -e 'python "$TESTDIR/dummyssh"' ssh://user@dummy/r4 r5
849 abort: remote error:
862 abort: remote error:
850
863
851 This repository uses the largefiles extension.
864 This repository uses the largefiles extension.
852
865
853 Please enable it in your Mercurial config file.
866 Please enable it in your Mercurial config file.
854 [255]
867 [255]
855
868
856 largefiles clients refuse to push largefiles repos to vanilla servers
869 largefiles clients refuse to push largefiles repos to vanilla servers
857 $ mkdir r6
870 $ mkdir r6
858 $ cd r6
871 $ cd r6
859 $ hg init
872 $ hg init
860 $ echo c1 > f1
873 $ echo c1 > f1
861 $ hg add f1
874 $ hg add f1
862 $ hg commit -m "m1"
875 $ hg commit -m "m1"
863 Invoking status precommit hook
876 Invoking status precommit hook
864 A f1
877 A f1
865 $ cat >> .hg/hgrc <<!
878 $ cat >> .hg/hgrc <<!
866 > [web]
879 > [web]
867 > push_ssl = false
880 > push_ssl = false
868 > allow_push = *
881 > allow_push = *
869 > !
882 > !
870 $ cd ..
883 $ cd ..
871 $ hg clone r6 r7
884 $ hg clone r6 r7
872 updating to branch default
885 updating to branch default
873 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
886 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
874 $ cd r7
887 $ cd r7
875 $ echo c2 > f2
888 $ echo c2 > f2
876 $ hg add --large f2
889 $ hg add --large f2
877 $ hg commit -m "m2"
890 $ hg commit -m "m2"
878 Invoking status precommit hook
891 Invoking status precommit hook
879 A f2
892 A f2
880 $ hg --config extensions.largefiles=! -R ../r6 serve -d -p $HGPORT --pid-file ../hg.pid
893 $ hg --config extensions.largefiles=! -R ../r6 serve -d -p $HGPORT --pid-file ../hg.pid
881 $ cat ../hg.pid >> $DAEMON_PIDS
894 $ cat ../hg.pid >> $DAEMON_PIDS
882 $ hg push http://localhost:$HGPORT
895 $ hg push http://localhost:$HGPORT
883 pushing to http://localhost:$HGPORT/
896 pushing to http://localhost:$HGPORT/
884 searching for changes
897 searching for changes
885 abort: http://localhost:$HGPORT/ does not appear to be a largefile store
898 abort: http://localhost:$HGPORT/ does not appear to be a largefile store
886 [255]
899 [255]
887 $ cd ..
900 $ cd ..
888
901
889 putlfile errors are shown (issue3123)
902 putlfile errors are shown (issue3123)
890 Corrupt the cached largefile in r7
903 Corrupt the cached largefile in r7
891 $ echo corruption > $USERCACHE/4cdac4d8b084d0b599525cf732437fb337d422a8
904 $ echo corruption > $USERCACHE/4cdac4d8b084d0b599525cf732437fb337d422a8
892 $ hg init empty
905 $ hg init empty
893 $ hg serve -R empty -d -p $HGPORT1 --pid-file hg.pid \
906 $ hg serve -R empty -d -p $HGPORT1 --pid-file hg.pid \
894 > --config 'web.allow_push=*' --config web.push_ssl=False
907 > --config 'web.allow_push=*' --config web.push_ssl=False
895 $ cat hg.pid >> $DAEMON_PIDS
908 $ cat hg.pid >> $DAEMON_PIDS
896 $ hg push -R r7 http://localhost:$HGPORT1
909 $ hg push -R r7 http://localhost:$HGPORT1
897 pushing to http://localhost:$HGPORT1/
910 pushing to http://localhost:$HGPORT1/
898 searching for changes
911 searching for changes
899 remote: largefiles: failed to put 4cdac4d8b084d0b599525cf732437fb337d422a8 into store: largefile contents do not match hash
912 remote: largefiles: failed to put 4cdac4d8b084d0b599525cf732437fb337d422a8 into store: largefile contents do not match hash
900 abort: remotestore: could not put $TESTTMP/r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 to remote store http://localhost:$HGPORT1/
913 abort: remotestore: could not put $TESTTMP/r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 to remote store http://localhost:$HGPORT1/
901 [255]
914 [255]
902 $ rm -rf empty
915 $ rm -rf empty
903
916
904 Clone a local repository owned by another user
917 Clone a local repository owned by another user
905 We have to simulate that here by setting $HOME and removing write permissions
918 We have to simulate that here by setting $HOME and removing write permissions
906 $ ORIGHOME="$HOME"
919 $ ORIGHOME="$HOME"
907 $ mkdir alice
920 $ mkdir alice
908 $ HOME="`pwd`/alice"
921 $ HOME="`pwd`/alice"
909 $ cd alice
922 $ cd alice
910 $ hg init pubrepo
923 $ hg init pubrepo
911 $ cd pubrepo
924 $ cd pubrepo
912 $ dd if=/dev/urandom bs=1k count=11k > a-large-file 2> /dev/null
925 $ dd if=/dev/urandom bs=1k count=11k > a-large-file 2> /dev/null
913 $ hg add --large a-large-file
926 $ hg add --large a-large-file
914 $ hg commit -m "Add a large file"
927 $ hg commit -m "Add a large file"
915 Invoking status precommit hook
928 Invoking status precommit hook
916 A a-large-file
929 A a-large-file
917 $ cd ..
930 $ cd ..
918 $ chmod -R a-w pubrepo
931 $ chmod -R a-w pubrepo
919 $ cd ..
932 $ cd ..
920 $ mkdir bob
933 $ mkdir bob
921 $ HOME="`pwd`/bob"
934 $ HOME="`pwd`/bob"
922 $ cd bob
935 $ cd bob
923 $ hg clone --pull ../alice/pubrepo pubrepo
936 $ hg clone --pull ../alice/pubrepo pubrepo
924 requesting all changes
937 requesting all changes
925 adding changesets
938 adding changesets
926 adding manifests
939 adding manifests
927 adding file changes
940 adding file changes
928 added 1 changesets with 1 changes to 1 files
941 added 1 changesets with 1 changes to 1 files
929 updating to branch default
942 updating to branch default
930 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
943 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
931 getting changed largefiles
944 getting changed largefiles
932 1 largefiles updated, 0 removed
945 1 largefiles updated, 0 removed
933 $ cd ..
946 $ cd ..
934 $ chmod -R u+w alice/pubrepo
947 $ chmod -R u+w alice/pubrepo
935 $ HOME="$ORIGHOME"
948 $ HOME="$ORIGHOME"
936
949
937 Symlink to a large largefile should behave the same as a symlink to a normal file
950 Symlink to a large largefile should behave the same as a symlink to a normal file
938 $ hg init largesymlink
951 $ hg init largesymlink
939 $ cd largesymlink
952 $ cd largesymlink
940 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
953 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
941 $ hg add --large largefile
954 $ hg add --large largefile
942 $ hg commit -m "commit a large file"
955 $ hg commit -m "commit a large file"
943 Invoking status precommit hook
956 Invoking status precommit hook
944 A largefile
957 A largefile
945 $ ln -s largefile largelink
958 $ ln -s largefile largelink
946 $ hg add largelink
959 $ hg add largelink
947 $ hg commit -m "commit a large symlink"
960 $ hg commit -m "commit a large symlink"
948 Invoking status precommit hook
961 Invoking status precommit hook
949 A largelink
962 A largelink
950 $ rm -f largelink
963 $ rm -f largelink
951 $ hg up >/dev/null
964 $ hg up >/dev/null
952 $ test -f largelink
965 $ test -f largelink
953 [1]
966 [1]
954 $ test -L largelink
967 $ test -L largelink
955 [1]
968 [1]
956 $ rm -f largelink # make next part of the test independent of the previous
969 $ rm -f largelink # make next part of the test independent of the previous
957 $ hg up -C >/dev/null
970 $ hg up -C >/dev/null
958 $ test -f largelink
971 $ test -f largelink
959 $ test -L largelink
972 $ test -L largelink
960 $ cd ..
973 $ cd ..
961
974
962 test for pattern matching on 'hg status':
975 test for pattern matching on 'hg status':
963 to boost performance, largefiles checks whether specified patterns are
976 to boost performance, largefiles checks whether specified patterns are
964 related to largefiles in working directory (NOT to STANDIN) or not.
977 related to largefiles in working directory (NOT to STANDIN) or not.
965
978
966 $ hg init statusmatch
979 $ hg init statusmatch
967 $ cd statusmatch
980 $ cd statusmatch
968
981
969 $ mkdir -p a/b/c/d
982 $ mkdir -p a/b/c/d
970 $ echo normal > a/b/c/d/e.normal.txt
983 $ echo normal > a/b/c/d/e.normal.txt
971 $ hg add a/b/c/d/e.normal.txt
984 $ hg add a/b/c/d/e.normal.txt
972 $ echo large > a/b/c/d/e.large.txt
985 $ echo large > a/b/c/d/e.large.txt
973 $ hg add --large a/b/c/d/e.large.txt
986 $ hg add --large a/b/c/d/e.large.txt
974 $ mkdir -p a/b/c/x
987 $ mkdir -p a/b/c/x
975 $ echo normal > a/b/c/x/y.normal.txt
988 $ echo normal > a/b/c/x/y.normal.txt
976 $ hg add a/b/c/x/y.normal.txt
989 $ hg add a/b/c/x/y.normal.txt
977 $ hg commit -m 'add files'
990 $ hg commit -m 'add files'
978 Invoking status precommit hook
991 Invoking status precommit hook
979 A a/b/c/d/e.large.txt
992 A a/b/c/d/e.large.txt
980 A a/b/c/d/e.normal.txt
993 A a/b/c/d/e.normal.txt
981 A a/b/c/x/y.normal.txt
994 A a/b/c/x/y.normal.txt
982
995
983 (1) no pattern: no performance boost
996 (1) no pattern: no performance boost
984 $ hg status -A
997 $ hg status -A
985 C a/b/c/d/e.large.txt
998 C a/b/c/d/e.large.txt
986 C a/b/c/d/e.normal.txt
999 C a/b/c/d/e.normal.txt
987 C a/b/c/x/y.normal.txt
1000 C a/b/c/x/y.normal.txt
988
1001
989 (2) pattern not related to largefiles: performance boost
1002 (2) pattern not related to largefiles: performance boost
990 $ hg status -A a/b/c/x
1003 $ hg status -A a/b/c/x
991 C a/b/c/x/y.normal.txt
1004 C a/b/c/x/y.normal.txt
992
1005
993 (3) pattern related to largefiles: no performance boost
1006 (3) pattern related to largefiles: no performance boost
994 $ hg status -A a/b/c/d
1007 $ hg status -A a/b/c/d
995 C a/b/c/d/e.large.txt
1008 C a/b/c/d/e.large.txt
996 C a/b/c/d/e.normal.txt
1009 C a/b/c/d/e.normal.txt
997
1010
998 (4) pattern related to STANDIN (not to largefiles): performance boost
1011 (4) pattern related to STANDIN (not to largefiles): performance boost
999 $ hg status -A .hglf/a
1012 $ hg status -A .hglf/a
1000 C .hglf/a/b/c/d/e.large.txt
1013 C .hglf/a/b/c/d/e.large.txt
1001
1014
1002 (5) mixed case: no performance boost
1015 (5) mixed case: no performance boost
1003 $ hg status -A a/b/c/x a/b/c/d
1016 $ hg status -A a/b/c/x a/b/c/d
1004 C a/b/c/d/e.large.txt
1017 C a/b/c/d/e.large.txt
1005 C a/b/c/d/e.normal.txt
1018 C a/b/c/d/e.normal.txt
1006 C a/b/c/x/y.normal.txt
1019 C a/b/c/x/y.normal.txt
1007
1020
1008 verify that largefiles doesn't break filesets
1021 verify that largefiles doesn't break filesets
1009
1022
1010 $ hg log --rev . --exclude "set:binary()"
1023 $ hg log --rev . --exclude "set:binary()"
1011 changeset: 0:41bd42f10efa
1024 changeset: 0:41bd42f10efa
1012 tag: tip
1025 tag: tip
1013 user: test
1026 user: test
1014 date: Thu Jan 01 00:00:00 1970 +0000
1027 date: Thu Jan 01 00:00:00 1970 +0000
1015 summary: add files
1028 summary: add files
1016
1029
1017
1030
1018 $ cd ..
1031 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now