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