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