##// END OF EJS Templates
lfs: allow the pointer file to be viewed with `hg cat -T '{rawdata}'`...
Matt Harbison -
r35681:a9858349 default
parent child Browse files
Show More
@@ -1,246 +1,248 b''
1 # lfs - hash-preserving large file support using Git-LFS protocol
1 # lfs - hash-preserving large file support using Git-LFS protocol
2 #
2 #
3 # Copyright 2017 Facebook, Inc.
3 # Copyright 2017 Facebook, Inc.
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 """lfs - large file support (EXPERIMENTAL)
8 """lfs - large file support (EXPERIMENTAL)
9
9
10 Configs::
10 Configs::
11
11
12 [lfs]
12 [lfs]
13 # Remote endpoint. Multiple protocols are supported:
13 # Remote endpoint. Multiple protocols are supported:
14 # - http(s)://user:pass@example.com/path
14 # - http(s)://user:pass@example.com/path
15 # git-lfs endpoint
15 # git-lfs endpoint
16 # - file:///tmp/path
16 # - file:///tmp/path
17 # local filesystem, usually for testing
17 # local filesystem, usually for testing
18 # if unset, lfs will prompt setting this when it must use this value.
18 # if unset, lfs will prompt setting this when it must use this value.
19 # (default: unset)
19 # (default: unset)
20 url = https://example.com/lfs
20 url = https://example.com/lfs
21
21
22 # Which files to track in LFS. Path tests are "**.extname" for file
22 # Which files to track in LFS. Path tests are "**.extname" for file
23 # extensions, and "path:under/some/directory" for path prefix. Both
23 # extensions, and "path:under/some/directory" for path prefix. Both
24 # are relative to the repository root, and the latter must be quoted.
24 # are relative to the repository root, and the latter must be quoted.
25 # File size can be tested with the "size()" fileset, and tests can be
25 # File size can be tested with the "size()" fileset, and tests can be
26 # joined with fileset operators. (See "hg help filesets.operators".)
26 # joined with fileset operators. (See "hg help filesets.operators".)
27 #
27 #
28 # Some examples:
28 # Some examples:
29 # - all() # everything
29 # - all() # everything
30 # - none() # nothing
30 # - none() # nothing
31 # - size(">20MB") # larger than 20MB
31 # - size(">20MB") # larger than 20MB
32 # - !**.txt # anything not a *.txt file
32 # - !**.txt # anything not a *.txt file
33 # - **.zip | **.tar.gz | **.7z # some types of compressed files
33 # - **.zip | **.tar.gz | **.7z # some types of compressed files
34 # - "path:bin" # files under "bin" in the project root
34 # - "path:bin" # files under "bin" in the project root
35 # - (**.php & size(">2MB")) | (**.js & size(">5MB")) | **.tar.gz
35 # - (**.php & size(">2MB")) | (**.js & size(">5MB")) | **.tar.gz
36 # | ("path:bin" & !"path:/bin/README") | size(">1GB")
36 # | ("path:bin" & !"path:/bin/README") | size(">1GB")
37 # (default: none())
37 # (default: none())
38 track = size(">10M")
38 track = size(">10M")
39
39
40 # how many times to retry before giving up on transferring an object
40 # how many times to retry before giving up on transferring an object
41 retry = 5
41 retry = 5
42
42
43 # the local directory to store lfs files for sharing across local clones.
43 # the local directory to store lfs files for sharing across local clones.
44 # If not set, the cache is located in an OS specific cache location.
44 # If not set, the cache is located in an OS specific cache location.
45 usercache = /path/to/global/cache
45 usercache = /path/to/global/cache
46 """
46 """
47
47
48 from __future__ import absolute_import
48 from __future__ import absolute_import
49
49
50 from mercurial.i18n import _
50 from mercurial.i18n import _
51
51
52 from mercurial import (
52 from mercurial import (
53 bundle2,
53 bundle2,
54 changegroup,
54 changegroup,
55 cmdutil,
55 context,
56 context,
56 exchange,
57 exchange,
57 extensions,
58 extensions,
58 filelog,
59 filelog,
59 fileset,
60 fileset,
60 hg,
61 hg,
61 localrepo,
62 localrepo,
62 minifileset,
63 minifileset,
63 node,
64 node,
64 pycompat,
65 pycompat,
65 registrar,
66 registrar,
66 revlog,
67 revlog,
67 scmutil,
68 scmutil,
68 templatekw,
69 templatekw,
69 upgrade,
70 upgrade,
70 vfs as vfsmod,
71 vfs as vfsmod,
71 wireproto,
72 wireproto,
72 )
73 )
73
74
74 from . import (
75 from . import (
75 blobstore,
76 blobstore,
76 wrapper,
77 wrapper,
77 )
78 )
78
79
79 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
80 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
80 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
81 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
81 # be specifying the version(s) of Mercurial they are tested with, or
82 # be specifying the version(s) of Mercurial they are tested with, or
82 # leave the attribute unspecified.
83 # leave the attribute unspecified.
83 testedwith = 'ships-with-hg-core'
84 testedwith = 'ships-with-hg-core'
84
85
85 configtable = {}
86 configtable = {}
86 configitem = registrar.configitem(configtable)
87 configitem = registrar.configitem(configtable)
87
88
88 configitem('experimental', 'lfs.user-agent',
89 configitem('experimental', 'lfs.user-agent',
89 default=None,
90 default=None,
90 )
91 )
91
92
92 configitem('lfs', 'url',
93 configitem('lfs', 'url',
93 default=None,
94 default=None,
94 )
95 )
95 configitem('lfs', 'usercache',
96 configitem('lfs', 'usercache',
96 default=None,
97 default=None,
97 )
98 )
98 # Deprecated
99 # Deprecated
99 configitem('lfs', 'threshold',
100 configitem('lfs', 'threshold',
100 default=None,
101 default=None,
101 )
102 )
102 configitem('lfs', 'track',
103 configitem('lfs', 'track',
103 default='none()',
104 default='none()',
104 )
105 )
105 configitem('lfs', 'retry',
106 configitem('lfs', 'retry',
106 default=5,
107 default=5,
107 )
108 )
108
109
109 cmdtable = {}
110 cmdtable = {}
110 command = registrar.command(cmdtable)
111 command = registrar.command(cmdtable)
111
112
112 templatekeyword = registrar.templatekeyword()
113 templatekeyword = registrar.templatekeyword()
113
114
114 def featuresetup(ui, supported):
115 def featuresetup(ui, supported):
115 # don't die on seeing a repo with the lfs requirement
116 # don't die on seeing a repo with the lfs requirement
116 supported |= {'lfs'}
117 supported |= {'lfs'}
117
118
118 def uisetup(ui):
119 def uisetup(ui):
119 localrepo.localrepository.featuresetupfuncs.add(featuresetup)
120 localrepo.localrepository.featuresetupfuncs.add(featuresetup)
120
121
121 def reposetup(ui, repo):
122 def reposetup(ui, repo):
122 # Nothing to do with a remote repo
123 # Nothing to do with a remote repo
123 if not repo.local():
124 if not repo.local():
124 return
125 return
125
126
126 trackspec = repo.ui.config('lfs', 'track')
127 trackspec = repo.ui.config('lfs', 'track')
127
128
128 # deprecated config: lfs.threshold
129 # deprecated config: lfs.threshold
129 threshold = repo.ui.configbytes('lfs', 'threshold')
130 threshold = repo.ui.configbytes('lfs', 'threshold')
130 if threshold:
131 if threshold:
131 fileset.parse(trackspec) # make sure syntax errors are confined
132 fileset.parse(trackspec) # make sure syntax errors are confined
132 trackspec = "(%s) | size('>%d')" % (trackspec, threshold)
133 trackspec = "(%s) | size('>%d')" % (trackspec, threshold)
133
134
134 repo.svfs.options['lfstrack'] = minifileset.compile(trackspec)
135 repo.svfs.options['lfstrack'] = minifileset.compile(trackspec)
135 repo.svfs.lfslocalblobstore = blobstore.local(repo)
136 repo.svfs.lfslocalblobstore = blobstore.local(repo)
136 repo.svfs.lfsremoteblobstore = blobstore.remote(repo)
137 repo.svfs.lfsremoteblobstore = blobstore.remote(repo)
137
138
138 # Push hook
139 # Push hook
139 repo.prepushoutgoinghooks.add('lfs', wrapper.prepush)
140 repo.prepushoutgoinghooks.add('lfs', wrapper.prepush)
140
141
141 if 'lfs' not in repo.requirements:
142 if 'lfs' not in repo.requirements:
142 def checkrequireslfs(ui, repo, **kwargs):
143 def checkrequireslfs(ui, repo, **kwargs):
143 if 'lfs' not in repo.requirements:
144 if 'lfs' not in repo.requirements:
144 last = kwargs.get('node_last')
145 last = kwargs.get('node_last')
145 _bin = node.bin
146 _bin = node.bin
146 if last:
147 if last:
147 s = repo.set('%n:%n', _bin(kwargs['node']), _bin(last))
148 s = repo.set('%n:%n', _bin(kwargs['node']), _bin(last))
148 else:
149 else:
149 s = repo.set('%n', _bin(kwargs['node']))
150 s = repo.set('%n', _bin(kwargs['node']))
150 for ctx in s:
151 for ctx in s:
151 # TODO: is there a way to just walk the files in the commit?
152 # TODO: is there a way to just walk the files in the commit?
152 if any(ctx[f].islfs() for f in ctx.files() if f in ctx):
153 if any(ctx[f].islfs() for f in ctx.files() if f in ctx):
153 repo.requirements.add('lfs')
154 repo.requirements.add('lfs')
154 repo._writerequirements()
155 repo._writerequirements()
155 break
156 break
156
157
157 ui.setconfig('hooks', 'commit.lfs', checkrequireslfs, 'lfs')
158 ui.setconfig('hooks', 'commit.lfs', checkrequireslfs, 'lfs')
158 ui.setconfig('hooks', 'pretxnchangegroup.lfs', checkrequireslfs, 'lfs')
159 ui.setconfig('hooks', 'pretxnchangegroup.lfs', checkrequireslfs, 'lfs')
159
160
160 def wrapfilelog(filelog):
161 def wrapfilelog(filelog):
161 wrapfunction = extensions.wrapfunction
162 wrapfunction = extensions.wrapfunction
162
163
163 wrapfunction(filelog, 'addrevision', wrapper.filelogaddrevision)
164 wrapfunction(filelog, 'addrevision', wrapper.filelogaddrevision)
164 wrapfunction(filelog, 'renamed', wrapper.filelogrenamed)
165 wrapfunction(filelog, 'renamed', wrapper.filelogrenamed)
165 wrapfunction(filelog, 'size', wrapper.filelogsize)
166 wrapfunction(filelog, 'size', wrapper.filelogsize)
166
167
167 def extsetup(ui):
168 def extsetup(ui):
168 wrapfilelog(filelog.filelog)
169 wrapfilelog(filelog.filelog)
169
170
170 wrapfunction = extensions.wrapfunction
171 wrapfunction = extensions.wrapfunction
171
172
173 wrapfunction(cmdutil, '_updatecatformatter', wrapper._updatecatformatter)
172 wrapfunction(scmutil, 'wrapconvertsink', wrapper.convertsink)
174 wrapfunction(scmutil, 'wrapconvertsink', wrapper.convertsink)
173
175
174 wrapfunction(upgrade, '_finishdatamigration',
176 wrapfunction(upgrade, '_finishdatamigration',
175 wrapper.upgradefinishdatamigration)
177 wrapper.upgradefinishdatamigration)
176
178
177 wrapfunction(upgrade, 'preservedrequirements',
179 wrapfunction(upgrade, 'preservedrequirements',
178 wrapper.upgraderequirements)
180 wrapper.upgraderequirements)
179
181
180 wrapfunction(upgrade, 'supporteddestrequirements',
182 wrapfunction(upgrade, 'supporteddestrequirements',
181 wrapper.upgraderequirements)
183 wrapper.upgraderequirements)
182
184
183 wrapfunction(changegroup,
185 wrapfunction(changegroup,
184 'supportedoutgoingversions',
186 'supportedoutgoingversions',
185 wrapper.supportedoutgoingversions)
187 wrapper.supportedoutgoingversions)
186 wrapfunction(changegroup,
188 wrapfunction(changegroup,
187 'allsupportedversions',
189 'allsupportedversions',
188 wrapper.allsupportedversions)
190 wrapper.allsupportedversions)
189
191
190 wrapfunction(exchange, 'push', wrapper.push)
192 wrapfunction(exchange, 'push', wrapper.push)
191 wrapfunction(wireproto, '_capabilities', wrapper._capabilities)
193 wrapfunction(wireproto, '_capabilities', wrapper._capabilities)
192
194
193 wrapfunction(context.basefilectx, 'cmp', wrapper.filectxcmp)
195 wrapfunction(context.basefilectx, 'cmp', wrapper.filectxcmp)
194 wrapfunction(context.basefilectx, 'isbinary', wrapper.filectxisbinary)
196 wrapfunction(context.basefilectx, 'isbinary', wrapper.filectxisbinary)
195 context.basefilectx.islfs = wrapper.filectxislfs
197 context.basefilectx.islfs = wrapper.filectxislfs
196
198
197 revlog.addflagprocessor(
199 revlog.addflagprocessor(
198 revlog.REVIDX_EXTSTORED,
200 revlog.REVIDX_EXTSTORED,
199 (
201 (
200 wrapper.readfromstore,
202 wrapper.readfromstore,
201 wrapper.writetostore,
203 wrapper.writetostore,
202 wrapper.bypasscheckhash,
204 wrapper.bypasscheckhash,
203 ),
205 ),
204 )
206 )
205
207
206 wrapfunction(hg, 'clone', wrapper.hgclone)
208 wrapfunction(hg, 'clone', wrapper.hgclone)
207 wrapfunction(hg, 'postshare', wrapper.hgpostshare)
209 wrapfunction(hg, 'postshare', wrapper.hgpostshare)
208
210
209 # Make bundle choose changegroup3 instead of changegroup2. This affects
211 # Make bundle choose changegroup3 instead of changegroup2. This affects
210 # "hg bundle" command. Note: it does not cover all bundle formats like
212 # "hg bundle" command. Note: it does not cover all bundle formats like
211 # "packed1". Using "packed1" with lfs will likely cause trouble.
213 # "packed1". Using "packed1" with lfs will likely cause trouble.
212 names = [k for k, v in exchange._bundlespeccgversions.items() if v == '02']
214 names = [k for k, v in exchange._bundlespeccgversions.items() if v == '02']
213 for k in names:
215 for k in names:
214 exchange._bundlespeccgversions[k] = '03'
216 exchange._bundlespeccgversions[k] = '03'
215
217
216 # bundlerepo uses "vfsmod.readonlyvfs(othervfs)", we need to make sure lfs
218 # bundlerepo uses "vfsmod.readonlyvfs(othervfs)", we need to make sure lfs
217 # options and blob stores are passed from othervfs to the new readonlyvfs.
219 # options and blob stores are passed from othervfs to the new readonlyvfs.
218 wrapfunction(vfsmod.readonlyvfs, '__init__', wrapper.vfsinit)
220 wrapfunction(vfsmod.readonlyvfs, '__init__', wrapper.vfsinit)
219
221
220 # when writing a bundle via "hg bundle" command, upload related LFS blobs
222 # when writing a bundle via "hg bundle" command, upload related LFS blobs
221 wrapfunction(bundle2, 'writenewbundle', wrapper.writenewbundle)
223 wrapfunction(bundle2, 'writenewbundle', wrapper.writenewbundle)
222
224
223 @templatekeyword('lfs_files')
225 @templatekeyword('lfs_files')
224 def lfsfiles(repo, ctx, **args):
226 def lfsfiles(repo, ctx, **args):
225 """List of strings. LFS files added or modified by the changeset."""
227 """List of strings. LFS files added or modified by the changeset."""
226 args = pycompat.byteskwargs(args)
228 args = pycompat.byteskwargs(args)
227
229
228 pointers = wrapper.pointersfromctx(ctx) # {path: pointer}
230 pointers = wrapper.pointersfromctx(ctx) # {path: pointer}
229 files = sorted(pointers.keys())
231 files = sorted(pointers.keys())
230
232
231 makemap = lambda v: {
233 makemap = lambda v: {
232 'file': v,
234 'file': v,
233 'oid': pointers[v].oid(),
235 'oid': pointers[v].oid(),
234 }
236 }
235
237
236 # TODO: make the separator ', '?
238 # TODO: make the separator ', '?
237 f = templatekw._showlist('lfs_file', files, args)
239 f = templatekw._showlist('lfs_file', files, args)
238 return templatekw._hybrid(f, files, makemap, pycompat.identity)
240 return templatekw._hybrid(f, files, makemap, pycompat.identity)
239
241
240 @command('debuglfsupload',
242 @command('debuglfsupload',
241 [('r', 'rev', [], _('upload large files introduced by REV'))])
243 [('r', 'rev', [], _('upload large files introduced by REV'))])
242 def debuglfsupload(ui, repo, **opts):
244 def debuglfsupload(ui, repo, **opts):
243 """upload lfs blobs added by the working copy parent or given revisions"""
245 """upload lfs blobs added by the working copy parent or given revisions"""
244 revs = opts.get('rev', [])
246 revs = opts.get('rev', [])
245 pointers = wrapper.extractpointers(repo, scmutil.revrange(repo, revs))
247 pointers = wrapper.extractpointers(repo, scmutil.revrange(repo, revs))
246 wrapper.uploadblobs(repo, pointers)
248 wrapper.uploadblobs(repo, pointers)
@@ -1,346 +1,350 b''
1 # wrapper.py - methods wrapping core mercurial logic
1 # wrapper.py - methods wrapping core mercurial logic
2 #
2 #
3 # Copyright 2017 Facebook, Inc.
3 # Copyright 2017 Facebook, Inc.
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import hashlib
10 import hashlib
11
11
12 from mercurial.i18n import _
12 from mercurial.i18n import _
13 from mercurial.node import bin, nullid, short
13 from mercurial.node import bin, nullid, short
14
14
15 from mercurial import (
15 from mercurial import (
16 error,
16 error,
17 filelog,
17 filelog,
18 revlog,
18 revlog,
19 util,
19 util,
20 )
20 )
21
21
22 from ..largefiles import lfutil
22 from ..largefiles import lfutil
23
23
24 from . import (
24 from . import (
25 blobstore,
25 blobstore,
26 pointer,
26 pointer,
27 )
27 )
28
28
29 def supportedoutgoingversions(orig, repo):
29 def supportedoutgoingversions(orig, repo):
30 versions = orig(repo)
30 versions = orig(repo)
31 if 'lfs' in repo.requirements:
31 if 'lfs' in repo.requirements:
32 versions.discard('01')
32 versions.discard('01')
33 versions.discard('02')
33 versions.discard('02')
34 versions.add('03')
34 versions.add('03')
35 return versions
35 return versions
36
36
37 def allsupportedversions(orig, ui):
37 def allsupportedversions(orig, ui):
38 versions = orig(ui)
38 versions = orig(ui)
39 versions.add('03')
39 versions.add('03')
40 return versions
40 return versions
41
41
42 def _capabilities(orig, repo, proto):
42 def _capabilities(orig, repo, proto):
43 '''Wrap server command to announce lfs server capability'''
43 '''Wrap server command to announce lfs server capability'''
44 caps = orig(repo, proto)
44 caps = orig(repo, proto)
45 # XXX: change to 'lfs=serve' when separate git server isn't required?
45 # XXX: change to 'lfs=serve' when separate git server isn't required?
46 caps.append('lfs')
46 caps.append('lfs')
47 return caps
47 return caps
48
48
49 def bypasscheckhash(self, text):
49 def bypasscheckhash(self, text):
50 return False
50 return False
51
51
52 def readfromstore(self, text):
52 def readfromstore(self, text):
53 """Read filelog content from local blobstore transform for flagprocessor.
53 """Read filelog content from local blobstore transform for flagprocessor.
54
54
55 Default tranform for flagprocessor, returning contents from blobstore.
55 Default tranform for flagprocessor, returning contents from blobstore.
56 Returns a 2-typle (text, validatehash) where validatehash is True as the
56 Returns a 2-typle (text, validatehash) where validatehash is True as the
57 contents of the blobstore should be checked using checkhash.
57 contents of the blobstore should be checked using checkhash.
58 """
58 """
59 p = pointer.deserialize(text)
59 p = pointer.deserialize(text)
60 oid = p.oid()
60 oid = p.oid()
61 store = self.opener.lfslocalblobstore
61 store = self.opener.lfslocalblobstore
62 if not store.has(oid):
62 if not store.has(oid):
63 p.filename = self.filename
63 p.filename = self.filename
64 self.opener.lfsremoteblobstore.readbatch([p], store)
64 self.opener.lfsremoteblobstore.readbatch([p], store)
65
65
66 # The caller will validate the content
66 # The caller will validate the content
67 text = store.read(oid, verify=False)
67 text = store.read(oid, verify=False)
68
68
69 # pack hg filelog metadata
69 # pack hg filelog metadata
70 hgmeta = {}
70 hgmeta = {}
71 for k in p.keys():
71 for k in p.keys():
72 if k.startswith('x-hg-'):
72 if k.startswith('x-hg-'):
73 name = k[len('x-hg-'):]
73 name = k[len('x-hg-'):]
74 hgmeta[name] = p[k]
74 hgmeta[name] = p[k]
75 if hgmeta or text.startswith('\1\n'):
75 if hgmeta or text.startswith('\1\n'):
76 text = filelog.packmeta(hgmeta, text)
76 text = filelog.packmeta(hgmeta, text)
77
77
78 return (text, True)
78 return (text, True)
79
79
80 def writetostore(self, text):
80 def writetostore(self, text):
81 # hg filelog metadata (includes rename, etc)
81 # hg filelog metadata (includes rename, etc)
82 hgmeta, offset = filelog.parsemeta(text)
82 hgmeta, offset = filelog.parsemeta(text)
83 if offset and offset > 0:
83 if offset and offset > 0:
84 # lfs blob does not contain hg filelog metadata
84 # lfs blob does not contain hg filelog metadata
85 text = text[offset:]
85 text = text[offset:]
86
86
87 # git-lfs only supports sha256
87 # git-lfs only supports sha256
88 oid = hashlib.sha256(text).hexdigest()
88 oid = hashlib.sha256(text).hexdigest()
89 self.opener.lfslocalblobstore.write(oid, text)
89 self.opener.lfslocalblobstore.write(oid, text)
90
90
91 # replace contents with metadata
91 # replace contents with metadata
92 longoid = 'sha256:%s' % oid
92 longoid = 'sha256:%s' % oid
93 metadata = pointer.gitlfspointer(oid=longoid, size=str(len(text)))
93 metadata = pointer.gitlfspointer(oid=longoid, size=str(len(text)))
94
94
95 # by default, we expect the content to be binary. however, LFS could also
95 # by default, we expect the content to be binary. however, LFS could also
96 # be used for non-binary content. add a special entry for non-binary data.
96 # be used for non-binary content. add a special entry for non-binary data.
97 # this will be used by filectx.isbinary().
97 # this will be used by filectx.isbinary().
98 if not util.binary(text):
98 if not util.binary(text):
99 # not hg filelog metadata (affecting commit hash), no "x-hg-" prefix
99 # not hg filelog metadata (affecting commit hash), no "x-hg-" prefix
100 metadata['x-is-binary'] = '0'
100 metadata['x-is-binary'] = '0'
101
101
102 # translate hg filelog metadata to lfs metadata with "x-hg-" prefix
102 # translate hg filelog metadata to lfs metadata with "x-hg-" prefix
103 if hgmeta is not None:
103 if hgmeta is not None:
104 for k, v in hgmeta.iteritems():
104 for k, v in hgmeta.iteritems():
105 metadata['x-hg-%s' % k] = v
105 metadata['x-hg-%s' % k] = v
106
106
107 rawtext = metadata.serialize()
107 rawtext = metadata.serialize()
108 return (rawtext, False)
108 return (rawtext, False)
109
109
110 def _islfs(rlog, node=None, rev=None):
110 def _islfs(rlog, node=None, rev=None):
111 if rev is None:
111 if rev is None:
112 if node is None:
112 if node is None:
113 # both None - likely working copy content where node is not ready
113 # both None - likely working copy content where node is not ready
114 return False
114 return False
115 rev = rlog.rev(node)
115 rev = rlog.rev(node)
116 else:
116 else:
117 node = rlog.node(rev)
117 node = rlog.node(rev)
118 if node == nullid:
118 if node == nullid:
119 return False
119 return False
120 flags = rlog.flags(rev)
120 flags = rlog.flags(rev)
121 return bool(flags & revlog.REVIDX_EXTSTORED)
121 return bool(flags & revlog.REVIDX_EXTSTORED)
122
122
123 def filelogaddrevision(orig, self, text, transaction, link, p1, p2,
123 def filelogaddrevision(orig, self, text, transaction, link, p1, p2,
124 cachedelta=None, node=None,
124 cachedelta=None, node=None,
125 flags=revlog.REVIDX_DEFAULT_FLAGS, **kwds):
125 flags=revlog.REVIDX_DEFAULT_FLAGS, **kwds):
126 textlen = len(text)
126 textlen = len(text)
127 # exclude hg rename meta from file size
127 # exclude hg rename meta from file size
128 meta, offset = filelog.parsemeta(text)
128 meta, offset = filelog.parsemeta(text)
129 if offset:
129 if offset:
130 textlen -= offset
130 textlen -= offset
131
131
132 lfstrack = self.opener.options['lfstrack']
132 lfstrack = self.opener.options['lfstrack']
133
133
134 # Always exclude hg owned files
134 # Always exclude hg owned files
135 if not self.filename.startswith('.hg') and lfstrack(self.filename, textlen):
135 if not self.filename.startswith('.hg') and lfstrack(self.filename, textlen):
136 flags |= revlog.REVIDX_EXTSTORED
136 flags |= revlog.REVIDX_EXTSTORED
137
137
138 return orig(self, text, transaction, link, p1, p2, cachedelta=cachedelta,
138 return orig(self, text, transaction, link, p1, p2, cachedelta=cachedelta,
139 node=node, flags=flags, **kwds)
139 node=node, flags=flags, **kwds)
140
140
141 def filelogrenamed(orig, self, node):
141 def filelogrenamed(orig, self, node):
142 if _islfs(self, node):
142 if _islfs(self, node):
143 rawtext = self.revision(node, raw=True)
143 rawtext = self.revision(node, raw=True)
144 if not rawtext:
144 if not rawtext:
145 return False
145 return False
146 metadata = pointer.deserialize(rawtext)
146 metadata = pointer.deserialize(rawtext)
147 if 'x-hg-copy' in metadata and 'x-hg-copyrev' in metadata:
147 if 'x-hg-copy' in metadata and 'x-hg-copyrev' in metadata:
148 return metadata['x-hg-copy'], bin(metadata['x-hg-copyrev'])
148 return metadata['x-hg-copy'], bin(metadata['x-hg-copyrev'])
149 else:
149 else:
150 return False
150 return False
151 return orig(self, node)
151 return orig(self, node)
152
152
153 def filelogsize(orig, self, rev):
153 def filelogsize(orig, self, rev):
154 if _islfs(self, rev=rev):
154 if _islfs(self, rev=rev):
155 # fast path: use lfs metadata to answer size
155 # fast path: use lfs metadata to answer size
156 rawtext = self.revision(rev, raw=True)
156 rawtext = self.revision(rev, raw=True)
157 metadata = pointer.deserialize(rawtext)
157 metadata = pointer.deserialize(rawtext)
158 return int(metadata['size'])
158 return int(metadata['size'])
159 return orig(self, rev)
159 return orig(self, rev)
160
160
161 def filectxcmp(orig, self, fctx):
161 def filectxcmp(orig, self, fctx):
162 """returns True if text is different than fctx"""
162 """returns True if text is different than fctx"""
163 # some fctx (ex. hg-git) is not based on basefilectx and do not have islfs
163 # some fctx (ex. hg-git) is not based on basefilectx and do not have islfs
164 if self.islfs() and getattr(fctx, 'islfs', lambda: False)():
164 if self.islfs() and getattr(fctx, 'islfs', lambda: False)():
165 # fast path: check LFS oid
165 # fast path: check LFS oid
166 p1 = pointer.deserialize(self.rawdata())
166 p1 = pointer.deserialize(self.rawdata())
167 p2 = pointer.deserialize(fctx.rawdata())
167 p2 = pointer.deserialize(fctx.rawdata())
168 return p1.oid() != p2.oid()
168 return p1.oid() != p2.oid()
169 return orig(self, fctx)
169 return orig(self, fctx)
170
170
171 def filectxisbinary(orig, self):
171 def filectxisbinary(orig, self):
172 if self.islfs():
172 if self.islfs():
173 # fast path: use lfs metadata to answer isbinary
173 # fast path: use lfs metadata to answer isbinary
174 metadata = pointer.deserialize(self.rawdata())
174 metadata = pointer.deserialize(self.rawdata())
175 # if lfs metadata says nothing, assume it's binary by default
175 # if lfs metadata says nothing, assume it's binary by default
176 return bool(int(metadata.get('x-is-binary', 1)))
176 return bool(int(metadata.get('x-is-binary', 1)))
177 return orig(self)
177 return orig(self)
178
178
179 def filectxislfs(self):
179 def filectxislfs(self):
180 return _islfs(self.filelog(), self.filenode())
180 return _islfs(self.filelog(), self.filenode())
181
181
182 def _updatecatformatter(orig, fm, ctx, matcher, path, decode):
183 orig(fm, ctx, matcher, path, decode)
184 fm.data(rawdata=ctx[path].rawdata())
185
182 def convertsink(orig, sink):
186 def convertsink(orig, sink):
183 sink = orig(sink)
187 sink = orig(sink)
184 if sink.repotype == 'hg':
188 if sink.repotype == 'hg':
185 class lfssink(sink.__class__):
189 class lfssink(sink.__class__):
186 def putcommit(self, files, copies, parents, commit, source, revmap,
190 def putcommit(self, files, copies, parents, commit, source, revmap,
187 full, cleanp2):
191 full, cleanp2):
188 pc = super(lfssink, self).putcommit
192 pc = super(lfssink, self).putcommit
189 node = pc(files, copies, parents, commit, source, revmap, full,
193 node = pc(files, copies, parents, commit, source, revmap, full,
190 cleanp2)
194 cleanp2)
191
195
192 if 'lfs' not in self.repo.requirements:
196 if 'lfs' not in self.repo.requirements:
193 ctx = self.repo[node]
197 ctx = self.repo[node]
194
198
195 # The file list may contain removed files, so check for
199 # The file list may contain removed files, so check for
196 # membership before assuming it is in the context.
200 # membership before assuming it is in the context.
197 if any(f in ctx and ctx[f].islfs() for f, n in files):
201 if any(f in ctx and ctx[f].islfs() for f, n in files):
198 self.repo.requirements.add('lfs')
202 self.repo.requirements.add('lfs')
199 self.repo._writerequirements()
203 self.repo._writerequirements()
200
204
201 # Permanently enable lfs locally
205 # Permanently enable lfs locally
202 self.repo.vfs.append(
206 self.repo.vfs.append(
203 'hgrc', util.tonativeeol('\n[extensions]\nlfs=\n'))
207 'hgrc', util.tonativeeol('\n[extensions]\nlfs=\n'))
204
208
205 return node
209 return node
206
210
207 sink.__class__ = lfssink
211 sink.__class__ = lfssink
208
212
209 return sink
213 return sink
210
214
211 def vfsinit(orig, self, othervfs):
215 def vfsinit(orig, self, othervfs):
212 orig(self, othervfs)
216 orig(self, othervfs)
213 # copy lfs related options
217 # copy lfs related options
214 for k, v in othervfs.options.items():
218 for k, v in othervfs.options.items():
215 if k.startswith('lfs'):
219 if k.startswith('lfs'):
216 self.options[k] = v
220 self.options[k] = v
217 # also copy lfs blobstores. note: this can run before reposetup, so lfs
221 # also copy lfs blobstores. note: this can run before reposetup, so lfs
218 # blobstore attributes are not always ready at this time.
222 # blobstore attributes are not always ready at this time.
219 for name in ['lfslocalblobstore', 'lfsremoteblobstore']:
223 for name in ['lfslocalblobstore', 'lfsremoteblobstore']:
220 if util.safehasattr(othervfs, name):
224 if util.safehasattr(othervfs, name):
221 setattr(self, name, getattr(othervfs, name))
225 setattr(self, name, getattr(othervfs, name))
222
226
223 def hgclone(orig, ui, opts, *args, **kwargs):
227 def hgclone(orig, ui, opts, *args, **kwargs):
224 result = orig(ui, opts, *args, **kwargs)
228 result = orig(ui, opts, *args, **kwargs)
225
229
226 if result is not None:
230 if result is not None:
227 sourcerepo, destrepo = result
231 sourcerepo, destrepo = result
228 repo = destrepo.local()
232 repo = destrepo.local()
229
233
230 # When cloning to a remote repo (like through SSH), no repo is available
234 # When cloning to a remote repo (like through SSH), no repo is available
231 # from the peer. Therefore the hgrc can't be updated.
235 # from the peer. Therefore the hgrc can't be updated.
232 if not repo:
236 if not repo:
233 return result
237 return result
234
238
235 # If lfs is required for this repo, permanently enable it locally
239 # If lfs is required for this repo, permanently enable it locally
236 if 'lfs' in repo.requirements:
240 if 'lfs' in repo.requirements:
237 repo.vfs.append('hgrc',
241 repo.vfs.append('hgrc',
238 util.tonativeeol('\n[extensions]\nlfs=\n'))
242 util.tonativeeol('\n[extensions]\nlfs=\n'))
239
243
240 return result
244 return result
241
245
242 def hgpostshare(orig, sourcerepo, destrepo, bookmarks=True, defaultpath=None):
246 def hgpostshare(orig, sourcerepo, destrepo, bookmarks=True, defaultpath=None):
243 orig(sourcerepo, destrepo, bookmarks, defaultpath)
247 orig(sourcerepo, destrepo, bookmarks, defaultpath)
244
248
245 # If lfs is required for this repo, permanently enable it locally
249 # If lfs is required for this repo, permanently enable it locally
246 if 'lfs' in destrepo.requirements:
250 if 'lfs' in destrepo.requirements:
247 destrepo.vfs.append('hgrc', util.tonativeeol('\n[extensions]\nlfs=\n'))
251 destrepo.vfs.append('hgrc', util.tonativeeol('\n[extensions]\nlfs=\n'))
248
252
249 def _canskipupload(repo):
253 def _canskipupload(repo):
250 # if remotestore is a null store, upload is a no-op and can be skipped
254 # if remotestore is a null store, upload is a no-op and can be skipped
251 return isinstance(repo.svfs.lfsremoteblobstore, blobstore._nullremote)
255 return isinstance(repo.svfs.lfsremoteblobstore, blobstore._nullremote)
252
256
253 def candownload(repo):
257 def candownload(repo):
254 # if remotestore is a null store, downloads will lead to nothing
258 # if remotestore is a null store, downloads will lead to nothing
255 return not isinstance(repo.svfs.lfsremoteblobstore, blobstore._nullremote)
259 return not isinstance(repo.svfs.lfsremoteblobstore, blobstore._nullremote)
256
260
257 def uploadblobsfromrevs(repo, revs):
261 def uploadblobsfromrevs(repo, revs):
258 '''upload lfs blobs introduced by revs
262 '''upload lfs blobs introduced by revs
259
263
260 Note: also used by other extensions e. g. infinitepush. avoid renaming.
264 Note: also used by other extensions e. g. infinitepush. avoid renaming.
261 '''
265 '''
262 if _canskipupload(repo):
266 if _canskipupload(repo):
263 return
267 return
264 pointers = extractpointers(repo, revs)
268 pointers = extractpointers(repo, revs)
265 uploadblobs(repo, pointers)
269 uploadblobs(repo, pointers)
266
270
267 def prepush(pushop):
271 def prepush(pushop):
268 """Prepush hook.
272 """Prepush hook.
269
273
270 Read through the revisions to push, looking for filelog entries that can be
274 Read through the revisions to push, looking for filelog entries that can be
271 deserialized into metadata so that we can block the push on their upload to
275 deserialized into metadata so that we can block the push on their upload to
272 the remote blobstore.
276 the remote blobstore.
273 """
277 """
274 return uploadblobsfromrevs(pushop.repo, pushop.outgoing.missing)
278 return uploadblobsfromrevs(pushop.repo, pushop.outgoing.missing)
275
279
276 def push(orig, repo, remote, *args, **kwargs):
280 def push(orig, repo, remote, *args, **kwargs):
277 """bail on push if the extension isn't enabled on remote when needed"""
281 """bail on push if the extension isn't enabled on remote when needed"""
278 if 'lfs' in repo.requirements:
282 if 'lfs' in repo.requirements:
279 # If the remote peer is for a local repo, the requirement tests in the
283 # If the remote peer is for a local repo, the requirement tests in the
280 # base class method enforce lfs support. Otherwise, some revisions in
284 # base class method enforce lfs support. Otherwise, some revisions in
281 # this repo use lfs, and the remote repo needs the extension loaded.
285 # this repo use lfs, and the remote repo needs the extension loaded.
282 if not remote.local() and not remote.capable('lfs'):
286 if not remote.local() and not remote.capable('lfs'):
283 # This is a copy of the message in exchange.push() when requirements
287 # This is a copy of the message in exchange.push() when requirements
284 # are missing between local repos.
288 # are missing between local repos.
285 m = _("required features are not supported in the destination: %s")
289 m = _("required features are not supported in the destination: %s")
286 raise error.Abort(m % 'lfs',
290 raise error.Abort(m % 'lfs',
287 hint=_('enable the lfs extension on the server'))
291 hint=_('enable the lfs extension on the server'))
288 return orig(repo, remote, *args, **kwargs)
292 return orig(repo, remote, *args, **kwargs)
289
293
290 def writenewbundle(orig, ui, repo, source, filename, bundletype, outgoing,
294 def writenewbundle(orig, ui, repo, source, filename, bundletype, outgoing,
291 *args, **kwargs):
295 *args, **kwargs):
292 """upload LFS blobs added by outgoing revisions on 'hg bundle'"""
296 """upload LFS blobs added by outgoing revisions on 'hg bundle'"""
293 uploadblobsfromrevs(repo, outgoing.missing)
297 uploadblobsfromrevs(repo, outgoing.missing)
294 return orig(ui, repo, source, filename, bundletype, outgoing, *args,
298 return orig(ui, repo, source, filename, bundletype, outgoing, *args,
295 **kwargs)
299 **kwargs)
296
300
297 def extractpointers(repo, revs):
301 def extractpointers(repo, revs):
298 """return a list of lfs pointers added by given revs"""
302 """return a list of lfs pointers added by given revs"""
299 repo.ui.debug('lfs: computing set of blobs to upload\n')
303 repo.ui.debug('lfs: computing set of blobs to upload\n')
300 pointers = {}
304 pointers = {}
301 for r in revs:
305 for r in revs:
302 ctx = repo[r]
306 ctx = repo[r]
303 for p in pointersfromctx(ctx).values():
307 for p in pointersfromctx(ctx).values():
304 pointers[p.oid()] = p
308 pointers[p.oid()] = p
305 return sorted(pointers.values())
309 return sorted(pointers.values())
306
310
307 def pointersfromctx(ctx):
311 def pointersfromctx(ctx):
308 """return a dict {path: pointer} for given single changectx"""
312 """return a dict {path: pointer} for given single changectx"""
309 result = {}
313 result = {}
310 for f in ctx.files():
314 for f in ctx.files():
311 if f not in ctx:
315 if f not in ctx:
312 continue
316 continue
313 fctx = ctx[f]
317 fctx = ctx[f]
314 if not _islfs(fctx.filelog(), fctx.filenode()):
318 if not _islfs(fctx.filelog(), fctx.filenode()):
315 continue
319 continue
316 try:
320 try:
317 result[f] = pointer.deserialize(fctx.rawdata())
321 result[f] = pointer.deserialize(fctx.rawdata())
318 except pointer.InvalidPointer as ex:
322 except pointer.InvalidPointer as ex:
319 raise error.Abort(_('lfs: corrupted pointer (%s@%s): %s\n')
323 raise error.Abort(_('lfs: corrupted pointer (%s@%s): %s\n')
320 % (f, short(ctx.node()), ex))
324 % (f, short(ctx.node()), ex))
321 return result
325 return result
322
326
323 def uploadblobs(repo, pointers):
327 def uploadblobs(repo, pointers):
324 """upload given pointers from local blobstore"""
328 """upload given pointers from local blobstore"""
325 if not pointers:
329 if not pointers:
326 return
330 return
327
331
328 remoteblob = repo.svfs.lfsremoteblobstore
332 remoteblob = repo.svfs.lfsremoteblobstore
329 remoteblob.writebatch(pointers, repo.svfs.lfslocalblobstore)
333 remoteblob.writebatch(pointers, repo.svfs.lfslocalblobstore)
330
334
331 def upgradefinishdatamigration(orig, ui, srcrepo, dstrepo, requirements):
335 def upgradefinishdatamigration(orig, ui, srcrepo, dstrepo, requirements):
332 orig(ui, srcrepo, dstrepo, requirements)
336 orig(ui, srcrepo, dstrepo, requirements)
333
337
334 srclfsvfs = srcrepo.svfs.lfslocalblobstore.vfs
338 srclfsvfs = srcrepo.svfs.lfslocalblobstore.vfs
335 dstlfsvfs = dstrepo.svfs.lfslocalblobstore.vfs
339 dstlfsvfs = dstrepo.svfs.lfslocalblobstore.vfs
336
340
337 for dirpath, dirs, files in srclfsvfs.walk():
341 for dirpath, dirs, files in srclfsvfs.walk():
338 for oid in files:
342 for oid in files:
339 ui.write(_('copying lfs blob %s\n') % oid)
343 ui.write(_('copying lfs blob %s\n') % oid)
340 lfutil.link(srclfsvfs.join(oid), dstlfsvfs.join(oid))
344 lfutil.link(srclfsvfs.join(oid), dstlfsvfs.join(oid))
341
345
342 def upgraderequirements(orig, repo):
346 def upgraderequirements(orig, repo):
343 reqs = orig(repo)
347 reqs = orig(repo)
344 if 'lfs' in repo.requirements:
348 if 'lfs' in repo.requirements:
345 reqs.add('lfs')
349 reqs.add('lfs')
346 return reqs
350 return reqs
@@ -1,975 +1,981 b''
1 # Initial setup
1 # Initial setup
2
2
3 $ cat >> $HGRCPATH << EOF
3 $ cat >> $HGRCPATH << EOF
4 > [extensions]
4 > [extensions]
5 > lfs=
5 > lfs=
6 > [lfs]
6 > [lfs]
7 > # Test deprecated config
7 > # Test deprecated config
8 > threshold=1000B
8 > threshold=1000B
9 > EOF
9 > EOF
10
10
11 $ LONG=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
11 $ LONG=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
12
12
13 # Prepare server and enable extension
13 # Prepare server and enable extension
14 $ hg init server
14 $ hg init server
15 $ hg clone -q server client
15 $ hg clone -q server client
16 $ cd client
16 $ cd client
17
17
18 # Commit small file
18 # Commit small file
19 $ echo s > smallfile
19 $ echo s > smallfile
20 $ echo '**.py = LF' > .hgeol
20 $ echo '**.py = LF' > .hgeol
21 $ hg --config lfs.track='size(">1000B") | "path:.hgeol"' commit -Aqm "add small file"
21 $ hg --config lfs.track='size(">1000B") | "path:.hgeol"' commit -Aqm "add small file"
22 $ hg debugdata .hgeol 0
22 $ hg debugdata .hgeol 0
23 **.py = LF
23 **.py = LF
24
24
25 # Commit large file
25 # Commit large file
26 $ echo $LONG > largefile
26 $ echo $LONG > largefile
27 $ grep lfs .hg/requires
27 $ grep lfs .hg/requires
28 [1]
28 [1]
29 $ hg commit --traceback -Aqm "add large file"
29 $ hg commit --traceback -Aqm "add large file"
30 $ grep lfs .hg/requires
30 $ grep lfs .hg/requires
31 lfs
31 lfs
32
32
33 # Ensure metadata is stored
33 # Ensure metadata is stored
34 $ hg debugdata largefile 0
34 $ hg debugdata largefile 0
35 version https://git-lfs.github.com/spec/v1
35 version https://git-lfs.github.com/spec/v1
36 oid sha256:f11e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
36 oid sha256:f11e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
37 size 1501
37 size 1501
38 x-is-binary 0
38 x-is-binary 0
39
39
40 # Check the blobstore is populated
40 # Check the blobstore is populated
41 $ find .hg/store/lfs/objects | sort
41 $ find .hg/store/lfs/objects | sort
42 .hg/store/lfs/objects
42 .hg/store/lfs/objects
43 .hg/store/lfs/objects/f1
43 .hg/store/lfs/objects/f1
44 .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
44 .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
45
45
46 # Check the blob stored contains the actual contents of the file
46 # Check the blob stored contains the actual contents of the file
47 $ cat .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
47 $ cat .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
48 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
48 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
49
49
50 # Push changes to the server
50 # Push changes to the server
51
51
52 $ hg push
52 $ hg push
53 pushing to $TESTTMP/server
53 pushing to $TESTTMP/server
54 searching for changes
54 searching for changes
55 abort: lfs.url needs to be configured
55 abort: lfs.url needs to be configured
56 [255]
56 [255]
57
57
58 $ cat >> $HGRCPATH << EOF
58 $ cat >> $HGRCPATH << EOF
59 > [lfs]
59 > [lfs]
60 > url=file:$TESTTMP/dummy-remote/
60 > url=file:$TESTTMP/dummy-remote/
61 > EOF
61 > EOF
62
62
63 Push to a local non-lfs repo with the extension enabled will add the
63 Push to a local non-lfs repo with the extension enabled will add the
64 lfs requirement
64 lfs requirement
65
65
66 $ grep lfs $TESTTMP/server/.hg/requires
66 $ grep lfs $TESTTMP/server/.hg/requires
67 [1]
67 [1]
68 $ hg push -v | egrep -v '^(uncompressed| )'
68 $ hg push -v | egrep -v '^(uncompressed| )'
69 pushing to $TESTTMP/server
69 pushing to $TESTTMP/server
70 searching for changes
70 searching for changes
71 lfs: found f11e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b in the local lfs store
71 lfs: found f11e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b in the local lfs store
72 2 changesets found
72 2 changesets found
73 adding changesets
73 adding changesets
74 adding manifests
74 adding manifests
75 adding file changes
75 adding file changes
76 added 2 changesets with 3 changes to 3 files
76 added 2 changesets with 3 changes to 3 files
77 calling hook pretxnchangegroup.lfs: hgext.lfs.checkrequireslfs
77 calling hook pretxnchangegroup.lfs: hgext.lfs.checkrequireslfs
78 $ grep lfs $TESTTMP/server/.hg/requires
78 $ grep lfs $TESTTMP/server/.hg/requires
79 lfs
79 lfs
80
80
81 # Unknown URL scheme
81 # Unknown URL scheme
82
82
83 $ hg push --config lfs.url=ftp://foobar
83 $ hg push --config lfs.url=ftp://foobar
84 abort: lfs: unknown url scheme: ftp
84 abort: lfs: unknown url scheme: ftp
85 [255]
85 [255]
86
86
87 $ cd ../
87 $ cd ../
88
88
89 # Initialize new client (not cloning) and setup extension
89 # Initialize new client (not cloning) and setup extension
90 $ hg init client2
90 $ hg init client2
91 $ cd client2
91 $ cd client2
92 $ cat >> .hg/hgrc <<EOF
92 $ cat >> .hg/hgrc <<EOF
93 > [paths]
93 > [paths]
94 > default = $TESTTMP/server
94 > default = $TESTTMP/server
95 > EOF
95 > EOF
96
96
97 # Pull from server
97 # Pull from server
98
98
99 Pulling a local lfs repo into a local non-lfs repo with the extension
99 Pulling a local lfs repo into a local non-lfs repo with the extension
100 enabled adds the lfs requirement
100 enabled adds the lfs requirement
101
101
102 $ grep lfs .hg/requires $TESTTMP/server/.hg/requires
102 $ grep lfs .hg/requires $TESTTMP/server/.hg/requires
103 $TESTTMP/server/.hg/requires:lfs
103 $TESTTMP/server/.hg/requires:lfs
104 $ hg pull default
104 $ hg pull default
105 pulling from $TESTTMP/server
105 pulling from $TESTTMP/server
106 requesting all changes
106 requesting all changes
107 adding changesets
107 adding changesets
108 adding manifests
108 adding manifests
109 adding file changes
109 adding file changes
110 added 2 changesets with 3 changes to 3 files
110 added 2 changesets with 3 changes to 3 files
111 new changesets 0ead593177f7:b88141481348
111 new changesets 0ead593177f7:b88141481348
112 (run 'hg update' to get a working copy)
112 (run 'hg update' to get a working copy)
113 $ grep lfs .hg/requires $TESTTMP/server/.hg/requires
113 $ grep lfs .hg/requires $TESTTMP/server/.hg/requires
114 .hg/requires:lfs
114 .hg/requires:lfs
115 $TESTTMP/server/.hg/requires:lfs
115 $TESTTMP/server/.hg/requires:lfs
116
116
117 # Check the blobstore is not yet populated
117 # Check the blobstore is not yet populated
118 $ [ -d .hg/store/lfs/objects ]
118 $ [ -d .hg/store/lfs/objects ]
119 [1]
119 [1]
120
120
121 # Update to the last revision containing the large file
121 # Update to the last revision containing the large file
122 $ hg update
122 $ hg update
123 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
123 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
124
124
125 # Check the blobstore has been populated on update
125 # Check the blobstore has been populated on update
126 $ find .hg/store/lfs/objects | sort
126 $ find .hg/store/lfs/objects | sort
127 .hg/store/lfs/objects
127 .hg/store/lfs/objects
128 .hg/store/lfs/objects/f1
128 .hg/store/lfs/objects/f1
129 .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
129 .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
130
130
131 # Check the contents of the file are fetched from blobstore when requested
131 # Check the contents of the file are fetched from blobstore when requested
132 $ hg cat -r . largefile
132 $ hg cat -r . largefile
133 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
133 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
134
134
135 # Check the file has been copied in the working copy
135 # Check the file has been copied in the working copy
136 $ cat largefile
136 $ cat largefile
137 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
137 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
138
138
139 $ cd ..
139 $ cd ..
140
140
141 # Check rename, and switch between large and small files
141 # Check rename, and switch between large and small files
142
142
143 $ hg init repo3
143 $ hg init repo3
144 $ cd repo3
144 $ cd repo3
145 $ cat >> .hg/hgrc << EOF
145 $ cat >> .hg/hgrc << EOF
146 > [lfs]
146 > [lfs]
147 > track=size(">10B")
147 > track=size(">10B")
148 > EOF
148 > EOF
149
149
150 $ echo LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS > large
150 $ echo LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS > large
151 $ echo SHORTER > small
151 $ echo SHORTER > small
152 $ hg add . -q
152 $ hg add . -q
153 $ hg commit -m 'commit with lfs content'
153 $ hg commit -m 'commit with lfs content'
154
154
155 $ hg mv large l
155 $ hg mv large l
156 $ hg mv small s
156 $ hg mv small s
157 $ hg commit -m 'renames'
157 $ hg commit -m 'renames'
158
158
159 $ echo SHORT > l
159 $ echo SHORT > l
160 $ echo BECOME-LARGER-FROM-SHORTER > s
160 $ echo BECOME-LARGER-FROM-SHORTER > s
161 $ hg commit -m 'large to small, small to large'
161 $ hg commit -m 'large to small, small to large'
162
162
163 $ echo 1 >> l
163 $ echo 1 >> l
164 $ echo 2 >> s
164 $ echo 2 >> s
165 $ hg commit -m 'random modifications'
165 $ hg commit -m 'random modifications'
166
166
167 $ echo RESTORE-TO-BE-LARGE > l
167 $ echo RESTORE-TO-BE-LARGE > l
168 $ echo SHORTER > s
168 $ echo SHORTER > s
169 $ hg commit -m 'switch large and small again'
169 $ hg commit -m 'switch large and small again'
170
170
171 # Test lfs_files template
171 # Test lfs_files template
172
172
173 $ hg log -r 'all()' -T '{rev} {join(lfs_files, ", ")}\n'
173 $ hg log -r 'all()' -T '{rev} {join(lfs_files, ", ")}\n'
174 0 large
174 0 large
175 1 l
175 1 l
176 2 s
176 2 s
177 3 s
177 3 s
178 4 l
178 4 l
179
179
180 # Push and pull the above repo
180 # Push and pull the above repo
181
181
182 $ hg --cwd .. init repo4
182 $ hg --cwd .. init repo4
183 $ hg push ../repo4
183 $ hg push ../repo4
184 pushing to ../repo4
184 pushing to ../repo4
185 searching for changes
185 searching for changes
186 adding changesets
186 adding changesets
187 adding manifests
187 adding manifests
188 adding file changes
188 adding file changes
189 added 5 changesets with 10 changes to 4 files
189 added 5 changesets with 10 changes to 4 files
190
190
191 $ hg --cwd .. init repo5
191 $ hg --cwd .. init repo5
192 $ hg --cwd ../repo5 pull ../repo3
192 $ hg --cwd ../repo5 pull ../repo3
193 pulling from ../repo3
193 pulling from ../repo3
194 requesting all changes
194 requesting all changes
195 adding changesets
195 adding changesets
196 adding manifests
196 adding manifests
197 adding file changes
197 adding file changes
198 added 5 changesets with 10 changes to 4 files
198 added 5 changesets with 10 changes to 4 files
199 new changesets fd47a419c4f7:5adf850972b9
199 new changesets fd47a419c4f7:5adf850972b9
200 (run 'hg update' to get a working copy)
200 (run 'hg update' to get a working copy)
201
201
202 $ cd ..
202 $ cd ..
203
203
204 # Test clone
204 # Test clone
205
205
206 $ hg init repo6
206 $ hg init repo6
207 $ cd repo6
207 $ cd repo6
208 $ cat >> .hg/hgrc << EOF
208 $ cat >> .hg/hgrc << EOF
209 > [lfs]
209 > [lfs]
210 > track=size(">30B")
210 > track=size(">30B")
211 > EOF
211 > EOF
212
212
213 $ echo LARGE-BECAUSE-IT-IS-MORE-THAN-30-BYTES > large
213 $ echo LARGE-BECAUSE-IT-IS-MORE-THAN-30-BYTES > large
214 $ echo SMALL > small
214 $ echo SMALL > small
215 $ hg commit -Aqm 'create a lfs file' large small
215 $ hg commit -Aqm 'create a lfs file' large small
216 $ hg debuglfsupload -r 'all()' -v
216 $ hg debuglfsupload -r 'all()' -v
217 lfs: found 8e92251415339ae9b148c8da89ed5ec665905166a1ab11b09dca8fad83344738 in the local lfs store
217 lfs: found 8e92251415339ae9b148c8da89ed5ec665905166a1ab11b09dca8fad83344738 in the local lfs store
218
218
219 $ cd ..
219 $ cd ..
220
220
221 $ hg clone repo6 repo7
221 $ hg clone repo6 repo7
222 updating to branch default
222 updating to branch default
223 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
223 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
224 $ cd repo7
224 $ cd repo7
225 $ hg config extensions --debug | grep lfs
225 $ hg config extensions --debug | grep lfs
226 $TESTTMP/repo7/.hg/hgrc:*: extensions.lfs= (glob)
226 $TESTTMP/repo7/.hg/hgrc:*: extensions.lfs= (glob)
227 $ cat large
227 $ cat large
228 LARGE-BECAUSE-IT-IS-MORE-THAN-30-BYTES
228 LARGE-BECAUSE-IT-IS-MORE-THAN-30-BYTES
229 $ cat small
229 $ cat small
230 SMALL
230 SMALL
231
231
232 $ cd ..
232 $ cd ..
233
233
234 $ hg --config extensions.share= share repo7 sharedrepo
234 $ hg --config extensions.share= share repo7 sharedrepo
235 updating working directory
235 updating working directory
236 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
236 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
237 $ hg -R sharedrepo config extensions --debug | grep lfs
237 $ hg -R sharedrepo config extensions --debug | grep lfs
238 $TESTTMP/sharedrepo/.hg/hgrc:*: extensions.lfs= (glob)
238 $TESTTMP/sharedrepo/.hg/hgrc:*: extensions.lfs= (glob)
239
239
240 # Test rename and status
240 # Test rename and status
241
241
242 $ hg init repo8
242 $ hg init repo8
243 $ cd repo8
243 $ cd repo8
244 $ cat >> .hg/hgrc << EOF
244 $ cat >> .hg/hgrc << EOF
245 > [lfs]
245 > [lfs]
246 > track=size(">10B")
246 > track=size(">10B")
247 > EOF
247 > EOF
248
248
249 $ echo THIS-IS-LFS-BECAUSE-10-BYTES > a1
249 $ echo THIS-IS-LFS-BECAUSE-10-BYTES > a1
250 $ echo SMALL > a2
250 $ echo SMALL > a2
251 $ hg commit -m a -A a1 a2
251 $ hg commit -m a -A a1 a2
252 $ hg status
252 $ hg status
253 $ hg mv a1 b1
253 $ hg mv a1 b1
254 $ hg mv a2 a1
254 $ hg mv a2 a1
255 $ hg mv b1 a2
255 $ hg mv b1 a2
256 $ hg commit -m b
256 $ hg commit -m b
257 $ hg status
257 $ hg status
258 >>> with open('a2', 'wb') as f:
258 >>> with open('a2', 'wb') as f:
259 ... f.write(b'\1\nSTART-WITH-HG-FILELOG-METADATA')
259 ... f.write(b'\1\nSTART-WITH-HG-FILELOG-METADATA')
260 >>> with open('a1', 'wb') as f:
260 >>> with open('a1', 'wb') as f:
261 ... f.write(b'\1\nMETA\n')
261 ... f.write(b'\1\nMETA\n')
262 $ hg commit -m meta
262 $ hg commit -m meta
263 $ hg status
263 $ hg status
264 $ hg log -T '{rev}: {file_copies} | {file_dels} | {file_adds}\n'
264 $ hg log -T '{rev}: {file_copies} | {file_dels} | {file_adds}\n'
265 2: | |
265 2: | |
266 1: a1 (a2)a2 (a1) | |
266 1: a1 (a2)a2 (a1) | |
267 0: | | a1 a2
267 0: | | a1 a2
268
268
269 $ for n in a1 a2; do
269 $ for n in a1 a2; do
270 > for r in 0 1 2; do
270 > for r in 0 1 2; do
271 > printf '\n%s @ %s\n' $n $r
271 > printf '\n%s @ %s\n' $n $r
272 > hg debugdata $n $r
272 > hg debugdata $n $r
273 > done
273 > done
274 > done
274 > done
275
275
276 a1 @ 0
276 a1 @ 0
277 version https://git-lfs.github.com/spec/v1
277 version https://git-lfs.github.com/spec/v1
278 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
278 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
279 size 29
279 size 29
280 x-is-binary 0
280 x-is-binary 0
281
281
282 a1 @ 1
282 a1 @ 1
283 \x01 (esc)
283 \x01 (esc)
284 copy: a2
284 copy: a2
285 copyrev: 50470ad23cf937b1f4b9f80bfe54df38e65b50d9
285 copyrev: 50470ad23cf937b1f4b9f80bfe54df38e65b50d9
286 \x01 (esc)
286 \x01 (esc)
287 SMALL
287 SMALL
288
288
289 a1 @ 2
289 a1 @ 2
290 \x01 (esc)
290 \x01 (esc)
291 \x01 (esc)
291 \x01 (esc)
292 \x01 (esc)
292 \x01 (esc)
293 META
293 META
294
294
295 a2 @ 0
295 a2 @ 0
296 SMALL
296 SMALL
297
297
298 a2 @ 1
298 a2 @ 1
299 version https://git-lfs.github.com/spec/v1
299 version https://git-lfs.github.com/spec/v1
300 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
300 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
301 size 29
301 size 29
302 x-hg-copy a1
302 x-hg-copy a1
303 x-hg-copyrev be23af27908a582af43e5cda209a5a9b319de8d4
303 x-hg-copyrev be23af27908a582af43e5cda209a5a9b319de8d4
304 x-is-binary 0
304 x-is-binary 0
305
305
306 a2 @ 2
306 a2 @ 2
307 version https://git-lfs.github.com/spec/v1
307 version https://git-lfs.github.com/spec/v1
308 oid sha256:876dadc86a8542f9798048f2c47f51dbf8e4359aed883e8ec80c5db825f0d943
308 oid sha256:876dadc86a8542f9798048f2c47f51dbf8e4359aed883e8ec80c5db825f0d943
309 size 32
309 size 32
310 x-is-binary 0
310 x-is-binary 0
311
311
312 # Verify commit hashes include rename metadata
312 # Verify commit hashes include rename metadata
313
313
314 $ hg log -T '{rev}:{node|short} {desc}\n'
314 $ hg log -T '{rev}:{node|short} {desc}\n'
315 2:0fae949de7fa meta
315 2:0fae949de7fa meta
316 1:9cd6bdffdac0 b
316 1:9cd6bdffdac0 b
317 0:7f96794915f7 a
317 0:7f96794915f7 a
318
318
319 $ cd ..
319 $ cd ..
320
320
321 # Test bundle
321 # Test bundle
322
322
323 $ hg init repo9
323 $ hg init repo9
324 $ cd repo9
324 $ cd repo9
325 $ cat >> .hg/hgrc << EOF
325 $ cat >> .hg/hgrc << EOF
326 > [lfs]
326 > [lfs]
327 > track=size(">10B")
327 > track=size(">10B")
328 > [diff]
328 > [diff]
329 > git=1
329 > git=1
330 > EOF
330 > EOF
331
331
332 $ for i in 0 single two three 4; do
332 $ for i in 0 single two three 4; do
333 > echo 'THIS-IS-LFS-'$i > a
333 > echo 'THIS-IS-LFS-'$i > a
334 > hg commit -m a-$i -A a
334 > hg commit -m a-$i -A a
335 > done
335 > done
336
336
337 $ hg update 2 -q
337 $ hg update 2 -q
338 $ echo 'THIS-IS-LFS-2-CHILD' > a
338 $ echo 'THIS-IS-LFS-2-CHILD' > a
339 $ hg commit -m branching -q
339 $ hg commit -m branching -q
340
340
341 $ hg bundle --base 1 bundle.hg -v
341 $ hg bundle --base 1 bundle.hg -v
342 lfs: found 5ab7a3739a5feec94a562d070a14f36dba7cad17e5484a4a89eea8e5f3166888 in the local lfs store
342 lfs: found 5ab7a3739a5feec94a562d070a14f36dba7cad17e5484a4a89eea8e5f3166888 in the local lfs store
343 lfs: found a9c7d1cd6ce2b9bbdf46ed9a862845228717b921c089d0d42e3bcaed29eb612e in the local lfs store
343 lfs: found a9c7d1cd6ce2b9bbdf46ed9a862845228717b921c089d0d42e3bcaed29eb612e in the local lfs store
344 lfs: found f693890c49c409ec33673b71e53f297681f76c1166daf33b2ad7ebf8b1d3237e in the local lfs store
344 lfs: found f693890c49c409ec33673b71e53f297681f76c1166daf33b2ad7ebf8b1d3237e in the local lfs store
345 lfs: found fda198fea753eb66a252e9856915e1f5cddbe41723bd4b695ece2604ad3c9f75 in the local lfs store
345 lfs: found fda198fea753eb66a252e9856915e1f5cddbe41723bd4b695ece2604ad3c9f75 in the local lfs store
346 4 changesets found
346 4 changesets found
347 uncompressed size of bundle content:
347 uncompressed size of bundle content:
348 * (changelog) (glob)
348 * (changelog) (glob)
349 * (manifests) (glob)
349 * (manifests) (glob)
350 * a (glob)
350 * a (glob)
351 $ hg --config extensions.strip= strip -r 2 --no-backup --force -q
351 $ hg --config extensions.strip= strip -r 2 --no-backup --force -q
352 $ hg -R bundle.hg log -p -T '{rev} {desc}\n' a
352 $ hg -R bundle.hg log -p -T '{rev} {desc}\n' a
353 5 branching
353 5 branching
354 diff --git a/a b/a
354 diff --git a/a b/a
355 --- a/a
355 --- a/a
356 +++ b/a
356 +++ b/a
357 @@ -1,1 +1,1 @@
357 @@ -1,1 +1,1 @@
358 -THIS-IS-LFS-two
358 -THIS-IS-LFS-two
359 +THIS-IS-LFS-2-CHILD
359 +THIS-IS-LFS-2-CHILD
360
360
361 4 a-4
361 4 a-4
362 diff --git a/a b/a
362 diff --git a/a b/a
363 --- a/a
363 --- a/a
364 +++ b/a
364 +++ b/a
365 @@ -1,1 +1,1 @@
365 @@ -1,1 +1,1 @@
366 -THIS-IS-LFS-three
366 -THIS-IS-LFS-three
367 +THIS-IS-LFS-4
367 +THIS-IS-LFS-4
368
368
369 3 a-three
369 3 a-three
370 diff --git a/a b/a
370 diff --git a/a b/a
371 --- a/a
371 --- a/a
372 +++ b/a
372 +++ b/a
373 @@ -1,1 +1,1 @@
373 @@ -1,1 +1,1 @@
374 -THIS-IS-LFS-two
374 -THIS-IS-LFS-two
375 +THIS-IS-LFS-three
375 +THIS-IS-LFS-three
376
376
377 2 a-two
377 2 a-two
378 diff --git a/a b/a
378 diff --git a/a b/a
379 --- a/a
379 --- a/a
380 +++ b/a
380 +++ b/a
381 @@ -1,1 +1,1 @@
381 @@ -1,1 +1,1 @@
382 -THIS-IS-LFS-single
382 -THIS-IS-LFS-single
383 +THIS-IS-LFS-two
383 +THIS-IS-LFS-two
384
384
385 1 a-single
385 1 a-single
386 diff --git a/a b/a
386 diff --git a/a b/a
387 --- a/a
387 --- a/a
388 +++ b/a
388 +++ b/a
389 @@ -1,1 +1,1 @@
389 @@ -1,1 +1,1 @@
390 -THIS-IS-LFS-0
390 -THIS-IS-LFS-0
391 +THIS-IS-LFS-single
391 +THIS-IS-LFS-single
392
392
393 0 a-0
393 0 a-0
394 diff --git a/a b/a
394 diff --git a/a b/a
395 new file mode 100644
395 new file mode 100644
396 --- /dev/null
396 --- /dev/null
397 +++ b/a
397 +++ b/a
398 @@ -0,0 +1,1 @@
398 @@ -0,0 +1,1 @@
399 +THIS-IS-LFS-0
399 +THIS-IS-LFS-0
400
400
401 $ hg bundle -R bundle.hg --base 1 bundle-again.hg -q
401 $ hg bundle -R bundle.hg --base 1 bundle-again.hg -q
402 $ hg -R bundle-again.hg log -p -T '{rev} {desc}\n' a
402 $ hg -R bundle-again.hg log -p -T '{rev} {desc}\n' a
403 5 branching
403 5 branching
404 diff --git a/a b/a
404 diff --git a/a b/a
405 --- a/a
405 --- a/a
406 +++ b/a
406 +++ b/a
407 @@ -1,1 +1,1 @@
407 @@ -1,1 +1,1 @@
408 -THIS-IS-LFS-two
408 -THIS-IS-LFS-two
409 +THIS-IS-LFS-2-CHILD
409 +THIS-IS-LFS-2-CHILD
410
410
411 4 a-4
411 4 a-4
412 diff --git a/a b/a
412 diff --git a/a b/a
413 --- a/a
413 --- a/a
414 +++ b/a
414 +++ b/a
415 @@ -1,1 +1,1 @@
415 @@ -1,1 +1,1 @@
416 -THIS-IS-LFS-three
416 -THIS-IS-LFS-three
417 +THIS-IS-LFS-4
417 +THIS-IS-LFS-4
418
418
419 3 a-three
419 3 a-three
420 diff --git a/a b/a
420 diff --git a/a b/a
421 --- a/a
421 --- a/a
422 +++ b/a
422 +++ b/a
423 @@ -1,1 +1,1 @@
423 @@ -1,1 +1,1 @@
424 -THIS-IS-LFS-two
424 -THIS-IS-LFS-two
425 +THIS-IS-LFS-three
425 +THIS-IS-LFS-three
426
426
427 2 a-two
427 2 a-two
428 diff --git a/a b/a
428 diff --git a/a b/a
429 --- a/a
429 --- a/a
430 +++ b/a
430 +++ b/a
431 @@ -1,1 +1,1 @@
431 @@ -1,1 +1,1 @@
432 -THIS-IS-LFS-single
432 -THIS-IS-LFS-single
433 +THIS-IS-LFS-two
433 +THIS-IS-LFS-two
434
434
435 1 a-single
435 1 a-single
436 diff --git a/a b/a
436 diff --git a/a b/a
437 --- a/a
437 --- a/a
438 +++ b/a
438 +++ b/a
439 @@ -1,1 +1,1 @@
439 @@ -1,1 +1,1 @@
440 -THIS-IS-LFS-0
440 -THIS-IS-LFS-0
441 +THIS-IS-LFS-single
441 +THIS-IS-LFS-single
442
442
443 0 a-0
443 0 a-0
444 diff --git a/a b/a
444 diff --git a/a b/a
445 new file mode 100644
445 new file mode 100644
446 --- /dev/null
446 --- /dev/null
447 +++ b/a
447 +++ b/a
448 @@ -0,0 +1,1 @@
448 @@ -0,0 +1,1 @@
449 +THIS-IS-LFS-0
449 +THIS-IS-LFS-0
450
450
451 $ cd ..
451 $ cd ..
452
452
453 # Test isbinary
453 # Test isbinary
454
454
455 $ hg init repo10
455 $ hg init repo10
456 $ cd repo10
456 $ cd repo10
457 $ cat >> .hg/hgrc << EOF
457 $ cat >> .hg/hgrc << EOF
458 > [extensions]
458 > [extensions]
459 > lfs=
459 > lfs=
460 > [lfs]
460 > [lfs]
461 > track=all()
461 > track=all()
462 > EOF
462 > EOF
463 $ $PYTHON <<'EOF'
463 $ $PYTHON <<'EOF'
464 > def write(path, content):
464 > def write(path, content):
465 > with open(path, 'wb') as f:
465 > with open(path, 'wb') as f:
466 > f.write(content)
466 > f.write(content)
467 > write('a', b'\0\0')
467 > write('a', b'\0\0')
468 > write('b', b'\1\n')
468 > write('b', b'\1\n')
469 > write('c', b'\1\n\0')
469 > write('c', b'\1\n\0')
470 > write('d', b'xx')
470 > write('d', b'xx')
471 > EOF
471 > EOF
472 $ hg add a b c d
472 $ hg add a b c d
473 $ hg diff --stat
473 $ hg diff --stat
474 a | Bin
474 a | Bin
475 b | 1 +
475 b | 1 +
476 c | Bin
476 c | Bin
477 d | 1 +
477 d | 1 +
478 4 files changed, 2 insertions(+), 0 deletions(-)
478 4 files changed, 2 insertions(+), 0 deletions(-)
479 $ hg commit -m binarytest
479 $ hg commit -m binarytest
480 $ cat > $TESTTMP/dumpbinary.py << EOF
480 $ cat > $TESTTMP/dumpbinary.py << EOF
481 > def reposetup(ui, repo):
481 > def reposetup(ui, repo):
482 > for n in 'abcd':
482 > for n in 'abcd':
483 > ui.write(('%s: binary=%s\n') % (n, repo['.'][n].isbinary()))
483 > ui.write(('%s: binary=%s\n') % (n, repo['.'][n].isbinary()))
484 > EOF
484 > EOF
485 $ hg --config extensions.dumpbinary=$TESTTMP/dumpbinary.py id --trace
485 $ hg --config extensions.dumpbinary=$TESTTMP/dumpbinary.py id --trace
486 a: binary=True
486 a: binary=True
487 b: binary=False
487 b: binary=False
488 c: binary=True
488 c: binary=True
489 d: binary=False
489 d: binary=False
490 b55353847f02 tip
490 b55353847f02 tip
491
491
492 $ cd ..
492 $ cd ..
493
493
494 # Test fctx.cmp fastpath - diff without LFS blobs
494 # Test fctx.cmp fastpath - diff without LFS blobs
495
495
496 $ hg init repo12
496 $ hg init repo12
497 $ cd repo12
497 $ cd repo12
498 $ cat >> .hg/hgrc <<EOF
498 $ cat >> .hg/hgrc <<EOF
499 > [lfs]
499 > [lfs]
500 > threshold=1
500 > threshold=1
501 > EOF
501 > EOF
502 $ cat > ../patch.diff <<EOF
502 $ cat > ../patch.diff <<EOF
503 > # HG changeset patch
503 > # HG changeset patch
504 > 2
504 > 2
505 >
505 >
506 > diff --git a/a b/a
506 > diff --git a/a b/a
507 > old mode 100644
507 > old mode 100644
508 > new mode 100755
508 > new mode 100755
509 > EOF
509 > EOF
510
510
511 $ for i in 1 2 3; do
511 $ for i in 1 2 3; do
512 > cp ../repo10/a a
512 > cp ../repo10/a a
513 > if [ $i = 3 ]; then
513 > if [ $i = 3 ]; then
514 > # make a content-only change
514 > # make a content-only change
515 > hg import -q --bypass ../patch.diff
515 > hg import -q --bypass ../patch.diff
516 > hg update -q
516 > hg update -q
517 > rm ../patch.diff
517 > rm ../patch.diff
518 > else
518 > else
519 > echo $i >> a
519 > echo $i >> a
520 > hg commit -m $i -A a
520 > hg commit -m $i -A a
521 > fi
521 > fi
522 > done
522 > done
523 $ [ -d .hg/store/lfs/objects ]
523 $ [ -d .hg/store/lfs/objects ]
524
524
525 $ cd ..
525 $ cd ..
526
526
527 $ hg clone repo12 repo13 --noupdate
527 $ hg clone repo12 repo13 --noupdate
528 $ cd repo13
528 $ cd repo13
529 $ hg log --removed -p a -T '{desc}\n' --config diff.nobinary=1 --git
529 $ hg log --removed -p a -T '{desc}\n' --config diff.nobinary=1 --git
530 2
530 2
531 diff --git a/a b/a
531 diff --git a/a b/a
532 old mode 100644
532 old mode 100644
533 new mode 100755
533 new mode 100755
534
534
535 2
535 2
536 diff --git a/a b/a
536 diff --git a/a b/a
537 Binary file a has changed
537 Binary file a has changed
538
538
539 1
539 1
540 diff --git a/a b/a
540 diff --git a/a b/a
541 new file mode 100644
541 new file mode 100644
542 Binary file a has changed
542 Binary file a has changed
543
543
544 $ [ -d .hg/store/lfs/objects ]
544 $ [ -d .hg/store/lfs/objects ]
545 [1]
545 [1]
546
546
547 $ cd ..
547 $ cd ..
548
548
549 # Test filter
549 # Test filter
550
550
551 $ hg init repo11
551 $ hg init repo11
552 $ cd repo11
552 $ cd repo11
553 $ cat >> .hg/hgrc << EOF
553 $ cat >> .hg/hgrc << EOF
554 > [lfs]
554 > [lfs]
555 > track=(**.a & size(">5B")) | (**.b & !size(">5B"))
555 > track=(**.a & size(">5B")) | (**.b & !size(">5B"))
556 > | (**.c & "path:d" & !"path:d/c.c") | size(">10B")
556 > | (**.c & "path:d" & !"path:d/c.c") | size(">10B")
557 > EOF
557 > EOF
558
558
559 $ mkdir a
559 $ mkdir a
560 $ echo aaaaaa > a/1.a
560 $ echo aaaaaa > a/1.a
561 $ echo a > a/2.a
561 $ echo a > a/2.a
562 $ echo aaaaaa > 1.b
562 $ echo aaaaaa > 1.b
563 $ echo a > 2.b
563 $ echo a > 2.b
564 $ echo a > 1.c
564 $ echo a > 1.c
565 $ mkdir d
565 $ mkdir d
566 $ echo a > d/c.c
566 $ echo a > d/c.c
567 $ echo a > d/d.c
567 $ echo a > d/d.c
568 $ echo aaaaaaaaaaaa > x
568 $ echo aaaaaaaaaaaa > x
569 $ hg add . -q
569 $ hg add . -q
570 $ hg commit -m files
570 $ hg commit -m files
571
571
572 $ for p in a/1.a a/2.a 1.b 2.b 1.c d/c.c d/d.c x; do
572 $ for p in a/1.a a/2.a 1.b 2.b 1.c d/c.c d/d.c x; do
573 > if hg debugdata $p 0 2>&1 | grep git-lfs >/dev/null; then
573 > if hg debugdata $p 0 2>&1 | grep git-lfs >/dev/null; then
574 > echo "${p}: is lfs"
574 > echo "${p}: is lfs"
575 > else
575 > else
576 > echo "${p}: not lfs"
576 > echo "${p}: not lfs"
577 > fi
577 > fi
578 > done
578 > done
579 a/1.a: is lfs
579 a/1.a: is lfs
580 a/2.a: not lfs
580 a/2.a: not lfs
581 1.b: not lfs
581 1.b: not lfs
582 2.b: is lfs
582 2.b: is lfs
583 1.c: not lfs
583 1.c: not lfs
584 d/c.c: not lfs
584 d/c.c: not lfs
585 d/d.c: is lfs
585 d/d.c: is lfs
586 x: is lfs
586 x: is lfs
587
587
588 $ cd ..
588 $ cd ..
589
589
590 # Verify the repos
590 # Verify the repos
591
591
592 $ cat > $TESTTMP/dumpflog.py << EOF
592 $ cat > $TESTTMP/dumpflog.py << EOF
593 > # print raw revision sizes, flags, and hashes for certain files
593 > # print raw revision sizes, flags, and hashes for certain files
594 > import hashlib
594 > import hashlib
595 > from mercurial import revlog
595 > from mercurial import revlog
596 > from mercurial.node import short
596 > from mercurial.node import short
597 > def hash(rawtext):
597 > def hash(rawtext):
598 > h = hashlib.sha512()
598 > h = hashlib.sha512()
599 > h.update(rawtext)
599 > h.update(rawtext)
600 > return h.hexdigest()[:4]
600 > return h.hexdigest()[:4]
601 > def reposetup(ui, repo):
601 > def reposetup(ui, repo):
602 > # these 2 files are interesting
602 > # these 2 files are interesting
603 > for name in ['l', 's']:
603 > for name in ['l', 's']:
604 > fl = repo.file(name)
604 > fl = repo.file(name)
605 > if len(fl) == 0:
605 > if len(fl) == 0:
606 > continue
606 > continue
607 > sizes = [revlog.revlog.rawsize(fl, i) for i in fl]
607 > sizes = [revlog.revlog.rawsize(fl, i) for i in fl]
608 > texts = [fl.revision(i, raw=True) for i in fl]
608 > texts = [fl.revision(i, raw=True) for i in fl]
609 > flags = [int(fl.flags(i)) for i in fl]
609 > flags = [int(fl.flags(i)) for i in fl]
610 > hashes = [hash(t) for t in texts]
610 > hashes = [hash(t) for t in texts]
611 > print(' %s: rawsizes=%r flags=%r hashes=%r'
611 > print(' %s: rawsizes=%r flags=%r hashes=%r'
612 > % (name, sizes, flags, hashes))
612 > % (name, sizes, flags, hashes))
613 > EOF
613 > EOF
614
614
615 $ for i in client client2 server repo3 repo4 repo5 repo6 repo7 repo8 repo9 \
615 $ for i in client client2 server repo3 repo4 repo5 repo6 repo7 repo8 repo9 \
616 > repo10; do
616 > repo10; do
617 > echo 'repo:' $i
617 > echo 'repo:' $i
618 > hg --cwd $i verify --config extensions.dumpflog=$TESTTMP/dumpflog.py -q
618 > hg --cwd $i verify --config extensions.dumpflog=$TESTTMP/dumpflog.py -q
619 > done
619 > done
620 repo: client
620 repo: client
621 repo: client2
621 repo: client2
622 repo: server
622 repo: server
623 repo: repo3
623 repo: repo3
624 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
624 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
625 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
625 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
626 repo: repo4
626 repo: repo4
627 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
627 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
628 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
628 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
629 repo: repo5
629 repo: repo5
630 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
630 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
631 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
631 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
632 repo: repo6
632 repo: repo6
633 repo: repo7
633 repo: repo7
634 repo: repo8
634 repo: repo8
635 repo: repo9
635 repo: repo9
636 repo: repo10
636 repo: repo10
637
637
638 repo13 doesn't have any cached lfs files and its source never pushed its
638 repo13 doesn't have any cached lfs files and its source never pushed its
639 files. Therefore, the files don't exist in the remote store. Use the files in
639 files. Therefore, the files don't exist in the remote store. Use the files in
640 the user cache.
640 the user cache.
641
641
642 $ test -d $TESTTMP/repo13/.hg/store/lfs/objects
642 $ test -d $TESTTMP/repo13/.hg/store/lfs/objects
643 [1]
643 [1]
644
644
645 $ hg --config extensions.share= share repo13 repo14
645 $ hg --config extensions.share= share repo13 repo14
646 updating working directory
646 updating working directory
647 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
647 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
648 $ hg -R repo14 -q verify
648 $ hg -R repo14 -q verify
649
649
650 $ hg clone repo13 repo15
650 $ hg clone repo13 repo15
651 updating to branch default
651 updating to branch default
652 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
652 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
653 $ hg -R repo15 -q verify
653 $ hg -R repo15 -q verify
654
654
655 If the source repo doesn't have the blob (maybe it was pulled or cloned with
655 If the source repo doesn't have the blob (maybe it was pulled or cloned with
656 --noupdate), the blob is still accessible via the global cache to send to the
656 --noupdate), the blob is still accessible via the global cache to send to the
657 remote store.
657 remote store.
658
658
659 $ rm -rf $TESTTMP/repo15/.hg/store/lfs
659 $ rm -rf $TESTTMP/repo15/.hg/store/lfs
660 $ hg init repo16
660 $ hg init repo16
661 $ hg -R repo15 push repo16
661 $ hg -R repo15 push repo16
662 pushing to repo16
662 pushing to repo16
663 searching for changes
663 searching for changes
664 adding changesets
664 adding changesets
665 adding manifests
665 adding manifests
666 adding file changes
666 adding file changes
667 added 3 changesets with 2 changes to 1 files
667 added 3 changesets with 2 changes to 1 files
668 $ hg -R repo15 -q verify
668 $ hg -R repo15 -q verify
669
669
670 Test damaged file scenarios. (This also damages the usercache because of the
670 Test damaged file scenarios. (This also damages the usercache because of the
671 hardlinks.)
671 hardlinks.)
672
672
673 $ echo 'damage' >> repo5/.hg/store/lfs/objects/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
673 $ echo 'damage' >> repo5/.hg/store/lfs/objects/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
674
674
675 Repo with damaged lfs objects in any revision will fail verification.
675 Repo with damaged lfs objects in any revision will fail verification.
676
676
677 $ hg -R repo5 verify
677 $ hg -R repo5 verify
678 checking changesets
678 checking changesets
679 checking manifests
679 checking manifests
680 crosschecking files in changesets and manifests
680 crosschecking files in changesets and manifests
681 checking files
681 checking files
682 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
682 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
683 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
683 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
684 4 files, 5 changesets, 10 total revisions
684 4 files, 5 changesets, 10 total revisions
685 2 integrity errors encountered!
685 2 integrity errors encountered!
686 (first damaged changeset appears to be 0)
686 (first damaged changeset appears to be 0)
687 [1]
687 [1]
688
688
689 Updates work after cloning a damaged repo, if the damaged lfs objects aren't in
689 Updates work after cloning a damaged repo, if the damaged lfs objects aren't in
690 the update destination. Those objects won't be added to the new repo's store
690 the update destination. Those objects won't be added to the new repo's store
691 because they aren't accessed.
691 because they aren't accessed.
692
692
693 $ hg clone -v repo5 fromcorrupt
693 $ hg clone -v repo5 fromcorrupt
694 updating to branch default
694 updating to branch default
695 resolving manifests
695 resolving manifests
696 getting l
696 getting l
697 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the usercache
697 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the usercache
698 getting s
698 getting s
699 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
699 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
700 $ test -f fromcorrupt/.hg/store/lfs/objects/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
700 $ test -f fromcorrupt/.hg/store/lfs/objects/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
701 [1]
701 [1]
702
702
703 Verify will copy/link all lfs objects into the local store that aren't already
703 Verify will copy/link all lfs objects into the local store that aren't already
704 present. Bypass the corrupted usercache to show that verify works when fed by
704 present. Bypass the corrupted usercache to show that verify works when fed by
705 the (uncorrupted) remote store.
705 the (uncorrupted) remote store.
706
706
707 $ hg -R fromcorrupt --config lfs.usercache=emptycache verify -v
707 $ hg -R fromcorrupt --config lfs.usercache=emptycache verify -v
708 repository uses revlog format 1
708 repository uses revlog format 1
709 checking changesets
709 checking changesets
710 checking manifests
710 checking manifests
711 crosschecking files in changesets and manifests
711 crosschecking files in changesets and manifests
712 checking files
712 checking files
713 lfs: adding 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e to the usercache
713 lfs: adding 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e to the usercache
714 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
714 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
715 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
715 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
716 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
716 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
717 lfs: adding 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 to the usercache
717 lfs: adding 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 to the usercache
718 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
718 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
719 lfs: adding b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c to the usercache
719 lfs: adding b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c to the usercache
720 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
720 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
721 4 files, 5 changesets, 10 total revisions
721 4 files, 5 changesets, 10 total revisions
722
722
723 Verify will not copy/link a corrupted file from the usercache into the local
723 Verify will not copy/link a corrupted file from the usercache into the local
724 store, and poison it. (The verify with a good remote now works.)
724 store, and poison it. (The verify with a good remote now works.)
725
725
726 $ rm -r fromcorrupt/.hg/store/lfs/objects/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
726 $ rm -r fromcorrupt/.hg/store/lfs/objects/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
727 $ hg -R fromcorrupt verify -v
727 $ hg -R fromcorrupt verify -v
728 repository uses revlog format 1
728 repository uses revlog format 1
729 checking changesets
729 checking changesets
730 checking manifests
730 checking manifests
731 crosschecking files in changesets and manifests
731 crosschecking files in changesets and manifests
732 checking files
732 checking files
733 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
733 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
734 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
734 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
735 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
735 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
736 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
736 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
737 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
737 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
738 4 files, 5 changesets, 10 total revisions
738 4 files, 5 changesets, 10 total revisions
739 2 integrity errors encountered!
739 2 integrity errors encountered!
740 (first damaged changeset appears to be 0)
740 (first damaged changeset appears to be 0)
741 [1]
741 [1]
742 $ hg -R fromcorrupt --config lfs.usercache=emptycache verify -v
742 $ hg -R fromcorrupt --config lfs.usercache=emptycache verify -v
743 repository uses revlog format 1
743 repository uses revlog format 1
744 checking changesets
744 checking changesets
745 checking manifests
745 checking manifests
746 crosschecking files in changesets and manifests
746 crosschecking files in changesets and manifests
747 checking files
747 checking files
748 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the usercache
748 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the usercache
749 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
749 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
750 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
750 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
751 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
751 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
752 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
752 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
753 4 files, 5 changesets, 10 total revisions
753 4 files, 5 changesets, 10 total revisions
754
754
755 Damaging a file required by the update destination fails the update.
755 Damaging a file required by the update destination fails the update.
756
756
757 $ echo 'damage' >> $TESTTMP/dummy-remote/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
757 $ echo 'damage' >> $TESTTMP/dummy-remote/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
758 $ hg --config lfs.usercache=emptycache clone -v repo5 fromcorrupt2
758 $ hg --config lfs.usercache=emptycache clone -v repo5 fromcorrupt2
759 updating to branch default
759 updating to branch default
760 resolving manifests
760 resolving manifests
761 getting l
761 getting l
762 abort: corrupt remote lfs object: 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
762 abort: corrupt remote lfs object: 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
763 [255]
763 [255]
764
764
765 A corrupted lfs blob is not transferred from a file://remotestore to the
765 A corrupted lfs blob is not transferred from a file://remotestore to the
766 usercache or local store.
766 usercache or local store.
767
767
768 $ test -f emptycache/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
768 $ test -f emptycache/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
769 [1]
769 [1]
770 $ test -f fromcorrupt2/.hg/store/lfs/objects/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
770 $ test -f fromcorrupt2/.hg/store/lfs/objects/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
771 [1]
771 [1]
772
772
773 $ hg -R fromcorrupt2 verify
773 $ hg -R fromcorrupt2 verify
774 checking changesets
774 checking changesets
775 checking manifests
775 checking manifests
776 crosschecking files in changesets and manifests
776 crosschecking files in changesets and manifests
777 checking files
777 checking files
778 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
778 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
779 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
779 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
780 4 files, 5 changesets, 10 total revisions
780 4 files, 5 changesets, 10 total revisions
781 2 integrity errors encountered!
781 2 integrity errors encountered!
782 (first damaged changeset appears to be 0)
782 (first damaged changeset appears to be 0)
783 [1]
783 [1]
784
784
785 Corrupt local files are not sent upstream. (The alternate dummy remote
785 Corrupt local files are not sent upstream. (The alternate dummy remote
786 avoids the corrupt lfs object in the original remote.)
786 avoids the corrupt lfs object in the original remote.)
787
787
788 $ mkdir $TESTTMP/dummy-remote2
788 $ mkdir $TESTTMP/dummy-remote2
789 $ hg init dest
789 $ hg init dest
790 $ hg -R fromcorrupt2 --config lfs.url=file:///$TESTTMP/dummy-remote2 push -v dest
790 $ hg -R fromcorrupt2 --config lfs.url=file:///$TESTTMP/dummy-remote2 push -v dest
791 pushing to dest
791 pushing to dest
792 searching for changes
792 searching for changes
793 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
793 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
794 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
794 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
795 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
795 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
796 abort: detected corrupt lfs object: 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
796 abort: detected corrupt lfs object: 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
797 (run hg verify)
797 (run hg verify)
798 [255]
798 [255]
799
799
800 $ hg -R fromcorrupt2 --config lfs.url=file:///$TESTTMP/dummy-remote2 verify -v
800 $ hg -R fromcorrupt2 --config lfs.url=file:///$TESTTMP/dummy-remote2 verify -v
801 repository uses revlog format 1
801 repository uses revlog format 1
802 checking changesets
802 checking changesets
803 checking manifests
803 checking manifests
804 crosschecking files in changesets and manifests
804 crosschecking files in changesets and manifests
805 checking files
805 checking files
806 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
806 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
807 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
807 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
808 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
808 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
809 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
809 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
810 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
810 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
811 4 files, 5 changesets, 10 total revisions
811 4 files, 5 changesets, 10 total revisions
812 2 integrity errors encountered!
812 2 integrity errors encountered!
813 (first damaged changeset appears to be 0)
813 (first damaged changeset appears to be 0)
814 [1]
814 [1]
815
815
816 $ cat $TESTTMP/dummy-remote2/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b | $TESTDIR/f --sha256
816 $ cat $TESTTMP/dummy-remote2/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b | $TESTDIR/f --sha256
817 sha256=22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
817 sha256=22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
818 $ cat fromcorrupt2/.hg/store/lfs/objects/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b | $TESTDIR/f --sha256
818 $ cat fromcorrupt2/.hg/store/lfs/objects/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b | $TESTDIR/f --sha256
819 sha256=22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
819 sha256=22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
820 $ test -f $TESTTMP/dummy-remote2/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
820 $ test -f $TESTTMP/dummy-remote2/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
821 [1]
821 [1]
822
822
823 Accessing a corrupt file will complain
823 Accessing a corrupt file will complain
824
824
825 $ hg --cwd fromcorrupt2 cat -r 0 large
825 $ hg --cwd fromcorrupt2 cat -r 0 large
826 abort: integrity check failed on data/large.i:0!
826 abort: integrity check failed on data/large.i:0!
827 [255]
827 [255]
828
828
829 lfs -> normal -> lfs round trip conversions are possible. The 'none()'
829 lfs -> normal -> lfs round trip conversions are possible. The 'none()'
830 predicate on the command line will override whatever is configured globally and
830 predicate on the command line will override whatever is configured globally and
831 locally, and ensures everything converts to a regular file. For lfs -> normal,
831 locally, and ensures everything converts to a regular file. For lfs -> normal,
832 there's no 'lfs' destination repo requirement. For normal -> lfs, there is.
832 there's no 'lfs' destination repo requirement. For normal -> lfs, there is.
833
833
834 $ hg --config extensions.convert= --config 'lfs.track=none()' \
834 $ hg --config extensions.convert= --config 'lfs.track=none()' \
835 > convert repo8 convert_normal
835 > convert repo8 convert_normal
836 initializing destination convert_normal repository
836 initializing destination convert_normal repository
837 scanning source...
837 scanning source...
838 sorting...
838 sorting...
839 converting...
839 converting...
840 2 a
840 2 a
841 1 b
841 1 b
842 0 meta
842 0 meta
843 $ grep 'lfs' convert_normal/.hg/requires
843 $ grep 'lfs' convert_normal/.hg/requires
844 [1]
844 [1]
845 $ hg --cwd convert_normal debugdata a1 0
845 $ hg --cwd convert_normal cat a1 -r 0 -T '{rawdata}'
846 THIS-IS-LFS-BECAUSE-10-BYTES
846 THIS-IS-LFS-BECAUSE-10-BYTES
847
847
848 $ hg --config extensions.convert= --config lfs.threshold=10B \
848 $ hg --config extensions.convert= --config lfs.threshold=10B \
849 > convert convert_normal convert_lfs
849 > convert convert_normal convert_lfs
850 initializing destination convert_lfs repository
850 initializing destination convert_lfs repository
851 scanning source...
851 scanning source...
852 sorting...
852 sorting...
853 converting...
853 converting...
854 2 a
854 2 a
855 1 b
855 1 b
856 0 meta
856 0 meta
857
858 $ hg --cwd convert_lfs cat -r 0 a1 -T '{rawdata}'
859 version https://git-lfs.github.com/spec/v1
860 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
861 size 29
862 x-is-binary 0
857 $ hg --cwd convert_lfs debugdata a1 0
863 $ hg --cwd convert_lfs debugdata a1 0
858 version https://git-lfs.github.com/spec/v1
864 version https://git-lfs.github.com/spec/v1
859 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
865 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
860 size 29
866 size 29
861 x-is-binary 0
867 x-is-binary 0
862 $ hg --cwd convert_lfs \
868 $ hg --cwd convert_lfs \
863 > log -r 'all()' -T '{rev}: {lfs_files % "{file}: {oid}\n"}'
869 > log -r 'all()' -T '{rev}: {lfs_files % "{file}: {oid}\n"}'
864 0: a1: 5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
870 0: a1: 5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
865 1: a2: 5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
871 1: a2: 5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
866 2: a2: 876dadc86a8542f9798048f2c47f51dbf8e4359aed883e8ec80c5db825f0d943
872 2: a2: 876dadc86a8542f9798048f2c47f51dbf8e4359aed883e8ec80c5db825f0d943
867
873
868 $ grep 'lfs' convert_lfs/.hg/requires
874 $ grep 'lfs' convert_lfs/.hg/requires
869 lfs
875 lfs
870
876
871 The hashes in all stages of the conversion are unchanged.
877 The hashes in all stages of the conversion are unchanged.
872
878
873 $ hg -R repo8 log -T '{node|short}\n'
879 $ hg -R repo8 log -T '{node|short}\n'
874 0fae949de7fa
880 0fae949de7fa
875 9cd6bdffdac0
881 9cd6bdffdac0
876 7f96794915f7
882 7f96794915f7
877 $ hg -R convert_normal log -T '{node|short}\n'
883 $ hg -R convert_normal log -T '{node|short}\n'
878 0fae949de7fa
884 0fae949de7fa
879 9cd6bdffdac0
885 9cd6bdffdac0
880 7f96794915f7
886 7f96794915f7
881 $ hg -R convert_lfs log -T '{node|short}\n'
887 $ hg -R convert_lfs log -T '{node|short}\n'
882 0fae949de7fa
888 0fae949de7fa
883 9cd6bdffdac0
889 9cd6bdffdac0
884 7f96794915f7
890 7f96794915f7
885
891
886 This convert is trickier, because it contains deleted files (via `hg mv`)
892 This convert is trickier, because it contains deleted files (via `hg mv`)
887
893
888 $ hg --config extensions.convert= --config lfs.threshold=1000M \
894 $ hg --config extensions.convert= --config lfs.threshold=1000M \
889 > convert repo3 convert_normal2
895 > convert repo3 convert_normal2
890 initializing destination convert_normal2 repository
896 initializing destination convert_normal2 repository
891 scanning source...
897 scanning source...
892 sorting...
898 sorting...
893 converting...
899 converting...
894 4 commit with lfs content
900 4 commit with lfs content
895 3 renames
901 3 renames
896 2 large to small, small to large
902 2 large to small, small to large
897 1 random modifications
903 1 random modifications
898 0 switch large and small again
904 0 switch large and small again
899 $ grep 'lfs' convert_normal2/.hg/requires
905 $ grep 'lfs' convert_normal2/.hg/requires
900 [1]
906 [1]
901 $ hg --cwd convert_normal2 debugdata large 0
907 $ hg --cwd convert_normal2 debugdata large 0
902 LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS
908 LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS
903
909
904 $ hg --config extensions.convert= --config lfs.threshold=10B \
910 $ hg --config extensions.convert= --config lfs.threshold=10B \
905 > convert convert_normal2 convert_lfs2
911 > convert convert_normal2 convert_lfs2
906 initializing destination convert_lfs2 repository
912 initializing destination convert_lfs2 repository
907 scanning source...
913 scanning source...
908 sorting...
914 sorting...
909 converting...
915 converting...
910 4 commit with lfs content
916 4 commit with lfs content
911 3 renames
917 3 renames
912 2 large to small, small to large
918 2 large to small, small to large
913 1 random modifications
919 1 random modifications
914 0 switch large and small again
920 0 switch large and small again
915 $ grep 'lfs' convert_lfs2/.hg/requires
921 $ grep 'lfs' convert_lfs2/.hg/requires
916 lfs
922 lfs
917 $ hg --cwd convert_lfs2 debugdata large 0
923 $ hg --cwd convert_lfs2 debugdata large 0
918 version https://git-lfs.github.com/spec/v1
924 version https://git-lfs.github.com/spec/v1
919 oid sha256:66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
925 oid sha256:66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
920 size 39
926 size 39
921 x-is-binary 0
927 x-is-binary 0
922
928
923 $ hg -R convert_lfs2 config --debug extensions | grep lfs
929 $ hg -R convert_lfs2 config --debug extensions | grep lfs
924 $TESTTMP/convert_lfs2/.hg/hgrc:*: extensions.lfs= (glob)
930 $TESTTMP/convert_lfs2/.hg/hgrc:*: extensions.lfs= (glob)
925
931
926 Committing deleted files works:
932 Committing deleted files works:
927
933
928 $ hg init $TESTTMP/repo-del
934 $ hg init $TESTTMP/repo-del
929 $ cd $TESTTMP/repo-del
935 $ cd $TESTTMP/repo-del
930 $ echo 1 > A
936 $ echo 1 > A
931 $ hg commit -m 'add A' -A A
937 $ hg commit -m 'add A' -A A
932 $ hg rm A
938 $ hg rm A
933 $ hg commit -m 'rm A'
939 $ hg commit -m 'rm A'
934 $ cd ..
940 $ cd ..
935
941
936 Unbundling adds a requirement to a non-lfs repo, if necessary.
942 Unbundling adds a requirement to a non-lfs repo, if necessary.
937
943
938 $ hg bundle -R $TESTTMP/repo-del -qr 0 --base null nolfs.hg
944 $ hg bundle -R $TESTTMP/repo-del -qr 0 --base null nolfs.hg
939 $ hg bundle -R convert_lfs2 -qr tip --base null lfs.hg
945 $ hg bundle -R convert_lfs2 -qr tip --base null lfs.hg
940 $ hg init unbundle
946 $ hg init unbundle
941 $ hg pull -R unbundle -q nolfs.hg
947 $ hg pull -R unbundle -q nolfs.hg
942 $ grep lfs unbundle/.hg/requires
948 $ grep lfs unbundle/.hg/requires
943 [1]
949 [1]
944 $ hg pull -R unbundle -q lfs.hg
950 $ hg pull -R unbundle -q lfs.hg
945 $ grep lfs unbundle/.hg/requires
951 $ grep lfs unbundle/.hg/requires
946 lfs
952 lfs
947
953
948 $ hg init no_lfs
954 $ hg init no_lfs
949 $ cat >> no_lfs/.hg/hgrc <<EOF
955 $ cat >> no_lfs/.hg/hgrc <<EOF
950 > [experimental]
956 > [experimental]
951 > changegroup3 = True
957 > changegroup3 = True
952 > [extensions]
958 > [extensions]
953 > lfs=!
959 > lfs=!
954 > EOF
960 > EOF
955 $ cp -R no_lfs no_lfs2
961 $ cp -R no_lfs no_lfs2
956
962
957 Pushing from a local lfs repo to a local repo without an lfs requirement and
963 Pushing from a local lfs repo to a local repo without an lfs requirement and
958 with lfs disabled, fails.
964 with lfs disabled, fails.
959
965
960 $ hg push -R convert_lfs2 no_lfs
966 $ hg push -R convert_lfs2 no_lfs
961 pushing to no_lfs
967 pushing to no_lfs
962 abort: required features are not supported in the destination: lfs
968 abort: required features are not supported in the destination: lfs
963 [255]
969 [255]
964 $ grep lfs no_lfs/.hg/requires
970 $ grep lfs no_lfs/.hg/requires
965 [1]
971 [1]
966
972
967 Pulling from a local lfs repo to a local repo without an lfs requirement and
973 Pulling from a local lfs repo to a local repo without an lfs requirement and
968 with lfs disabled, fails.
974 with lfs disabled, fails.
969
975
970 $ hg pull -R no_lfs2 convert_lfs2
976 $ hg pull -R no_lfs2 convert_lfs2
971 pulling from convert_lfs2
977 pulling from convert_lfs2
972 abort: required features are not supported in the destination: lfs
978 abort: required features are not supported in the destination: lfs
973 [255]
979 [255]
974 $ grep lfs no_lfs2/.hg/requires
980 $ grep lfs no_lfs2/.hg/requires
975 [1]
981 [1]
General Comments 0
You need to be logged in to leave comments. Login now