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