##// END OF EJS Templates
largefiles: simplify iteration over standins...
Martin von Zweigbergk -
r22525:764127f5 default
parent child Browse files
Show More
@@ -1,486 +1,484 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''setup for largefiles repositories: reposetup'''
9 '''setup for largefiles repositories: reposetup'''
10 import copy
10 import copy
11 import os
11 import os
12
12
13 from mercurial import error, manifest, match as match_, util
13 from mercurial import error, manifest, match as match_, util
14 from mercurial.i18n import _
14 from mercurial.i18n import _
15 from mercurial import localrepo
15 from mercurial import localrepo
16
16
17 import lfcommands
17 import lfcommands
18 import lfutil
18 import lfutil
19
19
20 def reposetup(ui, repo):
20 def reposetup(ui, repo):
21 # wire repositories should be given new wireproto functions
21 # wire repositories should be given new wireproto functions
22 # by "proto.wirereposetup()" via "hg.wirepeersetupfuncs"
22 # by "proto.wirereposetup()" via "hg.wirepeersetupfuncs"
23 if not repo.local():
23 if not repo.local():
24 return
24 return
25
25
26 class lfilesrepo(repo.__class__):
26 class lfilesrepo(repo.__class__):
27 lfstatus = False
27 lfstatus = False
28 def status_nolfiles(self, *args, **kwargs):
28 def status_nolfiles(self, *args, **kwargs):
29 return super(lfilesrepo, self).status(*args, **kwargs)
29 return super(lfilesrepo, self).status(*args, **kwargs)
30
30
31 # When lfstatus is set, return a context that gives the names
31 # When lfstatus is set, return a context that gives the names
32 # of largefiles instead of their corresponding standins and
32 # of largefiles instead of their corresponding standins and
33 # identifies the largefiles as always binary, regardless of
33 # identifies the largefiles as always binary, regardless of
34 # their actual contents.
34 # their actual contents.
35 def __getitem__(self, changeid):
35 def __getitem__(self, changeid):
36 ctx = super(lfilesrepo, self).__getitem__(changeid)
36 ctx = super(lfilesrepo, self).__getitem__(changeid)
37 if self.lfstatus:
37 if self.lfstatus:
38 class lfilesmanifestdict(manifest.manifestdict):
38 class lfilesmanifestdict(manifest.manifestdict):
39 def __contains__(self, filename):
39 def __contains__(self, filename):
40 orig = super(lfilesmanifestdict, self).__contains__
40 orig = super(lfilesmanifestdict, self).__contains__
41 return orig(filename) or orig(lfutil.standin(filename))
41 return orig(filename) or orig(lfutil.standin(filename))
42 class lfilesctx(ctx.__class__):
42 class lfilesctx(ctx.__class__):
43 def files(self):
43 def files(self):
44 filenames = super(lfilesctx, self).files()
44 filenames = super(lfilesctx, self).files()
45 return [lfutil.splitstandin(f) or f for f in filenames]
45 return [lfutil.splitstandin(f) or f for f in filenames]
46 def manifest(self):
46 def manifest(self):
47 man1 = super(lfilesctx, self).manifest()
47 man1 = super(lfilesctx, self).manifest()
48 man1.__class__ = lfilesmanifestdict
48 man1.__class__ = lfilesmanifestdict
49 return man1
49 return man1
50 def filectx(self, path, fileid=None, filelog=None):
50 def filectx(self, path, fileid=None, filelog=None):
51 orig = super(lfilesctx, self).filectx
51 orig = super(lfilesctx, self).filectx
52 try:
52 try:
53 if filelog is not None:
53 if filelog is not None:
54 result = orig(path, fileid, filelog)
54 result = orig(path, fileid, filelog)
55 else:
55 else:
56 result = orig(path, fileid)
56 result = orig(path, fileid)
57 except error.LookupError:
57 except error.LookupError:
58 # Adding a null character will cause Mercurial to
58 # Adding a null character will cause Mercurial to
59 # identify this as a binary file.
59 # identify this as a binary file.
60 if filelog is not None:
60 if filelog is not None:
61 result = orig(lfutil.standin(path), fileid,
61 result = orig(lfutil.standin(path), fileid,
62 filelog)
62 filelog)
63 else:
63 else:
64 result = orig(lfutil.standin(path), fileid)
64 result = orig(lfutil.standin(path), fileid)
65 olddata = result.data
65 olddata = result.data
66 result.data = lambda: olddata() + '\0'
66 result.data = lambda: olddata() + '\0'
67 return result
67 return result
68 ctx.__class__ = lfilesctx
68 ctx.__class__ = lfilesctx
69 return ctx
69 return ctx
70
70
71 # Figure out the status of big files and insert them into the
71 # Figure out the status of big files and insert them into the
72 # appropriate list in the result. Also removes standin files
72 # appropriate list in the result. Also removes standin files
73 # from the listing. Revert to the original status if
73 # from the listing. Revert to the original status if
74 # self.lfstatus is False.
74 # self.lfstatus is False.
75 # XXX large file status is buggy when used on repo proxy.
75 # XXX large file status is buggy when used on repo proxy.
76 # XXX this needs to be investigated.
76 # XXX this needs to be investigated.
77 @localrepo.unfilteredmethod
77 @localrepo.unfilteredmethod
78 def status(self, node1='.', node2=None, match=None, ignored=False,
78 def status(self, node1='.', node2=None, match=None, ignored=False,
79 clean=False, unknown=False, listsubrepos=False):
79 clean=False, unknown=False, listsubrepos=False):
80 listignored, listclean, listunknown = ignored, clean, unknown
80 listignored, listclean, listunknown = ignored, clean, unknown
81 orig = super(lfilesrepo, self).status
81 orig = super(lfilesrepo, self).status
82 if not self.lfstatus:
82 if not self.lfstatus:
83 return orig(node1, node2, match, listignored, listclean,
83 return orig(node1, node2, match, listignored, listclean,
84 listunknown, listsubrepos)
84 listunknown, listsubrepos)
85
85
86 # some calls in this function rely on the old version of status
86 # some calls in this function rely on the old version of status
87 self.lfstatus = False
87 self.lfstatus = False
88 ctx1 = self[node1]
88 ctx1 = self[node1]
89 ctx2 = self[node2]
89 ctx2 = self[node2]
90 working = ctx2.rev() is None
90 working = ctx2.rev() is None
91 parentworking = working and ctx1 == self['.']
91 parentworking = working and ctx1 == self['.']
92
92
93 def inctx(file, ctx):
93 def inctx(file, ctx):
94 try:
94 try:
95 if ctx.rev() is None:
95 if ctx.rev() is None:
96 return file in ctx.manifest()
96 return file in ctx.manifest()
97 ctx[file]
97 ctx[file]
98 return True
98 return True
99 except KeyError:
99 except KeyError:
100 return False
100 return False
101
101
102 if match is None:
102 if match is None:
103 match = match_.always(self.root, self.getcwd())
103 match = match_.always(self.root, self.getcwd())
104
104
105 wlock = None
105 wlock = None
106 try:
106 try:
107 try:
107 try:
108 # updating the dirstate is optional
108 # updating the dirstate is optional
109 # so we don't wait on the lock
109 # so we don't wait on the lock
110 wlock = self.wlock(False)
110 wlock = self.wlock(False)
111 except error.LockError:
111 except error.LockError:
112 pass
112 pass
113
113
114 # First check if there were files specified on the
114 # First check if there were files specified on the
115 # command line. If there were, and none of them were
115 # command line. If there were, and none of them were
116 # largefiles, we should just bail here and let super
116 # largefiles, we should just bail here and let super
117 # handle it -- thus gaining a big performance boost.
117 # handle it -- thus gaining a big performance boost.
118 lfdirstate = lfutil.openlfdirstate(ui, self)
118 lfdirstate = lfutil.openlfdirstate(ui, self)
119 if match.files() and not match.anypats():
119 if match.files() and not match.anypats():
120 for f in lfdirstate:
120 for f in lfdirstate:
121 if match(f):
121 if match(f):
122 break
122 break
123 else:
123 else:
124 return orig(node1, node2, match, listignored, listclean,
124 return orig(node1, node2, match, listignored, listclean,
125 listunknown, listsubrepos)
125 listunknown, listsubrepos)
126
126
127 # Create a copy of match that matches standins instead
127 # Create a copy of match that matches standins instead
128 # of largefiles.
128 # of largefiles.
129 def tostandins(files):
129 def tostandins(files):
130 if not working:
130 if not working:
131 return files
131 return files
132 newfiles = []
132 newfiles = []
133 dirstate = self.dirstate
133 dirstate = self.dirstate
134 for f in files:
134 for f in files:
135 sf = lfutil.standin(f)
135 sf = lfutil.standin(f)
136 if sf in dirstate:
136 if sf in dirstate:
137 newfiles.append(sf)
137 newfiles.append(sf)
138 elif sf in dirstate.dirs():
138 elif sf in dirstate.dirs():
139 # Directory entries could be regular or
139 # Directory entries could be regular or
140 # standin, check both
140 # standin, check both
141 newfiles.extend((f, sf))
141 newfiles.extend((f, sf))
142 else:
142 else:
143 newfiles.append(f)
143 newfiles.append(f)
144 return newfiles
144 return newfiles
145
145
146 m = copy.copy(match)
146 m = copy.copy(match)
147 m._files = tostandins(m._files)
147 m._files = tostandins(m._files)
148
148
149 result = orig(node1, node2, m, ignored, clean, unknown,
149 result = orig(node1, node2, m, ignored, clean, unknown,
150 listsubrepos)
150 listsubrepos)
151 if working:
151 if working:
152
152
153 def sfindirstate(f):
153 def sfindirstate(f):
154 sf = lfutil.standin(f)
154 sf = lfutil.standin(f)
155 dirstate = self.dirstate
155 dirstate = self.dirstate
156 return sf in dirstate or sf in dirstate.dirs()
156 return sf in dirstate or sf in dirstate.dirs()
157
157
158 match._files = [f for f in match._files
158 match._files = [f for f in match._files
159 if sfindirstate(f)]
159 if sfindirstate(f)]
160 # Don't waste time getting the ignored and unknown
160 # Don't waste time getting the ignored and unknown
161 # files from lfdirstate
161 # files from lfdirstate
162 s = lfdirstate.status(match, [], False,
162 s = lfdirstate.status(match, [], False,
163 listclean, False)
163 listclean, False)
164 (unsure, modified, added, removed, missing, _unknown,
164 (unsure, modified, added, removed, missing, _unknown,
165 _ignored, clean) = s
165 _ignored, clean) = s
166 if parentworking:
166 if parentworking:
167 for lfile in unsure:
167 for lfile in unsure:
168 standin = lfutil.standin(lfile)
168 standin = lfutil.standin(lfile)
169 if standin not in ctx1:
169 if standin not in ctx1:
170 # from second parent
170 # from second parent
171 modified.append(lfile)
171 modified.append(lfile)
172 elif ctx1[standin].data().strip() \
172 elif ctx1[standin].data().strip() \
173 != lfutil.hashfile(self.wjoin(lfile)):
173 != lfutil.hashfile(self.wjoin(lfile)):
174 modified.append(lfile)
174 modified.append(lfile)
175 else:
175 else:
176 if listclean:
176 if listclean:
177 clean.append(lfile)
177 clean.append(lfile)
178 lfdirstate.normal(lfile)
178 lfdirstate.normal(lfile)
179 else:
179 else:
180 tocheck = unsure + modified + added + clean
180 tocheck = unsure + modified + added + clean
181 modified, added, clean = [], [], []
181 modified, added, clean = [], [], []
182
182
183 for lfile in tocheck:
183 for lfile in tocheck:
184 standin = lfutil.standin(lfile)
184 standin = lfutil.standin(lfile)
185 if inctx(standin, ctx1):
185 if inctx(standin, ctx1):
186 if ctx1[standin].data().strip() != \
186 if ctx1[standin].data().strip() != \
187 lfutil.hashfile(self.wjoin(lfile)):
187 lfutil.hashfile(self.wjoin(lfile)):
188 modified.append(lfile)
188 modified.append(lfile)
189 elif listclean:
189 elif listclean:
190 clean.append(lfile)
190 clean.append(lfile)
191 else:
191 else:
192 added.append(lfile)
192 added.append(lfile)
193
193
194 # Standins no longer found in lfdirstate has been
194 # Standins no longer found in lfdirstate has been
195 # removed
195 # removed
196 for standin in ctx1.manifest():
196 for standin in ctx1.walk(lfutil.getstandinmatcher(self)):
197 if not lfutil.isstandin(standin):
198 continue
199 lfile = lfutil.splitstandin(standin)
197 lfile = lfutil.splitstandin(standin)
200 if not match(lfile):
198 if not match(lfile):
201 continue
199 continue
202 if lfile not in lfdirstate:
200 if lfile not in lfdirstate:
203 removed.append(lfile)
201 removed.append(lfile)
204
202
205 # Filter result lists
203 # Filter result lists
206 result = list(result)
204 result = list(result)
207
205
208 # Largefiles are not really removed when they're
206 # Largefiles are not really removed when they're
209 # still in the normal dirstate. Likewise, normal
207 # still in the normal dirstate. Likewise, normal
210 # files are not really removed if they are still in
208 # files are not really removed if they are still in
211 # lfdirstate. This happens in merges where files
209 # lfdirstate. This happens in merges where files
212 # change type.
210 # change type.
213 removed = [f for f in removed
211 removed = [f for f in removed
214 if f not in self.dirstate]
212 if f not in self.dirstate]
215 result[2] = [f for f in result[2]
213 result[2] = [f for f in result[2]
216 if f not in lfdirstate]
214 if f not in lfdirstate]
217
215
218 lfiles = set(lfdirstate._map)
216 lfiles = set(lfdirstate._map)
219 # Unknown files
217 # Unknown files
220 result[4] = set(result[4]).difference(lfiles)
218 result[4] = set(result[4]).difference(lfiles)
221 # Ignored files
219 # Ignored files
222 result[5] = set(result[5]).difference(lfiles)
220 result[5] = set(result[5]).difference(lfiles)
223 # combine normal files and largefiles
221 # combine normal files and largefiles
224 normals = [[fn for fn in filelist
222 normals = [[fn for fn in filelist
225 if not lfutil.isstandin(fn)]
223 if not lfutil.isstandin(fn)]
226 for filelist in result]
224 for filelist in result]
227 lfstatus = (modified, added, removed, missing, [], [],
225 lfstatus = (modified, added, removed, missing, [], [],
228 clean)
226 clean)
229 result = [sorted(list1 + list2)
227 result = [sorted(list1 + list2)
230 for (list1, list2) in zip(normals, lfstatus)]
228 for (list1, list2) in zip(normals, lfstatus)]
231 else:
229 else:
232 def toname(f):
230 def toname(f):
233 if lfutil.isstandin(f):
231 if lfutil.isstandin(f):
234 return lfutil.splitstandin(f)
232 return lfutil.splitstandin(f)
235 return f
233 return f
236 result = [[toname(f) for f in items]
234 result = [[toname(f) for f in items]
237 for items in result]
235 for items in result]
238
236
239 if wlock:
237 if wlock:
240 lfdirstate.write()
238 lfdirstate.write()
241
239
242 finally:
240 finally:
243 if wlock:
241 if wlock:
244 wlock.release()
242 wlock.release()
245
243
246 self.lfstatus = True
244 self.lfstatus = True
247 return result
245 return result
248
246
249 # As part of committing, copy all of the largefiles into the
247 # As part of committing, copy all of the largefiles into the
250 # cache.
248 # cache.
251 def commitctx(self, *args, **kwargs):
249 def commitctx(self, *args, **kwargs):
252 node = super(lfilesrepo, self).commitctx(*args, **kwargs)
250 node = super(lfilesrepo, self).commitctx(*args, **kwargs)
253 lfutil.copyalltostore(self, node)
251 lfutil.copyalltostore(self, node)
254 return node
252 return node
255
253
256 # Before commit, largefile standins have not had their
254 # Before commit, largefile standins have not had their
257 # contents updated to reflect the hash of their largefile.
255 # contents updated to reflect the hash of their largefile.
258 # Do that here.
256 # Do that here.
259 def commit(self, text="", user=None, date=None, match=None,
257 def commit(self, text="", user=None, date=None, match=None,
260 force=False, editor=False, extra={}):
258 force=False, editor=False, extra={}):
261 orig = super(lfilesrepo, self).commit
259 orig = super(lfilesrepo, self).commit
262
260
263 wlock = self.wlock()
261 wlock = self.wlock()
264 try:
262 try:
265 # Case 0: Automated committing
263 # Case 0: Automated committing
266 #
264 #
267 # While automated committing (like rebase, transplant
265 # While automated committing (like rebase, transplant
268 # and so on), this code path is used to avoid:
266 # and so on), this code path is used to avoid:
269 # (1) updating standins, because standins should
267 # (1) updating standins, because standins should
270 # be already updated at this point
268 # be already updated at this point
271 # (2) aborting when stadnins are matched by "match",
269 # (2) aborting when stadnins are matched by "match",
272 # because automated committing may specify them directly
270 # because automated committing may specify them directly
273 #
271 #
274 if getattr(self, "_isrebasing", False) or \
272 if getattr(self, "_isrebasing", False) or \
275 getattr(self, "_istransplanting", False):
273 getattr(self, "_istransplanting", False):
276 result = orig(text=text, user=user, date=date, match=match,
274 result = orig(text=text, user=user, date=date, match=match,
277 force=force, editor=editor, extra=extra)
275 force=force, editor=editor, extra=extra)
278
276
279 if result:
277 if result:
280 lfdirstate = lfutil.openlfdirstate(ui, self)
278 lfdirstate = lfutil.openlfdirstate(ui, self)
281 for f in self[result].files():
279 for f in self[result].files():
282 if lfutil.isstandin(f):
280 if lfutil.isstandin(f):
283 lfile = lfutil.splitstandin(f)
281 lfile = lfutil.splitstandin(f)
284 lfutil.synclfdirstate(self, lfdirstate, lfile,
282 lfutil.synclfdirstate(self, lfdirstate, lfile,
285 False)
283 False)
286 lfdirstate.write()
284 lfdirstate.write()
287
285
288 return result
286 return result
289 # Case 1: user calls commit with no specific files or
287 # Case 1: user calls commit with no specific files or
290 # include/exclude patterns: refresh and commit all files that
288 # include/exclude patterns: refresh and commit all files that
291 # are "dirty".
289 # are "dirty".
292 if ((match is None) or
290 if ((match is None) or
293 (not match.anypats() and not match.files())):
291 (not match.anypats() and not match.files())):
294 # Spend a bit of time here to get a list of files we know
292 # Spend a bit of time here to get a list of files we know
295 # are modified so we can compare only against those.
293 # are modified so we can compare only against those.
296 # It can cost a lot of time (several seconds)
294 # It can cost a lot of time (several seconds)
297 # otherwise to update all standins if the largefiles are
295 # otherwise to update all standins if the largefiles are
298 # large.
296 # large.
299 lfdirstate = lfutil.openlfdirstate(ui, self)
297 lfdirstate = lfutil.openlfdirstate(ui, self)
300 dirtymatch = match_.always(self.root, self.getcwd())
298 dirtymatch = match_.always(self.root, self.getcwd())
301 s = lfdirstate.status(dirtymatch, [], False, False, False)
299 s = lfdirstate.status(dirtymatch, [], False, False, False)
302 (unsure, modified, added, removed, _missing, _unknown,
300 (unsure, modified, added, removed, _missing, _unknown,
303 _ignored, _clean) = s
301 _ignored, _clean) = s
304 modifiedfiles = unsure + modified + added + removed
302 modifiedfiles = unsure + modified + added + removed
305 lfiles = lfutil.listlfiles(self)
303 lfiles = lfutil.listlfiles(self)
306 # this only loops through largefiles that exist (not
304 # this only loops through largefiles that exist (not
307 # removed/renamed)
305 # removed/renamed)
308 for lfile in lfiles:
306 for lfile in lfiles:
309 if lfile in modifiedfiles:
307 if lfile in modifiedfiles:
310 if os.path.exists(
308 if os.path.exists(
311 self.wjoin(lfutil.standin(lfile))):
309 self.wjoin(lfutil.standin(lfile))):
312 # this handles the case where a rebase is being
310 # this handles the case where a rebase is being
313 # performed and the working copy is not updated
311 # performed and the working copy is not updated
314 # yet.
312 # yet.
315 if os.path.exists(self.wjoin(lfile)):
313 if os.path.exists(self.wjoin(lfile)):
316 lfutil.updatestandin(self,
314 lfutil.updatestandin(self,
317 lfutil.standin(lfile))
315 lfutil.standin(lfile))
318 lfdirstate.normal(lfile)
316 lfdirstate.normal(lfile)
319
317
320 result = orig(text=text, user=user, date=date, match=match,
318 result = orig(text=text, user=user, date=date, match=match,
321 force=force, editor=editor, extra=extra)
319 force=force, editor=editor, extra=extra)
322
320
323 if result is not None:
321 if result is not None:
324 for lfile in lfdirstate:
322 for lfile in lfdirstate:
325 if lfile in modifiedfiles:
323 if lfile in modifiedfiles:
326 if (not os.path.exists(self.wjoin(
324 if (not os.path.exists(self.wjoin(
327 lfutil.standin(lfile)))) or \
325 lfutil.standin(lfile)))) or \
328 (not os.path.exists(self.wjoin(lfile))):
326 (not os.path.exists(self.wjoin(lfile))):
329 lfdirstate.drop(lfile)
327 lfdirstate.drop(lfile)
330
328
331 # This needs to be after commit; otherwise precommit hooks
329 # This needs to be after commit; otherwise precommit hooks
332 # get the wrong status
330 # get the wrong status
333 lfdirstate.write()
331 lfdirstate.write()
334 return result
332 return result
335
333
336 lfiles = lfutil.listlfiles(self)
334 lfiles = lfutil.listlfiles(self)
337 match._files = self._subdirlfs(match.files(), lfiles)
335 match._files = self._subdirlfs(match.files(), lfiles)
338
336
339 # Case 2: user calls commit with specified patterns: refresh
337 # Case 2: user calls commit with specified patterns: refresh
340 # any matching big files.
338 # any matching big files.
341 smatcher = lfutil.composestandinmatcher(self, match)
339 smatcher = lfutil.composestandinmatcher(self, match)
342 standins = self.dirstate.walk(smatcher, [], False, False)
340 standins = self.dirstate.walk(smatcher, [], False, False)
343
341
344 # No matching big files: get out of the way and pass control to
342 # No matching big files: get out of the way and pass control to
345 # the usual commit() method.
343 # the usual commit() method.
346 if not standins:
344 if not standins:
347 return orig(text=text, user=user, date=date, match=match,
345 return orig(text=text, user=user, date=date, match=match,
348 force=force, editor=editor, extra=extra)
346 force=force, editor=editor, extra=extra)
349
347
350 # Refresh all matching big files. It's possible that the
348 # Refresh all matching big files. It's possible that the
351 # commit will end up failing, in which case the big files will
349 # commit will end up failing, in which case the big files will
352 # stay refreshed. No harm done: the user modified them and
350 # stay refreshed. No harm done: the user modified them and
353 # asked to commit them, so sooner or later we're going to
351 # asked to commit them, so sooner or later we're going to
354 # refresh the standins. Might as well leave them refreshed.
352 # refresh the standins. Might as well leave them refreshed.
355 lfdirstate = lfutil.openlfdirstate(ui, self)
353 lfdirstate = lfutil.openlfdirstate(ui, self)
356 for standin in standins:
354 for standin in standins:
357 lfile = lfutil.splitstandin(standin)
355 lfile = lfutil.splitstandin(standin)
358 if lfdirstate[lfile] != 'r':
356 if lfdirstate[lfile] != 'r':
359 lfutil.updatestandin(self, standin)
357 lfutil.updatestandin(self, standin)
360 lfdirstate.normal(lfile)
358 lfdirstate.normal(lfile)
361 else:
359 else:
362 lfdirstate.drop(lfile)
360 lfdirstate.drop(lfile)
363
361
364 # Cook up a new matcher that only matches regular files or
362 # Cook up a new matcher that only matches regular files or
365 # standins corresponding to the big files requested by the
363 # standins corresponding to the big files requested by the
366 # user. Have to modify _files to prevent commit() from
364 # user. Have to modify _files to prevent commit() from
367 # complaining "not tracked" for big files.
365 # complaining "not tracked" for big files.
368 match = copy.copy(match)
366 match = copy.copy(match)
369 origmatchfn = match.matchfn
367 origmatchfn = match.matchfn
370
368
371 # Check both the list of largefiles and the list of
369 # Check both the list of largefiles and the list of
372 # standins because if a largefile was removed, it
370 # standins because if a largefile was removed, it
373 # won't be in the list of largefiles at this point
371 # won't be in the list of largefiles at this point
374 match._files += sorted(standins)
372 match._files += sorted(standins)
375
373
376 actualfiles = []
374 actualfiles = []
377 for f in match._files:
375 for f in match._files:
378 fstandin = lfutil.standin(f)
376 fstandin = lfutil.standin(f)
379
377
380 # ignore known largefiles and standins
378 # ignore known largefiles and standins
381 if f in lfiles or fstandin in standins:
379 if f in lfiles or fstandin in standins:
382 continue
380 continue
383
381
384 actualfiles.append(f)
382 actualfiles.append(f)
385 match._files = actualfiles
383 match._files = actualfiles
386
384
387 def matchfn(f):
385 def matchfn(f):
388 if origmatchfn(f):
386 if origmatchfn(f):
389 return f not in lfiles
387 return f not in lfiles
390 else:
388 else:
391 return f in standins
389 return f in standins
392
390
393 match.matchfn = matchfn
391 match.matchfn = matchfn
394 result = orig(text=text, user=user, date=date, match=match,
392 result = orig(text=text, user=user, date=date, match=match,
395 force=force, editor=editor, extra=extra)
393 force=force, editor=editor, extra=extra)
396 # This needs to be after commit; otherwise precommit hooks
394 # This needs to be after commit; otherwise precommit hooks
397 # get the wrong status
395 # get the wrong status
398 lfdirstate.write()
396 lfdirstate.write()
399 return result
397 return result
400 finally:
398 finally:
401 wlock.release()
399 wlock.release()
402
400
403 def push(self, remote, force=False, revs=None, newbranch=False):
401 def push(self, remote, force=False, revs=None, newbranch=False):
404 if remote.local():
402 if remote.local():
405 missing = set(self.requirements) - remote.local().supported
403 missing = set(self.requirements) - remote.local().supported
406 if missing:
404 if missing:
407 msg = _("required features are not"
405 msg = _("required features are not"
408 " supported in the destination:"
406 " supported in the destination:"
409 " %s") % (', '.join(sorted(missing)))
407 " %s") % (', '.join(sorted(missing)))
410 raise util.Abort(msg)
408 raise util.Abort(msg)
411 return super(lfilesrepo, self).push(remote, force=force, revs=revs,
409 return super(lfilesrepo, self).push(remote, force=force, revs=revs,
412 newbranch=newbranch)
410 newbranch=newbranch)
413
411
414 def _subdirlfs(self, files, lfiles):
412 def _subdirlfs(self, files, lfiles):
415 '''
413 '''
416 Adjust matched file list
414 Adjust matched file list
417 If we pass a directory to commit whose only commitable files
415 If we pass a directory to commit whose only commitable files
418 are largefiles, the core commit code aborts before finding
416 are largefiles, the core commit code aborts before finding
419 the largefiles.
417 the largefiles.
420 So we do the following:
418 So we do the following:
421 For directories that only have largefiles as matches,
419 For directories that only have largefiles as matches,
422 we explicitly add the largefiles to the match list and remove
420 we explicitly add the largefiles to the match list and remove
423 the directory.
421 the directory.
424 In other cases, we leave the match list unmodified.
422 In other cases, we leave the match list unmodified.
425 '''
423 '''
426 actualfiles = []
424 actualfiles = []
427 dirs = []
425 dirs = []
428 regulars = []
426 regulars = []
429
427
430 for f in files:
428 for f in files:
431 if lfutil.isstandin(f + '/'):
429 if lfutil.isstandin(f + '/'):
432 raise util.Abort(
430 raise util.Abort(
433 _('file "%s" is a largefile standin') % f,
431 _('file "%s" is a largefile standin') % f,
434 hint=('commit the largefile itself instead'))
432 hint=('commit the largefile itself instead'))
435 # Scan directories
433 # Scan directories
436 if os.path.isdir(self.wjoin(f)):
434 if os.path.isdir(self.wjoin(f)):
437 dirs.append(f)
435 dirs.append(f)
438 else:
436 else:
439 regulars.append(f)
437 regulars.append(f)
440
438
441 for f in dirs:
439 for f in dirs:
442 matcheddir = False
440 matcheddir = False
443 d = self.dirstate.normalize(f) + '/'
441 d = self.dirstate.normalize(f) + '/'
444 # Check for matched normal files
442 # Check for matched normal files
445 for mf in regulars:
443 for mf in regulars:
446 if self.dirstate.normalize(mf).startswith(d):
444 if self.dirstate.normalize(mf).startswith(d):
447 actualfiles.append(f)
445 actualfiles.append(f)
448 matcheddir = True
446 matcheddir = True
449 break
447 break
450 if not matcheddir:
448 if not matcheddir:
451 # If no normal match, manually append
449 # If no normal match, manually append
452 # any matching largefiles
450 # any matching largefiles
453 for lf in lfiles:
451 for lf in lfiles:
454 if self.dirstate.normalize(lf).startswith(d):
452 if self.dirstate.normalize(lf).startswith(d):
455 actualfiles.append(lf)
453 actualfiles.append(lf)
456 if not matcheddir:
454 if not matcheddir:
457 actualfiles.append(lfutil.standin(f))
455 actualfiles.append(lfutil.standin(f))
458 matcheddir = True
456 matcheddir = True
459 # Nothing in dir, so readd it
457 # Nothing in dir, so readd it
460 # and let commit reject it
458 # and let commit reject it
461 if not matcheddir:
459 if not matcheddir:
462 actualfiles.append(f)
460 actualfiles.append(f)
463
461
464 # Always add normal files
462 # Always add normal files
465 actualfiles += regulars
463 actualfiles += regulars
466 return actualfiles
464 return actualfiles
467
465
468 repo.__class__ = lfilesrepo
466 repo.__class__ = lfilesrepo
469
467
470 def prepushoutgoinghook(local, remote, outgoing):
468 def prepushoutgoinghook(local, remote, outgoing):
471 if outgoing.missing:
469 if outgoing.missing:
472 toupload = set()
470 toupload = set()
473 addfunc = lambda fn, lfhash: toupload.add(lfhash)
471 addfunc = lambda fn, lfhash: toupload.add(lfhash)
474 lfutil.getlfilestoupload(local, outgoing.missing, addfunc)
472 lfutil.getlfilestoupload(local, outgoing.missing, addfunc)
475 lfcommands.uploadlfiles(ui, local, remote, toupload)
473 lfcommands.uploadlfiles(ui, local, remote, toupload)
476 repo.prepushoutgoinghooks.add("largefiles", prepushoutgoinghook)
474 repo.prepushoutgoinghooks.add("largefiles", prepushoutgoinghook)
477
475
478 def checkrequireslfiles(ui, repo, **kwargs):
476 def checkrequireslfiles(ui, repo, **kwargs):
479 if 'largefiles' not in repo.requirements and util.any(
477 if 'largefiles' not in repo.requirements and util.any(
480 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
478 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
481 repo.requirements.add('largefiles')
479 repo.requirements.add('largefiles')
482 repo._writerequirements()
480 repo._writerequirements()
483
481
484 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles,
482 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles,
485 'largefiles')
483 'largefiles')
486 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles, 'largefiles')
484 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles, 'largefiles')
General Comments 0
You need to be logged in to leave comments. Login now