##// END OF EJS Templates
largefiles: optimize performance of status on largefiles repos (issue3136)
Na'Tosha Bard -
r15617:74e691b1 default
parent child Browse files
Show More
@@ -1,409 +1,433 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 types
11 import types
12 import os
12 import os
13 import re
13 import re
14
14
15 from mercurial import context, error, manifest, match as match_, node, util
15 from mercurial import context, error, manifest, match as match_, node, util
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17
17
18 import lfcommands
18 import lfcommands
19 import proto
19 import proto
20 import lfutil
20 import lfutil
21
21
22 def reposetup(ui, repo):
22 def reposetup(ui, repo):
23 # wire repositories should be given new wireproto functions but not the
23 # wire repositories should be given new wireproto functions but not the
24 # other largefiles modifications
24 # other largefiles modifications
25 if not repo.local():
25 if not repo.local():
26 return proto.wirereposetup(ui, repo)
26 return proto.wirereposetup(ui, repo)
27
27
28 for name in ('status', 'commitctx', 'commit', 'push'):
28 for name in ('status', 'commitctx', 'commit', 'push'):
29 method = getattr(repo, name)
29 method = getattr(repo, name)
30 #if not (isinstance(method, types.MethodType) and
30 #if not (isinstance(method, types.MethodType) and
31 # method.im_func is repo.__class__.commitctx.im_func):
31 # method.im_func is repo.__class__.commitctx.im_func):
32 if (isinstance(method, types.FunctionType) and
32 if (isinstance(method, types.FunctionType) and
33 method.func_name == 'wrap'):
33 method.func_name == 'wrap'):
34 ui.warn(_('largefiles: repo method %r appears to have already been'
34 ui.warn(_('largefiles: repo method %r appears to have already been'
35 ' wrapped by another extension: '
35 ' wrapped by another extension: '
36 'largefiles may behave incorrectly\n')
36 'largefiles may behave incorrectly\n')
37 % name)
37 % name)
38
38
39 class lfiles_repo(repo.__class__):
39 class lfiles_repo(repo.__class__):
40 lfstatus = False
40 lfstatus = False
41 def status_nolfiles(self, *args, **kwargs):
41 def status_nolfiles(self, *args, **kwargs):
42 return super(lfiles_repo, self).status(*args, **kwargs)
42 return super(lfiles_repo, self).status(*args, **kwargs)
43
43
44 # When lfstatus is set, return a context that gives the names
44 # When lfstatus is set, return a context that gives the names
45 # of largefiles instead of their corresponding standins and
45 # of largefiles instead of their corresponding standins and
46 # identifies the largefiles as always binary, regardless of
46 # identifies the largefiles as always binary, regardless of
47 # their actual contents.
47 # their actual contents.
48 def __getitem__(self, changeid):
48 def __getitem__(self, changeid):
49 ctx = super(lfiles_repo, self).__getitem__(changeid)
49 ctx = super(lfiles_repo, self).__getitem__(changeid)
50 if self.lfstatus:
50 if self.lfstatus:
51 class lfiles_manifestdict(manifest.manifestdict):
51 class lfiles_manifestdict(manifest.manifestdict):
52 def __contains__(self, filename):
52 def __contains__(self, filename):
53 if super(lfiles_manifestdict,
53 if super(lfiles_manifestdict,
54 self).__contains__(filename):
54 self).__contains__(filename):
55 return True
55 return True
56 return super(lfiles_manifestdict,
56 return super(lfiles_manifestdict,
57 self).__contains__(lfutil.shortname+'/' + filename)
57 self).__contains__(lfutil.shortname+'/' + filename)
58 class lfiles_ctx(ctx.__class__):
58 class lfiles_ctx(ctx.__class__):
59 def files(self):
59 def files(self):
60 filenames = super(lfiles_ctx, self).files()
60 filenames = super(lfiles_ctx, self).files()
61 return [re.sub('^\\'+lfutil.shortname+'/', '',
61 return [re.sub('^\\'+lfutil.shortname+'/', '',
62 filename) for filename in filenames]
62 filename) for filename in filenames]
63 def manifest(self):
63 def manifest(self):
64 man1 = super(lfiles_ctx, self).manifest()
64 man1 = super(lfiles_ctx, self).manifest()
65 man1.__class__ = lfiles_manifestdict
65 man1.__class__ = lfiles_manifestdict
66 return man1
66 return man1
67 def filectx(self, path, fileid=None, filelog=None):
67 def filectx(self, path, fileid=None, filelog=None):
68 try:
68 try:
69 result = super(lfiles_ctx, self).filectx(path,
69 result = super(lfiles_ctx, self).filectx(path,
70 fileid, filelog)
70 fileid, filelog)
71 except error.LookupError:
71 except error.LookupError:
72 # Adding a null character will cause Mercurial to
72 # Adding a null character will cause Mercurial to
73 # identify this as a binary file.
73 # identify this as a binary file.
74 result = super(lfiles_ctx, self).filectx(
74 result = super(lfiles_ctx, self).filectx(
75 lfutil.shortname + '/' + path, fileid,
75 lfutil.shortname + '/' + path, fileid,
76 filelog)
76 filelog)
77 olddata = result.data
77 olddata = result.data
78 result.data = lambda: olddata() + '\0'
78 result.data = lambda: olddata() + '\0'
79 return result
79 return result
80 ctx.__class__ = lfiles_ctx
80 ctx.__class__ = lfiles_ctx
81 return ctx
81 return ctx
82
82
83 # Figure out the status of big files and insert them into the
83 # Figure out the status of big files and insert them into the
84 # appropriate list in the result. Also removes standin files
84 # appropriate list in the result. Also removes standin files
85 # from the listing. Revert to the original status if
85 # from the listing. Revert to the original status if
86 # self.lfstatus is False.
86 # self.lfstatus is False.
87 def status(self, node1='.', node2=None, match=None, ignored=False,
87 def status(self, node1='.', node2=None, match=None, ignored=False,
88 clean=False, unknown=False, listsubrepos=False):
88 clean=False, unknown=False, listsubrepos=False):
89 listignored, listclean, listunknown = ignored, clean, unknown
89 listignored, listclean, listunknown = ignored, clean, unknown
90 if not self.lfstatus:
90 if not self.lfstatus:
91 try:
91 try:
92 return super(lfiles_repo, self).status(node1, node2, match,
92 return super(lfiles_repo, self).status(node1, node2, match,
93 listignored, listclean, listunknown, listsubrepos)
93 listignored, listclean, listunknown, listsubrepos)
94 except TypeError:
94 except TypeError:
95 return super(lfiles_repo, self).status(node1, node2, match,
95 return super(lfiles_repo, self).status(node1, node2, match,
96 listignored, listclean, listunknown)
96 listignored, listclean, listunknown)
97 else:
97 else:
98 # some calls in this function rely on the old version of status
98 # some calls in this function rely on the old version of status
99 self.lfstatus = False
99 self.lfstatus = False
100 if isinstance(node1, context.changectx):
100 if isinstance(node1, context.changectx):
101 ctx1 = node1
101 ctx1 = node1
102 else:
102 else:
103 ctx1 = repo[node1]
103 ctx1 = repo[node1]
104 if isinstance(node2, context.changectx):
104 if isinstance(node2, context.changectx):
105 ctx2 = node2
105 ctx2 = node2
106 else:
106 else:
107 ctx2 = repo[node2]
107 ctx2 = repo[node2]
108 working = ctx2.rev() is None
108 working = ctx2.rev() is None
109 parentworking = working and ctx1 == self['.']
109 parentworking = working and ctx1 == self['.']
110
110
111 def inctx(file, ctx):
111 def inctx(file, ctx):
112 try:
112 try:
113 if ctx.rev() is None:
113 if ctx.rev() is None:
114 return file in ctx.manifest()
114 return file in ctx.manifest()
115 ctx[file]
115 ctx[file]
116 return True
116 return True
117 except KeyError:
117 except KeyError:
118 return False
118 return False
119
119
120 if match is None:
120 if match is None:
121 match = match_.always(self.root, self.getcwd())
121 match = match_.always(self.root, self.getcwd())
122
122
123 # Create a copy of match that matches standins instead
123 # Create a copy of match that matches standins instead
124 # of largefiles.
124 # of largefiles.
125 def tostandin(file):
125 def tostandin(file):
126 if inctx(lfutil.standin(file), ctx2):
126 if inctx(lfutil.standin(file), ctx2):
127 return lfutil.standin(file)
127 return lfutil.standin(file)
128 return file
128 return file
129
129
130 # Create a function that we can use to override what is
131 # normally the ignore matcher. We've already checked
132 # for ignored files on the first dirstate walk, and
133 # unecessarily re-checking here causes a huge performance
134 # hit because lfdirstate only knows about largefiles
135 def _ignoreoverride(self):
136 return False
137
130 m = copy.copy(match)
138 m = copy.copy(match)
131 m._files = [tostandin(f) for f in m._files]
139 m._files = [tostandin(f) for f in m._files]
132
140
133 # get ignored, clean, and unknown but remove them
141 # Get ignored files here even if we weren't asked for them; we
134 # later if they were not asked for
142 # must use the result here for filtering later
135 try:
143 try:
136 result = super(lfiles_repo, self).status(node1, node2, m,
144 result = super(lfiles_repo, self).status(node1, node2, m,
137 True, True, True, listsubrepos)
145 True, clean, unknown, listsubrepos)
138 except TypeError:
146 except TypeError:
139 result = super(lfiles_repo, self).status(node1, node2, m,
147 result = super(lfiles_repo, self).status(node1, node2, m,
140 True, True, True)
148 True, clean, unknown)
141 if working:
149 if working:
142 # hold the wlock while we read largefiles and
150 # hold the wlock while we read largefiles and
143 # update the lfdirstate
151 # update the lfdirstate
144 wlock = repo.wlock()
152 wlock = repo.wlock()
145 try:
153 try:
146 # Any non-largefiles that were explicitly listed must be
154 # Any non-largefiles that were explicitly listed must be
147 # taken out or lfdirstate.status will report an error.
155 # taken out or lfdirstate.status will report an error.
148 # The status of these files was already computed using
156 # The status of these files was already computed using
149 # super's status.
157 # super's status.
150 lfdirstate = lfutil.openlfdirstate(ui, self)
158 lfdirstate = lfutil.openlfdirstate(ui, self)
159 # Override lfdirstate's ignore matcher to not do
160 # anything
161 orig_ignore = lfdirstate._ignore
162 lfdirstate._ignore = _ignoreoverride
163
151 match._files = [f for f in match._files if f in
164 match._files = [f for f in match._files if f in
152 lfdirstate]
165 lfdirstate]
153 s = lfdirstate.status(match, [], listignored,
166 # Don't waste time getting the ignored and unknown
154 listclean, listunknown)
167 # files again; we already have them
168 s = lfdirstate.status(match, [], False,
169 listclean, False)
155 (unsure, modified, added, removed, missing, unknown,
170 (unsure, modified, added, removed, missing, unknown,
156 ignored, clean) = s
171 ignored, clean) = s
172 # Replace the list of ignored and unknown files with
173 # the previously caclulated lists, and strip out the
174 # largefiles
175 lfiles = set(lfdirstate._map)
176 ignored = set(result[5]).difference(lfiles)
177 unknown = set(result[4]).difference(lfiles)
157 if parentworking:
178 if parentworking:
158 for lfile in unsure:
179 for lfile in unsure:
159 if ctx1[lfutil.standin(lfile)].data().strip() \
180 if ctx1[lfutil.standin(lfile)].data().strip() \
160 != lfutil.hashfile(self.wjoin(lfile)):
181 != lfutil.hashfile(self.wjoin(lfile)):
161 modified.append(lfile)
182 modified.append(lfile)
162 else:
183 else:
163 clean.append(lfile)
184 clean.append(lfile)
164 lfdirstate.normal(lfile)
185 lfdirstate.normal(lfile)
165 lfdirstate.write()
186 lfdirstate.write()
166 else:
187 else:
167 tocheck = unsure + modified + added + clean
188 tocheck = unsure + modified + added + clean
168 modified, added, clean = [], [], []
189 modified, added, clean = [], [], []
169
190
170 for lfile in tocheck:
191 for lfile in tocheck:
171 standin = lfutil.standin(lfile)
192 standin = lfutil.standin(lfile)
172 if inctx(standin, ctx1):
193 if inctx(standin, ctx1):
173 if ctx1[standin].data().strip() != \
194 if ctx1[standin].data().strip() != \
174 lfutil.hashfile(self.wjoin(lfile)):
195 lfutil.hashfile(self.wjoin(lfile)):
175 modified.append(lfile)
196 modified.append(lfile)
176 else:
197 else:
177 clean.append(lfile)
198 clean.append(lfile)
178 else:
199 else:
179 added.append(lfile)
200 added.append(lfile)
201 # Replace the original ignore function
202 lfdirstate._ignore = orig_ignore
180 finally:
203 finally:
181 wlock.release()
204 wlock.release()
182
205
183 for standin in ctx1.manifest():
206 for standin in ctx1.manifest():
184 if not lfutil.isstandin(standin):
207 if not lfutil.isstandin(standin):
185 continue
208 continue
186 lfile = lfutil.splitstandin(standin)
209 lfile = lfutil.splitstandin(standin)
187 if not match(lfile):
210 if not match(lfile):
188 continue
211 continue
189 if lfile not in lfdirstate:
212 if lfile not in lfdirstate:
190 removed.append(lfile)
213 removed.append(lfile)
191 # Handle unknown and ignored differently
214 # Handle unknown and ignored differently
192 lfiles = (modified, added, removed, missing, [], [], clean)
215 lfiles = (modified, added, removed, missing, [], [], clean)
193 result = list(result)
216 result = list(result)
194 # Unknown files
217 # Unknown files
218 unknown = set(unknown).difference(ignored)
195 result[4] = [f for f in unknown
219 result[4] = [f for f in unknown
196 if (repo.dirstate[f] == '?' and
220 if (repo.dirstate[f] == '?' and
197 not lfutil.isstandin(f))]
221 not lfutil.isstandin(f))]
198 # Ignored files must be ignored by both the dirstate and
222 # Ignored files were calculated earlier by the dirstate,
199 # lfdirstate
223 # and we already stripped out the largefiles from the list
200 result[5] = set(ignored).intersection(set(result[5]))
224 result[5] = ignored
201 # combine normal files and largefiles
225 # combine normal files and largefiles
202 normals = [[fn for fn in filelist
226 normals = [[fn for fn in filelist
203 if not lfutil.isstandin(fn)]
227 if not lfutil.isstandin(fn)]
204 for filelist in result]
228 for filelist in result]
205 result = [sorted(list1 + list2)
229 result = [sorted(list1 + list2)
206 for (list1, list2) in zip(normals, lfiles)]
230 for (list1, list2) in zip(normals, lfiles)]
207 else:
231 else:
208 def toname(f):
232 def toname(f):
209 if lfutil.isstandin(f):
233 if lfutil.isstandin(f):
210 return lfutil.splitstandin(f)
234 return lfutil.splitstandin(f)
211 return f
235 return f
212 result = [[toname(f) for f in items] for items in result]
236 result = [[toname(f) for f in items] for items in result]
213
237
214 if not listunknown:
238 if not listunknown:
215 result[4] = []
239 result[4] = []
216 if not listignored:
240 if not listignored:
217 result[5] = []
241 result[5] = []
218 if not listclean:
242 if not listclean:
219 result[6] = []
243 result[6] = []
220 self.lfstatus = True
244 self.lfstatus = True
221 return result
245 return result
222
246
223 # As part of committing, copy all of the largefiles into the
247 # As part of committing, copy all of the largefiles into the
224 # cache.
248 # cache.
225 def commitctx(self, *args, **kwargs):
249 def commitctx(self, *args, **kwargs):
226 node = super(lfiles_repo, self).commitctx(*args, **kwargs)
250 node = super(lfiles_repo, self).commitctx(*args, **kwargs)
227 ctx = self[node]
251 ctx = self[node]
228 for filename in ctx.files():
252 for filename in ctx.files():
229 if lfutil.isstandin(filename) and filename in ctx.manifest():
253 if lfutil.isstandin(filename) and filename in ctx.manifest():
230 realfile = lfutil.splitstandin(filename)
254 realfile = lfutil.splitstandin(filename)
231 lfutil.copytostore(self, ctx.node(), realfile)
255 lfutil.copytostore(self, ctx.node(), realfile)
232
256
233 return node
257 return node
234
258
235 # Before commit, largefile standins have not had their
259 # Before commit, largefile standins have not had their
236 # contents updated to reflect the hash of their largefile.
260 # contents updated to reflect the hash of their largefile.
237 # Do that here.
261 # Do that here.
238 def commit(self, text="", user=None, date=None, match=None,
262 def commit(self, text="", user=None, date=None, match=None,
239 force=False, editor=False, extra={}):
263 force=False, editor=False, extra={}):
240 orig = super(lfiles_repo, self).commit
264 orig = super(lfiles_repo, self).commit
241
265
242 wlock = repo.wlock()
266 wlock = repo.wlock()
243 try:
267 try:
244 if getattr(repo, "_isrebasing", False):
268 if getattr(repo, "_isrebasing", False):
245 # We have to take the time to pull down the new
269 # We have to take the time to pull down the new
246 # largefiles now. Otherwise if we are rebasing,
270 # largefiles now. Otherwise if we are rebasing,
247 # any largefiles that were modified in the
271 # any largefiles that were modified in the
248 # destination changesets get overwritten, either
272 # destination changesets get overwritten, either
249 # by the rebase or in the first commit after the
273 # by the rebase or in the first commit after the
250 # rebase.
274 # rebase.
251 lfcommands.updatelfiles(repo.ui, repo)
275 lfcommands.updatelfiles(repo.ui, repo)
252 # Case 1: user calls commit with no specific files or
276 # Case 1: user calls commit with no specific files or
253 # include/exclude patterns: refresh and commit all files that
277 # include/exclude patterns: refresh and commit all files that
254 # are "dirty".
278 # are "dirty".
255 if ((match is None) or
279 if ((match is None) or
256 (not match.anypats() and not match.files())):
280 (not match.anypats() and not match.files())):
257 # Spend a bit of time here to get a list of files we know
281 # Spend a bit of time here to get a list of files we know
258 # are modified so we can compare only against those.
282 # are modified so we can compare only against those.
259 # It can cost a lot of time (several seconds)
283 # It can cost a lot of time (several seconds)
260 # otherwise to update all standins if the largefiles are
284 # otherwise to update all standins if the largefiles are
261 # large.
285 # large.
262 lfdirstate = lfutil.openlfdirstate(ui, self)
286 lfdirstate = lfutil.openlfdirstate(ui, self)
263 dirtymatch = match_.always(repo.root, repo.getcwd())
287 dirtymatch = match_.always(repo.root, repo.getcwd())
264 s = lfdirstate.status(dirtymatch, [], False, False, False)
288 s = lfdirstate.status(dirtymatch, [], False, False, False)
265 modifiedfiles = []
289 modifiedfiles = []
266 for i in s:
290 for i in s:
267 modifiedfiles.extend(i)
291 modifiedfiles.extend(i)
268 lfiles = lfutil.listlfiles(self)
292 lfiles = lfutil.listlfiles(self)
269 # this only loops through largefiles that exist (not
293 # this only loops through largefiles that exist (not
270 # removed/renamed)
294 # removed/renamed)
271 for lfile in lfiles:
295 for lfile in lfiles:
272 if lfile in modifiedfiles:
296 if lfile in modifiedfiles:
273 if os.path.exists(self.wjoin(lfutil.standin(lfile))):
297 if os.path.exists(self.wjoin(lfutil.standin(lfile))):
274 # this handles the case where a rebase is being
298 # this handles the case where a rebase is being
275 # performed and the working copy is not updated
299 # performed and the working copy is not updated
276 # yet.
300 # yet.
277 if os.path.exists(self.wjoin(lfile)):
301 if os.path.exists(self.wjoin(lfile)):
278 lfutil.updatestandin(self,
302 lfutil.updatestandin(self,
279 lfutil.standin(lfile))
303 lfutil.standin(lfile))
280 lfdirstate.normal(lfile)
304 lfdirstate.normal(lfile)
281 for lfile in lfdirstate:
305 for lfile in lfdirstate:
282 if lfile in modifiedfiles:
306 if lfile in modifiedfiles:
283 if not os.path.exists(
307 if not os.path.exists(
284 repo.wjoin(lfutil.standin(lfile))):
308 repo.wjoin(lfutil.standin(lfile))):
285 lfdirstate.drop(lfile)
309 lfdirstate.drop(lfile)
286 lfdirstate.write()
310 lfdirstate.write()
287
311
288 return orig(text=text, user=user, date=date, match=match,
312 return orig(text=text, user=user, date=date, match=match,
289 force=force, editor=editor, extra=extra)
313 force=force, editor=editor, extra=extra)
290
314
291 for f in match.files():
315 for f in match.files():
292 if lfutil.isstandin(f):
316 if lfutil.isstandin(f):
293 raise util.Abort(
317 raise util.Abort(
294 _('file "%s" is a largefile standin') % f,
318 _('file "%s" is a largefile standin') % f,
295 hint=('commit the largefile itself instead'))
319 hint=('commit the largefile itself instead'))
296
320
297 # Case 2: user calls commit with specified patterns: refresh
321 # Case 2: user calls commit with specified patterns: refresh
298 # any matching big files.
322 # any matching big files.
299 smatcher = lfutil.composestandinmatcher(self, match)
323 smatcher = lfutil.composestandinmatcher(self, match)
300 standins = lfutil.dirstate_walk(self.dirstate, smatcher)
324 standins = lfutil.dirstate_walk(self.dirstate, smatcher)
301
325
302 # No matching big files: get out of the way and pass control to
326 # No matching big files: get out of the way and pass control to
303 # the usual commit() method.
327 # the usual commit() method.
304 if not standins:
328 if not standins:
305 return orig(text=text, user=user, date=date, match=match,
329 return orig(text=text, user=user, date=date, match=match,
306 force=force, editor=editor, extra=extra)
330 force=force, editor=editor, extra=extra)
307
331
308 # Refresh all matching big files. It's possible that the
332 # Refresh all matching big files. It's possible that the
309 # commit will end up failing, in which case the big files will
333 # commit will end up failing, in which case the big files will
310 # stay refreshed. No harm done: the user modified them and
334 # stay refreshed. No harm done: the user modified them and
311 # asked to commit them, so sooner or later we're going to
335 # asked to commit them, so sooner or later we're going to
312 # refresh the standins. Might as well leave them refreshed.
336 # refresh the standins. Might as well leave them refreshed.
313 lfdirstate = lfutil.openlfdirstate(ui, self)
337 lfdirstate = lfutil.openlfdirstate(ui, self)
314 for standin in standins:
338 for standin in standins:
315 lfile = lfutil.splitstandin(standin)
339 lfile = lfutil.splitstandin(standin)
316 if lfdirstate[lfile] <> 'r':
340 if lfdirstate[lfile] <> 'r':
317 lfutil.updatestandin(self, standin)
341 lfutil.updatestandin(self, standin)
318 lfdirstate.normal(lfile)
342 lfdirstate.normal(lfile)
319 else:
343 else:
320 lfdirstate.drop(lfile)
344 lfdirstate.drop(lfile)
321 lfdirstate.write()
345 lfdirstate.write()
322
346
323 # Cook up a new matcher that only matches regular files or
347 # Cook up a new matcher that only matches regular files or
324 # standins corresponding to the big files requested by the
348 # standins corresponding to the big files requested by the
325 # user. Have to modify _files to prevent commit() from
349 # user. Have to modify _files to prevent commit() from
326 # complaining "not tracked" for big files.
350 # complaining "not tracked" for big files.
327 lfiles = lfutil.listlfiles(repo)
351 lfiles = lfutil.listlfiles(repo)
328 match = copy.copy(match)
352 match = copy.copy(match)
329 orig_matchfn = match.matchfn
353 orig_matchfn = match.matchfn
330
354
331 # Check both the list of largefiles and the list of
355 # Check both the list of largefiles and the list of
332 # standins because if a largefile was removed, it
356 # standins because if a largefile was removed, it
333 # won't be in the list of largefiles at this point
357 # won't be in the list of largefiles at this point
334 match._files += sorted(standins)
358 match._files += sorted(standins)
335
359
336 actualfiles = []
360 actualfiles = []
337 for f in match._files:
361 for f in match._files:
338 fstandin = lfutil.standin(f)
362 fstandin = lfutil.standin(f)
339
363
340 # ignore known largefiles and standins
364 # ignore known largefiles and standins
341 if f in lfiles or fstandin in standins:
365 if f in lfiles or fstandin in standins:
342 continue
366 continue
343
367
344 # append directory separator to avoid collisions
368 # append directory separator to avoid collisions
345 if not fstandin.endswith(os.sep):
369 if not fstandin.endswith(os.sep):
346 fstandin += os.sep
370 fstandin += os.sep
347
371
348 # prevalidate matching standin directories
372 # prevalidate matching standin directories
349 if util.any(st for st in match._files
373 if util.any(st for st in match._files
350 if st.startswith(fstandin)):
374 if st.startswith(fstandin)):
351 continue
375 continue
352 actualfiles.append(f)
376 actualfiles.append(f)
353 match._files = actualfiles
377 match._files = actualfiles
354
378
355 def matchfn(f):
379 def matchfn(f):
356 if orig_matchfn(f):
380 if orig_matchfn(f):
357 return f not in lfiles
381 return f not in lfiles
358 else:
382 else:
359 return f in standins
383 return f in standins
360
384
361 match.matchfn = matchfn
385 match.matchfn = matchfn
362 return orig(text=text, user=user, date=date, match=match,
386 return orig(text=text, user=user, date=date, match=match,
363 force=force, editor=editor, extra=extra)
387 force=force, editor=editor, extra=extra)
364 finally:
388 finally:
365 wlock.release()
389 wlock.release()
366
390
367 def push(self, remote, force=False, revs=None, newbranch=False):
391 def push(self, remote, force=False, revs=None, newbranch=False):
368 o = lfutil.findoutgoing(repo, remote, force)
392 o = lfutil.findoutgoing(repo, remote, force)
369 if o:
393 if o:
370 toupload = set()
394 toupload = set()
371 o = repo.changelog.nodesbetween(o, revs)[0]
395 o = repo.changelog.nodesbetween(o, revs)[0]
372 for n in o:
396 for n in o:
373 parents = [p for p in repo.changelog.parents(n)
397 parents = [p for p in repo.changelog.parents(n)
374 if p != node.nullid]
398 if p != node.nullid]
375 ctx = repo[n]
399 ctx = repo[n]
376 files = set(ctx.files())
400 files = set(ctx.files())
377 if len(parents) == 2:
401 if len(parents) == 2:
378 mc = ctx.manifest()
402 mc = ctx.manifest()
379 mp1 = ctx.parents()[0].manifest()
403 mp1 = ctx.parents()[0].manifest()
380 mp2 = ctx.parents()[1].manifest()
404 mp2 = ctx.parents()[1].manifest()
381 for f in mp1:
405 for f in mp1:
382 if f not in mc:
406 if f not in mc:
383 files.add(f)
407 files.add(f)
384 for f in mp2:
408 for f in mp2:
385 if f not in mc:
409 if f not in mc:
386 files.add(f)
410 files.add(f)
387 for f in mc:
411 for f in mc:
388 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f,
412 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f,
389 None):
413 None):
390 files.add(f)
414 files.add(f)
391
415
392 toupload = toupload.union(
416 toupload = toupload.union(
393 set([ctx[f].data().strip()
417 set([ctx[f].data().strip()
394 for f in files
418 for f in files
395 if lfutil.isstandin(f) and f in ctx]))
419 if lfutil.isstandin(f) and f in ctx]))
396 lfcommands.uploadlfiles(ui, self, remote, toupload)
420 lfcommands.uploadlfiles(ui, self, remote, toupload)
397 return super(lfiles_repo, self).push(remote, force, revs,
421 return super(lfiles_repo, self).push(remote, force, revs,
398 newbranch)
422 newbranch)
399
423
400 repo.__class__ = lfiles_repo
424 repo.__class__ = lfiles_repo
401
425
402 def checkrequireslfiles(ui, repo, **kwargs):
426 def checkrequireslfiles(ui, repo, **kwargs):
403 if 'largefiles' not in repo.requirements and util.any(
427 if 'largefiles' not in repo.requirements and util.any(
404 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
428 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
405 repo.requirements.add('largefiles')
429 repo.requirements.add('largefiles')
406 repo._writerequirements()
430 repo._writerequirements()
407
431
408 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles)
432 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles)
409 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles)
433 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles)
General Comments 0
You need to be logged in to leave comments. Login now