##// END OF EJS Templates
largefiles: file storage should be relative to repo, not relative to cwd...
Mads Kiilerich -
r15553:e89385e4 stable
parent child Browse files
Show More
@@ -1,453 +1,453 b''
1 1 # Copyright 2009-2010 Gregory P. Ward
2 2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 3 # Copyright 2010-2011 Fog Creek Software
4 4 # Copyright 2010-2011 Unity Technologies
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 '''largefiles utility code: must not import other modules in this package.'''
10 10
11 11 import os
12 12 import errno
13 13 import platform
14 14 import shutil
15 15 import stat
16 16 import tempfile
17 17
18 18 from mercurial import dirstate, httpconnection, match as match_, util, scmutil
19 19 from mercurial.i18n import _
20 20
21 21 shortname = '.hglf'
22 22 longname = 'largefiles'
23 23
24 24
25 25 # -- Portability wrappers ----------------------------------------------
26 26
27 27 def dirstate_walk(dirstate, matcher, unknown=False, ignored=False):
28 28 return dirstate.walk(matcher, [], unknown, ignored)
29 29
30 30 def repo_add(repo, list):
31 31 add = repo[None].add
32 32 return add(list)
33 33
34 34 def repo_remove(repo, list, unlink=False):
35 35 def remove(list, unlink):
36 36 wlock = repo.wlock()
37 37 try:
38 38 if unlink:
39 39 for f in list:
40 40 try:
41 41 util.unlinkpath(repo.wjoin(f))
42 42 except OSError, inst:
43 43 if inst.errno != errno.ENOENT:
44 44 raise
45 45 repo[None].forget(list)
46 46 finally:
47 47 wlock.release()
48 48 return remove(list, unlink=unlink)
49 49
50 50 def repo_forget(repo, list):
51 51 forget = repo[None].forget
52 52 return forget(list)
53 53
54 54 def findoutgoing(repo, remote, force):
55 55 from mercurial import discovery
56 56 common, _anyinc, _heads = discovery.findcommonincoming(repo,
57 57 remote, force=force)
58 58 return repo.changelog.findmissing(common)
59 59
60 60 # -- Private worker functions ------------------------------------------
61 61
62 62 def getminsize(ui, assumelfiles, opt, default=10):
63 63 lfsize = opt
64 64 if not lfsize and assumelfiles:
65 65 lfsize = ui.config(longname, 'minsize', default=default)
66 66 if lfsize:
67 67 try:
68 68 lfsize = float(lfsize)
69 69 except ValueError:
70 70 raise util.Abort(_('largefiles: size must be number (not %s)\n')
71 71 % lfsize)
72 72 if lfsize is None:
73 73 raise util.Abort(_('minimum size for largefiles must be specified'))
74 74 return lfsize
75 75
76 76 def link(src, dest):
77 77 try:
78 78 util.oslink(src, dest)
79 79 except OSError:
80 80 # if hardlinks fail, fallback on copy
81 81 shutil.copyfile(src, dest)
82 82 os.chmod(dest, os.stat(src).st_mode)
83 83
84 84 def usercachepath(ui, hash):
85 85 path = ui.configpath(longname, 'usercache', None)
86 86 if path:
87 87 path = os.path.join(path, hash)
88 88 else:
89 89 if os.name == 'nt':
90 90 appdata = os.getenv('LOCALAPPDATA', os.getenv('APPDATA'))
91 91 path = os.path.join(appdata, longname, hash)
92 92 elif platform.system() == 'Darwin':
93 93 path = os.path.join(os.getenv('HOME'), 'Library', 'Caches',
94 94 longname, hash)
95 95 elif os.name == 'posix':
96 96 path = os.getenv('XDG_CACHE_HOME')
97 97 if path:
98 98 path = os.path.join(path, longname, hash)
99 99 else:
100 100 path = os.path.join(os.getenv('HOME'), '.cache', longname, hash)
101 101 else:
102 102 raise util.Abort(_('unknown operating system: %s\n') % os.name)
103 103 return path
104 104
105 105 def inusercache(ui, hash):
106 106 return os.path.exists(usercachepath(ui, hash))
107 107
108 108 def findfile(repo, hash):
109 109 if instore(repo, hash):
110 110 repo.ui.note(_('Found %s in store\n') % hash)
111 111 elif inusercache(repo.ui, hash):
112 112 repo.ui.note(_('Found %s in system cache\n') % hash)
113 113 path = storepath(repo, hash)
114 114 util.makedirs(os.path.dirname(path))
115 115 link(usercachepath(repo.ui, hash), path)
116 116 else:
117 117 return None
118 118 return storepath(repo, hash)
119 119
120 120 class largefiles_dirstate(dirstate.dirstate):
121 121 def __getitem__(self, key):
122 122 return super(largefiles_dirstate, self).__getitem__(unixpath(key))
123 123 def normal(self, f):
124 124 return super(largefiles_dirstate, self).normal(unixpath(f))
125 125 def remove(self, f):
126 126 return super(largefiles_dirstate, self).remove(unixpath(f))
127 127 def add(self, f):
128 128 return super(largefiles_dirstate, self).add(unixpath(f))
129 129 def drop(self, f):
130 130 return super(largefiles_dirstate, self).drop(unixpath(f))
131 131 def forget(self, f):
132 132 return super(largefiles_dirstate, self).forget(unixpath(f))
133 133
134 134 def openlfdirstate(ui, repo):
135 135 '''
136 136 Return a dirstate object that tracks largefiles: i.e. its root is
137 137 the repo root, but it is saved in .hg/largefiles/dirstate.
138 138 '''
139 139 admin = repo.join(longname)
140 140 opener = scmutil.opener(admin)
141 141 lfdirstate = largefiles_dirstate(opener, ui, repo.root,
142 142 repo.dirstate._validate)
143 143
144 144 # If the largefiles dirstate does not exist, populate and create
145 145 # it. This ensures that we create it on the first meaningful
146 146 # largefiles operation in a new clone. It also gives us an easy
147 147 # way to forcibly rebuild largefiles state:
148 148 # rm .hg/largefiles/dirstate && hg status
149 149 # Or even, if things are really messed up:
150 150 # rm -rf .hg/largefiles && hg status
151 151 if not os.path.exists(os.path.join(admin, 'dirstate')):
152 152 util.makedirs(admin)
153 153 matcher = getstandinmatcher(repo)
154 154 for standin in dirstate_walk(repo.dirstate, matcher):
155 155 lfile = splitstandin(standin)
156 156 hash = readstandin(repo, lfile)
157 157 lfdirstate.normallookup(lfile)
158 158 try:
159 if hash == hashfile(lfile):
159 if hash == hashfile(repo.wjoin(lfile)):
160 160 lfdirstate.normal(lfile)
161 161 except OSError, err:
162 162 if err.errno != errno.ENOENT:
163 163 raise
164 164
165 165 lfdirstate.write()
166 166
167 167 return lfdirstate
168 168
169 169 def lfdirstate_status(lfdirstate, repo, rev):
170 170 wlock = repo.wlock()
171 171 try:
172 172 match = match_.always(repo.root, repo.getcwd())
173 173 s = lfdirstate.status(match, [], False, False, False)
174 174 unsure, modified, added, removed, missing, unknown, ignored, clean = s
175 175 for lfile in unsure:
176 176 if repo[rev][standin(lfile)].data().strip() != \
177 177 hashfile(repo.wjoin(lfile)):
178 178 modified.append(lfile)
179 179 else:
180 180 clean.append(lfile)
181 181 lfdirstate.normal(lfile)
182 182 lfdirstate.write()
183 183 finally:
184 184 wlock.release()
185 185 return (modified, added, removed, missing, unknown, ignored, clean)
186 186
187 187 def listlfiles(repo, rev=None, matcher=None):
188 188 '''return a list of largefiles in the working copy or the
189 189 specified changeset'''
190 190
191 191 if matcher is None:
192 192 matcher = getstandinmatcher(repo)
193 193
194 194 # ignore unknown files in working directory
195 195 return [splitstandin(f)
196 196 for f in repo[rev].walk(matcher)
197 197 if rev is not None or repo.dirstate[f] != '?']
198 198
199 199 def instore(repo, hash):
200 200 return os.path.exists(storepath(repo, hash))
201 201
202 202 def storepath(repo, hash):
203 203 return repo.join(os.path.join(longname, hash))
204 204
205 205 def copyfromcache(repo, hash, filename):
206 206 '''Copy the specified largefile from the repo or system cache to
207 207 filename in the repository. Return true on success or false if the
208 208 file was not found in either cache (which should not happened:
209 209 this is meant to be called only after ensuring that the needed
210 210 largefile exists in the cache).'''
211 211 path = findfile(repo, hash)
212 212 if path is None:
213 213 return False
214 214 util.makedirs(os.path.dirname(repo.wjoin(filename)))
215 215 shutil.copy(path, repo.wjoin(filename))
216 216 return True
217 217
218 218 def copytostore(repo, rev, file, uploaded=False):
219 219 hash = readstandin(repo, file)
220 220 if instore(repo, hash):
221 221 return
222 222 copytostoreabsolute(repo, repo.wjoin(file), hash)
223 223
224 224 def copytostoreabsolute(repo, file, hash):
225 225 util.makedirs(os.path.dirname(storepath(repo, hash)))
226 226 if inusercache(repo.ui, hash):
227 227 link(usercachepath(repo.ui, hash), storepath(repo, hash))
228 228 else:
229 229 shutil.copyfile(file, storepath(repo, hash))
230 230 os.chmod(storepath(repo, hash), os.stat(file).st_mode)
231 231 linktousercache(repo, hash)
232 232
233 233 def linktousercache(repo, hash):
234 234 util.makedirs(os.path.dirname(usercachepath(repo.ui, hash)))
235 235 link(storepath(repo, hash), usercachepath(repo.ui, hash))
236 236
237 237 def getstandinmatcher(repo, pats=[], opts={}):
238 238 '''Return a match object that applies pats to the standin directory'''
239 239 standindir = repo.pathto(shortname)
240 240 if pats:
241 241 # patterns supplied: search standin directory relative to current dir
242 242 cwd = repo.getcwd()
243 243 if os.path.isabs(cwd):
244 244 # cwd is an absolute path for hg -R <reponame>
245 245 # work relative to the repository root in this case
246 246 cwd = ''
247 247 pats = [os.path.join(standindir, cwd, pat) for pat in pats]
248 248 elif os.path.isdir(standindir):
249 249 # no patterns: relative to repo root
250 250 pats = [standindir]
251 251 else:
252 252 # no patterns and no standin dir: return matcher that matches nothing
253 253 match = match_.match(repo.root, None, [], exact=True)
254 254 match.matchfn = lambda f: False
255 255 return match
256 256 return getmatcher(repo, pats, opts, showbad=False)
257 257
258 258 def getmatcher(repo, pats=[], opts={}, showbad=True):
259 259 '''Wrapper around scmutil.match() that adds showbad: if false,
260 260 neuter the match object's bad() method so it does not print any
261 261 warnings about missing files or directories.'''
262 262 match = scmutil.match(repo[None], pats, opts)
263 263
264 264 if not showbad:
265 265 match.bad = lambda f, msg: None
266 266 return match
267 267
268 268 def composestandinmatcher(repo, rmatcher):
269 269 '''Return a matcher that accepts standins corresponding to the
270 270 files accepted by rmatcher. Pass the list of files in the matcher
271 271 as the paths specified by the user.'''
272 272 smatcher = getstandinmatcher(repo, rmatcher.files())
273 273 isstandin = smatcher.matchfn
274 274 def composed_matchfn(f):
275 275 return isstandin(f) and rmatcher.matchfn(splitstandin(f))
276 276 smatcher.matchfn = composed_matchfn
277 277
278 278 return smatcher
279 279
280 280 def standin(filename):
281 281 '''Return the repo-relative path to the standin for the specified big
282 282 file.'''
283 283 # Notes:
284 284 # 1) Most callers want an absolute path, but _create_standin() needs
285 285 # it repo-relative so lfadd() can pass it to repo_add(). So leave
286 286 # it up to the caller to use repo.wjoin() to get an absolute path.
287 287 # 2) Join with '/' because that's what dirstate always uses, even on
288 288 # Windows. Change existing separator to '/' first in case we are
289 289 # passed filenames from an external source (like the command line).
290 290 return shortname + '/' + filename.replace(os.sep, '/')
291 291
292 292 def isstandin(filename):
293 293 '''Return true if filename is a big file standin. filename must be
294 294 in Mercurial's internal form (slash-separated).'''
295 295 return filename.startswith(shortname + '/')
296 296
297 297 def splitstandin(filename):
298 298 # Split on / because that's what dirstate always uses, even on Windows.
299 299 # Change local separator to / first just in case we are passed filenames
300 300 # from an external source (like the command line).
301 301 bits = filename.replace(os.sep, '/').split('/', 1)
302 302 if len(bits) == 2 and bits[0] == shortname:
303 303 return bits[1]
304 304 else:
305 305 return None
306 306
307 307 def updatestandin(repo, standin):
308 308 file = repo.wjoin(splitstandin(standin))
309 309 if os.path.exists(file):
310 310 hash = hashfile(file)
311 311 executable = getexecutable(file)
312 312 writestandin(repo, standin, hash, executable)
313 313
314 314 def readstandin(repo, filename, node=None):
315 315 '''read hex hash from standin for filename at given node, or working
316 316 directory if no node is given'''
317 317 return repo[node][standin(filename)].data().strip()
318 318
319 319 def writestandin(repo, standin, hash, executable):
320 320 '''write hash to <repo.root>/<standin>'''
321 321 writehash(hash, repo.wjoin(standin), executable)
322 322
323 323 def copyandhash(instream, outfile):
324 324 '''Read bytes from instream (iterable) and write them to outfile,
325 325 computing the SHA-1 hash of the data along the way. Close outfile
326 326 when done and return the binary hash.'''
327 327 hasher = util.sha1('')
328 328 for data in instream:
329 329 hasher.update(data)
330 330 outfile.write(data)
331 331
332 332 # Blecch: closing a file that somebody else opened is rude and
333 333 # wrong. But it's so darn convenient and practical! After all,
334 334 # outfile was opened just to copy and hash.
335 335 outfile.close()
336 336
337 337 return hasher.digest()
338 338
339 339 def hashrepofile(repo, file):
340 340 return hashfile(repo.wjoin(file))
341 341
342 342 def hashfile(file):
343 343 if not os.path.exists(file):
344 344 return ''
345 345 hasher = util.sha1('')
346 346 fd = open(file, 'rb')
347 347 for data in blockstream(fd):
348 348 hasher.update(data)
349 349 fd.close()
350 350 return hasher.hexdigest()
351 351
352 352 class limitreader(object):
353 353 def __init__(self, f, limit):
354 354 self.f = f
355 355 self.limit = limit
356 356
357 357 def read(self, length):
358 358 if self.limit == 0:
359 359 return ''
360 360 length = length > self.limit and self.limit or length
361 361 self.limit -= length
362 362 return self.f.read(length)
363 363
364 364 def close(self):
365 365 pass
366 366
367 367 def blockstream(infile, blocksize=128 * 1024):
368 368 """Generator that yields blocks of data from infile and closes infile."""
369 369 while True:
370 370 data = infile.read(blocksize)
371 371 if not data:
372 372 break
373 373 yield data
374 374 # same blecch as copyandhash() above
375 375 infile.close()
376 376
377 377 def readhash(filename):
378 378 rfile = open(filename, 'rb')
379 379 hash = rfile.read(40)
380 380 rfile.close()
381 381 if len(hash) < 40:
382 382 raise util.Abort(_('bad hash in \'%s\' (only %d bytes long)')
383 383 % (filename, len(hash)))
384 384 return hash
385 385
386 386 def writehash(hash, filename, executable):
387 387 util.makedirs(os.path.dirname(filename))
388 388 if os.path.exists(filename):
389 389 os.unlink(filename)
390 390 wfile = open(filename, 'wb')
391 391
392 392 try:
393 393 wfile.write(hash)
394 394 wfile.write('\n')
395 395 finally:
396 396 wfile.close()
397 397 if os.path.exists(filename):
398 398 os.chmod(filename, getmode(executable))
399 399
400 400 def getexecutable(filename):
401 401 mode = os.stat(filename).st_mode
402 402 return ((mode & stat.S_IXUSR) and
403 403 (mode & stat.S_IXGRP) and
404 404 (mode & stat.S_IXOTH))
405 405
406 406 def getmode(executable):
407 407 if executable:
408 408 return 0755
409 409 else:
410 410 return 0644
411 411
412 412 def urljoin(first, second, *arg):
413 413 def join(left, right):
414 414 if not left.endswith('/'):
415 415 left += '/'
416 416 if right.startswith('/'):
417 417 right = right[1:]
418 418 return left + right
419 419
420 420 url = join(first, second)
421 421 for a in arg:
422 422 url = join(url, a)
423 423 return url
424 424
425 425 def hexsha1(data):
426 426 """hexsha1 returns the hex-encoded sha1 sum of the data in the file-like
427 427 object data"""
428 428 h = util.sha1()
429 429 for chunk in util.filechunkiter(data):
430 430 h.update(chunk)
431 431 return h.hexdigest()
432 432
433 433 def httpsendfile(ui, filename):
434 434 return httpconnection.httpsendfile(ui, filename, 'rb')
435 435
436 436 def unixpath(path):
437 437 '''Return a version of path normalized for use with the lfdirstate.'''
438 438 return os.path.normpath(path).replace(os.sep, '/')
439 439
440 440 def islfilesrepo(repo):
441 441 return ('largefiles' in repo.requirements and
442 442 util.any(shortname + '/' in f[0] for f in repo.store.datafiles()))
443 443
444 444 def mkstemp(repo, prefix):
445 445 '''Returns a file descriptor and a filename corresponding to a temporary
446 446 file in the repo's largefiles store.'''
447 447 path = repo.join(longname)
448 448 util.makedirs(path)
449 449 return tempfile.mkstemp(prefix=prefix, dir=path)
450 450
451 451 class storeprotonotcapable(Exception):
452 452 def __init__(self, storetypes):
453 453 self.storetypes = storetypes
@@ -1,841 +1,847 b''
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extensions]
3 3 > largefiles=
4 4 > purge=
5 5 > rebase=
6 6 > transplant=
7 7 > [largefiles]
8 8 > minsize=2
9 9 > patterns=glob:**.dat
10 10 > EOF
11 11
12 12 Create the repo with a couple of revisions of both large and normal
13 13 files, testing that status correctly shows largefiles.
14 14
15 15 $ hg init a
16 16 $ cd a
17 17 $ mkdir sub
18 18 $ echo normal1 > normal1
19 19 $ echo normal2 > sub/normal2
20 20 $ echo large1 > large1
21 21 $ echo large2 > sub/large2
22 22 $ hg add normal1 sub/normal2
23 23 $ hg add --large large1 sub/large2
24 24 $ hg commit -m "add files"
25 25 $ echo normal11 > normal1
26 26 $ echo normal22 > sub/normal2
27 27 $ echo large11 > large1
28 28 $ echo large22 > sub/large2
29 29 $ hg st
30 30 M large1
31 31 M normal1
32 32 M sub/large2
33 33 M sub/normal2
34 34 $ hg commit -m "edit files"
35 35
36 36 Commit preserved largefile contents.
37 37
38 38 $ cat normal1
39 39 normal11
40 40 $ cat large1
41 41 large11
42 42 $ cat sub/normal2
43 43 normal22
44 44 $ cat sub/large2
45 45 large22
46 46
47 47 Remove both largefiles and normal files.
48 48
49 49 $ hg remove normal1 large1
50 50 $ hg commit -m "remove files"
51 51 $ ls
52 52 sub
53 53
54 54 Copy both largefiles and normal files.
55 55
56 56 $ hg cp sub/normal2 normal1
57 57 $ hg cp sub/large2 large1
58 58 $ hg commit -m "copy files"
59 59 $ cat normal1
60 60 normal22
61 61 $ cat large1
62 62 large22
63 63
64 64 Test moving largefiles and verify that normal files are also unaffected.
65 65
66 66 $ hg mv normal1 normal3
67 67 $ hg mv large1 large3
68 68 $ hg mv sub/normal2 sub/normal4
69 69 $ hg mv sub/large2 sub/large4
70 70 $ hg commit -m "move files"
71 71 $ cat normal3
72 72 normal22
73 73 $ cat large3
74 74 large22
75 75 $ cat sub/normal4
76 76 normal22
77 77 $ cat sub/large4
78 78 large22
79 79
80 80 Test archiving the various revisions. These hit corner cases known with
81 81 archiving.
82 82
83 83 $ hg archive -r 0 ../archive0
84 84 $ hg archive -r 1 ../archive1
85 85 $ hg archive -r 2 ../archive2
86 86 $ hg archive -r 3 ../archive3
87 87 $ hg archive -r 4 ../archive4
88 88 $ cd ../archive0
89 89 $ cat normal1
90 90 normal1
91 91 $ cat large1
92 92 large1
93 93 $ cat sub/normal2
94 94 normal2
95 95 $ cat sub/large2
96 96 large2
97 97 $ cd ../archive1
98 98 $ cat normal1
99 99 normal11
100 100 $ cat large1
101 101 large11
102 102 $ cat sub/normal2
103 103 normal22
104 104 $ cat sub/large2
105 105 large22
106 106 $ cd ../archive2
107 107 $ ls
108 108 sub
109 109 $ cat sub/normal2
110 110 normal22
111 111 $ cat sub/large2
112 112 large22
113 113 $ cd ../archive3
114 114 $ cat normal1
115 115 normal22
116 116 $ cat large1
117 117 large22
118 118 $ cat sub/normal2
119 119 normal22
120 120 $ cat sub/large2
121 121 large22
122 122 $ cd ../archive4
123 123 $ cat normal3
124 124 normal22
125 125 $ cat large3
126 126 large22
127 127 $ cat sub/normal4
128 128 normal22
129 129 $ cat sub/large4
130 130 large22
131 131
132 132 Commit corner case: specify files to commit.
133 133
134 134 $ cd ../a
135 135 $ echo normal3 > normal3
136 136 $ echo large3 > large3
137 137 $ echo normal4 > sub/normal4
138 138 $ echo large4 > sub/large4
139 139 $ hg commit normal3 large3 sub/normal4 sub/large4 -m "edit files again"
140 140 $ cat normal3
141 141 normal3
142 142 $ cat large3
143 143 large3
144 144 $ cat sub/normal4
145 145 normal4
146 146 $ cat sub/large4
147 147 large4
148 148
149 149 One more commit corner case: commit from a subdirectory.
150 150
151 151 $ cd ../a
152 152 $ echo normal33 > normal3
153 153 $ echo large33 > large3
154 154 $ echo normal44 > sub/normal4
155 155 $ echo large44 > sub/large4
156 156 $ cd sub
157 157 $ hg commit -m "edit files yet again"
158 158 $ cat ../normal3
159 159 normal33
160 160 $ cat ../large3
161 161 large33
162 162 $ cat normal4
163 163 normal44
164 164 $ cat large4
165 165 large44
166 166
167 167 Committing standins is not allowed.
168 168
169 169 $ cd ..
170 170 $ echo large3 > large3
171 171 $ hg commit .hglf/large3 -m "try to commit standin"
172 172 abort: file ".hglf/large3" is a largefile standin
173 173 (commit the largefile itself instead)
174 174 [255]
175 175
176 176 Corner cases for adding largefiles.
177 177
178 178 $ echo large5 > large5
179 179 $ hg add --large large5
180 180 $ hg add --large large5
181 181 large5 already a largefile
182 182 $ mkdir sub2
183 183 $ echo large6 > sub2/large6
184 184 $ echo large7 > sub2/large7
185 185 $ hg add --large sub2
186 186 adding sub2/large6 as a largefile
187 187 adding sub2/large7 as a largefile
188 188 $ hg st
189 189 M large3
190 190 A large5
191 191 A sub2/large6
192 192 A sub2/large7
193 193
194 194 Config settings (pattern **.dat, minsize 2 MB) are respected.
195 195
196 196 $ echo testdata > test.dat
197 197 $ dd bs=1k count=2k if=/dev/zero of=reallylarge > /dev/null 2> /dev/null
198 198 $ hg add
199 199 adding reallylarge as a largefile
200 200 adding test.dat as a largefile
201 201
202 202 Test that minsize and --lfsize handle float values;
203 203 also tests that --lfsize overrides largefiles.minsize.
204 204 (0.250 MB = 256 kB = 262144 B)
205 205
206 206 $ dd if=/dev/zero of=ratherlarge bs=1024 count=256 > /dev/null 2> /dev/null
207 207 $ dd if=/dev/zero of=medium bs=1024 count=128 > /dev/null 2> /dev/null
208 208 $ hg --config largefiles.minsize=.25 add
209 209 adding ratherlarge as a largefile
210 210 adding medium
211 211 $ hg forget medium
212 212 $ hg --config largefiles.minsize=.25 add --lfsize=.125
213 213 adding medium as a largefile
214 214 $ dd if=/dev/zero of=notlarge bs=1024 count=127 > /dev/null 2> /dev/null
215 215 $ hg --config largefiles.minsize=.25 add --lfsize=.125
216 216 adding notlarge
217 217 $ hg forget notlarge
218 218
219 219 Test forget on largefiles.
220 220
221 221 $ hg forget large3 large5 test.dat reallylarge ratherlarge medium
222 222 $ hg st
223 223 A sub2/large6
224 224 A sub2/large7
225 225 R large3
226 226 ? large5
227 227 ? medium
228 228 ? notlarge
229 229 ? ratherlarge
230 230 ? reallylarge
231 231 ? test.dat
232 232 $ hg commit -m "add/edit more largefiles"
233 233 $ hg st
234 234 ? large3
235 235 ? large5
236 236 ? medium
237 237 ? notlarge
238 238 ? ratherlarge
239 239 ? reallylarge
240 240 ? test.dat
241 241
242 242 Purge with largefiles: verify that largefiles are still in the working
243 243 dir after a purge.
244 244
245 245 $ hg purge --all
246 246 $ cat sub/large4
247 247 large44
248 248 $ cat sub2/large6
249 249 large6
250 250 $ cat sub2/large7
251 251 large7
252 252
253 253 Clone a largefiles repo.
254 254
255 255 $ hg clone . ../b
256 256 updating to branch default
257 257 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
258 258 getting changed largefiles
259 259 3 largefiles updated, 0 removed
260 $ hg debugstate --nodates
261 n 644 41 .hglf/sub/large4
262 n 0 -1 .hglf/sub2/large6
263 n 0 -1 .hglf/sub2/large7
264 n 644 9 normal3
265 n 644 9 sub/normal4
260 266 $ cd ../b
261 267 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
262 268 7:daea875e9014 add/edit more largefiles
263 269 6:4355d653f84f edit files yet again
264 270 5:9d5af5072dbd edit files again
265 271 4:74c02385b94c move files
266 272 3:9e8fbc4bce62 copy files
267 273 2:51a0ae4d5864 remove files
268 274 1:ce8896473775 edit files
269 275 0:30d30fe6a5be add files
270 276 $ cat normal3
271 277 normal33
272 278 $ cat sub/normal4
273 279 normal44
274 280 $ cat sub/large4
275 281 large44
276 282 $ cat sub2/large6
277 283 large6
278 284 $ cat sub2/large7
279 285 large7
280 286 $ cd ..
281 287 $ hg clone a -r 3 c
282 288 adding changesets
283 289 adding manifests
284 290 adding file changes
285 291 added 4 changesets with 10 changes to 4 files
286 292 updating to branch default
287 293 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
288 294 getting changed largefiles
289 295 2 largefiles updated, 0 removed
290 296 $ cd c
291 297 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
292 298 3:9e8fbc4bce62 copy files
293 299 2:51a0ae4d5864 remove files
294 300 1:ce8896473775 edit files
295 301 0:30d30fe6a5be add files
296 302 $ cat normal1
297 303 normal22
298 304 $ cat large1
299 305 large22
300 306 $ cat sub/normal2
301 307 normal22
302 308 $ cat sub/large2
303 309 large22
304 310
305 311 Old revisions of a clone have correct largefiles content (this also
306 312 tests update).
307 313
308 314 $ hg update -r 1
309 315 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
310 316 getting changed largefiles
311 317 1 largefiles updated, 0 removed
312 318 $ cat large1
313 319 large11
314 320 $ cat sub/large2
315 321 large22
316 322
317 323 Rebasing between two repositories does not revert largefiles to old
318 324 revisions (this was a very bad bug that took a lot of work to fix).
319 325
320 326 $ cd ..
321 327 $ hg clone a d
322 328 updating to branch default
323 329 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
324 330 getting changed largefiles
325 331 3 largefiles updated, 0 removed
326 332 $ cd b
327 333 $ echo large4-modified > sub/large4
328 334 $ echo normal3-modified > normal3
329 335 $ hg commit -m "modify normal file and largefile in repo b"
330 336 $ cd ../d
331 337 $ echo large6-modified > sub2/large6
332 338 $ echo normal4-modified > sub/normal4
333 339 $ hg commit -m "modify normal file largefile in repo d"
334 340 $ cd ..
335 341 $ hg clone d e
336 342 updating to branch default
337 343 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
338 344 getting changed largefiles
339 345 3 largefiles updated, 0 removed
340 346 $ cd d
341 347 $ hg pull --rebase ../b
342 348 pulling from ../b
343 349 searching for changes
344 350 adding changesets
345 351 adding manifests
346 352 adding file changes
347 353 added 1 changesets with 2 changes to 2 files (+1 heads)
348 354 getting changed largefiles
349 355 1 largefiles updated, 0 removed
350 356 saved backup bundle to $TESTTMP/d/.hg/strip-backup/f574fb32bb45-backup.hg
351 357 nothing to rebase
352 358 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
353 359 9:598410d3eb9a modify normal file largefile in repo d
354 360 8:a381d2c8c80e modify normal file and largefile in repo b
355 361 7:daea875e9014 add/edit more largefiles
356 362 6:4355d653f84f edit files yet again
357 363 5:9d5af5072dbd edit files again
358 364 4:74c02385b94c move files
359 365 3:9e8fbc4bce62 copy files
360 366 2:51a0ae4d5864 remove files
361 367 1:ce8896473775 edit files
362 368 0:30d30fe6a5be add files
363 369 $ cat normal3
364 370 normal3-modified
365 371 $ cat sub/normal4
366 372 normal4-modified
367 373 $ cat sub/large4
368 374 large4-modified
369 375 $ cat sub2/large6
370 376 large6-modified
371 377 $ cat sub2/large7
372 378 large7
373 379 $ cd ../e
374 380 $ hg pull ../b
375 381 pulling from ../b
376 382 searching for changes
377 383 adding changesets
378 384 adding manifests
379 385 adding file changes
380 386 added 1 changesets with 2 changes to 2 files (+1 heads)
381 387 (run 'hg heads' to see heads, 'hg merge' to merge)
382 388 $ hg rebase
383 389 getting changed largefiles
384 390 1 largefiles updated, 0 removed
385 391 saved backup bundle to $TESTTMP/e/.hg/strip-backup/f574fb32bb45-backup.hg
386 392 $ hg log
387 393 changeset: 9:598410d3eb9a
388 394 tag: tip
389 395 user: test
390 396 date: Thu Jan 01 00:00:00 1970 +0000
391 397 summary: modify normal file largefile in repo d
392 398
393 399 changeset: 8:a381d2c8c80e
394 400 user: test
395 401 date: Thu Jan 01 00:00:00 1970 +0000
396 402 summary: modify normal file and largefile in repo b
397 403
398 404 changeset: 7:daea875e9014
399 405 user: test
400 406 date: Thu Jan 01 00:00:00 1970 +0000
401 407 summary: add/edit more largefiles
402 408
403 409 changeset: 6:4355d653f84f
404 410 user: test
405 411 date: Thu Jan 01 00:00:00 1970 +0000
406 412 summary: edit files yet again
407 413
408 414 changeset: 5:9d5af5072dbd
409 415 user: test
410 416 date: Thu Jan 01 00:00:00 1970 +0000
411 417 summary: edit files again
412 418
413 419 changeset: 4:74c02385b94c
414 420 user: test
415 421 date: Thu Jan 01 00:00:00 1970 +0000
416 422 summary: move files
417 423
418 424 changeset: 3:9e8fbc4bce62
419 425 user: test
420 426 date: Thu Jan 01 00:00:00 1970 +0000
421 427 summary: copy files
422 428
423 429 changeset: 2:51a0ae4d5864
424 430 user: test
425 431 date: Thu Jan 01 00:00:00 1970 +0000
426 432 summary: remove files
427 433
428 434 changeset: 1:ce8896473775
429 435 user: test
430 436 date: Thu Jan 01 00:00:00 1970 +0000
431 437 summary: edit files
432 438
433 439 changeset: 0:30d30fe6a5be
434 440 user: test
435 441 date: Thu Jan 01 00:00:00 1970 +0000
436 442 summary: add files
437 443
438 444 $ cat normal3
439 445 normal3-modified
440 446 $ cat sub/normal4
441 447 normal4-modified
442 448 $ cat sub/large4
443 449 large4-modified
444 450 $ cat sub2/large6
445 451 large6-modified
446 452 $ cat sub2/large7
447 453 large7
448 454
449 455 Rollback on largefiles.
450 456
451 457 $ echo large4-modified-again > sub/large4
452 458 $ hg commit -m "Modify large4 again"
453 459 $ hg rollback
454 460 repository tip rolled back to revision 9 (undo commit)
455 461 working directory now based on revision 9
456 462 $ hg st
457 463 M sub/large4
458 464 $ hg log
459 465 changeset: 9:598410d3eb9a
460 466 tag: tip
461 467 user: test
462 468 date: Thu Jan 01 00:00:00 1970 +0000
463 469 summary: modify normal file largefile in repo d
464 470
465 471 changeset: 8:a381d2c8c80e
466 472 user: test
467 473 date: Thu Jan 01 00:00:00 1970 +0000
468 474 summary: modify normal file and largefile in repo b
469 475
470 476 changeset: 7:daea875e9014
471 477 user: test
472 478 date: Thu Jan 01 00:00:00 1970 +0000
473 479 summary: add/edit more largefiles
474 480
475 481 changeset: 6:4355d653f84f
476 482 user: test
477 483 date: Thu Jan 01 00:00:00 1970 +0000
478 484 summary: edit files yet again
479 485
480 486 changeset: 5:9d5af5072dbd
481 487 user: test
482 488 date: Thu Jan 01 00:00:00 1970 +0000
483 489 summary: edit files again
484 490
485 491 changeset: 4:74c02385b94c
486 492 user: test
487 493 date: Thu Jan 01 00:00:00 1970 +0000
488 494 summary: move files
489 495
490 496 changeset: 3:9e8fbc4bce62
491 497 user: test
492 498 date: Thu Jan 01 00:00:00 1970 +0000
493 499 summary: copy files
494 500
495 501 changeset: 2:51a0ae4d5864
496 502 user: test
497 503 date: Thu Jan 01 00:00:00 1970 +0000
498 504 summary: remove files
499 505
500 506 changeset: 1:ce8896473775
501 507 user: test
502 508 date: Thu Jan 01 00:00:00 1970 +0000
503 509 summary: edit files
504 510
505 511 changeset: 0:30d30fe6a5be
506 512 user: test
507 513 date: Thu Jan 01 00:00:00 1970 +0000
508 514 summary: add files
509 515
510 516 $ cat sub/large4
511 517 large4-modified-again
512 518
513 519 "update --check" refuses to update with uncommitted changes.
514 520 $ hg update --check 8
515 521 abort: uncommitted local changes
516 522 [255]
517 523
518 524 "update --clean" leaves correct largefiles in working copy.
519 525
520 526 $ hg update --clean
521 527 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
522 528 getting changed largefiles
523 529 1 largefiles updated, 0 removed
524 530 $ cat normal3
525 531 normal3-modified
526 532 $ cat sub/normal4
527 533 normal4-modified
528 534 $ cat sub/large4
529 535 large4-modified
530 536 $ cat sub2/large6
531 537 large6-modified
532 538 $ cat sub2/large7
533 539 large7
534 540
535 541 Now "update check" is happy.
536 542 $ hg update --check 8
537 543 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
538 544 getting changed largefiles
539 545 1 largefiles updated, 0 removed
540 546 $ hg update --check
541 547 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
542 548 getting changed largefiles
543 549 1 largefiles updated, 0 removed
544 550
545 551 "revert" works on largefiles (and normal files too).
546 552 $ echo hack3 >> normal3
547 553 $ echo hack4 >> sub/normal4
548 554 $ echo hack4 >> sub/large4
549 555 $ hg rm sub2/large6
550 556 $ echo new >> sub2/large8
551 557 $ hg add --large sub2/large8
552 558 # XXX we don't really want to report that we're reverting the standin;
553 559 # that's just an implementation detail. But I don't see an obvious fix. ;-(
554 560 $ hg revert sub
555 561 reverting .hglf/sub/large4
556 562 reverting sub/normal4
557 563 $ hg status
558 564 M normal3
559 565 A sub2/large8
560 566 R sub2/large6
561 567 ? sub/large4.orig
562 568 ? sub/normal4.orig
563 569 $ cat sub/normal4
564 570 normal4-modified
565 571 $ cat sub/large4
566 572 large4-modified
567 573 $ hg revert -a --no-backup
568 574 undeleting .hglf/sub2/large6
569 575 forgetting .hglf/sub2/large8
570 576 reverting normal3
571 577 $ hg status
572 578 ? sub/large4.orig
573 579 ? sub/normal4.orig
574 580 ? sub2/large8
575 581 $ cat normal3
576 582 normal3-modified
577 583 $ cat sub2/large6
578 584 large6-modified
579 585 $ rm sub/*.orig sub2/large8
580 586
581 587 revert some files to an older revision
582 588 $ hg revert --no-backup -r 8 sub2
583 589 reverting .hglf/sub2/large6
584 590 $ cat sub2/large6
585 591 large6
586 592 $ hg revert --no-backup sub2
587 593 reverting .hglf/sub2/large6
588 594 $ hg status
589 595
590 596 "verify --large" actually verifies largefiles
591 597
592 598 $ hg verify --large
593 599 checking changesets
594 600 checking manifests
595 601 crosschecking files in changesets and manifests
596 602 checking files
597 603 10 files, 10 changesets, 28 total revisions
598 604 searching 1 changesets for largefiles
599 605 verified existence of 3 revisions of 3 largefiles
600 606
601 607 Merging does not revert to old versions of largefiles (this has also
602 608 been very problematic).
603 609
604 610 $ cd ..
605 611 $ hg clone -r 7 e f
606 612 adding changesets
607 613 adding manifests
608 614 adding file changes
609 615 added 8 changesets with 24 changes to 10 files
610 616 updating to branch default
611 617 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
612 618 getting changed largefiles
613 619 3 largefiles updated, 0 removed
614 620 $ cd f
615 621 $ echo "large4-merge-test" > sub/large4
616 622 $ hg commit -m "Modify large4 to test merge"
617 623 $ hg pull ../e
618 624 pulling from ../e
619 625 searching for changes
620 626 adding changesets
621 627 adding manifests
622 628 adding file changes
623 629 added 2 changesets with 4 changes to 4 files (+1 heads)
624 630 (run 'hg heads' to see heads, 'hg merge' to merge)
625 631 $ hg merge
626 632 merging sub/large4
627 633 largefile sub/large4 has a merge conflict
628 634 keep (l)ocal or take (o)ther? l
629 635 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
630 636 (branch merge, don't forget to commit)
631 637 getting changed largefiles
632 638 1 largefiles updated, 0 removed
633 639 $ hg commit -m "Merge repos e and f"
634 640 $ cat normal3
635 641 normal3-modified
636 642 $ cat sub/normal4
637 643 normal4-modified
638 644 $ cat sub/large4
639 645 large4-merge-test
640 646 $ cat sub2/large6
641 647 large6-modified
642 648 $ cat sub2/large7
643 649 large7
644 650
645 651 Test that a normal file and a largefile with the same name and path cannot
646 652 coexist.
647 653
648 654 $ rm sub2/large7
649 655 $ echo "largeasnormal" > sub2/large7
650 656 $ hg add sub2/large7
651 657 sub2/large7 already a largefile
652 658
653 659 Test that transplanting a largefile change works correctly.
654 660
655 661 $ cd ..
656 662 $ hg clone -r 8 d g
657 663 adding changesets
658 664 adding manifests
659 665 adding file changes
660 666 added 9 changesets with 26 changes to 10 files
661 667 updating to branch default
662 668 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
663 669 getting changed largefiles
664 670 3 largefiles updated, 0 removed
665 671 $ cd g
666 672 $ hg transplant -s ../d 598410d3eb9a
667 673 searching for changes
668 674 searching for changes
669 675 adding changesets
670 676 adding manifests
671 677 adding file changes
672 678 added 1 changesets with 2 changes to 2 files
673 679 getting changed largefiles
674 680 1 largefiles updated, 0 removed
675 681 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
676 682 9:598410d3eb9a modify normal file largefile in repo d
677 683 8:a381d2c8c80e modify normal file and largefile in repo b
678 684 7:daea875e9014 add/edit more largefiles
679 685 6:4355d653f84f edit files yet again
680 686 5:9d5af5072dbd edit files again
681 687 4:74c02385b94c move files
682 688 3:9e8fbc4bce62 copy files
683 689 2:51a0ae4d5864 remove files
684 690 1:ce8896473775 edit files
685 691 0:30d30fe6a5be add files
686 692 $ cat normal3
687 693 normal3-modified
688 694 $ cat sub/normal4
689 695 normal4-modified
690 696 $ cat sub/large4
691 697 large4-modified
692 698 $ cat sub2/large6
693 699 large6-modified
694 700 $ cat sub2/large7
695 701 large7
696 702 $ cd ..
697 703
698 704 vanilla clients not locked out from largefiles servers on vanilla repos
699 705 $ mkdir r1
700 706 $ cd r1
701 707 $ hg init
702 708 $ echo c1 > f1
703 709 $ hg add f1
704 710 $ hg com -m "m1"
705 711 $ cd ..
706 712 $ hg serve -R r1 -d -p $HGPORT --pid-file hg.pid
707 713 $ cat hg.pid >> $DAEMON_PIDS
708 714 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT r2
709 715 requesting all changes
710 716 adding changesets
711 717 adding manifests
712 718 adding file changes
713 719 added 1 changesets with 1 changes to 1 files
714 720 updating to branch default
715 721 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
716 722
717 723 largefiles clients still work with vanilla servers
718 724 $ hg --config extensions.largefiles=! serve -R r1 -d -p $HGPORT1 --pid-file hg.pid
719 725 $ cat hg.pid >> $DAEMON_PIDS
720 726 $ hg clone http://localhost:$HGPORT1 r3
721 727 requesting all changes
722 728 adding changesets
723 729 adding manifests
724 730 adding file changes
725 731 added 1 changesets with 1 changes to 1 files
726 732 updating to branch default
727 733 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
728 734
729 735 vanilla clients locked out from largefiles http repos
730 736 $ mkdir r4
731 737 $ cd r4
732 738 $ hg init
733 739 $ echo c1 > f1
734 740 $ hg add --large f1
735 741 $ hg com -m "m1"
736 742 $ cd ..
737 743 $ hg serve -R r4 -d -p $HGPORT2 --pid-file hg.pid
738 744 $ cat hg.pid >> $DAEMON_PIDS
739 745 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT2 r5
740 746 abort: remote error:
741 747
742 748 This repository uses the largefiles extension.
743 749
744 750 Please enable it in your Mercurial config file.
745 751 [255]
746 752
747 753 used all HGPORTs, kill all daemons
748 754 $ "$TESTDIR/killdaemons.py"
749 755
750 756 vanilla clients locked out from largefiles ssh repos
751 757 $ hg --config extensions.largefiles=! clone -e "python $TESTDIR/dummyssh" ssh://user@dummy/r4 r5
752 758 abort: remote error:
753 759
754 760 This repository uses the largefiles extension.
755 761
756 762 Please enable it in your Mercurial config file.
757 763 [255]
758 764
759 765 largefiles clients refuse to push largefiles repos to vanilla servers
760 766 $ mkdir r6
761 767 $ cd r6
762 768 $ hg init
763 769 $ echo c1 > f1
764 770 $ hg add f1
765 771 $ hg com -m "m1"
766 772 $ cat >> .hg/hgrc <<!
767 773 > [web]
768 774 > push_ssl = false
769 775 > allow_push = *
770 776 > !
771 777 $ cd ..
772 778 $ hg clone r6 r7
773 779 updating to branch default
774 780 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
775 781 $ cd r7
776 782 $ echo c2 > f2
777 783 $ hg add --large f2
778 784 $ hg com -m "m2"
779 785 $ hg --config extensions.largefiles=! -R ../r6 serve -d -p $HGPORT --pid-file ../hg.pid
780 786 $ cat ../hg.pid >> $DAEMON_PIDS
781 787 $ hg push http://localhost:$HGPORT
782 788 pushing to http://localhost:$HGPORT/
783 789 searching for changes
784 790 abort: http://localhost:$HGPORT/ does not appear to be a largefile store
785 791 [255]
786 792 $ cd ..
787 793
788 794 $ cd ..
789 795
790 796 Clone a local repository owned by another user
791 797 We have to simulate that here by setting $HOME and removing write permissions
792 798 $ ORIGHOME="$HOME"
793 799 $ mkdir alice
794 800 $ HOME="`pwd`/alice"
795 801 $ cd alice
796 802 $ hg init pubrepo
797 803 $ cd pubrepo
798 804 $ dd if=/dev/urandom bs=1k count=11k > a-large-file 2> /dev/null
799 805 $ hg add --large a-large-file
800 806 $ hg commit -m "Add a large file"
801 807 $ cd ..
802 808 $ chmod -R a-w pubrepo
803 809 $ cd ..
804 810 $ mkdir bob
805 811 $ HOME="`pwd`/bob"
806 812 $ cd bob
807 813 $ hg clone --pull ../alice/pubrepo pubrepo
808 814 requesting all changes
809 815 adding changesets
810 816 adding manifests
811 817 adding file changes
812 818 added 1 changesets with 1 changes to 1 files
813 819 updating to branch default
814 820 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
815 821 getting changed largefiles
816 822 1 largefiles updated, 0 removed
817 823 $ cd ..
818 824 $ HOME="$ORIGHOME"
819 825
820 826 Symlink to a large largefile should behave the same as a symlink to a normal file
821 827 $ hg init largesymlink
822 828 $ cd largesymlink
823 829 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
824 830 $ hg add --large largefile
825 831 $ hg commit -m "commit a large file"
826 832 $ ln -s largefile largelink
827 833 $ hg add largelink
828 834 $ hg commit -m "commit a large symlink"
829 835 $ rm -f largelink
830 836 $ hg up >/dev/null
831 837 $ test -f largelink
832 838 [1]
833 839 $ test -L largelink
834 840 [1]
835 841 $ rm -f largelink # make next part of the test independent of the previous
836 842 $ hg up -C >/dev/null
837 843 $ test -f largelink
838 844 $ test -L largelink
839 845 $ cd ..
840 846
841 847
General Comments 0
You need to be logged in to leave comments. Login now