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