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