##// END OF EJS Templates
lfs: control tracked file selection via a tracked file...
Matt Harbison -
r35683:1ad1e59b default
parent child Browse files
Show More
@@ -7,6 +7,30 b''
7
7
8 """lfs - large file support (EXPERIMENTAL)
8 """lfs - large file support (EXPERIMENTAL)
9
9
10 The extension reads its configuration from a versioned ``.hglfs``
11 configuration file found in the root of the working directory. The
12 ``.hglfs`` file uses the same syntax as all other Mercurial
13 configuration files. It uses a single section, ``[track]``.
14
15 The ``[track]`` section specifies which files are stored as LFS (or
16 not). Each line is keyed by a file pattern, with a predicate value.
17 The first file pattern match is used, so put more specific patterns
18 first. The available predicates are ``all()``, ``none()``, and
19 ``size()``. See "hg help filesets.size" for the latter.
20
21 Example versioned ``.hglfs`` file::
22
23 [track]
24 # No Makefile or python file, anywhere, will be LFS
25 **Makefile = none()
26 **.py = none()
27
28 **.zip = all()
29 **.exe = size(">1MB")
30
31 # Catchall for everything not matched above
32 ** = size(">10MB")
33
10 Configs::
34 Configs::
11
35
12 [lfs]
36 [lfs]
@@ -35,6 +59,9 b' Configs::'
35 # - (**.php & size(">2MB")) | (**.js & size(">5MB")) | **.tar.gz
59 # - (**.php & size(">2MB")) | (**.js & size(">5MB")) | **.tar.gz
36 # | ("path:bin" & !"path:/bin/README") | size(">1GB")
60 # | ("path:bin" & !"path:/bin/README") | size(">1GB")
37 # (default: none())
61 # (default: none())
62 #
63 # This is ignored if there is a tracked '.hglfs' file, and this setting
64 # will eventually be deprecated and removed.
38 track = size(">10M")
65 track = size(">10M")
39
66
40 # how many times to retry before giving up on transferring an object
67 # how many times to retry before giving up on transferring an object
@@ -53,7 +80,9 b' from mercurial import ('
53 bundle2,
80 bundle2,
54 changegroup,
81 changegroup,
55 cmdutil,
82 cmdutil,
83 config,
56 context,
84 context,
85 error,
57 exchange,
86 exchange,
58 extensions,
87 extensions,
59 filelog,
88 filelog,
@@ -124,13 +153,20 b' def reposetup(ui, repo):'
124 if not repo.local():
153 if not repo.local():
125 return
154 return
126
155
127 repo.svfs.options['lfstrack'] = _trackedmatcher(repo)
128 repo.svfs.lfslocalblobstore = blobstore.local(repo)
156 repo.svfs.lfslocalblobstore = blobstore.local(repo)
129 repo.svfs.lfsremoteblobstore = blobstore.remote(repo)
157 repo.svfs.lfsremoteblobstore = blobstore.remote(repo)
130
158
131 # Push hook
159 # Push hook
132 repo.prepushoutgoinghooks.add('lfs', wrapper.prepush)
160 repo.prepushoutgoinghooks.add('lfs', wrapper.prepush)
133
161
162 class lfsrepo(repo.__class__):
163 @localrepo.unfilteredmethod
164 def commitctx(self, ctx, error=False):
165 repo.svfs.options['lfstrack'] = _trackedmatcher(self, ctx)
166 return super(lfsrepo, self).commitctx(ctx, error)
167
168 repo.__class__ = lfsrepo
169
134 if 'lfs' not in repo.requirements:
170 if 'lfs' not in repo.requirements:
135 def checkrequireslfs(ui, repo, **kwargs):
171 def checkrequireslfs(ui, repo, **kwargs):
136 if 'lfs' not in repo.requirements:
172 if 'lfs' not in repo.requirements:
@@ -150,18 +186,58 b' def reposetup(ui, repo):'
150 ui.setconfig('hooks', 'commit.lfs', checkrequireslfs, 'lfs')
186 ui.setconfig('hooks', 'commit.lfs', checkrequireslfs, 'lfs')
151 ui.setconfig('hooks', 'pretxnchangegroup.lfs', checkrequireslfs, 'lfs')
187 ui.setconfig('hooks', 'pretxnchangegroup.lfs', checkrequireslfs, 'lfs')
152
188
153 def _trackedmatcher(repo):
189 def _trackedmatcher(repo, ctx):
154 """Return a function (path, size) -> bool indicating whether or not to
190 """Return a function (path, size) -> bool indicating whether or not to
155 track a given file with lfs."""
191 track a given file with lfs."""
156 trackspec = repo.ui.config('lfs', 'track')
192 data = ''
193
194 if '.hglfs' in ctx.added() or '.hglfs' in ctx.modified():
195 data = ctx['.hglfs'].data()
196 elif '.hglfs' not in ctx.removed():
197 p1 = repo['.']
198
199 if '.hglfs' not in p1:
200 # No '.hglfs' in wdir or in parent. Fallback to config
201 # for now.
202 trackspec = repo.ui.config('lfs', 'track')
203
204 # deprecated config: lfs.threshold
205 threshold = repo.ui.configbytes('lfs', 'threshold')
206 if threshold:
207 fileset.parse(trackspec) # make sure syntax errors are confined
208 trackspec = "(%s) | size('>%d')" % (trackspec, threshold)
209
210 return minifileset.compile(trackspec)
211
212 data = p1['.hglfs'].data()
157
213
158 # deprecated config: lfs.threshold
214 # In removed, or not in parent
159 threshold = repo.ui.configbytes('lfs', 'threshold')
215 if not data:
160 if threshold:
216 return lambda p, s: False
161 fileset.parse(trackspec) # make sure syntax errors are confined
217
162 trackspec = "(%s) | size('>%d')" % (trackspec, threshold)
218 # Parse errors here will abort with a message that points to the .hglfs file
219 # and line number.
220 cfg = config.config()
221 cfg.parse('.hglfs', data)
163
222
164 return minifileset.compile(trackspec)
223 try:
224 rules = [(minifileset.compile(pattern), minifileset.compile(rule))
225 for pattern, rule in cfg.items('track')]
226 except error.ParseError as e:
227 # The original exception gives no indicator that the error is in the
228 # .hglfs file, so add that.
229
230 # TODO: See if the line number of the file can be made available.
231 raise error.Abort(_('parse error in .hglfs: %s') % e)
232
233 def _match(path, size):
234 for pat, rule in rules:
235 if pat(path, size):
236 return rule(path, size)
237
238 return False
239
240 return _match
165
241
166 def wrapfilelog(filelog):
242 def wrapfilelog(filelog):
167 wrapfunction = extensions.wrapfunction
243 wrapfunction = extensions.wrapfunction
@@ -937,6 +937,79 b' Committing deleted files works:'
937 $ hg commit -m 'add A' -A A
937 $ hg commit -m 'add A' -A A
938 $ hg rm A
938 $ hg rm A
939 $ hg commit -m 'rm A'
939 $ hg commit -m 'rm A'
940
941 Bad .hglfs files will block the commit with a useful message
942
943 $ cat > .hglfs << EOF
944 > [track]
945 > **.test = size(">5B")
946 > bad file ... no commit
947 > EOF
948
949 $ echo x > file.txt
950 $ hg ci -Aqm 'should fail'
951 hg: parse error at .hglfs:3: bad file ... no commit
952 [255]
953
954 $ cat > .hglfs << EOF
955 > [track]
956 > **.test = size(">5B")
957 > ** = nonexistent()
958 > EOF
959
960 $ hg ci -Aqm 'should fail'
961 abort: parse error in .hglfs: unknown identifier: nonexistent
962 [255]
963
964 '**' works out to mean all files.
965
966 $ cat > .hglfs << EOF
967 > [track]
968 > **.test = size(">5B")
969 > **.exclude = none()
970 > ** = size(">10B")
971 > EOF
972
973 The LFS policy takes effect as the .hglfs file is committed
974
975 $ echo 'largefile' > lfs.test
976 $ echo '012345678901234567890' > nolfs.exclude
977 $ echo '01234567890123456' > lfs.catchall
978 $ hg ci -Aqm 'added .hglfs'
979 $ hg log -r . -T '{rev}: {lfs_files % "{file}: {oid}\n"}\n'
980 2: lfs.catchall: d4ec46c2869ba22eceb42a729377432052d9dd75d82fc40390ebaadecee87ee9
981 lfs.test: 5489e6ced8c36a7b267292bde9fd5242a5f80a7482e8f23fa0477393dfaa4d6c
982
983 The existing .hglfs file is used even when it is not in the 'A' or 'M' states
984
985 $ echo 'largefile2' > lfs.test
986 $ echo '012345678901234567890a' > nolfs.exclude
987 $ echo '01234567890123456a' > lfs.catchall
988 $ hg ci -qm 'unmodified .hglfs'
989 $ hg log -r . -T '{rev}: {lfs_files % "{file}: {oid}\n"}\n'
990 3: lfs.catchall: 31f43b9c62b540126b0ad5884dc013d21a61c9329b77de1fceeae2fc58511573
991 lfs.test: 8acd23467967bc7b8cc5a280056589b0ba0b17ff21dbd88a7b6474d6290378a6
992
993 Excluding the .hglfs file from the commit postpones the policy change
994
995 $ hg rm .hglfs
996 $ echo 'largefile3' > lfs.test
997 $ echo '012345678901234567890abc' > nolfs.exclude
998 $ echo '01234567890123456abc' > lfs.catchall
999 $ hg ci -qm 'file test' -X .hglfs
1000 $ hg log -r . -T '{rev}: {lfs_files % "{file}: {oid}\n"}\n'
1001 4: lfs.catchall: 6747cfb1b83965b4a884e7a6061813ae31e4122028bc6a88d2ac5e5f9e05c5af
1002 lfs.test: 3f40b70c2294e91e0fa789ebcf73c5a1d1c7aef270f83e477e40cb0513237e8c
1003
1004 The policy change takes effect when the .hglfs is committed
1005
1006 $ echo 'largefile4' > lfs.test
1007 $ echo '012345678901234567890abcdef' > nolfs.exclude
1008 $ echo '01234567890123456abcdef' > lfs.catchall
1009 $ hg ci -qm 'file test'
1010 $ hg log -r . -T '{rev}: {lfs_files % "{file}: {oid}\n"}\n'
1011 5:
1012
940 $ cd ..
1013 $ cd ..
941
1014
942 Unbundling adds a requirement to a non-lfs repo, if necessary.
1015 Unbundling adds a requirement to a non-lfs repo, if necessary.
General Comments 0
You need to be logged in to leave comments. Login now