##// END OF EJS Templates
largefiles: don't prompt for normal/largefile changes when doing plain updates...
Mads Kiilerich -
r19954:427ce563 stable
parent child Browse files
Show More
@@ -1,1216 +1,1218
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, discovery
15 node, archival, error, merge, discovery
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 import basestore
22 import basestore
23
23
24 # -- Utility functions: commonly/repeatedly needed functionality ---------------
24 # -- Utility functions: commonly/repeatedly needed functionality ---------------
25
25
26 def installnormalfilesmatchfn(manifest):
26 def installnormalfilesmatchfn(manifest):
27 '''overrides scmutil.match so that the matcher it returns will ignore all
27 '''overrides scmutil.match so that the matcher it returns will ignore all
28 largefiles'''
28 largefiles'''
29 oldmatch = None # for the closure
29 oldmatch = None # for the closure
30 def overridematch(ctx, pats=[], opts={}, globbed=False,
30 def overridematch(ctx, pats=[], opts={}, globbed=False,
31 default='relpath'):
31 default='relpath'):
32 match = oldmatch(ctx, pats, opts, globbed, default)
32 match = oldmatch(ctx, pats, opts, globbed, default)
33 m = copy.copy(match)
33 m = copy.copy(match)
34 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
34 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
35 manifest)
35 manifest)
36 m._files = filter(notlfile, m._files)
36 m._files = filter(notlfile, m._files)
37 m._fmap = set(m._files)
37 m._fmap = set(m._files)
38 m._always = False
38 m._always = False
39 origmatchfn = m.matchfn
39 origmatchfn = m.matchfn
40 m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None
40 m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None
41 return m
41 return m
42 oldmatch = installmatchfn(overridematch)
42 oldmatch = installmatchfn(overridematch)
43
43
44 def installmatchfn(f):
44 def installmatchfn(f):
45 oldmatch = scmutil.match
45 oldmatch = scmutil.match
46 setattr(f, 'oldmatch', oldmatch)
46 setattr(f, 'oldmatch', oldmatch)
47 scmutil.match = f
47 scmutil.match = f
48 return oldmatch
48 return oldmatch
49
49
50 def restorematchfn():
50 def restorematchfn():
51 '''restores scmutil.match to what it was before installnormalfilesmatchfn
51 '''restores scmutil.match to what it was before installnormalfilesmatchfn
52 was called. no-op if scmutil.match is its original function.
52 was called. no-op if scmutil.match is its original function.
53
53
54 Note that n calls to installnormalfilesmatchfn will require n calls to
54 Note that n calls to installnormalfilesmatchfn will require n calls to
55 restore matchfn to reverse'''
55 restore matchfn to reverse'''
56 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
56 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
57
57
58 def addlargefiles(ui, repo, *pats, **opts):
58 def addlargefiles(ui, repo, *pats, **opts):
59 large = opts.pop('large', None)
59 large = opts.pop('large', None)
60 lfsize = lfutil.getminsize(
60 lfsize = lfutil.getminsize(
61 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
61 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
62
62
63 lfmatcher = None
63 lfmatcher = None
64 if lfutil.islfilesrepo(repo):
64 if lfutil.islfilesrepo(repo):
65 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
65 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
66 if lfpats:
66 if lfpats:
67 lfmatcher = match_.match(repo.root, '', list(lfpats))
67 lfmatcher = match_.match(repo.root, '', list(lfpats))
68
68
69 lfnames = []
69 lfnames = []
70 m = scmutil.match(repo[None], pats, opts)
70 m = scmutil.match(repo[None], pats, opts)
71 m.bad = lambda x, y: None
71 m.bad = lambda x, y: None
72 wctx = repo[None]
72 wctx = repo[None]
73 for f in repo.walk(m):
73 for f in repo.walk(m):
74 exact = m.exact(f)
74 exact = m.exact(f)
75 lfile = lfutil.standin(f) in wctx
75 lfile = lfutil.standin(f) in wctx
76 nfile = f in wctx
76 nfile = f in wctx
77 exists = lfile or nfile
77 exists = lfile or nfile
78
78
79 # Don't warn the user when they attempt to add a normal tracked file.
79 # Don't warn the user when they attempt to add a normal tracked file.
80 # The normal add code will do that for us.
80 # The normal add code will do that for us.
81 if exact and exists:
81 if exact and exists:
82 if lfile:
82 if lfile:
83 ui.warn(_('%s already a largefile\n') % f)
83 ui.warn(_('%s already a largefile\n') % f)
84 continue
84 continue
85
85
86 if (exact or not exists) and not lfutil.isstandin(f):
86 if (exact or not exists) and not lfutil.isstandin(f):
87 wfile = repo.wjoin(f)
87 wfile = repo.wjoin(f)
88
88
89 # In case the file was removed previously, but not committed
89 # In case the file was removed previously, but not committed
90 # (issue3507)
90 # (issue3507)
91 if not os.path.exists(wfile):
91 if not os.path.exists(wfile):
92 continue
92 continue
93
93
94 abovemin = (lfsize and
94 abovemin = (lfsize and
95 os.lstat(wfile).st_size >= lfsize * 1024 * 1024)
95 os.lstat(wfile).st_size >= lfsize * 1024 * 1024)
96 if large or abovemin or (lfmatcher and lfmatcher(f)):
96 if large or abovemin or (lfmatcher and lfmatcher(f)):
97 lfnames.append(f)
97 lfnames.append(f)
98 if ui.verbose or not exact:
98 if ui.verbose or not exact:
99 ui.status(_('adding %s as a largefile\n') % m.rel(f))
99 ui.status(_('adding %s as a largefile\n') % m.rel(f))
100
100
101 bad = []
101 bad = []
102 standins = []
102 standins = []
103
103
104 # Need to lock, otherwise there could be a race condition between
104 # Need to lock, otherwise there could be a race condition between
105 # when standins are created and added to the repo.
105 # when standins are created and added to the repo.
106 wlock = repo.wlock()
106 wlock = repo.wlock()
107 try:
107 try:
108 if not opts.get('dry_run'):
108 if not opts.get('dry_run'):
109 lfdirstate = lfutil.openlfdirstate(ui, repo)
109 lfdirstate = lfutil.openlfdirstate(ui, repo)
110 for f in lfnames:
110 for f in lfnames:
111 standinname = lfutil.standin(f)
111 standinname = lfutil.standin(f)
112 lfutil.writestandin(repo, standinname, hash='',
112 lfutil.writestandin(repo, standinname, hash='',
113 executable=lfutil.getexecutable(repo.wjoin(f)))
113 executable=lfutil.getexecutable(repo.wjoin(f)))
114 standins.append(standinname)
114 standins.append(standinname)
115 if lfdirstate[f] == 'r':
115 if lfdirstate[f] == 'r':
116 lfdirstate.normallookup(f)
116 lfdirstate.normallookup(f)
117 else:
117 else:
118 lfdirstate.add(f)
118 lfdirstate.add(f)
119 lfdirstate.write()
119 lfdirstate.write()
120 bad += [lfutil.splitstandin(f)
120 bad += [lfutil.splitstandin(f)
121 for f in repo[None].add(standins)
121 for f in repo[None].add(standins)
122 if f in m.files()]
122 if f in m.files()]
123 finally:
123 finally:
124 wlock.release()
124 wlock.release()
125 return bad
125 return bad
126
126
127 def removelargefiles(ui, repo, *pats, **opts):
127 def removelargefiles(ui, repo, *pats, **opts):
128 after = opts.get('after')
128 after = opts.get('after')
129 if not pats and not after:
129 if not pats and not after:
130 raise util.Abort(_('no files specified'))
130 raise util.Abort(_('no files specified'))
131 m = scmutil.match(repo[None], pats, opts)
131 m = scmutil.match(repo[None], pats, opts)
132 try:
132 try:
133 repo.lfstatus = True
133 repo.lfstatus = True
134 s = repo.status(match=m, clean=True)
134 s = repo.status(match=m, clean=True)
135 finally:
135 finally:
136 repo.lfstatus = False
136 repo.lfstatus = False
137 manifest = repo[None].manifest()
137 manifest = repo[None].manifest()
138 modified, added, deleted, clean = [[f for f in list
138 modified, added, deleted, clean = [[f for f in list
139 if lfutil.standin(f) in manifest]
139 if lfutil.standin(f) in manifest]
140 for list in [s[0], s[1], s[3], s[6]]]
140 for list in [s[0], s[1], s[3], s[6]]]
141
141
142 def warn(files, msg):
142 def warn(files, msg):
143 for f in files:
143 for f in files:
144 ui.warn(msg % m.rel(f))
144 ui.warn(msg % m.rel(f))
145 return int(len(files) > 0)
145 return int(len(files) > 0)
146
146
147 result = 0
147 result = 0
148
148
149 if after:
149 if after:
150 remove, forget = deleted, []
150 remove, forget = deleted, []
151 result = warn(modified + added + clean,
151 result = warn(modified + added + clean,
152 _('not removing %s: file still exists\n'))
152 _('not removing %s: file still exists\n'))
153 else:
153 else:
154 remove, forget = deleted + clean, []
154 remove, forget = deleted + clean, []
155 result = warn(modified, _('not removing %s: file is modified (use -f'
155 result = warn(modified, _('not removing %s: file is modified (use -f'
156 ' to force removal)\n'))
156 ' to force removal)\n'))
157 result = warn(added, _('not removing %s: file has been marked for add'
157 result = warn(added, _('not removing %s: file has been marked for add'
158 ' (use forget to undo)\n')) or result
158 ' (use forget to undo)\n')) or result
159
159
160 for f in sorted(remove + forget):
160 for f in sorted(remove + forget):
161 if ui.verbose or not m.exact(f):
161 if ui.verbose or not m.exact(f):
162 ui.status(_('removing %s\n') % m.rel(f))
162 ui.status(_('removing %s\n') % m.rel(f))
163
163
164 # Need to lock because standin files are deleted then removed from the
164 # Need to lock because standin files are deleted then removed from the
165 # repository and we could race in-between.
165 # repository and we could race in-between.
166 wlock = repo.wlock()
166 wlock = repo.wlock()
167 try:
167 try:
168 lfdirstate = lfutil.openlfdirstate(ui, repo)
168 lfdirstate = lfutil.openlfdirstate(ui, repo)
169 for f in remove:
169 for f in remove:
170 if not after:
170 if not after:
171 # If this is being called by addremove, notify the user that we
171 # If this is being called by addremove, notify the user that we
172 # are removing the file.
172 # are removing the file.
173 if getattr(repo, "_isaddremove", False):
173 if getattr(repo, "_isaddremove", False):
174 ui.status(_('removing %s\n') % f)
174 ui.status(_('removing %s\n') % f)
175 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
175 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
176 lfdirstate.remove(f)
176 lfdirstate.remove(f)
177 lfdirstate.write()
177 lfdirstate.write()
178 forget = [lfutil.standin(f) for f in forget]
178 forget = [lfutil.standin(f) for f in forget]
179 remove = [lfutil.standin(f) for f in remove]
179 remove = [lfutil.standin(f) for f in remove]
180 repo[None].forget(forget)
180 repo[None].forget(forget)
181 # If this is being called by addremove, let the original addremove
181 # If this is being called by addremove, let the original addremove
182 # function handle this.
182 # function handle this.
183 if not getattr(repo, "_isaddremove", False):
183 if not getattr(repo, "_isaddremove", False):
184 for f in remove:
184 for f in remove:
185 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
185 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
186 repo[None].forget(remove)
186 repo[None].forget(remove)
187 finally:
187 finally:
188 wlock.release()
188 wlock.release()
189
189
190 return result
190 return result
191
191
192 # For overriding mercurial.hgweb.webcommands so that largefiles will
192 # For overriding mercurial.hgweb.webcommands so that largefiles will
193 # appear at their right place in the manifests.
193 # appear at their right place in the manifests.
194 def decodepath(orig, path):
194 def decodepath(orig, path):
195 return lfutil.splitstandin(path) or path
195 return lfutil.splitstandin(path) or path
196
196
197 # -- Wrappers: modify existing commands --------------------------------
197 # -- Wrappers: modify existing commands --------------------------------
198
198
199 # Add works by going through the files that the user wanted to add and
199 # Add works by going through the files that the user wanted to add and
200 # checking if they should be added as largefiles. Then it makes a new
200 # checking if they should be added as largefiles. Then it makes a new
201 # matcher which matches only the normal files and runs the original
201 # matcher which matches only the normal files and runs the original
202 # version of add.
202 # version of add.
203 def overrideadd(orig, ui, repo, *pats, **opts):
203 def overrideadd(orig, ui, repo, *pats, **opts):
204 normal = opts.pop('normal')
204 normal = opts.pop('normal')
205 if normal:
205 if normal:
206 if opts.get('large'):
206 if opts.get('large'):
207 raise util.Abort(_('--normal cannot be used with --large'))
207 raise util.Abort(_('--normal cannot be used with --large'))
208 return orig(ui, repo, *pats, **opts)
208 return orig(ui, repo, *pats, **opts)
209 bad = addlargefiles(ui, repo, *pats, **opts)
209 bad = addlargefiles(ui, repo, *pats, **opts)
210 installnormalfilesmatchfn(repo[None].manifest())
210 installnormalfilesmatchfn(repo[None].manifest())
211 result = orig(ui, repo, *pats, **opts)
211 result = orig(ui, repo, *pats, **opts)
212 restorematchfn()
212 restorematchfn()
213
213
214 return (result == 1 or bad) and 1 or 0
214 return (result == 1 or bad) and 1 or 0
215
215
216 def overrideremove(orig, ui, repo, *pats, **opts):
216 def overrideremove(orig, ui, repo, *pats, **opts):
217 installnormalfilesmatchfn(repo[None].manifest())
217 installnormalfilesmatchfn(repo[None].manifest())
218 result = orig(ui, repo, *pats, **opts)
218 result = orig(ui, repo, *pats, **opts)
219 restorematchfn()
219 restorematchfn()
220 return removelargefiles(ui, repo, *pats, **opts) or result
220 return removelargefiles(ui, repo, *pats, **opts) or result
221
221
222 def overridestatusfn(orig, repo, rev2, **opts):
222 def overridestatusfn(orig, repo, rev2, **opts):
223 try:
223 try:
224 repo._repo.lfstatus = True
224 repo._repo.lfstatus = True
225 return orig(repo, rev2, **opts)
225 return orig(repo, rev2, **opts)
226 finally:
226 finally:
227 repo._repo.lfstatus = False
227 repo._repo.lfstatus = False
228
228
229 def overridestatus(orig, ui, repo, *pats, **opts):
229 def overridestatus(orig, ui, repo, *pats, **opts):
230 try:
230 try:
231 repo.lfstatus = True
231 repo.lfstatus = True
232 return orig(ui, repo, *pats, **opts)
232 return orig(ui, repo, *pats, **opts)
233 finally:
233 finally:
234 repo.lfstatus = False
234 repo.lfstatus = False
235
235
236 def overridedirty(orig, repo, ignoreupdate=False):
236 def overridedirty(orig, repo, ignoreupdate=False):
237 try:
237 try:
238 repo._repo.lfstatus = True
238 repo._repo.lfstatus = True
239 return orig(repo, ignoreupdate)
239 return orig(repo, ignoreupdate)
240 finally:
240 finally:
241 repo._repo.lfstatus = False
241 repo._repo.lfstatus = False
242
242
243 def overridelog(orig, ui, repo, *pats, **opts):
243 def overridelog(orig, ui, repo, *pats, **opts):
244 def overridematch(ctx, pats=[], opts={}, globbed=False,
244 def overridematch(ctx, pats=[], opts={}, globbed=False,
245 default='relpath'):
245 default='relpath'):
246 """Matcher that merges root directory with .hglf, suitable for log.
246 """Matcher that merges root directory with .hglf, suitable for log.
247 It is still possible to match .hglf directly.
247 It is still possible to match .hglf directly.
248 For any listed files run log on the standin too.
248 For any listed files run log on the standin too.
249 matchfn tries both the given filename and with .hglf stripped.
249 matchfn tries both the given filename and with .hglf stripped.
250 """
250 """
251 match = oldmatch(ctx, pats, opts, globbed, default)
251 match = oldmatch(ctx, pats, opts, globbed, default)
252 m = copy.copy(match)
252 m = copy.copy(match)
253 for i in range(0, len(m._files)):
253 for i in range(0, len(m._files)):
254 standin = lfutil.standin(m._files[i])
254 standin = lfutil.standin(m._files[i])
255 if standin in repo[ctx.node()]:
255 if standin in repo[ctx.node()]:
256 m._files[i] = standin
256 m._files[i] = standin
257 m._fmap = set(m._files)
257 m._fmap = set(m._files)
258 m._always = False
258 m._always = False
259 origmatchfn = m.matchfn
259 origmatchfn = m.matchfn
260 def lfmatchfn(f):
260 def lfmatchfn(f):
261 lf = lfutil.splitstandin(f)
261 lf = lfutil.splitstandin(f)
262 if lf is not None and origmatchfn(lf):
262 if lf is not None and origmatchfn(lf):
263 return True
263 return True
264 r = origmatchfn(f)
264 r = origmatchfn(f)
265 return r
265 return r
266 m.matchfn = lfmatchfn
266 m.matchfn = lfmatchfn
267 return m
267 return m
268 oldmatch = installmatchfn(overridematch)
268 oldmatch = installmatchfn(overridematch)
269 try:
269 try:
270 repo.lfstatus = True
270 repo.lfstatus = True
271 return orig(ui, repo, *pats, **opts)
271 return orig(ui, repo, *pats, **opts)
272 finally:
272 finally:
273 repo.lfstatus = False
273 repo.lfstatus = False
274 restorematchfn()
274 restorematchfn()
275
275
276 def overrideverify(orig, ui, repo, *pats, **opts):
276 def overrideverify(orig, ui, repo, *pats, **opts):
277 large = opts.pop('large', False)
277 large = opts.pop('large', False)
278 all = opts.pop('lfa', False)
278 all = opts.pop('lfa', False)
279 contents = opts.pop('lfc', False)
279 contents = opts.pop('lfc', False)
280
280
281 result = orig(ui, repo, *pats, **opts)
281 result = orig(ui, repo, *pats, **opts)
282 if large or all or contents:
282 if large or all or contents:
283 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
283 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
284 return result
284 return result
285
285
286 def overridedebugstate(orig, ui, repo, *pats, **opts):
286 def overridedebugstate(orig, ui, repo, *pats, **opts):
287 large = opts.pop('large', False)
287 large = opts.pop('large', False)
288 if large:
288 if large:
289 lfcommands.debugdirstate(ui, repo)
289 lfcommands.debugdirstate(ui, repo)
290 else:
290 else:
291 orig(ui, repo, *pats, **opts)
291 orig(ui, repo, *pats, **opts)
292
292
293 # Override needs to refresh standins so that update's normal merge
293 # Override needs to refresh standins so that update's normal merge
294 # will go through properly. Then the other update hook (overriding repo.update)
294 # will go through properly. Then the other update hook (overriding repo.update)
295 # will get the new files. Filemerge is also overridden so that the merge
295 # will get the new files. Filemerge is also overridden so that the merge
296 # will merge standins correctly.
296 # will merge standins correctly.
297 def overrideupdate(orig, ui, repo, *pats, **opts):
297 def overrideupdate(orig, ui, repo, *pats, **opts):
298 lfdirstate = lfutil.openlfdirstate(ui, repo)
298 lfdirstate = lfutil.openlfdirstate(ui, repo)
299 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
299 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
300 False, False)
300 False, False)
301 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
301 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
302
302
303 # Need to lock between the standins getting updated and their
303 # Need to lock between the standins getting updated and their
304 # largefiles getting updated
304 # largefiles getting updated
305 wlock = repo.wlock()
305 wlock = repo.wlock()
306 try:
306 try:
307 if opts['check']:
307 if opts['check']:
308 mod = len(modified) > 0
308 mod = len(modified) > 0
309 for lfile in unsure:
309 for lfile in unsure:
310 standin = lfutil.standin(lfile)
310 standin = lfutil.standin(lfile)
311 if repo['.'][standin].data().strip() != \
311 if repo['.'][standin].data().strip() != \
312 lfutil.hashfile(repo.wjoin(lfile)):
312 lfutil.hashfile(repo.wjoin(lfile)):
313 mod = True
313 mod = True
314 else:
314 else:
315 lfdirstate.normal(lfile)
315 lfdirstate.normal(lfile)
316 lfdirstate.write()
316 lfdirstate.write()
317 if mod:
317 if mod:
318 raise util.Abort(_('uncommitted changes'))
318 raise util.Abort(_('uncommitted changes'))
319 # XXX handle removed differently
319 # XXX handle removed differently
320 if not opts['clean']:
320 if not opts['clean']:
321 for lfile in unsure + modified + added:
321 for lfile in unsure + modified + added:
322 lfutil.updatestandin(repo, lfutil.standin(lfile))
322 lfutil.updatestandin(repo, lfutil.standin(lfile))
323 finally:
323 finally:
324 wlock.release()
324 wlock.release()
325 return orig(ui, repo, *pats, **opts)
325 return orig(ui, repo, *pats, **opts)
326
326
327 # Before starting the manifest merge, merge.updates will call
327 # Before starting the manifest merge, merge.updates will call
328 # _checkunknown to check if there are any files in the merged-in
328 # _checkunknown to check if there are any files in the merged-in
329 # changeset that collide with unknown files in the working copy.
329 # changeset that collide with unknown files in the working copy.
330 #
330 #
331 # The largefiles are seen as unknown, so this prevents us from merging
331 # The largefiles are seen as unknown, so this prevents us from merging
332 # in a file 'foo' if we already have a largefile with the same name.
332 # in a file 'foo' if we already have a largefile with the same name.
333 #
333 #
334 # The overridden function filters the unknown files by removing any
334 # The overridden function filters the unknown files by removing any
335 # largefiles. This makes the merge proceed and we can then handle this
335 # largefiles. This makes the merge proceed and we can then handle this
336 # case further in the overridden manifestmerge function below.
336 # case further in the overridden manifestmerge function below.
337 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
337 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
338 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
338 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
339 return False
339 return False
340 return origfn(repo, wctx, mctx, f)
340 return origfn(repo, wctx, mctx, f)
341
341
342 # The manifest merge handles conflicts on the manifest level. We want
342 # The manifest merge handles conflicts on the manifest level. We want
343 # to handle changes in largefile-ness of files at this level too.
343 # to handle changes in largefile-ness of files at this level too.
344 #
344 #
345 # The strategy is to run the original manifestmerge and then process
345 # The strategy is to run the original manifestmerge and then process
346 # the action list it outputs. There are two cases we need to deal with:
346 # the action list it outputs. There are two cases we need to deal with:
347 #
347 #
348 # 1. Normal file in p1, largefile in p2. Here the largefile is
348 # 1. Normal file in p1, largefile in p2. Here the largefile is
349 # detected via its standin file, which will enter the working copy
349 # detected via its standin file, which will enter the working copy
350 # with a "get" action. It is not "merge" since the standin is all
350 # with a "get" action. It is not "merge" since the standin is all
351 # Mercurial is concerned with at this level -- the link to the
351 # Mercurial is concerned with at this level -- the link to the
352 # existing normal file is not relevant here.
352 # existing normal file is not relevant here.
353 #
353 #
354 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
354 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
355 # since the largefile will be present in the working copy and
355 # since the largefile will be present in the working copy and
356 # different from the normal file in p2. Mercurial therefore
356 # different from the normal file in p2. Mercurial therefore
357 # triggers a merge action.
357 # triggers a merge action.
358 #
358 #
359 # In both cases, we prompt the user and emit new actions to either
359 # In both cases, we prompt the user and emit new actions to either
360 # remove the standin (if the normal file was kept) or to remove the
360 # remove the standin (if the normal file was kept) or to remove the
361 # normal file and get the standin (if the largefile was kept). The
361 # normal file and get the standin (if the largefile was kept). The
362 # default prompt answer is to use the largefile version since it was
362 # default prompt answer is to use the largefile version since it was
363 # presumably changed on purpose.
363 # presumably changed on purpose.
364 #
364 #
365 # Finally, the merge.applyupdates function will then take care of
365 # Finally, the merge.applyupdates function will then take care of
366 # writing the files into the working copy and lfcommands.updatelfiles
366 # writing the files into the working copy and lfcommands.updatelfiles
367 # will update the largefiles.
367 # will update the largefiles.
368 def overridemanifestmerge(origfn, repo, p1, p2, pa, branchmerge, force,
368 def overridemanifestmerge(origfn, repo, p1, p2, pa, branchmerge, force,
369 partial, acceptremote=False):
369 partial, acceptremote=False):
370 overwrite = force and not branchmerge
370 overwrite = force and not branchmerge
371 actions = origfn(repo, p1, p2, pa, branchmerge, force, partial,
371 actions = origfn(repo, p1, p2, pa, branchmerge, force, partial,
372 acceptremote)
372 acceptremote)
373
373
374 if overwrite:
374 if overwrite:
375 return actions
375 return actions
376
376
377 removes = set(a[0] for a in actions if a[1] == 'r')
377 processed = []
378 processed = []
378
379
379 for action in actions:
380 for action in actions:
380 f, m, args, msg = action
381 f, m, args, msg = action
381
382
382 splitstandin = lfutil.splitstandin(f)
383 splitstandin = lfutil.splitstandin(f)
383 if (m == "g" and splitstandin is not None and
384 if (m == "g" and splitstandin is not None and
384 splitstandin in p1):
385 splitstandin in p1 and splitstandin not in removes):
385 # Case 1: normal file in the working copy, largefile in
386 # Case 1: normal file in the working copy, largefile in
386 # the second parent
387 # the second parent
387 lfile = splitstandin
388 lfile = splitstandin
388 standin = f
389 standin = f
389 msg = _('%s has been turned into a largefile\n'
390 msg = _('%s has been turned into a largefile\n'
390 'use (l)argefile or keep as (n)ormal file?'
391 'use (l)argefile or keep as (n)ormal file?'
391 '$$ &Largefile $$ &Normal file') % lfile
392 '$$ &Largefile $$ &Normal file') % lfile
392 if repo.ui.promptchoice(msg, 0) == 0:
393 if repo.ui.promptchoice(msg, 0) == 0:
393 processed.append((lfile, "r", None, msg))
394 processed.append((lfile, "r", None, msg))
394 processed.append((standin, "g", (p2.flags(standin),), msg))
395 processed.append((standin, "g", (p2.flags(standin),), msg))
395 else:
396 else:
396 processed.append((standin, "r", None, msg))
397 processed.append((standin, "r", None, msg))
397 elif m == "g" and lfutil.standin(f) in p1:
398 elif (m == "g" and
399 lfutil.standin(f) in p1 and lfutil.standin(f) not in removes):
398 # Case 2: largefile in the working copy, normal file in
400 # Case 2: largefile in the working copy, normal file in
399 # the second parent
401 # the second parent
400 standin = lfutil.standin(f)
402 standin = lfutil.standin(f)
401 lfile = f
403 lfile = f
402 msg = _('%s has been turned into a normal file\n'
404 msg = _('%s has been turned into a normal file\n'
403 'keep as (l)argefile or use (n)ormal file?'
405 'keep as (l)argefile or use (n)ormal file?'
404 '$$ &Largefile $$ &Normal file') % lfile
406 '$$ &Largefile $$ &Normal file') % lfile
405 if repo.ui.promptchoice(msg, 0) == 0:
407 if repo.ui.promptchoice(msg, 0) == 0:
406 processed.append((lfile, "r", None, msg))
408 processed.append((lfile, "r", None, msg))
407 else:
409 else:
408 processed.append((standin, "r", None, msg))
410 processed.append((standin, "r", None, msg))
409 processed.append((lfile, "g", (p2.flags(lfile),), msg))
411 processed.append((lfile, "g", (p2.flags(lfile),), msg))
410 else:
412 else:
411 processed.append(action)
413 processed.append(action)
412
414
413 return processed
415 return processed
414
416
415 # Override filemerge to prompt the user about how they wish to merge
417 # Override filemerge to prompt the user about how they wish to merge
416 # largefiles. This will handle identical edits, and copy/rename +
418 # largefiles. This will handle identical edits, and copy/rename +
417 # edit without prompting the user.
419 # edit without prompting the user.
418 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca):
420 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca):
419 # Use better variable names here. Because this is a wrapper we cannot
421 # Use better variable names here. Because this is a wrapper we cannot
420 # change the variable names in the function declaration.
422 # change the variable names in the function declaration.
421 fcdest, fcother, fcancestor = fcd, fco, fca
423 fcdest, fcother, fcancestor = fcd, fco, fca
422 if not lfutil.isstandin(orig):
424 if not lfutil.isstandin(orig):
423 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
425 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
424 else:
426 else:
425 if not fcother.cmp(fcdest): # files identical?
427 if not fcother.cmp(fcdest): # files identical?
426 return None
428 return None
427
429
428 # backwards, use working dir parent as ancestor
430 # backwards, use working dir parent as ancestor
429 if fcancestor == fcother:
431 if fcancestor == fcother:
430 fcancestor = fcdest.parents()[0]
432 fcancestor = fcdest.parents()[0]
431
433
432 if orig != fcother.path():
434 if orig != fcother.path():
433 repo.ui.status(_('merging %s and %s to %s\n')
435 repo.ui.status(_('merging %s and %s to %s\n')
434 % (lfutil.splitstandin(orig),
436 % (lfutil.splitstandin(orig),
435 lfutil.splitstandin(fcother.path()),
437 lfutil.splitstandin(fcother.path()),
436 lfutil.splitstandin(fcdest.path())))
438 lfutil.splitstandin(fcdest.path())))
437 else:
439 else:
438 repo.ui.status(_('merging %s\n')
440 repo.ui.status(_('merging %s\n')
439 % lfutil.splitstandin(fcdest.path()))
441 % lfutil.splitstandin(fcdest.path()))
440
442
441 if fcancestor.path() != fcother.path() and fcother.data() == \
443 if fcancestor.path() != fcother.path() and fcother.data() == \
442 fcancestor.data():
444 fcancestor.data():
443 return 0
445 return 0
444 if fcancestor.path() != fcdest.path() and fcdest.data() == \
446 if fcancestor.path() != fcdest.path() and fcdest.data() == \
445 fcancestor.data():
447 fcancestor.data():
446 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
448 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
447 return 0
449 return 0
448
450
449 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
451 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
450 'keep (l)ocal or take (o)ther?'
452 'keep (l)ocal or take (o)ther?'
451 '$$ &Local $$ &Other') %
453 '$$ &Local $$ &Other') %
452 lfutil.splitstandin(orig), 0) == 0:
454 lfutil.splitstandin(orig), 0) == 0:
453 return 0
455 return 0
454 else:
456 else:
455 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
457 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
456 return 0
458 return 0
457
459
458 # Copy first changes the matchers to match standins instead of
460 # Copy first changes the matchers to match standins instead of
459 # largefiles. Then it overrides util.copyfile in that function it
461 # largefiles. Then it overrides util.copyfile in that function it
460 # checks if the destination largefile already exists. It also keeps a
462 # checks if the destination largefile already exists. It also keeps a
461 # list of copied files so that the largefiles can be copied and the
463 # list of copied files so that the largefiles can be copied and the
462 # dirstate updated.
464 # dirstate updated.
463 def overridecopy(orig, ui, repo, pats, opts, rename=False):
465 def overridecopy(orig, ui, repo, pats, opts, rename=False):
464 # doesn't remove largefile on rename
466 # doesn't remove largefile on rename
465 if len(pats) < 2:
467 if len(pats) < 2:
466 # this isn't legal, let the original function deal with it
468 # this isn't legal, let the original function deal with it
467 return orig(ui, repo, pats, opts, rename)
469 return orig(ui, repo, pats, opts, rename)
468
470
469 def makestandin(relpath):
471 def makestandin(relpath):
470 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
472 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
471 return os.path.join(repo.wjoin(lfutil.standin(path)))
473 return os.path.join(repo.wjoin(lfutil.standin(path)))
472
474
473 fullpats = scmutil.expandpats(pats)
475 fullpats = scmutil.expandpats(pats)
474 dest = fullpats[-1]
476 dest = fullpats[-1]
475
477
476 if os.path.isdir(dest):
478 if os.path.isdir(dest):
477 if not os.path.isdir(makestandin(dest)):
479 if not os.path.isdir(makestandin(dest)):
478 os.makedirs(makestandin(dest))
480 os.makedirs(makestandin(dest))
479 # This could copy both lfiles and normal files in one command,
481 # This could copy both lfiles and normal files in one command,
480 # but we don't want to do that. First replace their matcher to
482 # but we don't want to do that. First replace their matcher to
481 # only match normal files and run it, then replace it to just
483 # only match normal files and run it, then replace it to just
482 # match largefiles and run it again.
484 # match largefiles and run it again.
483 nonormalfiles = False
485 nonormalfiles = False
484 nolfiles = False
486 nolfiles = False
485 try:
487 try:
486 try:
488 try:
487 installnormalfilesmatchfn(repo[None].manifest())
489 installnormalfilesmatchfn(repo[None].manifest())
488 result = orig(ui, repo, pats, opts, rename)
490 result = orig(ui, repo, pats, opts, rename)
489 except util.Abort, e:
491 except util.Abort, e:
490 if str(e) != _('no files to copy'):
492 if str(e) != _('no files to copy'):
491 raise e
493 raise e
492 else:
494 else:
493 nonormalfiles = True
495 nonormalfiles = True
494 result = 0
496 result = 0
495 finally:
497 finally:
496 restorematchfn()
498 restorematchfn()
497
499
498 # The first rename can cause our current working directory to be removed.
500 # The first rename can cause our current working directory to be removed.
499 # In that case there is nothing left to copy/rename so just quit.
501 # In that case there is nothing left to copy/rename so just quit.
500 try:
502 try:
501 repo.getcwd()
503 repo.getcwd()
502 except OSError:
504 except OSError:
503 return result
505 return result
504
506
505 try:
507 try:
506 try:
508 try:
507 # When we call orig below it creates the standins but we don't add
509 # When we call orig below it creates the standins but we don't add
508 # them to the dir state until later so lock during that time.
510 # them to the dir state until later so lock during that time.
509 wlock = repo.wlock()
511 wlock = repo.wlock()
510
512
511 manifest = repo[None].manifest()
513 manifest = repo[None].manifest()
512 oldmatch = None # for the closure
514 oldmatch = None # for the closure
513 def overridematch(ctx, pats=[], opts={}, globbed=False,
515 def overridematch(ctx, pats=[], opts={}, globbed=False,
514 default='relpath'):
516 default='relpath'):
515 newpats = []
517 newpats = []
516 # The patterns were previously mangled to add the standin
518 # The patterns were previously mangled to add the standin
517 # directory; we need to remove that now
519 # directory; we need to remove that now
518 for pat in pats:
520 for pat in pats:
519 if match_.patkind(pat) is None and lfutil.shortname in pat:
521 if match_.patkind(pat) is None and lfutil.shortname in pat:
520 newpats.append(pat.replace(lfutil.shortname, ''))
522 newpats.append(pat.replace(lfutil.shortname, ''))
521 else:
523 else:
522 newpats.append(pat)
524 newpats.append(pat)
523 match = oldmatch(ctx, newpats, opts, globbed, default)
525 match = oldmatch(ctx, newpats, opts, globbed, default)
524 m = copy.copy(match)
526 m = copy.copy(match)
525 lfile = lambda f: lfutil.standin(f) in manifest
527 lfile = lambda f: lfutil.standin(f) in manifest
526 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
528 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
527 m._fmap = set(m._files)
529 m._fmap = set(m._files)
528 m._always = False
530 m._always = False
529 origmatchfn = m.matchfn
531 origmatchfn = m.matchfn
530 m.matchfn = lambda f: (lfutil.isstandin(f) and
532 m.matchfn = lambda f: (lfutil.isstandin(f) and
531 (f in manifest) and
533 (f in manifest) and
532 origmatchfn(lfutil.splitstandin(f)) or
534 origmatchfn(lfutil.splitstandin(f)) or
533 None)
535 None)
534 return m
536 return m
535 oldmatch = installmatchfn(overridematch)
537 oldmatch = installmatchfn(overridematch)
536 listpats = []
538 listpats = []
537 for pat in pats:
539 for pat in pats:
538 if match_.patkind(pat) is not None:
540 if match_.patkind(pat) is not None:
539 listpats.append(pat)
541 listpats.append(pat)
540 else:
542 else:
541 listpats.append(makestandin(pat))
543 listpats.append(makestandin(pat))
542
544
543 try:
545 try:
544 origcopyfile = util.copyfile
546 origcopyfile = util.copyfile
545 copiedfiles = []
547 copiedfiles = []
546 def overridecopyfile(src, dest):
548 def overridecopyfile(src, dest):
547 if (lfutil.shortname in src and
549 if (lfutil.shortname in src and
548 dest.startswith(repo.wjoin(lfutil.shortname))):
550 dest.startswith(repo.wjoin(lfutil.shortname))):
549 destlfile = dest.replace(lfutil.shortname, '')
551 destlfile = dest.replace(lfutil.shortname, '')
550 if not opts['force'] and os.path.exists(destlfile):
552 if not opts['force'] and os.path.exists(destlfile):
551 raise IOError('',
553 raise IOError('',
552 _('destination largefile already exists'))
554 _('destination largefile already exists'))
553 copiedfiles.append((src, dest))
555 copiedfiles.append((src, dest))
554 origcopyfile(src, dest)
556 origcopyfile(src, dest)
555
557
556 util.copyfile = overridecopyfile
558 util.copyfile = overridecopyfile
557 result += orig(ui, repo, listpats, opts, rename)
559 result += orig(ui, repo, listpats, opts, rename)
558 finally:
560 finally:
559 util.copyfile = origcopyfile
561 util.copyfile = origcopyfile
560
562
561 lfdirstate = lfutil.openlfdirstate(ui, repo)
563 lfdirstate = lfutil.openlfdirstate(ui, repo)
562 for (src, dest) in copiedfiles:
564 for (src, dest) in copiedfiles:
563 if (lfutil.shortname in src and
565 if (lfutil.shortname in src and
564 dest.startswith(repo.wjoin(lfutil.shortname))):
566 dest.startswith(repo.wjoin(lfutil.shortname))):
565 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
567 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
566 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
568 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
567 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
569 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
568 if not os.path.isdir(destlfiledir):
570 if not os.path.isdir(destlfiledir):
569 os.makedirs(destlfiledir)
571 os.makedirs(destlfiledir)
570 if rename:
572 if rename:
571 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
573 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
572 lfdirstate.remove(srclfile)
574 lfdirstate.remove(srclfile)
573 else:
575 else:
574 util.copyfile(repo.wjoin(srclfile),
576 util.copyfile(repo.wjoin(srclfile),
575 repo.wjoin(destlfile))
577 repo.wjoin(destlfile))
576
578
577 lfdirstate.add(destlfile)
579 lfdirstate.add(destlfile)
578 lfdirstate.write()
580 lfdirstate.write()
579 except util.Abort, e:
581 except util.Abort, e:
580 if str(e) != _('no files to copy'):
582 if str(e) != _('no files to copy'):
581 raise e
583 raise e
582 else:
584 else:
583 nolfiles = True
585 nolfiles = True
584 finally:
586 finally:
585 restorematchfn()
587 restorematchfn()
586 wlock.release()
588 wlock.release()
587
589
588 if nolfiles and nonormalfiles:
590 if nolfiles and nonormalfiles:
589 raise util.Abort(_('no files to copy'))
591 raise util.Abort(_('no files to copy'))
590
592
591 return result
593 return result
592
594
593 # When the user calls revert, we have to be careful to not revert any
595 # When the user calls revert, we have to be careful to not revert any
594 # changes to other largefiles accidentally. This means we have to keep
596 # changes to other largefiles accidentally. This means we have to keep
595 # track of the largefiles that are being reverted so we only pull down
597 # track of the largefiles that are being reverted so we only pull down
596 # the necessary largefiles.
598 # the necessary largefiles.
597 #
599 #
598 # Standins are only updated (to match the hash of largefiles) before
600 # Standins are only updated (to match the hash of largefiles) before
599 # commits. Update the standins then run the original revert, changing
601 # commits. Update the standins then run the original revert, changing
600 # the matcher to hit standins instead of largefiles. Based on the
602 # the matcher to hit standins instead of largefiles. Based on the
601 # resulting standins update the largefiles. Then return the standins
603 # resulting standins update the largefiles. Then return the standins
602 # to their proper state
604 # to their proper state
603 def overriderevert(orig, ui, repo, *pats, **opts):
605 def overriderevert(orig, ui, repo, *pats, **opts):
604 # Because we put the standins in a bad state (by updating them)
606 # Because we put the standins in a bad state (by updating them)
605 # and then return them to a correct state we need to lock to
607 # and then return them to a correct state we need to lock to
606 # prevent others from changing them in their incorrect state.
608 # prevent others from changing them in their incorrect state.
607 wlock = repo.wlock()
609 wlock = repo.wlock()
608 try:
610 try:
609 lfdirstate = lfutil.openlfdirstate(ui, repo)
611 lfdirstate = lfutil.openlfdirstate(ui, repo)
610 (modified, added, removed, missing, unknown, ignored, clean) = \
612 (modified, added, removed, missing, unknown, ignored, clean) = \
611 lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
613 lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
612 lfdirstate.write()
614 lfdirstate.write()
613 for lfile in modified:
615 for lfile in modified:
614 lfutil.updatestandin(repo, lfutil.standin(lfile))
616 lfutil.updatestandin(repo, lfutil.standin(lfile))
615 for lfile in missing:
617 for lfile in missing:
616 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
618 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
617 os.unlink(repo.wjoin(lfutil.standin(lfile)))
619 os.unlink(repo.wjoin(lfutil.standin(lfile)))
618
620
619 try:
621 try:
620 ctx = scmutil.revsingle(repo, opts.get('rev'))
622 ctx = scmutil.revsingle(repo, opts.get('rev'))
621 oldmatch = None # for the closure
623 oldmatch = None # for the closure
622 def overridematch(ctx, pats=[], opts={}, globbed=False,
624 def overridematch(ctx, pats=[], opts={}, globbed=False,
623 default='relpath'):
625 default='relpath'):
624 match = oldmatch(ctx, pats, opts, globbed, default)
626 match = oldmatch(ctx, pats, opts, globbed, default)
625 m = copy.copy(match)
627 m = copy.copy(match)
626 def tostandin(f):
628 def tostandin(f):
627 if lfutil.standin(f) in ctx:
629 if lfutil.standin(f) in ctx:
628 return lfutil.standin(f)
630 return lfutil.standin(f)
629 elif lfutil.standin(f) in repo[None]:
631 elif lfutil.standin(f) in repo[None]:
630 return None
632 return None
631 return f
633 return f
632 m._files = [tostandin(f) for f in m._files]
634 m._files = [tostandin(f) for f in m._files]
633 m._files = [f for f in m._files if f is not None]
635 m._files = [f for f in m._files if f is not None]
634 m._fmap = set(m._files)
636 m._fmap = set(m._files)
635 m._always = False
637 m._always = False
636 origmatchfn = m.matchfn
638 origmatchfn = m.matchfn
637 def matchfn(f):
639 def matchfn(f):
638 if lfutil.isstandin(f):
640 if lfutil.isstandin(f):
639 # We need to keep track of what largefiles are being
641 # We need to keep track of what largefiles are being
640 # matched so we know which ones to update later --
642 # matched so we know which ones to update later --
641 # otherwise we accidentally revert changes to other
643 # otherwise we accidentally revert changes to other
642 # largefiles. This is repo-specific, so duckpunch the
644 # largefiles. This is repo-specific, so duckpunch the
643 # repo object to keep the list of largefiles for us
645 # repo object to keep the list of largefiles for us
644 # later.
646 # later.
645 if origmatchfn(lfutil.splitstandin(f)) and \
647 if origmatchfn(lfutil.splitstandin(f)) and \
646 (f in repo[None] or f in ctx):
648 (f in repo[None] or f in ctx):
647 lfileslist = getattr(repo, '_lfilestoupdate', [])
649 lfileslist = getattr(repo, '_lfilestoupdate', [])
648 lfileslist.append(lfutil.splitstandin(f))
650 lfileslist.append(lfutil.splitstandin(f))
649 repo._lfilestoupdate = lfileslist
651 repo._lfilestoupdate = lfileslist
650 return True
652 return True
651 else:
653 else:
652 return False
654 return False
653 return origmatchfn(f)
655 return origmatchfn(f)
654 m.matchfn = matchfn
656 m.matchfn = matchfn
655 return m
657 return m
656 oldmatch = installmatchfn(overridematch)
658 oldmatch = installmatchfn(overridematch)
657 scmutil.match
659 scmutil.match
658 matches = overridematch(repo[None], pats, opts)
660 matches = overridematch(repo[None], pats, opts)
659 orig(ui, repo, *pats, **opts)
661 orig(ui, repo, *pats, **opts)
660 finally:
662 finally:
661 restorematchfn()
663 restorematchfn()
662 lfileslist = getattr(repo, '_lfilestoupdate', [])
664 lfileslist = getattr(repo, '_lfilestoupdate', [])
663 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
665 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
664 printmessage=False)
666 printmessage=False)
665
667
666 # empty out the largefiles list so we start fresh next time
668 # empty out the largefiles list so we start fresh next time
667 repo._lfilestoupdate = []
669 repo._lfilestoupdate = []
668 for lfile in modified:
670 for lfile in modified:
669 if lfile in lfileslist:
671 if lfile in lfileslist:
670 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
672 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
671 in repo['.']:
673 in repo['.']:
672 lfutil.writestandin(repo, lfutil.standin(lfile),
674 lfutil.writestandin(repo, lfutil.standin(lfile),
673 repo['.'][lfile].data().strip(),
675 repo['.'][lfile].data().strip(),
674 'x' in repo['.'][lfile].flags())
676 'x' in repo['.'][lfile].flags())
675 lfdirstate = lfutil.openlfdirstate(ui, repo)
677 lfdirstate = lfutil.openlfdirstate(ui, repo)
676 for lfile in added:
678 for lfile in added:
677 standin = lfutil.standin(lfile)
679 standin = lfutil.standin(lfile)
678 if standin not in ctx and (standin in matches or opts.get('all')):
680 if standin not in ctx and (standin in matches or opts.get('all')):
679 if lfile in lfdirstate:
681 if lfile in lfdirstate:
680 lfdirstate.drop(lfile)
682 lfdirstate.drop(lfile)
681 util.unlinkpath(repo.wjoin(standin))
683 util.unlinkpath(repo.wjoin(standin))
682 lfdirstate.write()
684 lfdirstate.write()
683 finally:
685 finally:
684 wlock.release()
686 wlock.release()
685
687
686 def hgupdaterepo(orig, repo, node, overwrite):
688 def hgupdaterepo(orig, repo, node, overwrite):
687 if not overwrite:
689 if not overwrite:
688 # Only call updatelfiles on the standins that have changed to save time
690 # Only call updatelfiles on the standins that have changed to save time
689 oldstandins = lfutil.getstandinsstate(repo)
691 oldstandins = lfutil.getstandinsstate(repo)
690
692
691 result = orig(repo, node, overwrite)
693 result = orig(repo, node, overwrite)
692
694
693 filelist = None
695 filelist = None
694 if not overwrite:
696 if not overwrite:
695 newstandins = lfutil.getstandinsstate(repo)
697 newstandins = lfutil.getstandinsstate(repo)
696 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
698 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
697 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist)
699 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist)
698 return result
700 return result
699
701
700 def hgmerge(orig, repo, node, force=None, remind=True):
702 def hgmerge(orig, repo, node, force=None, remind=True):
701 result = orig(repo, node, force, remind)
703 result = orig(repo, node, force, remind)
702 lfcommands.updatelfiles(repo.ui, repo)
704 lfcommands.updatelfiles(repo.ui, repo)
703 return result
705 return result
704
706
705 # When we rebase a repository with remotely changed largefiles, we need to
707 # When we rebase a repository with remotely changed largefiles, we need to
706 # take some extra care so that the largefiles are correctly updated in the
708 # take some extra care so that the largefiles are correctly updated in the
707 # working copy
709 # working copy
708 def overridepull(orig, ui, repo, source=None, **opts):
710 def overridepull(orig, ui, repo, source=None, **opts):
709 revsprepull = len(repo)
711 revsprepull = len(repo)
710 if not source:
712 if not source:
711 source = 'default'
713 source = 'default'
712 repo.lfpullsource = source
714 repo.lfpullsource = source
713 if opts.get('rebase', False):
715 if opts.get('rebase', False):
714 repo._isrebasing = True
716 repo._isrebasing = True
715 try:
717 try:
716 if opts.get('update'):
718 if opts.get('update'):
717 del opts['update']
719 del opts['update']
718 ui.debug('--update and --rebase are not compatible, ignoring '
720 ui.debug('--update and --rebase are not compatible, ignoring '
719 'the update flag\n')
721 'the update flag\n')
720 del opts['rebase']
722 del opts['rebase']
721 origpostincoming = commands.postincoming
723 origpostincoming = commands.postincoming
722 def _dummy(*args, **kwargs):
724 def _dummy(*args, **kwargs):
723 pass
725 pass
724 commands.postincoming = _dummy
726 commands.postincoming = _dummy
725 try:
727 try:
726 result = commands.pull(ui, repo, source, **opts)
728 result = commands.pull(ui, repo, source, **opts)
727 finally:
729 finally:
728 commands.postincoming = origpostincoming
730 commands.postincoming = origpostincoming
729 revspostpull = len(repo)
731 revspostpull = len(repo)
730 if revspostpull > revsprepull:
732 if revspostpull > revsprepull:
731 result = result or rebase.rebase(ui, repo)
733 result = result or rebase.rebase(ui, repo)
732 finally:
734 finally:
733 repo._isrebasing = False
735 repo._isrebasing = False
734 else:
736 else:
735 result = orig(ui, repo, source, **opts)
737 result = orig(ui, repo, source, **opts)
736 revspostpull = len(repo)
738 revspostpull = len(repo)
737 lfrevs = opts.get('lfrev', [])
739 lfrevs = opts.get('lfrev', [])
738 if opts.get('all_largefiles'):
740 if opts.get('all_largefiles'):
739 lfrevs.append('pulled()')
741 lfrevs.append('pulled()')
740 if lfrevs and revspostpull > revsprepull:
742 if lfrevs and revspostpull > revsprepull:
741 numcached = 0
743 numcached = 0
742 repo.firstpulled = revsprepull # for pulled() revset expression
744 repo.firstpulled = revsprepull # for pulled() revset expression
743 try:
745 try:
744 for rev in scmutil.revrange(repo, lfrevs):
746 for rev in scmutil.revrange(repo, lfrevs):
745 ui.note(_('pulling largefiles for revision %s\n') % rev)
747 ui.note(_('pulling largefiles for revision %s\n') % rev)
746 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
748 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
747 numcached += len(cached)
749 numcached += len(cached)
748 finally:
750 finally:
749 del repo.firstpulled
751 del repo.firstpulled
750 ui.status(_("%d largefiles cached\n") % numcached)
752 ui.status(_("%d largefiles cached\n") % numcached)
751 return result
753 return result
752
754
753 def pulledrevsetsymbol(repo, subset, x):
755 def pulledrevsetsymbol(repo, subset, x):
754 """``pulled()``
756 """``pulled()``
755 Changesets that just has been pulled.
757 Changesets that just has been pulled.
756
758
757 Only available with largefiles from pull --lfrev expressions.
759 Only available with largefiles from pull --lfrev expressions.
758
760
759 .. container:: verbose
761 .. container:: verbose
760
762
761 Some examples:
763 Some examples:
762
764
763 - pull largefiles for all new changesets::
765 - pull largefiles for all new changesets::
764
766
765 hg pull -lfrev "pulled()"
767 hg pull -lfrev "pulled()"
766
768
767 - pull largefiles for all new branch heads::
769 - pull largefiles for all new branch heads::
768
770
769 hg pull -lfrev "head(pulled()) and not closed()"
771 hg pull -lfrev "head(pulled()) and not closed()"
770
772
771 """
773 """
772
774
773 try:
775 try:
774 firstpulled = repo.firstpulled
776 firstpulled = repo.firstpulled
775 except AttributeError:
777 except AttributeError:
776 raise util.Abort(_("pulled() only available in --lfrev"))
778 raise util.Abort(_("pulled() only available in --lfrev"))
777 return [r for r in subset if r >= firstpulled]
779 return [r for r in subset if r >= firstpulled]
778
780
779 def overrideclone(orig, ui, source, dest=None, **opts):
781 def overrideclone(orig, ui, source, dest=None, **opts):
780 d = dest
782 d = dest
781 if d is None:
783 if d is None:
782 d = hg.defaultdest(source)
784 d = hg.defaultdest(source)
783 if opts.get('all_largefiles') and not hg.islocal(d):
785 if opts.get('all_largefiles') and not hg.islocal(d):
784 raise util.Abort(_(
786 raise util.Abort(_(
785 '--all-largefiles is incompatible with non-local destination %s' %
787 '--all-largefiles is incompatible with non-local destination %s' %
786 d))
788 d))
787
789
788 return orig(ui, source, dest, **opts)
790 return orig(ui, source, dest, **opts)
789
791
790 def hgclone(orig, ui, opts, *args, **kwargs):
792 def hgclone(orig, ui, opts, *args, **kwargs):
791 result = orig(ui, opts, *args, **kwargs)
793 result = orig(ui, opts, *args, **kwargs)
792
794
793 if result is not None:
795 if result is not None:
794 sourcerepo, destrepo = result
796 sourcerepo, destrepo = result
795 repo = destrepo.local()
797 repo = destrepo.local()
796
798
797 # Caching is implicitly limited to 'rev' option, since the dest repo was
799 # Caching is implicitly limited to 'rev' option, since the dest repo was
798 # truncated at that point. The user may expect a download count with
800 # truncated at that point. The user may expect a download count with
799 # this option, so attempt whether or not this is a largefile repo.
801 # this option, so attempt whether or not this is a largefile repo.
800 if opts.get('all_largefiles'):
802 if opts.get('all_largefiles'):
801 success, missing = lfcommands.downloadlfiles(ui, repo, None)
803 success, missing = lfcommands.downloadlfiles(ui, repo, None)
802
804
803 if missing != 0:
805 if missing != 0:
804 return None
806 return None
805
807
806 return result
808 return result
807
809
808 def overriderebase(orig, ui, repo, **opts):
810 def overriderebase(orig, ui, repo, **opts):
809 repo._isrebasing = True
811 repo._isrebasing = True
810 try:
812 try:
811 return orig(ui, repo, **opts)
813 return orig(ui, repo, **opts)
812 finally:
814 finally:
813 repo._isrebasing = False
815 repo._isrebasing = False
814
816
815 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
817 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
816 prefix=None, mtime=None, subrepos=None):
818 prefix=None, mtime=None, subrepos=None):
817 # No need to lock because we are only reading history and
819 # No need to lock because we are only reading history and
818 # largefile caches, neither of which are modified.
820 # largefile caches, neither of which are modified.
819 lfcommands.cachelfiles(repo.ui, repo, node)
821 lfcommands.cachelfiles(repo.ui, repo, node)
820
822
821 if kind not in archival.archivers:
823 if kind not in archival.archivers:
822 raise util.Abort(_("unknown archive type '%s'") % kind)
824 raise util.Abort(_("unknown archive type '%s'") % kind)
823
825
824 ctx = repo[node]
826 ctx = repo[node]
825
827
826 if kind == 'files':
828 if kind == 'files':
827 if prefix:
829 if prefix:
828 raise util.Abort(
830 raise util.Abort(
829 _('cannot give prefix when archiving to files'))
831 _('cannot give prefix when archiving to files'))
830 else:
832 else:
831 prefix = archival.tidyprefix(dest, kind, prefix)
833 prefix = archival.tidyprefix(dest, kind, prefix)
832
834
833 def write(name, mode, islink, getdata):
835 def write(name, mode, islink, getdata):
834 if matchfn and not matchfn(name):
836 if matchfn and not matchfn(name):
835 return
837 return
836 data = getdata()
838 data = getdata()
837 if decode:
839 if decode:
838 data = repo.wwritedata(name, data)
840 data = repo.wwritedata(name, data)
839 archiver.addfile(prefix + name, mode, islink, data)
841 archiver.addfile(prefix + name, mode, islink, data)
840
842
841 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
843 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
842
844
843 if repo.ui.configbool("ui", "archivemeta", True):
845 if repo.ui.configbool("ui", "archivemeta", True):
844 def metadata():
846 def metadata():
845 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
847 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
846 hex(repo.changelog.node(0)), hex(node), ctx.branch())
848 hex(repo.changelog.node(0)), hex(node), ctx.branch())
847
849
848 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
850 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
849 if repo.tagtype(t) == 'global')
851 if repo.tagtype(t) == 'global')
850 if not tags:
852 if not tags:
851 repo.ui.pushbuffer()
853 repo.ui.pushbuffer()
852 opts = {'template': '{latesttag}\n{latesttagdistance}',
854 opts = {'template': '{latesttag}\n{latesttagdistance}',
853 'style': '', 'patch': None, 'git': None}
855 'style': '', 'patch': None, 'git': None}
854 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
856 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
855 ltags, dist = repo.ui.popbuffer().split('\n')
857 ltags, dist = repo.ui.popbuffer().split('\n')
856 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
858 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
857 tags += 'latesttagdistance: %s\n' % dist
859 tags += 'latesttagdistance: %s\n' % dist
858
860
859 return base + tags
861 return base + tags
860
862
861 write('.hg_archival.txt', 0644, False, metadata)
863 write('.hg_archival.txt', 0644, False, metadata)
862
864
863 for f in ctx:
865 for f in ctx:
864 ff = ctx.flags(f)
866 ff = ctx.flags(f)
865 getdata = ctx[f].data
867 getdata = ctx[f].data
866 if lfutil.isstandin(f):
868 if lfutil.isstandin(f):
867 path = lfutil.findfile(repo, getdata().strip())
869 path = lfutil.findfile(repo, getdata().strip())
868 if path is None:
870 if path is None:
869 raise util.Abort(
871 raise util.Abort(
870 _('largefile %s not found in repo store or system cache')
872 _('largefile %s not found in repo store or system cache')
871 % lfutil.splitstandin(f))
873 % lfutil.splitstandin(f))
872 f = lfutil.splitstandin(f)
874 f = lfutil.splitstandin(f)
873
875
874 def getdatafn():
876 def getdatafn():
875 fd = None
877 fd = None
876 try:
878 try:
877 fd = open(path, 'rb')
879 fd = open(path, 'rb')
878 return fd.read()
880 return fd.read()
879 finally:
881 finally:
880 if fd:
882 if fd:
881 fd.close()
883 fd.close()
882
884
883 getdata = getdatafn
885 getdata = getdatafn
884 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
886 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
885
887
886 if subrepos:
888 if subrepos:
887 for subpath in sorted(ctx.substate):
889 for subpath in sorted(ctx.substate):
888 sub = ctx.sub(subpath)
890 sub = ctx.sub(subpath)
889 submatch = match_.narrowmatcher(subpath, matchfn)
891 submatch = match_.narrowmatcher(subpath, matchfn)
890 sub.archive(repo.ui, archiver, prefix, submatch)
892 sub.archive(repo.ui, archiver, prefix, submatch)
891
893
892 archiver.done()
894 archiver.done()
893
895
894 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
896 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
895 repo._get(repo._state + ('hg',))
897 repo._get(repo._state + ('hg',))
896 rev = repo._state[1]
898 rev = repo._state[1]
897 ctx = repo._repo[rev]
899 ctx = repo._repo[rev]
898
900
899 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
901 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
900
902
901 def write(name, mode, islink, getdata):
903 def write(name, mode, islink, getdata):
902 # At this point, the standin has been replaced with the largefile name,
904 # At this point, the standin has been replaced with the largefile name,
903 # so the normal matcher works here without the lfutil variants.
905 # so the normal matcher works here without the lfutil variants.
904 if match and not match(f):
906 if match and not match(f):
905 return
907 return
906 data = getdata()
908 data = getdata()
907
909
908 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
910 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
909
911
910 for f in ctx:
912 for f in ctx:
911 ff = ctx.flags(f)
913 ff = ctx.flags(f)
912 getdata = ctx[f].data
914 getdata = ctx[f].data
913 if lfutil.isstandin(f):
915 if lfutil.isstandin(f):
914 path = lfutil.findfile(repo._repo, getdata().strip())
916 path = lfutil.findfile(repo._repo, getdata().strip())
915 if path is None:
917 if path is None:
916 raise util.Abort(
918 raise util.Abort(
917 _('largefile %s not found in repo store or system cache')
919 _('largefile %s not found in repo store or system cache')
918 % lfutil.splitstandin(f))
920 % lfutil.splitstandin(f))
919 f = lfutil.splitstandin(f)
921 f = lfutil.splitstandin(f)
920
922
921 def getdatafn():
923 def getdatafn():
922 fd = None
924 fd = None
923 try:
925 try:
924 fd = open(os.path.join(prefix, path), 'rb')
926 fd = open(os.path.join(prefix, path), 'rb')
925 return fd.read()
927 return fd.read()
926 finally:
928 finally:
927 if fd:
929 if fd:
928 fd.close()
930 fd.close()
929
931
930 getdata = getdatafn
932 getdata = getdatafn
931
933
932 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
934 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
933
935
934 for subpath in sorted(ctx.substate):
936 for subpath in sorted(ctx.substate):
935 sub = ctx.sub(subpath)
937 sub = ctx.sub(subpath)
936 submatch = match_.narrowmatcher(subpath, match)
938 submatch = match_.narrowmatcher(subpath, match)
937 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
939 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
938 submatch)
940 submatch)
939
941
940 # If a largefile is modified, the change is not reflected in its
942 # If a largefile is modified, the change is not reflected in its
941 # standin until a commit. cmdutil.bailifchanged() raises an exception
943 # standin until a commit. cmdutil.bailifchanged() raises an exception
942 # if the repo has uncommitted changes. Wrap it to also check if
944 # if the repo has uncommitted changes. Wrap it to also check if
943 # largefiles were changed. This is used by bisect and backout.
945 # largefiles were changed. This is used by bisect and backout.
944 def overridebailifchanged(orig, repo):
946 def overridebailifchanged(orig, repo):
945 orig(repo)
947 orig(repo)
946 repo.lfstatus = True
948 repo.lfstatus = True
947 modified, added, removed, deleted = repo.status()[:4]
949 modified, added, removed, deleted = repo.status()[:4]
948 repo.lfstatus = False
950 repo.lfstatus = False
949 if modified or added or removed or deleted:
951 if modified or added or removed or deleted:
950 raise util.Abort(_('uncommitted changes'))
952 raise util.Abort(_('uncommitted changes'))
951
953
952 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
954 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
953 def overridefetch(orig, ui, repo, *pats, **opts):
955 def overridefetch(orig, ui, repo, *pats, **opts):
954 repo.lfstatus = True
956 repo.lfstatus = True
955 modified, added, removed, deleted = repo.status()[:4]
957 modified, added, removed, deleted = repo.status()[:4]
956 repo.lfstatus = False
958 repo.lfstatus = False
957 if modified or added or removed or deleted:
959 if modified or added or removed or deleted:
958 raise util.Abort(_('uncommitted changes'))
960 raise util.Abort(_('uncommitted changes'))
959 return orig(ui, repo, *pats, **opts)
961 return orig(ui, repo, *pats, **opts)
960
962
961 def overrideforget(orig, ui, repo, *pats, **opts):
963 def overrideforget(orig, ui, repo, *pats, **opts):
962 installnormalfilesmatchfn(repo[None].manifest())
964 installnormalfilesmatchfn(repo[None].manifest())
963 result = orig(ui, repo, *pats, **opts)
965 result = orig(ui, repo, *pats, **opts)
964 restorematchfn()
966 restorematchfn()
965 m = scmutil.match(repo[None], pats, opts)
967 m = scmutil.match(repo[None], pats, opts)
966
968
967 try:
969 try:
968 repo.lfstatus = True
970 repo.lfstatus = True
969 s = repo.status(match=m, clean=True)
971 s = repo.status(match=m, clean=True)
970 finally:
972 finally:
971 repo.lfstatus = False
973 repo.lfstatus = False
972 forget = sorted(s[0] + s[1] + s[3] + s[6])
974 forget = sorted(s[0] + s[1] + s[3] + s[6])
973 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
975 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
974
976
975 for f in forget:
977 for f in forget:
976 if lfutil.standin(f) not in repo.dirstate and not \
978 if lfutil.standin(f) not in repo.dirstate and not \
977 os.path.isdir(m.rel(lfutil.standin(f))):
979 os.path.isdir(m.rel(lfutil.standin(f))):
978 ui.warn(_('not removing %s: file is already untracked\n')
980 ui.warn(_('not removing %s: file is already untracked\n')
979 % m.rel(f))
981 % m.rel(f))
980 result = 1
982 result = 1
981
983
982 for f in forget:
984 for f in forget:
983 if ui.verbose or not m.exact(f):
985 if ui.verbose or not m.exact(f):
984 ui.status(_('removing %s\n') % m.rel(f))
986 ui.status(_('removing %s\n') % m.rel(f))
985
987
986 # Need to lock because standin files are deleted then removed from the
988 # Need to lock because standin files are deleted then removed from the
987 # repository and we could race in-between.
989 # repository and we could race in-between.
988 wlock = repo.wlock()
990 wlock = repo.wlock()
989 try:
991 try:
990 lfdirstate = lfutil.openlfdirstate(ui, repo)
992 lfdirstate = lfutil.openlfdirstate(ui, repo)
991 for f in forget:
993 for f in forget:
992 if lfdirstate[f] == 'a':
994 if lfdirstate[f] == 'a':
993 lfdirstate.drop(f)
995 lfdirstate.drop(f)
994 else:
996 else:
995 lfdirstate.remove(f)
997 lfdirstate.remove(f)
996 lfdirstate.write()
998 lfdirstate.write()
997 standins = [lfutil.standin(f) for f in forget]
999 standins = [lfutil.standin(f) for f in forget]
998 for f in standins:
1000 for f in standins:
999 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1001 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1000 repo[None].forget(standins)
1002 repo[None].forget(standins)
1001 finally:
1003 finally:
1002 wlock.release()
1004 wlock.release()
1003
1005
1004 return result
1006 return result
1005
1007
1006 def getoutgoinglfiles(ui, repo, dest=None, **opts):
1008 def getoutgoinglfiles(ui, repo, dest=None, **opts):
1007 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1009 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1008 dest, branches = hg.parseurl(dest, opts.get('branch'))
1010 dest, branches = hg.parseurl(dest, opts.get('branch'))
1009 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
1011 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
1010 if revs:
1012 if revs:
1011 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
1013 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
1012
1014
1013 try:
1015 try:
1014 remote = hg.peer(repo, opts, dest)
1016 remote = hg.peer(repo, opts, dest)
1015 except error.RepoError:
1017 except error.RepoError:
1016 return None
1018 return None
1017 outgoing = discovery.findcommonoutgoing(repo, remote.peer(), force=False)
1019 outgoing = discovery.findcommonoutgoing(repo, remote.peer(), force=False)
1018 if not outgoing.missing:
1020 if not outgoing.missing:
1019 return outgoing.missing
1021 return outgoing.missing
1020 o = repo.changelog.nodesbetween(outgoing.missing, revs)[0]
1022 o = repo.changelog.nodesbetween(outgoing.missing, revs)[0]
1021 if opts.get('newest_first'):
1023 if opts.get('newest_first'):
1022 o.reverse()
1024 o.reverse()
1023
1025
1024 toupload = set()
1026 toupload = set()
1025 for n in o:
1027 for n in o:
1026 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
1028 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
1027 ctx = repo[n]
1029 ctx = repo[n]
1028 files = set(ctx.files())
1030 files = set(ctx.files())
1029 if len(parents) == 2:
1031 if len(parents) == 2:
1030 mc = ctx.manifest()
1032 mc = ctx.manifest()
1031 mp1 = ctx.parents()[0].manifest()
1033 mp1 = ctx.parents()[0].manifest()
1032 mp2 = ctx.parents()[1].manifest()
1034 mp2 = ctx.parents()[1].manifest()
1033 for f in mp1:
1035 for f in mp1:
1034 if f not in mc:
1036 if f not in mc:
1035 files.add(f)
1037 files.add(f)
1036 for f in mp2:
1038 for f in mp2:
1037 if f not in mc:
1039 if f not in mc:
1038 files.add(f)
1040 files.add(f)
1039 for f in mc:
1041 for f in mc:
1040 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
1042 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
1041 files.add(f)
1043 files.add(f)
1042 toupload = toupload.union(
1044 toupload = toupload.union(
1043 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
1045 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
1044 return sorted(toupload)
1046 return sorted(toupload)
1045
1047
1046 def overrideoutgoing(orig, ui, repo, dest=None, **opts):
1048 def overrideoutgoing(orig, ui, repo, dest=None, **opts):
1047 result = orig(ui, repo, dest, **opts)
1049 result = orig(ui, repo, dest, **opts)
1048
1050
1049 if opts.pop('large', None):
1051 if opts.pop('large', None):
1050 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
1052 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
1051 if toupload is None:
1053 if toupload is None:
1052 ui.status(_('largefiles: No remote repo\n'))
1054 ui.status(_('largefiles: No remote repo\n'))
1053 elif not toupload:
1055 elif not toupload:
1054 ui.status(_('largefiles: no files to upload\n'))
1056 ui.status(_('largefiles: no files to upload\n'))
1055 else:
1057 else:
1056 ui.status(_('largefiles to upload:\n'))
1058 ui.status(_('largefiles to upload:\n'))
1057 for file in toupload:
1059 for file in toupload:
1058 ui.status(lfutil.splitstandin(file) + '\n')
1060 ui.status(lfutil.splitstandin(file) + '\n')
1059 ui.status('\n')
1061 ui.status('\n')
1060
1062
1061 return result
1063 return result
1062
1064
1063 def overridesummary(orig, ui, repo, *pats, **opts):
1065 def overridesummary(orig, ui, repo, *pats, **opts):
1064 try:
1066 try:
1065 repo.lfstatus = True
1067 repo.lfstatus = True
1066 orig(ui, repo, *pats, **opts)
1068 orig(ui, repo, *pats, **opts)
1067 finally:
1069 finally:
1068 repo.lfstatus = False
1070 repo.lfstatus = False
1069
1071
1070 if opts.pop('large', None):
1072 if opts.pop('large', None):
1071 toupload = getoutgoinglfiles(ui, repo, None, **opts)
1073 toupload = getoutgoinglfiles(ui, repo, None, **opts)
1072 if toupload is None:
1074 if toupload is None:
1073 # i18n: column positioning for "hg summary"
1075 # i18n: column positioning for "hg summary"
1074 ui.status(_('largefiles: (no remote repo)\n'))
1076 ui.status(_('largefiles: (no remote repo)\n'))
1075 elif not toupload:
1077 elif not toupload:
1076 # i18n: column positioning for "hg summary"
1078 # i18n: column positioning for "hg summary"
1077 ui.status(_('largefiles: (no files to upload)\n'))
1079 ui.status(_('largefiles: (no files to upload)\n'))
1078 else:
1080 else:
1079 # i18n: column positioning for "hg summary"
1081 # i18n: column positioning for "hg summary"
1080 ui.status(_('largefiles: %d to upload\n') % len(toupload))
1082 ui.status(_('largefiles: %d to upload\n') % len(toupload))
1081
1083
1082 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1084 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1083 similarity=None):
1085 similarity=None):
1084 if not lfutil.islfilesrepo(repo):
1086 if not lfutil.islfilesrepo(repo):
1085 return orig(repo, pats, opts, dry_run, similarity)
1087 return orig(repo, pats, opts, dry_run, similarity)
1086 # Get the list of missing largefiles so we can remove them
1088 # Get the list of missing largefiles so we can remove them
1087 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1089 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1088 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
1090 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
1089 False, False)
1091 False, False)
1090 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
1092 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
1091
1093
1092 # Call into the normal remove code, but the removing of the standin, we want
1094 # Call into the normal remove code, but the removing of the standin, we want
1093 # to have handled by original addremove. Monkey patching here makes sure
1095 # to have handled by original addremove. Monkey patching here makes sure
1094 # we don't remove the standin in the largefiles code, preventing a very
1096 # we don't remove the standin in the largefiles code, preventing a very
1095 # confused state later.
1097 # confused state later.
1096 if missing:
1098 if missing:
1097 m = [repo.wjoin(f) for f in missing]
1099 m = [repo.wjoin(f) for f in missing]
1098 repo._isaddremove = True
1100 repo._isaddremove = True
1099 removelargefiles(repo.ui, repo, *m, **opts)
1101 removelargefiles(repo.ui, repo, *m, **opts)
1100 repo._isaddremove = False
1102 repo._isaddremove = False
1101 # Call into the normal add code, and any files that *should* be added as
1103 # Call into the normal add code, and any files that *should* be added as
1102 # largefiles will be
1104 # largefiles will be
1103 addlargefiles(repo.ui, repo, *pats, **opts)
1105 addlargefiles(repo.ui, repo, *pats, **opts)
1104 # Now that we've handled largefiles, hand off to the original addremove
1106 # Now that we've handled largefiles, hand off to the original addremove
1105 # function to take care of the rest. Make sure it doesn't do anything with
1107 # function to take care of the rest. Make sure it doesn't do anything with
1106 # largefiles by installing a matcher that will ignore them.
1108 # largefiles by installing a matcher that will ignore them.
1107 installnormalfilesmatchfn(repo[None].manifest())
1109 installnormalfilesmatchfn(repo[None].manifest())
1108 result = orig(repo, pats, opts, dry_run, similarity)
1110 result = orig(repo, pats, opts, dry_run, similarity)
1109 restorematchfn()
1111 restorematchfn()
1110 return result
1112 return result
1111
1113
1112 # Calling purge with --all will cause the largefiles to be deleted.
1114 # Calling purge with --all will cause the largefiles to be deleted.
1113 # Override repo.status to prevent this from happening.
1115 # Override repo.status to prevent this from happening.
1114 def overridepurge(orig, ui, repo, *dirs, **opts):
1116 def overridepurge(orig, ui, repo, *dirs, **opts):
1115 # XXX large file status is buggy when used on repo proxy.
1117 # XXX large file status is buggy when used on repo proxy.
1116 # XXX this needs to be investigate.
1118 # XXX this needs to be investigate.
1117 repo = repo.unfiltered()
1119 repo = repo.unfiltered()
1118 oldstatus = repo.status
1120 oldstatus = repo.status
1119 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1121 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1120 clean=False, unknown=False, listsubrepos=False):
1122 clean=False, unknown=False, listsubrepos=False):
1121 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1123 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1122 listsubrepos)
1124 listsubrepos)
1123 lfdirstate = lfutil.openlfdirstate(ui, repo)
1125 lfdirstate = lfutil.openlfdirstate(ui, repo)
1124 modified, added, removed, deleted, unknown, ignored, clean = r
1126 modified, added, removed, deleted, unknown, ignored, clean = r
1125 unknown = [f for f in unknown if lfdirstate[f] == '?']
1127 unknown = [f for f in unknown if lfdirstate[f] == '?']
1126 ignored = [f for f in ignored if lfdirstate[f] == '?']
1128 ignored = [f for f in ignored if lfdirstate[f] == '?']
1127 return modified, added, removed, deleted, unknown, ignored, clean
1129 return modified, added, removed, deleted, unknown, ignored, clean
1128 repo.status = overridestatus
1130 repo.status = overridestatus
1129 orig(ui, repo, *dirs, **opts)
1131 orig(ui, repo, *dirs, **opts)
1130 repo.status = oldstatus
1132 repo.status = oldstatus
1131
1133
1132 def overriderollback(orig, ui, repo, **opts):
1134 def overriderollback(orig, ui, repo, **opts):
1133 result = orig(ui, repo, **opts)
1135 result = orig(ui, repo, **opts)
1134 merge.update(repo, node=None, branchmerge=False, force=True,
1136 merge.update(repo, node=None, branchmerge=False, force=True,
1135 partial=lfutil.isstandin)
1137 partial=lfutil.isstandin)
1136 wlock = repo.wlock()
1138 wlock = repo.wlock()
1137 try:
1139 try:
1138 lfdirstate = lfutil.openlfdirstate(ui, repo)
1140 lfdirstate = lfutil.openlfdirstate(ui, repo)
1139 lfiles = lfutil.listlfiles(repo)
1141 lfiles = lfutil.listlfiles(repo)
1140 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
1142 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
1141 for file in lfiles:
1143 for file in lfiles:
1142 if file in oldlfiles:
1144 if file in oldlfiles:
1143 lfdirstate.normallookup(file)
1145 lfdirstate.normallookup(file)
1144 else:
1146 else:
1145 lfdirstate.add(file)
1147 lfdirstate.add(file)
1146 lfdirstate.write()
1148 lfdirstate.write()
1147 finally:
1149 finally:
1148 wlock.release()
1150 wlock.release()
1149 return result
1151 return result
1150
1152
1151 def overridetransplant(orig, ui, repo, *revs, **opts):
1153 def overridetransplant(orig, ui, repo, *revs, **opts):
1152 try:
1154 try:
1153 oldstandins = lfutil.getstandinsstate(repo)
1155 oldstandins = lfutil.getstandinsstate(repo)
1154 repo._istransplanting = True
1156 repo._istransplanting = True
1155 result = orig(ui, repo, *revs, **opts)
1157 result = orig(ui, repo, *revs, **opts)
1156 newstandins = lfutil.getstandinsstate(repo)
1158 newstandins = lfutil.getstandinsstate(repo)
1157 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1159 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1158 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1160 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1159 printmessage=True)
1161 printmessage=True)
1160 finally:
1162 finally:
1161 repo._istransplanting = False
1163 repo._istransplanting = False
1162 return result
1164 return result
1163
1165
1164 def overridecat(orig, ui, repo, file1, *pats, **opts):
1166 def overridecat(orig, ui, repo, file1, *pats, **opts):
1165 ctx = scmutil.revsingle(repo, opts.get('rev'))
1167 ctx = scmutil.revsingle(repo, opts.get('rev'))
1166 err = 1
1168 err = 1
1167 notbad = set()
1169 notbad = set()
1168 m = scmutil.match(ctx, (file1,) + pats, opts)
1170 m = scmutil.match(ctx, (file1,) + pats, opts)
1169 origmatchfn = m.matchfn
1171 origmatchfn = m.matchfn
1170 def lfmatchfn(f):
1172 def lfmatchfn(f):
1171 lf = lfutil.splitstandin(f)
1173 lf = lfutil.splitstandin(f)
1172 if lf is None:
1174 if lf is None:
1173 return origmatchfn(f)
1175 return origmatchfn(f)
1174 notbad.add(lf)
1176 notbad.add(lf)
1175 return origmatchfn(lf)
1177 return origmatchfn(lf)
1176 m.matchfn = lfmatchfn
1178 m.matchfn = lfmatchfn
1177 origbadfn = m.bad
1179 origbadfn = m.bad
1178 def lfbadfn(f, msg):
1180 def lfbadfn(f, msg):
1179 if not f in notbad:
1181 if not f in notbad:
1180 return origbadfn(f, msg)
1182 return origbadfn(f, msg)
1181 m.bad = lfbadfn
1183 m.bad = lfbadfn
1182 for f in ctx.walk(m):
1184 for f in ctx.walk(m):
1183 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1185 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1184 pathname=f)
1186 pathname=f)
1185 lf = lfutil.splitstandin(f)
1187 lf = lfutil.splitstandin(f)
1186 if lf is None:
1188 if lf is None:
1187 # duplicating unreachable code from commands.cat
1189 # duplicating unreachable code from commands.cat
1188 data = ctx[f].data()
1190 data = ctx[f].data()
1189 if opts.get('decode'):
1191 if opts.get('decode'):
1190 data = repo.wwritedata(f, data)
1192 data = repo.wwritedata(f, data)
1191 fp.write(data)
1193 fp.write(data)
1192 else:
1194 else:
1193 hash = lfutil.readstandin(repo, lf, ctx.rev())
1195 hash = lfutil.readstandin(repo, lf, ctx.rev())
1194 if not lfutil.inusercache(repo.ui, hash):
1196 if not lfutil.inusercache(repo.ui, hash):
1195 store = basestore._openstore(repo)
1197 store = basestore._openstore(repo)
1196 success, missing = store.get([(lf, hash)])
1198 success, missing = store.get([(lf, hash)])
1197 if len(success) != 1:
1199 if len(success) != 1:
1198 raise util.Abort(
1200 raise util.Abort(
1199 _('largefile %s is not in cache and could not be '
1201 _('largefile %s is not in cache and could not be '
1200 'downloaded') % lf)
1202 'downloaded') % lf)
1201 path = lfutil.usercachepath(repo.ui, hash)
1203 path = lfutil.usercachepath(repo.ui, hash)
1202 fpin = open(path, "rb")
1204 fpin = open(path, "rb")
1203 for chunk in util.filechunkiter(fpin, 128 * 1024):
1205 for chunk in util.filechunkiter(fpin, 128 * 1024):
1204 fp.write(chunk)
1206 fp.write(chunk)
1205 fpin.close()
1207 fpin.close()
1206 fp.close()
1208 fp.close()
1207 err = 0
1209 err = 0
1208 return err
1210 return err
1209
1211
1210 def mercurialsinkbefore(orig, sink):
1212 def mercurialsinkbefore(orig, sink):
1211 sink.repo._isconverting = True
1213 sink.repo._isconverting = True
1212 orig(sink)
1214 orig(sink)
1213
1215
1214 def mercurialsinkafter(orig, sink):
1216 def mercurialsinkafter(orig, sink):
1215 sink.repo._isconverting = False
1217 sink.repo._isconverting = False
1216 orig(sink)
1218 orig(sink)
@@ -1,112 +1,139
1
1
2 $ echo "[extensions]" >> $HGRCPATH
2 $ echo "[extensions]" >> $HGRCPATH
3 $ echo "largefiles =" >> $HGRCPATH
3 $ echo "largefiles =" >> $HGRCPATH
4
4
5 Create the repository outside $HOME since largefiles write to
5 Create the repository outside $HOME since largefiles write to
6 $HOME/.cache/largefiles.
6 $HOME/.cache/largefiles.
7
7
8 $ hg init test
8 $ hg init test
9 $ cd test
9 $ cd test
10 $ echo "root" > root
10 $ echo "root" > root
11 $ hg add root
11 $ hg add root
12 $ hg commit -m "Root commit"
12 $ hg commit -m "Root commit"
13
13
14 $ echo "large" > foo
14 $ echo "large" > foo
15 $ hg add --large foo
15 $ hg add --large foo
16 $ hg commit -m "Add foo as a largefile"
16 $ hg commit -m "Add foo as a largefile"
17
17
18 $ hg update -r 0
18 $ hg update -r 0
19 getting changed largefiles
19 getting changed largefiles
20 0 largefiles updated, 1 removed
20 0 largefiles updated, 1 removed
21 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
21 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
22
22
23 $ echo "normal" > foo
23 $ echo "normal" > foo
24 $ hg add foo
24 $ hg add foo
25 $ hg commit -m "Add foo as normal file"
25 $ hg commit -m "Add foo as normal file"
26 created new head
26 created new head
27
27
28 Normal file in the working copy, keeping the normal version:
28 Normal file in the working copy, keeping the normal version:
29
29
30 $ echo "n" | hg merge --config ui.interactive=Yes
30 $ echo "n" | hg merge --config ui.interactive=Yes
31 foo has been turned into a largefile
31 foo has been turned into a largefile
32 use (l)argefile or keep as (n)ormal file? 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
32 use (l)argefile or keep as (n)ormal file? 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
33 (branch merge, don't forget to commit)
33 (branch merge, don't forget to commit)
34 getting changed largefiles
34 getting changed largefiles
35 0 largefiles updated, 0 removed
35 0 largefiles updated, 0 removed
36
36
37 $ hg status
37 $ hg status
38 $ cat foo
38 $ cat foo
39 normal
39 normal
40
40
41 Normal file in the working copy, keeping the largefile version:
41 Normal file in the working copy, keeping the largefile version:
42
42
43 $ hg update -q -C
43 $ hg update -q -C
44 $ echo "l" | hg merge --config ui.interactive=Yes
44 $ echo "l" | hg merge --config ui.interactive=Yes
45 foo has been turned into a largefile
45 foo has been turned into a largefile
46 use (l)argefile or keep as (n)ormal file? 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
46 use (l)argefile or keep as (n)ormal file? 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
47 (branch merge, don't forget to commit)
47 (branch merge, don't forget to commit)
48 getting changed largefiles
48 getting changed largefiles
49 1 largefiles updated, 0 removed
49 1 largefiles updated, 0 removed
50
50
51 $ hg status
51 $ hg status
52 M foo
52 M foo
53
53
54 $ hg diff --nodates
54 $ hg diff --nodates
55 diff -r fa129ab6b5a7 .hglf/foo
55 diff -r fa129ab6b5a7 .hglf/foo
56 --- /dev/null
56 --- /dev/null
57 +++ b/.hglf/foo
57 +++ b/.hglf/foo
58 @@ -0,0 +1,1 @@
58 @@ -0,0 +1,1 @@
59 +7f7097b041ccf68cc5561e9600da4655d21c6d18
59 +7f7097b041ccf68cc5561e9600da4655d21c6d18
60 diff -r fa129ab6b5a7 foo
60 diff -r fa129ab6b5a7 foo
61 --- a/foo
61 --- a/foo
62 +++ /dev/null
62 +++ /dev/null
63 @@ -1,1 +0,0 @@
63 @@ -1,1 +0,0 @@
64 -normal
64 -normal
65
65
66 $ cat foo
66 $ cat foo
67 large
67 large
68
68
69 Largefile in the working copy, keeping the normal version:
69 Largefile in the working copy, keeping the normal version:
70
70
71 $ hg update -q -C -r 1
71 $ hg update -q -C -r 1
72 $ echo "n" | hg merge --config ui.interactive=Yes
72 $ echo "n" | hg merge --config ui.interactive=Yes
73 foo has been turned into a normal file
73 foo has been turned into a normal file
74 keep as (l)argefile or use (n)ormal file? 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
74 keep as (l)argefile or use (n)ormal file? 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
75 (branch merge, don't forget to commit)
75 (branch merge, don't forget to commit)
76 getting changed largefiles
76 getting changed largefiles
77 0 largefiles updated, 0 removed
77 0 largefiles updated, 0 removed
78
78
79 $ hg status
79 $ hg status
80 M foo
80 M foo
81
81
82 $ hg diff --nodates
82 $ hg diff --nodates
83 diff -r ff521236428a .hglf/foo
83 diff -r ff521236428a .hglf/foo
84 --- a/.hglf/foo
84 --- a/.hglf/foo
85 +++ /dev/null
85 +++ /dev/null
86 @@ -1,1 +0,0 @@
86 @@ -1,1 +0,0 @@
87 -7f7097b041ccf68cc5561e9600da4655d21c6d18
87 -7f7097b041ccf68cc5561e9600da4655d21c6d18
88 diff -r ff521236428a foo
88 diff -r ff521236428a foo
89 --- /dev/null
89 --- /dev/null
90 +++ b/foo
90 +++ b/foo
91 @@ -0,0 +1,1 @@
91 @@ -0,0 +1,1 @@
92 +normal
92 +normal
93
93
94 $ cat foo
94 $ cat foo
95 normal
95 normal
96
96
97 Largefile in the working copy, keeping the largefile version:
97 Largefile in the working copy, keeping the largefile version:
98
98
99 $ hg update -q -C -r 1
99 $ hg update -q -C -r 1
100 $ echo "l" | hg merge --config ui.interactive=Yes
100 $ echo "l" | hg merge --config ui.interactive=Yes
101 foo has been turned into a normal file
101 foo has been turned into a normal file
102 keep as (l)argefile or use (n)ormal file? 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
102 keep as (l)argefile or use (n)ormal file? 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
103 (branch merge, don't forget to commit)
103 (branch merge, don't forget to commit)
104 getting changed largefiles
104 getting changed largefiles
105 1 largefiles updated, 0 removed
105 1 largefiles updated, 0 removed
106
106
107 $ hg status
107 $ hg status
108
108
109 $ cat foo
109 $ cat foo
110 large
110 large
111
111
112 Whatever ... commit something so we can invoke merge when updating
113
114 $ hg commit -m '3: Merge'
115
116 Updating from largefile to normal - no reason to prompt
117
118 $ hg up -r 2
119 getting changed largefiles
120 0 largefiles updated, 0 removed
121 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
122 $ cat foo
123 normal
124
125 (the update above used to leave the working dir in a very weird state - clean it
126 $ hg up -qr null
127 $ hg up -qr 2
128 )
129
130 Updating from normal to largefile - no reason to prompt
131
132 $ hg up -r 3
133 getting changed largefiles
134 1 largefiles updated, 0 removed
135 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
136 $ cat foo
137 large
138
112 $ cd ..
139 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now