##// END OF EJS Templates
largefiles: fix addremove when largefile is missing (issue3227)
Na'Tosha Bard -
r16731:dcfc70aa default
parent child Browse files
Show More
@@ -1,1065 +1,1067 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 # -- 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 overridematch(ctx, pats=[], opts={}, globbed=False,
29 def overridematch(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 origmatchfn = m.matchfn
37 origmatchfn = m.matchfn
38 m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None
38 m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None
39 return m
39 return m
40 oldmatch = installmatchfn(overridematch)
40 oldmatch = installmatchfn(overridematch)
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 addlargefiles(ui, repo, *pats, **opts):
56 def addlargefiles(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.repoadd(repo, standins)
112 for f in lfutil.repoadd(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 removelargefiles(ui, repo, *pats, **opts):
118 def removelargefiles(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.repoforget(repo, forget)
167 lfutil.repoforget(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.reporemove(repo, remove, unlink=True)
171 lfutil.reporemove(repo, remove, unlink=True)
172 else:
173 lfutil.reporemove(repo, remove, unlink=False)
172 finally:
174 finally:
173 wlock.release()
175 wlock.release()
174
176
175 # For overriding mercurial.hgweb.webcommands so that largefiles will
177 # For overriding mercurial.hgweb.webcommands so that largefiles will
176 # appear at their right place in the manifests.
178 # appear at their right place in the manifests.
177 def decodepath(orig, path):
179 def decodepath(orig, path):
178 return lfutil.splitstandin(path) or path
180 return lfutil.splitstandin(path) or path
179
181
180 # -- Wrappers: modify existing commands --------------------------------
182 # -- Wrappers: modify existing commands --------------------------------
181
183
182 # Add works by going through the files that the user wanted to add and
184 # Add works by going through the files that the user wanted to add and
183 # checking if they should be added as largefiles. Then it makes a new
185 # checking if they should be added as largefiles. Then it makes a new
184 # matcher which matches only the normal files and runs the original
186 # matcher which matches only the normal files and runs the original
185 # version of add.
187 # version of add.
186 def overrideadd(orig, ui, repo, *pats, **opts):
188 def overrideadd(orig, ui, repo, *pats, **opts):
187 normal = opts.pop('normal')
189 normal = opts.pop('normal')
188 if normal:
190 if normal:
189 if opts.get('large'):
191 if opts.get('large'):
190 raise util.Abort(_('--normal cannot be used with --large'))
192 raise util.Abort(_('--normal cannot be used with --large'))
191 return orig(ui, repo, *pats, **opts)
193 return orig(ui, repo, *pats, **opts)
192 bad = addlargefiles(ui, repo, *pats, **opts)
194 bad = addlargefiles(ui, repo, *pats, **opts)
193 installnormalfilesmatchfn(repo[None].manifest())
195 installnormalfilesmatchfn(repo[None].manifest())
194 result = orig(ui, repo, *pats, **opts)
196 result = orig(ui, repo, *pats, **opts)
195 restorematchfn()
197 restorematchfn()
196
198
197 return (result == 1 or bad) and 1 or 0
199 return (result == 1 or bad) and 1 or 0
198
200
199 def overrideremove(orig, ui, repo, *pats, **opts):
201 def overrideremove(orig, ui, repo, *pats, **opts):
200 installnormalfilesmatchfn(repo[None].manifest())
202 installnormalfilesmatchfn(repo[None].manifest())
201 orig(ui, repo, *pats, **opts)
203 orig(ui, repo, *pats, **opts)
202 restorematchfn()
204 restorematchfn()
203 removelargefiles(ui, repo, *pats, **opts)
205 removelargefiles(ui, repo, *pats, **opts)
204
206
205 def overridestatusfn(orig, repo, rev2, **opts):
207 def overridestatusfn(orig, repo, rev2, **opts):
206 try:
208 try:
207 repo._repo.lfstatus = True
209 repo._repo.lfstatus = True
208 return orig(repo, rev2, **opts)
210 return orig(repo, rev2, **opts)
209 finally:
211 finally:
210 repo._repo.lfstatus = False
212 repo._repo.lfstatus = False
211
213
212 def overridestatus(orig, ui, repo, *pats, **opts):
214 def overridestatus(orig, ui, repo, *pats, **opts):
213 try:
215 try:
214 repo.lfstatus = True
216 repo.lfstatus = True
215 return orig(ui, repo, *pats, **opts)
217 return orig(ui, repo, *pats, **opts)
216 finally:
218 finally:
217 repo.lfstatus = False
219 repo.lfstatus = False
218
220
219 def overridedirty(orig, repo, ignoreupdate=False):
221 def overridedirty(orig, repo, ignoreupdate=False):
220 try:
222 try:
221 repo._repo.lfstatus = True
223 repo._repo.lfstatus = True
222 return orig(repo, ignoreupdate)
224 return orig(repo, ignoreupdate)
223 finally:
225 finally:
224 repo._repo.lfstatus = False
226 repo._repo.lfstatus = False
225
227
226 def overridelog(orig, ui, repo, *pats, **opts):
228 def overridelog(orig, ui, repo, *pats, **opts):
227 try:
229 try:
228 repo.lfstatus = True
230 repo.lfstatus = True
229 orig(ui, repo, *pats, **opts)
231 orig(ui, repo, *pats, **opts)
230 finally:
232 finally:
231 repo.lfstatus = False
233 repo.lfstatus = False
232
234
233 def overrideverify(orig, ui, repo, *pats, **opts):
235 def overrideverify(orig, ui, repo, *pats, **opts):
234 large = opts.pop('large', False)
236 large = opts.pop('large', False)
235 all = opts.pop('lfa', False)
237 all = opts.pop('lfa', False)
236 contents = opts.pop('lfc', False)
238 contents = opts.pop('lfc', False)
237
239
238 result = orig(ui, repo, *pats, **opts)
240 result = orig(ui, repo, *pats, **opts)
239 if large:
241 if large:
240 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
242 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
241 return result
243 return result
242
244
243 # Override needs to refresh standins so that update's normal merge
245 # Override needs to refresh standins so that update's normal merge
244 # will go through properly. Then the other update hook (overriding repo.update)
246 # will go through properly. Then the other update hook (overriding repo.update)
245 # will get the new files. Filemerge is also overriden so that the merge
247 # will get the new files. Filemerge is also overriden so that the merge
246 # will merge standins correctly.
248 # will merge standins correctly.
247 def overrideupdate(orig, ui, repo, *pats, **opts):
249 def overrideupdate(orig, ui, repo, *pats, **opts):
248 lfdirstate = lfutil.openlfdirstate(ui, repo)
250 lfdirstate = lfutil.openlfdirstate(ui, repo)
249 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
251 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
250 False, False)
252 False, False)
251 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
253 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
252
254
253 # Need to lock between the standins getting updated and their
255 # Need to lock between the standins getting updated and their
254 # largefiles getting updated
256 # largefiles getting updated
255 wlock = repo.wlock()
257 wlock = repo.wlock()
256 try:
258 try:
257 if opts['check']:
259 if opts['check']:
258 mod = len(modified) > 0
260 mod = len(modified) > 0
259 for lfile in unsure:
261 for lfile in unsure:
260 standin = lfutil.standin(lfile)
262 standin = lfutil.standin(lfile)
261 if repo['.'][standin].data().strip() != \
263 if repo['.'][standin].data().strip() != \
262 lfutil.hashfile(repo.wjoin(lfile)):
264 lfutil.hashfile(repo.wjoin(lfile)):
263 mod = True
265 mod = True
264 else:
266 else:
265 lfdirstate.normal(lfile)
267 lfdirstate.normal(lfile)
266 lfdirstate.write()
268 lfdirstate.write()
267 if mod:
269 if mod:
268 raise util.Abort(_('uncommitted local changes'))
270 raise util.Abort(_('uncommitted local changes'))
269 # XXX handle removed differently
271 # XXX handle removed differently
270 if not opts['clean']:
272 if not opts['clean']:
271 for lfile in unsure + modified + added:
273 for lfile in unsure + modified + added:
272 lfutil.updatestandin(repo, lfutil.standin(lfile))
274 lfutil.updatestandin(repo, lfutil.standin(lfile))
273 finally:
275 finally:
274 wlock.release()
276 wlock.release()
275 return orig(ui, repo, *pats, **opts)
277 return orig(ui, repo, *pats, **opts)
276
278
277 # Before starting the manifest merge, merge.updates will call
279 # Before starting the manifest merge, merge.updates will call
278 # _checkunknown to check if there are any files in the merged-in
280 # _checkunknown to check if there are any files in the merged-in
279 # changeset that collide with unknown files in the working copy.
281 # changeset that collide with unknown files in the working copy.
280 #
282 #
281 # The largefiles are seen as unknown, so this prevents us from merging
283 # The largefiles are seen as unknown, so this prevents us from merging
282 # in a file 'foo' if we already have a largefile with the same name.
284 # in a file 'foo' if we already have a largefile with the same name.
283 #
285 #
284 # The overridden function filters the unknown files by removing any
286 # The overridden function filters the unknown files by removing any
285 # largefiles. This makes the merge proceed and we can then handle this
287 # largefiles. This makes the merge proceed and we can then handle this
286 # case further in the overridden manifestmerge function below.
288 # case further in the overridden manifestmerge function below.
287 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
289 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
288 if lfutil.standin(f) in wctx:
290 if lfutil.standin(f) in wctx:
289 return False
291 return False
290 return origfn(repo, wctx, mctx, f)
292 return origfn(repo, wctx, mctx, f)
291
293
292 # The manifest merge handles conflicts on the manifest level. We want
294 # The manifest merge handles conflicts on the manifest level. We want
293 # to handle changes in largefile-ness of files at this level too.
295 # to handle changes in largefile-ness of files at this level too.
294 #
296 #
295 # The strategy is to run the original manifestmerge and then process
297 # The strategy is to run the original manifestmerge and then process
296 # the action list it outputs. There are two cases we need to deal with:
298 # the action list it outputs. There are two cases we need to deal with:
297 #
299 #
298 # 1. Normal file in p1, largefile in p2. Here the largefile is
300 # 1. Normal file in p1, largefile in p2. Here the largefile is
299 # detected via its standin file, which will enter the working copy
301 # detected via its standin file, which will enter the working copy
300 # with a "get" action. It is not "merge" since the standin is all
302 # with a "get" action. It is not "merge" since the standin is all
301 # Mercurial is concerned with at this level -- the link to the
303 # Mercurial is concerned with at this level -- the link to the
302 # existing normal file is not relevant here.
304 # existing normal file is not relevant here.
303 #
305 #
304 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
306 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
305 # since the largefile will be present in the working copy and
307 # since the largefile will be present in the working copy and
306 # different from the normal file in p2. Mercurial therefore
308 # different from the normal file in p2. Mercurial therefore
307 # triggers a merge action.
309 # triggers a merge action.
308 #
310 #
309 # In both cases, we prompt the user and emit new actions to either
311 # In both cases, we prompt the user and emit new actions to either
310 # remove the standin (if the normal file was kept) or to remove the
312 # remove the standin (if the normal file was kept) or to remove the
311 # normal file and get the standin (if the largefile was kept). The
313 # normal file and get the standin (if the largefile was kept). The
312 # default prompt answer is to use the largefile version since it was
314 # default prompt answer is to use the largefile version since it was
313 # presumably changed on purpose.
315 # presumably changed on purpose.
314 #
316 #
315 # Finally, the merge.applyupdates function will then take care of
317 # Finally, the merge.applyupdates function will then take care of
316 # writing the files into the working copy and lfcommands.updatelfiles
318 # writing the files into the working copy and lfcommands.updatelfiles
317 # will update the largefiles.
319 # will update the largefiles.
318 def overridemanifestmerge(origfn, repo, p1, p2, pa, overwrite, partial):
320 def overridemanifestmerge(origfn, repo, p1, p2, pa, overwrite, partial):
319 actions = origfn(repo, p1, p2, pa, overwrite, partial)
321 actions = origfn(repo, p1, p2, pa, overwrite, partial)
320 processed = []
322 processed = []
321
323
322 for action in actions:
324 for action in actions:
323 if overwrite:
325 if overwrite:
324 processed.append(action)
326 processed.append(action)
325 continue
327 continue
326 f, m = action[:2]
328 f, m = action[:2]
327
329
328 choices = (_('&Largefile'), _('&Normal file'))
330 choices = (_('&Largefile'), _('&Normal file'))
329 if m == "g" and lfutil.splitstandin(f) in p1 and f in p2:
331 if m == "g" and lfutil.splitstandin(f) in p1 and f in p2:
330 # Case 1: normal file in the working copy, largefile in
332 # Case 1: normal file in the working copy, largefile in
331 # the second parent
333 # the second parent
332 lfile = lfutil.splitstandin(f)
334 lfile = lfutil.splitstandin(f)
333 standin = f
335 standin = f
334 msg = _('%s has been turned into a largefile\n'
336 msg = _('%s has been turned into a largefile\n'
335 'use (l)argefile or keep as (n)ormal file?') % lfile
337 'use (l)argefile or keep as (n)ormal file?') % lfile
336 if repo.ui.promptchoice(msg, choices, 0) == 0:
338 if repo.ui.promptchoice(msg, choices, 0) == 0:
337 processed.append((lfile, "r"))
339 processed.append((lfile, "r"))
338 processed.append((standin, "g", p2.flags(standin)))
340 processed.append((standin, "g", p2.flags(standin)))
339 else:
341 else:
340 processed.append((standin, "r"))
342 processed.append((standin, "r"))
341 elif m == "g" and lfutil.standin(f) in p1 and f in p2:
343 elif m == "g" and lfutil.standin(f) in p1 and f in p2:
342 # Case 2: largefile in the working copy, normal file in
344 # Case 2: largefile in the working copy, normal file in
343 # the second parent
345 # the second parent
344 standin = lfutil.standin(f)
346 standin = lfutil.standin(f)
345 lfile = f
347 lfile = f
346 msg = _('%s has been turned into a normal file\n'
348 msg = _('%s has been turned into a normal file\n'
347 'keep as (l)argefile or use (n)ormal file?') % lfile
349 'keep as (l)argefile or use (n)ormal file?') % lfile
348 if repo.ui.promptchoice(msg, choices, 0) == 0:
350 if repo.ui.promptchoice(msg, choices, 0) == 0:
349 processed.append((lfile, "r"))
351 processed.append((lfile, "r"))
350 else:
352 else:
351 processed.append((standin, "r"))
353 processed.append((standin, "r"))
352 processed.append((lfile, "g", p2.flags(lfile)))
354 processed.append((lfile, "g", p2.flags(lfile)))
353 else:
355 else:
354 processed.append(action)
356 processed.append(action)
355
357
356 return processed
358 return processed
357
359
358 # Override filemerge to prompt the user about how they wish to merge
360 # Override filemerge to prompt the user about how they wish to merge
359 # largefiles. This will handle identical edits, and copy/rename +
361 # largefiles. This will handle identical edits, and copy/rename +
360 # edit without prompting the user.
362 # edit without prompting the user.
361 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca):
363 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca):
362 # Use better variable names here. Because this is a wrapper we cannot
364 # Use better variable names here. Because this is a wrapper we cannot
363 # change the variable names in the function declaration.
365 # change the variable names in the function declaration.
364 fcdest, fcother, fcancestor = fcd, fco, fca
366 fcdest, fcother, fcancestor = fcd, fco, fca
365 if not lfutil.isstandin(orig):
367 if not lfutil.isstandin(orig):
366 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
368 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
367 else:
369 else:
368 if not fcother.cmp(fcdest): # files identical?
370 if not fcother.cmp(fcdest): # files identical?
369 return None
371 return None
370
372
371 # backwards, use working dir parent as ancestor
373 # backwards, use working dir parent as ancestor
372 if fcancestor == fcother:
374 if fcancestor == fcother:
373 fcancestor = fcdest.parents()[0]
375 fcancestor = fcdest.parents()[0]
374
376
375 if orig != fcother.path():
377 if orig != fcother.path():
376 repo.ui.status(_('merging %s and %s to %s\n')
378 repo.ui.status(_('merging %s and %s to %s\n')
377 % (lfutil.splitstandin(orig),
379 % (lfutil.splitstandin(orig),
378 lfutil.splitstandin(fcother.path()),
380 lfutil.splitstandin(fcother.path()),
379 lfutil.splitstandin(fcdest.path())))
381 lfutil.splitstandin(fcdest.path())))
380 else:
382 else:
381 repo.ui.status(_('merging %s\n')
383 repo.ui.status(_('merging %s\n')
382 % lfutil.splitstandin(fcdest.path()))
384 % lfutil.splitstandin(fcdest.path()))
383
385
384 if fcancestor.path() != fcother.path() and fcother.data() == \
386 if fcancestor.path() != fcother.path() and fcother.data() == \
385 fcancestor.data():
387 fcancestor.data():
386 return 0
388 return 0
387 if fcancestor.path() != fcdest.path() and fcdest.data() == \
389 if fcancestor.path() != fcdest.path() and fcdest.data() == \
388 fcancestor.data():
390 fcancestor.data():
389 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
391 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
390 return 0
392 return 0
391
393
392 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
394 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
393 'keep (l)ocal or take (o)ther?') %
395 'keep (l)ocal or take (o)ther?') %
394 lfutil.splitstandin(orig),
396 lfutil.splitstandin(orig),
395 (_('&Local'), _('&Other')), 0) == 0:
397 (_('&Local'), _('&Other')), 0) == 0:
396 return 0
398 return 0
397 else:
399 else:
398 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
400 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
399 return 0
401 return 0
400
402
401 # Copy first changes the matchers to match standins instead of
403 # Copy first changes the matchers to match standins instead of
402 # largefiles. Then it overrides util.copyfile in that function it
404 # largefiles. Then it overrides util.copyfile in that function it
403 # checks if the destination largefile already exists. It also keeps a
405 # checks if the destination largefile already exists. It also keeps a
404 # list of copied files so that the largefiles can be copied and the
406 # list of copied files so that the largefiles can be copied and the
405 # dirstate updated.
407 # dirstate updated.
406 def overridecopy(orig, ui, repo, pats, opts, rename=False):
408 def overridecopy(orig, ui, repo, pats, opts, rename=False):
407 # doesn't remove largefile on rename
409 # doesn't remove largefile on rename
408 if len(pats) < 2:
410 if len(pats) < 2:
409 # this isn't legal, let the original function deal with it
411 # this isn't legal, let the original function deal with it
410 return orig(ui, repo, pats, opts, rename)
412 return orig(ui, repo, pats, opts, rename)
411
413
412 def makestandin(relpath):
414 def makestandin(relpath):
413 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
415 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
414 return os.path.join(repo.wjoin(lfutil.standin(path)))
416 return os.path.join(repo.wjoin(lfutil.standin(path)))
415
417
416 fullpats = scmutil.expandpats(pats)
418 fullpats = scmutil.expandpats(pats)
417 dest = fullpats[-1]
419 dest = fullpats[-1]
418
420
419 if os.path.isdir(dest):
421 if os.path.isdir(dest):
420 if not os.path.isdir(makestandin(dest)):
422 if not os.path.isdir(makestandin(dest)):
421 os.makedirs(makestandin(dest))
423 os.makedirs(makestandin(dest))
422 # This could copy both lfiles and normal files in one command,
424 # This could copy both lfiles and normal files in one command,
423 # but we don't want to do that. First replace their matcher to
425 # but we don't want to do that. First replace their matcher to
424 # only match normal files and run it, then replace it to just
426 # only match normal files and run it, then replace it to just
425 # match largefiles and run it again.
427 # match largefiles and run it again.
426 nonormalfiles = False
428 nonormalfiles = False
427 nolfiles = False
429 nolfiles = False
428 try:
430 try:
429 try:
431 try:
430 installnormalfilesmatchfn(repo[None].manifest())
432 installnormalfilesmatchfn(repo[None].manifest())
431 result = orig(ui, repo, pats, opts, rename)
433 result = orig(ui, repo, pats, opts, rename)
432 except util.Abort, e:
434 except util.Abort, e:
433 if str(e) != 'no files to copy':
435 if str(e) != 'no files to copy':
434 raise e
436 raise e
435 else:
437 else:
436 nonormalfiles = True
438 nonormalfiles = True
437 result = 0
439 result = 0
438 finally:
440 finally:
439 restorematchfn()
441 restorematchfn()
440
442
441 # The first rename can cause our current working directory to be removed.
443 # The first rename can cause our current working directory to be removed.
442 # In that case there is nothing left to copy/rename so just quit.
444 # In that case there is nothing left to copy/rename so just quit.
443 try:
445 try:
444 repo.getcwd()
446 repo.getcwd()
445 except OSError:
447 except OSError:
446 return result
448 return result
447
449
448 try:
450 try:
449 try:
451 try:
450 # When we call orig below it creates the standins but we don't add
452 # When we call orig below it creates the standins but we don't add
451 # them to the dir state until later so lock during that time.
453 # them to the dir state until later so lock during that time.
452 wlock = repo.wlock()
454 wlock = repo.wlock()
453
455
454 manifest = repo[None].manifest()
456 manifest = repo[None].manifest()
455 oldmatch = None # for the closure
457 oldmatch = None # for the closure
456 def overridematch(ctx, pats=[], opts={}, globbed=False,
458 def overridematch(ctx, pats=[], opts={}, globbed=False,
457 default='relpath'):
459 default='relpath'):
458 newpats = []
460 newpats = []
459 # The patterns were previously mangled to add the standin
461 # The patterns were previously mangled to add the standin
460 # directory; we need to remove that now
462 # directory; we need to remove that now
461 for pat in pats:
463 for pat in pats:
462 if match_.patkind(pat) is None and lfutil.shortname in pat:
464 if match_.patkind(pat) is None and lfutil.shortname in pat:
463 newpats.append(pat.replace(lfutil.shortname, ''))
465 newpats.append(pat.replace(lfutil.shortname, ''))
464 else:
466 else:
465 newpats.append(pat)
467 newpats.append(pat)
466 match = oldmatch(ctx, newpats, opts, globbed, default)
468 match = oldmatch(ctx, newpats, opts, globbed, default)
467 m = copy.copy(match)
469 m = copy.copy(match)
468 lfile = lambda f: lfutil.standin(f) in manifest
470 lfile = lambda f: lfutil.standin(f) in manifest
469 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
471 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
470 m._fmap = set(m._files)
472 m._fmap = set(m._files)
471 origmatchfn = m.matchfn
473 origmatchfn = m.matchfn
472 m.matchfn = lambda f: (lfutil.isstandin(f) and
474 m.matchfn = lambda f: (lfutil.isstandin(f) and
473 (f in manifest) and
475 (f in manifest) and
474 origmatchfn(lfutil.splitstandin(f)) or
476 origmatchfn(lfutil.splitstandin(f)) or
475 None)
477 None)
476 return m
478 return m
477 oldmatch = installmatchfn(overridematch)
479 oldmatch = installmatchfn(overridematch)
478 listpats = []
480 listpats = []
479 for pat in pats:
481 for pat in pats:
480 if match_.patkind(pat) is not None:
482 if match_.patkind(pat) is not None:
481 listpats.append(pat)
483 listpats.append(pat)
482 else:
484 else:
483 listpats.append(makestandin(pat))
485 listpats.append(makestandin(pat))
484
486
485 try:
487 try:
486 origcopyfile = util.copyfile
488 origcopyfile = util.copyfile
487 copiedfiles = []
489 copiedfiles = []
488 def overridecopyfile(src, dest):
490 def overridecopyfile(src, dest):
489 if (lfutil.shortname in src and
491 if (lfutil.shortname in src and
490 dest.startswith(repo.wjoin(lfutil.shortname))):
492 dest.startswith(repo.wjoin(lfutil.shortname))):
491 destlfile = dest.replace(lfutil.shortname, '')
493 destlfile = dest.replace(lfutil.shortname, '')
492 if not opts['force'] and os.path.exists(destlfile):
494 if not opts['force'] and os.path.exists(destlfile):
493 raise IOError('',
495 raise IOError('',
494 _('destination largefile already exists'))
496 _('destination largefile already exists'))
495 copiedfiles.append((src, dest))
497 copiedfiles.append((src, dest))
496 origcopyfile(src, dest)
498 origcopyfile(src, dest)
497
499
498 util.copyfile = overridecopyfile
500 util.copyfile = overridecopyfile
499 result += orig(ui, repo, listpats, opts, rename)
501 result += orig(ui, repo, listpats, opts, rename)
500 finally:
502 finally:
501 util.copyfile = origcopyfile
503 util.copyfile = origcopyfile
502
504
503 lfdirstate = lfutil.openlfdirstate(ui, repo)
505 lfdirstate = lfutil.openlfdirstate(ui, repo)
504 for (src, dest) in copiedfiles:
506 for (src, dest) in copiedfiles:
505 if (lfutil.shortname in src and
507 if (lfutil.shortname in src and
506 dest.startswith(repo.wjoin(lfutil.shortname))):
508 dest.startswith(repo.wjoin(lfutil.shortname))):
507 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
509 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
508 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
510 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
509 destlfiledir = os.path.dirname(destlfile) or '.'
511 destlfiledir = os.path.dirname(destlfile) or '.'
510 if not os.path.isdir(destlfiledir):
512 if not os.path.isdir(destlfiledir):
511 os.makedirs(destlfiledir)
513 os.makedirs(destlfiledir)
512 if rename:
514 if rename:
513 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
515 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
514 lfdirstate.remove(srclfile)
516 lfdirstate.remove(srclfile)
515 else:
517 else:
516 util.copyfile(srclfile, destlfile)
518 util.copyfile(srclfile, destlfile)
517 lfdirstate.add(destlfile)
519 lfdirstate.add(destlfile)
518 lfdirstate.write()
520 lfdirstate.write()
519 except util.Abort, e:
521 except util.Abort, e:
520 if str(e) != 'no files to copy':
522 if str(e) != 'no files to copy':
521 raise e
523 raise e
522 else:
524 else:
523 nolfiles = True
525 nolfiles = True
524 finally:
526 finally:
525 restorematchfn()
527 restorematchfn()
526 wlock.release()
528 wlock.release()
527
529
528 if nolfiles and nonormalfiles:
530 if nolfiles and nonormalfiles:
529 raise util.Abort(_('no files to copy'))
531 raise util.Abort(_('no files to copy'))
530
532
531 return result
533 return result
532
534
533 # When the user calls revert, we have to be careful to not revert any
535 # When the user calls revert, we have to be careful to not revert any
534 # changes to other largefiles accidentally. This means we have to keep
536 # changes to other largefiles accidentally. This means we have to keep
535 # track of the largefiles that are being reverted so we only pull down
537 # track of the largefiles that are being reverted so we only pull down
536 # the necessary largefiles.
538 # the necessary largefiles.
537 #
539 #
538 # Standins are only updated (to match the hash of largefiles) before
540 # Standins are only updated (to match the hash of largefiles) before
539 # commits. Update the standins then run the original revert, changing
541 # commits. Update the standins then run the original revert, changing
540 # the matcher to hit standins instead of largefiles. Based on the
542 # the matcher to hit standins instead of largefiles. Based on the
541 # resulting standins update the largefiles. Then return the standins
543 # resulting standins update the largefiles. Then return the standins
542 # to their proper state
544 # to their proper state
543 def overriderevert(orig, ui, repo, *pats, **opts):
545 def overriderevert(orig, ui, repo, *pats, **opts):
544 # Because we put the standins in a bad state (by updating them)
546 # Because we put the standins in a bad state (by updating them)
545 # and then return them to a correct state we need to lock to
547 # and then return them to a correct state we need to lock to
546 # prevent others from changing them in their incorrect state.
548 # prevent others from changing them in their incorrect state.
547 wlock = repo.wlock()
549 wlock = repo.wlock()
548 try:
550 try:
549 lfdirstate = lfutil.openlfdirstate(ui, repo)
551 lfdirstate = lfutil.openlfdirstate(ui, repo)
550 (modified, added, removed, missing, unknown, ignored, clean) = \
552 (modified, added, removed, missing, unknown, ignored, clean) = \
551 lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
553 lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
552 for lfile in modified:
554 for lfile in modified:
553 lfutil.updatestandin(repo, lfutil.standin(lfile))
555 lfutil.updatestandin(repo, lfutil.standin(lfile))
554 for lfile in missing:
556 for lfile in missing:
555 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
557 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
556 os.unlink(repo.wjoin(lfutil.standin(lfile)))
558 os.unlink(repo.wjoin(lfutil.standin(lfile)))
557
559
558 try:
560 try:
559 ctx = repo[opts.get('rev')]
561 ctx = repo[opts.get('rev')]
560 oldmatch = None # for the closure
562 oldmatch = None # for the closure
561 def overridematch(ctx, pats=[], opts={}, globbed=False,
563 def overridematch(ctx, pats=[], opts={}, globbed=False,
562 default='relpath'):
564 default='relpath'):
563 match = oldmatch(ctx, pats, opts, globbed, default)
565 match = oldmatch(ctx, pats, opts, globbed, default)
564 m = copy.copy(match)
566 m = copy.copy(match)
565 def tostandin(f):
567 def tostandin(f):
566 if lfutil.standin(f) in ctx:
568 if lfutil.standin(f) in ctx:
567 return lfutil.standin(f)
569 return lfutil.standin(f)
568 elif lfutil.standin(f) in repo[None]:
570 elif lfutil.standin(f) in repo[None]:
569 return None
571 return None
570 return f
572 return f
571 m._files = [tostandin(f) for f in m._files]
573 m._files = [tostandin(f) for f in m._files]
572 m._files = [f for f in m._files if f is not None]
574 m._files = [f for f in m._files if f is not None]
573 m._fmap = set(m._files)
575 m._fmap = set(m._files)
574 origmatchfn = m.matchfn
576 origmatchfn = m.matchfn
575 def matchfn(f):
577 def matchfn(f):
576 if lfutil.isstandin(f):
578 if lfutil.isstandin(f):
577 # We need to keep track of what largefiles are being
579 # We need to keep track of what largefiles are being
578 # matched so we know which ones to update later --
580 # matched so we know which ones to update later --
579 # otherwise we accidentally revert changes to other
581 # otherwise we accidentally revert changes to other
580 # largefiles. This is repo-specific, so duckpunch the
582 # largefiles. This is repo-specific, so duckpunch the
581 # repo object to keep the list of largefiles for us
583 # repo object to keep the list of largefiles for us
582 # later.
584 # later.
583 if origmatchfn(lfutil.splitstandin(f)) and \
585 if origmatchfn(lfutil.splitstandin(f)) and \
584 (f in repo[None] or f in ctx):
586 (f in repo[None] or f in ctx):
585 lfileslist = getattr(repo, '_lfilestoupdate', [])
587 lfileslist = getattr(repo, '_lfilestoupdate', [])
586 lfileslist.append(lfutil.splitstandin(f))
588 lfileslist.append(lfutil.splitstandin(f))
587 repo._lfilestoupdate = lfileslist
589 repo._lfilestoupdate = lfileslist
588 return True
590 return True
589 else:
591 else:
590 return False
592 return False
591 return origmatchfn(f)
593 return origmatchfn(f)
592 m.matchfn = matchfn
594 m.matchfn = matchfn
593 return m
595 return m
594 oldmatch = installmatchfn(overridematch)
596 oldmatch = installmatchfn(overridematch)
595 scmutil.match
597 scmutil.match
596 matches = overridematch(repo[None], pats, opts)
598 matches = overridematch(repo[None], pats, opts)
597 orig(ui, repo, *pats, **opts)
599 orig(ui, repo, *pats, **opts)
598 finally:
600 finally:
599 restorematchfn()
601 restorematchfn()
600 lfileslist = getattr(repo, '_lfilestoupdate', [])
602 lfileslist = getattr(repo, '_lfilestoupdate', [])
601 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
603 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
602 printmessage=False)
604 printmessage=False)
603
605
604 # empty out the largefiles list so we start fresh next time
606 # empty out the largefiles list so we start fresh next time
605 repo._lfilestoupdate = []
607 repo._lfilestoupdate = []
606 for lfile in modified:
608 for lfile in modified:
607 if lfile in lfileslist:
609 if lfile in lfileslist:
608 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
610 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
609 in repo['.']:
611 in repo['.']:
610 lfutil.writestandin(repo, lfutil.standin(lfile),
612 lfutil.writestandin(repo, lfutil.standin(lfile),
611 repo['.'][lfile].data().strip(),
613 repo['.'][lfile].data().strip(),
612 'x' in repo['.'][lfile].flags())
614 'x' in repo['.'][lfile].flags())
613 lfdirstate = lfutil.openlfdirstate(ui, repo)
615 lfdirstate = lfutil.openlfdirstate(ui, repo)
614 for lfile in added:
616 for lfile in added:
615 standin = lfutil.standin(lfile)
617 standin = lfutil.standin(lfile)
616 if standin not in ctx and (standin in matches or opts.get('all')):
618 if standin not in ctx and (standin in matches or opts.get('all')):
617 if lfile in lfdirstate:
619 if lfile in lfdirstate:
618 lfdirstate.drop(lfile)
620 lfdirstate.drop(lfile)
619 util.unlinkpath(repo.wjoin(standin))
621 util.unlinkpath(repo.wjoin(standin))
620 lfdirstate.write()
622 lfdirstate.write()
621 finally:
623 finally:
622 wlock.release()
624 wlock.release()
623
625
624 def hgupdate(orig, repo, node):
626 def hgupdate(orig, repo, node):
625 # Only call updatelfiles the standins that have changed to save time
627 # Only call updatelfiles the standins that have changed to save time
626 oldstandins = lfutil.getstandinsstate(repo)
628 oldstandins = lfutil.getstandinsstate(repo)
627 result = orig(repo, node)
629 result = orig(repo, node)
628 newstandins = lfutil.getstandinsstate(repo)
630 newstandins = lfutil.getstandinsstate(repo)
629 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
631 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
630 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist, printmessage=True)
632 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist, printmessage=True)
631 return result
633 return result
632
634
633 def hgclean(orig, repo, node, show_stats=True):
635 def hgclean(orig, repo, node, show_stats=True):
634 result = orig(repo, node, show_stats)
636 result = orig(repo, node, show_stats)
635 lfcommands.updatelfiles(repo.ui, repo)
637 lfcommands.updatelfiles(repo.ui, repo)
636 return result
638 return result
637
639
638 def hgmerge(orig, repo, node, force=None, remind=True):
640 def hgmerge(orig, repo, node, force=None, remind=True):
639 # Mark the repo as being in the middle of a merge, so that
641 # Mark the repo as being in the middle of a merge, so that
640 # updatelfiles() will know that it needs to trust the standins in
642 # updatelfiles() will know that it needs to trust the standins in
641 # the working copy, not in the standins in the current node
643 # the working copy, not in the standins in the current node
642 repo._ismerging = True
644 repo._ismerging = True
643 try:
645 try:
644 result = orig(repo, node, force, remind)
646 result = orig(repo, node, force, remind)
645 lfcommands.updatelfiles(repo.ui, repo)
647 lfcommands.updatelfiles(repo.ui, repo)
646 finally:
648 finally:
647 repo._ismerging = False
649 repo._ismerging = False
648 return result
650 return result
649
651
650 # When we rebase a repository with remotely changed largefiles, we need to
652 # When we rebase a repository with remotely changed largefiles, we need to
651 # take some extra care so that the largefiles are correctly updated in the
653 # take some extra care so that the largefiles are correctly updated in the
652 # working copy
654 # working copy
653 def overridepull(orig, ui, repo, source=None, **opts):
655 def overridepull(orig, ui, repo, source=None, **opts):
654 revsprepull = len(repo)
656 revsprepull = len(repo)
655 if opts.get('rebase', False):
657 if opts.get('rebase', False):
656 repo._isrebasing = True
658 repo._isrebasing = True
657 try:
659 try:
658 if opts.get('update'):
660 if opts.get('update'):
659 del opts['update']
661 del opts['update']
660 ui.debug('--update and --rebase are not compatible, ignoring '
662 ui.debug('--update and --rebase are not compatible, ignoring '
661 'the update flag\n')
663 'the update flag\n')
662 del opts['rebase']
664 del opts['rebase']
663 cmdutil.bailifchanged(repo)
665 cmdutil.bailifchanged(repo)
664 origpostincoming = commands.postincoming
666 origpostincoming = commands.postincoming
665 def _dummy(*args, **kwargs):
667 def _dummy(*args, **kwargs):
666 pass
668 pass
667 commands.postincoming = _dummy
669 commands.postincoming = _dummy
668 repo.lfpullsource = source
670 repo.lfpullsource = source
669 if not source:
671 if not source:
670 source = 'default'
672 source = 'default'
671 try:
673 try:
672 result = commands.pull(ui, repo, source, **opts)
674 result = commands.pull(ui, repo, source, **opts)
673 finally:
675 finally:
674 commands.postincoming = origpostincoming
676 commands.postincoming = origpostincoming
675 revspostpull = len(repo)
677 revspostpull = len(repo)
676 if revspostpull > revsprepull:
678 if revspostpull > revsprepull:
677 result = result or rebase.rebase(ui, repo)
679 result = result or rebase.rebase(ui, repo)
678 finally:
680 finally:
679 repo._isrebasing = False
681 repo._isrebasing = False
680 else:
682 else:
681 repo.lfpullsource = source
683 repo.lfpullsource = source
682 if not source:
684 if not source:
683 source = 'default'
685 source = 'default'
684 oldheads = lfutil.getcurrentheads(repo)
686 oldheads = lfutil.getcurrentheads(repo)
685 result = orig(ui, repo, source, **opts)
687 result = orig(ui, repo, source, **opts)
686 # If we do not have the new largefiles for any new heads we pulled, we
688 # If we do not have the new largefiles for any new heads we pulled, we
687 # will run into a problem later if we try to merge or rebase with one of
689 # will run into a problem later if we try to merge or rebase with one of
688 # these heads, so cache the largefiles now direclty into the system
690 # these heads, so cache the largefiles now direclty into the system
689 # cache.
691 # cache.
690 ui.status(_("caching new largefiles\n"))
692 ui.status(_("caching new largefiles\n"))
691 numcached = 0
693 numcached = 0
692 heads = lfutil.getcurrentheads(repo)
694 heads = lfutil.getcurrentheads(repo)
693 newheads = set(heads).difference(set(oldheads))
695 newheads = set(heads).difference(set(oldheads))
694 for head in newheads:
696 for head in newheads:
695 (cached, missing) = lfcommands.cachelfiles(ui, repo, head)
697 (cached, missing) = lfcommands.cachelfiles(ui, repo, head)
696 numcached += len(cached)
698 numcached += len(cached)
697 ui.status(_("%d largefiles cached\n") % numcached)
699 ui.status(_("%d largefiles cached\n") % numcached)
698 if opts.get('all_largefiles'):
700 if opts.get('all_largefiles'):
699 revspostpull = len(repo)
701 revspostpull = len(repo)
700 revs = []
702 revs = []
701 for rev in xrange(revsprepull + 1, revspostpull):
703 for rev in xrange(revsprepull + 1, revspostpull):
702 revs.append(repo[rev].rev())
704 revs.append(repo[rev].rev())
703 lfcommands.downloadlfiles(ui, repo, revs)
705 lfcommands.downloadlfiles(ui, repo, revs)
704 return result
706 return result
705
707
706 def overrideclone(orig, ui, source, dest=None, **opts):
708 def overrideclone(orig, ui, source, dest=None, **opts):
707 if dest is None:
709 if dest is None:
708 dest = hg.defaultdest(source)
710 dest = hg.defaultdest(source)
709 if opts.get('all_largefiles') and not hg.islocal(dest):
711 if opts.get('all_largefiles') and not hg.islocal(dest):
710 raise util.Abort(_(
712 raise util.Abort(_(
711 '--all-largefiles is incompatible with non-local destination %s' %
713 '--all-largefiles is incompatible with non-local destination %s' %
712 dest))
714 dest))
713 result = hg.clone(ui, opts, source, dest,
715 result = hg.clone(ui, opts, source, dest,
714 pull=opts.get('pull'),
716 pull=opts.get('pull'),
715 stream=opts.get('uncompressed'),
717 stream=opts.get('uncompressed'),
716 rev=opts.get('rev'),
718 rev=opts.get('rev'),
717 update=True, # required for successful walkchangerevs
719 update=True, # required for successful walkchangerevs
718 branch=opts.get('branch'))
720 branch=opts.get('branch'))
719 if result is None:
721 if result is None:
720 return True
722 return True
721 if opts.get('all_largefiles'):
723 if opts.get('all_largefiles'):
722 sourcerepo, destrepo = result
724 sourcerepo, destrepo = result
723 success, missing = lfcommands.downloadlfiles(ui, destrepo, None)
725 success, missing = lfcommands.downloadlfiles(ui, destrepo, None)
724 return missing != 0
726 return missing != 0
725 return result is None
727 return result is None
726
728
727 def overriderebase(orig, ui, repo, **opts):
729 def overriderebase(orig, ui, repo, **opts):
728 repo._isrebasing = True
730 repo._isrebasing = True
729 try:
731 try:
730 orig(ui, repo, **opts)
732 orig(ui, repo, **opts)
731 finally:
733 finally:
732 repo._isrebasing = False
734 repo._isrebasing = False
733
735
734 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
736 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
735 prefix=None, mtime=None, subrepos=None):
737 prefix=None, mtime=None, subrepos=None):
736 # No need to lock because we are only reading history and
738 # No need to lock because we are only reading history and
737 # largefile caches, neither of which are modified.
739 # largefile caches, neither of which are modified.
738 lfcommands.cachelfiles(repo.ui, repo, node)
740 lfcommands.cachelfiles(repo.ui, repo, node)
739
741
740 if kind not in archival.archivers:
742 if kind not in archival.archivers:
741 raise util.Abort(_("unknown archive type '%s'") % kind)
743 raise util.Abort(_("unknown archive type '%s'") % kind)
742
744
743 ctx = repo[node]
745 ctx = repo[node]
744
746
745 if kind == 'files':
747 if kind == 'files':
746 if prefix:
748 if prefix:
747 raise util.Abort(
749 raise util.Abort(
748 _('cannot give prefix when archiving to files'))
750 _('cannot give prefix when archiving to files'))
749 else:
751 else:
750 prefix = archival.tidyprefix(dest, kind, prefix)
752 prefix = archival.tidyprefix(dest, kind, prefix)
751
753
752 def write(name, mode, islink, getdata):
754 def write(name, mode, islink, getdata):
753 if matchfn and not matchfn(name):
755 if matchfn and not matchfn(name):
754 return
756 return
755 data = getdata()
757 data = getdata()
756 if decode:
758 if decode:
757 data = repo.wwritedata(name, data)
759 data = repo.wwritedata(name, data)
758 archiver.addfile(prefix + name, mode, islink, data)
760 archiver.addfile(prefix + name, mode, islink, data)
759
761
760 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
762 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
761
763
762 if repo.ui.configbool("ui", "archivemeta", True):
764 if repo.ui.configbool("ui", "archivemeta", True):
763 def metadata():
765 def metadata():
764 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
766 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
765 hex(repo.changelog.node(0)), hex(node), ctx.branch())
767 hex(repo.changelog.node(0)), hex(node), ctx.branch())
766
768
767 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
769 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
768 if repo.tagtype(t) == 'global')
770 if repo.tagtype(t) == 'global')
769 if not tags:
771 if not tags:
770 repo.ui.pushbuffer()
772 repo.ui.pushbuffer()
771 opts = {'template': '{latesttag}\n{latesttagdistance}',
773 opts = {'template': '{latesttag}\n{latesttagdistance}',
772 'style': '', 'patch': None, 'git': None}
774 'style': '', 'patch': None, 'git': None}
773 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
775 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
774 ltags, dist = repo.ui.popbuffer().split('\n')
776 ltags, dist = repo.ui.popbuffer().split('\n')
775 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
777 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
776 tags += 'latesttagdistance: %s\n' % dist
778 tags += 'latesttagdistance: %s\n' % dist
777
779
778 return base + tags
780 return base + tags
779
781
780 write('.hg_archival.txt', 0644, False, metadata)
782 write('.hg_archival.txt', 0644, False, metadata)
781
783
782 for f in ctx:
784 for f in ctx:
783 ff = ctx.flags(f)
785 ff = ctx.flags(f)
784 getdata = ctx[f].data
786 getdata = ctx[f].data
785 if lfutil.isstandin(f):
787 if lfutil.isstandin(f):
786 path = lfutil.findfile(repo, getdata().strip())
788 path = lfutil.findfile(repo, getdata().strip())
787 if path is None:
789 if path is None:
788 raise util.Abort(
790 raise util.Abort(
789 _('largefile %s not found in repo store or system cache')
791 _('largefile %s not found in repo store or system cache')
790 % lfutil.splitstandin(f))
792 % lfutil.splitstandin(f))
791 f = lfutil.splitstandin(f)
793 f = lfutil.splitstandin(f)
792
794
793 def getdatafn():
795 def getdatafn():
794 fd = None
796 fd = None
795 try:
797 try:
796 fd = open(path, 'rb')
798 fd = open(path, 'rb')
797 return fd.read()
799 return fd.read()
798 finally:
800 finally:
799 if fd:
801 if fd:
800 fd.close()
802 fd.close()
801
803
802 getdata = getdatafn
804 getdata = getdatafn
803 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
805 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
804
806
805 if subrepos:
807 if subrepos:
806 for subpath in ctx.substate:
808 for subpath in ctx.substate:
807 sub = ctx.sub(subpath)
809 sub = ctx.sub(subpath)
808 sub.archive(repo.ui, archiver, prefix)
810 sub.archive(repo.ui, archiver, prefix)
809
811
810 archiver.done()
812 archiver.done()
811
813
812 def hgsubrepoarchive(orig, repo, ui, archiver, prefix):
814 def hgsubrepoarchive(orig, repo, ui, archiver, prefix):
813 rev = repo._state[1]
815 rev = repo._state[1]
814 ctx = repo._repo[rev]
816 ctx = repo._repo[rev]
815
817
816 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
818 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
817
819
818 def write(name, mode, islink, getdata):
820 def write(name, mode, islink, getdata):
819 if lfutil.isstandin(name):
821 if lfutil.isstandin(name):
820 return
822 return
821 data = getdata()
823 data = getdata()
822
824
823 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
825 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
824
826
825 for f in ctx:
827 for f in ctx:
826 ff = ctx.flags(f)
828 ff = ctx.flags(f)
827 getdata = ctx[f].data
829 getdata = ctx[f].data
828 if lfutil.isstandin(f):
830 if lfutil.isstandin(f):
829 path = lfutil.findfile(repo._repo, getdata().strip())
831 path = lfutil.findfile(repo._repo, getdata().strip())
830 if path is None:
832 if path is None:
831 raise util.Abort(
833 raise util.Abort(
832 _('largefile %s not found in repo store or system cache')
834 _('largefile %s not found in repo store or system cache')
833 % lfutil.splitstandin(f))
835 % lfutil.splitstandin(f))
834 f = lfutil.splitstandin(f)
836 f = lfutil.splitstandin(f)
835
837
836 def getdatafn():
838 def getdatafn():
837 fd = None
839 fd = None
838 try:
840 try:
839 fd = open(os.path.join(prefix, path), 'rb')
841 fd = open(os.path.join(prefix, path), 'rb')
840 return fd.read()
842 return fd.read()
841 finally:
843 finally:
842 if fd:
844 if fd:
843 fd.close()
845 fd.close()
844
846
845 getdata = getdatafn
847 getdata = getdatafn
846
848
847 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
849 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
848
850
849 for subpath in ctx.substate:
851 for subpath in ctx.substate:
850 sub = ctx.sub(subpath)
852 sub = ctx.sub(subpath)
851 sub.archive(repo.ui, archiver, prefix)
853 sub.archive(repo.ui, archiver, prefix)
852
854
853 # If a largefile is modified, the change is not reflected in its
855 # If a largefile is modified, the change is not reflected in its
854 # standin until a commit. cmdutil.bailifchanged() raises an exception
856 # standin until a commit. cmdutil.bailifchanged() raises an exception
855 # if the repo has uncommitted changes. Wrap it to also check if
857 # if the repo has uncommitted changes. Wrap it to also check if
856 # largefiles were changed. This is used by bisect and backout.
858 # largefiles were changed. This is used by bisect and backout.
857 def overridebailifchanged(orig, repo):
859 def overridebailifchanged(orig, repo):
858 orig(repo)
860 orig(repo)
859 repo.lfstatus = True
861 repo.lfstatus = True
860 modified, added, removed, deleted = repo.status()[:4]
862 modified, added, removed, deleted = repo.status()[:4]
861 repo.lfstatus = False
863 repo.lfstatus = False
862 if modified or added or removed or deleted:
864 if modified or added or removed or deleted:
863 raise util.Abort(_('outstanding uncommitted changes'))
865 raise util.Abort(_('outstanding uncommitted changes'))
864
866
865 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
867 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
866 def overridefetch(orig, ui, repo, *pats, **opts):
868 def overridefetch(orig, ui, repo, *pats, **opts):
867 repo.lfstatus = True
869 repo.lfstatus = True
868 modified, added, removed, deleted = repo.status()[:4]
870 modified, added, removed, deleted = repo.status()[:4]
869 repo.lfstatus = False
871 repo.lfstatus = False
870 if modified or added or removed or deleted:
872 if modified or added or removed or deleted:
871 raise util.Abort(_('outstanding uncommitted changes'))
873 raise util.Abort(_('outstanding uncommitted changes'))
872 return orig(ui, repo, *pats, **opts)
874 return orig(ui, repo, *pats, **opts)
873
875
874 def overrideforget(orig, ui, repo, *pats, **opts):
876 def overrideforget(orig, ui, repo, *pats, **opts):
875 installnormalfilesmatchfn(repo[None].manifest())
877 installnormalfilesmatchfn(repo[None].manifest())
876 orig(ui, repo, *pats, **opts)
878 orig(ui, repo, *pats, **opts)
877 restorematchfn()
879 restorematchfn()
878 m = scmutil.match(repo[None], pats, opts)
880 m = scmutil.match(repo[None], pats, opts)
879
881
880 try:
882 try:
881 repo.lfstatus = True
883 repo.lfstatus = True
882 s = repo.status(match=m, clean=True)
884 s = repo.status(match=m, clean=True)
883 finally:
885 finally:
884 repo.lfstatus = False
886 repo.lfstatus = False
885 forget = sorted(s[0] + s[1] + s[3] + s[6])
887 forget = sorted(s[0] + s[1] + s[3] + s[6])
886 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
888 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
887
889
888 for f in forget:
890 for f in forget:
889 if lfutil.standin(f) not in repo.dirstate and not \
891 if lfutil.standin(f) not in repo.dirstate and not \
890 os.path.isdir(m.rel(lfutil.standin(f))):
892 os.path.isdir(m.rel(lfutil.standin(f))):
891 ui.warn(_('not removing %s: file is already untracked\n')
893 ui.warn(_('not removing %s: file is already untracked\n')
892 % m.rel(f))
894 % m.rel(f))
893
895
894 for f in forget:
896 for f in forget:
895 if ui.verbose or not m.exact(f):
897 if ui.verbose or not m.exact(f):
896 ui.status(_('removing %s\n') % m.rel(f))
898 ui.status(_('removing %s\n') % m.rel(f))
897
899
898 # Need to lock because standin files are deleted then removed from the
900 # Need to lock because standin files are deleted then removed from the
899 # repository and we could race inbetween.
901 # repository and we could race inbetween.
900 wlock = repo.wlock()
902 wlock = repo.wlock()
901 try:
903 try:
902 lfdirstate = lfutil.openlfdirstate(ui, repo)
904 lfdirstate = lfutil.openlfdirstate(ui, repo)
903 for f in forget:
905 for f in forget:
904 if lfdirstate[f] == 'a':
906 if lfdirstate[f] == 'a':
905 lfdirstate.drop(f)
907 lfdirstate.drop(f)
906 else:
908 else:
907 lfdirstate.remove(f)
909 lfdirstate.remove(f)
908 lfdirstate.write()
910 lfdirstate.write()
909 lfutil.reporemove(repo, [lfutil.standin(f) for f in forget],
911 lfutil.reporemove(repo, [lfutil.standin(f) for f in forget],
910 unlink=True)
912 unlink=True)
911 finally:
913 finally:
912 wlock.release()
914 wlock.release()
913
915
914 def getoutgoinglfiles(ui, repo, dest=None, **opts):
916 def getoutgoinglfiles(ui, repo, dest=None, **opts):
915 dest = ui.expandpath(dest or 'default-push', dest or 'default')
917 dest = ui.expandpath(dest or 'default-push', dest or 'default')
916 dest, branches = hg.parseurl(dest, opts.get('branch'))
918 dest, branches = hg.parseurl(dest, opts.get('branch'))
917 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
919 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
918 if revs:
920 if revs:
919 revs = [repo.lookup(rev) for rev in revs]
921 revs = [repo.lookup(rev) for rev in revs]
920
922
921 remoteui = hg.remoteui
923 remoteui = hg.remoteui
922
924
923 try:
925 try:
924 remote = hg.repository(remoteui(repo, opts), dest)
926 remote = hg.repository(remoteui(repo, opts), dest)
925 except error.RepoError:
927 except error.RepoError:
926 return None
928 return None
927 o = lfutil.findoutgoing(repo, remote, False)
929 o = lfutil.findoutgoing(repo, remote, False)
928 if not o:
930 if not o:
929 return None
931 return None
930 o = repo.changelog.nodesbetween(o, revs)[0]
932 o = repo.changelog.nodesbetween(o, revs)[0]
931 if opts.get('newest_first'):
933 if opts.get('newest_first'):
932 o.reverse()
934 o.reverse()
933
935
934 toupload = set()
936 toupload = set()
935 for n in o:
937 for n in o:
936 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
938 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
937 ctx = repo[n]
939 ctx = repo[n]
938 files = set(ctx.files())
940 files = set(ctx.files())
939 if len(parents) == 2:
941 if len(parents) == 2:
940 mc = ctx.manifest()
942 mc = ctx.manifest()
941 mp1 = ctx.parents()[0].manifest()
943 mp1 = ctx.parents()[0].manifest()
942 mp2 = ctx.parents()[1].manifest()
944 mp2 = ctx.parents()[1].manifest()
943 for f in mp1:
945 for f in mp1:
944 if f not in mc:
946 if f not in mc:
945 files.add(f)
947 files.add(f)
946 for f in mp2:
948 for f in mp2:
947 if f not in mc:
949 if f not in mc:
948 files.add(f)
950 files.add(f)
949 for f in mc:
951 for f in mc:
950 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
952 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
951 files.add(f)
953 files.add(f)
952 toupload = toupload.union(
954 toupload = toupload.union(
953 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
955 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
954 return toupload
956 return toupload
955
957
956 def overrideoutgoing(orig, ui, repo, dest=None, **opts):
958 def overrideoutgoing(orig, ui, repo, dest=None, **opts):
957 orig(ui, repo, dest, **opts)
959 orig(ui, repo, dest, **opts)
958
960
959 if opts.pop('large', None):
961 if opts.pop('large', None):
960 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
962 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
961 if toupload is None:
963 if toupload is None:
962 ui.status(_('largefiles: No remote repo\n'))
964 ui.status(_('largefiles: No remote repo\n'))
963 else:
965 else:
964 ui.status(_('largefiles to upload:\n'))
966 ui.status(_('largefiles to upload:\n'))
965 for file in toupload:
967 for file in toupload:
966 ui.status(lfutil.splitstandin(file) + '\n')
968 ui.status(lfutil.splitstandin(file) + '\n')
967 ui.status('\n')
969 ui.status('\n')
968
970
969 def overridesummary(orig, ui, repo, *pats, **opts):
971 def overridesummary(orig, ui, repo, *pats, **opts):
970 try:
972 try:
971 repo.lfstatus = True
973 repo.lfstatus = True
972 orig(ui, repo, *pats, **opts)
974 orig(ui, repo, *pats, **opts)
973 finally:
975 finally:
974 repo.lfstatus = False
976 repo.lfstatus = False
975
977
976 if opts.pop('large', None):
978 if opts.pop('large', None):
977 toupload = getoutgoinglfiles(ui, repo, None, **opts)
979 toupload = getoutgoinglfiles(ui, repo, None, **opts)
978 if toupload is None:
980 if toupload is None:
979 ui.status(_('largefiles: No remote repo\n'))
981 ui.status(_('largefiles: No remote repo\n'))
980 else:
982 else:
981 ui.status(_('largefiles: %d to upload\n') % len(toupload))
983 ui.status(_('largefiles: %d to upload\n') % len(toupload))
982
984
983 def overrideaddremove(orig, ui, repo, *pats, **opts):
985 def overrideaddremove(orig, ui, repo, *pats, **opts):
984 if not lfutil.islfilesrepo(repo):
986 if not lfutil.islfilesrepo(repo):
985 return orig(ui, repo, *pats, **opts)
987 return orig(ui, repo, *pats, **opts)
986 # Get the list of missing largefiles so we can remove them
988 # Get the list of missing largefiles so we can remove them
987 lfdirstate = lfutil.openlfdirstate(ui, repo)
989 lfdirstate = lfutil.openlfdirstate(ui, repo)
988 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
990 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
989 False, False)
991 False, False)
990 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
992 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
991
993
992 # Call into the normal remove code, but the removing of the standin, we want
994 # Call into the normal remove code, but the removing of the standin, we want
993 # to have handled by original addremove. Monkey patching here makes sure
995 # to have handled by original addremove. Monkey patching here makes sure
994 # we don't remove the standin in the largefiles code, preventing a very
996 # we don't remove the standin in the largefiles code, preventing a very
995 # confused state later.
997 # confused state later.
996 if missing:
998 if missing:
997 repo._isaddremove = True
999 repo._isaddremove = True
998 removelargefiles(ui, repo, *missing, **opts)
1000 removelargefiles(ui, repo, *missing, **opts)
999 repo._isaddremove = False
1001 repo._isaddremove = False
1000 # Call into the normal add code, and any files that *should* be added as
1002 # Call into the normal add code, and any files that *should* be added as
1001 # largefiles will be
1003 # largefiles will be
1002 addlargefiles(ui, repo, *pats, **opts)
1004 addlargefiles(ui, repo, *pats, **opts)
1003 # Now that we've handled largefiles, hand off to the original addremove
1005 # Now that we've handled largefiles, hand off to the original addremove
1004 # function to take care of the rest. Make sure it doesn't do anything with
1006 # function to take care of the rest. Make sure it doesn't do anything with
1005 # largefiles by installing a matcher that will ignore them.
1007 # largefiles by installing a matcher that will ignore them.
1006 installnormalfilesmatchfn(repo[None].manifest())
1008 installnormalfilesmatchfn(repo[None].manifest())
1007 result = orig(ui, repo, *pats, **opts)
1009 result = orig(ui, repo, *pats, **opts)
1008 restorematchfn()
1010 restorematchfn()
1009 return result
1011 return result
1010
1012
1011 # Calling purge with --all will cause the largefiles to be deleted.
1013 # Calling purge with --all will cause the largefiles to be deleted.
1012 # Override repo.status to prevent this from happening.
1014 # Override repo.status to prevent this from happening.
1013 def overridepurge(orig, ui, repo, *dirs, **opts):
1015 def overridepurge(orig, ui, repo, *dirs, **opts):
1014 oldstatus = repo.status
1016 oldstatus = repo.status
1015 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1017 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1016 clean=False, unknown=False, listsubrepos=False):
1018 clean=False, unknown=False, listsubrepos=False):
1017 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1019 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1018 listsubrepos)
1020 listsubrepos)
1019 lfdirstate = lfutil.openlfdirstate(ui, repo)
1021 lfdirstate = lfutil.openlfdirstate(ui, repo)
1020 modified, added, removed, deleted, unknown, ignored, clean = r
1022 modified, added, removed, deleted, unknown, ignored, clean = r
1021 unknown = [f for f in unknown if lfdirstate[f] == '?']
1023 unknown = [f for f in unknown if lfdirstate[f] == '?']
1022 ignored = [f for f in ignored if lfdirstate[f] == '?']
1024 ignored = [f for f in ignored if lfdirstate[f] == '?']
1023 return modified, added, removed, deleted, unknown, ignored, clean
1025 return modified, added, removed, deleted, unknown, ignored, clean
1024 repo.status = overridestatus
1026 repo.status = overridestatus
1025 orig(ui, repo, *dirs, **opts)
1027 orig(ui, repo, *dirs, **opts)
1026 repo.status = oldstatus
1028 repo.status = oldstatus
1027
1029
1028 def overriderollback(orig, ui, repo, **opts):
1030 def overriderollback(orig, ui, repo, **opts):
1029 result = orig(ui, repo, **opts)
1031 result = orig(ui, repo, **opts)
1030 merge.update(repo, node=None, branchmerge=False, force=True,
1032 merge.update(repo, node=None, branchmerge=False, force=True,
1031 partial=lfutil.isstandin)
1033 partial=lfutil.isstandin)
1032 wlock = repo.wlock()
1034 wlock = repo.wlock()
1033 try:
1035 try:
1034 lfdirstate = lfutil.openlfdirstate(ui, repo)
1036 lfdirstate = lfutil.openlfdirstate(ui, repo)
1035 lfiles = lfutil.listlfiles(repo)
1037 lfiles = lfutil.listlfiles(repo)
1036 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
1038 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
1037 for file in lfiles:
1039 for file in lfiles:
1038 if file in oldlfiles:
1040 if file in oldlfiles:
1039 lfdirstate.normallookup(file)
1041 lfdirstate.normallookup(file)
1040 else:
1042 else:
1041 lfdirstate.add(file)
1043 lfdirstate.add(file)
1042 lfdirstate.write()
1044 lfdirstate.write()
1043 finally:
1045 finally:
1044 wlock.release()
1046 wlock.release()
1045 return result
1047 return result
1046
1048
1047 def overridetransplant(orig, ui, repo, *revs, **opts):
1049 def overridetransplant(orig, ui, repo, *revs, **opts):
1048 try:
1050 try:
1049 oldstandins = lfutil.getstandinsstate(repo)
1051 oldstandins = lfutil.getstandinsstate(repo)
1050 repo._istransplanting = True
1052 repo._istransplanting = True
1051 result = orig(ui, repo, *revs, **opts)
1053 result = orig(ui, repo, *revs, **opts)
1052 newstandins = lfutil.getstandinsstate(repo)
1054 newstandins = lfutil.getstandinsstate(repo)
1053 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1055 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1054 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1056 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1055 printmessage=True)
1057 printmessage=True)
1056 finally:
1058 finally:
1057 repo._istransplanting = False
1059 repo._istransplanting = False
1058 return result
1060 return result
1059
1061
1060 def overridecat(orig, ui, repo, file1, *pats, **opts):
1062 def overridecat(orig, ui, repo, file1, *pats, **opts):
1061 rev = opts.get('rev')
1063 rev = opts.get('rev')
1062 if not lfutil.standin(file1) in repo[rev]:
1064 if not lfutil.standin(file1) in repo[rev]:
1063 result = orig(ui, repo, file1, *pats, **opts)
1065 result = orig(ui, repo, file1, *pats, **opts)
1064 return result
1066 return result
1065 return lfcommands.catlfile(repo, file1, opts.get('rev'), opts.get('output'))
1067 return lfcommands.catlfile(repo, file1, opts.get('rev'), opts.get('output'))
@@ -1,471 +1,472 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''setup for largefiles repositories: reposetup'''
9 '''setup for largefiles repositories: reposetup'''
10 import copy
10 import copy
11 import types
11 import types
12 import os
12 import os
13
13
14 from mercurial import context, error, manifest, match as match_, util
14 from mercurial import context, error, manifest, match as match_, util
15 from mercurial import node as node_
15 from mercurial import node as node_
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17
17
18 import lfcommands
18 import lfcommands
19 import proto
19 import proto
20 import lfutil
20 import lfutil
21
21
22 def reposetup(ui, repo):
22 def reposetup(ui, repo):
23 # wire repositories should be given new wireproto functions but not the
23 # wire repositories should be given new wireproto functions but not the
24 # other largefiles modifications
24 # other largefiles modifications
25 if not repo.local():
25 if not repo.local():
26 return proto.wirereposetup(ui, repo)
26 return proto.wirereposetup(ui, repo)
27
27
28 for name in ('status', 'commitctx', 'commit', 'push'):
28 for name in ('status', 'commitctx', 'commit', 'push'):
29 method = getattr(repo, name)
29 method = getattr(repo, name)
30 if (isinstance(method, types.FunctionType) and
30 if (isinstance(method, types.FunctionType) and
31 method.func_name == 'wrap'):
31 method.func_name == 'wrap'):
32 ui.warn(_('largefiles: repo method %r appears to have already been'
32 ui.warn(_('largefiles: repo method %r appears to have already been'
33 ' wrapped by another extension: '
33 ' wrapped by another extension: '
34 'largefiles may behave incorrectly\n')
34 'largefiles may behave incorrectly\n')
35 % name)
35 % name)
36
36
37 class lfilesrepo(repo.__class__):
37 class lfilesrepo(repo.__class__):
38 lfstatus = False
38 lfstatus = False
39 def status_nolfiles(self, *args, **kwargs):
39 def status_nolfiles(self, *args, **kwargs):
40 return super(lfilesrepo, self).status(*args, **kwargs)
40 return super(lfilesrepo, self).status(*args, **kwargs)
41
41
42 # When lfstatus is set, return a context that gives the names
42 # When lfstatus is set, return a context that gives the names
43 # of largefiles instead of their corresponding standins and
43 # of largefiles instead of their corresponding standins and
44 # identifies the largefiles as always binary, regardless of
44 # identifies the largefiles as always binary, regardless of
45 # their actual contents.
45 # their actual contents.
46 def __getitem__(self, changeid):
46 def __getitem__(self, changeid):
47 ctx = super(lfilesrepo, self).__getitem__(changeid)
47 ctx = super(lfilesrepo, self).__getitem__(changeid)
48 if self.lfstatus:
48 if self.lfstatus:
49 class lfilesmanifestdict(manifest.manifestdict):
49 class lfilesmanifestdict(manifest.manifestdict):
50 def __contains__(self, filename):
50 def __contains__(self, filename):
51 if super(lfilesmanifestdict,
51 if super(lfilesmanifestdict,
52 self).__contains__(filename):
52 self).__contains__(filename):
53 return True
53 return True
54 return super(lfilesmanifestdict,
54 return super(lfilesmanifestdict,
55 self).__contains__(lfutil.standin(filename))
55 self).__contains__(lfutil.standin(filename))
56 class lfilesctx(ctx.__class__):
56 class lfilesctx(ctx.__class__):
57 def files(self):
57 def files(self):
58 filenames = super(lfilesctx, self).files()
58 filenames = super(lfilesctx, self).files()
59 return [lfutil.splitstandin(f) or f for f in filenames]
59 return [lfutil.splitstandin(f) or f for f in filenames]
60 def manifest(self):
60 def manifest(self):
61 man1 = super(lfilesctx, self).manifest()
61 man1 = super(lfilesctx, self).manifest()
62 man1.__class__ = lfilesmanifestdict
62 man1.__class__ = lfilesmanifestdict
63 return man1
63 return man1
64 def filectx(self, path, fileid=None, filelog=None):
64 def filectx(self, path, fileid=None, filelog=None):
65 try:
65 try:
66 if filelog is not None:
66 if filelog is not None:
67 result = super(lfilesctx, self).filectx(
67 result = super(lfilesctx, self).filectx(
68 path, fileid, filelog)
68 path, fileid, filelog)
69 else:
69 else:
70 result = super(lfilesctx, self).filectx(
70 result = super(lfilesctx, self).filectx(
71 path, fileid)
71 path, fileid)
72 except error.LookupError:
72 except error.LookupError:
73 # Adding a null character will cause Mercurial to
73 # Adding a null character will cause Mercurial to
74 # identify this as a binary file.
74 # identify this as a binary file.
75 if filelog is not None:
75 if filelog is not None:
76 result = super(lfilesctx, self).filectx(
76 result = super(lfilesctx, self).filectx(
77 lfutil.standin(path), fileid, filelog)
77 lfutil.standin(path), fileid, filelog)
78 else:
78 else:
79 result = super(lfilesctx, self).filectx(
79 result = super(lfilesctx, self).filectx(
80 lfutil.standin(path), fileid)
80 lfutil.standin(path), fileid)
81 olddata = result.data
81 olddata = result.data
82 result.data = lambda: olddata() + '\0'
82 result.data = lambda: olddata() + '\0'
83 return result
83 return result
84 ctx.__class__ = lfilesctx
84 ctx.__class__ = lfilesctx
85 return ctx
85 return ctx
86
86
87 # Figure out the status of big files and insert them into the
87 # Figure out the status of big files and insert them into the
88 # appropriate list in the result. Also removes standin files
88 # appropriate list in the result. Also removes standin files
89 # from the listing. Revert to the original status if
89 # from the listing. Revert to the original status if
90 # self.lfstatus is False.
90 # self.lfstatus is False.
91 def status(self, node1='.', node2=None, match=None, ignored=False,
91 def status(self, node1='.', node2=None, match=None, ignored=False,
92 clean=False, unknown=False, listsubrepos=False):
92 clean=False, unknown=False, listsubrepos=False):
93 listignored, listclean, listunknown = ignored, clean, unknown
93 listignored, listclean, listunknown = ignored, clean, unknown
94 if not self.lfstatus:
94 if not self.lfstatus:
95 return super(lfilesrepo, self).status(node1, node2, match,
95 return super(lfilesrepo, self).status(node1, node2, match,
96 listignored, listclean, listunknown, listsubrepos)
96 listignored, listclean, listunknown, listsubrepos)
97 else:
97 else:
98 # some calls in this function rely on the old version of status
98 # some calls in this function rely on the old version of status
99 self.lfstatus = False
99 self.lfstatus = False
100 if isinstance(node1, context.changectx):
100 if isinstance(node1, context.changectx):
101 ctx1 = node1
101 ctx1 = node1
102 else:
102 else:
103 ctx1 = repo[node1]
103 ctx1 = repo[node1]
104 if isinstance(node2, context.changectx):
104 if isinstance(node2, context.changectx):
105 ctx2 = node2
105 ctx2 = node2
106 else:
106 else:
107 ctx2 = repo[node2]
107 ctx2 = repo[node2]
108 working = ctx2.rev() is None
108 working = ctx2.rev() is None
109 parentworking = working and ctx1 == self['.']
109 parentworking = working and ctx1 == self['.']
110
110
111 def inctx(file, ctx):
111 def inctx(file, ctx):
112 try:
112 try:
113 if ctx.rev() is None:
113 if ctx.rev() is None:
114 return file in ctx.manifest()
114 return file in ctx.manifest()
115 ctx[file]
115 ctx[file]
116 return True
116 return True
117 except KeyError:
117 except KeyError:
118 return False
118 return False
119
119
120 if match is None:
120 if match is None:
121 match = match_.always(self.root, self.getcwd())
121 match = match_.always(self.root, self.getcwd())
122
122
123 # First check if there were files specified on the
123 # First check if there were files specified on the
124 # command line. If there were, and none of them were
124 # command line. If there were, and none of them were
125 # largefiles, we should just bail here and let super
125 # largefiles, we should just bail here and let super
126 # handle it -- thus gaining a big performance boost.
126 # handle it -- thus gaining a big performance boost.
127 lfdirstate = lfutil.openlfdirstate(ui, self)
127 lfdirstate = lfutil.openlfdirstate(ui, self)
128 if match.files() and not match.anypats():
128 if match.files() and not match.anypats():
129 for f in lfdirstate:
129 for f in lfdirstate:
130 if match(f):
130 if match(f):
131 break
131 break
132 else:
132 else:
133 return super(lfilesrepo, self).status(node1, node2,
133 return super(lfilesrepo, self).status(node1, node2,
134 match, listignored, listclean,
134 match, listignored, listclean,
135 listunknown, listsubrepos)
135 listunknown, listsubrepos)
136
136
137 # Create a copy of match that matches standins instead
137 # Create a copy of match that matches standins instead
138 # of largefiles.
138 # of largefiles.
139 def tostandins(files):
139 def tostandins(files):
140 if not working:
140 if not working:
141 return files
141 return files
142 newfiles = []
142 newfiles = []
143 dirstate = repo.dirstate
143 dirstate = repo.dirstate
144 for f in files:
144 for f in files:
145 sf = lfutil.standin(f)
145 sf = lfutil.standin(f)
146 if sf in dirstate:
146 if sf in dirstate:
147 newfiles.append(sf)
147 newfiles.append(sf)
148 elif sf in dirstate.dirs():
148 elif sf in dirstate.dirs():
149 # Directory entries could be regular or
149 # Directory entries could be regular or
150 # standin, check both
150 # standin, check both
151 newfiles.extend((f, sf))
151 newfiles.extend((f, sf))
152 else:
152 else:
153 newfiles.append(f)
153 newfiles.append(f)
154 return newfiles
154 return newfiles
155
155
156 # Create a function that we can use to override what is
156 # Create a function that we can use to override what is
157 # normally the ignore matcher. We've already checked
157 # normally the ignore matcher. We've already checked
158 # for ignored files on the first dirstate walk, and
158 # for ignored files on the first dirstate walk, and
159 # unecessarily re-checking here causes a huge performance
159 # unecessarily re-checking here causes a huge performance
160 # hit because lfdirstate only knows about largefiles
160 # hit because lfdirstate only knows about largefiles
161 def _ignoreoverride(self):
161 def _ignoreoverride(self):
162 return False
162 return False
163
163
164 m = copy.copy(match)
164 m = copy.copy(match)
165 m._files = tostandins(m._files)
165 m._files = tostandins(m._files)
166
166
167 # Get ignored files here even if we weren't asked for them; we
167 # Get ignored files here even if we weren't asked for them; we
168 # must use the result here for filtering later
168 # must use the result here for filtering later
169 result = super(lfilesrepo, self).status(node1, node2, m,
169 result = super(lfilesrepo, self).status(node1, node2, m,
170 True, clean, unknown, listsubrepos)
170 True, clean, unknown, listsubrepos)
171 if working:
171 if working:
172 try:
172 try:
173 # Any non-largefiles that were explicitly listed must be
173 # Any non-largefiles that were explicitly listed must be
174 # taken out or lfdirstate.status will report an error.
174 # taken out or lfdirstate.status will report an error.
175 # The status of these files was already computed using
175 # The status of these files was already computed using
176 # super's status.
176 # super's status.
177 # Override lfdirstate's ignore matcher to not do
177 # Override lfdirstate's ignore matcher to not do
178 # anything
178 # anything
179 origignore = lfdirstate._ignore
179 origignore = lfdirstate._ignore
180 lfdirstate._ignore = _ignoreoverride
180 lfdirstate._ignore = _ignoreoverride
181
181
182 def sfindirstate(f):
182 def sfindirstate(f):
183 sf = lfutil.standin(f)
183 sf = lfutil.standin(f)
184 dirstate = repo.dirstate
184 dirstate = repo.dirstate
185 return sf in dirstate or sf in dirstate.dirs()
185 return sf in dirstate or sf in dirstate.dirs()
186 match._files = [f for f in match._files
186 match._files = [f for f in match._files
187 if sfindirstate(f)]
187 if sfindirstate(f)]
188 # Don't waste time getting the ignored and unknown
188 # Don't waste time getting the ignored and unknown
189 # files again; we already have them
189 # files again; we already have them
190 s = lfdirstate.status(match, [], False,
190 s = lfdirstate.status(match, [], False,
191 listclean, False)
191 listclean, False)
192 (unsure, modified, added, removed, missing, unknown,
192 (unsure, modified, added, removed, missing, unknown,
193 ignored, clean) = s
193 ignored, clean) = s
194 # Replace the list of ignored and unknown files with
194 # Replace the list of ignored and unknown files with
195 # the previously caclulated lists, and strip out the
195 # the previously caclulated lists, and strip out the
196 # largefiles
196 # largefiles
197 lfiles = set(lfdirstate._map)
197 lfiles = set(lfdirstate._map)
198 ignored = set(result[5]).difference(lfiles)
198 ignored = set(result[5]).difference(lfiles)
199 unknown = set(result[4]).difference(lfiles)
199 unknown = set(result[4]).difference(lfiles)
200 if parentworking:
200 if parentworking:
201 for lfile in unsure:
201 for lfile in unsure:
202 standin = lfutil.standin(lfile)
202 standin = lfutil.standin(lfile)
203 if standin not in ctx1:
203 if standin not in ctx1:
204 # from second parent
204 # from second parent
205 modified.append(lfile)
205 modified.append(lfile)
206 elif ctx1[standin].data().strip() \
206 elif ctx1[standin].data().strip() \
207 != lfutil.hashfile(self.wjoin(lfile)):
207 != lfutil.hashfile(self.wjoin(lfile)):
208 modified.append(lfile)
208 modified.append(lfile)
209 else:
209 else:
210 clean.append(lfile)
210 clean.append(lfile)
211 lfdirstate.normal(lfile)
211 lfdirstate.normal(lfile)
212 else:
212 else:
213 tocheck = unsure + modified + added + clean
213 tocheck = unsure + modified + added + clean
214 modified, added, clean = [], [], []
214 modified, added, clean = [], [], []
215
215
216 for lfile in tocheck:
216 for lfile in tocheck:
217 standin = lfutil.standin(lfile)
217 standin = lfutil.standin(lfile)
218 if inctx(standin, ctx1):
218 if inctx(standin, ctx1):
219 if ctx1[standin].data().strip() != \
219 if ctx1[standin].data().strip() != \
220 lfutil.hashfile(self.wjoin(lfile)):
220 lfutil.hashfile(self.wjoin(lfile)):
221 modified.append(lfile)
221 modified.append(lfile)
222 else:
222 else:
223 clean.append(lfile)
223 clean.append(lfile)
224 else:
224 else:
225 added.append(lfile)
225 added.append(lfile)
226 finally:
226 finally:
227 # Replace the original ignore function
227 # Replace the original ignore function
228 lfdirstate._ignore = origignore
228 lfdirstate._ignore = origignore
229
229
230 for standin in ctx1.manifest():
230 for standin in ctx1.manifest():
231 if not lfutil.isstandin(standin):
231 if not lfutil.isstandin(standin):
232 continue
232 continue
233 lfile = lfutil.splitstandin(standin)
233 lfile = lfutil.splitstandin(standin)
234 if not match(lfile):
234 if not match(lfile):
235 continue
235 continue
236 if lfile not in lfdirstate:
236 if lfile not in lfdirstate:
237 removed.append(lfile)
237 removed.append(lfile)
238
238
239 # Filter result lists
239 # Filter result lists
240 result = list(result)
240 result = list(result)
241
241
242 # Largefiles are not really removed when they're
242 # Largefiles are not really removed when they're
243 # still in the normal dirstate. Likewise, normal
243 # still in the normal dirstate. Likewise, normal
244 # files are not really removed if it's still in
244 # files are not really removed if it's still in
245 # lfdirstate. This happens in merges where files
245 # lfdirstate. This happens in merges where files
246 # change type.
246 # change type.
247 removed = [f for f in removed if f not in repo.dirstate]
247 removed = [f for f in removed if f not in repo.dirstate]
248 result[2] = [f for f in result[2] if f not in lfdirstate]
248 result[2] = [f for f in result[2] if f not in lfdirstate]
249
249
250 # Unknown files
250 # Unknown files
251 unknown = set(unknown).difference(ignored)
251 unknown = set(unknown).difference(ignored)
252 result[4] = [f for f in unknown
252 result[4] = [f for f in unknown
253 if (repo.dirstate[f] == '?' and
253 if (repo.dirstate[f] == '?' and
254 not lfutil.isstandin(f))]
254 not lfutil.isstandin(f))]
255 # Ignored files were calculated earlier by the dirstate,
255 # Ignored files were calculated earlier by the dirstate,
256 # and we already stripped out the largefiles from the list
256 # and we already stripped out the largefiles from the list
257 result[5] = ignored
257 result[5] = ignored
258 # combine normal files and largefiles
258 # combine normal files and largefiles
259 normals = [[fn for fn in filelist
259 normals = [[fn for fn in filelist
260 if not lfutil.isstandin(fn)]
260 if not lfutil.isstandin(fn)]
261 for filelist in result]
261 for filelist in result]
262 lfiles = (modified, added, removed, missing, [], [], clean)
262 lfiles = (modified, added, removed, missing, [], [], clean)
263 result = [sorted(list1 + list2)
263 result = [sorted(list1 + list2)
264 for (list1, list2) in zip(normals, lfiles)]
264 for (list1, list2) in zip(normals, lfiles)]
265 else:
265 else:
266 def toname(f):
266 def toname(f):
267 if lfutil.isstandin(f):
267 if lfutil.isstandin(f):
268 return lfutil.splitstandin(f)
268 return lfutil.splitstandin(f)
269 return f
269 return f
270 result = [[toname(f) for f in items] for items in result]
270 result = [[toname(f) for f in items] for items in result]
271
271
272 if not listunknown:
272 if not listunknown:
273 result[4] = []
273 result[4] = []
274 if not listignored:
274 if not listignored:
275 result[5] = []
275 result[5] = []
276 if not listclean:
276 if not listclean:
277 result[6] = []
277 result[6] = []
278 self.lfstatus = True
278 self.lfstatus = True
279 return result
279 return result
280
280
281 # As part of committing, copy all of the largefiles into the
281 # As part of committing, copy all of the largefiles into the
282 # cache.
282 # cache.
283 def commitctx(self, *args, **kwargs):
283 def commitctx(self, *args, **kwargs):
284 node = super(lfilesrepo, self).commitctx(*args, **kwargs)
284 node = super(lfilesrepo, self).commitctx(*args, **kwargs)
285 lfutil.copyalltostore(self, node)
285 lfutil.copyalltostore(self, node)
286 return node
286 return node
287
287
288 # Before commit, largefile standins have not had their
288 # Before commit, largefile standins have not had their
289 # contents updated to reflect the hash of their largefile.
289 # contents updated to reflect the hash of their largefile.
290 # Do that here.
290 # Do that here.
291 def commit(self, text="", user=None, date=None, match=None,
291 def commit(self, text="", user=None, date=None, match=None,
292 force=False, editor=False, extra={}):
292 force=False, editor=False, extra={}):
293 orig = super(lfilesrepo, self).commit
293 orig = super(lfilesrepo, self).commit
294
294
295 wlock = repo.wlock()
295 wlock = repo.wlock()
296 try:
296 try:
297 # Case 0: Rebase or Transplant
297 # Case 0: Rebase or Transplant
298 # We have to take the time to pull down the new largefiles now.
298 # We have to take the time to pull down the new largefiles now.
299 # Otherwise, any largefiles that were modified in the
299 # Otherwise, any largefiles that were modified in the
300 # destination changesets get overwritten, either by the rebase
300 # destination changesets get overwritten, either by the rebase
301 # or in the first commit after the rebase or transplant.
301 # or in the first commit after the rebase or transplant.
302 # updatelfiles will update the dirstate to mark any pulled
302 # updatelfiles will update the dirstate to mark any pulled
303 # largefiles as modified
303 # largefiles as modified
304 if getattr(repo, "_isrebasing", False) or \
304 if getattr(repo, "_isrebasing", False) or \
305 getattr(repo, "_istransplanting", False):
305 getattr(repo, "_istransplanting", False):
306 lfcommands.updatelfiles(repo.ui, repo, filelist=None,
306 lfcommands.updatelfiles(repo.ui, repo, filelist=None,
307 printmessage=False)
307 printmessage=False)
308 result = orig(text=text, user=user, date=date, match=match,
308 result = orig(text=text, user=user, date=date, match=match,
309 force=force, editor=editor, extra=extra)
309 force=force, editor=editor, extra=extra)
310 return result
310 return result
311 # Case 1: user calls commit with no specific files or
311 # Case 1: user calls commit with no specific files or
312 # include/exclude patterns: refresh and commit all files that
312 # include/exclude patterns: refresh and commit all files that
313 # are "dirty".
313 # are "dirty".
314 if ((match is None) or
314 if ((match is None) or
315 (not match.anypats() and not match.files())):
315 (not match.anypats() and not match.files())):
316 # Spend a bit of time here to get a list of files we know
316 # Spend a bit of time here to get a list of files we know
317 # are modified so we can compare only against those.
317 # are modified so we can compare only against those.
318 # It can cost a lot of time (several seconds)
318 # It can cost a lot of time (several seconds)
319 # otherwise to update all standins if the largefiles are
319 # otherwise to update all standins if the largefiles are
320 # large.
320 # large.
321 lfdirstate = lfutil.openlfdirstate(ui, self)
321 lfdirstate = lfutil.openlfdirstate(ui, self)
322 dirtymatch = match_.always(repo.root, repo.getcwd())
322 dirtymatch = match_.always(repo.root, repo.getcwd())
323 s = lfdirstate.status(dirtymatch, [], False, False, False)
323 s = lfdirstate.status(dirtymatch, [], False, False, False)
324 modifiedfiles = []
324 modifiedfiles = []
325 for i in s:
325 for i in s:
326 modifiedfiles.extend(i)
326 modifiedfiles.extend(i)
327 lfiles = lfutil.listlfiles(self)
327 lfiles = lfutil.listlfiles(self)
328 # this only loops through largefiles that exist (not
328 # this only loops through largefiles that exist (not
329 # removed/renamed)
329 # removed/renamed)
330 for lfile in lfiles:
330 for lfile in lfiles:
331 if lfile in modifiedfiles:
331 if lfile in modifiedfiles:
332 if os.path.exists(
332 if os.path.exists(
333 self.wjoin(lfutil.standin(lfile))):
333 self.wjoin(lfutil.standin(lfile))):
334 # this handles the case where a rebase is being
334 # this handles the case where a rebase is being
335 # performed and the working copy is not updated
335 # performed and the working copy is not updated
336 # yet.
336 # yet.
337 if os.path.exists(self.wjoin(lfile)):
337 if os.path.exists(self.wjoin(lfile)):
338 lfutil.updatestandin(self,
338 lfutil.updatestandin(self,
339 lfutil.standin(lfile))
339 lfutil.standin(lfile))
340 lfdirstate.normal(lfile)
340 lfdirstate.normal(lfile)
341 for lfile in lfdirstate:
341 for lfile in lfdirstate:
342 if lfile in modifiedfiles:
342 if lfile in modifiedfiles:
343 if not os.path.exists(
343 if (not os.path.exists(repo.wjoin(
344 repo.wjoin(lfutil.standin(lfile))):
344 lfutil.standin(lfile)))) or \
345 (not os.path.exists(repo.wjoin(lfile))):
345 lfdirstate.drop(lfile)
346 lfdirstate.drop(lfile)
346
347
347 result = orig(text=text, user=user, date=date, match=match,
348 result = orig(text=text, user=user, date=date, match=match,
348 force=force, editor=editor, extra=extra)
349 force=force, editor=editor, extra=extra)
349 # This needs to be after commit; otherwise precommit hooks
350 # This needs to be after commit; otherwise precommit hooks
350 # get the wrong status
351 # get the wrong status
351 lfdirstate.write()
352 lfdirstate.write()
352 return result
353 return result
353
354
354 for f in match.files():
355 for f in match.files():
355 if lfutil.isstandin(f):
356 if lfutil.isstandin(f):
356 raise util.Abort(
357 raise util.Abort(
357 _('file "%s" is a largefile standin') % f,
358 _('file "%s" is a largefile standin') % f,
358 hint=('commit the largefile itself instead'))
359 hint=('commit the largefile itself instead'))
359
360
360 # Case 2: user calls commit with specified patterns: refresh
361 # Case 2: user calls commit with specified patterns: refresh
361 # any matching big files.
362 # any matching big files.
362 smatcher = lfutil.composestandinmatcher(self, match)
363 smatcher = lfutil.composestandinmatcher(self, match)
363 standins = lfutil.dirstatewalk(self.dirstate, smatcher)
364 standins = lfutil.dirstatewalk(self.dirstate, smatcher)
364
365
365 # No matching big files: get out of the way and pass control to
366 # No matching big files: get out of the way and pass control to
366 # the usual commit() method.
367 # the usual commit() method.
367 if not standins:
368 if not standins:
368 return orig(text=text, user=user, date=date, match=match,
369 return orig(text=text, user=user, date=date, match=match,
369 force=force, editor=editor, extra=extra)
370 force=force, editor=editor, extra=extra)
370
371
371 # Refresh all matching big files. It's possible that the
372 # Refresh all matching big files. It's possible that the
372 # commit will end up failing, in which case the big files will
373 # commit will end up failing, in which case the big files will
373 # stay refreshed. No harm done: the user modified them and
374 # stay refreshed. No harm done: the user modified them and
374 # asked to commit them, so sooner or later we're going to
375 # asked to commit them, so sooner or later we're going to
375 # refresh the standins. Might as well leave them refreshed.
376 # refresh the standins. Might as well leave them refreshed.
376 lfdirstate = lfutil.openlfdirstate(ui, self)
377 lfdirstate = lfutil.openlfdirstate(ui, self)
377 for standin in standins:
378 for standin in standins:
378 lfile = lfutil.splitstandin(standin)
379 lfile = lfutil.splitstandin(standin)
379 if lfdirstate[lfile] <> 'r':
380 if lfdirstate[lfile] <> 'r':
380 lfutil.updatestandin(self, standin)
381 lfutil.updatestandin(self, standin)
381 lfdirstate.normal(lfile)
382 lfdirstate.normal(lfile)
382 else:
383 else:
383 lfdirstate.drop(lfile)
384 lfdirstate.drop(lfile)
384
385
385 # Cook up a new matcher that only matches regular files or
386 # Cook up a new matcher that only matches regular files or
386 # standins corresponding to the big files requested by the
387 # standins corresponding to the big files requested by the
387 # user. Have to modify _files to prevent commit() from
388 # user. Have to modify _files to prevent commit() from
388 # complaining "not tracked" for big files.
389 # complaining "not tracked" for big files.
389 lfiles = lfutil.listlfiles(repo)
390 lfiles = lfutil.listlfiles(repo)
390 match = copy.copy(match)
391 match = copy.copy(match)
391 origmatchfn = match.matchfn
392 origmatchfn = match.matchfn
392
393
393 # Check both the list of largefiles and the list of
394 # Check both the list of largefiles and the list of
394 # standins because if a largefile was removed, it
395 # standins because if a largefile was removed, it
395 # won't be in the list of largefiles at this point
396 # won't be in the list of largefiles at this point
396 match._files += sorted(standins)
397 match._files += sorted(standins)
397
398
398 actualfiles = []
399 actualfiles = []
399 for f in match._files:
400 for f in match._files:
400 fstandin = lfutil.standin(f)
401 fstandin = lfutil.standin(f)
401
402
402 # ignore known largefiles and standins
403 # ignore known largefiles and standins
403 if f in lfiles or fstandin in standins:
404 if f in lfiles or fstandin in standins:
404 continue
405 continue
405
406
406 # append directory separator to avoid collisions
407 # append directory separator to avoid collisions
407 if not fstandin.endswith(os.sep):
408 if not fstandin.endswith(os.sep):
408 fstandin += os.sep
409 fstandin += os.sep
409
410
410 actualfiles.append(f)
411 actualfiles.append(f)
411 match._files = actualfiles
412 match._files = actualfiles
412
413
413 def matchfn(f):
414 def matchfn(f):
414 if origmatchfn(f):
415 if origmatchfn(f):
415 return f not in lfiles
416 return f not in lfiles
416 else:
417 else:
417 return f in standins
418 return f in standins
418
419
419 match.matchfn = matchfn
420 match.matchfn = matchfn
420 result = orig(text=text, user=user, date=date, match=match,
421 result = orig(text=text, user=user, date=date, match=match,
421 force=force, editor=editor, extra=extra)
422 force=force, editor=editor, extra=extra)
422 # This needs to be after commit; otherwise precommit hooks
423 # This needs to be after commit; otherwise precommit hooks
423 # get the wrong status
424 # get the wrong status
424 lfdirstate.write()
425 lfdirstate.write()
425 return result
426 return result
426 finally:
427 finally:
427 wlock.release()
428 wlock.release()
428
429
429 def push(self, remote, force=False, revs=None, newbranch=False):
430 def push(self, remote, force=False, revs=None, newbranch=False):
430 o = lfutil.findoutgoing(repo, remote, force)
431 o = lfutil.findoutgoing(repo, remote, force)
431 if o:
432 if o:
432 toupload = set()
433 toupload = set()
433 o = repo.changelog.nodesbetween(o, revs)[0]
434 o = repo.changelog.nodesbetween(o, revs)[0]
434 for n in o:
435 for n in o:
435 parents = [p for p in repo.changelog.parents(n)
436 parents = [p for p in repo.changelog.parents(n)
436 if p != node_.nullid]
437 if p != node_.nullid]
437 ctx = repo[n]
438 ctx = repo[n]
438 files = set(ctx.files())
439 files = set(ctx.files())
439 if len(parents) == 2:
440 if len(parents) == 2:
440 mc = ctx.manifest()
441 mc = ctx.manifest()
441 mp1 = ctx.parents()[0].manifest()
442 mp1 = ctx.parents()[0].manifest()
442 mp2 = ctx.parents()[1].manifest()
443 mp2 = ctx.parents()[1].manifest()
443 for f in mp1:
444 for f in mp1:
444 if f not in mc:
445 if f not in mc:
445 files.add(f)
446 files.add(f)
446 for f in mp2:
447 for f in mp2:
447 if f not in mc:
448 if f not in mc:
448 files.add(f)
449 files.add(f)
449 for f in mc:
450 for f in mc:
450 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f,
451 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f,
451 None):
452 None):
452 files.add(f)
453 files.add(f)
453
454
454 toupload = toupload.union(
455 toupload = toupload.union(
455 set([ctx[f].data().strip()
456 set([ctx[f].data().strip()
456 for f in files
457 for f in files
457 if lfutil.isstandin(f) and f in ctx]))
458 if lfutil.isstandin(f) and f in ctx]))
458 lfcommands.uploadlfiles(ui, self, remote, toupload)
459 lfcommands.uploadlfiles(ui, self, remote, toupload)
459 return super(lfilesrepo, self).push(remote, force, revs,
460 return super(lfilesrepo, self).push(remote, force, revs,
460 newbranch)
461 newbranch)
461
462
462 repo.__class__ = lfilesrepo
463 repo.__class__ = lfilesrepo
463
464
464 def checkrequireslfiles(ui, repo, **kwargs):
465 def checkrequireslfiles(ui, repo, **kwargs):
465 if 'largefiles' not in repo.requirements and util.any(
466 if 'largefiles' not in repo.requirements and util.any(
466 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
467 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
467 repo.requirements.add('largefiles')
468 repo.requirements.add('largefiles')
468 repo._writerequirements()
469 repo._writerequirements()
469
470
470 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles)
471 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles)
471 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles)
472 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles)
General Comments 0
You need to be logged in to leave comments. Login now