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