##// END OF EJS Templates
lfs: enable the extension locally after cloning a repo with 'lfs' requirement...
Matt Harbison -
r35214:24aa4853 default
parent child Browse files
Show More
@@ -1,180 +1,183 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 # size of a file to make it use LFS
22 # size of a file to make it use LFS
23 threshold = 10M
23 threshold = 10M
24
24
25 # how many times to retry before giving up on transferring an object
25 # how many times to retry before giving up on transferring an object
26 retry = 5
26 retry = 5
27 """
27 """
28
28
29 from __future__ import absolute_import
29 from __future__ import absolute_import
30
30
31 from mercurial.i18n import _
31 from mercurial.i18n import _
32
32
33 from mercurial import (
33 from mercurial import (
34 bundle2,
34 bundle2,
35 changegroup,
35 changegroup,
36 context,
36 context,
37 exchange,
37 exchange,
38 extensions,
38 extensions,
39 filelog,
39 filelog,
40 hg,
40 localrepo,
41 localrepo,
41 registrar,
42 registrar,
42 revlog,
43 revlog,
43 scmutil,
44 scmutil,
44 vfs as vfsmod,
45 vfs as vfsmod,
45 )
46 )
46
47
47 from . import (
48 from . import (
48 blobstore,
49 blobstore,
49 wrapper,
50 wrapper,
50 )
51 )
51
52
52 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
53 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
53 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
54 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
54 # be specifying the version(s) of Mercurial they are tested with, or
55 # be specifying the version(s) of Mercurial they are tested with, or
55 # leave the attribute unspecified.
56 # leave the attribute unspecified.
56 testedwith = 'ships-with-hg-core'
57 testedwith = 'ships-with-hg-core'
57
58
58 configtable = {}
59 configtable = {}
59 configitem = registrar.configitem(configtable)
60 configitem = registrar.configitem(configtable)
60
61
61 configitem('lfs', 'url',
62 configitem('lfs', 'url',
62 default=configitem.dynamicdefault,
63 default=configitem.dynamicdefault,
63 )
64 )
64 configitem('lfs', 'threshold',
65 configitem('lfs', 'threshold',
65 default=None,
66 default=None,
66 )
67 )
67 configitem('lfs', 'retry',
68 configitem('lfs', 'retry',
68 default=5,
69 default=5,
69 )
70 )
70 # Deprecated
71 # Deprecated
71 configitem('lfs', 'remotestore',
72 configitem('lfs', 'remotestore',
72 default=None,
73 default=None,
73 )
74 )
74 # Deprecated
75 # Deprecated
75 configitem('lfs', 'dummy',
76 configitem('lfs', 'dummy',
76 default=None,
77 default=None,
77 )
78 )
78 # Deprecated
79 # Deprecated
79 configitem('lfs', 'git-lfs',
80 configitem('lfs', 'git-lfs',
80 default=None,
81 default=None,
81 )
82 )
82
83
83 cmdtable = {}
84 cmdtable = {}
84 command = registrar.command(cmdtable)
85 command = registrar.command(cmdtable)
85
86
86 templatekeyword = registrar.templatekeyword()
87 templatekeyword = registrar.templatekeyword()
87
88
88 def featuresetup(ui, supported):
89 def featuresetup(ui, supported):
89 # don't die on seeing a repo with the lfs requirement
90 # don't die on seeing a repo with the lfs requirement
90 supported |= {'lfs'}
91 supported |= {'lfs'}
91
92
92 def uisetup(ui):
93 def uisetup(ui):
93 localrepo.localrepository.featuresetupfuncs.add(featuresetup)
94 localrepo.localrepository.featuresetupfuncs.add(featuresetup)
94
95
95 def reposetup(ui, repo):
96 def reposetup(ui, repo):
96 # Nothing to do with a remote repo
97 # Nothing to do with a remote repo
97 if not repo.local():
98 if not repo.local():
98 return
99 return
99
100
100 threshold = repo.ui.configbytes('lfs', 'threshold')
101 threshold = repo.ui.configbytes('lfs', 'threshold')
101
102
102 repo.svfs.options['lfsthreshold'] = threshold
103 repo.svfs.options['lfsthreshold'] = threshold
103 repo.svfs.lfslocalblobstore = blobstore.local(repo)
104 repo.svfs.lfslocalblobstore = blobstore.local(repo)
104 repo.svfs.lfsremoteblobstore = blobstore.remote(repo)
105 repo.svfs.lfsremoteblobstore = blobstore.remote(repo)
105
106
106 # Push hook
107 # Push hook
107 repo.prepushoutgoinghooks.add('lfs', wrapper.prepush)
108 repo.prepushoutgoinghooks.add('lfs', wrapper.prepush)
108
109
109 if 'lfs' not in repo.requirements:
110 if 'lfs' not in repo.requirements:
110 def checkrequireslfs(ui, repo, **kwargs):
111 def checkrequireslfs(ui, repo, **kwargs):
111 if 'lfs' not in repo.requirements:
112 if 'lfs' not in repo.requirements:
112 ctx = repo[kwargs['node']]
113 ctx = repo[kwargs['node']]
113 # TODO: is there a way to just walk the files in the commit?
114 # TODO: is there a way to just walk the files in the commit?
114 if any(ctx[f].islfs() for f in ctx.files()):
115 if any(ctx[f].islfs() for f in ctx.files()):
115 repo.requirements.add('lfs')
116 repo.requirements.add('lfs')
116 repo._writerequirements()
117 repo._writerequirements()
117
118
118 ui.setconfig('hooks', 'commit.lfs', checkrequireslfs, 'lfs')
119 ui.setconfig('hooks', 'commit.lfs', checkrequireslfs, 'lfs')
119
120
120 def wrapfilelog(filelog):
121 def wrapfilelog(filelog):
121 wrapfunction = extensions.wrapfunction
122 wrapfunction = extensions.wrapfunction
122
123
123 wrapfunction(filelog, 'addrevision', wrapper.filelogaddrevision)
124 wrapfunction(filelog, 'addrevision', wrapper.filelogaddrevision)
124 wrapfunction(filelog, 'renamed', wrapper.filelogrenamed)
125 wrapfunction(filelog, 'renamed', wrapper.filelogrenamed)
125 wrapfunction(filelog, 'size', wrapper.filelogsize)
126 wrapfunction(filelog, 'size', wrapper.filelogsize)
126
127
127 def extsetup(ui):
128 def extsetup(ui):
128 wrapfilelog(filelog.filelog)
129 wrapfilelog(filelog.filelog)
129
130
130 wrapfunction = extensions.wrapfunction
131 wrapfunction = extensions.wrapfunction
131
132
132 wrapfunction(scmutil, 'wrapconvertsink', wrapper.convertsink)
133 wrapfunction(scmutil, 'wrapconvertsink', wrapper.convertsink)
133
134
134 wrapfunction(changegroup,
135 wrapfunction(changegroup,
135 'supportedoutgoingversions',
136 'supportedoutgoingversions',
136 wrapper.supportedoutgoingversions)
137 wrapper.supportedoutgoingversions)
137 wrapfunction(changegroup,
138 wrapfunction(changegroup,
138 'allsupportedversions',
139 'allsupportedversions',
139 wrapper.allsupportedversions)
140 wrapper.allsupportedversions)
140
141
141 wrapfunction(context.basefilectx, 'cmp', wrapper.filectxcmp)
142 wrapfunction(context.basefilectx, 'cmp', wrapper.filectxcmp)
142 wrapfunction(context.basefilectx, 'isbinary', wrapper.filectxisbinary)
143 wrapfunction(context.basefilectx, 'isbinary', wrapper.filectxisbinary)
143 context.basefilectx.islfs = wrapper.filectxislfs
144 context.basefilectx.islfs = wrapper.filectxislfs
144
145
145 revlog.addflagprocessor(
146 revlog.addflagprocessor(
146 revlog.REVIDX_EXTSTORED,
147 revlog.REVIDX_EXTSTORED,
147 (
148 (
148 wrapper.readfromstore,
149 wrapper.readfromstore,
149 wrapper.writetostore,
150 wrapper.writetostore,
150 wrapper.bypasscheckhash,
151 wrapper.bypasscheckhash,
151 ),
152 ),
152 )
153 )
153
154
155 wrapfunction(hg, 'clone', wrapper.hgclone)
156
154 # Make bundle choose changegroup3 instead of changegroup2. This affects
157 # Make bundle choose changegroup3 instead of changegroup2. This affects
155 # "hg bundle" command. Note: it does not cover all bundle formats like
158 # "hg bundle" command. Note: it does not cover all bundle formats like
156 # "packed1". Using "packed1" with lfs will likely cause trouble.
159 # "packed1". Using "packed1" with lfs will likely cause trouble.
157 names = [k for k, v in exchange._bundlespeccgversions.items() if v == '02']
160 names = [k for k, v in exchange._bundlespeccgversions.items() if v == '02']
158 for k in names:
161 for k in names:
159 exchange._bundlespeccgversions[k] = '03'
162 exchange._bundlespeccgversions[k] = '03'
160
163
161 # bundlerepo uses "vfsmod.readonlyvfs(othervfs)", we need to make sure lfs
164 # bundlerepo uses "vfsmod.readonlyvfs(othervfs)", we need to make sure lfs
162 # options and blob stores are passed from othervfs to the new readonlyvfs.
165 # options and blob stores are passed from othervfs to the new readonlyvfs.
163 wrapfunction(vfsmod.readonlyvfs, '__init__', wrapper.vfsinit)
166 wrapfunction(vfsmod.readonlyvfs, '__init__', wrapper.vfsinit)
164
167
165 # when writing a bundle via "hg bundle" command, upload related LFS blobs
168 # when writing a bundle via "hg bundle" command, upload related LFS blobs
166 wrapfunction(bundle2, 'writenewbundle', wrapper.writenewbundle)
169 wrapfunction(bundle2, 'writenewbundle', wrapper.writenewbundle)
167
170
168 @templatekeyword('lfs_files')
171 @templatekeyword('lfs_files')
169 def lfsfiles(repo, ctx, **args):
172 def lfsfiles(repo, ctx, **args):
170 """List of strings. LFS files added or modified by the changeset."""
173 """List of strings. LFS files added or modified by the changeset."""
171 pointers = wrapper.pointersfromctx(ctx) # {path: pointer}
174 pointers = wrapper.pointersfromctx(ctx) # {path: pointer}
172 return sorted(pointers.keys())
175 return sorted(pointers.keys())
173
176
174 @command('debuglfsupload',
177 @command('debuglfsupload',
175 [('r', 'rev', [], _('upload large files introduced by REV'))])
178 [('r', 'rev', [], _('upload large files introduced by REV'))])
176 def debuglfsupload(ui, repo, **opts):
179 def debuglfsupload(ui, repo, **opts):
177 """upload lfs blobs added by the working copy parent or given revisions"""
180 """upload lfs blobs added by the working copy parent or given revisions"""
178 revs = opts.get('rev', [])
181 revs = opts.get('rev', [])
179 pointers = wrapper.extractpointers(repo, scmutil.revrange(repo, revs))
182 pointers = wrapper.extractpointers(repo, scmutil.revrange(repo, revs))
180 wrapper.uploadblobs(repo, pointers)
183 wrapper.uploadblobs(repo, pointers)
@@ -1,273 +1,292 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 . import (
22 from . import (
23 blobstore,
23 blobstore,
24 pointer,
24 pointer,
25 )
25 )
26
26
27 def supportedoutgoingversions(orig, repo):
27 def supportedoutgoingversions(orig, repo):
28 versions = orig(repo)
28 versions = orig(repo)
29 versions.discard('01')
29 versions.discard('01')
30 versions.discard('02')
30 versions.discard('02')
31 versions.add('03')
31 versions.add('03')
32 return versions
32 return versions
33
33
34 def allsupportedversions(orig, ui):
34 def allsupportedversions(orig, ui):
35 versions = orig(ui)
35 versions = orig(ui)
36 versions.add('03')
36 versions.add('03')
37 return versions
37 return versions
38
38
39 def bypasscheckhash(self, text):
39 def bypasscheckhash(self, text):
40 return False
40 return False
41
41
42 def readfromstore(self, text):
42 def readfromstore(self, text):
43 """Read filelog content from local blobstore transform for flagprocessor.
43 """Read filelog content from local blobstore transform for flagprocessor.
44
44
45 Default tranform for flagprocessor, returning contents from blobstore.
45 Default tranform for flagprocessor, returning contents from blobstore.
46 Returns a 2-typle (text, validatehash) where validatehash is True as the
46 Returns a 2-typle (text, validatehash) where validatehash is True as the
47 contents of the blobstore should be checked using checkhash.
47 contents of the blobstore should be checked using checkhash.
48 """
48 """
49 p = pointer.deserialize(text)
49 p = pointer.deserialize(text)
50 oid = p.oid()
50 oid = p.oid()
51 store = self.opener.lfslocalblobstore
51 store = self.opener.lfslocalblobstore
52 if not store.has(oid):
52 if not store.has(oid):
53 p.filename = getattr(self, 'indexfile', None)
53 p.filename = getattr(self, 'indexfile', None)
54 self.opener.lfsremoteblobstore.readbatch([p], store)
54 self.opener.lfsremoteblobstore.readbatch([p], store)
55 text = store.read(oid)
55 text = store.read(oid)
56
56
57 # pack hg filelog metadata
57 # pack hg filelog metadata
58 hgmeta = {}
58 hgmeta = {}
59 for k in p.keys():
59 for k in p.keys():
60 if k.startswith('x-hg-'):
60 if k.startswith('x-hg-'):
61 name = k[len('x-hg-'):]
61 name = k[len('x-hg-'):]
62 hgmeta[name] = p[k]
62 hgmeta[name] = p[k]
63 if hgmeta or text.startswith('\1\n'):
63 if hgmeta or text.startswith('\1\n'):
64 text = filelog.packmeta(hgmeta, text)
64 text = filelog.packmeta(hgmeta, text)
65
65
66 return (text, True)
66 return (text, True)
67
67
68 def writetostore(self, text):
68 def writetostore(self, text):
69 # hg filelog metadata (includes rename, etc)
69 # hg filelog metadata (includes rename, etc)
70 hgmeta, offset = filelog.parsemeta(text)
70 hgmeta, offset = filelog.parsemeta(text)
71 if offset and offset > 0:
71 if offset and offset > 0:
72 # lfs blob does not contain hg filelog metadata
72 # lfs blob does not contain hg filelog metadata
73 text = text[offset:]
73 text = text[offset:]
74
74
75 # git-lfs only supports sha256
75 # git-lfs only supports sha256
76 oid = hashlib.sha256(text).hexdigest()
76 oid = hashlib.sha256(text).hexdigest()
77 self.opener.lfslocalblobstore.write(oid, text)
77 self.opener.lfslocalblobstore.write(oid, text)
78
78
79 # replace contents with metadata
79 # replace contents with metadata
80 longoid = 'sha256:%s' % oid
80 longoid = 'sha256:%s' % oid
81 metadata = pointer.gitlfspointer(oid=longoid, size=str(len(text)))
81 metadata = pointer.gitlfspointer(oid=longoid, size=str(len(text)))
82
82
83 # by default, we expect the content to be binary. however, LFS could also
83 # by default, we expect the content to be binary. however, LFS could also
84 # be used for non-binary content. add a special entry for non-binary data.
84 # be used for non-binary content. add a special entry for non-binary data.
85 # this will be used by filectx.isbinary().
85 # this will be used by filectx.isbinary().
86 if not util.binary(text):
86 if not util.binary(text):
87 # not hg filelog metadata (affecting commit hash), no "x-hg-" prefix
87 # not hg filelog metadata (affecting commit hash), no "x-hg-" prefix
88 metadata['x-is-binary'] = '0'
88 metadata['x-is-binary'] = '0'
89
89
90 # translate hg filelog metadata to lfs metadata with "x-hg-" prefix
90 # translate hg filelog metadata to lfs metadata with "x-hg-" prefix
91 if hgmeta is not None:
91 if hgmeta is not None:
92 for k, v in hgmeta.iteritems():
92 for k, v in hgmeta.iteritems():
93 metadata['x-hg-%s' % k] = v
93 metadata['x-hg-%s' % k] = v
94
94
95 rawtext = metadata.serialize()
95 rawtext = metadata.serialize()
96 return (rawtext, False)
96 return (rawtext, False)
97
97
98 def _islfs(rlog, node=None, rev=None):
98 def _islfs(rlog, node=None, rev=None):
99 if rev is None:
99 if rev is None:
100 if node is None:
100 if node is None:
101 # both None - likely working copy content where node is not ready
101 # both None - likely working copy content where node is not ready
102 return False
102 return False
103 rev = rlog.rev(node)
103 rev = rlog.rev(node)
104 else:
104 else:
105 node = rlog.node(rev)
105 node = rlog.node(rev)
106 if node == nullid:
106 if node == nullid:
107 return False
107 return False
108 flags = rlog.flags(rev)
108 flags = rlog.flags(rev)
109 return bool(flags & revlog.REVIDX_EXTSTORED)
109 return bool(flags & revlog.REVIDX_EXTSTORED)
110
110
111 def filelogaddrevision(orig, self, text, transaction, link, p1, p2,
111 def filelogaddrevision(orig, self, text, transaction, link, p1, p2,
112 cachedelta=None, node=None,
112 cachedelta=None, node=None,
113 flags=revlog.REVIDX_DEFAULT_FLAGS, **kwds):
113 flags=revlog.REVIDX_DEFAULT_FLAGS, **kwds):
114 threshold = self.opener.options['lfsthreshold']
114 threshold = self.opener.options['lfsthreshold']
115 textlen = len(text)
115 textlen = len(text)
116 # exclude hg rename meta from file size
116 # exclude hg rename meta from file size
117 meta, offset = filelog.parsemeta(text)
117 meta, offset = filelog.parsemeta(text)
118 if offset:
118 if offset:
119 textlen -= offset
119 textlen -= offset
120
120
121 if threshold and textlen > threshold:
121 if threshold and textlen > threshold:
122 flags |= revlog.REVIDX_EXTSTORED
122 flags |= revlog.REVIDX_EXTSTORED
123
123
124 return orig(self, text, transaction, link, p1, p2, cachedelta=cachedelta,
124 return orig(self, text, transaction, link, p1, p2, cachedelta=cachedelta,
125 node=node, flags=flags, **kwds)
125 node=node, flags=flags, **kwds)
126
126
127 def filelogrenamed(orig, self, node):
127 def filelogrenamed(orig, self, node):
128 if _islfs(self, node):
128 if _islfs(self, node):
129 rawtext = self.revision(node, raw=True)
129 rawtext = self.revision(node, raw=True)
130 if not rawtext:
130 if not rawtext:
131 return False
131 return False
132 metadata = pointer.deserialize(rawtext)
132 metadata = pointer.deserialize(rawtext)
133 if 'x-hg-copy' in metadata and 'x-hg-copyrev' in metadata:
133 if 'x-hg-copy' in metadata and 'x-hg-copyrev' in metadata:
134 return metadata['x-hg-copy'], bin(metadata['x-hg-copyrev'])
134 return metadata['x-hg-copy'], bin(metadata['x-hg-copyrev'])
135 else:
135 else:
136 return False
136 return False
137 return orig(self, node)
137 return orig(self, node)
138
138
139 def filelogsize(orig, self, rev):
139 def filelogsize(orig, self, rev):
140 if _islfs(self, rev=rev):
140 if _islfs(self, rev=rev):
141 # fast path: use lfs metadata to answer size
141 # fast path: use lfs metadata to answer size
142 rawtext = self.revision(rev, raw=True)
142 rawtext = self.revision(rev, raw=True)
143 metadata = pointer.deserialize(rawtext)
143 metadata = pointer.deserialize(rawtext)
144 return int(metadata['size'])
144 return int(metadata['size'])
145 return orig(self, rev)
145 return orig(self, rev)
146
146
147 def filectxcmp(orig, self, fctx):
147 def filectxcmp(orig, self, fctx):
148 """returns True if text is different than fctx"""
148 """returns True if text is different than fctx"""
149 # some fctx (ex. hg-git) is not based on basefilectx and do not have islfs
149 # some fctx (ex. hg-git) is not based on basefilectx and do not have islfs
150 if self.islfs() and getattr(fctx, 'islfs', lambda: False)():
150 if self.islfs() and getattr(fctx, 'islfs', lambda: False)():
151 # fast path: check LFS oid
151 # fast path: check LFS oid
152 p1 = pointer.deserialize(self.rawdata())
152 p1 = pointer.deserialize(self.rawdata())
153 p2 = pointer.deserialize(fctx.rawdata())
153 p2 = pointer.deserialize(fctx.rawdata())
154 return p1.oid() != p2.oid()
154 return p1.oid() != p2.oid()
155 return orig(self, fctx)
155 return orig(self, fctx)
156
156
157 def filectxisbinary(orig, self):
157 def filectxisbinary(orig, self):
158 if self.islfs():
158 if self.islfs():
159 # fast path: use lfs metadata to answer isbinary
159 # fast path: use lfs metadata to answer isbinary
160 metadata = pointer.deserialize(self.rawdata())
160 metadata = pointer.deserialize(self.rawdata())
161 # if lfs metadata says nothing, assume it's binary by default
161 # if lfs metadata says nothing, assume it's binary by default
162 return bool(int(metadata.get('x-is-binary', 1)))
162 return bool(int(metadata.get('x-is-binary', 1)))
163 return orig(self)
163 return orig(self)
164
164
165 def filectxislfs(self):
165 def filectxislfs(self):
166 return _islfs(self.filelog(), self.filenode())
166 return _islfs(self.filelog(), self.filenode())
167
167
168 def convertsink(orig, sink):
168 def convertsink(orig, sink):
169 sink = orig(sink)
169 sink = orig(sink)
170 if sink.repotype == 'hg':
170 if sink.repotype == 'hg':
171 class lfssink(sink.__class__):
171 class lfssink(sink.__class__):
172 def putcommit(self, files, copies, parents, commit, source, revmap,
172 def putcommit(self, files, copies, parents, commit, source, revmap,
173 full, cleanp2):
173 full, cleanp2):
174 pc = super(lfssink, self).putcommit
174 pc = super(lfssink, self).putcommit
175 node = pc(files, copies, parents, commit, source, revmap, full,
175 node = pc(files, copies, parents, commit, source, revmap, full,
176 cleanp2)
176 cleanp2)
177
177
178 if 'lfs' not in self.repo.requirements:
178 if 'lfs' not in self.repo.requirements:
179 ctx = self.repo[node]
179 ctx = self.repo[node]
180
180
181 # The file list may contain removed files, so check for
181 # The file list may contain removed files, so check for
182 # membership before assuming it is in the context.
182 # membership before assuming it is in the context.
183 if any(f in ctx and ctx[f].islfs() for f, n in files):
183 if any(f in ctx and ctx[f].islfs() for f, n in files):
184 self.repo.requirements.add('lfs')
184 self.repo.requirements.add('lfs')
185 self.repo._writerequirements()
185 self.repo._writerequirements()
186
186
187 return node
187 return node
188
188
189 sink.__class__ = lfssink
189 sink.__class__ = lfssink
190
190
191 return sink
191 return sink
192
192
193 def vfsinit(orig, self, othervfs):
193 def vfsinit(orig, self, othervfs):
194 orig(self, othervfs)
194 orig(self, othervfs)
195 # copy lfs related options
195 # copy lfs related options
196 for k, v in othervfs.options.items():
196 for k, v in othervfs.options.items():
197 if k.startswith('lfs'):
197 if k.startswith('lfs'):
198 self.options[k] = v
198 self.options[k] = v
199 # also copy lfs blobstores. note: this can run before reposetup, so lfs
199 # also copy lfs blobstores. note: this can run before reposetup, so lfs
200 # blobstore attributes are not always ready at this time.
200 # blobstore attributes are not always ready at this time.
201 for name in ['lfslocalblobstore', 'lfsremoteblobstore']:
201 for name in ['lfslocalblobstore', 'lfsremoteblobstore']:
202 if util.safehasattr(othervfs, name):
202 if util.safehasattr(othervfs, name):
203 setattr(self, name, getattr(othervfs, name))
203 setattr(self, name, getattr(othervfs, name))
204
204
205 def hgclone(orig, ui, opts, *args, **kwargs):
206 result = orig(ui, opts, *args, **kwargs)
207
208 if result is not None:
209 sourcerepo, destrepo = result
210 repo = destrepo.local()
211
212 # When cloning to a remote repo (like through SSH), no repo is available
213 # from the peer. Therefore the hgrc can't be updated.
214 if not repo:
215 return result
216
217 # If lfs is required for this repo, permanently enable it locally
218 if 'lfs' in repo.requirements:
219 with repo.vfs('hgrc', 'a', text=True) as fp:
220 fp.write('\n[extensions]\nlfs=\n')
221
222 return result
223
205 def _canskipupload(repo):
224 def _canskipupload(repo):
206 # if remotestore is a null store, upload is a no-op and can be skipped
225 # if remotestore is a null store, upload is a no-op and can be skipped
207 return isinstance(repo.svfs.lfsremoteblobstore, blobstore._nullremote)
226 return isinstance(repo.svfs.lfsremoteblobstore, blobstore._nullremote)
208
227
209 def candownload(repo):
228 def candownload(repo):
210 # if remotestore is a null store, downloads will lead to nothing
229 # if remotestore is a null store, downloads will lead to nothing
211 return not isinstance(repo.svfs.lfsremoteblobstore, blobstore._nullremote)
230 return not isinstance(repo.svfs.lfsremoteblobstore, blobstore._nullremote)
212
231
213 def uploadblobsfromrevs(repo, revs):
232 def uploadblobsfromrevs(repo, revs):
214 '''upload lfs blobs introduced by revs
233 '''upload lfs blobs introduced by revs
215
234
216 Note: also used by other extensions e. g. infinitepush. avoid renaming.
235 Note: also used by other extensions e. g. infinitepush. avoid renaming.
217 '''
236 '''
218 if _canskipupload(repo):
237 if _canskipupload(repo):
219 return
238 return
220 pointers = extractpointers(repo, revs)
239 pointers = extractpointers(repo, revs)
221 uploadblobs(repo, pointers)
240 uploadblobs(repo, pointers)
222
241
223 def prepush(pushop):
242 def prepush(pushop):
224 """Prepush hook.
243 """Prepush hook.
225
244
226 Read through the revisions to push, looking for filelog entries that can be
245 Read through the revisions to push, looking for filelog entries that can be
227 deserialized into metadata so that we can block the push on their upload to
246 deserialized into metadata so that we can block the push on their upload to
228 the remote blobstore.
247 the remote blobstore.
229 """
248 """
230 return uploadblobsfromrevs(pushop.repo, pushop.outgoing.missing)
249 return uploadblobsfromrevs(pushop.repo, pushop.outgoing.missing)
231
250
232 def writenewbundle(orig, ui, repo, source, filename, bundletype, outgoing,
251 def writenewbundle(orig, ui, repo, source, filename, bundletype, outgoing,
233 *args, **kwargs):
252 *args, **kwargs):
234 """upload LFS blobs added by outgoing revisions on 'hg bundle'"""
253 """upload LFS blobs added by outgoing revisions on 'hg bundle'"""
235 uploadblobsfromrevs(repo, outgoing.missing)
254 uploadblobsfromrevs(repo, outgoing.missing)
236 return orig(ui, repo, source, filename, bundletype, outgoing, *args,
255 return orig(ui, repo, source, filename, bundletype, outgoing, *args,
237 **kwargs)
256 **kwargs)
238
257
239 def extractpointers(repo, revs):
258 def extractpointers(repo, revs):
240 """return a list of lfs pointers added by given revs"""
259 """return a list of lfs pointers added by given revs"""
241 ui = repo.ui
260 ui = repo.ui
242 if ui.debugflag:
261 if ui.debugflag:
243 ui.write(_('lfs: computing set of blobs to upload\n'))
262 ui.write(_('lfs: computing set of blobs to upload\n'))
244 pointers = {}
263 pointers = {}
245 for r in revs:
264 for r in revs:
246 ctx = repo[r]
265 ctx = repo[r]
247 for p in pointersfromctx(ctx).values():
266 for p in pointersfromctx(ctx).values():
248 pointers[p.oid()] = p
267 pointers[p.oid()] = p
249 return pointers.values()
268 return pointers.values()
250
269
251 def pointersfromctx(ctx):
270 def pointersfromctx(ctx):
252 """return a dict {path: pointer} for given single changectx"""
271 """return a dict {path: pointer} for given single changectx"""
253 result = {}
272 result = {}
254 for f in ctx.files():
273 for f in ctx.files():
255 if f not in ctx:
274 if f not in ctx:
256 continue
275 continue
257 fctx = ctx[f]
276 fctx = ctx[f]
258 if not _islfs(fctx.filelog(), fctx.filenode()):
277 if not _islfs(fctx.filelog(), fctx.filenode()):
259 continue
278 continue
260 try:
279 try:
261 result[f] = pointer.deserialize(fctx.rawdata())
280 result[f] = pointer.deserialize(fctx.rawdata())
262 except pointer.InvalidPointer as ex:
281 except pointer.InvalidPointer as ex:
263 raise error.Abort(_('lfs: corrupted pointer (%s@%s): %s\n')
282 raise error.Abort(_('lfs: corrupted pointer (%s@%s): %s\n')
264 % (f, short(ctx.node()), ex))
283 % (f, short(ctx.node()), ex))
265 return result
284 return result
266
285
267 def uploadblobs(repo, pointers):
286 def uploadblobs(repo, pointers):
268 """upload given pointers from local blobstore"""
287 """upload given pointers from local blobstore"""
269 if not pointers:
288 if not pointers:
270 return
289 return
271
290
272 remoteblob = repo.svfs.lfsremoteblobstore
291 remoteblob = repo.svfs.lfsremoteblobstore
273 remoteblob.writebatch(pointers, repo.svfs.lfslocalblobstore)
292 remoteblob.writebatch(pointers, repo.svfs.lfslocalblobstore)
@@ -1,638 +1,640 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 > threshold=1000B
7 > threshold=1000B
8 > EOF
8 > EOF
9
9
10 $ LONG=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
10 $ LONG=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
11
11
12 # Prepare server and enable extension
12 # Prepare server and enable extension
13 $ hg init server
13 $ hg init server
14 $ hg clone -q server client
14 $ hg clone -q server client
15 $ cd client
15 $ cd client
16
16
17 # Commit small file
17 # Commit small file
18 $ echo s > smallfile
18 $ echo s > smallfile
19 $ hg commit -Aqm "add small file"
19 $ hg commit -Aqm "add small file"
20
20
21 # Commit large file
21 # Commit large file
22 $ echo $LONG > largefile
22 $ echo $LONG > largefile
23 $ grep lfs .hg/requires
23 $ grep lfs .hg/requires
24 [1]
24 [1]
25 $ hg commit --traceback -Aqm "add large file"
25 $ hg commit --traceback -Aqm "add large file"
26 $ grep lfs .hg/requires
26 $ grep lfs .hg/requires
27 lfs
27 lfs
28
28
29 # Ensure metadata is stored
29 # Ensure metadata is stored
30 $ hg debugdata largefile 0
30 $ hg debugdata largefile 0
31 version https://git-lfs.github.com/spec/v1
31 version https://git-lfs.github.com/spec/v1
32 oid sha256:f11e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
32 oid sha256:f11e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
33 size 1501
33 size 1501
34 x-is-binary 0
34 x-is-binary 0
35
35
36 # Check the blobstore is populated
36 # Check the blobstore is populated
37 $ find .hg/store/lfs/objects | sort
37 $ find .hg/store/lfs/objects | sort
38 .hg/store/lfs/objects
38 .hg/store/lfs/objects
39 .hg/store/lfs/objects/f1
39 .hg/store/lfs/objects/f1
40 .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
40 .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
41
41
42 # Check the blob stored contains the actual contents of the file
42 # Check the blob stored contains the actual contents of the file
43 $ cat .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
43 $ cat .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
44 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
44 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
45
45
46 # Push changes to the server
46 # Push changes to the server
47
47
48 $ hg push
48 $ hg push
49 pushing to $TESTTMP/server (glob)
49 pushing to $TESTTMP/server (glob)
50 searching for changes
50 searching for changes
51 abort: lfs.url needs to be configured
51 abort: lfs.url needs to be configured
52 [255]
52 [255]
53
53
54 $ cat >> $HGRCPATH << EOF
54 $ cat >> $HGRCPATH << EOF
55 > [lfs]
55 > [lfs]
56 > url=file:$TESTTMP/dummy-remote/
56 > url=file:$TESTTMP/dummy-remote/
57 > EOF
57 > EOF
58
58
59 $ hg push -v | egrep -v '^(uncompressed| )'
59 $ hg push -v | egrep -v '^(uncompressed| )'
60 pushing to $TESTTMP/server (glob)
60 pushing to $TESTTMP/server (glob)
61 searching for changes
61 searching for changes
62 2 changesets found
62 2 changesets found
63 adding changesets
63 adding changesets
64 adding manifests
64 adding manifests
65 adding file changes
65 adding file changes
66 added 2 changesets with 2 changes to 2 files
66 added 2 changesets with 2 changes to 2 files
67
67
68 # Unknown URL scheme
68 # Unknown URL scheme
69
69
70 $ hg push --config lfs.url=ftp://foobar
70 $ hg push --config lfs.url=ftp://foobar
71 abort: lfs: unknown url scheme: ftp
71 abort: lfs: unknown url scheme: ftp
72 [255]
72 [255]
73
73
74 $ cd ../
74 $ cd ../
75
75
76 # Initialize new client (not cloning) and setup extension
76 # Initialize new client (not cloning) and setup extension
77 $ hg init client2
77 $ hg init client2
78 $ cd client2
78 $ cd client2
79 $ cat >> .hg/hgrc <<EOF
79 $ cat >> .hg/hgrc <<EOF
80 > [paths]
80 > [paths]
81 > default = $TESTTMP/server
81 > default = $TESTTMP/server
82 > EOF
82 > EOF
83
83
84 # Pull from server
84 # Pull from server
85 $ hg pull default
85 $ hg pull default
86 pulling from $TESTTMP/server (glob)
86 pulling from $TESTTMP/server (glob)
87 requesting all changes
87 requesting all changes
88 adding changesets
88 adding changesets
89 adding manifests
89 adding manifests
90 adding file changes
90 adding file changes
91 added 2 changesets with 2 changes to 2 files
91 added 2 changesets with 2 changes to 2 files
92 new changesets b29ba743f89d:00c137947d30
92 new changesets b29ba743f89d:00c137947d30
93 (run 'hg update' to get a working copy)
93 (run 'hg update' to get a working copy)
94
94
95 # Check the blobstore is not yet populated
95 # Check the blobstore is not yet populated
96 $ [ -d .hg/store/lfs/objects ]
96 $ [ -d .hg/store/lfs/objects ]
97 [1]
97 [1]
98
98
99 # Update to the last revision containing the large file
99 # Update to the last revision containing the large file
100 $ hg update
100 $ hg update
101 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
102
102
103 # Check the blobstore has been populated on update
103 # Check the blobstore has been populated on update
104 $ find .hg/store/lfs/objects | sort
104 $ find .hg/store/lfs/objects | sort
105 .hg/store/lfs/objects
105 .hg/store/lfs/objects
106 .hg/store/lfs/objects/f1
106 .hg/store/lfs/objects/f1
107 .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
107 .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
108
108
109 # Check the contents of the file are fetched from blobstore when requested
109 # Check the contents of the file are fetched from blobstore when requested
110 $ hg cat -r . largefile
110 $ hg cat -r . largefile
111 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
111 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
112
112
113 # Check the file has been copied in the working copy
113 # Check the file has been copied in the working copy
114 $ cat largefile
114 $ cat largefile
115 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
115 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
116
116
117 $ cd ..
117 $ cd ..
118
118
119 # Check rename, and switch between large and small files
119 # Check rename, and switch between large and small files
120
120
121 $ hg init repo3
121 $ hg init repo3
122 $ cd repo3
122 $ cd repo3
123 $ cat >> .hg/hgrc << EOF
123 $ cat >> .hg/hgrc << EOF
124 > [lfs]
124 > [lfs]
125 > threshold=10B
125 > threshold=10B
126 > EOF
126 > EOF
127
127
128 $ echo LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS > large
128 $ echo LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS > large
129 $ echo SHORTER > small
129 $ echo SHORTER > small
130 $ hg add . -q
130 $ hg add . -q
131 $ hg commit -m 'commit with lfs content'
131 $ hg commit -m 'commit with lfs content'
132
132
133 $ hg mv large l
133 $ hg mv large l
134 $ hg mv small s
134 $ hg mv small s
135 $ hg commit -m 'renames'
135 $ hg commit -m 'renames'
136
136
137 $ echo SHORT > l
137 $ echo SHORT > l
138 $ echo BECOME-LARGER-FROM-SHORTER > s
138 $ echo BECOME-LARGER-FROM-SHORTER > s
139 $ hg commit -m 'large to small, small to large'
139 $ hg commit -m 'large to small, small to large'
140
140
141 $ echo 1 >> l
141 $ echo 1 >> l
142 $ echo 2 >> s
142 $ echo 2 >> s
143 $ hg commit -m 'random modifications'
143 $ hg commit -m 'random modifications'
144
144
145 $ echo RESTORE-TO-BE-LARGE > l
145 $ echo RESTORE-TO-BE-LARGE > l
146 $ echo SHORTER > s
146 $ echo SHORTER > s
147 $ hg commit -m 'switch large and small again'
147 $ hg commit -m 'switch large and small again'
148
148
149 # Test lfs_files template
149 # Test lfs_files template
150
150
151 $ hg log -r 'all()' -T '{rev} {join(lfs_files, ", ")}\n'
151 $ hg log -r 'all()' -T '{rev} {join(lfs_files, ", ")}\n'
152 0 large
152 0 large
153 1 l
153 1 l
154 2 s
154 2 s
155 3 s
155 3 s
156 4 l
156 4 l
157
157
158 # Push and pull the above repo
158 # Push and pull the above repo
159
159
160 $ hg --cwd .. init repo4
160 $ hg --cwd .. init repo4
161 $ hg push ../repo4
161 $ hg push ../repo4
162 pushing to ../repo4
162 pushing to ../repo4
163 searching for changes
163 searching for changes
164 adding changesets
164 adding changesets
165 adding manifests
165 adding manifests
166 adding file changes
166 adding file changes
167 added 5 changesets with 10 changes to 4 files
167 added 5 changesets with 10 changes to 4 files
168
168
169 $ hg --cwd .. init repo5
169 $ hg --cwd .. init repo5
170 $ hg --cwd ../repo5 pull ../repo3
170 $ hg --cwd ../repo5 pull ../repo3
171 pulling from ../repo3
171 pulling from ../repo3
172 requesting all changes
172 requesting all changes
173 adding changesets
173 adding changesets
174 adding manifests
174 adding manifests
175 adding file changes
175 adding file changes
176 added 5 changesets with 10 changes to 4 files
176 added 5 changesets with 10 changes to 4 files
177 new changesets fd47a419c4f7:5adf850972b9
177 new changesets fd47a419c4f7:5adf850972b9
178 (run 'hg update' to get a working copy)
178 (run 'hg update' to get a working copy)
179
179
180 $ cd ..
180 $ cd ..
181
181
182 # Test clone
182 # Test clone
183
183
184 $ hg init repo6
184 $ hg init repo6
185 $ cd repo6
185 $ cd repo6
186 $ cat >> .hg/hgrc << EOF
186 $ cat >> .hg/hgrc << EOF
187 > [lfs]
187 > [lfs]
188 > threshold=30B
188 > threshold=30B
189 > EOF
189 > EOF
190
190
191 $ echo LARGE-BECAUSE-IT-IS-MORE-THAN-30-BYTES > large
191 $ echo LARGE-BECAUSE-IT-IS-MORE-THAN-30-BYTES > large
192 $ echo SMALL > small
192 $ echo SMALL > small
193 $ hg commit -Aqm 'create a lfs file' large small
193 $ hg commit -Aqm 'create a lfs file' large small
194 $ hg debuglfsupload -r 'all()' -v
194 $ hg debuglfsupload -r 'all()' -v
195
195
196 $ cd ..
196 $ cd ..
197
197
198 $ hg clone repo6 repo7
198 $ hg clone repo6 repo7
199 updating to branch default
199 updating to branch default
200 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
200 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
201 $ cd repo7
201 $ cd repo7
202 $ hg config extensions --debug | grep lfs
203 $TESTTMP/repo7/.hg/hgrc:*: extensions.lfs= (glob)
202 $ cat large
204 $ cat large
203 LARGE-BECAUSE-IT-IS-MORE-THAN-30-BYTES
205 LARGE-BECAUSE-IT-IS-MORE-THAN-30-BYTES
204 $ cat small
206 $ cat small
205 SMALL
207 SMALL
206
208
207 $ cd ..
209 $ cd ..
208
210
209 # Test rename and status
211 # Test rename and status
210
212
211 $ hg init repo8
213 $ hg init repo8
212 $ cd repo8
214 $ cd repo8
213 $ cat >> .hg/hgrc << EOF
215 $ cat >> .hg/hgrc << EOF
214 > [lfs]
216 > [lfs]
215 > threshold=10B
217 > threshold=10B
216 > EOF
218 > EOF
217
219
218 $ echo THIS-IS-LFS-BECAUSE-10-BYTES > a1
220 $ echo THIS-IS-LFS-BECAUSE-10-BYTES > a1
219 $ echo SMALL > a2
221 $ echo SMALL > a2
220 $ hg commit -m a -A a1 a2
222 $ hg commit -m a -A a1 a2
221 $ hg status
223 $ hg status
222 $ hg mv a1 b1
224 $ hg mv a1 b1
223 $ hg mv a2 a1
225 $ hg mv a2 a1
224 $ hg mv b1 a2
226 $ hg mv b1 a2
225 $ hg commit -m b
227 $ hg commit -m b
226 $ hg status
228 $ hg status
227 $ HEADER=$'\1\n'
229 $ HEADER=$'\1\n'
228 $ printf '%sSTART-WITH-HG-FILELOG-METADATA' "$HEADER" > a2
230 $ printf '%sSTART-WITH-HG-FILELOG-METADATA' "$HEADER" > a2
229 $ printf '%sMETA\n' "$HEADER" > a1
231 $ printf '%sMETA\n' "$HEADER" > a1
230 $ hg commit -m meta
232 $ hg commit -m meta
231 $ hg status
233 $ hg status
232 $ hg log -T '{rev}: {file_copies} | {file_dels} | {file_adds}\n'
234 $ hg log -T '{rev}: {file_copies} | {file_dels} | {file_adds}\n'
233 2: | |
235 2: | |
234 1: a1 (a2)a2 (a1) | |
236 1: a1 (a2)a2 (a1) | |
235 0: | | a1 a2
237 0: | | a1 a2
236
238
237 $ for n in a1 a2; do
239 $ for n in a1 a2; do
238 > for r in 0 1 2; do
240 > for r in 0 1 2; do
239 > printf '\n%s @ %s\n' $n $r
241 > printf '\n%s @ %s\n' $n $r
240 > hg debugdata $n $r
242 > hg debugdata $n $r
241 > done
243 > done
242 > done
244 > done
243
245
244 a1 @ 0
246 a1 @ 0
245 version https://git-lfs.github.com/spec/v1
247 version https://git-lfs.github.com/spec/v1
246 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
248 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
247 size 29
249 size 29
248 x-is-binary 0
250 x-is-binary 0
249
251
250 a1 @ 1
252 a1 @ 1
251 \x01 (esc)
253 \x01 (esc)
252 copy: a2
254 copy: a2
253 copyrev: 50470ad23cf937b1f4b9f80bfe54df38e65b50d9
255 copyrev: 50470ad23cf937b1f4b9f80bfe54df38e65b50d9
254 \x01 (esc)
256 \x01 (esc)
255 SMALL
257 SMALL
256
258
257 a1 @ 2
259 a1 @ 2
258 \x01 (esc)
260 \x01 (esc)
259 \x01 (esc)
261 \x01 (esc)
260 \x01 (esc)
262 \x01 (esc)
261 META
263 META
262
264
263 a2 @ 0
265 a2 @ 0
264 SMALL
266 SMALL
265
267
266 a2 @ 1
268 a2 @ 1
267 version https://git-lfs.github.com/spec/v1
269 version https://git-lfs.github.com/spec/v1
268 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
270 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
269 size 29
271 size 29
270 x-hg-copy a1
272 x-hg-copy a1
271 x-hg-copyrev be23af27908a582af43e5cda209a5a9b319de8d4
273 x-hg-copyrev be23af27908a582af43e5cda209a5a9b319de8d4
272 x-is-binary 0
274 x-is-binary 0
273
275
274 a2 @ 2
276 a2 @ 2
275 version https://git-lfs.github.com/spec/v1
277 version https://git-lfs.github.com/spec/v1
276 oid sha256:876dadc86a8542f9798048f2c47f51dbf8e4359aed883e8ec80c5db825f0d943
278 oid sha256:876dadc86a8542f9798048f2c47f51dbf8e4359aed883e8ec80c5db825f0d943
277 size 32
279 size 32
278 x-is-binary 0
280 x-is-binary 0
279
281
280 # Verify commit hashes include rename metadata
282 # Verify commit hashes include rename metadata
281
283
282 $ hg log -T '{rev}:{node|short} {desc}\n'
284 $ hg log -T '{rev}:{node|short} {desc}\n'
283 2:0fae949de7fa meta
285 2:0fae949de7fa meta
284 1:9cd6bdffdac0 b
286 1:9cd6bdffdac0 b
285 0:7f96794915f7 a
287 0:7f96794915f7 a
286
288
287 $ cd ..
289 $ cd ..
288
290
289 # Test bundle
291 # Test bundle
290
292
291 $ hg init repo9
293 $ hg init repo9
292 $ cd repo9
294 $ cd repo9
293 $ cat >> .hg/hgrc << EOF
295 $ cat >> .hg/hgrc << EOF
294 > [lfs]
296 > [lfs]
295 > threshold=10B
297 > threshold=10B
296 > [diff]
298 > [diff]
297 > git=1
299 > git=1
298 > EOF
300 > EOF
299
301
300 $ for i in 0 single two three 4; do
302 $ for i in 0 single two three 4; do
301 > echo 'THIS-IS-LFS-'$i > a
303 > echo 'THIS-IS-LFS-'$i > a
302 > hg commit -m a-$i -A a
304 > hg commit -m a-$i -A a
303 > done
305 > done
304
306
305 $ hg update 2 -q
307 $ hg update 2 -q
306 $ echo 'THIS-IS-LFS-2-CHILD' > a
308 $ echo 'THIS-IS-LFS-2-CHILD' > a
307 $ hg commit -m branching -q
309 $ hg commit -m branching -q
308
310
309 $ hg bundle --base 1 bundle.hg -v
311 $ hg bundle --base 1 bundle.hg -v
310 4 changesets found
312 4 changesets found
311 uncompressed size of bundle content:
313 uncompressed size of bundle content:
312 * (changelog) (glob)
314 * (changelog) (glob)
313 * (manifests) (glob)
315 * (manifests) (glob)
314 * a (glob)
316 * a (glob)
315 $ hg --config extensions.strip= strip -r 2 --no-backup --force -q
317 $ hg --config extensions.strip= strip -r 2 --no-backup --force -q
316 $ hg -R bundle.hg log -p -T '{rev} {desc}\n' a
318 $ hg -R bundle.hg log -p -T '{rev} {desc}\n' a
317 5 branching
319 5 branching
318 diff --git a/a b/a
320 diff --git a/a b/a
319 --- a/a
321 --- a/a
320 +++ b/a
322 +++ b/a
321 @@ -1,1 +1,1 @@
323 @@ -1,1 +1,1 @@
322 -THIS-IS-LFS-two
324 -THIS-IS-LFS-two
323 +THIS-IS-LFS-2-CHILD
325 +THIS-IS-LFS-2-CHILD
324
326
325 4 a-4
327 4 a-4
326 diff --git a/a b/a
328 diff --git a/a b/a
327 --- a/a
329 --- a/a
328 +++ b/a
330 +++ b/a
329 @@ -1,1 +1,1 @@
331 @@ -1,1 +1,1 @@
330 -THIS-IS-LFS-three
332 -THIS-IS-LFS-three
331 +THIS-IS-LFS-4
333 +THIS-IS-LFS-4
332
334
333 3 a-three
335 3 a-three
334 diff --git a/a b/a
336 diff --git a/a b/a
335 --- a/a
337 --- a/a
336 +++ b/a
338 +++ b/a
337 @@ -1,1 +1,1 @@
339 @@ -1,1 +1,1 @@
338 -THIS-IS-LFS-two
340 -THIS-IS-LFS-two
339 +THIS-IS-LFS-three
341 +THIS-IS-LFS-three
340
342
341 2 a-two
343 2 a-two
342 diff --git a/a b/a
344 diff --git a/a b/a
343 --- a/a
345 --- a/a
344 +++ b/a
346 +++ b/a
345 @@ -1,1 +1,1 @@
347 @@ -1,1 +1,1 @@
346 -THIS-IS-LFS-single
348 -THIS-IS-LFS-single
347 +THIS-IS-LFS-two
349 +THIS-IS-LFS-two
348
350
349 1 a-single
351 1 a-single
350 diff --git a/a b/a
352 diff --git a/a b/a
351 --- a/a
353 --- a/a
352 +++ b/a
354 +++ b/a
353 @@ -1,1 +1,1 @@
355 @@ -1,1 +1,1 @@
354 -THIS-IS-LFS-0
356 -THIS-IS-LFS-0
355 +THIS-IS-LFS-single
357 +THIS-IS-LFS-single
356
358
357 0 a-0
359 0 a-0
358 diff --git a/a b/a
360 diff --git a/a b/a
359 new file mode 100644
361 new file mode 100644
360 --- /dev/null
362 --- /dev/null
361 +++ b/a
363 +++ b/a
362 @@ -0,0 +1,1 @@
364 @@ -0,0 +1,1 @@
363 +THIS-IS-LFS-0
365 +THIS-IS-LFS-0
364
366
365 $ hg bundle -R bundle.hg --base 1 bundle-again.hg -q
367 $ hg bundle -R bundle.hg --base 1 bundle-again.hg -q
366 $ hg -R bundle-again.hg log -p -T '{rev} {desc}\n' a
368 $ hg -R bundle-again.hg log -p -T '{rev} {desc}\n' a
367 5 branching
369 5 branching
368 diff --git a/a b/a
370 diff --git a/a b/a
369 --- a/a
371 --- a/a
370 +++ b/a
372 +++ b/a
371 @@ -1,1 +1,1 @@
373 @@ -1,1 +1,1 @@
372 -THIS-IS-LFS-two
374 -THIS-IS-LFS-two
373 +THIS-IS-LFS-2-CHILD
375 +THIS-IS-LFS-2-CHILD
374
376
375 4 a-4
377 4 a-4
376 diff --git a/a b/a
378 diff --git a/a b/a
377 --- a/a
379 --- a/a
378 +++ b/a
380 +++ b/a
379 @@ -1,1 +1,1 @@
381 @@ -1,1 +1,1 @@
380 -THIS-IS-LFS-three
382 -THIS-IS-LFS-three
381 +THIS-IS-LFS-4
383 +THIS-IS-LFS-4
382
384
383 3 a-three
385 3 a-three
384 diff --git a/a b/a
386 diff --git a/a b/a
385 --- a/a
387 --- a/a
386 +++ b/a
388 +++ b/a
387 @@ -1,1 +1,1 @@
389 @@ -1,1 +1,1 @@
388 -THIS-IS-LFS-two
390 -THIS-IS-LFS-two
389 +THIS-IS-LFS-three
391 +THIS-IS-LFS-three
390
392
391 2 a-two
393 2 a-two
392 diff --git a/a b/a
394 diff --git a/a b/a
393 --- a/a
395 --- a/a
394 +++ b/a
396 +++ b/a
395 @@ -1,1 +1,1 @@
397 @@ -1,1 +1,1 @@
396 -THIS-IS-LFS-single
398 -THIS-IS-LFS-single
397 +THIS-IS-LFS-two
399 +THIS-IS-LFS-two
398
400
399 1 a-single
401 1 a-single
400 diff --git a/a b/a
402 diff --git a/a b/a
401 --- a/a
403 --- a/a
402 +++ b/a
404 +++ b/a
403 @@ -1,1 +1,1 @@
405 @@ -1,1 +1,1 @@
404 -THIS-IS-LFS-0
406 -THIS-IS-LFS-0
405 +THIS-IS-LFS-single
407 +THIS-IS-LFS-single
406
408
407 0 a-0
409 0 a-0
408 diff --git a/a b/a
410 diff --git a/a b/a
409 new file mode 100644
411 new file mode 100644
410 --- /dev/null
412 --- /dev/null
411 +++ b/a
413 +++ b/a
412 @@ -0,0 +1,1 @@
414 @@ -0,0 +1,1 @@
413 +THIS-IS-LFS-0
415 +THIS-IS-LFS-0
414
416
415 $ cd ..
417 $ cd ..
416
418
417 # Test isbinary
419 # Test isbinary
418
420
419 $ hg init repo10
421 $ hg init repo10
420 $ cd repo10
422 $ cd repo10
421 $ cat >> .hg/hgrc << EOF
423 $ cat >> .hg/hgrc << EOF
422 > [extensions]
424 > [extensions]
423 > lfs=
425 > lfs=
424 > [lfs]
426 > [lfs]
425 > threshold=1
427 > threshold=1
426 > EOF
428 > EOF
427 $ $PYTHON <<'EOF'
429 $ $PYTHON <<'EOF'
428 > def write(path, content):
430 > def write(path, content):
429 > with open(path, 'wb') as f:
431 > with open(path, 'wb') as f:
430 > f.write(content)
432 > f.write(content)
431 > write('a', b'\0\0')
433 > write('a', b'\0\0')
432 > write('b', b'\1\n')
434 > write('b', b'\1\n')
433 > write('c', b'\1\n\0')
435 > write('c', b'\1\n\0')
434 > write('d', b'xx')
436 > write('d', b'xx')
435 > EOF
437 > EOF
436 $ hg add a b c d
438 $ hg add a b c d
437 $ hg diff --stat
439 $ hg diff --stat
438 a | Bin
440 a | Bin
439 b | 1 +
441 b | 1 +
440 c | Bin
442 c | Bin
441 d | 1 +
443 d | 1 +
442 4 files changed, 2 insertions(+), 0 deletions(-)
444 4 files changed, 2 insertions(+), 0 deletions(-)
443 $ hg commit -m binarytest
445 $ hg commit -m binarytest
444 $ cat > $TESTTMP/dumpbinary.py << EOF
446 $ cat > $TESTTMP/dumpbinary.py << EOF
445 > def reposetup(ui, repo):
447 > def reposetup(ui, repo):
446 > for n in 'abcd':
448 > for n in 'abcd':
447 > ui.write(('%s: binary=%s\n') % (n, repo['.'][n].isbinary()))
449 > ui.write(('%s: binary=%s\n') % (n, repo['.'][n].isbinary()))
448 > EOF
450 > EOF
449 $ hg --config extensions.dumpbinary=$TESTTMP/dumpbinary.py id --trace
451 $ hg --config extensions.dumpbinary=$TESTTMP/dumpbinary.py id --trace
450 a: binary=True
452 a: binary=True
451 b: binary=False
453 b: binary=False
452 c: binary=True
454 c: binary=True
453 d: binary=False
455 d: binary=False
454 b55353847f02 tip
456 b55353847f02 tip
455
457
456 $ cd ..
458 $ cd ..
457
459
458 # Test fctx.cmp fastpath - diff without LFS blobs
460 # Test fctx.cmp fastpath - diff without LFS blobs
459
461
460 $ hg init repo11
462 $ hg init repo11
461 $ cd repo11
463 $ cd repo11
462 $ cat >> .hg/hgrc <<EOF
464 $ cat >> .hg/hgrc <<EOF
463 > [lfs]
465 > [lfs]
464 > threshold=1
466 > threshold=1
465 > EOF
467 > EOF
466 $ cat > ../patch.diff <<EOF
468 $ cat > ../patch.diff <<EOF
467 > # HG changeset patch
469 > # HG changeset patch
468 > 2
470 > 2
469 >
471 >
470 > diff --git a/a b/a
472 > diff --git a/a b/a
471 > old mode 100644
473 > old mode 100644
472 > new mode 100755
474 > new mode 100755
473 > EOF
475 > EOF
474
476
475 $ for i in 1 2 3; do
477 $ for i in 1 2 3; do
476 > cp ../repo10/a a
478 > cp ../repo10/a a
477 > if [ $i = 3 ]; then
479 > if [ $i = 3 ]; then
478 > # make a content-only change
480 > # make a content-only change
479 > hg import -q --bypass ../patch.diff
481 > hg import -q --bypass ../patch.diff
480 > hg update -q
482 > hg update -q
481 > rm ../patch.diff
483 > rm ../patch.diff
482 > else
484 > else
483 > echo $i >> a
485 > echo $i >> a
484 > hg commit -m $i -A a
486 > hg commit -m $i -A a
485 > fi
487 > fi
486 > done
488 > done
487 $ [ -d .hg/store/lfs/objects ]
489 $ [ -d .hg/store/lfs/objects ]
488
490
489 $ cd ..
491 $ cd ..
490
492
491 $ hg clone repo11 repo12 --noupdate
493 $ hg clone repo11 repo12 --noupdate
492 $ cd repo12
494 $ cd repo12
493 $ hg log --removed -p a -T '{desc}\n' --config diff.nobinary=1 --git
495 $ hg log --removed -p a -T '{desc}\n' --config diff.nobinary=1 --git
494 2
496 2
495 diff --git a/a b/a
497 diff --git a/a b/a
496 old mode 100644
498 old mode 100644
497 new mode 100755
499 new mode 100755
498
500
499 2
501 2
500 diff --git a/a b/a
502 diff --git a/a b/a
501 Binary file a has changed
503 Binary file a has changed
502
504
503 1
505 1
504 diff --git a/a b/a
506 diff --git a/a b/a
505 new file mode 100644
507 new file mode 100644
506 Binary file a has changed
508 Binary file a has changed
507
509
508 $ [ -d .hg/store/lfs/objects ]
510 $ [ -d .hg/store/lfs/objects ]
509 [1]
511 [1]
510
512
511 $ cd ..
513 $ cd ..
512
514
513 # Verify the repos
515 # Verify the repos
514
516
515 $ cat > $TESTTMP/dumpflog.py << EOF
517 $ cat > $TESTTMP/dumpflog.py << EOF
516 > # print raw revision sizes, flags, and hashes for certain files
518 > # print raw revision sizes, flags, and hashes for certain files
517 > import hashlib
519 > import hashlib
518 > from mercurial import revlog
520 > from mercurial import revlog
519 > from mercurial.node import short
521 > from mercurial.node import short
520 > def hash(rawtext):
522 > def hash(rawtext):
521 > h = hashlib.sha512()
523 > h = hashlib.sha512()
522 > h.update(rawtext)
524 > h.update(rawtext)
523 > return h.hexdigest()[:4]
525 > return h.hexdigest()[:4]
524 > def reposetup(ui, repo):
526 > def reposetup(ui, repo):
525 > # these 2 files are interesting
527 > # these 2 files are interesting
526 > for name in ['l', 's']:
528 > for name in ['l', 's']:
527 > fl = repo.file(name)
529 > fl = repo.file(name)
528 > if len(fl) == 0:
530 > if len(fl) == 0:
529 > continue
531 > continue
530 > sizes = [revlog.revlog.rawsize(fl, i) for i in fl]
532 > sizes = [revlog.revlog.rawsize(fl, i) for i in fl]
531 > texts = [fl.revision(i, raw=True) for i in fl]
533 > texts = [fl.revision(i, raw=True) for i in fl]
532 > flags = [int(fl.flags(i)) for i in fl]
534 > flags = [int(fl.flags(i)) for i in fl]
533 > hashes = [hash(t) for t in texts]
535 > hashes = [hash(t) for t in texts]
534 > print(' %s: rawsizes=%r flags=%r hashes=%r'
536 > print(' %s: rawsizes=%r flags=%r hashes=%r'
535 > % (name, sizes, flags, hashes))
537 > % (name, sizes, flags, hashes))
536 > EOF
538 > EOF
537
539
538 $ for i in client client2 server repo3 repo4 repo5 repo6 repo7 repo8 repo9 \
540 $ for i in client client2 server repo3 repo4 repo5 repo6 repo7 repo8 repo9 \
539 > repo10; do
541 > repo10; do
540 > echo 'repo:' $i
542 > echo 'repo:' $i
541 > hg --cwd $i verify --config extensions.dumpflog=$TESTTMP/dumpflog.py -q
543 > hg --cwd $i verify --config extensions.dumpflog=$TESTTMP/dumpflog.py -q
542 > done
544 > done
543 repo: client
545 repo: client
544 repo: client2
546 repo: client2
545 repo: server
547 repo: server
546 repo: repo3
548 repo: repo3
547 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
549 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
548 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
550 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
549 repo: repo4
551 repo: repo4
550 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
552 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
551 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
553 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
552 repo: repo5
554 repo: repo5
553 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
555 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
554 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
556 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
555 repo: repo6
557 repo: repo6
556 repo: repo7
558 repo: repo7
557 repo: repo8
559 repo: repo8
558 repo: repo9
560 repo: repo9
559 repo: repo10
561 repo: repo10
560
562
561 lfs -> normal -> lfs round trip conversions are possible. The threshold for the
563 lfs -> normal -> lfs round trip conversions are possible. The threshold for the
562 lfs destination is specified here because it was originally listed in the local
564 lfs destination is specified here because it was originally listed in the local
563 .hgrc, and the global one is too high to trigger lfs usage. For lfs -> normal,
565 .hgrc, and the global one is too high to trigger lfs usage. For lfs -> normal,
564 there's no 'lfs' destination repo requirement. For normal -> lfs, there is.
566 there's no 'lfs' destination repo requirement. For normal -> lfs, there is.
565
567
566 XXX: There's not a great way to ensure that the conversion to normal files
568 XXX: There's not a great way to ensure that the conversion to normal files
567 actually converts _everything_ to normal. The extension needs to be loaded for
569 actually converts _everything_ to normal. The extension needs to be loaded for
568 the source, but there's no way to disable it for the destination. The best that
570 the source, but there's no way to disable it for the destination. The best that
569 can be done is to raise the threshold so that lfs isn't used on the destination.
571 can be done is to raise the threshold so that lfs isn't used on the destination.
570 It doesn't like using '!' to unset the value on the command line.
572 It doesn't like using '!' to unset the value on the command line.
571
573
572 $ hg --config extensions.convert= --config lfs.threshold=1000M \
574 $ hg --config extensions.convert= --config lfs.threshold=1000M \
573 > convert repo8 convert_normal
575 > convert repo8 convert_normal
574 initializing destination convert_normal repository
576 initializing destination convert_normal repository
575 scanning source...
577 scanning source...
576 sorting...
578 sorting...
577 converting...
579 converting...
578 2 a
580 2 a
579 1 b
581 1 b
580 0 meta
582 0 meta
581 $ grep 'lfs' convert_normal/.hg/requires
583 $ grep 'lfs' convert_normal/.hg/requires
582 [1]
584 [1]
583 $ hg --cwd convert_normal debugdata a1 0
585 $ hg --cwd convert_normal debugdata a1 0
584 THIS-IS-LFS-BECAUSE-10-BYTES
586 THIS-IS-LFS-BECAUSE-10-BYTES
585
587
586 $ hg --config extensions.convert= --config lfs.threshold=10B \
588 $ hg --config extensions.convert= --config lfs.threshold=10B \
587 > convert convert_normal convert_lfs
589 > convert convert_normal convert_lfs
588 initializing destination convert_lfs repository
590 initializing destination convert_lfs repository
589 scanning source...
591 scanning source...
590 sorting...
592 sorting...
591 converting...
593 converting...
592 2 a
594 2 a
593 1 b
595 1 b
594 0 meta
596 0 meta
595 $ hg --cwd convert_lfs debugdata a1 0
597 $ hg --cwd convert_lfs debugdata a1 0
596 version https://git-lfs.github.com/spec/v1
598 version https://git-lfs.github.com/spec/v1
597 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
599 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
598 size 29
600 size 29
599 x-is-binary 0
601 x-is-binary 0
600 $ grep 'lfs' convert_lfs/.hg/requires
602 $ grep 'lfs' convert_lfs/.hg/requires
601 lfs
603 lfs
602
604
603 This convert is trickier, because it contains deleted files (via `hg mv`)
605 This convert is trickier, because it contains deleted files (via `hg mv`)
604
606
605 $ hg --config extensions.convert= --config lfs.threshold=1000M \
607 $ hg --config extensions.convert= --config lfs.threshold=1000M \
606 > convert repo3 convert_normal2
608 > convert repo3 convert_normal2
607 initializing destination convert_normal2 repository
609 initializing destination convert_normal2 repository
608 scanning source...
610 scanning source...
609 sorting...
611 sorting...
610 converting...
612 converting...
611 4 commit with lfs content
613 4 commit with lfs content
612 3 renames
614 3 renames
613 2 large to small, small to large
615 2 large to small, small to large
614 1 random modifications
616 1 random modifications
615 0 switch large and small again
617 0 switch large and small again
616 $ grep 'lfs' convert_normal2/.hg/requires
618 $ grep 'lfs' convert_normal2/.hg/requires
617 [1]
619 [1]
618 $ hg --cwd convert_normal2 debugdata large 0
620 $ hg --cwd convert_normal2 debugdata large 0
619 LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS
621 LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS
620
622
621 $ hg --config extensions.convert= --config lfs.threshold=10B \
623 $ hg --config extensions.convert= --config lfs.threshold=10B \
622 > convert convert_normal2 convert_lfs2
624 > convert convert_normal2 convert_lfs2
623 initializing destination convert_lfs2 repository
625 initializing destination convert_lfs2 repository
624 scanning source...
626 scanning source...
625 sorting...
627 sorting...
626 converting...
628 converting...
627 4 commit with lfs content
629 4 commit with lfs content
628 3 renames
630 3 renames
629 2 large to small, small to large
631 2 large to small, small to large
630 1 random modifications
632 1 random modifications
631 0 switch large and small again
633 0 switch large and small again
632 $ grep 'lfs' convert_lfs2/.hg/requires
634 $ grep 'lfs' convert_lfs2/.hg/requires
633 lfs
635 lfs
634 $ hg --cwd convert_lfs2 debugdata large 0
636 $ hg --cwd convert_lfs2 debugdata large 0
635 version https://git-lfs.github.com/spec/v1
637 version https://git-lfs.github.com/spec/v1
636 oid sha256:66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
638 oid sha256:66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
637 size 39
639 size 39
638 x-is-binary 0
640 x-is-binary 0
General Comments 0
You need to be logged in to leave comments. Login now