##// END OF EJS Templates
largefiles: remove pre-1.7 compatibility code...
Martin Geisler -
r15626:931dc4af default
parent child Browse files
Show More
@@ -1,828 +1,825 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
10
10
11 import os
11 import os
12 import copy
12 import copy
13
13
14 from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
14 from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
15 node, archival, error, merge
15 node, archival, error, merge
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17 from mercurial.node import hex
17 from mercurial.node import hex
18 from hgext import rebase
18 from hgext import rebase
19
19
20 import lfutil
20 import lfutil
21 import lfcommands
21 import lfcommands
22
22
23 def installnormalfilesmatchfn(manifest):
23 def installnormalfilesmatchfn(manifest):
24 '''overrides scmutil.match so that the matcher it returns will ignore all
24 '''overrides scmutil.match so that the matcher it returns will ignore all
25 largefiles'''
25 largefiles'''
26 oldmatch = None # for the closure
26 oldmatch = None # for the closure
27 def override_match(ctx, pats=[], opts={}, globbed=False,
27 def override_match(ctx, pats=[], opts={}, globbed=False,
28 default='relpath'):
28 default='relpath'):
29 match = oldmatch(ctx, pats, opts, globbed, default)
29 match = oldmatch(ctx, pats, opts, globbed, default)
30 m = copy.copy(match)
30 m = copy.copy(match)
31 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
31 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
32 manifest)
32 manifest)
33 m._files = filter(notlfile, m._files)
33 m._files = filter(notlfile, m._files)
34 m._fmap = set(m._files)
34 m._fmap = set(m._files)
35 orig_matchfn = m.matchfn
35 orig_matchfn = m.matchfn
36 m.matchfn = lambda f: notlfile(f) and orig_matchfn(f) or None
36 m.matchfn = lambda f: notlfile(f) and orig_matchfn(f) or None
37 return m
37 return m
38 oldmatch = installmatchfn(override_match)
38 oldmatch = installmatchfn(override_match)
39
39
40 def installmatchfn(f):
40 def installmatchfn(f):
41 oldmatch = scmutil.match
41 oldmatch = scmutil.match
42 setattr(f, 'oldmatch', oldmatch)
42 setattr(f, 'oldmatch', oldmatch)
43 scmutil.match = f
43 scmutil.match = f
44 return oldmatch
44 return oldmatch
45
45
46 def restorematchfn():
46 def restorematchfn():
47 '''restores scmutil.match to what it was before installnormalfilesmatchfn
47 '''restores scmutil.match to what it was before installnormalfilesmatchfn
48 was called. no-op if scmutil.match is its original function.
48 was called. no-op if scmutil.match is its original function.
49
49
50 Note that n calls to installnormalfilesmatchfn will require n calls to
50 Note that n calls to installnormalfilesmatchfn will require n calls to
51 restore matchfn to reverse'''
51 restore matchfn to reverse'''
52 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
52 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
53
53
54 # -- Wrappers: modify existing commands --------------------------------
54 # -- Wrappers: modify existing commands --------------------------------
55
55
56 # Add works by going through the files that the user wanted to add and
56 # Add works by going through the files that the user wanted to add and
57 # checking if they should be added as largefiles. Then it makes a new
57 # checking if they should be added as largefiles. Then it makes a new
58 # matcher which matches only the normal files and runs the original
58 # matcher which matches only the normal files and runs the original
59 # version of add.
59 # version of add.
60 def override_add(orig, ui, repo, *pats, **opts):
60 def override_add(orig, ui, repo, *pats, **opts):
61 large = opts.pop('large', None)
61 large = opts.pop('large', None)
62 lfsize = lfutil.getminsize(
62 lfsize = lfutil.getminsize(
63 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
63 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
64
64
65 lfmatcher = None
65 lfmatcher = None
66 if os.path.exists(repo.wjoin(lfutil.shortname)):
66 if os.path.exists(repo.wjoin(lfutil.shortname)):
67 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
67 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
68 if lfpats:
68 if lfpats:
69 lfmatcher = match_.match(repo.root, '', list(lfpats))
69 lfmatcher = match_.match(repo.root, '', list(lfpats))
70
70
71 lfnames = []
71 lfnames = []
72 m = scmutil.match(repo[None], pats, opts)
72 m = scmutil.match(repo[None], pats, opts)
73 m.bad = lambda x, y: None
73 m.bad = lambda x, y: None
74 wctx = repo[None]
74 wctx = repo[None]
75 for f in repo.walk(m):
75 for f in repo.walk(m):
76 exact = m.exact(f)
76 exact = m.exact(f)
77 lfile = lfutil.standin(f) in wctx
77 lfile = lfutil.standin(f) in wctx
78 nfile = f in wctx
78 nfile = f in wctx
79 exists = lfile or nfile
79 exists = lfile or nfile
80
80
81 # Don't warn the user when they attempt to add a normal tracked file.
81 # Don't warn the user when they attempt to add a normal tracked file.
82 # The normal add code will do that for us.
82 # The normal add code will do that for us.
83 if exact and exists:
83 if exact and exists:
84 if lfile:
84 if lfile:
85 ui.warn(_('%s already a largefile\n') % f)
85 ui.warn(_('%s already a largefile\n') % f)
86 continue
86 continue
87
87
88 if exact or not exists:
88 if exact or not exists:
89 abovemin = (lfsize and
89 abovemin = (lfsize and
90 os.lstat(repo.wjoin(f)).st_size >= lfsize * 1024 * 1024)
90 os.lstat(repo.wjoin(f)).st_size >= lfsize * 1024 * 1024)
91 if large or abovemin or (lfmatcher and lfmatcher(f)):
91 if large or abovemin or (lfmatcher and lfmatcher(f)):
92 lfnames.append(f)
92 lfnames.append(f)
93 if ui.verbose or not exact:
93 if ui.verbose or not exact:
94 ui.status(_('adding %s as a largefile\n') % m.rel(f))
94 ui.status(_('adding %s as a largefile\n') % m.rel(f))
95
95
96 bad = []
96 bad = []
97 standins = []
97 standins = []
98
98
99 # Need to lock, otherwise there could be a race condition between
99 # Need to lock, otherwise there could be a race condition between
100 # when standins are created and added to the repo.
100 # when standins are created and added to the repo.
101 wlock = repo.wlock()
101 wlock = repo.wlock()
102 try:
102 try:
103 if not opts.get('dry_run'):
103 if not opts.get('dry_run'):
104 lfdirstate = lfutil.openlfdirstate(ui, repo)
104 lfdirstate = lfutil.openlfdirstate(ui, repo)
105 for f in lfnames:
105 for f in lfnames:
106 standinname = lfutil.standin(f)
106 standinname = lfutil.standin(f)
107 lfutil.writestandin(repo, standinname, hash='',
107 lfutil.writestandin(repo, standinname, hash='',
108 executable=lfutil.getexecutable(repo.wjoin(f)))
108 executable=lfutil.getexecutable(repo.wjoin(f)))
109 standins.append(standinname)
109 standins.append(standinname)
110 if lfdirstate[f] == 'r':
110 if lfdirstate[f] == 'r':
111 lfdirstate.normallookup(f)
111 lfdirstate.normallookup(f)
112 else:
112 else:
113 lfdirstate.add(f)
113 lfdirstate.add(f)
114 lfdirstate.write()
114 lfdirstate.write()
115 bad += [lfutil.splitstandin(f)
115 bad += [lfutil.splitstandin(f)
116 for f in lfutil.repo_add(repo, standins)
116 for f in lfutil.repo_add(repo, standins)
117 if f in m.files()]
117 if f in m.files()]
118 finally:
118 finally:
119 wlock.release()
119 wlock.release()
120
120
121 installnormalfilesmatchfn(repo[None].manifest())
121 installnormalfilesmatchfn(repo[None].manifest())
122 result = orig(ui, repo, *pats, **opts)
122 result = orig(ui, repo, *pats, **opts)
123 restorematchfn()
123 restorematchfn()
124
124
125 return (result == 1 or bad) and 1 or 0
125 return (result == 1 or bad) and 1 or 0
126
126
127 def override_remove(orig, ui, repo, *pats, **opts):
127 def override_remove(orig, ui, repo, *pats, **opts):
128 manifest = repo[None].manifest()
128 manifest = repo[None].manifest()
129 installnormalfilesmatchfn(manifest)
129 installnormalfilesmatchfn(manifest)
130 orig(ui, repo, *pats, **opts)
130 orig(ui, repo, *pats, **opts)
131 restorematchfn()
131 restorematchfn()
132
132
133 after, force = opts.get('after'), opts.get('force')
133 after, force = opts.get('after'), opts.get('force')
134 if not pats and not after:
134 if not pats and not after:
135 raise util.Abort(_('no files specified'))
135 raise util.Abort(_('no files specified'))
136 m = scmutil.match(repo[None], pats, opts)
136 m = scmutil.match(repo[None], pats, opts)
137 try:
137 try:
138 repo.lfstatus = True
138 repo.lfstatus = True
139 s = repo.status(match=m, clean=True)
139 s = repo.status(match=m, clean=True)
140 finally:
140 finally:
141 repo.lfstatus = False
141 repo.lfstatus = False
142 modified, added, deleted, clean = [[f for f in list
142 modified, added, deleted, clean = [[f for f in list
143 if lfutil.standin(f) in manifest]
143 if lfutil.standin(f) in manifest]
144 for list in [s[0], s[1], s[3], s[6]]]
144 for list in [s[0], s[1], s[3], s[6]]]
145
145
146 def warn(files, reason):
146 def warn(files, reason):
147 for f in files:
147 for f in files:
148 ui.warn(_('not removing %s: %s (use -f to force removal)\n')
148 ui.warn(_('not removing %s: %s (use -f to force removal)\n')
149 % (m.rel(f), reason))
149 % (m.rel(f), reason))
150
150
151 if force:
151 if force:
152 remove, forget = modified + deleted + clean, added
152 remove, forget = modified + deleted + clean, added
153 elif after:
153 elif after:
154 remove, forget = deleted, []
154 remove, forget = deleted, []
155 warn(modified + added + clean, _('file still exists'))
155 warn(modified + added + clean, _('file still exists'))
156 else:
156 else:
157 remove, forget = deleted + clean, []
157 remove, forget = deleted + clean, []
158 warn(modified, _('file is modified'))
158 warn(modified, _('file is modified'))
159 warn(added, _('file has been marked for add'))
159 warn(added, _('file has been marked for add'))
160
160
161 for f in sorted(remove + forget):
161 for f in sorted(remove + forget):
162 if ui.verbose or not m.exact(f):
162 if ui.verbose or not m.exact(f):
163 ui.status(_('removing %s\n') % m.rel(f))
163 ui.status(_('removing %s\n') % m.rel(f))
164
164
165 # Need to lock because standin files are deleted then removed from the
165 # Need to lock because standin files are deleted then removed from the
166 # repository and we could race inbetween.
166 # repository and we could race inbetween.
167 wlock = repo.wlock()
167 wlock = repo.wlock()
168 try:
168 try:
169 lfdirstate = lfutil.openlfdirstate(ui, repo)
169 lfdirstate = lfutil.openlfdirstate(ui, repo)
170 for f in remove:
170 for f in remove:
171 if not after:
171 if not after:
172 os.unlink(repo.wjoin(f))
172 os.unlink(repo.wjoin(f))
173 currentdir = os.path.split(f)[0]
173 currentdir = os.path.split(f)[0]
174 while currentdir and not os.listdir(repo.wjoin(currentdir)):
174 while currentdir and not os.listdir(repo.wjoin(currentdir)):
175 os.rmdir(repo.wjoin(currentdir))
175 os.rmdir(repo.wjoin(currentdir))
176 currentdir = os.path.split(currentdir)[0]
176 currentdir = os.path.split(currentdir)[0]
177 lfdirstate.remove(f)
177 lfdirstate.remove(f)
178 lfdirstate.write()
178 lfdirstate.write()
179
179
180 forget = [lfutil.standin(f) for f in forget]
180 forget = [lfutil.standin(f) for f in forget]
181 remove = [lfutil.standin(f) for f in remove]
181 remove = [lfutil.standin(f) for f in remove]
182 lfutil.repo_forget(repo, forget)
182 lfutil.repo_forget(repo, forget)
183 lfutil.repo_remove(repo, remove, unlink=True)
183 lfutil.repo_remove(repo, remove, unlink=True)
184 finally:
184 finally:
185 wlock.release()
185 wlock.release()
186
186
187 def override_status(orig, ui, repo, *pats, **opts):
187 def override_status(orig, ui, repo, *pats, **opts):
188 try:
188 try:
189 repo.lfstatus = True
189 repo.lfstatus = True
190 return orig(ui, repo, *pats, **opts)
190 return orig(ui, repo, *pats, **opts)
191 finally:
191 finally:
192 repo.lfstatus = False
192 repo.lfstatus = False
193
193
194 def override_log(orig, ui, repo, *pats, **opts):
194 def override_log(orig, ui, repo, *pats, **opts):
195 try:
195 try:
196 repo.lfstatus = True
196 repo.lfstatus = True
197 orig(ui, repo, *pats, **opts)
197 orig(ui, repo, *pats, **opts)
198 finally:
198 finally:
199 repo.lfstatus = False
199 repo.lfstatus = False
200
200
201 def override_verify(orig, ui, repo, *pats, **opts):
201 def override_verify(orig, ui, repo, *pats, **opts):
202 large = opts.pop('large', False)
202 large = opts.pop('large', False)
203 all = opts.pop('lfa', False)
203 all = opts.pop('lfa', False)
204 contents = opts.pop('lfc', False)
204 contents = opts.pop('lfc', False)
205
205
206 result = orig(ui, repo, *pats, **opts)
206 result = orig(ui, repo, *pats, **opts)
207 if large:
207 if large:
208 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
208 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
209 return result
209 return result
210
210
211 # Override needs to refresh standins so that update's normal merge
211 # Override needs to refresh standins so that update's normal merge
212 # will go through properly. Then the other update hook (overriding repo.update)
212 # will go through properly. Then the other update hook (overriding repo.update)
213 # will get the new files. Filemerge is also overriden so that the merge
213 # will get the new files. Filemerge is also overriden so that the merge
214 # will merge standins correctly.
214 # will merge standins correctly.
215 def override_update(orig, ui, repo, *pats, **opts):
215 def override_update(orig, ui, repo, *pats, **opts):
216 lfdirstate = lfutil.openlfdirstate(ui, repo)
216 lfdirstate = lfutil.openlfdirstate(ui, repo)
217 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
217 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
218 False, False)
218 False, False)
219 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
219 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
220
220
221 # Need to lock between the standins getting updated and their
221 # Need to lock between the standins getting updated and their
222 # largefiles getting updated
222 # largefiles getting updated
223 wlock = repo.wlock()
223 wlock = repo.wlock()
224 try:
224 try:
225 if opts['check']:
225 if opts['check']:
226 mod = len(modified) > 0
226 mod = len(modified) > 0
227 for lfile in unsure:
227 for lfile in unsure:
228 standin = lfutil.standin(lfile)
228 standin = lfutil.standin(lfile)
229 if repo['.'][standin].data().strip() != \
229 if repo['.'][standin].data().strip() != \
230 lfutil.hashfile(repo.wjoin(lfile)):
230 lfutil.hashfile(repo.wjoin(lfile)):
231 mod = True
231 mod = True
232 else:
232 else:
233 lfdirstate.normal(lfile)
233 lfdirstate.normal(lfile)
234 lfdirstate.write()
234 lfdirstate.write()
235 if mod:
235 if mod:
236 raise util.Abort(_('uncommitted local changes'))
236 raise util.Abort(_('uncommitted local changes'))
237 # XXX handle removed differently
237 # XXX handle removed differently
238 if not opts['clean']:
238 if not opts['clean']:
239 for lfile in unsure + modified + added:
239 for lfile in unsure + modified + added:
240 lfutil.updatestandin(repo, lfutil.standin(lfile))
240 lfutil.updatestandin(repo, lfutil.standin(lfile))
241 finally:
241 finally:
242 wlock.release()
242 wlock.release()
243 return orig(ui, repo, *pats, **opts)
243 return orig(ui, repo, *pats, **opts)
244
244
245 # Override filemerge to prompt the user about how they wish to merge
245 # Override filemerge to prompt the user about how they wish to merge
246 # largefiles. This will handle identical edits, and copy/rename +
246 # largefiles. This will handle identical edits, and copy/rename +
247 # edit without prompting the user.
247 # edit without prompting the user.
248 def override_filemerge(origfn, repo, mynode, orig, fcd, fco, fca):
248 def override_filemerge(origfn, repo, mynode, orig, fcd, fco, fca):
249 # Use better variable names here. Because this is a wrapper we cannot
249 # Use better variable names here. Because this is a wrapper we cannot
250 # change the variable names in the function declaration.
250 # change the variable names in the function declaration.
251 fcdest, fcother, fcancestor = fcd, fco, fca
251 fcdest, fcother, fcancestor = fcd, fco, fca
252 if not lfutil.isstandin(orig):
252 if not lfutil.isstandin(orig):
253 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
253 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
254 else:
254 else:
255 if not fcother.cmp(fcdest): # files identical?
255 if not fcother.cmp(fcdest): # files identical?
256 return None
256 return None
257
257
258 # backwards, use working dir parent as ancestor
258 # backwards, use working dir parent as ancestor
259 if fcancestor == fcother:
259 if fcancestor == fcother:
260 fcancestor = fcdest.parents()[0]
260 fcancestor = fcdest.parents()[0]
261
261
262 if orig != fcother.path():
262 if orig != fcother.path():
263 repo.ui.status(_('merging %s and %s to %s\n')
263 repo.ui.status(_('merging %s and %s to %s\n')
264 % (lfutil.splitstandin(orig),
264 % (lfutil.splitstandin(orig),
265 lfutil.splitstandin(fcother.path()),
265 lfutil.splitstandin(fcother.path()),
266 lfutil.splitstandin(fcdest.path())))
266 lfutil.splitstandin(fcdest.path())))
267 else:
267 else:
268 repo.ui.status(_('merging %s\n')
268 repo.ui.status(_('merging %s\n')
269 % lfutil.splitstandin(fcdest.path()))
269 % lfutil.splitstandin(fcdest.path()))
270
270
271 if fcancestor.path() != fcother.path() and fcother.data() == \
271 if fcancestor.path() != fcother.path() and fcother.data() == \
272 fcancestor.data():
272 fcancestor.data():
273 return 0
273 return 0
274 if fcancestor.path() != fcdest.path() and fcdest.data() == \
274 if fcancestor.path() != fcdest.path() and fcdest.data() == \
275 fcancestor.data():
275 fcancestor.data():
276 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
276 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
277 return 0
277 return 0
278
278
279 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
279 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
280 'keep (l)ocal or take (o)ther?') %
280 'keep (l)ocal or take (o)ther?') %
281 lfutil.splitstandin(orig),
281 lfutil.splitstandin(orig),
282 (_('&Local'), _('&Other')), 0) == 0:
282 (_('&Local'), _('&Other')), 0) == 0:
283 return 0
283 return 0
284 else:
284 else:
285 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
285 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
286 return 0
286 return 0
287
287
288 # Copy first changes the matchers to match standins instead of
288 # Copy first changes the matchers to match standins instead of
289 # largefiles. Then it overrides util.copyfile in that function it
289 # largefiles. Then it overrides util.copyfile in that function it
290 # checks if the destination largefile already exists. It also keeps a
290 # checks if the destination largefile already exists. It also keeps a
291 # list of copied files so that the largefiles can be copied and the
291 # list of copied files so that the largefiles can be copied and the
292 # dirstate updated.
292 # dirstate updated.
293 def override_copy(orig, ui, repo, pats, opts, rename=False):
293 def override_copy(orig, ui, repo, pats, opts, rename=False):
294 # doesn't remove largefile on rename
294 # doesn't remove largefile on rename
295 if len(pats) < 2:
295 if len(pats) < 2:
296 # this isn't legal, let the original function deal with it
296 # this isn't legal, let the original function deal with it
297 return orig(ui, repo, pats, opts, rename)
297 return orig(ui, repo, pats, opts, rename)
298
298
299 def makestandin(relpath):
299 def makestandin(relpath):
300 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
300 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
301 return os.path.join(repo.wjoin(lfutil.standin(path)))
301 return os.path.join(repo.wjoin(lfutil.standin(path)))
302
302
303 fullpats = scmutil.expandpats(pats)
303 fullpats = scmutil.expandpats(pats)
304 dest = fullpats[-1]
304 dest = fullpats[-1]
305
305
306 if os.path.isdir(dest):
306 if os.path.isdir(dest):
307 if not os.path.isdir(makestandin(dest)):
307 if not os.path.isdir(makestandin(dest)):
308 os.makedirs(makestandin(dest))
308 os.makedirs(makestandin(dest))
309 # This could copy both lfiles and normal files in one command,
309 # This could copy both lfiles and normal files in one command,
310 # but we don't want to do that. First replace their matcher to
310 # but we don't want to do that. First replace their matcher to
311 # only match normal files and run it, then replace it to just
311 # only match normal files and run it, then replace it to just
312 # match largefiles and run it again.
312 # match largefiles and run it again.
313 nonormalfiles = False
313 nonormalfiles = False
314 nolfiles = False
314 nolfiles = False
315 try:
315 try:
316 try:
316 try:
317 installnormalfilesmatchfn(repo[None].manifest())
317 installnormalfilesmatchfn(repo[None].manifest())
318 result = orig(ui, repo, pats, opts, rename)
318 result = orig(ui, repo, pats, opts, rename)
319 except util.Abort, e:
319 except util.Abort, e:
320 if str(e) != 'no files to copy':
320 if str(e) != 'no files to copy':
321 raise e
321 raise e
322 else:
322 else:
323 nonormalfiles = True
323 nonormalfiles = True
324 result = 0
324 result = 0
325 finally:
325 finally:
326 restorematchfn()
326 restorematchfn()
327
327
328 # The first rename can cause our current working directory to be removed.
328 # The first rename can cause our current working directory to be removed.
329 # In that case there is nothing left to copy/rename so just quit.
329 # In that case there is nothing left to copy/rename so just quit.
330 try:
330 try:
331 repo.getcwd()
331 repo.getcwd()
332 except OSError:
332 except OSError:
333 return result
333 return result
334
334
335 try:
335 try:
336 try:
336 try:
337 # When we call orig below it creates the standins but we don't add them
337 # When we call orig below it creates the standins but we don't add them
338 # to the dir state until later so lock during that time.
338 # to the dir state until later so lock during that time.
339 wlock = repo.wlock()
339 wlock = repo.wlock()
340
340
341 manifest = repo[None].manifest()
341 manifest = repo[None].manifest()
342 oldmatch = None # for the closure
342 oldmatch = None # for the closure
343 def override_match(ctx, pats=[], opts={}, globbed=False,
343 def override_match(ctx, pats=[], opts={}, globbed=False,
344 default='relpath'):
344 default='relpath'):
345 newpats = []
345 newpats = []
346 # The patterns were previously mangled to add the standin
346 # The patterns were previously mangled to add the standin
347 # directory; we need to remove that now
347 # directory; we need to remove that now
348 for pat in pats:
348 for pat in pats:
349 if match_.patkind(pat) is None and lfutil.shortname in pat:
349 if match_.patkind(pat) is None and lfutil.shortname in pat:
350 newpats.append(pat.replace(lfutil.shortname, ''))
350 newpats.append(pat.replace(lfutil.shortname, ''))
351 else:
351 else:
352 newpats.append(pat)
352 newpats.append(pat)
353 match = oldmatch(ctx, newpats, opts, globbed, default)
353 match = oldmatch(ctx, newpats, opts, globbed, default)
354 m = copy.copy(match)
354 m = copy.copy(match)
355 lfile = lambda f: lfutil.standin(f) in manifest
355 lfile = lambda f: lfutil.standin(f) in manifest
356 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
356 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
357 m._fmap = set(m._files)
357 m._fmap = set(m._files)
358 orig_matchfn = m.matchfn
358 orig_matchfn = m.matchfn
359 m.matchfn = lambda f: (lfutil.isstandin(f) and
359 m.matchfn = lambda f: (lfutil.isstandin(f) and
360 lfile(lfutil.splitstandin(f)) and
360 lfile(lfutil.splitstandin(f)) and
361 orig_matchfn(lfutil.splitstandin(f)) or
361 orig_matchfn(lfutil.splitstandin(f)) or
362 None)
362 None)
363 return m
363 return m
364 oldmatch = installmatchfn(override_match)
364 oldmatch = installmatchfn(override_match)
365 listpats = []
365 listpats = []
366 for pat in pats:
366 for pat in pats:
367 if match_.patkind(pat) is not None:
367 if match_.patkind(pat) is not None:
368 listpats.append(pat)
368 listpats.append(pat)
369 else:
369 else:
370 listpats.append(makestandin(pat))
370 listpats.append(makestandin(pat))
371
371
372 try:
372 try:
373 origcopyfile = util.copyfile
373 origcopyfile = util.copyfile
374 copiedfiles = []
374 copiedfiles = []
375 def override_copyfile(src, dest):
375 def override_copyfile(src, dest):
376 if (lfutil.shortname in src and
376 if (lfutil.shortname in src and
377 dest.startswith(repo.wjoin(lfutil.shortname))):
377 dest.startswith(repo.wjoin(lfutil.shortname))):
378 destlfile = dest.replace(lfutil.shortname, '')
378 destlfile = dest.replace(lfutil.shortname, '')
379 if not opts['force'] and os.path.exists(destlfile):
379 if not opts['force'] and os.path.exists(destlfile):
380 raise IOError('',
380 raise IOError('',
381 _('destination largefile already exists'))
381 _('destination largefile already exists'))
382 copiedfiles.append((src, dest))
382 copiedfiles.append((src, dest))
383 origcopyfile(src, dest)
383 origcopyfile(src, dest)
384
384
385 util.copyfile = override_copyfile
385 util.copyfile = override_copyfile
386 result += orig(ui, repo, listpats, opts, rename)
386 result += orig(ui, repo, listpats, opts, rename)
387 finally:
387 finally:
388 util.copyfile = origcopyfile
388 util.copyfile = origcopyfile
389
389
390 lfdirstate = lfutil.openlfdirstate(ui, repo)
390 lfdirstate = lfutil.openlfdirstate(ui, repo)
391 for (src, dest) in copiedfiles:
391 for (src, dest) in copiedfiles:
392 if (lfutil.shortname in src and
392 if (lfutil.shortname in src and
393 dest.startswith(repo.wjoin(lfutil.shortname))):
393 dest.startswith(repo.wjoin(lfutil.shortname))):
394 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
394 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
395 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
395 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
396 destlfiledir = os.path.dirname(destlfile) or '.'
396 destlfiledir = os.path.dirname(destlfile) or '.'
397 if not os.path.isdir(destlfiledir):
397 if not os.path.isdir(destlfiledir):
398 os.makedirs(destlfiledir)
398 os.makedirs(destlfiledir)
399 if rename:
399 if rename:
400 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
400 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
401 lfdirstate.remove(srclfile)
401 lfdirstate.remove(srclfile)
402 else:
402 else:
403 util.copyfile(srclfile, destlfile)
403 util.copyfile(srclfile, destlfile)
404 lfdirstate.add(destlfile)
404 lfdirstate.add(destlfile)
405 lfdirstate.write()
405 lfdirstate.write()
406 except util.Abort, e:
406 except util.Abort, e:
407 if str(e) != 'no files to copy':
407 if str(e) != 'no files to copy':
408 raise e
408 raise e
409 else:
409 else:
410 nolfiles = True
410 nolfiles = True
411 finally:
411 finally:
412 restorematchfn()
412 restorematchfn()
413 wlock.release()
413 wlock.release()
414
414
415 if nolfiles and nonormalfiles:
415 if nolfiles and nonormalfiles:
416 raise util.Abort(_('no files to copy'))
416 raise util.Abort(_('no files to copy'))
417
417
418 return result
418 return result
419
419
420 # When the user calls revert, we have to be careful to not revert any
420 # When the user calls revert, we have to be careful to not revert any
421 # changes to other largefiles accidentally. This means we have to keep
421 # changes to other largefiles accidentally. This means we have to keep
422 # track of the largefiles that are being reverted so we only pull down
422 # track of the largefiles that are being reverted so we only pull down
423 # the necessary largefiles.
423 # the necessary largefiles.
424 #
424 #
425 # Standins are only updated (to match the hash of largefiles) before
425 # Standins are only updated (to match the hash of largefiles) before
426 # commits. Update the standins then run the original revert, changing
426 # commits. Update the standins then run the original revert, changing
427 # the matcher to hit standins instead of largefiles. Based on the
427 # the matcher to hit standins instead of largefiles. Based on the
428 # resulting standins update the largefiles. Then return the standins
428 # resulting standins update the largefiles. Then return the standins
429 # to their proper state
429 # to their proper state
430 def override_revert(orig, ui, repo, *pats, **opts):
430 def override_revert(orig, ui, repo, *pats, **opts):
431 # Because we put the standins in a bad state (by updating them)
431 # Because we put the standins in a bad state (by updating them)
432 # and then return them to a correct state we need to lock to
432 # and then return them to a correct state we need to lock to
433 # prevent others from changing them in their incorrect state.
433 # prevent others from changing them in their incorrect state.
434 wlock = repo.wlock()
434 wlock = repo.wlock()
435 try:
435 try:
436 lfdirstate = lfutil.openlfdirstate(ui, repo)
436 lfdirstate = lfutil.openlfdirstate(ui, repo)
437 (modified, added, removed, missing, unknown, ignored, clean) = \
437 (modified, added, removed, missing, unknown, ignored, clean) = \
438 lfutil.lfdirstate_status(lfdirstate, repo, repo['.'].rev())
438 lfutil.lfdirstate_status(lfdirstate, repo, repo['.'].rev())
439 for lfile in modified:
439 for lfile in modified:
440 lfutil.updatestandin(repo, lfutil.standin(lfile))
440 lfutil.updatestandin(repo, lfutil.standin(lfile))
441
441
442 try:
442 try:
443 ctx = repo[opts.get('rev')]
443 ctx = repo[opts.get('rev')]
444 oldmatch = None # for the closure
444 oldmatch = None # for the closure
445 def override_match(ctx, pats=[], opts={}, globbed=False,
445 def override_match(ctx, pats=[], opts={}, globbed=False,
446 default='relpath'):
446 default='relpath'):
447 match = oldmatch(ctx, pats, opts, globbed, default)
447 match = oldmatch(ctx, pats, opts, globbed, default)
448 m = copy.copy(match)
448 m = copy.copy(match)
449 def tostandin(f):
449 def tostandin(f):
450 if lfutil.standin(f) in ctx or lfutil.standin(f) in ctx:
450 if lfutil.standin(f) in ctx or lfutil.standin(f) in ctx:
451 return lfutil.standin(f)
451 return lfutil.standin(f)
452 elif lfutil.standin(f) in repo[None]:
452 elif lfutil.standin(f) in repo[None]:
453 return None
453 return None
454 return f
454 return f
455 m._files = [tostandin(f) for f in m._files]
455 m._files = [tostandin(f) for f in m._files]
456 m._files = [f for f in m._files if f is not None]
456 m._files = [f for f in m._files if f is not None]
457 m._fmap = set(m._files)
457 m._fmap = set(m._files)
458 orig_matchfn = m.matchfn
458 orig_matchfn = m.matchfn
459 def matchfn(f):
459 def matchfn(f):
460 if lfutil.isstandin(f):
460 if lfutil.isstandin(f):
461 # We need to keep track of what largefiles are being
461 # We need to keep track of what largefiles are being
462 # matched so we know which ones to update later --
462 # matched so we know which ones to update later --
463 # otherwise we accidentally revert changes to other
463 # otherwise we accidentally revert changes to other
464 # largefiles. This is repo-specific, so duckpunch the
464 # largefiles. This is repo-specific, so duckpunch the
465 # repo object to keep the list of largefiles for us
465 # repo object to keep the list of largefiles for us
466 # later.
466 # later.
467 if orig_matchfn(lfutil.splitstandin(f)) and \
467 if orig_matchfn(lfutil.splitstandin(f)) and \
468 (f in repo[None] or f in ctx):
468 (f in repo[None] or f in ctx):
469 lfileslist = getattr(repo, '_lfilestoupdate', [])
469 lfileslist = getattr(repo, '_lfilestoupdate', [])
470 lfileslist.append(lfutil.splitstandin(f))
470 lfileslist.append(lfutil.splitstandin(f))
471 repo._lfilestoupdate = lfileslist
471 repo._lfilestoupdate = lfileslist
472 return True
472 return True
473 else:
473 else:
474 return False
474 return False
475 return orig_matchfn(f)
475 return orig_matchfn(f)
476 m.matchfn = matchfn
476 m.matchfn = matchfn
477 return m
477 return m
478 oldmatch = installmatchfn(override_match)
478 oldmatch = installmatchfn(override_match)
479 scmutil.match
479 scmutil.match
480 matches = override_match(repo[None], pats, opts)
480 matches = override_match(repo[None], pats, opts)
481 orig(ui, repo, *pats, **opts)
481 orig(ui, repo, *pats, **opts)
482 finally:
482 finally:
483 restorematchfn()
483 restorematchfn()
484 lfileslist = getattr(repo, '_lfilestoupdate', [])
484 lfileslist = getattr(repo, '_lfilestoupdate', [])
485 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
485 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
486 printmessage=False)
486 printmessage=False)
487
487
488 # empty out the largefiles list so we start fresh next time
488 # empty out the largefiles list so we start fresh next time
489 repo._lfilestoupdate = []
489 repo._lfilestoupdate = []
490 for lfile in modified:
490 for lfile in modified:
491 if lfile in lfileslist:
491 if lfile in lfileslist:
492 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
492 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
493 in repo['.']:
493 in repo['.']:
494 lfutil.writestandin(repo, lfutil.standin(lfile),
494 lfutil.writestandin(repo, lfutil.standin(lfile),
495 repo['.'][lfile].data().strip(),
495 repo['.'][lfile].data().strip(),
496 'x' in repo['.'][lfile].flags())
496 'x' in repo['.'][lfile].flags())
497 lfdirstate = lfutil.openlfdirstate(ui, repo)
497 lfdirstate = lfutil.openlfdirstate(ui, repo)
498 for lfile in added:
498 for lfile in added:
499 standin = lfutil.standin(lfile)
499 standin = lfutil.standin(lfile)
500 if standin not in ctx and (standin in matches or opts.get('all')):
500 if standin not in ctx and (standin in matches or opts.get('all')):
501 if lfile in lfdirstate:
501 if lfile in lfdirstate:
502 lfdirstate.drop(lfile)
502 lfdirstate.drop(lfile)
503 util.unlinkpath(repo.wjoin(standin))
503 util.unlinkpath(repo.wjoin(standin))
504 lfdirstate.write()
504 lfdirstate.write()
505 finally:
505 finally:
506 wlock.release()
506 wlock.release()
507
507
508 def hg_update(orig, repo, node):
508 def hg_update(orig, repo, node):
509 result = orig(repo, node)
509 result = orig(repo, node)
510 # XXX check if it worked first
510 # XXX check if it worked first
511 lfcommands.updatelfiles(repo.ui, repo)
511 lfcommands.updatelfiles(repo.ui, repo)
512 return result
512 return result
513
513
514 def hg_clean(orig, repo, node, show_stats=True):
514 def hg_clean(orig, repo, node, show_stats=True):
515 result = orig(repo, node, show_stats)
515 result = orig(repo, node, show_stats)
516 lfcommands.updatelfiles(repo.ui, repo)
516 lfcommands.updatelfiles(repo.ui, repo)
517 return result
517 return result
518
518
519 def hg_merge(orig, repo, node, force=None, remind=True):
519 def hg_merge(orig, repo, node, force=None, remind=True):
520 result = orig(repo, node, force, remind)
520 result = orig(repo, node, force, remind)
521 lfcommands.updatelfiles(repo.ui, repo)
521 lfcommands.updatelfiles(repo.ui, repo)
522 return result
522 return result
523
523
524 # When we rebase a repository with remotely changed largefiles, we need to
524 # When we rebase a repository with remotely changed largefiles, we need to
525 # take some extra care so that the largefiles are correctly updated in the
525 # take some extra care so that the largefiles are correctly updated in the
526 # working copy
526 # working copy
527 def override_pull(orig, ui, repo, source=None, **opts):
527 def override_pull(orig, ui, repo, source=None, **opts):
528 if opts.get('rebase', False):
528 if opts.get('rebase', False):
529 repo._isrebasing = True
529 repo._isrebasing = True
530 try:
530 try:
531 if opts.get('update'):
531 if opts.get('update'):
532 del opts['update']
532 del opts['update']
533 ui.debug('--update and --rebase are not compatible, ignoring '
533 ui.debug('--update and --rebase are not compatible, ignoring '
534 'the update flag\n')
534 'the update flag\n')
535 del opts['rebase']
535 del opts['rebase']
536 cmdutil.bailifchanged(repo)
536 cmdutil.bailifchanged(repo)
537 revsprepull = len(repo)
537 revsprepull = len(repo)
538 origpostincoming = commands.postincoming
538 origpostincoming = commands.postincoming
539 def _dummy(*args, **kwargs):
539 def _dummy(*args, **kwargs):
540 pass
540 pass
541 commands.postincoming = _dummy
541 commands.postincoming = _dummy
542 repo.lfpullsource = source
542 repo.lfpullsource = source
543 if not source:
543 if not source:
544 source = 'default'
544 source = 'default'
545 try:
545 try:
546 result = commands.pull(ui, repo, source, **opts)
546 result = commands.pull(ui, repo, source, **opts)
547 finally:
547 finally:
548 commands.postincoming = origpostincoming
548 commands.postincoming = origpostincoming
549 revspostpull = len(repo)
549 revspostpull = len(repo)
550 if revspostpull > revsprepull:
550 if revspostpull > revsprepull:
551 result = result or rebase.rebase(ui, repo)
551 result = result or rebase.rebase(ui, repo)
552 finally:
552 finally:
553 repo._isrebasing = False
553 repo._isrebasing = False
554 else:
554 else:
555 repo.lfpullsource = source
555 repo.lfpullsource = source
556 if not source:
556 if not source:
557 source = 'default'
557 source = 'default'
558 result = orig(ui, repo, source, **opts)
558 result = orig(ui, repo, source, **opts)
559 return result
559 return result
560
560
561 def override_rebase(orig, ui, repo, **opts):
561 def override_rebase(orig, ui, repo, **opts):
562 repo._isrebasing = True
562 repo._isrebasing = True
563 try:
563 try:
564 orig(ui, repo, **opts)
564 orig(ui, repo, **opts)
565 finally:
565 finally:
566 repo._isrebasing = False
566 repo._isrebasing = False
567
567
568 def override_archive(orig, repo, dest, node, kind, decode=True, matchfn=None,
568 def override_archive(orig, repo, dest, node, kind, decode=True, matchfn=None,
569 prefix=None, mtime=None, subrepos=None):
569 prefix=None, mtime=None, subrepos=None):
570 # No need to lock because we are only reading history and
570 # No need to lock because we are only reading history and
571 # largefile caches, neither of which are modified.
571 # largefile caches, neither of which are modified.
572 lfcommands.cachelfiles(repo.ui, repo, node)
572 lfcommands.cachelfiles(repo.ui, repo, node)
573
573
574 if kind not in archival.archivers:
574 if kind not in archival.archivers:
575 raise util.Abort(_("unknown archive type '%s'") % kind)
575 raise util.Abort(_("unknown archive type '%s'") % kind)
576
576
577 ctx = repo[node]
577 ctx = repo[node]
578
578
579 if kind == 'files':
579 if kind == 'files':
580 if prefix:
580 if prefix:
581 raise util.Abort(
581 raise util.Abort(
582 _('cannot give prefix when archiving to files'))
582 _('cannot give prefix when archiving to files'))
583 else:
583 else:
584 prefix = archival.tidyprefix(dest, kind, prefix)
584 prefix = archival.tidyprefix(dest, kind, prefix)
585
585
586 def write(name, mode, islink, getdata):
586 def write(name, mode, islink, getdata):
587 if matchfn and not matchfn(name):
587 if matchfn and not matchfn(name):
588 return
588 return
589 data = getdata()
589 data = getdata()
590 if decode:
590 if decode:
591 data = repo.wwritedata(name, data)
591 data = repo.wwritedata(name, data)
592 archiver.addfile(prefix + name, mode, islink, data)
592 archiver.addfile(prefix + name, mode, islink, data)
593
593
594 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
594 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
595
595
596 if repo.ui.configbool("ui", "archivemeta", True):
596 if repo.ui.configbool("ui", "archivemeta", True):
597 def metadata():
597 def metadata():
598 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
598 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
599 hex(repo.changelog.node(0)), hex(node), ctx.branch())
599 hex(repo.changelog.node(0)), hex(node), ctx.branch())
600
600
601 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
601 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
602 if repo.tagtype(t) == 'global')
602 if repo.tagtype(t) == 'global')
603 if not tags:
603 if not tags:
604 repo.ui.pushbuffer()
604 repo.ui.pushbuffer()
605 opts = {'template': '{latesttag}\n{latesttagdistance}',
605 opts = {'template': '{latesttag}\n{latesttagdistance}',
606 'style': '', 'patch': None, 'git': None}
606 'style': '', 'patch': None, 'git': None}
607 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
607 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
608 ltags, dist = repo.ui.popbuffer().split('\n')
608 ltags, dist = repo.ui.popbuffer().split('\n')
609 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
609 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
610 tags += 'latesttagdistance: %s\n' % dist
610 tags += 'latesttagdistance: %s\n' % dist
611
611
612 return base + tags
612 return base + tags
613
613
614 write('.hg_archival.txt', 0644, False, metadata)
614 write('.hg_archival.txt', 0644, False, metadata)
615
615
616 for f in ctx:
616 for f in ctx:
617 ff = ctx.flags(f)
617 ff = ctx.flags(f)
618 getdata = ctx[f].data
618 getdata = ctx[f].data
619 if lfutil.isstandin(f):
619 if lfutil.isstandin(f):
620 path = lfutil.findfile(repo, getdata().strip())
620 path = lfutil.findfile(repo, getdata().strip())
621 f = lfutil.splitstandin(f)
621 f = lfutil.splitstandin(f)
622
622
623 def getdatafn():
623 def getdatafn():
624 fd = None
624 fd = None
625 try:
625 try:
626 fd = open(path, 'rb')
626 fd = open(path, 'rb')
627 return fd.read()
627 return fd.read()
628 finally:
628 finally:
629 if fd:
629 if fd:
630 fd.close()
630 fd.close()
631
631
632 getdata = getdatafn
632 getdata = getdatafn
633 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
633 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
634
634
635 if subrepos:
635 if subrepos:
636 for subpath in ctx.substate:
636 for subpath in ctx.substate:
637 sub = ctx.sub(subpath)
637 sub = ctx.sub(subpath)
638 try:
638 sub.archive(repo.ui, archiver, prefix)
639 sub.archive(repo.ui, archiver, prefix)
640 except TypeError:
641 sub.archive(archiver, prefix)
642
639
643 archiver.done()
640 archiver.done()
644
641
645 # If a largefile is modified, the change is not reflected in its
642 # If a largefile is modified, the change is not reflected in its
646 # standin until a commit. cmdutil.bailifchanged() raises an exception
643 # standin until a commit. cmdutil.bailifchanged() raises an exception
647 # if the repo has uncommitted changes. Wrap it to also check if
644 # if the repo has uncommitted changes. Wrap it to also check if
648 # largefiles were changed. This is used by bisect and backout.
645 # largefiles were changed. This is used by bisect and backout.
649 def override_bailifchanged(orig, repo):
646 def override_bailifchanged(orig, repo):
650 orig(repo)
647 orig(repo)
651 repo.lfstatus = True
648 repo.lfstatus = True
652 modified, added, removed, deleted = repo.status()[:4]
649 modified, added, removed, deleted = repo.status()[:4]
653 repo.lfstatus = False
650 repo.lfstatus = False
654 if modified or added or removed or deleted:
651 if modified or added or removed or deleted:
655 raise util.Abort(_('outstanding uncommitted changes'))
652 raise util.Abort(_('outstanding uncommitted changes'))
656
653
657 # Fetch doesn't use cmdutil.bail_if_changed so override it to add the check
654 # Fetch doesn't use cmdutil.bail_if_changed so override it to add the check
658 def override_fetch(orig, ui, repo, *pats, **opts):
655 def override_fetch(orig, ui, repo, *pats, **opts):
659 repo.lfstatus = True
656 repo.lfstatus = True
660 modified, added, removed, deleted = repo.status()[:4]
657 modified, added, removed, deleted = repo.status()[:4]
661 repo.lfstatus = False
658 repo.lfstatus = False
662 if modified or added or removed or deleted:
659 if modified or added or removed or deleted:
663 raise util.Abort(_('outstanding uncommitted changes'))
660 raise util.Abort(_('outstanding uncommitted changes'))
664 return orig(ui, repo, *pats, **opts)
661 return orig(ui, repo, *pats, **opts)
665
662
666 def override_forget(orig, ui, repo, *pats, **opts):
663 def override_forget(orig, ui, repo, *pats, **opts):
667 installnormalfilesmatchfn(repo[None].manifest())
664 installnormalfilesmatchfn(repo[None].manifest())
668 orig(ui, repo, *pats, **opts)
665 orig(ui, repo, *pats, **opts)
669 restorematchfn()
666 restorematchfn()
670 m = scmutil.match(repo[None], pats, opts)
667 m = scmutil.match(repo[None], pats, opts)
671
668
672 try:
669 try:
673 repo.lfstatus = True
670 repo.lfstatus = True
674 s = repo.status(match=m, clean=True)
671 s = repo.status(match=m, clean=True)
675 finally:
672 finally:
676 repo.lfstatus = False
673 repo.lfstatus = False
677 forget = sorted(s[0] + s[1] + s[3] + s[6])
674 forget = sorted(s[0] + s[1] + s[3] + s[6])
678 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
675 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
679
676
680 for f in forget:
677 for f in forget:
681 if lfutil.standin(f) not in repo.dirstate and not \
678 if lfutil.standin(f) not in repo.dirstate and not \
682 os.path.isdir(m.rel(lfutil.standin(f))):
679 os.path.isdir(m.rel(lfutil.standin(f))):
683 ui.warn(_('not removing %s: file is already untracked\n')
680 ui.warn(_('not removing %s: file is already untracked\n')
684 % m.rel(f))
681 % m.rel(f))
685
682
686 for f in forget:
683 for f in forget:
687 if ui.verbose or not m.exact(f):
684 if ui.verbose or not m.exact(f):
688 ui.status(_('removing %s\n') % m.rel(f))
685 ui.status(_('removing %s\n') % m.rel(f))
689
686
690 # Need to lock because standin files are deleted then removed from the
687 # Need to lock because standin files are deleted then removed from the
691 # repository and we could race inbetween.
688 # repository and we could race inbetween.
692 wlock = repo.wlock()
689 wlock = repo.wlock()
693 try:
690 try:
694 lfdirstate = lfutil.openlfdirstate(ui, repo)
691 lfdirstate = lfutil.openlfdirstate(ui, repo)
695 for f in forget:
692 for f in forget:
696 if lfdirstate[f] == 'a':
693 if lfdirstate[f] == 'a':
697 lfdirstate.drop(f)
694 lfdirstate.drop(f)
698 else:
695 else:
699 lfdirstate.remove(f)
696 lfdirstate.remove(f)
700 lfdirstate.write()
697 lfdirstate.write()
701 lfutil.repo_remove(repo, [lfutil.standin(f) for f in forget],
698 lfutil.repo_remove(repo, [lfutil.standin(f) for f in forget],
702 unlink=True)
699 unlink=True)
703 finally:
700 finally:
704 wlock.release()
701 wlock.release()
705
702
706 def getoutgoinglfiles(ui, repo, dest=None, **opts):
703 def getoutgoinglfiles(ui, repo, dest=None, **opts):
707 dest = ui.expandpath(dest or 'default-push', dest or 'default')
704 dest = ui.expandpath(dest or 'default-push', dest or 'default')
708 dest, branches = hg.parseurl(dest, opts.get('branch'))
705 dest, branches = hg.parseurl(dest, opts.get('branch'))
709 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
706 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
710 if revs:
707 if revs:
711 revs = [repo.lookup(rev) for rev in revs]
708 revs = [repo.lookup(rev) for rev in revs]
712
709
713 remoteui = hg.remoteui
710 remoteui = hg.remoteui
714
711
715 try:
712 try:
716 remote = hg.repository(remoteui(repo, opts), dest)
713 remote = hg.repository(remoteui(repo, opts), dest)
717 except error.RepoError:
714 except error.RepoError:
718 return None
715 return None
719 o = lfutil.findoutgoing(repo, remote, False)
716 o = lfutil.findoutgoing(repo, remote, False)
720 if not o:
717 if not o:
721 return None
718 return None
722 o = repo.changelog.nodesbetween(o, revs)[0]
719 o = repo.changelog.nodesbetween(o, revs)[0]
723 if opts.get('newest_first'):
720 if opts.get('newest_first'):
724 o.reverse()
721 o.reverse()
725
722
726 toupload = set()
723 toupload = set()
727 for n in o:
724 for n in o:
728 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
725 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
729 ctx = repo[n]
726 ctx = repo[n]
730 files = set(ctx.files())
727 files = set(ctx.files())
731 if len(parents) == 2:
728 if len(parents) == 2:
732 mc = ctx.manifest()
729 mc = ctx.manifest()
733 mp1 = ctx.parents()[0].manifest()
730 mp1 = ctx.parents()[0].manifest()
734 mp2 = ctx.parents()[1].manifest()
731 mp2 = ctx.parents()[1].manifest()
735 for f in mp1:
732 for f in mp1:
736 if f not in mc:
733 if f not in mc:
737 files.add(f)
734 files.add(f)
738 for f in mp2:
735 for f in mp2:
739 if f not in mc:
736 if f not in mc:
740 files.add(f)
737 files.add(f)
741 for f in mc:
738 for f in mc:
742 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
739 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
743 files.add(f)
740 files.add(f)
744 toupload = toupload.union(
741 toupload = toupload.union(
745 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
742 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
746 return toupload
743 return toupload
747
744
748 def override_outgoing(orig, ui, repo, dest=None, **opts):
745 def override_outgoing(orig, ui, repo, dest=None, **opts):
749 orig(ui, repo, dest, **opts)
746 orig(ui, repo, dest, **opts)
750
747
751 if opts.pop('large', None):
748 if opts.pop('large', None):
752 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
749 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
753 if toupload is None:
750 if toupload is None:
754 ui.status(_('largefiles: No remote repo\n'))
751 ui.status(_('largefiles: No remote repo\n'))
755 else:
752 else:
756 ui.status(_('largefiles to upload:\n'))
753 ui.status(_('largefiles to upload:\n'))
757 for file in toupload:
754 for file in toupload:
758 ui.status(lfutil.splitstandin(file) + '\n')
755 ui.status(lfutil.splitstandin(file) + '\n')
759 ui.status('\n')
756 ui.status('\n')
760
757
761 def override_summary(orig, ui, repo, *pats, **opts):
758 def override_summary(orig, ui, repo, *pats, **opts):
762 orig(ui, repo, *pats, **opts)
759 orig(ui, repo, *pats, **opts)
763
760
764 if opts.pop('large', None):
761 if opts.pop('large', None):
765 toupload = getoutgoinglfiles(ui, repo, None, **opts)
762 toupload = getoutgoinglfiles(ui, repo, None, **opts)
766 if toupload is None:
763 if toupload is None:
767 ui.status(_('largefiles: No remote repo\n'))
764 ui.status(_('largefiles: No remote repo\n'))
768 else:
765 else:
769 ui.status(_('largefiles: %d to upload\n') % len(toupload))
766 ui.status(_('largefiles: %d to upload\n') % len(toupload))
770
767
771 def override_addremove(orig, ui, repo, *pats, **opts):
768 def override_addremove(orig, ui, repo, *pats, **opts):
772 # Check if the parent or child has largefiles; if so, disallow
769 # Check if the parent or child has largefiles; if so, disallow
773 # addremove. If there is a symlink in the manifest then getting
770 # addremove. If there is a symlink in the manifest then getting
774 # the manifest throws an exception: catch it and let addremove
771 # the manifest throws an exception: catch it and let addremove
775 # deal with it.
772 # deal with it.
776 try:
773 try:
777 manifesttip = set(repo['tip'].manifest())
774 manifesttip = set(repo['tip'].manifest())
778 except util.Abort:
775 except util.Abort:
779 manifesttip = set()
776 manifesttip = set()
780 try:
777 try:
781 manifestworking = set(repo[None].manifest())
778 manifestworking = set(repo[None].manifest())
782 except util.Abort:
779 except util.Abort:
783 manifestworking = set()
780 manifestworking = set()
784
781
785 # Manifests are only iterable so turn them into sets then union
782 # Manifests are only iterable so turn them into sets then union
786 for file in manifesttip.union(manifestworking):
783 for file in manifesttip.union(manifestworking):
787 if file.startswith(lfutil.shortname):
784 if file.startswith(lfutil.shortname):
788 raise util.Abort(
785 raise util.Abort(
789 _('addremove cannot be run on a repo with largefiles'))
786 _('addremove cannot be run on a repo with largefiles'))
790
787
791 return orig(ui, repo, *pats, **opts)
788 return orig(ui, repo, *pats, **opts)
792
789
793 # Calling purge with --all will cause the largefiles to be deleted.
790 # Calling purge with --all will cause the largefiles to be deleted.
794 # Override repo.status to prevent this from happening.
791 # Override repo.status to prevent this from happening.
795 def override_purge(orig, ui, repo, *dirs, **opts):
792 def override_purge(orig, ui, repo, *dirs, **opts):
796 oldstatus = repo.status
793 oldstatus = repo.status
797 def override_status(node1='.', node2=None, match=None, ignored=False,
794 def override_status(node1='.', node2=None, match=None, ignored=False,
798 clean=False, unknown=False, listsubrepos=False):
795 clean=False, unknown=False, listsubrepos=False):
799 r = oldstatus(node1, node2, match, ignored, clean, unknown,
796 r = oldstatus(node1, node2, match, ignored, clean, unknown,
800 listsubrepos)
797 listsubrepos)
801 lfdirstate = lfutil.openlfdirstate(ui, repo)
798 lfdirstate = lfutil.openlfdirstate(ui, repo)
802 modified, added, removed, deleted, unknown, ignored, clean = r
799 modified, added, removed, deleted, unknown, ignored, clean = r
803 unknown = [f for f in unknown if lfdirstate[f] == '?']
800 unknown = [f for f in unknown if lfdirstate[f] == '?']
804 ignored = [f for f in ignored if lfdirstate[f] == '?']
801 ignored = [f for f in ignored if lfdirstate[f] == '?']
805 return modified, added, removed, deleted, unknown, ignored, clean
802 return modified, added, removed, deleted, unknown, ignored, clean
806 repo.status = override_status
803 repo.status = override_status
807 orig(ui, repo, *dirs, **opts)
804 orig(ui, repo, *dirs, **opts)
808 repo.status = oldstatus
805 repo.status = oldstatus
809
806
810 def override_rollback(orig, ui, repo, **opts):
807 def override_rollback(orig, ui, repo, **opts):
811 result = orig(ui, repo, **opts)
808 result = orig(ui, repo, **opts)
812 merge.update(repo, node=None, branchmerge=False, force=True,
809 merge.update(repo, node=None, branchmerge=False, force=True,
813 partial=lfutil.isstandin)
810 partial=lfutil.isstandin)
814 lfdirstate = lfutil.openlfdirstate(ui, repo)
811 lfdirstate = lfutil.openlfdirstate(ui, repo)
815 lfiles = lfutil.listlfiles(repo)
812 lfiles = lfutil.listlfiles(repo)
816 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
813 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
817 for file in lfiles:
814 for file in lfiles:
818 if file in oldlfiles:
815 if file in oldlfiles:
819 lfdirstate.normallookup(file)
816 lfdirstate.normallookup(file)
820 else:
817 else:
821 lfdirstate.add(file)
818 lfdirstate.add(file)
822 lfdirstate.write()
819 lfdirstate.write()
823 return result
820 return result
824
821
825 def override_transplant(orig, ui, repo, *revs, **opts):
822 def override_transplant(orig, ui, repo, *revs, **opts):
826 result = orig(ui, repo, *revs, **opts)
823 result = orig(ui, repo, *revs, **opts)
827 lfcommands.updatelfiles(repo.ui, repo)
824 lfcommands.updatelfiles(repo.ui, repo)
828 return result
825 return result
@@ -1,433 +1,425 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''setup for largefiles repositories: reposetup'''
9 '''setup for largefiles repositories: reposetup'''
10 import copy
10 import copy
11 import types
11 import types
12 import os
12 import os
13 import re
13 import re
14
14
15 from mercurial import context, error, manifest, match as match_, node, util
15 from mercurial import context, error, manifest, match as match_, node, util
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17
17
18 import lfcommands
18 import lfcommands
19 import proto
19 import proto
20 import lfutil
20 import lfutil
21
21
22 def reposetup(ui, repo):
22 def reposetup(ui, repo):
23 # wire repositories should be given new wireproto functions but not the
23 # wire repositories should be given new wireproto functions but not the
24 # other largefiles modifications
24 # other largefiles modifications
25 if not repo.local():
25 if not repo.local():
26 return proto.wirereposetup(ui, repo)
26 return proto.wirereposetup(ui, repo)
27
27
28 for name in ('status', 'commitctx', 'commit', 'push'):
28 for name in ('status', 'commitctx', 'commit', 'push'):
29 method = getattr(repo, name)
29 method = getattr(repo, name)
30 #if not (isinstance(method, types.MethodType) and
30 #if not (isinstance(method, types.MethodType) and
31 # method.im_func is repo.__class__.commitctx.im_func):
31 # method.im_func is repo.__class__.commitctx.im_func):
32 if (isinstance(method, types.FunctionType) and
32 if (isinstance(method, types.FunctionType) and
33 method.func_name == 'wrap'):
33 method.func_name == 'wrap'):
34 ui.warn(_('largefiles: repo method %r appears to have already been'
34 ui.warn(_('largefiles: repo method %r appears to have already been'
35 ' wrapped by another extension: '
35 ' wrapped by another extension: '
36 'largefiles may behave incorrectly\n')
36 'largefiles may behave incorrectly\n')
37 % name)
37 % name)
38
38
39 class lfiles_repo(repo.__class__):
39 class lfiles_repo(repo.__class__):
40 lfstatus = False
40 lfstatus = False
41 def status_nolfiles(self, *args, **kwargs):
41 def status_nolfiles(self, *args, **kwargs):
42 return super(lfiles_repo, self).status(*args, **kwargs)
42 return super(lfiles_repo, self).status(*args, **kwargs)
43
43
44 # When lfstatus is set, return a context that gives the names
44 # When lfstatus is set, return a context that gives the names
45 # of largefiles instead of their corresponding standins and
45 # of largefiles instead of their corresponding standins and
46 # identifies the largefiles as always binary, regardless of
46 # identifies the largefiles as always binary, regardless of
47 # their actual contents.
47 # their actual contents.
48 def __getitem__(self, changeid):
48 def __getitem__(self, changeid):
49 ctx = super(lfiles_repo, self).__getitem__(changeid)
49 ctx = super(lfiles_repo, self).__getitem__(changeid)
50 if self.lfstatus:
50 if self.lfstatus:
51 class lfiles_manifestdict(manifest.manifestdict):
51 class lfiles_manifestdict(manifest.manifestdict):
52 def __contains__(self, filename):
52 def __contains__(self, filename):
53 if super(lfiles_manifestdict,
53 if super(lfiles_manifestdict,
54 self).__contains__(filename):
54 self).__contains__(filename):
55 return True
55 return True
56 return super(lfiles_manifestdict,
56 return super(lfiles_manifestdict,
57 self).__contains__(lfutil.shortname+'/' + filename)
57 self).__contains__(lfutil.shortname+'/' + filename)
58 class lfiles_ctx(ctx.__class__):
58 class lfiles_ctx(ctx.__class__):
59 def files(self):
59 def files(self):
60 filenames = super(lfiles_ctx, self).files()
60 filenames = super(lfiles_ctx, self).files()
61 return [re.sub('^\\'+lfutil.shortname+'/', '',
61 return [re.sub('^\\'+lfutil.shortname+'/', '',
62 filename) for filename in filenames]
62 filename) for filename in filenames]
63 def manifest(self):
63 def manifest(self):
64 man1 = super(lfiles_ctx, self).manifest()
64 man1 = super(lfiles_ctx, self).manifest()
65 man1.__class__ = lfiles_manifestdict
65 man1.__class__ = lfiles_manifestdict
66 return man1
66 return man1
67 def filectx(self, path, fileid=None, filelog=None):
67 def filectx(self, path, fileid=None, filelog=None):
68 try:
68 try:
69 result = super(lfiles_ctx, self).filectx(path,
69 result = super(lfiles_ctx, self).filectx(path,
70 fileid, filelog)
70 fileid, filelog)
71 except error.LookupError:
71 except error.LookupError:
72 # Adding a null character will cause Mercurial to
72 # Adding a null character will cause Mercurial to
73 # identify this as a binary file.
73 # identify this as a binary file.
74 result = super(lfiles_ctx, self).filectx(
74 result = super(lfiles_ctx, self).filectx(
75 lfutil.shortname + '/' + path, fileid,
75 lfutil.shortname + '/' + path, fileid,
76 filelog)
76 filelog)
77 olddata = result.data
77 olddata = result.data
78 result.data = lambda: olddata() + '\0'
78 result.data = lambda: olddata() + '\0'
79 return result
79 return result
80 ctx.__class__ = lfiles_ctx
80 ctx.__class__ = lfiles_ctx
81 return ctx
81 return ctx
82
82
83 # Figure out the status of big files and insert them into the
83 # Figure out the status of big files and insert them into the
84 # appropriate list in the result. Also removes standin files
84 # appropriate list in the result. Also removes standin files
85 # from the listing. Revert to the original status if
85 # from the listing. Revert to the original status if
86 # self.lfstatus is False.
86 # self.lfstatus is False.
87 def status(self, node1='.', node2=None, match=None, ignored=False,
87 def status(self, node1='.', node2=None, match=None, ignored=False,
88 clean=False, unknown=False, listsubrepos=False):
88 clean=False, unknown=False, listsubrepos=False):
89 listignored, listclean, listunknown = ignored, clean, unknown
89 listignored, listclean, listunknown = ignored, clean, unknown
90 if not self.lfstatus:
90 if not self.lfstatus:
91 try:
91 return super(lfiles_repo, self).status(node1, node2, match,
92 return super(lfiles_repo, self).status(node1, node2, match,
92 listignored, listclean, listunknown, listsubrepos)
93 listignored, listclean, listunknown, listsubrepos)
94 except TypeError:
95 return super(lfiles_repo, self).status(node1, node2, match,
96 listignored, listclean, listunknown)
97 else:
93 else:
98 # some calls in this function rely on the old version of status
94 # some calls in this function rely on the old version of status
99 self.lfstatus = False
95 self.lfstatus = False
100 if isinstance(node1, context.changectx):
96 if isinstance(node1, context.changectx):
101 ctx1 = node1
97 ctx1 = node1
102 else:
98 else:
103 ctx1 = repo[node1]
99 ctx1 = repo[node1]
104 if isinstance(node2, context.changectx):
100 if isinstance(node2, context.changectx):
105 ctx2 = node2
101 ctx2 = node2
106 else:
102 else:
107 ctx2 = repo[node2]
103 ctx2 = repo[node2]
108 working = ctx2.rev() is None
104 working = ctx2.rev() is None
109 parentworking = working and ctx1 == self['.']
105 parentworking = working and ctx1 == self['.']
110
106
111 def inctx(file, ctx):
107 def inctx(file, ctx):
112 try:
108 try:
113 if ctx.rev() is None:
109 if ctx.rev() is None:
114 return file in ctx.manifest()
110 return file in ctx.manifest()
115 ctx[file]
111 ctx[file]
116 return True
112 return True
117 except KeyError:
113 except KeyError:
118 return False
114 return False
119
115
120 if match is None:
116 if match is None:
121 match = match_.always(self.root, self.getcwd())
117 match = match_.always(self.root, self.getcwd())
122
118
123 # Create a copy of match that matches standins instead
119 # Create a copy of match that matches standins instead
124 # of largefiles.
120 # of largefiles.
125 def tostandin(file):
121 def tostandin(file):
126 if inctx(lfutil.standin(file), ctx2):
122 if inctx(lfutil.standin(file), ctx2):
127 return lfutil.standin(file)
123 return lfutil.standin(file)
128 return file
124 return file
129
125
130 # Create a function that we can use to override what is
126 # Create a function that we can use to override what is
131 # normally the ignore matcher. We've already checked
127 # normally the ignore matcher. We've already checked
132 # for ignored files on the first dirstate walk, and
128 # for ignored files on the first dirstate walk, and
133 # unecessarily re-checking here causes a huge performance
129 # unecessarily re-checking here causes a huge performance
134 # hit because lfdirstate only knows about largefiles
130 # hit because lfdirstate only knows about largefiles
135 def _ignoreoverride(self):
131 def _ignoreoverride(self):
136 return False
132 return False
137
133
138 m = copy.copy(match)
134 m = copy.copy(match)
139 m._files = [tostandin(f) for f in m._files]
135 m._files = [tostandin(f) for f in m._files]
140
136
141 # Get ignored files here even if we weren't asked for them; we
137 # Get ignored files here even if we weren't asked for them; we
142 # must use the result here for filtering later
138 # must use the result here for filtering later
143 try:
139 result = super(lfiles_repo, self).status(node1, node2, m,
144 result = super(lfiles_repo, self).status(node1, node2, m,
140 True, clean, unknown, listsubrepos)
145 True, clean, unknown, listsubrepos)
146 except TypeError:
147 result = super(lfiles_repo, self).status(node1, node2, m,
148 True, clean, unknown)
149 if working:
141 if working:
150 # hold the wlock while we read largefiles and
142 # hold the wlock while we read largefiles and
151 # update the lfdirstate
143 # update the lfdirstate
152 wlock = repo.wlock()
144 wlock = repo.wlock()
153 try:
145 try:
154 # Any non-largefiles that were explicitly listed must be
146 # Any non-largefiles that were explicitly listed must be
155 # taken out or lfdirstate.status will report an error.
147 # taken out or lfdirstate.status will report an error.
156 # The status of these files was already computed using
148 # The status of these files was already computed using
157 # super's status.
149 # super's status.
158 lfdirstate = lfutil.openlfdirstate(ui, self)
150 lfdirstate = lfutil.openlfdirstate(ui, self)
159 # Override lfdirstate's ignore matcher to not do
151 # Override lfdirstate's ignore matcher to not do
160 # anything
152 # anything
161 orig_ignore = lfdirstate._ignore
153 orig_ignore = lfdirstate._ignore
162 lfdirstate._ignore = _ignoreoverride
154 lfdirstate._ignore = _ignoreoverride
163
155
164 match._files = [f for f in match._files if f in
156 match._files = [f for f in match._files if f in
165 lfdirstate]
157 lfdirstate]
166 # Don't waste time getting the ignored and unknown
158 # Don't waste time getting the ignored and unknown
167 # files again; we already have them
159 # files again; we already have them
168 s = lfdirstate.status(match, [], False,
160 s = lfdirstate.status(match, [], False,
169 listclean, False)
161 listclean, False)
170 (unsure, modified, added, removed, missing, unknown,
162 (unsure, modified, added, removed, missing, unknown,
171 ignored, clean) = s
163 ignored, clean) = s
172 # Replace the list of ignored and unknown files with
164 # Replace the list of ignored and unknown files with
173 # the previously caclulated lists, and strip out the
165 # the previously caclulated lists, and strip out the
174 # largefiles
166 # largefiles
175 lfiles = set(lfdirstate._map)
167 lfiles = set(lfdirstate._map)
176 ignored = set(result[5]).difference(lfiles)
168 ignored = set(result[5]).difference(lfiles)
177 unknown = set(result[4]).difference(lfiles)
169 unknown = set(result[4]).difference(lfiles)
178 if parentworking:
170 if parentworking:
179 for lfile in unsure:
171 for lfile in unsure:
180 if ctx1[lfutil.standin(lfile)].data().strip() \
172 if ctx1[lfutil.standin(lfile)].data().strip() \
181 != lfutil.hashfile(self.wjoin(lfile)):
173 != lfutil.hashfile(self.wjoin(lfile)):
182 modified.append(lfile)
174 modified.append(lfile)
183 else:
175 else:
184 clean.append(lfile)
176 clean.append(lfile)
185 lfdirstate.normal(lfile)
177 lfdirstate.normal(lfile)
186 lfdirstate.write()
178 lfdirstate.write()
187 else:
179 else:
188 tocheck = unsure + modified + added + clean
180 tocheck = unsure + modified + added + clean
189 modified, added, clean = [], [], []
181 modified, added, clean = [], [], []
190
182
191 for lfile in tocheck:
183 for lfile in tocheck:
192 standin = lfutil.standin(lfile)
184 standin = lfutil.standin(lfile)
193 if inctx(standin, ctx1):
185 if inctx(standin, ctx1):
194 if ctx1[standin].data().strip() != \
186 if ctx1[standin].data().strip() != \
195 lfutil.hashfile(self.wjoin(lfile)):
187 lfutil.hashfile(self.wjoin(lfile)):
196 modified.append(lfile)
188 modified.append(lfile)
197 else:
189 else:
198 clean.append(lfile)
190 clean.append(lfile)
199 else:
191 else:
200 added.append(lfile)
192 added.append(lfile)
201 # Replace the original ignore function
193 # Replace the original ignore function
202 lfdirstate._ignore = orig_ignore
194 lfdirstate._ignore = orig_ignore
203 finally:
195 finally:
204 wlock.release()
196 wlock.release()
205
197
206 for standin in ctx1.manifest():
198 for standin in ctx1.manifest():
207 if not lfutil.isstandin(standin):
199 if not lfutil.isstandin(standin):
208 continue
200 continue
209 lfile = lfutil.splitstandin(standin)
201 lfile = lfutil.splitstandin(standin)
210 if not match(lfile):
202 if not match(lfile):
211 continue
203 continue
212 if lfile not in lfdirstate:
204 if lfile not in lfdirstate:
213 removed.append(lfile)
205 removed.append(lfile)
214 # Handle unknown and ignored differently
206 # Handle unknown and ignored differently
215 lfiles = (modified, added, removed, missing, [], [], clean)
207 lfiles = (modified, added, removed, missing, [], [], clean)
216 result = list(result)
208 result = list(result)
217 # Unknown files
209 # Unknown files
218 unknown = set(unknown).difference(ignored)
210 unknown = set(unknown).difference(ignored)
219 result[4] = [f for f in unknown
211 result[4] = [f for f in unknown
220 if (repo.dirstate[f] == '?' and
212 if (repo.dirstate[f] == '?' and
221 not lfutil.isstandin(f))]
213 not lfutil.isstandin(f))]
222 # Ignored files were calculated earlier by the dirstate,
214 # Ignored files were calculated earlier by the dirstate,
223 # and we already stripped out the largefiles from the list
215 # and we already stripped out the largefiles from the list
224 result[5] = ignored
216 result[5] = ignored
225 # combine normal files and largefiles
217 # combine normal files and largefiles
226 normals = [[fn for fn in filelist
218 normals = [[fn for fn in filelist
227 if not lfutil.isstandin(fn)]
219 if not lfutil.isstandin(fn)]
228 for filelist in result]
220 for filelist in result]
229 result = [sorted(list1 + list2)
221 result = [sorted(list1 + list2)
230 for (list1, list2) in zip(normals, lfiles)]
222 for (list1, list2) in zip(normals, lfiles)]
231 else:
223 else:
232 def toname(f):
224 def toname(f):
233 if lfutil.isstandin(f):
225 if lfutil.isstandin(f):
234 return lfutil.splitstandin(f)
226 return lfutil.splitstandin(f)
235 return f
227 return f
236 result = [[toname(f) for f in items] for items in result]
228 result = [[toname(f) for f in items] for items in result]
237
229
238 if not listunknown:
230 if not listunknown:
239 result[4] = []
231 result[4] = []
240 if not listignored:
232 if not listignored:
241 result[5] = []
233 result[5] = []
242 if not listclean:
234 if not listclean:
243 result[6] = []
235 result[6] = []
244 self.lfstatus = True
236 self.lfstatus = True
245 return result
237 return result
246
238
247 # As part of committing, copy all of the largefiles into the
239 # As part of committing, copy all of the largefiles into the
248 # cache.
240 # cache.
249 def commitctx(self, *args, **kwargs):
241 def commitctx(self, *args, **kwargs):
250 node = super(lfiles_repo, self).commitctx(*args, **kwargs)
242 node = super(lfiles_repo, self).commitctx(*args, **kwargs)
251 ctx = self[node]
243 ctx = self[node]
252 for filename in ctx.files():
244 for filename in ctx.files():
253 if lfutil.isstandin(filename) and filename in ctx.manifest():
245 if lfutil.isstandin(filename) and filename in ctx.manifest():
254 realfile = lfutil.splitstandin(filename)
246 realfile = lfutil.splitstandin(filename)
255 lfutil.copytostore(self, ctx.node(), realfile)
247 lfutil.copytostore(self, ctx.node(), realfile)
256
248
257 return node
249 return node
258
250
259 # Before commit, largefile standins have not had their
251 # Before commit, largefile standins have not had their
260 # contents updated to reflect the hash of their largefile.
252 # contents updated to reflect the hash of their largefile.
261 # Do that here.
253 # Do that here.
262 def commit(self, text="", user=None, date=None, match=None,
254 def commit(self, text="", user=None, date=None, match=None,
263 force=False, editor=False, extra={}):
255 force=False, editor=False, extra={}):
264 orig = super(lfiles_repo, self).commit
256 orig = super(lfiles_repo, self).commit
265
257
266 wlock = repo.wlock()
258 wlock = repo.wlock()
267 try:
259 try:
268 if getattr(repo, "_isrebasing", False):
260 if getattr(repo, "_isrebasing", False):
269 # We have to take the time to pull down the new
261 # We have to take the time to pull down the new
270 # largefiles now. Otherwise if we are rebasing,
262 # largefiles now. Otherwise if we are rebasing,
271 # any largefiles that were modified in the
263 # any largefiles that were modified in the
272 # destination changesets get overwritten, either
264 # destination changesets get overwritten, either
273 # by the rebase or in the first commit after the
265 # by the rebase or in the first commit after the
274 # rebase.
266 # rebase.
275 lfcommands.updatelfiles(repo.ui, repo)
267 lfcommands.updatelfiles(repo.ui, repo)
276 # Case 1: user calls commit with no specific files or
268 # Case 1: user calls commit with no specific files or
277 # include/exclude patterns: refresh and commit all files that
269 # include/exclude patterns: refresh and commit all files that
278 # are "dirty".
270 # are "dirty".
279 if ((match is None) or
271 if ((match is None) or
280 (not match.anypats() and not match.files())):
272 (not match.anypats() and not match.files())):
281 # Spend a bit of time here to get a list of files we know
273 # Spend a bit of time here to get a list of files we know
282 # are modified so we can compare only against those.
274 # are modified so we can compare only against those.
283 # It can cost a lot of time (several seconds)
275 # It can cost a lot of time (several seconds)
284 # otherwise to update all standins if the largefiles are
276 # otherwise to update all standins if the largefiles are
285 # large.
277 # large.
286 lfdirstate = lfutil.openlfdirstate(ui, self)
278 lfdirstate = lfutil.openlfdirstate(ui, self)
287 dirtymatch = match_.always(repo.root, repo.getcwd())
279 dirtymatch = match_.always(repo.root, repo.getcwd())
288 s = lfdirstate.status(dirtymatch, [], False, False, False)
280 s = lfdirstate.status(dirtymatch, [], False, False, False)
289 modifiedfiles = []
281 modifiedfiles = []
290 for i in s:
282 for i in s:
291 modifiedfiles.extend(i)
283 modifiedfiles.extend(i)
292 lfiles = lfutil.listlfiles(self)
284 lfiles = lfutil.listlfiles(self)
293 # this only loops through largefiles that exist (not
285 # this only loops through largefiles that exist (not
294 # removed/renamed)
286 # removed/renamed)
295 for lfile in lfiles:
287 for lfile in lfiles:
296 if lfile in modifiedfiles:
288 if lfile in modifiedfiles:
297 if os.path.exists(self.wjoin(lfutil.standin(lfile))):
289 if os.path.exists(self.wjoin(lfutil.standin(lfile))):
298 # this handles the case where a rebase is being
290 # this handles the case where a rebase is being
299 # performed and the working copy is not updated
291 # performed and the working copy is not updated
300 # yet.
292 # yet.
301 if os.path.exists(self.wjoin(lfile)):
293 if os.path.exists(self.wjoin(lfile)):
302 lfutil.updatestandin(self,
294 lfutil.updatestandin(self,
303 lfutil.standin(lfile))
295 lfutil.standin(lfile))
304 lfdirstate.normal(lfile)
296 lfdirstate.normal(lfile)
305 for lfile in lfdirstate:
297 for lfile in lfdirstate:
306 if lfile in modifiedfiles:
298 if lfile in modifiedfiles:
307 if not os.path.exists(
299 if not os.path.exists(
308 repo.wjoin(lfutil.standin(lfile))):
300 repo.wjoin(lfutil.standin(lfile))):
309 lfdirstate.drop(lfile)
301 lfdirstate.drop(lfile)
310 lfdirstate.write()
302 lfdirstate.write()
311
303
312 return orig(text=text, user=user, date=date, match=match,
304 return orig(text=text, user=user, date=date, match=match,
313 force=force, editor=editor, extra=extra)
305 force=force, editor=editor, extra=extra)
314
306
315 for f in match.files():
307 for f in match.files():
316 if lfutil.isstandin(f):
308 if lfutil.isstandin(f):
317 raise util.Abort(
309 raise util.Abort(
318 _('file "%s" is a largefile standin') % f,
310 _('file "%s" is a largefile standin') % f,
319 hint=('commit the largefile itself instead'))
311 hint=('commit the largefile itself instead'))
320
312
321 # Case 2: user calls commit with specified patterns: refresh
313 # Case 2: user calls commit with specified patterns: refresh
322 # any matching big files.
314 # any matching big files.
323 smatcher = lfutil.composestandinmatcher(self, match)
315 smatcher = lfutil.composestandinmatcher(self, match)
324 standins = lfutil.dirstate_walk(self.dirstate, smatcher)
316 standins = lfutil.dirstate_walk(self.dirstate, smatcher)
325
317
326 # No matching big files: get out of the way and pass control to
318 # No matching big files: get out of the way and pass control to
327 # the usual commit() method.
319 # the usual commit() method.
328 if not standins:
320 if not standins:
329 return orig(text=text, user=user, date=date, match=match,
321 return orig(text=text, user=user, date=date, match=match,
330 force=force, editor=editor, extra=extra)
322 force=force, editor=editor, extra=extra)
331
323
332 # Refresh all matching big files. It's possible that the
324 # Refresh all matching big files. It's possible that the
333 # commit will end up failing, in which case the big files will
325 # commit will end up failing, in which case the big files will
334 # stay refreshed. No harm done: the user modified them and
326 # stay refreshed. No harm done: the user modified them and
335 # asked to commit them, so sooner or later we're going to
327 # asked to commit them, so sooner or later we're going to
336 # refresh the standins. Might as well leave them refreshed.
328 # refresh the standins. Might as well leave them refreshed.
337 lfdirstate = lfutil.openlfdirstate(ui, self)
329 lfdirstate = lfutil.openlfdirstate(ui, self)
338 for standin in standins:
330 for standin in standins:
339 lfile = lfutil.splitstandin(standin)
331 lfile = lfutil.splitstandin(standin)
340 if lfdirstate[lfile] <> 'r':
332 if lfdirstate[lfile] <> 'r':
341 lfutil.updatestandin(self, standin)
333 lfutil.updatestandin(self, standin)
342 lfdirstate.normal(lfile)
334 lfdirstate.normal(lfile)
343 else:
335 else:
344 lfdirstate.drop(lfile)
336 lfdirstate.drop(lfile)
345 lfdirstate.write()
337 lfdirstate.write()
346
338
347 # Cook up a new matcher that only matches regular files or
339 # Cook up a new matcher that only matches regular files or
348 # standins corresponding to the big files requested by the
340 # standins corresponding to the big files requested by the
349 # user. Have to modify _files to prevent commit() from
341 # user. Have to modify _files to prevent commit() from
350 # complaining "not tracked" for big files.
342 # complaining "not tracked" for big files.
351 lfiles = lfutil.listlfiles(repo)
343 lfiles = lfutil.listlfiles(repo)
352 match = copy.copy(match)
344 match = copy.copy(match)
353 orig_matchfn = match.matchfn
345 orig_matchfn = match.matchfn
354
346
355 # Check both the list of largefiles and the list of
347 # Check both the list of largefiles and the list of
356 # standins because if a largefile was removed, it
348 # standins because if a largefile was removed, it
357 # won't be in the list of largefiles at this point
349 # won't be in the list of largefiles at this point
358 match._files += sorted(standins)
350 match._files += sorted(standins)
359
351
360 actualfiles = []
352 actualfiles = []
361 for f in match._files:
353 for f in match._files:
362 fstandin = lfutil.standin(f)
354 fstandin = lfutil.standin(f)
363
355
364 # ignore known largefiles and standins
356 # ignore known largefiles and standins
365 if f in lfiles or fstandin in standins:
357 if f in lfiles or fstandin in standins:
366 continue
358 continue
367
359
368 # append directory separator to avoid collisions
360 # append directory separator to avoid collisions
369 if not fstandin.endswith(os.sep):
361 if not fstandin.endswith(os.sep):
370 fstandin += os.sep
362 fstandin += os.sep
371
363
372 # prevalidate matching standin directories
364 # prevalidate matching standin directories
373 if util.any(st for st in match._files
365 if util.any(st for st in match._files
374 if st.startswith(fstandin)):
366 if st.startswith(fstandin)):
375 continue
367 continue
376 actualfiles.append(f)
368 actualfiles.append(f)
377 match._files = actualfiles
369 match._files = actualfiles
378
370
379 def matchfn(f):
371 def matchfn(f):
380 if orig_matchfn(f):
372 if orig_matchfn(f):
381 return f not in lfiles
373 return f not in lfiles
382 else:
374 else:
383 return f in standins
375 return f in standins
384
376
385 match.matchfn = matchfn
377 match.matchfn = matchfn
386 return orig(text=text, user=user, date=date, match=match,
378 return orig(text=text, user=user, date=date, match=match,
387 force=force, editor=editor, extra=extra)
379 force=force, editor=editor, extra=extra)
388 finally:
380 finally:
389 wlock.release()
381 wlock.release()
390
382
391 def push(self, remote, force=False, revs=None, newbranch=False):
383 def push(self, remote, force=False, revs=None, newbranch=False):
392 o = lfutil.findoutgoing(repo, remote, force)
384 o = lfutil.findoutgoing(repo, remote, force)
393 if o:
385 if o:
394 toupload = set()
386 toupload = set()
395 o = repo.changelog.nodesbetween(o, revs)[0]
387 o = repo.changelog.nodesbetween(o, revs)[0]
396 for n in o:
388 for n in o:
397 parents = [p for p in repo.changelog.parents(n)
389 parents = [p for p in repo.changelog.parents(n)
398 if p != node.nullid]
390 if p != node.nullid]
399 ctx = repo[n]
391 ctx = repo[n]
400 files = set(ctx.files())
392 files = set(ctx.files())
401 if len(parents) == 2:
393 if len(parents) == 2:
402 mc = ctx.manifest()
394 mc = ctx.manifest()
403 mp1 = ctx.parents()[0].manifest()
395 mp1 = ctx.parents()[0].manifest()
404 mp2 = ctx.parents()[1].manifest()
396 mp2 = ctx.parents()[1].manifest()
405 for f in mp1:
397 for f in mp1:
406 if f not in mc:
398 if f not in mc:
407 files.add(f)
399 files.add(f)
408 for f in mp2:
400 for f in mp2:
409 if f not in mc:
401 if f not in mc:
410 files.add(f)
402 files.add(f)
411 for f in mc:
403 for f in mc:
412 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f,
404 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f,
413 None):
405 None):
414 files.add(f)
406 files.add(f)
415
407
416 toupload = toupload.union(
408 toupload = toupload.union(
417 set([ctx[f].data().strip()
409 set([ctx[f].data().strip()
418 for f in files
410 for f in files
419 if lfutil.isstandin(f) and f in ctx]))
411 if lfutil.isstandin(f) and f in ctx]))
420 lfcommands.uploadlfiles(ui, self, remote, toupload)
412 lfcommands.uploadlfiles(ui, self, remote, toupload)
421 return super(lfiles_repo, self).push(remote, force, revs,
413 return super(lfiles_repo, self).push(remote, force, revs,
422 newbranch)
414 newbranch)
423
415
424 repo.__class__ = lfiles_repo
416 repo.__class__ = lfiles_repo
425
417
426 def checkrequireslfiles(ui, repo, **kwargs):
418 def checkrequireslfiles(ui, repo, **kwargs):
427 if 'largefiles' not in repo.requirements and util.any(
419 if 'largefiles' not in repo.requirements and util.any(
428 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
420 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
429 repo.requirements.add('largefiles')
421 repo.requirements.add('largefiles')
430 repo._writerequirements()
422 repo._writerequirements()
431
423
432 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles)
424 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles)
433 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles)
425 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles)
General Comments 0
You need to be logged in to leave comments. Login now