##// END OF EJS Templates
largefiles: don't walk through all ignored files...
Mads Kiilerich -
r18141:a907826c stable
parent child Browse files
Show More
@@ -1,527 +1,525
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
13
14 from mercurial import context, error, manifest, match as match_, util
14 from mercurial import context, error, manifest, match as match_, util
15 from mercurial import node as node_
15 from mercurial import node as node_
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 (isinstance(method, types.FunctionType) and
30 if (isinstance(method, types.FunctionType) and
31 method.func_name == 'wrap'):
31 method.func_name == 'wrap'):
32 ui.warn(_('largefiles: repo method %r appears to have already been'
32 ui.warn(_('largefiles: repo method %r appears to have already been'
33 ' wrapped by another extension: '
33 ' wrapped by another extension: '
34 'largefiles may behave incorrectly\n')
34 'largefiles may behave incorrectly\n')
35 % name)
35 % name)
36
36
37 class lfilesrepo(repo.__class__):
37 class lfilesrepo(repo.__class__):
38 lfstatus = False
38 lfstatus = False
39 def status_nolfiles(self, *args, **kwargs):
39 def status_nolfiles(self, *args, **kwargs):
40 return super(lfilesrepo, self).status(*args, **kwargs)
40 return super(lfilesrepo, self).status(*args, **kwargs)
41
41
42 # When lfstatus is set, return a context that gives the names
42 # When lfstatus is set, return a context that gives the names
43 # of largefiles instead of their corresponding standins and
43 # of largefiles instead of their corresponding standins and
44 # identifies the largefiles as always binary, regardless of
44 # identifies the largefiles as always binary, regardless of
45 # their actual contents.
45 # their actual contents.
46 def __getitem__(self, changeid):
46 def __getitem__(self, changeid):
47 ctx = super(lfilesrepo, self).__getitem__(changeid)
47 ctx = super(lfilesrepo, self).__getitem__(changeid)
48 if self.lfstatus:
48 if self.lfstatus:
49 class lfilesmanifestdict(manifest.manifestdict):
49 class lfilesmanifestdict(manifest.manifestdict):
50 def __contains__(self, filename):
50 def __contains__(self, filename):
51 if super(lfilesmanifestdict,
51 if super(lfilesmanifestdict,
52 self).__contains__(filename):
52 self).__contains__(filename):
53 return True
53 return True
54 return super(lfilesmanifestdict,
54 return super(lfilesmanifestdict,
55 self).__contains__(lfutil.standin(filename))
55 self).__contains__(lfutil.standin(filename))
56 class lfilesctx(ctx.__class__):
56 class lfilesctx(ctx.__class__):
57 def files(self):
57 def files(self):
58 filenames = super(lfilesctx, self).files()
58 filenames = super(lfilesctx, self).files()
59 return [lfutil.splitstandin(f) or f for f in filenames]
59 return [lfutil.splitstandin(f) or f for f in filenames]
60 def manifest(self):
60 def manifest(self):
61 man1 = super(lfilesctx, self).manifest()
61 man1 = super(lfilesctx, self).manifest()
62 man1.__class__ = lfilesmanifestdict
62 man1.__class__ = lfilesmanifestdict
63 return man1
63 return man1
64 def filectx(self, path, fileid=None, filelog=None):
64 def filectx(self, path, fileid=None, filelog=None):
65 try:
65 try:
66 if filelog is not None:
66 if filelog is not None:
67 result = super(lfilesctx, self).filectx(
67 result = super(lfilesctx, self).filectx(
68 path, fileid, filelog)
68 path, fileid, filelog)
69 else:
69 else:
70 result = super(lfilesctx, self).filectx(
70 result = super(lfilesctx, self).filectx(
71 path, fileid)
71 path, fileid)
72 except error.LookupError:
72 except error.LookupError:
73 # Adding a null character will cause Mercurial to
73 # Adding a null character will cause Mercurial to
74 # identify this as a binary file.
74 # identify this as a binary file.
75 if filelog is not None:
75 if filelog is not None:
76 result = super(lfilesctx, self).filectx(
76 result = super(lfilesctx, self).filectx(
77 lfutil.standin(path), fileid, filelog)
77 lfutil.standin(path), fileid, filelog)
78 else:
78 else:
79 result = super(lfilesctx, self).filectx(
79 result = super(lfilesctx, self).filectx(
80 lfutil.standin(path), fileid)
80 lfutil.standin(path), fileid)
81 olddata = result.data
81 olddata = result.data
82 result.data = lambda: olddata() + '\0'
82 result.data = lambda: olddata() + '\0'
83 return result
83 return result
84 ctx.__class__ = lfilesctx
84 ctx.__class__ = lfilesctx
85 return ctx
85 return ctx
86
86
87 # Figure out the status of big files and insert them into the
87 # Figure out the status of big files and insert them into the
88 # appropriate list in the result. Also removes standin files
88 # appropriate list in the result. Also removes standin files
89 # from the listing. Revert to the original status if
89 # from the listing. Revert to the original status if
90 # self.lfstatus is False.
90 # self.lfstatus is False.
91 def status(self, node1='.', node2=None, match=None, ignored=False,
91 def status(self, node1='.', node2=None, match=None, ignored=False,
92 clean=False, unknown=False, listsubrepos=False):
92 clean=False, unknown=False, listsubrepos=False):
93 listignored, listclean, listunknown = ignored, clean, unknown
93 listignored, listclean, listunknown = ignored, clean, unknown
94 if not self.lfstatus:
94 if not self.lfstatus:
95 return super(lfilesrepo, self).status(node1, node2, match,
95 return super(lfilesrepo, self).status(node1, node2, match,
96 listignored, listclean, listunknown, listsubrepos)
96 listignored, listclean, listunknown, listsubrepos)
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 = self[node1]
103 ctx1 = self[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 = self[node2]
107 ctx2 = self[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 # First check if there were files specified on the
123 # First check if there were files specified on the
124 # command line. If there were, and none of them were
124 # command line. If there were, and none of them were
125 # largefiles, we should just bail here and let super
125 # largefiles, we should just bail here and let super
126 # handle it -- thus gaining a big performance boost.
126 # handle it -- thus gaining a big performance boost.
127 lfdirstate = lfutil.openlfdirstate(ui, self)
127 lfdirstate = lfutil.openlfdirstate(ui, self)
128 if match.files() and not match.anypats():
128 if match.files() and not match.anypats():
129 for f in lfdirstate:
129 for f in lfdirstate:
130 if match(f):
130 if match(f):
131 break
131 break
132 else:
132 else:
133 return super(lfilesrepo, self).status(node1, node2,
133 return super(lfilesrepo, self).status(node1, node2,
134 match, listignored, listclean,
134 match, listignored, listclean,
135 listunknown, listsubrepos)
135 listunknown, listsubrepos)
136
136
137 # Create a copy of match that matches standins instead
137 # Create a copy of match that matches standins instead
138 # of largefiles.
138 # of largefiles.
139 def tostandins(files):
139 def tostandins(files):
140 if not working:
140 if not working:
141 return files
141 return files
142 newfiles = []
142 newfiles = []
143 dirstate = self.dirstate
143 dirstate = self.dirstate
144 for f in files:
144 for f in files:
145 sf = lfutil.standin(f)
145 sf = lfutil.standin(f)
146 if sf in dirstate:
146 if sf in dirstate:
147 newfiles.append(sf)
147 newfiles.append(sf)
148 elif sf in dirstate.dirs():
148 elif sf in dirstate.dirs():
149 # Directory entries could be regular or
149 # Directory entries could be regular or
150 # standin, check both
150 # standin, check both
151 newfiles.extend((f, sf))
151 newfiles.extend((f, sf))
152 else:
152 else:
153 newfiles.append(f)
153 newfiles.append(f)
154 return newfiles
154 return newfiles
155
155
156 # Create a function that we can use to override what is
156 # Create a function that we can use to override what is
157 # normally the ignore matcher. We've already checked
157 # normally the ignore matcher. We've already checked
158 # for ignored files on the first dirstate walk, and
158 # for ignored files on the first dirstate walk, and
159 # unnecessarily re-checking here causes a huge performance
159 # unnecessarily re-checking here causes a huge performance
160 # hit because lfdirstate only knows about largefiles
160 # hit because lfdirstate only knows about largefiles
161 def _ignoreoverride(self):
161 def _ignoreoverride(self):
162 return False
162 return False
163
163
164 m = copy.copy(match)
164 m = copy.copy(match)
165 m._files = tostandins(m._files)
165 m._files = tostandins(m._files)
166
166
167 # Get ignored files here even if we weren't asked for them; we
168 # must use the result here for filtering later
169 result = super(lfilesrepo, self).status(node1, node2, m,
167 result = super(lfilesrepo, self).status(node1, node2, m,
170 True, clean, unknown, listsubrepos)
168 ignored, clean, unknown, listsubrepos)
171 if working:
169 if working:
172 try:
170 try:
173 # Any non-largefiles that were explicitly listed must be
171 # Any non-largefiles that were explicitly listed must be
174 # taken out or lfdirstate.status will report an error.
172 # taken out or lfdirstate.status will report an error.
175 # The status of these files was already computed using
173 # The status of these files was already computed using
176 # super's status.
174 # super's status.
177 # Override lfdirstate's ignore matcher to not do
175 # Override lfdirstate's ignore matcher to not do
178 # anything
176 # anything
179 origignore = lfdirstate._ignore
177 origignore = lfdirstate._ignore
180 lfdirstate._ignore = _ignoreoverride
178 lfdirstate._ignore = _ignoreoverride
181
179
182 def sfindirstate(f):
180 def sfindirstate(f):
183 sf = lfutil.standin(f)
181 sf = lfutil.standin(f)
184 dirstate = self.dirstate
182 dirstate = self.dirstate
185 return sf in dirstate or sf in dirstate.dirs()
183 return sf in dirstate or sf in dirstate.dirs()
186 match._files = [f for f in match._files
184 match._files = [f for f in match._files
187 if sfindirstate(f)]
185 if sfindirstate(f)]
188 # Don't waste time getting the ignored and unknown
186 # Don't waste time getting the ignored and unknown
189 # files again; we already have them
187 # files again; we already have them
190 s = lfdirstate.status(match, [], False,
188 s = lfdirstate.status(match, [], False,
191 listclean, False)
189 listclean, False)
192 (unsure, modified, added, removed, missing, unknown,
190 (unsure, modified, added, removed, missing, unknown,
193 ignored, clean) = s
191 ignored, clean) = s
194 # Replace the list of ignored and unknown files with
192 # Replace the list of ignored and unknown files with
195 # the previously calculated lists, and strip out the
193 # the previously calculated lists, and strip out the
196 # largefiles
194 # largefiles
197 lfiles = set(lfdirstate._map)
195 lfiles = set(lfdirstate._map)
198 ignored = set(result[5]).difference(lfiles)
196 ignored = set(result[5]).difference(lfiles)
199 unknown = set(result[4]).difference(lfiles)
197 unknown = set(result[4]).difference(lfiles)
200 if parentworking:
198 if parentworking:
201 for lfile in unsure:
199 for lfile in unsure:
202 standin = lfutil.standin(lfile)
200 standin = lfutil.standin(lfile)
203 if standin not in ctx1:
201 if standin not in ctx1:
204 # from second parent
202 # from second parent
205 modified.append(lfile)
203 modified.append(lfile)
206 elif ctx1[standin].data().strip() \
204 elif ctx1[standin].data().strip() \
207 != lfutil.hashfile(self.wjoin(lfile)):
205 != lfutil.hashfile(self.wjoin(lfile)):
208 modified.append(lfile)
206 modified.append(lfile)
209 else:
207 else:
210 clean.append(lfile)
208 clean.append(lfile)
211 lfdirstate.normal(lfile)
209 lfdirstate.normal(lfile)
212 else:
210 else:
213 tocheck = unsure + modified + added + clean
211 tocheck = unsure + modified + added + clean
214 modified, added, clean = [], [], []
212 modified, added, clean = [], [], []
215
213
216 for lfile in tocheck:
214 for lfile in tocheck:
217 standin = lfutil.standin(lfile)
215 standin = lfutil.standin(lfile)
218 if inctx(standin, ctx1):
216 if inctx(standin, ctx1):
219 if ctx1[standin].data().strip() != \
217 if ctx1[standin].data().strip() != \
220 lfutil.hashfile(self.wjoin(lfile)):
218 lfutil.hashfile(self.wjoin(lfile)):
221 modified.append(lfile)
219 modified.append(lfile)
222 else:
220 else:
223 clean.append(lfile)
221 clean.append(lfile)
224 else:
222 else:
225 added.append(lfile)
223 added.append(lfile)
226 finally:
224 finally:
227 # Replace the original ignore function
225 # Replace the original ignore function
228 lfdirstate._ignore = origignore
226 lfdirstate._ignore = origignore
229
227
230 for standin in ctx1.manifest():
228 for standin in ctx1.manifest():
231 if not lfutil.isstandin(standin):
229 if not lfutil.isstandin(standin):
232 continue
230 continue
233 lfile = lfutil.splitstandin(standin)
231 lfile = lfutil.splitstandin(standin)
234 if not match(lfile):
232 if not match(lfile):
235 continue
233 continue
236 if lfile not in lfdirstate:
234 if lfile not in lfdirstate:
237 removed.append(lfile)
235 removed.append(lfile)
238
236
239 # Filter result lists
237 # Filter result lists
240 result = list(result)
238 result = list(result)
241
239
242 # Largefiles are not really removed when they're
240 # Largefiles are not really removed when they're
243 # still in the normal dirstate. Likewise, normal
241 # still in the normal dirstate. Likewise, normal
244 # files are not really removed if it's still in
242 # files are not really removed if it's still in
245 # lfdirstate. This happens in merges where files
243 # lfdirstate. This happens in merges where files
246 # change type.
244 # change type.
247 removed = [f for f in removed if f not in self.dirstate]
245 removed = [f for f in removed if f not in self.dirstate]
248 result[2] = [f for f in result[2] if f not in lfdirstate]
246 result[2] = [f for f in result[2] if f not in lfdirstate]
249
247
250 # Unknown files
248 # Unknown files
251 unknown = set(unknown).difference(ignored)
249 unknown = set(unknown).difference(ignored)
252 result[4] = [f for f in unknown
250 result[4] = [f for f in unknown
253 if (self.dirstate[f] == '?' and
251 if (self.dirstate[f] == '?' and
254 not lfutil.isstandin(f))]
252 not lfutil.isstandin(f))]
255 # Ignored files were calculated earlier by the dirstate,
253 # Ignored files were calculated earlier by the dirstate,
256 # and we already stripped out the largefiles from the list
254 # and we already stripped out the largefiles from the list
257 result[5] = ignored
255 result[5] = ignored
258 # combine normal files and largefiles
256 # combine normal files and largefiles
259 normals = [[fn for fn in filelist
257 normals = [[fn for fn in filelist
260 if not lfutil.isstandin(fn)]
258 if not lfutil.isstandin(fn)]
261 for filelist in result]
259 for filelist in result]
262 lfiles = (modified, added, removed, missing, [], [], clean)
260 lfiles = (modified, added, removed, missing, [], [], clean)
263 result = [sorted(list1 + list2)
261 result = [sorted(list1 + list2)
264 for (list1, list2) in zip(normals, lfiles)]
262 for (list1, list2) in zip(normals, lfiles)]
265 else:
263 else:
266 def toname(f):
264 def toname(f):
267 if lfutil.isstandin(f):
265 if lfutil.isstandin(f):
268 return lfutil.splitstandin(f)
266 return lfutil.splitstandin(f)
269 return f
267 return f
270 result = [[toname(f) for f in items] for items in result]
268 result = [[toname(f) for f in items] for items in result]
271
269
272 lfdirstate.write()
270 lfdirstate.write()
273
271
274 if not listunknown:
272 if not listunknown:
275 result[4] = []
273 result[4] = []
276 if not listignored:
274 if not listignored:
277 result[5] = []
275 result[5] = []
278 if not listclean:
276 if not listclean:
279 result[6] = []
277 result[6] = []
280 self.lfstatus = True
278 self.lfstatus = True
281 return result
279 return result
282
280
283 # As part of committing, copy all of the largefiles into the
281 # As part of committing, copy all of the largefiles into the
284 # cache.
282 # cache.
285 def commitctx(self, *args, **kwargs):
283 def commitctx(self, *args, **kwargs):
286 node = super(lfilesrepo, self).commitctx(*args, **kwargs)
284 node = super(lfilesrepo, self).commitctx(*args, **kwargs)
287 lfutil.copyalltostore(self, node)
285 lfutil.copyalltostore(self, node)
288 return node
286 return node
289
287
290 # Before commit, largefile standins have not had their
288 # Before commit, largefile standins have not had their
291 # contents updated to reflect the hash of their largefile.
289 # contents updated to reflect the hash of their largefile.
292 # Do that here.
290 # Do that here.
293 def commit(self, text="", user=None, date=None, match=None,
291 def commit(self, text="", user=None, date=None, match=None,
294 force=False, editor=False, extra={}):
292 force=False, editor=False, extra={}):
295 orig = super(lfilesrepo, self).commit
293 orig = super(lfilesrepo, self).commit
296
294
297 wlock = self.wlock()
295 wlock = self.wlock()
298 try:
296 try:
299 # Case 0: Rebase or Transplant
297 # Case 0: Rebase or Transplant
300 # We have to take the time to pull down the new largefiles now.
298 # We have to take the time to pull down the new largefiles now.
301 # Otherwise, any largefiles that were modified in the
299 # Otherwise, any largefiles that were modified in the
302 # destination changesets get overwritten, either by the rebase
300 # destination changesets get overwritten, either by the rebase
303 # or in the first commit after the rebase or transplant.
301 # or in the first commit after the rebase or transplant.
304 # updatelfiles will update the dirstate to mark any pulled
302 # updatelfiles will update the dirstate to mark any pulled
305 # largefiles as modified
303 # largefiles as modified
306 if getattr(self, "_isrebasing", False) or \
304 if getattr(self, "_isrebasing", False) or \
307 getattr(self, "_istransplanting", False):
305 getattr(self, "_istransplanting", False):
308 lfcommands.updatelfiles(self.ui, self, filelist=None,
306 lfcommands.updatelfiles(self.ui, self, filelist=None,
309 printmessage=False)
307 printmessage=False)
310 result = orig(text=text, user=user, date=date, match=match,
308 result = orig(text=text, user=user, date=date, match=match,
311 force=force, editor=editor, extra=extra)
309 force=force, editor=editor, extra=extra)
312 return result
310 return result
313 # Case 1: user calls commit with no specific files or
311 # Case 1: user calls commit with no specific files or
314 # include/exclude patterns: refresh and commit all files that
312 # include/exclude patterns: refresh and commit all files that
315 # are "dirty".
313 # are "dirty".
316 if ((match is None) or
314 if ((match is None) or
317 (not match.anypats() and not match.files())):
315 (not match.anypats() and not match.files())):
318 # Spend a bit of time here to get a list of files we know
316 # Spend a bit of time here to get a list of files we know
319 # are modified so we can compare only against those.
317 # are modified so we can compare only against those.
320 # It can cost a lot of time (several seconds)
318 # It can cost a lot of time (several seconds)
321 # otherwise to update all standins if the largefiles are
319 # otherwise to update all standins if the largefiles are
322 # large.
320 # large.
323 lfdirstate = lfutil.openlfdirstate(ui, self)
321 lfdirstate = lfutil.openlfdirstate(ui, self)
324 dirtymatch = match_.always(self.root, self.getcwd())
322 dirtymatch = match_.always(self.root, self.getcwd())
325 s = lfdirstate.status(dirtymatch, [], False, False, False)
323 s = lfdirstate.status(dirtymatch, [], False, False, False)
326 modifiedfiles = []
324 modifiedfiles = []
327 for i in s:
325 for i in s:
328 modifiedfiles.extend(i)
326 modifiedfiles.extend(i)
329 lfiles = lfutil.listlfiles(self)
327 lfiles = lfutil.listlfiles(self)
330 # this only loops through largefiles that exist (not
328 # this only loops through largefiles that exist (not
331 # removed/renamed)
329 # removed/renamed)
332 for lfile in lfiles:
330 for lfile in lfiles:
333 if lfile in modifiedfiles:
331 if lfile in modifiedfiles:
334 if os.path.exists(
332 if os.path.exists(
335 self.wjoin(lfutil.standin(lfile))):
333 self.wjoin(lfutil.standin(lfile))):
336 # this handles the case where a rebase is being
334 # this handles the case where a rebase is being
337 # performed and the working copy is not updated
335 # performed and the working copy is not updated
338 # yet.
336 # yet.
339 if os.path.exists(self.wjoin(lfile)):
337 if os.path.exists(self.wjoin(lfile)):
340 lfutil.updatestandin(self,
338 lfutil.updatestandin(self,
341 lfutil.standin(lfile))
339 lfutil.standin(lfile))
342 lfdirstate.normal(lfile)
340 lfdirstate.normal(lfile)
343
341
344 result = orig(text=text, user=user, date=date, match=match,
342 result = orig(text=text, user=user, date=date, match=match,
345 force=force, editor=editor, extra=extra)
343 force=force, editor=editor, extra=extra)
346
344
347 if result is not None:
345 if result is not None:
348 for lfile in lfdirstate:
346 for lfile in lfdirstate:
349 if lfile in modifiedfiles:
347 if lfile in modifiedfiles:
350 if (not os.path.exists(self.wjoin(
348 if (not os.path.exists(self.wjoin(
351 lfutil.standin(lfile)))) or \
349 lfutil.standin(lfile)))) or \
352 (not os.path.exists(self.wjoin(lfile))):
350 (not os.path.exists(self.wjoin(lfile))):
353 lfdirstate.drop(lfile)
351 lfdirstate.drop(lfile)
354
352
355 # This needs to be after commit; otherwise precommit hooks
353 # This needs to be after commit; otherwise precommit hooks
356 # get the wrong status
354 # get the wrong status
357 lfdirstate.write()
355 lfdirstate.write()
358 return result
356 return result
359
357
360 lfiles = lfutil.listlfiles(self)
358 lfiles = lfutil.listlfiles(self)
361 match._files = self._subdirlfs(match.files(), lfiles)
359 match._files = self._subdirlfs(match.files(), lfiles)
362
360
363 # Case 2: user calls commit with specified patterns: refresh
361 # Case 2: user calls commit with specified patterns: refresh
364 # any matching big files.
362 # any matching big files.
365 smatcher = lfutil.composestandinmatcher(self, match)
363 smatcher = lfutil.composestandinmatcher(self, match)
366 standins = lfutil.dirstatewalk(self.dirstate, smatcher)
364 standins = lfutil.dirstatewalk(self.dirstate, smatcher)
367
365
368 # No matching big files: get out of the way and pass control to
366 # No matching big files: get out of the way and pass control to
369 # the usual commit() method.
367 # the usual commit() method.
370 if not standins:
368 if not standins:
371 return orig(text=text, user=user, date=date, match=match,
369 return orig(text=text, user=user, date=date, match=match,
372 force=force, editor=editor, extra=extra)
370 force=force, editor=editor, extra=extra)
373
371
374 # Refresh all matching big files. It's possible that the
372 # Refresh all matching big files. It's possible that the
375 # commit will end up failing, in which case the big files will
373 # commit will end up failing, in which case the big files will
376 # stay refreshed. No harm done: the user modified them and
374 # stay refreshed. No harm done: the user modified them and
377 # asked to commit them, so sooner or later we're going to
375 # asked to commit them, so sooner or later we're going to
378 # refresh the standins. Might as well leave them refreshed.
376 # refresh the standins. Might as well leave them refreshed.
379 lfdirstate = lfutil.openlfdirstate(ui, self)
377 lfdirstate = lfutil.openlfdirstate(ui, self)
380 for standin in standins:
378 for standin in standins:
381 lfile = lfutil.splitstandin(standin)
379 lfile = lfutil.splitstandin(standin)
382 if lfdirstate[lfile] <> 'r':
380 if lfdirstate[lfile] <> 'r':
383 lfutil.updatestandin(self, standin)
381 lfutil.updatestandin(self, standin)
384 lfdirstate.normal(lfile)
382 lfdirstate.normal(lfile)
385 else:
383 else:
386 lfdirstate.drop(lfile)
384 lfdirstate.drop(lfile)
387
385
388 # Cook up a new matcher that only matches regular files or
386 # Cook up a new matcher that only matches regular files or
389 # standins corresponding to the big files requested by the
387 # standins corresponding to the big files requested by the
390 # user. Have to modify _files to prevent commit() from
388 # user. Have to modify _files to prevent commit() from
391 # complaining "not tracked" for big files.
389 # complaining "not tracked" for big files.
392 match = copy.copy(match)
390 match = copy.copy(match)
393 origmatchfn = match.matchfn
391 origmatchfn = match.matchfn
394
392
395 # Check both the list of largefiles and the list of
393 # Check both the list of largefiles and the list of
396 # standins because if a largefile was removed, it
394 # standins because if a largefile was removed, it
397 # won't be in the list of largefiles at this point
395 # won't be in the list of largefiles at this point
398 match._files += sorted(standins)
396 match._files += sorted(standins)
399
397
400 actualfiles = []
398 actualfiles = []
401 for f in match._files:
399 for f in match._files:
402 fstandin = lfutil.standin(f)
400 fstandin = lfutil.standin(f)
403
401
404 # ignore known largefiles and standins
402 # ignore known largefiles and standins
405 if f in lfiles or fstandin in standins:
403 if f in lfiles or fstandin in standins:
406 continue
404 continue
407
405
408 # append directory separator to avoid collisions
406 # append directory separator to avoid collisions
409 if not fstandin.endswith(os.sep):
407 if not fstandin.endswith(os.sep):
410 fstandin += os.sep
408 fstandin += os.sep
411
409
412 actualfiles.append(f)
410 actualfiles.append(f)
413 match._files = actualfiles
411 match._files = actualfiles
414
412
415 def matchfn(f):
413 def matchfn(f):
416 if origmatchfn(f):
414 if origmatchfn(f):
417 return f not in lfiles
415 return f not in lfiles
418 else:
416 else:
419 return f in standins
417 return f in standins
420
418
421 match.matchfn = matchfn
419 match.matchfn = matchfn
422 result = orig(text=text, user=user, date=date, match=match,
420 result = orig(text=text, user=user, date=date, match=match,
423 force=force, editor=editor, extra=extra)
421 force=force, editor=editor, extra=extra)
424 # This needs to be after commit; otherwise precommit hooks
422 # This needs to be after commit; otherwise precommit hooks
425 # get the wrong status
423 # get the wrong status
426 lfdirstate.write()
424 lfdirstate.write()
427 return result
425 return result
428 finally:
426 finally:
429 wlock.release()
427 wlock.release()
430
428
431 def push(self, remote, force=False, revs=None, newbranch=False):
429 def push(self, remote, force=False, revs=None, newbranch=False):
432 o = lfutil.findoutgoing(self, remote, force)
430 o = lfutil.findoutgoing(self, remote, force)
433 if o:
431 if o:
434 toupload = set()
432 toupload = set()
435 o = self.changelog.nodesbetween(o, revs)[0]
433 o = self.changelog.nodesbetween(o, revs)[0]
436 for n in o:
434 for n in o:
437 parents = [p for p in self.changelog.parents(n)
435 parents = [p for p in self.changelog.parents(n)
438 if p != node_.nullid]
436 if p != node_.nullid]
439 ctx = self[n]
437 ctx = self[n]
440 files = set(ctx.files())
438 files = set(ctx.files())
441 if len(parents) == 2:
439 if len(parents) == 2:
442 mc = ctx.manifest()
440 mc = ctx.manifest()
443 mp1 = ctx.parents()[0].manifest()
441 mp1 = ctx.parents()[0].manifest()
444 mp2 = ctx.parents()[1].manifest()
442 mp2 = ctx.parents()[1].manifest()
445 for f in mp1:
443 for f in mp1:
446 if f not in mc:
444 if f not in mc:
447 files.add(f)
445 files.add(f)
448 for f in mp2:
446 for f in mp2:
449 if f not in mc:
447 if f not in mc:
450 files.add(f)
448 files.add(f)
451 for f in mc:
449 for f in mc:
452 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f,
450 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f,
453 None):
451 None):
454 files.add(f)
452 files.add(f)
455
453
456 toupload = toupload.union(
454 toupload = toupload.union(
457 set([ctx[f].data().strip()
455 set([ctx[f].data().strip()
458 for f in files
456 for f in files
459 if lfutil.isstandin(f) and f in ctx]))
457 if lfutil.isstandin(f) and f in ctx]))
460 lfcommands.uploadlfiles(ui, self, remote, toupload)
458 lfcommands.uploadlfiles(ui, self, remote, toupload)
461 return super(lfilesrepo, self).push(remote, force, revs,
459 return super(lfilesrepo, self).push(remote, force, revs,
462 newbranch)
460 newbranch)
463
461
464 def _subdirlfs(self, files, lfiles):
462 def _subdirlfs(self, files, lfiles):
465 '''
463 '''
466 Adjust matched file list
464 Adjust matched file list
467 If we pass a directory to commit whose only commitable files
465 If we pass a directory to commit whose only commitable files
468 are largefiles, the core commit code aborts before finding
466 are largefiles, the core commit code aborts before finding
469 the largefiles.
467 the largefiles.
470 So we do the following:
468 So we do the following:
471 For directories that only have largefiles as matches,
469 For directories that only have largefiles as matches,
472 we explicitly add the largefiles to the matchlist and remove
470 we explicitly add the largefiles to the matchlist and remove
473 the directory.
471 the directory.
474 In other cases, we leave the match list unmodified.
472 In other cases, we leave the match list unmodified.
475 '''
473 '''
476 actualfiles = []
474 actualfiles = []
477 dirs = []
475 dirs = []
478 regulars = []
476 regulars = []
479
477
480 for f in files:
478 for f in files:
481 if lfutil.isstandin(f + '/'):
479 if lfutil.isstandin(f + '/'):
482 raise util.Abort(
480 raise util.Abort(
483 _('file "%s" is a largefile standin') % f,
481 _('file "%s" is a largefile standin') % f,
484 hint=('commit the largefile itself instead'))
482 hint=('commit the largefile itself instead'))
485 # Scan directories
483 # Scan directories
486 if os.path.isdir(self.wjoin(f)):
484 if os.path.isdir(self.wjoin(f)):
487 dirs.append(f)
485 dirs.append(f)
488 else:
486 else:
489 regulars.append(f)
487 regulars.append(f)
490
488
491 for f in dirs:
489 for f in dirs:
492 matcheddir = False
490 matcheddir = False
493 d = self.dirstate.normalize(f) + '/'
491 d = self.dirstate.normalize(f) + '/'
494 # Check for matched normal files
492 # Check for matched normal files
495 for mf in regulars:
493 for mf in regulars:
496 if self.dirstate.normalize(mf).startswith(d):
494 if self.dirstate.normalize(mf).startswith(d):
497 actualfiles.append(f)
495 actualfiles.append(f)
498 matcheddir = True
496 matcheddir = True
499 break
497 break
500 if not matcheddir:
498 if not matcheddir:
501 # If no normal match, manually append
499 # If no normal match, manually append
502 # any matching largefiles
500 # any matching largefiles
503 for lf in lfiles:
501 for lf in lfiles:
504 if self.dirstate.normalize(lf).startswith(d):
502 if self.dirstate.normalize(lf).startswith(d):
505 actualfiles.append(lf)
503 actualfiles.append(lf)
506 if not matcheddir:
504 if not matcheddir:
507 actualfiles.append(lfutil.standin(f))
505 actualfiles.append(lfutil.standin(f))
508 matcheddir = True
506 matcheddir = True
509 # Nothing in dir, so readd it
507 # Nothing in dir, so readd it
510 # and let commit reject it
508 # and let commit reject it
511 if not matcheddir:
509 if not matcheddir:
512 actualfiles.append(f)
510 actualfiles.append(f)
513
511
514 # Always add normal files
512 # Always add normal files
515 actualfiles += regulars
513 actualfiles += regulars
516 return actualfiles
514 return actualfiles
517
515
518 repo.__class__ = lfilesrepo
516 repo.__class__ = lfilesrepo
519
517
520 def checkrequireslfiles(ui, repo, **kwargs):
518 def checkrequireslfiles(ui, repo, **kwargs):
521 if 'largefiles' not in repo.requirements and util.any(
519 if 'largefiles' not in repo.requirements and util.any(
522 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
520 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
523 repo.requirements.add('largefiles')
521 repo.requirements.add('largefiles')
524 repo._writerequirements()
522 repo._writerequirements()
525
523
526 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles)
524 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles)
527 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles)
525 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles)
General Comments 0
You need to be logged in to leave comments. Login now