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