##// END OF EJS Templates
lfs: drop unused import...
Gregory Szorc -
r39904:b06303a2 default
parent child Browse files
Show More
@@ -1,402 +1,401 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 This extension allows large files to be tracked outside of the normal
10 This extension allows large files to be tracked outside of the normal
11 repository storage and stored on a centralized server, similar to the
11 repository storage and stored on a centralized server, similar to the
12 ``largefiles`` extension. The ``git-lfs`` protocol is used when
12 ``largefiles`` extension. The ``git-lfs`` protocol is used when
13 communicating with the server, so existing git infrastructure can be
13 communicating with the server, so existing git infrastructure can be
14 harnessed. Even though the files are stored outside of the repository,
14 harnessed. Even though the files are stored outside of the repository,
15 they are still integrity checked in the same manner as normal files.
15 they are still integrity checked in the same manner as normal files.
16
16
17 The files stored outside of the repository are downloaded on demand,
17 The files stored outside of the repository are downloaded on demand,
18 which reduces the time to clone, and possibly the local disk usage.
18 which reduces the time to clone, and possibly the local disk usage.
19 This changes fundamental workflows in a DVCS, so careful thought
19 This changes fundamental workflows in a DVCS, so careful thought
20 should be given before deploying it. :hg:`convert` can be used to
20 should be given before deploying it. :hg:`convert` can be used to
21 convert LFS repositories to normal repositories that no longer
21 convert LFS repositories to normal repositories that no longer
22 require this extension, and do so without changing the commit hashes.
22 require this extension, and do so without changing the commit hashes.
23 This allows the extension to be disabled if the centralized workflow
23 This allows the extension to be disabled if the centralized workflow
24 becomes burdensome. However, the pre and post convert clones will
24 becomes burdensome. However, the pre and post convert clones will
25 not be able to communicate with each other unless the extension is
25 not be able to communicate with each other unless the extension is
26 enabled on both.
26 enabled on both.
27
27
28 To start a new repository, or to add LFS files to an existing one, just
28 To start a new repository, or to add LFS files to an existing one, just
29 create an ``.hglfs`` file as described below in the root directory of
29 create an ``.hglfs`` file as described below in the root directory of
30 the repository. Typically, this file should be put under version
30 the repository. Typically, this file should be put under version
31 control, so that the settings will propagate to other repositories with
31 control, so that the settings will propagate to other repositories with
32 push and pull. During any commit, Mercurial will consult this file to
32 push and pull. During any commit, Mercurial will consult this file to
33 determine if an added or modified file should be stored externally. The
33 determine if an added or modified file should be stored externally. The
34 type of storage depends on the characteristics of the file at each
34 type of storage depends on the characteristics of the file at each
35 commit. A file that is near a size threshold may switch back and forth
35 commit. A file that is near a size threshold may switch back and forth
36 between LFS and normal storage, as needed.
36 between LFS and normal storage, as needed.
37
37
38 Alternately, both normal repositories and largefile controlled
38 Alternately, both normal repositories and largefile controlled
39 repositories can be converted to LFS by using :hg:`convert` and the
39 repositories can be converted to LFS by using :hg:`convert` and the
40 ``lfs.track`` config option described below. The ``.hglfs`` file
40 ``lfs.track`` config option described below. The ``.hglfs`` file
41 should then be created and added, to control subsequent LFS selection.
41 should then be created and added, to control subsequent LFS selection.
42 The hashes are also unchanged in this case. The LFS and non-LFS
42 The hashes are also unchanged in this case. The LFS and non-LFS
43 repositories can be distinguished because the LFS repository will
43 repositories can be distinguished because the LFS repository will
44 abort any command if this extension is disabled.
44 abort any command if this extension is disabled.
45
45
46 Committed LFS files are held locally, until the repository is pushed.
46 Committed LFS files are held locally, until the repository is pushed.
47 Prior to pushing the normal repository data, the LFS files that are
47 Prior to pushing the normal repository data, the LFS files that are
48 tracked by the outgoing commits are automatically uploaded to the
48 tracked by the outgoing commits are automatically uploaded to the
49 configured central server. No LFS files are transferred on
49 configured central server. No LFS files are transferred on
50 :hg:`pull` or :hg:`clone`. Instead, the files are downloaded on
50 :hg:`pull` or :hg:`clone`. Instead, the files are downloaded on
51 demand as they need to be read, if a cached copy cannot be found
51 demand as they need to be read, if a cached copy cannot be found
52 locally. Both committing and downloading an LFS file will link the
52 locally. Both committing and downloading an LFS file will link the
53 file to a usercache, to speed up future access. See the `usercache`
53 file to a usercache, to speed up future access. See the `usercache`
54 config setting described below.
54 config setting described below.
55
55
56 .hglfs::
56 .hglfs::
57
57
58 The extension reads its configuration from a versioned ``.hglfs``
58 The extension reads its configuration from a versioned ``.hglfs``
59 configuration file found in the root of the working directory. The
59 configuration file found in the root of the working directory. The
60 ``.hglfs`` file uses the same syntax as all other Mercurial
60 ``.hglfs`` file uses the same syntax as all other Mercurial
61 configuration files. It uses a single section, ``[track]``.
61 configuration files. It uses a single section, ``[track]``.
62
62
63 The ``[track]`` section specifies which files are stored as LFS (or
63 The ``[track]`` section specifies which files are stored as LFS (or
64 not). Each line is keyed by a file pattern, with a predicate value.
64 not). Each line is keyed by a file pattern, with a predicate value.
65 The first file pattern match is used, so put more specific patterns
65 The first file pattern match is used, so put more specific patterns
66 first. The available predicates are ``all()``, ``none()``, and
66 first. The available predicates are ``all()``, ``none()``, and
67 ``size()``. See "hg help filesets.size" for the latter.
67 ``size()``. See "hg help filesets.size" for the latter.
68
68
69 Example versioned ``.hglfs`` file::
69 Example versioned ``.hglfs`` file::
70
70
71 [track]
71 [track]
72 # No Makefile or python file, anywhere, will be LFS
72 # No Makefile or python file, anywhere, will be LFS
73 **Makefile = none()
73 **Makefile = none()
74 **.py = none()
74 **.py = none()
75
75
76 **.zip = all()
76 **.zip = all()
77 **.exe = size(">1MB")
77 **.exe = size(">1MB")
78
78
79 # Catchall for everything not matched above
79 # Catchall for everything not matched above
80 ** = size(">10MB")
80 ** = size(">10MB")
81
81
82 Configs::
82 Configs::
83
83
84 [lfs]
84 [lfs]
85 # Remote endpoint. Multiple protocols are supported:
85 # Remote endpoint. Multiple protocols are supported:
86 # - http(s)://user:pass@example.com/path
86 # - http(s)://user:pass@example.com/path
87 # git-lfs endpoint
87 # git-lfs endpoint
88 # - file:///tmp/path
88 # - file:///tmp/path
89 # local filesystem, usually for testing
89 # local filesystem, usually for testing
90 # if unset, lfs will assume the remote repository also handles blob storage
90 # if unset, lfs will assume the remote repository also handles blob storage
91 # for http(s) URLs. Otherwise, lfs will prompt to set this when it must
91 # for http(s) URLs. Otherwise, lfs will prompt to set this when it must
92 # use this value.
92 # use this value.
93 # (default: unset)
93 # (default: unset)
94 url = https://example.com/repo.git/info/lfs
94 url = https://example.com/repo.git/info/lfs
95
95
96 # Which files to track in LFS. Path tests are "**.extname" for file
96 # Which files to track in LFS. Path tests are "**.extname" for file
97 # extensions, and "path:under/some/directory" for path prefix. Both
97 # extensions, and "path:under/some/directory" for path prefix. Both
98 # are relative to the repository root.
98 # are relative to the repository root.
99 # File size can be tested with the "size()" fileset, and tests can be
99 # File size can be tested with the "size()" fileset, and tests can be
100 # joined with fileset operators. (See "hg help filesets.operators".)
100 # joined with fileset operators. (See "hg help filesets.operators".)
101 #
101 #
102 # Some examples:
102 # Some examples:
103 # - all() # everything
103 # - all() # everything
104 # - none() # nothing
104 # - none() # nothing
105 # - size(">20MB") # larger than 20MB
105 # - size(">20MB") # larger than 20MB
106 # - !**.txt # anything not a *.txt file
106 # - !**.txt # anything not a *.txt file
107 # - **.zip | **.tar.gz | **.7z # some types of compressed files
107 # - **.zip | **.tar.gz | **.7z # some types of compressed files
108 # - path:bin # files under "bin" in the project root
108 # - path:bin # files under "bin" in the project root
109 # - (**.php & size(">2MB")) | (**.js & size(">5MB")) | **.tar.gz
109 # - (**.php & size(">2MB")) | (**.js & size(">5MB")) | **.tar.gz
110 # | (path:bin & !path:/bin/README) | size(">1GB")
110 # | (path:bin & !path:/bin/README) | size(">1GB")
111 # (default: none())
111 # (default: none())
112 #
112 #
113 # This is ignored if there is a tracked '.hglfs' file, and this setting
113 # This is ignored if there is a tracked '.hglfs' file, and this setting
114 # will eventually be deprecated and removed.
114 # will eventually be deprecated and removed.
115 track = size(">10M")
115 track = size(">10M")
116
116
117 # how many times to retry before giving up on transferring an object
117 # how many times to retry before giving up on transferring an object
118 retry = 5
118 retry = 5
119
119
120 # the local directory to store lfs files for sharing across local clones.
120 # the local directory to store lfs files for sharing across local clones.
121 # If not set, the cache is located in an OS specific cache location.
121 # If not set, the cache is located in an OS specific cache location.
122 usercache = /path/to/global/cache
122 usercache = /path/to/global/cache
123 """
123 """
124
124
125 from __future__ import absolute_import
125 from __future__ import absolute_import
126
126
127 from mercurial.i18n import _
127 from mercurial.i18n import _
128
128
129 from mercurial import (
129 from mercurial import (
130 bundle2,
130 bundle2,
131 changegroup,
131 changegroup,
132 cmdutil,
132 cmdutil,
133 config,
133 config,
134 context,
134 context,
135 error,
135 error,
136 exchange,
136 exchange,
137 extensions,
137 extensions,
138 filelog,
138 filelog,
139 filesetlang,
139 filesetlang,
140 hg,
141 localrepo,
140 localrepo,
142 minifileset,
141 minifileset,
143 node,
142 node,
144 pycompat,
143 pycompat,
145 registrar,
144 registrar,
146 repository,
145 repository,
147 revlog,
146 revlog,
148 scmutil,
147 scmutil,
149 templateutil,
148 templateutil,
150 upgrade,
149 upgrade,
151 util,
150 util,
152 vfs as vfsmod,
151 vfs as vfsmod,
153 wireprotoserver,
152 wireprotoserver,
154 wireprotov1server,
153 wireprotov1server,
155 )
154 )
156
155
157 from . import (
156 from . import (
158 blobstore,
157 blobstore,
159 wireprotolfsserver,
158 wireprotolfsserver,
160 wrapper,
159 wrapper,
161 )
160 )
162
161
163 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
162 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
164 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
163 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
165 # be specifying the version(s) of Mercurial they are tested with, or
164 # be specifying the version(s) of Mercurial they are tested with, or
166 # leave the attribute unspecified.
165 # leave the attribute unspecified.
167 testedwith = 'ships-with-hg-core'
166 testedwith = 'ships-with-hg-core'
168
167
169 configtable = {}
168 configtable = {}
170 configitem = registrar.configitem(configtable)
169 configitem = registrar.configitem(configtable)
171
170
172 configitem('experimental', 'lfs.serve',
171 configitem('experimental', 'lfs.serve',
173 default=True,
172 default=True,
174 )
173 )
175 configitem('experimental', 'lfs.user-agent',
174 configitem('experimental', 'lfs.user-agent',
176 default=None,
175 default=None,
177 )
176 )
178 configitem('experimental', 'lfs.disableusercache',
177 configitem('experimental', 'lfs.disableusercache',
179 default=False,
178 default=False,
180 )
179 )
181 configitem('experimental', 'lfs.worker-enable',
180 configitem('experimental', 'lfs.worker-enable',
182 default=False,
181 default=False,
183 )
182 )
184
183
185 configitem('lfs', 'url',
184 configitem('lfs', 'url',
186 default=None,
185 default=None,
187 )
186 )
188 configitem('lfs', 'usercache',
187 configitem('lfs', 'usercache',
189 default=None,
188 default=None,
190 )
189 )
191 # Deprecated
190 # Deprecated
192 configitem('lfs', 'threshold',
191 configitem('lfs', 'threshold',
193 default=None,
192 default=None,
194 )
193 )
195 configitem('lfs', 'track',
194 configitem('lfs', 'track',
196 default='none()',
195 default='none()',
197 )
196 )
198 configitem('lfs', 'retry',
197 configitem('lfs', 'retry',
199 default=5,
198 default=5,
200 )
199 )
201
200
202 cmdtable = {}
201 cmdtable = {}
203 command = registrar.command(cmdtable)
202 command = registrar.command(cmdtable)
204
203
205 templatekeyword = registrar.templatekeyword()
204 templatekeyword = registrar.templatekeyword()
206 filesetpredicate = registrar.filesetpredicate()
205 filesetpredicate = registrar.filesetpredicate()
207
206
208 def featuresetup(ui, supported):
207 def featuresetup(ui, supported):
209 # don't die on seeing a repo with the lfs requirement
208 # don't die on seeing a repo with the lfs requirement
210 supported |= {'lfs'}
209 supported |= {'lfs'}
211
210
212 def uisetup(ui):
211 def uisetup(ui):
213 localrepo.featuresetupfuncs.add(featuresetup)
212 localrepo.featuresetupfuncs.add(featuresetup)
214
213
215 def reposetup(ui, repo):
214 def reposetup(ui, repo):
216 # Nothing to do with a remote repo
215 # Nothing to do with a remote repo
217 if not repo.local():
216 if not repo.local():
218 return
217 return
219
218
220 repo.svfs.lfslocalblobstore = blobstore.local(repo)
219 repo.svfs.lfslocalblobstore = blobstore.local(repo)
221 repo.svfs.lfsremoteblobstore = blobstore.remote(repo)
220 repo.svfs.lfsremoteblobstore = blobstore.remote(repo)
222
221
223 class lfsrepo(repo.__class__):
222 class lfsrepo(repo.__class__):
224 @localrepo.unfilteredmethod
223 @localrepo.unfilteredmethod
225 def commitctx(self, ctx, error=False):
224 def commitctx(self, ctx, error=False):
226 repo.svfs.options['lfstrack'] = _trackedmatcher(self)
225 repo.svfs.options['lfstrack'] = _trackedmatcher(self)
227 return super(lfsrepo, self).commitctx(ctx, error)
226 return super(lfsrepo, self).commitctx(ctx, error)
228
227
229 repo.__class__ = lfsrepo
228 repo.__class__ = lfsrepo
230
229
231 if 'lfs' not in repo.requirements:
230 if 'lfs' not in repo.requirements:
232 def checkrequireslfs(ui, repo, **kwargs):
231 def checkrequireslfs(ui, repo, **kwargs):
233 if 'lfs' not in repo.requirements:
232 if 'lfs' not in repo.requirements:
234 last = kwargs.get(r'node_last')
233 last = kwargs.get(r'node_last')
235 _bin = node.bin
234 _bin = node.bin
236 if last:
235 if last:
237 s = repo.set('%n:%n', _bin(kwargs[r'node']), _bin(last))
236 s = repo.set('%n:%n', _bin(kwargs[r'node']), _bin(last))
238 else:
237 else:
239 s = repo.set('%n', _bin(kwargs[r'node']))
238 s = repo.set('%n', _bin(kwargs[r'node']))
240 match = repo.narrowmatch()
239 match = repo.narrowmatch()
241 for ctx in s:
240 for ctx in s:
242 # TODO: is there a way to just walk the files in the commit?
241 # TODO: is there a way to just walk the files in the commit?
243 if any(ctx[f].islfs() for f in ctx.files()
242 if any(ctx[f].islfs() for f in ctx.files()
244 if f in ctx and match(f)):
243 if f in ctx and match(f)):
245 repo.requirements.add('lfs')
244 repo.requirements.add('lfs')
246 repo.features.add(repository.REPO_FEATURE_LFS)
245 repo.features.add(repository.REPO_FEATURE_LFS)
247 repo._writerequirements()
246 repo._writerequirements()
248 repo.prepushoutgoinghooks.add('lfs', wrapper.prepush)
247 repo.prepushoutgoinghooks.add('lfs', wrapper.prepush)
249 break
248 break
250
249
251 ui.setconfig('hooks', 'commit.lfs', checkrequireslfs, 'lfs')
250 ui.setconfig('hooks', 'commit.lfs', checkrequireslfs, 'lfs')
252 ui.setconfig('hooks', 'pretxnchangegroup.lfs', checkrequireslfs, 'lfs')
251 ui.setconfig('hooks', 'pretxnchangegroup.lfs', checkrequireslfs, 'lfs')
253 else:
252 else:
254 repo.prepushoutgoinghooks.add('lfs', wrapper.prepush)
253 repo.prepushoutgoinghooks.add('lfs', wrapper.prepush)
255
254
256 def _trackedmatcher(repo):
255 def _trackedmatcher(repo):
257 """Return a function (path, size) -> bool indicating whether or not to
256 """Return a function (path, size) -> bool indicating whether or not to
258 track a given file with lfs."""
257 track a given file with lfs."""
259 if not repo.wvfs.exists('.hglfs'):
258 if not repo.wvfs.exists('.hglfs'):
260 # No '.hglfs' in wdir. Fallback to config for now.
259 # No '.hglfs' in wdir. Fallback to config for now.
261 trackspec = repo.ui.config('lfs', 'track')
260 trackspec = repo.ui.config('lfs', 'track')
262
261
263 # deprecated config: lfs.threshold
262 # deprecated config: lfs.threshold
264 threshold = repo.ui.configbytes('lfs', 'threshold')
263 threshold = repo.ui.configbytes('lfs', 'threshold')
265 if threshold:
264 if threshold:
266 filesetlang.parse(trackspec) # make sure syntax errors are confined
265 filesetlang.parse(trackspec) # make sure syntax errors are confined
267 trackspec = "(%s) | size('>%d')" % (trackspec, threshold)
266 trackspec = "(%s) | size('>%d')" % (trackspec, threshold)
268
267
269 return minifileset.compile(trackspec)
268 return minifileset.compile(trackspec)
270
269
271 data = repo.wvfs.tryread('.hglfs')
270 data = repo.wvfs.tryread('.hglfs')
272 if not data:
271 if not data:
273 return lambda p, s: False
272 return lambda p, s: False
274
273
275 # Parse errors here will abort with a message that points to the .hglfs file
274 # Parse errors here will abort with a message that points to the .hglfs file
276 # and line number.
275 # and line number.
277 cfg = config.config()
276 cfg = config.config()
278 cfg.parse('.hglfs', data)
277 cfg.parse('.hglfs', data)
279
278
280 try:
279 try:
281 rules = [(minifileset.compile(pattern), minifileset.compile(rule))
280 rules = [(minifileset.compile(pattern), minifileset.compile(rule))
282 for pattern, rule in cfg.items('track')]
281 for pattern, rule in cfg.items('track')]
283 except error.ParseError as e:
282 except error.ParseError as e:
284 # The original exception gives no indicator that the error is in the
283 # The original exception gives no indicator that the error is in the
285 # .hglfs file, so add that.
284 # .hglfs file, so add that.
286
285
287 # TODO: See if the line number of the file can be made available.
286 # TODO: See if the line number of the file can be made available.
288 raise error.Abort(_('parse error in .hglfs: %s') % e)
287 raise error.Abort(_('parse error in .hglfs: %s') % e)
289
288
290 def _match(path, size):
289 def _match(path, size):
291 for pat, rule in rules:
290 for pat, rule in rules:
292 if pat(path, size):
291 if pat(path, size):
293 return rule(path, size)
292 return rule(path, size)
294
293
295 return False
294 return False
296
295
297 return _match
296 return _match
298
297
299 def wrapfilelog(filelog):
298 def wrapfilelog(filelog):
300 wrapfunction = extensions.wrapfunction
299 wrapfunction = extensions.wrapfunction
301
300
302 wrapfunction(filelog, 'addrevision', wrapper.filelogaddrevision)
301 wrapfunction(filelog, 'addrevision', wrapper.filelogaddrevision)
303 wrapfunction(filelog, 'renamed', wrapper.filelogrenamed)
302 wrapfunction(filelog, 'renamed', wrapper.filelogrenamed)
304 wrapfunction(filelog, 'size', wrapper.filelogsize)
303 wrapfunction(filelog, 'size', wrapper.filelogsize)
305
304
306 def extsetup(ui):
305 def extsetup(ui):
307 wrapfilelog(filelog.filelog)
306 wrapfilelog(filelog.filelog)
308
307
309 wrapfunction = extensions.wrapfunction
308 wrapfunction = extensions.wrapfunction
310
309
311 wrapfunction(localrepo, 'makefilestorage', wrapper.localrepomakefilestorage)
310 wrapfunction(localrepo, 'makefilestorage', wrapper.localrepomakefilestorage)
312
311
313 wrapfunction(cmdutil, '_updatecatformatter', wrapper._updatecatformatter)
312 wrapfunction(cmdutil, '_updatecatformatter', wrapper._updatecatformatter)
314 wrapfunction(scmutil, 'wrapconvertsink', wrapper.convertsink)
313 wrapfunction(scmutil, 'wrapconvertsink', wrapper.convertsink)
315
314
316 wrapfunction(upgrade, '_finishdatamigration',
315 wrapfunction(upgrade, '_finishdatamigration',
317 wrapper.upgradefinishdatamigration)
316 wrapper.upgradefinishdatamigration)
318
317
319 wrapfunction(upgrade, 'preservedrequirements',
318 wrapfunction(upgrade, 'preservedrequirements',
320 wrapper.upgraderequirements)
319 wrapper.upgraderequirements)
321
320
322 wrapfunction(upgrade, 'supporteddestrequirements',
321 wrapfunction(upgrade, 'supporteddestrequirements',
323 wrapper.upgraderequirements)
322 wrapper.upgraderequirements)
324
323
325 wrapfunction(changegroup,
324 wrapfunction(changegroup,
326 'allsupportedversions',
325 'allsupportedversions',
327 wrapper.allsupportedversions)
326 wrapper.allsupportedversions)
328
327
329 wrapfunction(exchange, 'push', wrapper.push)
328 wrapfunction(exchange, 'push', wrapper.push)
330 wrapfunction(wireprotov1server, '_capabilities', wrapper._capabilities)
329 wrapfunction(wireprotov1server, '_capabilities', wrapper._capabilities)
331 wrapfunction(wireprotoserver, 'handlewsgirequest',
330 wrapfunction(wireprotoserver, 'handlewsgirequest',
332 wireprotolfsserver.handlewsgirequest)
331 wireprotolfsserver.handlewsgirequest)
333
332
334 wrapfunction(context.basefilectx, 'cmp', wrapper.filectxcmp)
333 wrapfunction(context.basefilectx, 'cmp', wrapper.filectxcmp)
335 wrapfunction(context.basefilectx, 'isbinary', wrapper.filectxisbinary)
334 wrapfunction(context.basefilectx, 'isbinary', wrapper.filectxisbinary)
336 context.basefilectx.islfs = wrapper.filectxislfs
335 context.basefilectx.islfs = wrapper.filectxislfs
337
336
338 revlog.addflagprocessor(
337 revlog.addflagprocessor(
339 revlog.REVIDX_EXTSTORED,
338 revlog.REVIDX_EXTSTORED,
340 (
339 (
341 wrapper.readfromstore,
340 wrapper.readfromstore,
342 wrapper.writetostore,
341 wrapper.writetostore,
343 wrapper.bypasscheckhash,
342 wrapper.bypasscheckhash,
344 ),
343 ),
345 )
344 )
346
345
347 scmutil.fileprefetchhooks.add('lfs', wrapper._prefetchfiles)
346 scmutil.fileprefetchhooks.add('lfs', wrapper._prefetchfiles)
348
347
349 # Make bundle choose changegroup3 instead of changegroup2. This affects
348 # Make bundle choose changegroup3 instead of changegroup2. This affects
350 # "hg bundle" command. Note: it does not cover all bundle formats like
349 # "hg bundle" command. Note: it does not cover all bundle formats like
351 # "packed1". Using "packed1" with lfs will likely cause trouble.
350 # "packed1". Using "packed1" with lfs will likely cause trouble.
352 exchange._bundlespeccontentopts["v2"]["cg.version"] = "03"
351 exchange._bundlespeccontentopts["v2"]["cg.version"] = "03"
353
352
354 # bundlerepo uses "vfsmod.readonlyvfs(othervfs)", we need to make sure lfs
353 # bundlerepo uses "vfsmod.readonlyvfs(othervfs)", we need to make sure lfs
355 # options and blob stores are passed from othervfs to the new readonlyvfs.
354 # options and blob stores are passed from othervfs to the new readonlyvfs.
356 wrapfunction(vfsmod.readonlyvfs, '__init__', wrapper.vfsinit)
355 wrapfunction(vfsmod.readonlyvfs, '__init__', wrapper.vfsinit)
357
356
358 # when writing a bundle via "hg bundle" command, upload related LFS blobs
357 # when writing a bundle via "hg bundle" command, upload related LFS blobs
359 wrapfunction(bundle2, 'writenewbundle', wrapper.writenewbundle)
358 wrapfunction(bundle2, 'writenewbundle', wrapper.writenewbundle)
360
359
361 @filesetpredicate('lfs()')
360 @filesetpredicate('lfs()')
362 def lfsfileset(mctx, x):
361 def lfsfileset(mctx, x):
363 """File that uses LFS storage."""
362 """File that uses LFS storage."""
364 # i18n: "lfs" is a keyword
363 # i18n: "lfs" is a keyword
365 filesetlang.getargs(x, 0, 0, _("lfs takes no arguments"))
364 filesetlang.getargs(x, 0, 0, _("lfs takes no arguments"))
366 ctx = mctx.ctx
365 ctx = mctx.ctx
367 def lfsfilep(f):
366 def lfsfilep(f):
368 return wrapper.pointerfromctx(ctx, f, removed=True) is not None
367 return wrapper.pointerfromctx(ctx, f, removed=True) is not None
369 return mctx.predicate(lfsfilep, predrepr='<lfs>')
368 return mctx.predicate(lfsfilep, predrepr='<lfs>')
370
369
371 @templatekeyword('lfs_files', requires={'ctx'})
370 @templatekeyword('lfs_files', requires={'ctx'})
372 def lfsfiles(context, mapping):
371 def lfsfiles(context, mapping):
373 """List of strings. All files modified, added, or removed by this
372 """List of strings. All files modified, added, or removed by this
374 changeset."""
373 changeset."""
375 ctx = context.resource(mapping, 'ctx')
374 ctx = context.resource(mapping, 'ctx')
376
375
377 pointers = wrapper.pointersfromctx(ctx, removed=True) # {path: pointer}
376 pointers = wrapper.pointersfromctx(ctx, removed=True) # {path: pointer}
378 files = sorted(pointers.keys())
377 files = sorted(pointers.keys())
379
378
380 def pointer(v):
379 def pointer(v):
381 # In the file spec, version is first and the other keys are sorted.
380 # In the file spec, version is first and the other keys are sorted.
382 sortkeyfunc = lambda x: (x[0] != 'version', x)
381 sortkeyfunc = lambda x: (x[0] != 'version', x)
383 items = sorted(pointers[v].iteritems(), key=sortkeyfunc)
382 items = sorted(pointers[v].iteritems(), key=sortkeyfunc)
384 return util.sortdict(items)
383 return util.sortdict(items)
385
384
386 makemap = lambda v: {
385 makemap = lambda v: {
387 'file': v,
386 'file': v,
388 'lfsoid': pointers[v].oid() if pointers[v] else None,
387 'lfsoid': pointers[v].oid() if pointers[v] else None,
389 'lfspointer': templateutil.hybriddict(pointer(v)),
388 'lfspointer': templateutil.hybriddict(pointer(v)),
390 }
389 }
391
390
392 # TODO: make the separator ', '?
391 # TODO: make the separator ', '?
393 f = templateutil._showcompatlist(context, mapping, 'lfs_file', files)
392 f = templateutil._showcompatlist(context, mapping, 'lfs_file', files)
394 return templateutil.hybrid(f, files, makemap, pycompat.identity)
393 return templateutil.hybrid(f, files, makemap, pycompat.identity)
395
394
396 @command('debuglfsupload',
395 @command('debuglfsupload',
397 [('r', 'rev', [], _('upload large files introduced by REV'))])
396 [('r', 'rev', [], _('upload large files introduced by REV'))])
398 def debuglfsupload(ui, repo, **opts):
397 def debuglfsupload(ui, repo, **opts):
399 """upload lfs blobs added by the working copy parent or given revisions"""
398 """upload lfs blobs added by the working copy parent or given revisions"""
400 revs = opts.get(r'rev', [])
399 revs = opts.get(r'rev', [])
401 pointers = wrapper.extractpointers(repo, scmutil.revrange(repo, revs))
400 pointers = wrapper.extractpointers(repo, scmutil.revrange(repo, revs))
402 wrapper.uploadblobs(repo, pointers)
401 wrapper.uploadblobs(repo, pointers)
General Comments 0
You need to be logged in to leave comments. Login now