##// END OF EJS Templates
largefiles status: update lfdirstate with result from cleanliness check...
Mads Kiilerich -
r18139:03faf12f stable
parent child Browse files
Show More
@@ -1,525 +1,527
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
167 # Get ignored files here even if we weren't asked for them; we
168 # must use the result here for filtering later
168 # must use the result here for filtering later
169 result = super(lfilesrepo, self).status(node1, node2, m,
169 result = super(lfilesrepo, self).status(node1, node2, m,
170 True, clean, unknown, listsubrepos)
170 True, clean, unknown, listsubrepos)
171 if working:
171 if working:
172 try:
172 try:
173 # Any non-largefiles that were explicitly listed must be
173 # Any non-largefiles that were explicitly listed must be
174 # taken out or lfdirstate.status will report an error.
174 # taken out or lfdirstate.status will report an error.
175 # The status of these files was already computed using
175 # The status of these files was already computed using
176 # super's status.
176 # super's status.
177 # Override lfdirstate's ignore matcher to not do
177 # Override lfdirstate's ignore matcher to not do
178 # anything
178 # anything
179 origignore = lfdirstate._ignore
179 origignore = lfdirstate._ignore
180 lfdirstate._ignore = _ignoreoverride
180 lfdirstate._ignore = _ignoreoverride
181
181
182 def sfindirstate(f):
182 def sfindirstate(f):
183 sf = lfutil.standin(f)
183 sf = lfutil.standin(f)
184 dirstate = self.dirstate
184 dirstate = self.dirstate
185 return sf in dirstate or sf in dirstate.dirs()
185 return sf in dirstate or sf in dirstate.dirs()
186 match._files = [f for f in match._files
186 match._files = [f for f in match._files
187 if sfindirstate(f)]
187 if sfindirstate(f)]
188 # Don't waste time getting the ignored and unknown
188 # Don't waste time getting the ignored and unknown
189 # files again; we already have them
189 # files again; we already have them
190 s = lfdirstate.status(match, [], False,
190 s = lfdirstate.status(match, [], False,
191 listclean, False)
191 listclean, False)
192 (unsure, modified, added, removed, missing, unknown,
192 (unsure, modified, added, removed, missing, unknown,
193 ignored, clean) = s
193 ignored, clean) = s
194 # Replace the list of ignored and unknown files with
194 # Replace the list of ignored and unknown files with
195 # the previously calculated lists, and strip out the
195 # the previously calculated lists, and strip out the
196 # largefiles
196 # largefiles
197 lfiles = set(lfdirstate._map)
197 lfiles = set(lfdirstate._map)
198 ignored = set(result[5]).difference(lfiles)
198 ignored = set(result[5]).difference(lfiles)
199 unknown = set(result[4]).difference(lfiles)
199 unknown = set(result[4]).difference(lfiles)
200 if parentworking:
200 if parentworking:
201 for lfile in unsure:
201 for lfile in unsure:
202 standin = lfutil.standin(lfile)
202 standin = lfutil.standin(lfile)
203 if standin not in ctx1:
203 if standin not in ctx1:
204 # from second parent
204 # from second parent
205 modified.append(lfile)
205 modified.append(lfile)
206 elif ctx1[standin].data().strip() \
206 elif ctx1[standin].data().strip() \
207 != lfutil.hashfile(self.wjoin(lfile)):
207 != lfutil.hashfile(self.wjoin(lfile)):
208 modified.append(lfile)
208 modified.append(lfile)
209 else:
209 else:
210 clean.append(lfile)
210 clean.append(lfile)
211 lfdirstate.normal(lfile)
211 lfdirstate.normal(lfile)
212 else:
212 else:
213 tocheck = unsure + modified + added + clean
213 tocheck = unsure + modified + added + clean
214 modified, added, clean = [], [], []
214 modified, added, clean = [], [], []
215
215
216 for lfile in tocheck:
216 for lfile in tocheck:
217 standin = lfutil.standin(lfile)
217 standin = lfutil.standin(lfile)
218 if inctx(standin, ctx1):
218 if inctx(standin, ctx1):
219 if ctx1[standin].data().strip() != \
219 if ctx1[standin].data().strip() != \
220 lfutil.hashfile(self.wjoin(lfile)):
220 lfutil.hashfile(self.wjoin(lfile)):
221 modified.append(lfile)
221 modified.append(lfile)
222 else:
222 else:
223 clean.append(lfile)
223 clean.append(lfile)
224 else:
224 else:
225 added.append(lfile)
225 added.append(lfile)
226 finally:
226 finally:
227 # Replace the original ignore function
227 # Replace the original ignore function
228 lfdirstate._ignore = origignore
228 lfdirstate._ignore = origignore
229
229
230 for standin in ctx1.manifest():
230 for standin in ctx1.manifest():
231 if not lfutil.isstandin(standin):
231 if not lfutil.isstandin(standin):
232 continue
232 continue
233 lfile = lfutil.splitstandin(standin)
233 lfile = lfutil.splitstandin(standin)
234 if not match(lfile):
234 if not match(lfile):
235 continue
235 continue
236 if lfile not in lfdirstate:
236 if lfile not in lfdirstate:
237 removed.append(lfile)
237 removed.append(lfile)
238
238
239 # Filter result lists
239 # Filter result lists
240 result = list(result)
240 result = list(result)
241
241
242 # Largefiles are not really removed when they're
242 # Largefiles are not really removed when they're
243 # still in the normal dirstate. Likewise, normal
243 # still in the normal dirstate. Likewise, normal
244 # files are not really removed if it's still in
244 # files are not really removed if it's still in
245 # lfdirstate. This happens in merges where files
245 # lfdirstate. This happens in merges where files
246 # change type.
246 # change type.
247 removed = [f for f in removed if f not in self.dirstate]
247 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]
248 result[2] = [f for f in result[2] if f not in lfdirstate]
249
249
250 # Unknown files
250 # Unknown files
251 unknown = set(unknown).difference(ignored)
251 unknown = set(unknown).difference(ignored)
252 result[4] = [f for f in unknown
252 result[4] = [f for f in unknown
253 if (self.dirstate[f] == '?' and
253 if (self.dirstate[f] == '?' and
254 not lfutil.isstandin(f))]
254 not lfutil.isstandin(f))]
255 # Ignored files were calculated earlier by the dirstate,
255 # Ignored files were calculated earlier by the dirstate,
256 # and we already stripped out the largefiles from the list
256 # and we already stripped out the largefiles from the list
257 result[5] = ignored
257 result[5] = ignored
258 # combine normal files and largefiles
258 # combine normal files and largefiles
259 normals = [[fn for fn in filelist
259 normals = [[fn for fn in filelist
260 if not lfutil.isstandin(fn)]
260 if not lfutil.isstandin(fn)]
261 for filelist in result]
261 for filelist in result]
262 lfiles = (modified, added, removed, missing, [], [], clean)
262 lfiles = (modified, added, removed, missing, [], [], clean)
263 result = [sorted(list1 + list2)
263 result = [sorted(list1 + list2)
264 for (list1, list2) in zip(normals, lfiles)]
264 for (list1, list2) in zip(normals, lfiles)]
265 else:
265 else:
266 def toname(f):
266 def toname(f):
267 if lfutil.isstandin(f):
267 if lfutil.isstandin(f):
268 return lfutil.splitstandin(f)
268 return lfutil.splitstandin(f)
269 return f
269 return f
270 result = [[toname(f) for f in items] for items in result]
270 result = [[toname(f) for f in items] for items in result]
271
271
272 lfdirstate.write()
273
272 if not listunknown:
274 if not listunknown:
273 result[4] = []
275 result[4] = []
274 if not listignored:
276 if not listignored:
275 result[5] = []
277 result[5] = []
276 if not listclean:
278 if not listclean:
277 result[6] = []
279 result[6] = []
278 self.lfstatus = True
280 self.lfstatus = True
279 return result
281 return result
280
282
281 # As part of committing, copy all of the largefiles into the
283 # As part of committing, copy all of the largefiles into the
282 # cache.
284 # cache.
283 def commitctx(self, *args, **kwargs):
285 def commitctx(self, *args, **kwargs):
284 node = super(lfilesrepo, self).commitctx(*args, **kwargs)
286 node = super(lfilesrepo, self).commitctx(*args, **kwargs)
285 lfutil.copyalltostore(self, node)
287 lfutil.copyalltostore(self, node)
286 return node
288 return node
287
289
288 # Before commit, largefile standins have not had their
290 # Before commit, largefile standins have not had their
289 # contents updated to reflect the hash of their largefile.
291 # contents updated to reflect the hash of their largefile.
290 # Do that here.
292 # Do that here.
291 def commit(self, text="", user=None, date=None, match=None,
293 def commit(self, text="", user=None, date=None, match=None,
292 force=False, editor=False, extra={}):
294 force=False, editor=False, extra={}):
293 orig = super(lfilesrepo, self).commit
295 orig = super(lfilesrepo, self).commit
294
296
295 wlock = self.wlock()
297 wlock = self.wlock()
296 try:
298 try:
297 # Case 0: Rebase or Transplant
299 # Case 0: Rebase or Transplant
298 # We have to take the time to pull down the new largefiles now.
300 # We have to take the time to pull down the new largefiles now.
299 # Otherwise, any largefiles that were modified in the
301 # Otherwise, any largefiles that were modified in the
300 # destination changesets get overwritten, either by the rebase
302 # destination changesets get overwritten, either by the rebase
301 # or in the first commit after the rebase or transplant.
303 # or in the first commit after the rebase or transplant.
302 # updatelfiles will update the dirstate to mark any pulled
304 # updatelfiles will update the dirstate to mark any pulled
303 # largefiles as modified
305 # largefiles as modified
304 if getattr(self, "_isrebasing", False) or \
306 if getattr(self, "_isrebasing", False) or \
305 getattr(self, "_istransplanting", False):
307 getattr(self, "_istransplanting", False):
306 lfcommands.updatelfiles(self.ui, self, filelist=None,
308 lfcommands.updatelfiles(self.ui, self, filelist=None,
307 printmessage=False)
309 printmessage=False)
308 result = orig(text=text, user=user, date=date, match=match,
310 result = orig(text=text, user=user, date=date, match=match,
309 force=force, editor=editor, extra=extra)
311 force=force, editor=editor, extra=extra)
310 return result
312 return result
311 # Case 1: user calls commit with no specific files or
313 # Case 1: user calls commit with no specific files or
312 # include/exclude patterns: refresh and commit all files that
314 # include/exclude patterns: refresh and commit all files that
313 # are "dirty".
315 # are "dirty".
314 if ((match is None) or
316 if ((match is None) or
315 (not match.anypats() and not match.files())):
317 (not match.anypats() and not match.files())):
316 # Spend a bit of time here to get a list of files we know
318 # Spend a bit of time here to get a list of files we know
317 # are modified so we can compare only against those.
319 # are modified so we can compare only against those.
318 # It can cost a lot of time (several seconds)
320 # It can cost a lot of time (several seconds)
319 # otherwise to update all standins if the largefiles are
321 # otherwise to update all standins if the largefiles are
320 # large.
322 # large.
321 lfdirstate = lfutil.openlfdirstate(ui, self)
323 lfdirstate = lfutil.openlfdirstate(ui, self)
322 dirtymatch = match_.always(self.root, self.getcwd())
324 dirtymatch = match_.always(self.root, self.getcwd())
323 s = lfdirstate.status(dirtymatch, [], False, False, False)
325 s = lfdirstate.status(dirtymatch, [], False, False, False)
324 modifiedfiles = []
326 modifiedfiles = []
325 for i in s:
327 for i in s:
326 modifiedfiles.extend(i)
328 modifiedfiles.extend(i)
327 lfiles = lfutil.listlfiles(self)
329 lfiles = lfutil.listlfiles(self)
328 # this only loops through largefiles that exist (not
330 # this only loops through largefiles that exist (not
329 # removed/renamed)
331 # removed/renamed)
330 for lfile in lfiles:
332 for lfile in lfiles:
331 if lfile in modifiedfiles:
333 if lfile in modifiedfiles:
332 if os.path.exists(
334 if os.path.exists(
333 self.wjoin(lfutil.standin(lfile))):
335 self.wjoin(lfutil.standin(lfile))):
334 # this handles the case where a rebase is being
336 # this handles the case where a rebase is being
335 # performed and the working copy is not updated
337 # performed and the working copy is not updated
336 # yet.
338 # yet.
337 if os.path.exists(self.wjoin(lfile)):
339 if os.path.exists(self.wjoin(lfile)):
338 lfutil.updatestandin(self,
340 lfutil.updatestandin(self,
339 lfutil.standin(lfile))
341 lfutil.standin(lfile))
340 lfdirstate.normal(lfile)
342 lfdirstate.normal(lfile)
341
343
342 result = orig(text=text, user=user, date=date, match=match,
344 result = orig(text=text, user=user, date=date, match=match,
343 force=force, editor=editor, extra=extra)
345 force=force, editor=editor, extra=extra)
344
346
345 if result is not None:
347 if result is not None:
346 for lfile in lfdirstate:
348 for lfile in lfdirstate:
347 if lfile in modifiedfiles:
349 if lfile in modifiedfiles:
348 if (not os.path.exists(self.wjoin(
350 if (not os.path.exists(self.wjoin(
349 lfutil.standin(lfile)))) or \
351 lfutil.standin(lfile)))) or \
350 (not os.path.exists(self.wjoin(lfile))):
352 (not os.path.exists(self.wjoin(lfile))):
351 lfdirstate.drop(lfile)
353 lfdirstate.drop(lfile)
352
354
353 # This needs to be after commit; otherwise precommit hooks
355 # This needs to be after commit; otherwise precommit hooks
354 # get the wrong status
356 # get the wrong status
355 lfdirstate.write()
357 lfdirstate.write()
356 return result
358 return result
357
359
358 lfiles = lfutil.listlfiles(self)
360 lfiles = lfutil.listlfiles(self)
359 match._files = self._subdirlfs(match.files(), lfiles)
361 match._files = self._subdirlfs(match.files(), lfiles)
360
362
361 # Case 2: user calls commit with specified patterns: refresh
363 # Case 2: user calls commit with specified patterns: refresh
362 # any matching big files.
364 # any matching big files.
363 smatcher = lfutil.composestandinmatcher(self, match)
365 smatcher = lfutil.composestandinmatcher(self, match)
364 standins = lfutil.dirstatewalk(self.dirstate, smatcher)
366 standins = lfutil.dirstatewalk(self.dirstate, smatcher)
365
367
366 # No matching big files: get out of the way and pass control to
368 # No matching big files: get out of the way and pass control to
367 # the usual commit() method.
369 # the usual commit() method.
368 if not standins:
370 if not standins:
369 return orig(text=text, user=user, date=date, match=match,
371 return orig(text=text, user=user, date=date, match=match,
370 force=force, editor=editor, extra=extra)
372 force=force, editor=editor, extra=extra)
371
373
372 # Refresh all matching big files. It's possible that the
374 # Refresh all matching big files. It's possible that the
373 # commit will end up failing, in which case the big files will
375 # commit will end up failing, in which case the big files will
374 # stay refreshed. No harm done: the user modified them and
376 # stay refreshed. No harm done: the user modified them and
375 # asked to commit them, so sooner or later we're going to
377 # asked to commit them, so sooner or later we're going to
376 # refresh the standins. Might as well leave them refreshed.
378 # refresh the standins. Might as well leave them refreshed.
377 lfdirstate = lfutil.openlfdirstate(ui, self)
379 lfdirstate = lfutil.openlfdirstate(ui, self)
378 for standin in standins:
380 for standin in standins:
379 lfile = lfutil.splitstandin(standin)
381 lfile = lfutil.splitstandin(standin)
380 if lfdirstate[lfile] <> 'r':
382 if lfdirstate[lfile] <> 'r':
381 lfutil.updatestandin(self, standin)
383 lfutil.updatestandin(self, standin)
382 lfdirstate.normal(lfile)
384 lfdirstate.normal(lfile)
383 else:
385 else:
384 lfdirstate.drop(lfile)
386 lfdirstate.drop(lfile)
385
387
386 # Cook up a new matcher that only matches regular files or
388 # Cook up a new matcher that only matches regular files or
387 # standins corresponding to the big files requested by the
389 # standins corresponding to the big files requested by the
388 # user. Have to modify _files to prevent commit() from
390 # user. Have to modify _files to prevent commit() from
389 # complaining "not tracked" for big files.
391 # complaining "not tracked" for big files.
390 match = copy.copy(match)
392 match = copy.copy(match)
391 origmatchfn = match.matchfn
393 origmatchfn = match.matchfn
392
394
393 # Check both the list of largefiles and the list of
395 # Check both the list of largefiles and the list of
394 # standins because if a largefile was removed, it
396 # standins because if a largefile was removed, it
395 # won't be in the list of largefiles at this point
397 # won't be in the list of largefiles at this point
396 match._files += sorted(standins)
398 match._files += sorted(standins)
397
399
398 actualfiles = []
400 actualfiles = []
399 for f in match._files:
401 for f in match._files:
400 fstandin = lfutil.standin(f)
402 fstandin = lfutil.standin(f)
401
403
402 # ignore known largefiles and standins
404 # ignore known largefiles and standins
403 if f in lfiles or fstandin in standins:
405 if f in lfiles or fstandin in standins:
404 continue
406 continue
405
407
406 # append directory separator to avoid collisions
408 # append directory separator to avoid collisions
407 if not fstandin.endswith(os.sep):
409 if not fstandin.endswith(os.sep):
408 fstandin += os.sep
410 fstandin += os.sep
409
411
410 actualfiles.append(f)
412 actualfiles.append(f)
411 match._files = actualfiles
413 match._files = actualfiles
412
414
413 def matchfn(f):
415 def matchfn(f):
414 if origmatchfn(f):
416 if origmatchfn(f):
415 return f not in lfiles
417 return f not in lfiles
416 else:
418 else:
417 return f in standins
419 return f in standins
418
420
419 match.matchfn = matchfn
421 match.matchfn = matchfn
420 result = orig(text=text, user=user, date=date, match=match,
422 result = orig(text=text, user=user, date=date, match=match,
421 force=force, editor=editor, extra=extra)
423 force=force, editor=editor, extra=extra)
422 # This needs to be after commit; otherwise precommit hooks
424 # This needs to be after commit; otherwise precommit hooks
423 # get the wrong status
425 # get the wrong status
424 lfdirstate.write()
426 lfdirstate.write()
425 return result
427 return result
426 finally:
428 finally:
427 wlock.release()
429 wlock.release()
428
430
429 def push(self, remote, force=False, revs=None, newbranch=False):
431 def push(self, remote, force=False, revs=None, newbranch=False):
430 o = lfutil.findoutgoing(self, remote, force)
432 o = lfutil.findoutgoing(self, remote, force)
431 if o:
433 if o:
432 toupload = set()
434 toupload = set()
433 o = self.changelog.nodesbetween(o, revs)[0]
435 o = self.changelog.nodesbetween(o, revs)[0]
434 for n in o:
436 for n in o:
435 parents = [p for p in self.changelog.parents(n)
437 parents = [p for p in self.changelog.parents(n)
436 if p != node_.nullid]
438 if p != node_.nullid]
437 ctx = self[n]
439 ctx = self[n]
438 files = set(ctx.files())
440 files = set(ctx.files())
439 if len(parents) == 2:
441 if len(parents) == 2:
440 mc = ctx.manifest()
442 mc = ctx.manifest()
441 mp1 = ctx.parents()[0].manifest()
443 mp1 = ctx.parents()[0].manifest()
442 mp2 = ctx.parents()[1].manifest()
444 mp2 = ctx.parents()[1].manifest()
443 for f in mp1:
445 for f in mp1:
444 if f not in mc:
446 if f not in mc:
445 files.add(f)
447 files.add(f)
446 for f in mp2:
448 for f in mp2:
447 if f not in mc:
449 if f not in mc:
448 files.add(f)
450 files.add(f)
449 for f in mc:
451 for f in mc:
450 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f,
452 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f,
451 None):
453 None):
452 files.add(f)
454 files.add(f)
453
455
454 toupload = toupload.union(
456 toupload = toupload.union(
455 set([ctx[f].data().strip()
457 set([ctx[f].data().strip()
456 for f in files
458 for f in files
457 if lfutil.isstandin(f) and f in ctx]))
459 if lfutil.isstandin(f) and f in ctx]))
458 lfcommands.uploadlfiles(ui, self, remote, toupload)
460 lfcommands.uploadlfiles(ui, self, remote, toupload)
459 return super(lfilesrepo, self).push(remote, force, revs,
461 return super(lfilesrepo, self).push(remote, force, revs,
460 newbranch)
462 newbranch)
461
463
462 def _subdirlfs(self, files, lfiles):
464 def _subdirlfs(self, files, lfiles):
463 '''
465 '''
464 Adjust matched file list
466 Adjust matched file list
465 If we pass a directory to commit whose only commitable files
467 If we pass a directory to commit whose only commitable files
466 are largefiles, the core commit code aborts before finding
468 are largefiles, the core commit code aborts before finding
467 the largefiles.
469 the largefiles.
468 So we do the following:
470 So we do the following:
469 For directories that only have largefiles as matches,
471 For directories that only have largefiles as matches,
470 we explicitly add the largefiles to the matchlist and remove
472 we explicitly add the largefiles to the matchlist and remove
471 the directory.
473 the directory.
472 In other cases, we leave the match list unmodified.
474 In other cases, we leave the match list unmodified.
473 '''
475 '''
474 actualfiles = []
476 actualfiles = []
475 dirs = []
477 dirs = []
476 regulars = []
478 regulars = []
477
479
478 for f in files:
480 for f in files:
479 if lfutil.isstandin(f + '/'):
481 if lfutil.isstandin(f + '/'):
480 raise util.Abort(
482 raise util.Abort(
481 _('file "%s" is a largefile standin') % f,
483 _('file "%s" is a largefile standin') % f,
482 hint=('commit the largefile itself instead'))
484 hint=('commit the largefile itself instead'))
483 # Scan directories
485 # Scan directories
484 if os.path.isdir(self.wjoin(f)):
486 if os.path.isdir(self.wjoin(f)):
485 dirs.append(f)
487 dirs.append(f)
486 else:
488 else:
487 regulars.append(f)
489 regulars.append(f)
488
490
489 for f in dirs:
491 for f in dirs:
490 matcheddir = False
492 matcheddir = False
491 d = self.dirstate.normalize(f) + '/'
493 d = self.dirstate.normalize(f) + '/'
492 # Check for matched normal files
494 # Check for matched normal files
493 for mf in regulars:
495 for mf in regulars:
494 if self.dirstate.normalize(mf).startswith(d):
496 if self.dirstate.normalize(mf).startswith(d):
495 actualfiles.append(f)
497 actualfiles.append(f)
496 matcheddir = True
498 matcheddir = True
497 break
499 break
498 if not matcheddir:
500 if not matcheddir:
499 # If no normal match, manually append
501 # If no normal match, manually append
500 # any matching largefiles
502 # any matching largefiles
501 for lf in lfiles:
503 for lf in lfiles:
502 if self.dirstate.normalize(lf).startswith(d):
504 if self.dirstate.normalize(lf).startswith(d):
503 actualfiles.append(lf)
505 actualfiles.append(lf)
504 if not matcheddir:
506 if not matcheddir:
505 actualfiles.append(lfutil.standin(f))
507 actualfiles.append(lfutil.standin(f))
506 matcheddir = True
508 matcheddir = True
507 # Nothing in dir, so readd it
509 # Nothing in dir, so readd it
508 # and let commit reject it
510 # and let commit reject it
509 if not matcheddir:
511 if not matcheddir:
510 actualfiles.append(f)
512 actualfiles.append(f)
511
513
512 # Always add normal files
514 # Always add normal files
513 actualfiles += regulars
515 actualfiles += regulars
514 return actualfiles
516 return actualfiles
515
517
516 repo.__class__ = lfilesrepo
518 repo.__class__ = lfilesrepo
517
519
518 def checkrequireslfiles(ui, repo, **kwargs):
520 def checkrequireslfiles(ui, repo, **kwargs):
519 if 'largefiles' not in repo.requirements and util.any(
521 if 'largefiles' not in repo.requirements and util.any(
520 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
522 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
521 repo.requirements.add('largefiles')
523 repo.requirements.add('largefiles')
522 repo._writerequirements()
524 repo._writerequirements()
523
525
524 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles)
526 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles)
525 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles)
527 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles)
General Comments 0
You need to be logged in to leave comments. Login now