##// END OF EJS Templates
largefiles: ignore hidden changesets with 'verify --large --lfa'...
Matt Harbison -
r25508:b8fd605b default
parent child Browse files
Show More
@@ -1,547 +1,545 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''High-level command function for lfconvert, plus the cmdtable.'''
9 '''High-level command function for lfconvert, plus the cmdtable.'''
10
10
11 import os, errno
11 import os, errno
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, \
15 cmdutil, scmutil, commands
15 cmdutil, scmutil, commands
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17 from mercurial.lock import release
17 from mercurial.lock import release
18
18
19 from hgext.convert import convcmd
19 from hgext.convert import convcmd
20 from hgext.convert import filemap
20 from hgext.convert import filemap
21
21
22 import lfutil
22 import lfutil
23 import basestore
23 import basestore
24
24
25 # -- Commands ----------------------------------------------------------
25 # -- Commands ----------------------------------------------------------
26
26
27 cmdtable = {}
27 cmdtable = {}
28 command = cmdutil.command(cmdtable)
28 command = cmdutil.command(cmdtable)
29
29
30 @command('lfconvert',
30 @command('lfconvert',
31 [('s', 'size', '',
31 [('s', 'size', '',
32 _('minimum size (MB) for files to be converted as largefiles'), 'SIZE'),
32 _('minimum size (MB) for files to be converted as largefiles'), 'SIZE'),
33 ('', 'to-normal', False,
33 ('', 'to-normal', False,
34 _('convert from a largefiles repo to a normal repo')),
34 _('convert from a largefiles repo to a normal repo')),
35 ],
35 ],
36 _('hg lfconvert SOURCE DEST [FILE ...]'),
36 _('hg lfconvert SOURCE DEST [FILE ...]'),
37 norepo=True,
37 norepo=True,
38 inferrepo=True)
38 inferrepo=True)
39 def lfconvert(ui, src, dest, *pats, **opts):
39 def lfconvert(ui, src, dest, *pats, **opts):
40 '''convert a normal repository to a largefiles repository
40 '''convert a normal repository to a largefiles repository
41
41
42 Convert repository SOURCE to a new repository DEST, identical to
42 Convert repository SOURCE to a new repository DEST, identical to
43 SOURCE except that certain files will be converted as largefiles:
43 SOURCE except that certain files will be converted as largefiles:
44 specifically, any file that matches any PATTERN *or* whose size is
44 specifically, any file that matches any PATTERN *or* whose size is
45 above the minimum size threshold is converted as a largefile. The
45 above the minimum size threshold is converted as a largefile. The
46 size used to determine whether or not to track a file as a
46 size used to determine whether or not to track a file as a
47 largefile is the size of the first version of the file. The
47 largefile is the size of the first version of the file. The
48 minimum size can be specified either with --size or in
48 minimum size can be specified either with --size or in
49 configuration as ``largefiles.size``.
49 configuration as ``largefiles.size``.
50
50
51 After running this command you will need to make sure that
51 After running this command you will need to make sure that
52 largefiles is enabled anywhere you intend to push the new
52 largefiles is enabled anywhere you intend to push the new
53 repository.
53 repository.
54
54
55 Use --to-normal to convert largefiles back to normal files; after
55 Use --to-normal to convert largefiles back to normal files; after
56 this, the DEST repository can be used without largefiles at all.'''
56 this, the DEST repository can be used without largefiles at all.'''
57
57
58 if opts['to_normal']:
58 if opts['to_normal']:
59 tolfile = False
59 tolfile = False
60 else:
60 else:
61 tolfile = True
61 tolfile = True
62 size = lfutil.getminsize(ui, True, opts.get('size'), default=None)
62 size = lfutil.getminsize(ui, True, opts.get('size'), default=None)
63
63
64 if not hg.islocal(src):
64 if not hg.islocal(src):
65 raise util.Abort(_('%s is not a local Mercurial repo') % src)
65 raise util.Abort(_('%s is not a local Mercurial repo') % src)
66 if not hg.islocal(dest):
66 if not hg.islocal(dest):
67 raise util.Abort(_('%s is not a local Mercurial repo') % dest)
67 raise util.Abort(_('%s is not a local Mercurial repo') % dest)
68
68
69 rsrc = hg.repository(ui, src)
69 rsrc = hg.repository(ui, src)
70 ui.status(_('initializing destination %s\n') % dest)
70 ui.status(_('initializing destination %s\n') % dest)
71 rdst = hg.repository(ui, dest, create=True)
71 rdst = hg.repository(ui, dest, create=True)
72
72
73 success = False
73 success = False
74 dstwlock = dstlock = None
74 dstwlock = dstlock = None
75 try:
75 try:
76 # Get a list of all changesets in the source. The easy way to do this
76 # Get a list of all changesets in the source. The easy way to do this
77 # is to simply walk the changelog, using changelog.nodesbetween().
77 # is to simply walk the changelog, using changelog.nodesbetween().
78 # Take a look at mercurial/revlog.py:639 for more details.
78 # Take a look at mercurial/revlog.py:639 for more details.
79 # Use a generator instead of a list to decrease memory usage
79 # Use a generator instead of a list to decrease memory usage
80 ctxs = (rsrc[ctx] for ctx in rsrc.changelog.nodesbetween(None,
80 ctxs = (rsrc[ctx] for ctx in rsrc.changelog.nodesbetween(None,
81 rsrc.heads())[0])
81 rsrc.heads())[0])
82 revmap = {node.nullid: node.nullid}
82 revmap = {node.nullid: node.nullid}
83 if tolfile:
83 if tolfile:
84 # Lock destination to prevent modification while it is converted to.
84 # Lock destination to prevent modification while it is converted to.
85 # Don't need to lock src because we are just reading from its
85 # Don't need to lock src because we are just reading from its
86 # history which can't change.
86 # history which can't change.
87 dstwlock = rdst.wlock()
87 dstwlock = rdst.wlock()
88 dstlock = rdst.lock()
88 dstlock = rdst.lock()
89
89
90 lfiles = set()
90 lfiles = set()
91 normalfiles = set()
91 normalfiles = set()
92 if not pats:
92 if not pats:
93 pats = ui.configlist(lfutil.longname, 'patterns', default=[])
93 pats = ui.configlist(lfutil.longname, 'patterns', default=[])
94 if pats:
94 if pats:
95 matcher = match_.match(rsrc.root, '', list(pats))
95 matcher = match_.match(rsrc.root, '', list(pats))
96 else:
96 else:
97 matcher = None
97 matcher = None
98
98
99 lfiletohash = {}
99 lfiletohash = {}
100 for ctx in ctxs:
100 for ctx in ctxs:
101 ui.progress(_('converting revisions'), ctx.rev(),
101 ui.progress(_('converting revisions'), ctx.rev(),
102 unit=_('revision'), total=rsrc['tip'].rev())
102 unit=_('revision'), total=rsrc['tip'].rev())
103 _lfconvert_addchangeset(rsrc, rdst, ctx, revmap,
103 _lfconvert_addchangeset(rsrc, rdst, ctx, revmap,
104 lfiles, normalfiles, matcher, size, lfiletohash)
104 lfiles, normalfiles, matcher, size, lfiletohash)
105 ui.progress(_('converting revisions'), None)
105 ui.progress(_('converting revisions'), None)
106
106
107 if os.path.exists(rdst.wjoin(lfutil.shortname)):
107 if os.path.exists(rdst.wjoin(lfutil.shortname)):
108 shutil.rmtree(rdst.wjoin(lfutil.shortname))
108 shutil.rmtree(rdst.wjoin(lfutil.shortname))
109
109
110 for f in lfiletohash.keys():
110 for f in lfiletohash.keys():
111 if os.path.isfile(rdst.wjoin(f)):
111 if os.path.isfile(rdst.wjoin(f)):
112 os.unlink(rdst.wjoin(f))
112 os.unlink(rdst.wjoin(f))
113 try:
113 try:
114 os.removedirs(os.path.dirname(rdst.wjoin(f)))
114 os.removedirs(os.path.dirname(rdst.wjoin(f)))
115 except OSError:
115 except OSError:
116 pass
116 pass
117
117
118 # If there were any files converted to largefiles, add largefiles
118 # If there were any files converted to largefiles, add largefiles
119 # to the destination repository's requirements.
119 # to the destination repository's requirements.
120 if lfiles:
120 if lfiles:
121 rdst.requirements.add('largefiles')
121 rdst.requirements.add('largefiles')
122 rdst._writerequirements()
122 rdst._writerequirements()
123 else:
123 else:
124 class lfsource(filemap.filemap_source):
124 class lfsource(filemap.filemap_source):
125 def __init__(self, ui, source):
125 def __init__(self, ui, source):
126 super(lfsource, self).__init__(ui, source, None)
126 super(lfsource, self).__init__(ui, source, None)
127 self.filemapper.rename[lfutil.shortname] = '.'
127 self.filemapper.rename[lfutil.shortname] = '.'
128
128
129 def getfile(self, name, rev):
129 def getfile(self, name, rev):
130 realname, realrev = rev
130 realname, realrev = rev
131 f = super(lfsource, self).getfile(name, rev)
131 f = super(lfsource, self).getfile(name, rev)
132
132
133 if (not realname.startswith(lfutil.shortnameslash)
133 if (not realname.startswith(lfutil.shortnameslash)
134 or f[0] is None):
134 or f[0] is None):
135 return f
135 return f
136
136
137 # Substitute in the largefile data for the hash
137 # Substitute in the largefile data for the hash
138 hash = f[0].strip()
138 hash = f[0].strip()
139 path = lfutil.findfile(rsrc, hash)
139 path = lfutil.findfile(rsrc, hash)
140
140
141 if path is None:
141 if path is None:
142 raise util.Abort(_("missing largefile for \'%s\' in %s")
142 raise util.Abort(_("missing largefile for \'%s\' in %s")
143 % (realname, realrev))
143 % (realname, realrev))
144 fp = open(path, 'rb')
144 fp = open(path, 'rb')
145
145
146 try:
146 try:
147 return (fp.read(), f[1])
147 return (fp.read(), f[1])
148 finally:
148 finally:
149 fp.close()
149 fp.close()
150
150
151 class converter(convcmd.converter):
151 class converter(convcmd.converter):
152 def __init__(self, ui, source, dest, revmapfile, opts):
152 def __init__(self, ui, source, dest, revmapfile, opts):
153 src = lfsource(ui, source)
153 src = lfsource(ui, source)
154
154
155 super(converter, self).__init__(ui, src, dest, revmapfile,
155 super(converter, self).__init__(ui, src, dest, revmapfile,
156 opts)
156 opts)
157
157
158 found, missing = downloadlfiles(ui, rsrc)
158 found, missing = downloadlfiles(ui, rsrc)
159 if missing != 0:
159 if missing != 0:
160 raise util.Abort(_("all largefiles must be present locally"))
160 raise util.Abort(_("all largefiles must be present locally"))
161
161
162 convcmd.converter = converter
162 convcmd.converter = converter
163 convcmd.convert(ui, src, dest)
163 convcmd.convert(ui, src, dest)
164 success = True
164 success = True
165 finally:
165 finally:
166 if tolfile:
166 if tolfile:
167 rdst.dirstate.clear()
167 rdst.dirstate.clear()
168 release(dstlock, dstwlock)
168 release(dstlock, dstwlock)
169 if not success:
169 if not success:
170 # we failed, remove the new directory
170 # we failed, remove the new directory
171 shutil.rmtree(rdst.root)
171 shutil.rmtree(rdst.root)
172
172
173 def _lfconvert_addchangeset(rsrc, rdst, ctx, revmap, lfiles, normalfiles,
173 def _lfconvert_addchangeset(rsrc, rdst, ctx, revmap, lfiles, normalfiles,
174 matcher, size, lfiletohash):
174 matcher, size, lfiletohash):
175 # Convert src parents to dst parents
175 # Convert src parents to dst parents
176 parents = _convertparents(ctx, revmap)
176 parents = _convertparents(ctx, revmap)
177
177
178 # Generate list of changed files
178 # Generate list of changed files
179 files = _getchangedfiles(ctx, parents)
179 files = _getchangedfiles(ctx, parents)
180
180
181 dstfiles = []
181 dstfiles = []
182 for f in files:
182 for f in files:
183 if f not in lfiles and f not in normalfiles:
183 if f not in lfiles and f not in normalfiles:
184 islfile = _islfile(f, ctx, matcher, size)
184 islfile = _islfile(f, ctx, matcher, size)
185 # If this file was renamed or copied then copy
185 # If this file was renamed or copied then copy
186 # the largefile-ness of its predecessor
186 # the largefile-ness of its predecessor
187 if f in ctx.manifest():
187 if f in ctx.manifest():
188 fctx = ctx.filectx(f)
188 fctx = ctx.filectx(f)
189 renamed = fctx.renamed()
189 renamed = fctx.renamed()
190 renamedlfile = renamed and renamed[0] in lfiles
190 renamedlfile = renamed and renamed[0] in lfiles
191 islfile |= renamedlfile
191 islfile |= renamedlfile
192 if 'l' in fctx.flags():
192 if 'l' in fctx.flags():
193 if renamedlfile:
193 if renamedlfile:
194 raise util.Abort(
194 raise util.Abort(
195 _('renamed/copied largefile %s becomes symlink')
195 _('renamed/copied largefile %s becomes symlink')
196 % f)
196 % f)
197 islfile = False
197 islfile = False
198 if islfile:
198 if islfile:
199 lfiles.add(f)
199 lfiles.add(f)
200 else:
200 else:
201 normalfiles.add(f)
201 normalfiles.add(f)
202
202
203 if f in lfiles:
203 if f in lfiles:
204 dstfiles.append(lfutil.standin(f))
204 dstfiles.append(lfutil.standin(f))
205 # largefile in manifest if it has not been removed/renamed
205 # largefile in manifest if it has not been removed/renamed
206 if f in ctx.manifest():
206 if f in ctx.manifest():
207 fctx = ctx.filectx(f)
207 fctx = ctx.filectx(f)
208 if 'l' in fctx.flags():
208 if 'l' in fctx.flags():
209 renamed = fctx.renamed()
209 renamed = fctx.renamed()
210 if renamed and renamed[0] in lfiles:
210 if renamed and renamed[0] in lfiles:
211 raise util.Abort(_('largefile %s becomes symlink') % f)
211 raise util.Abort(_('largefile %s becomes symlink') % f)
212
212
213 # largefile was modified, update standins
213 # largefile was modified, update standins
214 m = util.sha1('')
214 m = util.sha1('')
215 m.update(ctx[f].data())
215 m.update(ctx[f].data())
216 hash = m.hexdigest()
216 hash = m.hexdigest()
217 if f not in lfiletohash or lfiletohash[f] != hash:
217 if f not in lfiletohash or lfiletohash[f] != hash:
218 rdst.wwrite(f, ctx[f].data(), ctx[f].flags())
218 rdst.wwrite(f, ctx[f].data(), ctx[f].flags())
219 executable = 'x' in ctx[f].flags()
219 executable = 'x' in ctx[f].flags()
220 lfutil.writestandin(rdst, lfutil.standin(f), hash,
220 lfutil.writestandin(rdst, lfutil.standin(f), hash,
221 executable)
221 executable)
222 lfiletohash[f] = hash
222 lfiletohash[f] = hash
223 else:
223 else:
224 # normal file
224 # normal file
225 dstfiles.append(f)
225 dstfiles.append(f)
226
226
227 def getfilectx(repo, memctx, f):
227 def getfilectx(repo, memctx, f):
228 if lfutil.isstandin(f):
228 if lfutil.isstandin(f):
229 # if the file isn't in the manifest then it was removed
229 # if the file isn't in the manifest then it was removed
230 # or renamed, raise IOError to indicate this
230 # or renamed, raise IOError to indicate this
231 srcfname = lfutil.splitstandin(f)
231 srcfname = lfutil.splitstandin(f)
232 try:
232 try:
233 fctx = ctx.filectx(srcfname)
233 fctx = ctx.filectx(srcfname)
234 except error.LookupError:
234 except error.LookupError:
235 return None
235 return None
236 renamed = fctx.renamed()
236 renamed = fctx.renamed()
237 if renamed:
237 if renamed:
238 # standin is always a largefile because largefile-ness
238 # standin is always a largefile because largefile-ness
239 # doesn't change after rename or copy
239 # doesn't change after rename or copy
240 renamed = lfutil.standin(renamed[0])
240 renamed = lfutil.standin(renamed[0])
241
241
242 return context.memfilectx(repo, f, lfiletohash[srcfname] + '\n',
242 return context.memfilectx(repo, f, lfiletohash[srcfname] + '\n',
243 'l' in fctx.flags(), 'x' in fctx.flags(),
243 'l' in fctx.flags(), 'x' in fctx.flags(),
244 renamed)
244 renamed)
245 else:
245 else:
246 return _getnormalcontext(repo, ctx, f, revmap)
246 return _getnormalcontext(repo, ctx, f, revmap)
247
247
248 # Commit
248 # Commit
249 _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap)
249 _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap)
250
250
251 def _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap):
251 def _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap):
252 mctx = context.memctx(rdst, parents, ctx.description(), dstfiles,
252 mctx = context.memctx(rdst, parents, ctx.description(), dstfiles,
253 getfilectx, ctx.user(), ctx.date(), ctx.extra())
253 getfilectx, ctx.user(), ctx.date(), ctx.extra())
254 ret = rdst.commitctx(mctx)
254 ret = rdst.commitctx(mctx)
255 lfutil.copyalltostore(rdst, ret)
255 lfutil.copyalltostore(rdst, ret)
256 rdst.setparents(ret)
256 rdst.setparents(ret)
257 revmap[ctx.node()] = rdst.changelog.tip()
257 revmap[ctx.node()] = rdst.changelog.tip()
258
258
259 # Generate list of changed files
259 # Generate list of changed files
260 def _getchangedfiles(ctx, parents):
260 def _getchangedfiles(ctx, parents):
261 files = set(ctx.files())
261 files = set(ctx.files())
262 if node.nullid not in parents:
262 if node.nullid not in parents:
263 mc = ctx.manifest()
263 mc = ctx.manifest()
264 mp1 = ctx.parents()[0].manifest()
264 mp1 = ctx.parents()[0].manifest()
265 mp2 = ctx.parents()[1].manifest()
265 mp2 = ctx.parents()[1].manifest()
266 files |= (set(mp1) | set(mp2)) - set(mc)
266 files |= (set(mp1) | set(mp2)) - set(mc)
267 for f in mc:
267 for f in mc:
268 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
268 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
269 files.add(f)
269 files.add(f)
270 return files
270 return files
271
271
272 # Convert src parents to dst parents
272 # Convert src parents to dst parents
273 def _convertparents(ctx, revmap):
273 def _convertparents(ctx, revmap):
274 parents = []
274 parents = []
275 for p in ctx.parents():
275 for p in ctx.parents():
276 parents.append(revmap[p.node()])
276 parents.append(revmap[p.node()])
277 while len(parents) < 2:
277 while len(parents) < 2:
278 parents.append(node.nullid)
278 parents.append(node.nullid)
279 return parents
279 return parents
280
280
281 # Get memfilectx for a normal file
281 # Get memfilectx for a normal file
282 def _getnormalcontext(repo, ctx, f, revmap):
282 def _getnormalcontext(repo, ctx, f, revmap):
283 try:
283 try:
284 fctx = ctx.filectx(f)
284 fctx = ctx.filectx(f)
285 except error.LookupError:
285 except error.LookupError:
286 return None
286 return None
287 renamed = fctx.renamed()
287 renamed = fctx.renamed()
288 if renamed:
288 if renamed:
289 renamed = renamed[0]
289 renamed = renamed[0]
290
290
291 data = fctx.data()
291 data = fctx.data()
292 if f == '.hgtags':
292 if f == '.hgtags':
293 data = _converttags (repo.ui, revmap, data)
293 data = _converttags (repo.ui, revmap, data)
294 return context.memfilectx(repo, f, data, 'l' in fctx.flags(),
294 return context.memfilectx(repo, f, data, 'l' in fctx.flags(),
295 'x' in fctx.flags(), renamed)
295 'x' in fctx.flags(), renamed)
296
296
297 # Remap tag data using a revision map
297 # Remap tag data using a revision map
298 def _converttags(ui, revmap, data):
298 def _converttags(ui, revmap, data):
299 newdata = []
299 newdata = []
300 for line in data.splitlines():
300 for line in data.splitlines():
301 try:
301 try:
302 id, name = line.split(' ', 1)
302 id, name = line.split(' ', 1)
303 except ValueError:
303 except ValueError:
304 ui.warn(_('skipping incorrectly formatted tag %s\n')
304 ui.warn(_('skipping incorrectly formatted tag %s\n')
305 % line)
305 % line)
306 continue
306 continue
307 try:
307 try:
308 newid = node.bin(id)
308 newid = node.bin(id)
309 except TypeError:
309 except TypeError:
310 ui.warn(_('skipping incorrectly formatted id %s\n')
310 ui.warn(_('skipping incorrectly formatted id %s\n')
311 % id)
311 % id)
312 continue
312 continue
313 try:
313 try:
314 newdata.append('%s %s\n' % (node.hex(revmap[newid]),
314 newdata.append('%s %s\n' % (node.hex(revmap[newid]),
315 name))
315 name))
316 except KeyError:
316 except KeyError:
317 ui.warn(_('no mapping for id %s\n') % id)
317 ui.warn(_('no mapping for id %s\n') % id)
318 continue
318 continue
319 return ''.join(newdata)
319 return ''.join(newdata)
320
320
321 def _islfile(file, ctx, matcher, size):
321 def _islfile(file, ctx, matcher, size):
322 '''Return true if file should be considered a largefile, i.e.
322 '''Return true if file should be considered a largefile, i.e.
323 matcher matches it or it is larger than size.'''
323 matcher matches it or it is larger than size.'''
324 # never store special .hg* files as largefiles
324 # never store special .hg* files as largefiles
325 if file == '.hgtags' or file == '.hgignore' or file == '.hgsigs':
325 if file == '.hgtags' or file == '.hgignore' or file == '.hgsigs':
326 return False
326 return False
327 if matcher and matcher(file):
327 if matcher and matcher(file):
328 return True
328 return True
329 try:
329 try:
330 return ctx.filectx(file).size() >= size * 1024 * 1024
330 return ctx.filectx(file).size() >= size * 1024 * 1024
331 except error.LookupError:
331 except error.LookupError:
332 return False
332 return False
333
333
334 def uploadlfiles(ui, rsrc, rdst, files):
334 def uploadlfiles(ui, rsrc, rdst, files):
335 '''upload largefiles to the central store'''
335 '''upload largefiles to the central store'''
336
336
337 if not files:
337 if not files:
338 return
338 return
339
339
340 store = basestore._openstore(rsrc, rdst, put=True)
340 store = basestore._openstore(rsrc, rdst, put=True)
341
341
342 at = 0
342 at = 0
343 ui.debug("sending statlfile command for %d largefiles\n" % len(files))
343 ui.debug("sending statlfile command for %d largefiles\n" % len(files))
344 retval = store.exists(files)
344 retval = store.exists(files)
345 files = filter(lambda h: not retval[h], files)
345 files = filter(lambda h: not retval[h], files)
346 ui.debug("%d largefiles need to be uploaded\n" % len(files))
346 ui.debug("%d largefiles need to be uploaded\n" % len(files))
347
347
348 for hash in files:
348 for hash in files:
349 ui.progress(_('uploading largefiles'), at, unit='largefile',
349 ui.progress(_('uploading largefiles'), at, unit='largefile',
350 total=len(files))
350 total=len(files))
351 source = lfutil.findfile(rsrc, hash)
351 source = lfutil.findfile(rsrc, hash)
352 if not source:
352 if not source:
353 raise util.Abort(_('largefile %s missing from store'
353 raise util.Abort(_('largefile %s missing from store'
354 ' (needs to be uploaded)') % hash)
354 ' (needs to be uploaded)') % hash)
355 # XXX check for errors here
355 # XXX check for errors here
356 store.put(source, hash)
356 store.put(source, hash)
357 at += 1
357 at += 1
358 ui.progress(_('uploading largefiles'), None)
358 ui.progress(_('uploading largefiles'), None)
359
359
360 def verifylfiles(ui, repo, all=False, contents=False):
360 def verifylfiles(ui, repo, all=False, contents=False):
361 '''Verify that every largefile revision in the current changeset
361 '''Verify that every largefile revision in the current changeset
362 exists in the central store. With --contents, also verify that
362 exists in the central store. With --contents, also verify that
363 the contents of each local largefile file revision are correct (SHA-1 hash
363 the contents of each local largefile file revision are correct (SHA-1 hash
364 matches the revision ID). With --all, check every changeset in
364 matches the revision ID). With --all, check every changeset in
365 this repository.'''
365 this repository.'''
366 if all:
366 if all:
367 # Pass a list to the function rather than an iterator because we know a
367 revs = repo.revs('all()')
368 # list will work.
369 revs = range(len(repo))
370 else:
368 else:
371 revs = ['.']
369 revs = ['.']
372
370
373 store = basestore._openstore(repo)
371 store = basestore._openstore(repo)
374 return store.verify(revs, contents=contents)
372 return store.verify(revs, contents=contents)
375
373
376 def cachelfiles(ui, repo, node, filelist=None):
374 def cachelfiles(ui, repo, node, filelist=None):
377 '''cachelfiles ensures that all largefiles needed by the specified revision
375 '''cachelfiles ensures that all largefiles needed by the specified revision
378 are present in the repository's largefile cache.
376 are present in the repository's largefile cache.
379
377
380 returns a tuple (cached, missing). cached is the list of files downloaded
378 returns a tuple (cached, missing). cached is the list of files downloaded
381 by this operation; missing is the list of files that were needed but could
379 by this operation; missing is the list of files that were needed but could
382 not be found.'''
380 not be found.'''
383 lfiles = lfutil.listlfiles(repo, node)
381 lfiles = lfutil.listlfiles(repo, node)
384 if filelist:
382 if filelist:
385 lfiles = set(lfiles) & set(filelist)
383 lfiles = set(lfiles) & set(filelist)
386 toget = []
384 toget = []
387
385
388 for lfile in lfiles:
386 for lfile in lfiles:
389 try:
387 try:
390 expectedhash = repo[node][lfutil.standin(lfile)].data().strip()
388 expectedhash = repo[node][lfutil.standin(lfile)].data().strip()
391 except IOError, err:
389 except IOError, err:
392 if err.errno == errno.ENOENT:
390 if err.errno == errno.ENOENT:
393 continue # node must be None and standin wasn't found in wctx
391 continue # node must be None and standin wasn't found in wctx
394 raise
392 raise
395 if not lfutil.findfile(repo, expectedhash):
393 if not lfutil.findfile(repo, expectedhash):
396 toget.append((lfile, expectedhash))
394 toget.append((lfile, expectedhash))
397
395
398 if toget:
396 if toget:
399 store = basestore._openstore(repo)
397 store = basestore._openstore(repo)
400 ret = store.get(toget)
398 ret = store.get(toget)
401 return ret
399 return ret
402
400
403 return ([], [])
401 return ([], [])
404
402
405 def downloadlfiles(ui, repo, rev=None):
403 def downloadlfiles(ui, repo, rev=None):
406 matchfn = scmutil.match(repo[None],
404 matchfn = scmutil.match(repo[None],
407 [repo.wjoin(lfutil.shortname)], {})
405 [repo.wjoin(lfutil.shortname)], {})
408 def prepare(ctx, fns):
406 def prepare(ctx, fns):
409 pass
407 pass
410 totalsuccess = 0
408 totalsuccess = 0
411 totalmissing = 0
409 totalmissing = 0
412 if rev != []: # walkchangerevs on empty list would return all revs
410 if rev != []: # walkchangerevs on empty list would return all revs
413 for ctx in cmdutil.walkchangerevs(repo, matchfn, {'rev' : rev},
411 for ctx in cmdutil.walkchangerevs(repo, matchfn, {'rev' : rev},
414 prepare):
412 prepare):
415 success, missing = cachelfiles(ui, repo, ctx.node())
413 success, missing = cachelfiles(ui, repo, ctx.node())
416 totalsuccess += len(success)
414 totalsuccess += len(success)
417 totalmissing += len(missing)
415 totalmissing += len(missing)
418 ui.status(_("%d additional largefiles cached\n") % totalsuccess)
416 ui.status(_("%d additional largefiles cached\n") % totalsuccess)
419 if totalmissing > 0:
417 if totalmissing > 0:
420 ui.status(_("%d largefiles failed to download\n") % totalmissing)
418 ui.status(_("%d largefiles failed to download\n") % totalmissing)
421 return totalsuccess, totalmissing
419 return totalsuccess, totalmissing
422
420
423 def updatelfiles(ui, repo, filelist=None, printmessage=None,
421 def updatelfiles(ui, repo, filelist=None, printmessage=None,
424 normallookup=False):
422 normallookup=False):
425 '''Update largefiles according to standins in the working directory
423 '''Update largefiles according to standins in the working directory
426
424
427 If ``printmessage`` is other than ``None``, it means "print (or
425 If ``printmessage`` is other than ``None``, it means "print (or
428 ignore, for false) message forcibly".
426 ignore, for false) message forcibly".
429 '''
427 '''
430 statuswriter = lfutil.getstatuswriter(ui, repo, printmessage)
428 statuswriter = lfutil.getstatuswriter(ui, repo, printmessage)
431 wlock = repo.wlock()
429 wlock = repo.wlock()
432 try:
430 try:
433 lfdirstate = lfutil.openlfdirstate(ui, repo)
431 lfdirstate = lfutil.openlfdirstate(ui, repo)
434 lfiles = set(lfutil.listlfiles(repo)) | set(lfdirstate)
432 lfiles = set(lfutil.listlfiles(repo)) | set(lfdirstate)
435
433
436 if filelist is not None:
434 if filelist is not None:
437 filelist = set(filelist)
435 filelist = set(filelist)
438 lfiles = [f for f in lfiles if f in filelist]
436 lfiles = [f for f in lfiles if f in filelist]
439
437
440 update = {}
438 update = {}
441 updated, removed = 0, 0
439 updated, removed = 0, 0
442 for lfile in lfiles:
440 for lfile in lfiles:
443 abslfile = repo.wjoin(lfile)
441 abslfile = repo.wjoin(lfile)
444 absstandin = repo.wjoin(lfutil.standin(lfile))
442 absstandin = repo.wjoin(lfutil.standin(lfile))
445 if os.path.exists(absstandin):
443 if os.path.exists(absstandin):
446 if (os.path.exists(absstandin + '.orig') and
444 if (os.path.exists(absstandin + '.orig') and
447 os.path.exists(abslfile)):
445 os.path.exists(abslfile)):
448 shutil.copyfile(abslfile, abslfile + '.orig')
446 shutil.copyfile(abslfile, abslfile + '.orig')
449 util.unlinkpath(absstandin + '.orig')
447 util.unlinkpath(absstandin + '.orig')
450 expecthash = lfutil.readstandin(repo, lfile)
448 expecthash = lfutil.readstandin(repo, lfile)
451 if expecthash != '':
449 if expecthash != '':
452 if lfile not in repo[None]: # not switched to normal file
450 if lfile not in repo[None]: # not switched to normal file
453 util.unlinkpath(abslfile, ignoremissing=True)
451 util.unlinkpath(abslfile, ignoremissing=True)
454 # use normallookup() to allocate an entry in largefiles
452 # use normallookup() to allocate an entry in largefiles
455 # dirstate to prevent lfilesrepo.status() from reporting
453 # dirstate to prevent lfilesrepo.status() from reporting
456 # missing files as removed.
454 # missing files as removed.
457 lfdirstate.normallookup(lfile)
455 lfdirstate.normallookup(lfile)
458 update[lfile] = expecthash
456 update[lfile] = expecthash
459 else:
457 else:
460 # Remove lfiles for which the standin is deleted, unless the
458 # Remove lfiles for which the standin is deleted, unless the
461 # lfile is added to the repository again. This happens when a
459 # lfile is added to the repository again. This happens when a
462 # largefile is converted back to a normal file: the standin
460 # largefile is converted back to a normal file: the standin
463 # disappears, but a new (normal) file appears as the lfile.
461 # disappears, but a new (normal) file appears as the lfile.
464 if (os.path.exists(abslfile) and
462 if (os.path.exists(abslfile) and
465 repo.dirstate.normalize(lfile) not in repo[None]):
463 repo.dirstate.normalize(lfile) not in repo[None]):
466 util.unlinkpath(abslfile)
464 util.unlinkpath(abslfile)
467 removed += 1
465 removed += 1
468
466
469 # largefile processing might be slow and be interrupted - be prepared
467 # largefile processing might be slow and be interrupted - be prepared
470 lfdirstate.write()
468 lfdirstate.write()
471
469
472 if lfiles:
470 if lfiles:
473 statuswriter(_('getting changed largefiles\n'))
471 statuswriter(_('getting changed largefiles\n'))
474 cachelfiles(ui, repo, None, lfiles)
472 cachelfiles(ui, repo, None, lfiles)
475
473
476 for lfile in lfiles:
474 for lfile in lfiles:
477 update1 = 0
475 update1 = 0
478
476
479 expecthash = update.get(lfile)
477 expecthash = update.get(lfile)
480 if expecthash:
478 if expecthash:
481 if not lfutil.copyfromcache(repo, expecthash, lfile):
479 if not lfutil.copyfromcache(repo, expecthash, lfile):
482 # failed ... but already removed and set to normallookup
480 # failed ... but already removed and set to normallookup
483 continue
481 continue
484 # Synchronize largefile dirstate to the last modified
482 # Synchronize largefile dirstate to the last modified
485 # time of the file
483 # time of the file
486 lfdirstate.normal(lfile)
484 lfdirstate.normal(lfile)
487 update1 = 1
485 update1 = 1
488
486
489 # copy the state of largefile standin from the repository's
487 # copy the state of largefile standin from the repository's
490 # dirstate to its state in the lfdirstate.
488 # dirstate to its state in the lfdirstate.
491 abslfile = repo.wjoin(lfile)
489 abslfile = repo.wjoin(lfile)
492 absstandin = repo.wjoin(lfutil.standin(lfile))
490 absstandin = repo.wjoin(lfutil.standin(lfile))
493 if os.path.exists(absstandin):
491 if os.path.exists(absstandin):
494 mode = os.stat(absstandin).st_mode
492 mode = os.stat(absstandin).st_mode
495 if mode != os.stat(abslfile).st_mode:
493 if mode != os.stat(abslfile).st_mode:
496 os.chmod(abslfile, mode)
494 os.chmod(abslfile, mode)
497 update1 = 1
495 update1 = 1
498
496
499 updated += update1
497 updated += update1
500
498
501 lfutil.synclfdirstate(repo, lfdirstate, lfile, normallookup)
499 lfutil.synclfdirstate(repo, lfdirstate, lfile, normallookup)
502
500
503 lfdirstate.write()
501 lfdirstate.write()
504 if lfiles:
502 if lfiles:
505 statuswriter(_('%d largefiles updated, %d removed\n') % (updated,
503 statuswriter(_('%d largefiles updated, %d removed\n') % (updated,
506 removed))
504 removed))
507 finally:
505 finally:
508 wlock.release()
506 wlock.release()
509
507
510 @command('lfpull',
508 @command('lfpull',
511 [('r', 'rev', [], _('pull largefiles for these revisions'))
509 [('r', 'rev', [], _('pull largefiles for these revisions'))
512 ] + commands.remoteopts,
510 ] + commands.remoteopts,
513 _('-r REV... [-e CMD] [--remotecmd CMD] [SOURCE]'))
511 _('-r REV... [-e CMD] [--remotecmd CMD] [SOURCE]'))
514 def lfpull(ui, repo, source="default", **opts):
512 def lfpull(ui, repo, source="default", **opts):
515 """pull largefiles for the specified revisions from the specified source
513 """pull largefiles for the specified revisions from the specified source
516
514
517 Pull largefiles that are referenced from local changesets but missing
515 Pull largefiles that are referenced from local changesets but missing
518 locally, pulling from a remote repository to the local cache.
516 locally, pulling from a remote repository to the local cache.
519
517
520 If SOURCE is omitted, the 'default' path will be used.
518 If SOURCE is omitted, the 'default' path will be used.
521 See :hg:`help urls` for more information.
519 See :hg:`help urls` for more information.
522
520
523 .. container:: verbose
521 .. container:: verbose
524
522
525 Some examples:
523 Some examples:
526
524
527 - pull largefiles for all branch heads::
525 - pull largefiles for all branch heads::
528
526
529 hg lfpull -r "head() and not closed()"
527 hg lfpull -r "head() and not closed()"
530
528
531 - pull largefiles on the default branch::
529 - pull largefiles on the default branch::
532
530
533 hg lfpull -r "branch(default)"
531 hg lfpull -r "branch(default)"
534 """
532 """
535 repo.lfpullsource = source
533 repo.lfpullsource = source
536
534
537 revs = opts.get('rev', [])
535 revs = opts.get('rev', [])
538 if not revs:
536 if not revs:
539 raise util.Abort(_('no revisions specified'))
537 raise util.Abort(_('no revisions specified'))
540 revs = scmutil.revrange(repo, revs)
538 revs = scmutil.revrange(repo, revs)
541
539
542 numcached = 0
540 numcached = 0
543 for rev in revs:
541 for rev in revs:
544 ui.note(_('pulling largefiles for revision %s\n') % rev)
542 ui.note(_('pulling largefiles for revision %s\n') % rev)
545 (cached, missing) = cachelfiles(ui, repo, rev)
543 (cached, missing) = cachelfiles(ui, repo, rev)
546 numcached += len(cached)
544 numcached += len(cached)
547 ui.status(_("%d largefiles cached\n") % numcached)
545 ui.status(_("%d largefiles cached\n") % numcached)
@@ -1,385 +1,393 b''
1 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
1 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
2 $ mkdir "${USERCACHE}"
2 $ mkdir "${USERCACHE}"
3 $ cat >> $HGRCPATH <<EOF
3 $ cat >> $HGRCPATH <<EOF
4 > [extensions]
4 > [extensions]
5 > largefiles =
5 > largefiles =
6 > share =
6 > share =
7 > strip =
7 > strip =
8 > convert =
8 > convert =
9 > [largefiles]
9 > [largefiles]
10 > minsize = 0.5
10 > minsize = 0.5
11 > patterns = **.other
11 > patterns = **.other
12 > **.dat
12 > **.dat
13 > usercache=${USERCACHE}
13 > usercache=${USERCACHE}
14 > EOF
14 > EOF
15
15
16 "lfconvert" works
16 "lfconvert" works
17 $ hg init bigfile-repo
17 $ hg init bigfile-repo
18 $ cd bigfile-repo
18 $ cd bigfile-repo
19 $ cat >> .hg/hgrc <<EOF
19 $ cat >> .hg/hgrc <<EOF
20 > [extensions]
20 > [extensions]
21 > largefiles = !
21 > largefiles = !
22 > EOF
22 > EOF
23 $ mkdir sub
23 $ mkdir sub
24 $ dd if=/dev/zero bs=1k count=256 > large 2> /dev/null
24 $ dd if=/dev/zero bs=1k count=256 > large 2> /dev/null
25 $ dd if=/dev/zero bs=1k count=256 > large2 2> /dev/null
25 $ dd if=/dev/zero bs=1k count=256 > large2 2> /dev/null
26 $ echo normal > normal1
26 $ echo normal > normal1
27 $ echo alsonormal > sub/normal2
27 $ echo alsonormal > sub/normal2
28 $ dd if=/dev/zero bs=1k count=10 > sub/maybelarge.dat 2> /dev/null
28 $ dd if=/dev/zero bs=1k count=10 > sub/maybelarge.dat 2> /dev/null
29 $ hg addremove
29 $ hg addremove
30 adding large
30 adding large
31 adding large2
31 adding large2
32 adding normal1
32 adding normal1
33 adding sub/maybelarge.dat
33 adding sub/maybelarge.dat
34 adding sub/normal2
34 adding sub/normal2
35 $ hg commit -m"add large, normal1" large normal1
35 $ hg commit -m"add large, normal1" large normal1
36 $ hg commit -m"add sub/*" sub
36 $ hg commit -m"add sub/*" sub
37
37
38 Test tag parsing
38 Test tag parsing
39 $ cat >> .hgtags <<EOF
39 $ cat >> .hgtags <<EOF
40 > IncorrectlyFormattedTag!
40 > IncorrectlyFormattedTag!
41 > invalidhash sometag
41 > invalidhash sometag
42 > 0123456789abcdef anothertag
42 > 0123456789abcdef anothertag
43 > EOF
43 > EOF
44 $ hg add .hgtags
44 $ hg add .hgtags
45 $ hg commit -m"add large2" large2 .hgtags
45 $ hg commit -m"add large2" large2 .hgtags
46
46
47 Test link+rename largefile codepath
47 Test link+rename largefile codepath
48 $ [ -d .hg/largefiles ] && echo fail || echo pass
48 $ [ -d .hg/largefiles ] && echo fail || echo pass
49 pass
49 pass
50 $ cd ..
50 $ cd ..
51 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo
51 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo
52 initializing destination largefiles-repo
52 initializing destination largefiles-repo
53 skipping incorrectly formatted tag IncorrectlyFormattedTag!
53 skipping incorrectly formatted tag IncorrectlyFormattedTag!
54 skipping incorrectly formatted id invalidhash
54 skipping incorrectly formatted id invalidhash
55 no mapping for id 0123456789abcdef
55 no mapping for id 0123456789abcdef
56 #if symlink
56 #if symlink
57 $ hg --cwd bigfile-repo rename large2 large3
57 $ hg --cwd bigfile-repo rename large2 large3
58 $ ln -sf large bigfile-repo/large3
58 $ ln -sf large bigfile-repo/large3
59 $ hg --cwd bigfile-repo commit -m"make large2 a symlink" large2 large3
59 $ hg --cwd bigfile-repo commit -m"make large2 a symlink" large2 large3
60 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo-symlink
60 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo-symlink
61 initializing destination largefiles-repo-symlink
61 initializing destination largefiles-repo-symlink
62 skipping incorrectly formatted tag IncorrectlyFormattedTag!
62 skipping incorrectly formatted tag IncorrectlyFormattedTag!
63 skipping incorrectly formatted id invalidhash
63 skipping incorrectly formatted id invalidhash
64 no mapping for id 0123456789abcdef
64 no mapping for id 0123456789abcdef
65 abort: renamed/copied largefile large3 becomes symlink
65 abort: renamed/copied largefile large3 becomes symlink
66 [255]
66 [255]
67 #endif
67 #endif
68 $ cd bigfile-repo
68 $ cd bigfile-repo
69 $ hg strip --no-backup 2
69 $ hg strip --no-backup 2
70 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
70 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
71 $ cd ..
71 $ cd ..
72 $ rm -rf largefiles-repo largefiles-repo-symlink
72 $ rm -rf largefiles-repo largefiles-repo-symlink
73
73
74 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo
74 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo
75 initializing destination largefiles-repo
75 initializing destination largefiles-repo
76
76
77 "lfconvert" converts content correctly
77 "lfconvert" converts content correctly
78 $ cd largefiles-repo
78 $ cd largefiles-repo
79 $ hg up
79 $ hg up
80 getting changed largefiles
80 getting changed largefiles
81 2 largefiles updated, 0 removed
81 2 largefiles updated, 0 removed
82 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
82 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
83 $ hg locate
83 $ hg locate
84 .hglf/large
84 .hglf/large
85 .hglf/sub/maybelarge.dat
85 .hglf/sub/maybelarge.dat
86 normal1
86 normal1
87 sub/normal2
87 sub/normal2
88 $ cat normal1
88 $ cat normal1
89 normal
89 normal
90 $ cat sub/normal2
90 $ cat sub/normal2
91 alsonormal
91 alsonormal
92 $ md5sum.py large sub/maybelarge.dat
92 $ md5sum.py large sub/maybelarge.dat
93 ec87a838931d4d5d2e94a04644788a55 large
93 ec87a838931d4d5d2e94a04644788a55 large
94 1276481102f218c981e0324180bafd9f sub/maybelarge.dat
94 1276481102f218c981e0324180bafd9f sub/maybelarge.dat
95
95
96 "lfconvert" adds 'largefiles' to .hg/requires.
96 "lfconvert" adds 'largefiles' to .hg/requires.
97 $ cat .hg/requires
97 $ cat .hg/requires
98 dotencode
98 dotencode
99 fncache
99 fncache
100 largefiles
100 largefiles
101 revlogv1
101 revlogv1
102 store
102 store
103
103
104 "lfconvert" includes a newline at the end of the standin files.
104 "lfconvert" includes a newline at the end of the standin files.
105 $ cat .hglf/large .hglf/sub/maybelarge.dat
105 $ cat .hglf/large .hglf/sub/maybelarge.dat
106 2e000fa7e85759c7f4c254d4d9c33ef481e459a7
106 2e000fa7e85759c7f4c254d4d9c33ef481e459a7
107 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c
107 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c
108 $ cd ..
108 $ cd ..
109
109
110 add some changesets to rename/remove/merge
110 add some changesets to rename/remove/merge
111 $ cd bigfile-repo
111 $ cd bigfile-repo
112 $ hg mv -q sub stuff
112 $ hg mv -q sub stuff
113 $ hg commit -m"rename sub/ to stuff/"
113 $ hg commit -m"rename sub/ to stuff/"
114 $ hg update -q 1
114 $ hg update -q 1
115 $ echo blah >> normal3
115 $ echo blah >> normal3
116 $ echo blah >> sub/normal2
116 $ echo blah >> sub/normal2
117 $ echo blah >> sub/maybelarge.dat
117 $ echo blah >> sub/maybelarge.dat
118 $ md5sum.py sub/maybelarge.dat
118 $ md5sum.py sub/maybelarge.dat
119 1dd0b99ff80e19cff409702a1d3f5e15 sub/maybelarge.dat
119 1dd0b99ff80e19cff409702a1d3f5e15 sub/maybelarge.dat
120 $ hg commit -A -m"add normal3, modify sub/*"
120 $ hg commit -A -m"add normal3, modify sub/*"
121 adding normal3
121 adding normal3
122 created new head
122 created new head
123 $ hg rm large normal3
123 $ hg rm large normal3
124 $ hg commit -q -m"remove large, normal3"
124 $ hg commit -q -m"remove large, normal3"
125 $ hg merge
125 $ hg merge
126 merging sub/maybelarge.dat and stuff/maybelarge.dat to stuff/maybelarge.dat
126 merging sub/maybelarge.dat and stuff/maybelarge.dat to stuff/maybelarge.dat
127 warning: $TESTTMP/bigfile-repo/stuff/maybelarge.dat looks like a binary file. (glob)
127 warning: $TESTTMP/bigfile-repo/stuff/maybelarge.dat looks like a binary file. (glob)
128 merging stuff/maybelarge.dat incomplete! (edit conflicts, then use 'hg resolve --mark')
128 merging stuff/maybelarge.dat incomplete! (edit conflicts, then use 'hg resolve --mark')
129 merging sub/normal2 and stuff/normal2 to stuff/normal2
129 merging sub/normal2 and stuff/normal2 to stuff/normal2
130 0 files updated, 1 files merged, 0 files removed, 1 files unresolved
130 0 files updated, 1 files merged, 0 files removed, 1 files unresolved
131 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
131 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
132 [1]
132 [1]
133 $ hg cat -r . sub/maybelarge.dat > stuff/maybelarge.dat
133 $ hg cat -r . sub/maybelarge.dat > stuff/maybelarge.dat
134 $ hg resolve -m stuff/maybelarge.dat
134 $ hg resolve -m stuff/maybelarge.dat
135 (no more unresolved files)
135 (no more unresolved files)
136 $ hg commit -m"merge"
136 $ hg commit -m"merge"
137 $ hg log -G --template "{rev}:{node|short} {desc|firstline}\n"
137 $ hg log -G --template "{rev}:{node|short} {desc|firstline}\n"
138 @ 5:4884f215abda merge
138 @ 5:4884f215abda merge
139 |\
139 |\
140 | o 4:7285f817b77e remove large, normal3
140 | o 4:7285f817b77e remove large, normal3
141 | |
141 | |
142 | o 3:67e3892e3534 add normal3, modify sub/*
142 | o 3:67e3892e3534 add normal3, modify sub/*
143 | |
143 | |
144 o | 2:c96c8beb5d56 rename sub/ to stuff/
144 o | 2:c96c8beb5d56 rename sub/ to stuff/
145 |/
145 |/
146 o 1:020c65d24e11 add sub/*
146 o 1:020c65d24e11 add sub/*
147 |
147 |
148 o 0:117b8328f97a add large, normal1
148 o 0:117b8328f97a add large, normal1
149
149
150 $ cd ..
150 $ cd ..
151
151
152 lfconvert with rename, merge, and remove
152 lfconvert with rename, merge, and remove
153 $ rm -rf largefiles-repo
153 $ rm -rf largefiles-repo
154 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo
154 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo
155 initializing destination largefiles-repo
155 initializing destination largefiles-repo
156 $ cd largefiles-repo
156 $ cd largefiles-repo
157 $ hg log -G --template "{rev}:{node|short} {desc|firstline}\n"
157 $ hg log -G --template "{rev}:{node|short} {desc|firstline}\n"
158 o 5:8e05f5f2b77e merge
158 o 5:8e05f5f2b77e merge
159 |\
159 |\
160 | o 4:a5a02de7a8e4 remove large, normal3
160 | o 4:a5a02de7a8e4 remove large, normal3
161 | |
161 | |
162 | o 3:55759520c76f add normal3, modify sub/*
162 | o 3:55759520c76f add normal3, modify sub/*
163 | |
163 | |
164 o | 2:261ad3f3f037 rename sub/ to stuff/
164 o | 2:261ad3f3f037 rename sub/ to stuff/
165 |/
165 |/
166 o 1:334e5237836d add sub/*
166 o 1:334e5237836d add sub/*
167 |
167 |
168 o 0:d4892ec57ce2 add large, normal1
168 o 0:d4892ec57ce2 add large, normal1
169
169
170 $ hg locate -r 2
170 $ hg locate -r 2
171 .hglf/large
171 .hglf/large
172 .hglf/stuff/maybelarge.dat
172 .hglf/stuff/maybelarge.dat
173 normal1
173 normal1
174 stuff/normal2
174 stuff/normal2
175 $ hg locate -r 3
175 $ hg locate -r 3
176 .hglf/large
176 .hglf/large
177 .hglf/sub/maybelarge.dat
177 .hglf/sub/maybelarge.dat
178 normal1
178 normal1
179 normal3
179 normal3
180 sub/normal2
180 sub/normal2
181 $ hg locate -r 4
181 $ hg locate -r 4
182 .hglf/sub/maybelarge.dat
182 .hglf/sub/maybelarge.dat
183 normal1
183 normal1
184 sub/normal2
184 sub/normal2
185 $ hg locate -r 5
185 $ hg locate -r 5
186 .hglf/stuff/maybelarge.dat
186 .hglf/stuff/maybelarge.dat
187 normal1
187 normal1
188 stuff/normal2
188 stuff/normal2
189 $ hg update
189 $ hg update
190 getting changed largefiles
190 getting changed largefiles
191 1 largefiles updated, 0 removed
191 1 largefiles updated, 0 removed
192 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
192 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
193 $ cat stuff/normal2
193 $ cat stuff/normal2
194 alsonormal
194 alsonormal
195 blah
195 blah
196 $ md5sum.py stuff/maybelarge.dat
196 $ md5sum.py stuff/maybelarge.dat
197 1dd0b99ff80e19cff409702a1d3f5e15 stuff/maybelarge.dat
197 1dd0b99ff80e19cff409702a1d3f5e15 stuff/maybelarge.dat
198 $ cat .hglf/stuff/maybelarge.dat
198 $ cat .hglf/stuff/maybelarge.dat
199 76236b6a2c6102826c61af4297dd738fb3b1de38
199 76236b6a2c6102826c61af4297dd738fb3b1de38
200 $ cd ..
200 $ cd ..
201
201
202 "lfconvert" error cases
202 "lfconvert" error cases
203 $ hg lfconvert http://localhost/foo foo
203 $ hg lfconvert http://localhost/foo foo
204 abort: http://localhost/foo is not a local Mercurial repo
204 abort: http://localhost/foo is not a local Mercurial repo
205 [255]
205 [255]
206 $ hg lfconvert foo ssh://localhost/foo
206 $ hg lfconvert foo ssh://localhost/foo
207 abort: ssh://localhost/foo is not a local Mercurial repo
207 abort: ssh://localhost/foo is not a local Mercurial repo
208 [255]
208 [255]
209 $ hg lfconvert nosuchrepo foo
209 $ hg lfconvert nosuchrepo foo
210 abort: repository nosuchrepo not found!
210 abort: repository nosuchrepo not found!
211 [255]
211 [255]
212 $ hg share -q -U bigfile-repo shared
212 $ hg share -q -U bigfile-repo shared
213 $ printf 'bogus' > shared/.hg/sharedpath
213 $ printf 'bogus' > shared/.hg/sharedpath
214 $ hg lfconvert shared foo
214 $ hg lfconvert shared foo
215 abort: .hg/sharedpath points to nonexistent directory $TESTTMP/bogus! (glob)
215 abort: .hg/sharedpath points to nonexistent directory $TESTTMP/bogus! (glob)
216 [255]
216 [255]
217 $ hg lfconvert bigfile-repo largefiles-repo
217 $ hg lfconvert bigfile-repo largefiles-repo
218 initializing destination largefiles-repo
218 initializing destination largefiles-repo
219 abort: repository largefiles-repo already exists!
219 abort: repository largefiles-repo already exists!
220 [255]
220 [255]
221
221
222 add another largefile to the new largefiles repo
222 add another largefile to the new largefiles repo
223 $ cd largefiles-repo
223 $ cd largefiles-repo
224 $ dd if=/dev/zero bs=1k count=1k > anotherlarge 2> /dev/null
224 $ dd if=/dev/zero bs=1k count=1k > anotherlarge 2> /dev/null
225 $ hg add --lfsize=1 anotherlarge
225 $ hg add --lfsize=1 anotherlarge
226 $ hg commit -m "add anotherlarge (should be a largefile)"
226 $ hg commit -m "add anotherlarge (should be a largefile)"
227 $ cat .hglf/anotherlarge
227 $ cat .hglf/anotherlarge
228 3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3
228 3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3
229 $ hg tag mytag
229 $ hg tag mytag
230 $ cd ..
230 $ cd ..
231
231
232 round-trip: converting back to a normal (non-largefiles) repo with
232 round-trip: converting back to a normal (non-largefiles) repo with
233 "lfconvert --to-normal" should give the same as ../bigfile-repo
233 "lfconvert --to-normal" should give the same as ../bigfile-repo
234 $ cd largefiles-repo
234 $ cd largefiles-repo
235 $ hg lfconvert --to-normal . ../normal-repo
235 $ hg lfconvert --to-normal . ../normal-repo
236 initializing destination ../normal-repo
236 initializing destination ../normal-repo
237 0 additional largefiles cached
237 0 additional largefiles cached
238 scanning source...
238 scanning source...
239 sorting...
239 sorting...
240 converting...
240 converting...
241 7 add large, normal1
241 7 add large, normal1
242 6 add sub/*
242 6 add sub/*
243 5 rename sub/ to stuff/
243 5 rename sub/ to stuff/
244 4 add normal3, modify sub/*
244 4 add normal3, modify sub/*
245 3 remove large, normal3
245 3 remove large, normal3
246 2 merge
246 2 merge
247 1 add anotherlarge (should be a largefile)
247 1 add anotherlarge (should be a largefile)
248 0 Added tag mytag for changeset abacddda7028
248 0 Added tag mytag for changeset abacddda7028
249 $ cd ../normal-repo
249 $ cd ../normal-repo
250 $ cat >> .hg/hgrc <<EOF
250 $ cat >> .hg/hgrc <<EOF
251 > [extensions]
251 > [extensions]
252 > largefiles = !
252 > largefiles = !
253 > EOF
253 > EOF
254
254
255 $ hg log -G --template "{rev}:{node|short} {desc|firstline}\n"
255 $ hg log -G --template "{rev}:{node|short} {desc|firstline}\n"
256 o 7:b5fedc110b9d Added tag mytag for changeset 867ab992ecf4
256 o 7:b5fedc110b9d Added tag mytag for changeset 867ab992ecf4
257 |
257 |
258 o 6:867ab992ecf4 add anotherlarge (should be a largefile)
258 o 6:867ab992ecf4 add anotherlarge (should be a largefile)
259 |
259 |
260 o 5:4884f215abda merge
260 o 5:4884f215abda merge
261 |\
261 |\
262 | o 4:7285f817b77e remove large, normal3
262 | o 4:7285f817b77e remove large, normal3
263 | |
263 | |
264 | o 3:67e3892e3534 add normal3, modify sub/*
264 | o 3:67e3892e3534 add normal3, modify sub/*
265 | |
265 | |
266 o | 2:c96c8beb5d56 rename sub/ to stuff/
266 o | 2:c96c8beb5d56 rename sub/ to stuff/
267 |/
267 |/
268 o 1:020c65d24e11 add sub/*
268 o 1:020c65d24e11 add sub/*
269 |
269 |
270 o 0:117b8328f97a add large, normal1
270 o 0:117b8328f97a add large, normal1
271
271
272 $ hg update
272 $ hg update
273 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
273 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
274 $ hg locate
274 $ hg locate
275 .hgtags
275 .hgtags
276 anotherlarge
276 anotherlarge
277 normal1
277 normal1
278 stuff/maybelarge.dat
278 stuff/maybelarge.dat
279 stuff/normal2
279 stuff/normal2
280 $ [ -d .hg/largefiles ] && echo fail || echo pass
280 $ [ -d .hg/largefiles ] && echo fail || echo pass
281 pass
281 pass
282
282
283 $ cd ..
283 $ cd ..
284
284
285 Clearing the usercache ensures that commitctx doesn't try to cache largefiles
285 Clearing the usercache ensures that commitctx doesn't try to cache largefiles
286 from the working dir on a convert.
286 from the working dir on a convert.
287 $ rm "${USERCACHE}"/*
287 $ rm "${USERCACHE}"/*
288 $ hg convert largefiles-repo
288 $ hg convert largefiles-repo
289 assuming destination largefiles-repo-hg
289 assuming destination largefiles-repo-hg
290 initializing destination largefiles-repo-hg repository
290 initializing destination largefiles-repo-hg repository
291 scanning source...
291 scanning source...
292 sorting...
292 sorting...
293 converting...
293 converting...
294 7 add large, normal1
294 7 add large, normal1
295 6 add sub/*
295 6 add sub/*
296 5 rename sub/ to stuff/
296 5 rename sub/ to stuff/
297 4 add normal3, modify sub/*
297 4 add normal3, modify sub/*
298 3 remove large, normal3
298 3 remove large, normal3
299 2 merge
299 2 merge
300 1 add anotherlarge (should be a largefile)
300 1 add anotherlarge (should be a largefile)
301 0 Added tag mytag for changeset abacddda7028
301 0 Added tag mytag for changeset abacddda7028
302
302
303 $ hg -R largefiles-repo-hg log -G --template "{rev}:{node|short} {desc|firstline}\n"
303 $ hg -R largefiles-repo-hg log -G --template "{rev}:{node|short} {desc|firstline}\n"
304 o 7:2f08f66459b7 Added tag mytag for changeset 17126745edfd
304 o 7:2f08f66459b7 Added tag mytag for changeset 17126745edfd
305 |
305 |
306 o 6:17126745edfd add anotherlarge (should be a largefile)
306 o 6:17126745edfd add anotherlarge (should be a largefile)
307 |
307 |
308 o 5:9cc5aa7204f0 merge
308 o 5:9cc5aa7204f0 merge
309 |\
309 |\
310 | o 4:a5a02de7a8e4 remove large, normal3
310 | o 4:a5a02de7a8e4 remove large, normal3
311 | |
311 | |
312 | o 3:55759520c76f add normal3, modify sub/*
312 | o 3:55759520c76f add normal3, modify sub/*
313 | |
313 | |
314 o | 2:261ad3f3f037 rename sub/ to stuff/
314 o | 2:261ad3f3f037 rename sub/ to stuff/
315 |/
315 |/
316 o 1:334e5237836d add sub/*
316 o 1:334e5237836d add sub/*
317 |
317 |
318 o 0:d4892ec57ce2 add large, normal1
318 o 0:d4892ec57ce2 add large, normal1
319
319
320 Verify will fail (for now) if the usercache is purged before converting, since
320 Verify will fail (for now) if the usercache is purged before converting, since
321 largefiles are not cached in the converted repo's local store by the conversion
321 largefiles are not cached in the converted repo's local store by the conversion
322 process.
322 process.
323 $ cd largefiles-repo-hg
324 $ cat >> .hg/hgrc <<EOF
325 > [experimental]
326 > evolution=createmarkers
327 > EOF
328 $ hg debugobsolete `hg log -r tip -T "{node}"`
329 $ cd ..
330
323 $ hg -R largefiles-repo-hg verify --large --lfa
331 $ hg -R largefiles-repo-hg verify --large --lfa
324 checking changesets
332 checking changesets
325 checking manifests
333 checking manifests
326 crosschecking files in changesets and manifests
334 crosschecking files in changesets and manifests
327 checking files
335 checking files
328 9 files, 8 changesets, 13 total revisions
336 9 files, 8 changesets, 13 total revisions
329 searching 8 changesets for largefiles
337 searching 7 changesets for largefiles
330 changeset 0:d4892ec57ce2: large references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/2e000fa7e85759c7f4c254d4d9c33ef481e459a7 (glob)
338 changeset 0:d4892ec57ce2: large references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/2e000fa7e85759c7f4c254d4d9c33ef481e459a7 (glob)
331 changeset 1:334e5237836d: sub/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c (glob)
339 changeset 1:334e5237836d: sub/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c (glob)
332 changeset 2:261ad3f3f037: stuff/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c (glob)
340 changeset 2:261ad3f3f037: stuff/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c (glob)
333 changeset 3:55759520c76f: sub/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/76236b6a2c6102826c61af4297dd738fb3b1de38 (glob)
341 changeset 3:55759520c76f: sub/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/76236b6a2c6102826c61af4297dd738fb3b1de38 (glob)
334 changeset 5:9cc5aa7204f0: stuff/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/76236b6a2c6102826c61af4297dd738fb3b1de38 (glob)
342 changeset 5:9cc5aa7204f0: stuff/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/76236b6a2c6102826c61af4297dd738fb3b1de38 (glob)
335 changeset 6:17126745edfd: anotherlarge references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3 (glob)
343 changeset 6:17126745edfd: anotherlarge references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3 (glob)
336 verified existence of 6 revisions of 4 largefiles
344 verified existence of 6 revisions of 4 largefiles
337 [1]
345 [1]
338 $ hg -R largefiles-repo-hg showconfig paths
346 $ hg -R largefiles-repo-hg showconfig paths
339 [1]
347 [1]
340
348
341
349
342 Avoid a traceback if a largefile isn't available (issue3519)
350 Avoid a traceback if a largefile isn't available (issue3519)
343
351
344 Ensure the largefile can be cached in the source if necessary
352 Ensure the largefile can be cached in the source if necessary
345 $ hg clone -U largefiles-repo issue3519
353 $ hg clone -U largefiles-repo issue3519
346 $ rm -f "${USERCACHE}"/*
354 $ rm -f "${USERCACHE}"/*
347 $ hg lfconvert --to-normal issue3519 normalized3519
355 $ hg lfconvert --to-normal issue3519 normalized3519
348 initializing destination normalized3519
356 initializing destination normalized3519
349 4 additional largefiles cached
357 4 additional largefiles cached
350 scanning source...
358 scanning source...
351 sorting...
359 sorting...
352 converting...
360 converting...
353 7 add large, normal1
361 7 add large, normal1
354 6 add sub/*
362 6 add sub/*
355 5 rename sub/ to stuff/
363 5 rename sub/ to stuff/
356 4 add normal3, modify sub/*
364 4 add normal3, modify sub/*
357 3 remove large, normal3
365 3 remove large, normal3
358 2 merge
366 2 merge
359 1 add anotherlarge (should be a largefile)
367 1 add anotherlarge (should be a largefile)
360 0 Added tag mytag for changeset abacddda7028
368 0 Added tag mytag for changeset abacddda7028
361
369
362 Ensure the abort message is useful if a largefile is entirely unavailable
370 Ensure the abort message is useful if a largefile is entirely unavailable
363 $ rm -rf normalized3519
371 $ rm -rf normalized3519
364 $ rm "${USERCACHE}"/*
372 $ rm "${USERCACHE}"/*
365 $ rm issue3519/.hg/largefiles/*
373 $ rm issue3519/.hg/largefiles/*
366 $ rm largefiles-repo/.hg/largefiles/*
374 $ rm largefiles-repo/.hg/largefiles/*
367 $ hg lfconvert --to-normal issue3519 normalized3519
375 $ hg lfconvert --to-normal issue3519 normalized3519
368 initializing destination normalized3519
376 initializing destination normalized3519
369 anotherlarge: largefile 3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3 not available from file:/*/$TESTTMP/largefiles-repo (glob)
377 anotherlarge: largefile 3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3 not available from file:/*/$TESTTMP/largefiles-repo (glob)
370 stuff/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob)
378 stuff/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob)
371 stuff/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob)
379 stuff/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob)
372 sub/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob)
380 sub/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob)
373 large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
381 large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
374 sub/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob)
382 sub/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob)
375 large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
383 large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
376 stuff/maybelarge.dat: largefile 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c not available from file:/*/$TESTTMP/largefiles-repo (glob)
384 stuff/maybelarge.dat: largefile 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c not available from file:/*/$TESTTMP/largefiles-repo (glob)
377 large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
385 large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
378 sub/maybelarge.dat: largefile 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c not available from file:/*/$TESTTMP/largefiles-repo (glob)
386 sub/maybelarge.dat: largefile 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c not available from file:/*/$TESTTMP/largefiles-repo (glob)
379 large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
387 large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
380 0 additional largefiles cached
388 0 additional largefiles cached
381 11 largefiles failed to download
389 11 largefiles failed to download
382 abort: all largefiles must be present locally
390 abort: all largefiles must be present locally
383 [255]
391 [255]
384
392
385
393
General Comments 0
You need to be logged in to leave comments. Login now