##// END OF EJS Templates
merge: move cd/dc prompts after largefiles prompts...
Martin von Zweigbergk -
r23541:495bc1b6 default
parent child Browse files
Show More
@@ -1,1285 +1,1289 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
10
10
11 import os
11 import os
12 import copy
12 import copy
13
13
14 from mercurial import hg, util, cmdutil, scmutil, match as match_, \
14 from mercurial import hg, 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
18
19 import lfutil
19 import lfutil
20 import lfcommands
20 import lfcommands
21 import basestore
21 import basestore
22
22
23 # -- Utility functions: commonly/repeatedly needed functionality ---------------
23 # -- Utility functions: commonly/repeatedly needed functionality ---------------
24
24
25 def composenormalfilematcher(match, manifest):
25 def composenormalfilematcher(match, manifest):
26 m = copy.copy(match)
26 m = copy.copy(match)
27 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
27 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
28 manifest)
28 manifest)
29 m._files = filter(notlfile, m._files)
29 m._files = filter(notlfile, m._files)
30 m._fmap = set(m._files)
30 m._fmap = set(m._files)
31 m._always = False
31 m._always = False
32 origmatchfn = m.matchfn
32 origmatchfn = m.matchfn
33 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
33 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
34 return m
34 return m
35
35
36 def installnormalfilesmatchfn(manifest):
36 def installnormalfilesmatchfn(manifest):
37 '''installmatchfn with a matchfn that ignores all largefiles'''
37 '''installmatchfn with a matchfn that ignores all largefiles'''
38 def overridematch(ctx, pats=[], opts={}, globbed=False,
38 def overridematch(ctx, pats=[], opts={}, globbed=False,
39 default='relpath'):
39 default='relpath'):
40 match = oldmatch(ctx, pats, opts, globbed, default)
40 match = oldmatch(ctx, pats, opts, globbed, default)
41 return composenormalfilematcher(match, manifest)
41 return composenormalfilematcher(match, manifest)
42 oldmatch = installmatchfn(overridematch)
42 oldmatch = installmatchfn(overridematch)
43
43
44 def installmatchfn(f):
44 def installmatchfn(f):
45 '''monkey patch the scmutil module with a custom match function.
45 '''monkey patch the scmutil module with a custom match function.
46 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
46 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
47 oldmatch = scmutil.match
47 oldmatch = scmutil.match
48 setattr(f, 'oldmatch', oldmatch)
48 setattr(f, 'oldmatch', oldmatch)
49 scmutil.match = f
49 scmutil.match = f
50 return oldmatch
50 return oldmatch
51
51
52 def restorematchfn():
52 def restorematchfn():
53 '''restores scmutil.match to what it was before installmatchfn
53 '''restores scmutil.match to what it was before installmatchfn
54 was called. no-op if scmutil.match is its original function.
54 was called. no-op if scmutil.match is its original function.
55
55
56 Note that n calls to installmatchfn will require n calls to
56 Note that n calls to installmatchfn will require n calls to
57 restore matchfn to reverse'''
57 restore matchfn to reverse'''
58 scmutil.match = getattr(scmutil.match, 'oldmatch')
58 scmutil.match = getattr(scmutil.match, 'oldmatch')
59
59
60 def installmatchandpatsfn(f):
60 def installmatchandpatsfn(f):
61 oldmatchandpats = scmutil.matchandpats
61 oldmatchandpats = scmutil.matchandpats
62 setattr(f, 'oldmatchandpats', oldmatchandpats)
62 setattr(f, 'oldmatchandpats', oldmatchandpats)
63 scmutil.matchandpats = f
63 scmutil.matchandpats = f
64 return oldmatchandpats
64 return oldmatchandpats
65
65
66 def restorematchandpatsfn():
66 def restorematchandpatsfn():
67 '''restores scmutil.matchandpats to what it was before
67 '''restores scmutil.matchandpats to what it was before
68 installmatchandpatsfn was called. No-op if scmutil.matchandpats
68 installmatchandpatsfn was called. No-op if scmutil.matchandpats
69 is its original function.
69 is its original function.
70
70
71 Note that n calls to installmatchandpatsfn will require n calls
71 Note that n calls to installmatchandpatsfn will require n calls
72 to restore matchfn to reverse'''
72 to restore matchfn to reverse'''
73 scmutil.matchandpats = getattr(scmutil.matchandpats, 'oldmatchandpats',
73 scmutil.matchandpats = getattr(scmutil.matchandpats, 'oldmatchandpats',
74 scmutil.matchandpats)
74 scmutil.matchandpats)
75
75
76 def addlargefiles(ui, repo, matcher, **opts):
76 def addlargefiles(ui, repo, matcher, **opts):
77 large = opts.pop('large', None)
77 large = opts.pop('large', None)
78 lfsize = lfutil.getminsize(
78 lfsize = lfutil.getminsize(
79 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
79 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
80
80
81 lfmatcher = None
81 lfmatcher = None
82 if lfutil.islfilesrepo(repo):
82 if lfutil.islfilesrepo(repo):
83 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
83 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
84 if lfpats:
84 if lfpats:
85 lfmatcher = match_.match(repo.root, '', list(lfpats))
85 lfmatcher = match_.match(repo.root, '', list(lfpats))
86
86
87 lfnames = []
87 lfnames = []
88 m = copy.copy(matcher)
88 m = copy.copy(matcher)
89 m.bad = lambda x, y: None
89 m.bad = lambda x, y: None
90 wctx = repo[None]
90 wctx = repo[None]
91 for f in repo.walk(m):
91 for f in repo.walk(m):
92 exact = m.exact(f)
92 exact = m.exact(f)
93 lfile = lfutil.standin(f) in wctx
93 lfile = lfutil.standin(f) in wctx
94 nfile = f in wctx
94 nfile = f in wctx
95 exists = lfile or nfile
95 exists = lfile or nfile
96
96
97 # Don't warn the user when they attempt to add a normal tracked file.
97 # Don't warn the user when they attempt to add a normal tracked file.
98 # The normal add code will do that for us.
98 # The normal add code will do that for us.
99 if exact and exists:
99 if exact and exists:
100 if lfile:
100 if lfile:
101 ui.warn(_('%s already a largefile\n') % f)
101 ui.warn(_('%s already a largefile\n') % f)
102 continue
102 continue
103
103
104 if (exact or not exists) and not lfutil.isstandin(f):
104 if (exact or not exists) and not lfutil.isstandin(f):
105 wfile = repo.wjoin(f)
105 wfile = repo.wjoin(f)
106
106
107 # In case the file was removed previously, but not committed
107 # In case the file was removed previously, but not committed
108 # (issue3507)
108 # (issue3507)
109 if not os.path.exists(wfile):
109 if not os.path.exists(wfile):
110 continue
110 continue
111
111
112 abovemin = (lfsize and
112 abovemin = (lfsize and
113 os.lstat(wfile).st_size >= lfsize * 1024 * 1024)
113 os.lstat(wfile).st_size >= lfsize * 1024 * 1024)
114 if large or abovemin or (lfmatcher and lfmatcher(f)):
114 if large or abovemin or (lfmatcher and lfmatcher(f)):
115 lfnames.append(f)
115 lfnames.append(f)
116 if ui.verbose or not exact:
116 if ui.verbose or not exact:
117 ui.status(_('adding %s as a largefile\n') % m.rel(f))
117 ui.status(_('adding %s as a largefile\n') % m.rel(f))
118
118
119 bad = []
119 bad = []
120
120
121 # Need to lock, otherwise there could be a race condition between
121 # Need to lock, otherwise there could be a race condition between
122 # when standins are created and added to the repo.
122 # when standins are created and added to the repo.
123 wlock = repo.wlock()
123 wlock = repo.wlock()
124 try:
124 try:
125 if not opts.get('dry_run'):
125 if not opts.get('dry_run'):
126 standins = []
126 standins = []
127 lfdirstate = lfutil.openlfdirstate(ui, repo)
127 lfdirstate = lfutil.openlfdirstate(ui, repo)
128 for f in lfnames:
128 for f in lfnames:
129 standinname = lfutil.standin(f)
129 standinname = lfutil.standin(f)
130 lfutil.writestandin(repo, standinname, hash='',
130 lfutil.writestandin(repo, standinname, hash='',
131 executable=lfutil.getexecutable(repo.wjoin(f)))
131 executable=lfutil.getexecutable(repo.wjoin(f)))
132 standins.append(standinname)
132 standins.append(standinname)
133 if lfdirstate[f] == 'r':
133 if lfdirstate[f] == 'r':
134 lfdirstate.normallookup(f)
134 lfdirstate.normallookup(f)
135 else:
135 else:
136 lfdirstate.add(f)
136 lfdirstate.add(f)
137 lfdirstate.write()
137 lfdirstate.write()
138 bad += [lfutil.splitstandin(f)
138 bad += [lfutil.splitstandin(f)
139 for f in repo[None].add(standins)
139 for f in repo[None].add(standins)
140 if f in m.files()]
140 if f in m.files()]
141 finally:
141 finally:
142 wlock.release()
142 wlock.release()
143 return bad
143 return bad
144
144
145 def removelargefiles(ui, repo, isaddremove, *pats, **opts):
145 def removelargefiles(ui, repo, isaddremove, *pats, **opts):
146 after = opts.get('after')
146 after = opts.get('after')
147 if not pats and not after:
147 if not pats and not after:
148 raise util.Abort(_('no files specified'))
148 raise util.Abort(_('no files specified'))
149 m = scmutil.match(repo[None], pats, opts)
149 m = scmutil.match(repo[None], pats, opts)
150 try:
150 try:
151 repo.lfstatus = True
151 repo.lfstatus = True
152 s = repo.status(match=m, clean=True)
152 s = repo.status(match=m, clean=True)
153 finally:
153 finally:
154 repo.lfstatus = False
154 repo.lfstatus = False
155 manifest = repo[None].manifest()
155 manifest = repo[None].manifest()
156 modified, added, deleted, clean = [[f for f in list
156 modified, added, deleted, clean = [[f for f in list
157 if lfutil.standin(f) in manifest]
157 if lfutil.standin(f) in manifest]
158 for list in (s.modified, s.added,
158 for list in (s.modified, s.added,
159 s.deleted, s.clean)]
159 s.deleted, s.clean)]
160
160
161 def warn(files, msg):
161 def warn(files, msg):
162 for f in files:
162 for f in files:
163 ui.warn(msg % m.rel(f))
163 ui.warn(msg % m.rel(f))
164 return int(len(files) > 0)
164 return int(len(files) > 0)
165
165
166 result = 0
166 result = 0
167
167
168 if after:
168 if after:
169 remove = deleted
169 remove = deleted
170 result = warn(modified + added + clean,
170 result = warn(modified + added + clean,
171 _('not removing %s: file still exists\n'))
171 _('not removing %s: file still exists\n'))
172 else:
172 else:
173 remove = deleted + clean
173 remove = deleted + clean
174 result = warn(modified, _('not removing %s: file is modified (use -f'
174 result = warn(modified, _('not removing %s: file is modified (use -f'
175 ' to force removal)\n'))
175 ' to force removal)\n'))
176 result = warn(added, _('not removing %s: file has been marked for add'
176 result = warn(added, _('not removing %s: file has been marked for add'
177 ' (use forget to undo)\n')) or result
177 ' (use forget to undo)\n')) or result
178
178
179 for f in sorted(remove):
179 for f in sorted(remove):
180 if ui.verbose or not m.exact(f):
180 if ui.verbose or not m.exact(f):
181 ui.status(_('removing %s\n') % m.rel(f))
181 ui.status(_('removing %s\n') % m.rel(f))
182
182
183 # Need to lock because standin files are deleted then removed from the
183 # Need to lock because standin files are deleted then removed from the
184 # repository and we could race in-between.
184 # repository and we could race in-between.
185 wlock = repo.wlock()
185 wlock = repo.wlock()
186 try:
186 try:
187 lfdirstate = lfutil.openlfdirstate(ui, repo)
187 lfdirstate = lfutil.openlfdirstate(ui, repo)
188 for f in remove:
188 for f in remove:
189 if not after:
189 if not after:
190 # If this is being called by addremove, notify the user that we
190 # If this is being called by addremove, notify the user that we
191 # are removing the file.
191 # are removing the file.
192 if isaddremove:
192 if isaddremove:
193 ui.status(_('removing %s\n') % f)
193 ui.status(_('removing %s\n') % f)
194 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
194 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
195 lfdirstate.remove(f)
195 lfdirstate.remove(f)
196 lfdirstate.write()
196 lfdirstate.write()
197 remove = [lfutil.standin(f) for f in remove]
197 remove = [lfutil.standin(f) for f in remove]
198 # If this is being called by addremove, let the original addremove
198 # If this is being called by addremove, let the original addremove
199 # function handle this.
199 # function handle this.
200 if not isaddremove:
200 if not isaddremove:
201 for f in remove:
201 for f in remove:
202 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
202 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
203 repo[None].forget(remove)
203 repo[None].forget(remove)
204 finally:
204 finally:
205 wlock.release()
205 wlock.release()
206
206
207 return result
207 return result
208
208
209 # For overriding mercurial.hgweb.webcommands so that largefiles will
209 # For overriding mercurial.hgweb.webcommands so that largefiles will
210 # appear at their right place in the manifests.
210 # appear at their right place in the manifests.
211 def decodepath(orig, path):
211 def decodepath(orig, path):
212 return lfutil.splitstandin(path) or path
212 return lfutil.splitstandin(path) or path
213
213
214 # -- Wrappers: modify existing commands --------------------------------
214 # -- Wrappers: modify existing commands --------------------------------
215
215
216 # Add works by going through the files that the user wanted to add and
216 # Add works by going through the files that the user wanted to add and
217 # checking if they should be added as largefiles. Then it makes a new
217 # checking if they should be added as largefiles. Then it makes a new
218 # matcher which matches only the normal files and runs the original
218 # matcher which matches only the normal files and runs the original
219 # version of add.
219 # version of add.
220 def overrideadd(orig, ui, repo, *pats, **opts):
220 def overrideadd(orig, ui, repo, *pats, **opts):
221 normal = opts.pop('normal')
221 normal = opts.pop('normal')
222 if normal:
222 if normal:
223 if opts.get('large'):
223 if opts.get('large'):
224 raise util.Abort(_('--normal cannot be used with --large'))
224 raise util.Abort(_('--normal cannot be used with --large'))
225 return orig(ui, repo, *pats, **opts)
225 return orig(ui, repo, *pats, **opts)
226 matcher = scmutil.match(repo[None], pats, opts)
226 matcher = scmutil.match(repo[None], pats, opts)
227 bad = addlargefiles(ui, repo, matcher, **opts)
227 bad = addlargefiles(ui, repo, matcher, **opts)
228 installnormalfilesmatchfn(repo[None].manifest())
228 installnormalfilesmatchfn(repo[None].manifest())
229 result = orig(ui, repo, *pats, **opts)
229 result = orig(ui, repo, *pats, **opts)
230 restorematchfn()
230 restorematchfn()
231
231
232 return (result == 1 or bad) and 1 or 0
232 return (result == 1 or bad) and 1 or 0
233
233
234 def overrideremove(orig, ui, repo, *pats, **opts):
234 def overrideremove(orig, ui, repo, *pats, **opts):
235 installnormalfilesmatchfn(repo[None].manifest())
235 installnormalfilesmatchfn(repo[None].manifest())
236 result = orig(ui, repo, *pats, **opts)
236 result = orig(ui, repo, *pats, **opts)
237 restorematchfn()
237 restorematchfn()
238 return removelargefiles(ui, repo, False, *pats, **opts) or result
238 return removelargefiles(ui, repo, False, *pats, **opts) or result
239
239
240 def overridestatusfn(orig, repo, rev2, **opts):
240 def overridestatusfn(orig, repo, rev2, **opts):
241 try:
241 try:
242 repo._repo.lfstatus = True
242 repo._repo.lfstatus = True
243 return orig(repo, rev2, **opts)
243 return orig(repo, rev2, **opts)
244 finally:
244 finally:
245 repo._repo.lfstatus = False
245 repo._repo.lfstatus = False
246
246
247 def overridestatus(orig, ui, repo, *pats, **opts):
247 def overridestatus(orig, ui, repo, *pats, **opts):
248 try:
248 try:
249 repo.lfstatus = True
249 repo.lfstatus = True
250 return orig(ui, repo, *pats, **opts)
250 return orig(ui, repo, *pats, **opts)
251 finally:
251 finally:
252 repo.lfstatus = False
252 repo.lfstatus = False
253
253
254 def overridedirty(orig, repo, ignoreupdate=False):
254 def overridedirty(orig, repo, ignoreupdate=False):
255 try:
255 try:
256 repo._repo.lfstatus = True
256 repo._repo.lfstatus = True
257 return orig(repo, ignoreupdate)
257 return orig(repo, ignoreupdate)
258 finally:
258 finally:
259 repo._repo.lfstatus = False
259 repo._repo.lfstatus = False
260
260
261 def overridelog(orig, ui, repo, *pats, **opts):
261 def overridelog(orig, ui, repo, *pats, **opts):
262 def overridematchandpats(ctx, pats=[], opts={}, globbed=False,
262 def overridematchandpats(ctx, pats=[], opts={}, globbed=False,
263 default='relpath'):
263 default='relpath'):
264 """Matcher that merges root directory with .hglf, suitable for log.
264 """Matcher that merges root directory with .hglf, suitable for log.
265 It is still possible to match .hglf directly.
265 It is still possible to match .hglf directly.
266 For any listed files run log on the standin too.
266 For any listed files run log on the standin too.
267 matchfn tries both the given filename and with .hglf stripped.
267 matchfn tries both the given filename and with .hglf stripped.
268 """
268 """
269 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default)
269 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default)
270 m, p = copy.copy(matchandpats)
270 m, p = copy.copy(matchandpats)
271
271
272 if m.always():
272 if m.always():
273 # We want to match everything anyway, so there's no benefit trying
273 # We want to match everything anyway, so there's no benefit trying
274 # to add standins.
274 # to add standins.
275 return matchandpats
275 return matchandpats
276
276
277 pats = set(p)
277 pats = set(p)
278 # TODO: handling of patterns in both cases below
278 # TODO: handling of patterns in both cases below
279 if m._cwd:
279 if m._cwd:
280 if os.path.isabs(m._cwd):
280 if os.path.isabs(m._cwd):
281 # TODO: handle largefile magic when invoked from other cwd
281 # TODO: handle largefile magic when invoked from other cwd
282 return matchandpats
282 return matchandpats
283 back = (m._cwd.count('/') + 1) * '../'
283 back = (m._cwd.count('/') + 1) * '../'
284 pats.update(back + lfutil.standin(m._cwd + '/' + f) for f in p)
284 pats.update(back + lfutil.standin(m._cwd + '/' + f) for f in p)
285 else:
285 else:
286 pats.update(lfutil.standin(f) for f in p)
286 pats.update(lfutil.standin(f) for f in p)
287
287
288 for i in range(0, len(m._files)):
288 for i in range(0, len(m._files)):
289 standin = lfutil.standin(m._files[i])
289 standin = lfutil.standin(m._files[i])
290 if standin in repo[ctx.node()]:
290 if standin in repo[ctx.node()]:
291 m._files[i] = standin
291 m._files[i] = standin
292 elif m._files[i] not in repo[ctx.node()]:
292 elif m._files[i] not in repo[ctx.node()]:
293 m._files.append(standin)
293 m._files.append(standin)
294 pats.add(standin)
294 pats.add(standin)
295
295
296 m._fmap = set(m._files)
296 m._fmap = set(m._files)
297 m._always = False
297 m._always = False
298 origmatchfn = m.matchfn
298 origmatchfn = m.matchfn
299 def lfmatchfn(f):
299 def lfmatchfn(f):
300 lf = lfutil.splitstandin(f)
300 lf = lfutil.splitstandin(f)
301 if lf is not None and origmatchfn(lf):
301 if lf is not None and origmatchfn(lf):
302 return True
302 return True
303 r = origmatchfn(f)
303 r = origmatchfn(f)
304 return r
304 return r
305 m.matchfn = lfmatchfn
305 m.matchfn = lfmatchfn
306
306
307 return m, pats
307 return m, pats
308
308
309 # For hg log --patch, the match object is used in two different senses:
309 # For hg log --patch, the match object is used in two different senses:
310 # (1) to determine what revisions should be printed out, and
310 # (1) to determine what revisions should be printed out, and
311 # (2) to determine what files to print out diffs for.
311 # (2) to determine what files to print out diffs for.
312 # The magic matchandpats override should be used for case (1) but not for
312 # The magic matchandpats override should be used for case (1) but not for
313 # case (2).
313 # case (2).
314 def overridemakelogfilematcher(repo, pats, opts):
314 def overridemakelogfilematcher(repo, pats, opts):
315 pctx = repo[None]
315 pctx = repo[None]
316 match, pats = oldmatchandpats(pctx, pats, opts)
316 match, pats = oldmatchandpats(pctx, pats, opts)
317 return lambda rev: match
317 return lambda rev: match
318
318
319 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
319 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
320 oldmakelogfilematcher = cmdutil._makenofollowlogfilematcher
320 oldmakelogfilematcher = cmdutil._makenofollowlogfilematcher
321 setattr(cmdutil, '_makenofollowlogfilematcher', overridemakelogfilematcher)
321 setattr(cmdutil, '_makenofollowlogfilematcher', overridemakelogfilematcher)
322
322
323 try:
323 try:
324 return orig(ui, repo, *pats, **opts)
324 return orig(ui, repo, *pats, **opts)
325 finally:
325 finally:
326 restorematchandpatsfn()
326 restorematchandpatsfn()
327 setattr(cmdutil, '_makenofollowlogfilematcher', oldmakelogfilematcher)
327 setattr(cmdutil, '_makenofollowlogfilematcher', oldmakelogfilematcher)
328
328
329 def overrideverify(orig, ui, repo, *pats, **opts):
329 def overrideverify(orig, ui, repo, *pats, **opts):
330 large = opts.pop('large', False)
330 large = opts.pop('large', False)
331 all = opts.pop('lfa', False)
331 all = opts.pop('lfa', False)
332 contents = opts.pop('lfc', False)
332 contents = opts.pop('lfc', False)
333
333
334 result = orig(ui, repo, *pats, **opts)
334 result = orig(ui, repo, *pats, **opts)
335 if large or all or contents:
335 if large or all or contents:
336 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
336 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
337 return result
337 return result
338
338
339 def overridedebugstate(orig, ui, repo, *pats, **opts):
339 def overridedebugstate(orig, ui, repo, *pats, **opts):
340 large = opts.pop('large', False)
340 large = opts.pop('large', False)
341 if large:
341 if large:
342 class fakerepo(object):
342 class fakerepo(object):
343 dirstate = lfutil.openlfdirstate(ui, repo)
343 dirstate = lfutil.openlfdirstate(ui, repo)
344 orig(ui, fakerepo, *pats, **opts)
344 orig(ui, fakerepo, *pats, **opts)
345 else:
345 else:
346 orig(ui, repo, *pats, **opts)
346 orig(ui, repo, *pats, **opts)
347
347
348 # Override needs to refresh standins so that update's normal merge
348 # Override needs to refresh standins so that update's normal merge
349 # will go through properly. Then the other update hook (overriding repo.update)
349 # will go through properly. Then the other update hook (overriding repo.update)
350 # will get the new files. Filemerge is also overridden so that the merge
350 # will get the new files. Filemerge is also overridden so that the merge
351 # will merge standins correctly.
351 # will merge standins correctly.
352 def overrideupdate(orig, ui, repo, *pats, **opts):
352 def overrideupdate(orig, ui, repo, *pats, **opts):
353 # Need to lock between the standins getting updated and their
353 # Need to lock between the standins getting updated and their
354 # largefiles getting updated
354 # largefiles getting updated
355 wlock = repo.wlock()
355 wlock = repo.wlock()
356 try:
356 try:
357 if opts['check']:
357 if opts['check']:
358 lfdirstate = lfutil.openlfdirstate(ui, repo)
358 lfdirstate = lfutil.openlfdirstate(ui, repo)
359 unsure, s = lfdirstate.status(
359 unsure, s = lfdirstate.status(
360 match_.always(repo.root, repo.getcwd()),
360 match_.always(repo.root, repo.getcwd()),
361 [], False, False, False)
361 [], False, False, False)
362
362
363 mod = len(s.modified) > 0
363 mod = len(s.modified) > 0
364 for lfile in unsure:
364 for lfile in unsure:
365 standin = lfutil.standin(lfile)
365 standin = lfutil.standin(lfile)
366 if repo['.'][standin].data().strip() != \
366 if repo['.'][standin].data().strip() != \
367 lfutil.hashfile(repo.wjoin(lfile)):
367 lfutil.hashfile(repo.wjoin(lfile)):
368 mod = True
368 mod = True
369 else:
369 else:
370 lfdirstate.normal(lfile)
370 lfdirstate.normal(lfile)
371 lfdirstate.write()
371 lfdirstate.write()
372 if mod:
372 if mod:
373 raise util.Abort(_('uncommitted changes'))
373 raise util.Abort(_('uncommitted changes'))
374 return orig(ui, repo, *pats, **opts)
374 return orig(ui, repo, *pats, **opts)
375 finally:
375 finally:
376 wlock.release()
376 wlock.release()
377
377
378 # Before starting the manifest merge, merge.updates will call
378 # Before starting the manifest merge, merge.updates will call
379 # checkunknown to check if there are any files in the merged-in
379 # checkunknown to check if there are any files in the merged-in
380 # changeset that collide with unknown files in the working copy.
380 # changeset that collide with unknown files in the working copy.
381 #
381 #
382 # The largefiles are seen as unknown, so this prevents us from merging
382 # The largefiles are seen as unknown, so this prevents us from merging
383 # in a file 'foo' if we already have a largefile with the same name.
383 # in a file 'foo' if we already have a largefile with the same name.
384 #
384 #
385 # The overridden function filters the unknown files by removing any
385 # The overridden function filters the unknown files by removing any
386 # largefiles. This makes the merge proceed and we can then handle this
386 # largefiles. This makes the merge proceed and we can then handle this
387 # case further in the overridden calculateupdates function below.
387 # case further in the overridden calculateupdates function below.
388 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
388 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
389 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
389 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
390 return False
390 return False
391 return origfn(repo, wctx, mctx, f)
391 return origfn(repo, wctx, mctx, f)
392
392
393 # The manifest merge handles conflicts on the manifest level. We want
393 # The manifest merge handles conflicts on the manifest level. We want
394 # to handle changes in largefile-ness of files at this level too.
394 # to handle changes in largefile-ness of files at this level too.
395 #
395 #
396 # The strategy is to run the original calculateupdates and then process
396 # The strategy is to run the original calculateupdates and then process
397 # the action list it outputs. There are two cases we need to deal with:
397 # the action list it outputs. There are two cases we need to deal with:
398 #
398 #
399 # 1. Normal file in p1, largefile in p2. Here the largefile is
399 # 1. Normal file in p1, largefile in p2. Here the largefile is
400 # detected via its standin file, which will enter the working copy
400 # detected via its standin file, which will enter the working copy
401 # with a "get" action. It is not "merge" since the standin is all
401 # with a "get" action. It is not "merge" since the standin is all
402 # Mercurial is concerned with at this level -- the link to the
402 # Mercurial is concerned with at this level -- the link to the
403 # existing normal file is not relevant here.
403 # existing normal file is not relevant here.
404 #
404 #
405 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
405 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
406 # since the largefile will be present in the working copy and
406 # since the largefile will be present in the working copy and
407 # different from the normal file in p2. Mercurial therefore
407 # different from the normal file in p2. Mercurial therefore
408 # triggers a merge action.
408 # triggers a merge action.
409 #
409 #
410 # In both cases, we prompt the user and emit new actions to either
410 # In both cases, we prompt the user and emit new actions to either
411 # remove the standin (if the normal file was kept) or to remove the
411 # remove the standin (if the normal file was kept) or to remove the
412 # normal file and get the standin (if the largefile was kept). The
412 # normal file and get the standin (if the largefile was kept). The
413 # default prompt answer is to use the largefile version since it was
413 # default prompt answer is to use the largefile version since it was
414 # presumably changed on purpose.
414 # presumably changed on purpose.
415 #
415 #
416 # Finally, the merge.applyupdates function will then take care of
416 # Finally, the merge.applyupdates function will then take care of
417 # writing the files into the working copy and lfcommands.updatelfiles
417 # writing the files into the working copy and lfcommands.updatelfiles
418 # will update the largefiles.
418 # will update the largefiles.
419 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
419 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
420 partial, acceptremote, followcopies):
420 partial, acceptremote, followcopies):
421 overwrite = force and not branchmerge
421 overwrite = force and not branchmerge
422 actions, diverge, renamedelete = origfn(
422 actions, diverge, renamedelete = origfn(
423 repo, p1, p2, pas, branchmerge, force, partial, acceptremote,
423 repo, p1, p2, pas, branchmerge, force, partial, acceptremote,
424 followcopies)
424 followcopies)
425
425
426 if overwrite:
426 if overwrite:
427 return actions, diverge, renamedelete
427 return actions, diverge, renamedelete
428
428
429 # Convert to dictionary with filename as key and action as value.
429 # Convert to dictionary with filename as key and action as value.
430 lfiles = set()
430 lfiles = set()
431 actionbyfile = {}
431 actionbyfile = {}
432 for m, l in actions.iteritems():
432 for m, l in actions.iteritems():
433 for f, args, msg in l:
433 for f, args, msg in l:
434 actionbyfile[f] = m, args, msg
434 actionbyfile[f] = m, args, msg
435 splitstandin = f and lfutil.splitstandin(f)
435 splitstandin = f and lfutil.splitstandin(f)
436 if splitstandin in p1:
436 if splitstandin in p1:
437 lfiles.add(splitstandin)
437 lfiles.add(splitstandin)
438 elif lfutil.standin(f) in p1:
438 elif lfutil.standin(f) in p1:
439 lfiles.add(f)
439 lfiles.add(f)
440
440
441 for lfile in lfiles:
441 for lfile in lfiles:
442 standin = lfutil.standin(lfile)
442 standin = lfutil.standin(lfile)
443 lm = actionbyfile.get(lfile, (None, None, None))[0]
443 (lm, largs, lmsg) = actionbyfile.get(lfile, (None, None, None))
444 sm = actionbyfile.get(standin, (None, None, None))[0]
444 (sm, sargs, smsg) = actionbyfile.get(standin, (None, None, None))
445 if sm == 'g' and lm != 'r':
445 if sm in ('g', 'dc') and lm != 'r':
446 # Case 1: normal file in the working copy, largefile in
446 # Case 1: normal file in the working copy, largefile in
447 # the second parent
447 # the second parent
448 usermsg = _('remote turned local normal file %s into a largefile\n'
448 usermsg = _('remote turned local normal file %s into a largefile\n'
449 'use (l)argefile or keep (n)ormal file?'
449 'use (l)argefile or keep (n)ormal file?'
450 '$$ &Largefile $$ &Normal file') % lfile
450 '$$ &Largefile $$ &Normal file') % lfile
451 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
451 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
452 actionbyfile[lfile] = ('r', None, 'replaced by standin')
452 actionbyfile[lfile] = ('r', None, 'replaced by standin')
453 actionbyfile[standin] = ('g', sargs, 'replaces standin')
453 else: # keep local normal file
454 else: # keep local normal file
455 actionbyfile[lfile] = ('k', None, 'replaces standin')
454 if branchmerge:
456 if branchmerge:
455 actionbyfile[standin] = ('k', None,
457 actionbyfile[standin] = ('k', None,
456 'replaced by non-standin')
458 'replaced by non-standin')
457 else:
459 else:
458 actionbyfile[standin] = ('r', None,
460 actionbyfile[standin] = ('r', None,
459 'replaced by non-standin')
461 'replaced by non-standin')
460 elif lm == 'g' and sm != 'r':
462 elif lm in ('g', 'dc') and sm != 'r':
461 # Case 2: largefile in the working copy, normal file in
463 # Case 2: largefile in the working copy, normal file in
462 # the second parent
464 # the second parent
463 usermsg = _('remote turned local largefile %s into a normal file\n'
465 usermsg = _('remote turned local largefile %s into a normal file\n'
464 'keep (l)argefile or use (n)ormal file?'
466 'keep (l)argefile or use (n)ormal file?'
465 '$$ &Largefile $$ &Normal file') % lfile
467 '$$ &Largefile $$ &Normal file') % lfile
466 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
468 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
467 if branchmerge:
469 if branchmerge:
468 # largefile can be restored from standin safely
470 # largefile can be restored from standin safely
469 actionbyfile[lfile] = ('k', None, 'replaced by standin')
471 actionbyfile[lfile] = ('k', None, 'replaced by standin')
472 actionbyfile[standin] = ('k', None, 'replaces standin')
470 else:
473 else:
471 # "lfile" should be marked as "removed" without
474 # "lfile" should be marked as "removed" without
472 # removal of itself
475 # removal of itself
473 actionbyfile[lfile] = ('lfmr', None,
476 actionbyfile[lfile] = ('lfmr', None,
474 'forget non-standin largefile')
477 'forget non-standin largefile')
475
478
476 # linear-merge should treat this largefile as 're-added'
479 # linear-merge should treat this largefile as 're-added'
477 actionbyfile[standin] = ('a', None, 'keep standin')
480 actionbyfile[standin] = ('a', None, 'keep standin')
478 else: # pick remote normal file
481 else: # pick remote normal file
482 actionbyfile[lfile] = ('g', largs, 'replaces standin')
479 actionbyfile[standin] = ('r', None, 'replaced by non-standin')
483 actionbyfile[standin] = ('r', None, 'replaced by non-standin')
480
484
481 # Convert back to dictionary-of-lists format
485 # Convert back to dictionary-of-lists format
482 for l in actions.itervalues():
486 for l in actions.itervalues():
483 l[:] = []
487 l[:] = []
484 actions['lfmr'] = []
488 actions['lfmr'] = []
485 for f, (m, args, msg) in actionbyfile.iteritems():
489 for f, (m, args, msg) in actionbyfile.iteritems():
486 actions[m].append((f, args, msg))
490 actions[m].append((f, args, msg))
487
491
488 return actions, diverge, renamedelete
492 return actions, diverge, renamedelete
489
493
490 def mergerecordupdates(orig, repo, actions, branchmerge):
494 def mergerecordupdates(orig, repo, actions, branchmerge):
491 if 'lfmr' in actions:
495 if 'lfmr' in actions:
492 # this should be executed before 'orig', to execute 'remove'
496 # this should be executed before 'orig', to execute 'remove'
493 # before all other actions
497 # before all other actions
494 for lfile, args, msg in actions['lfmr']:
498 for lfile, args, msg in actions['lfmr']:
495 repo.dirstate.remove(lfile)
499 repo.dirstate.remove(lfile)
496
500
497 return orig(repo, actions, branchmerge)
501 return orig(repo, actions, branchmerge)
498
502
499
503
500 # Override filemerge to prompt the user about how they wish to merge
504 # Override filemerge to prompt the user about how they wish to merge
501 # largefiles. This will handle identical edits without prompting the user.
505 # largefiles. This will handle identical edits without prompting the user.
502 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca, labels=None):
506 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca, labels=None):
503 if not lfutil.isstandin(orig):
507 if not lfutil.isstandin(orig):
504 return origfn(repo, mynode, orig, fcd, fco, fca, labels=labels)
508 return origfn(repo, mynode, orig, fcd, fco, fca, labels=labels)
505
509
506 ahash = fca.data().strip().lower()
510 ahash = fca.data().strip().lower()
507 dhash = fcd.data().strip().lower()
511 dhash = fcd.data().strip().lower()
508 ohash = fco.data().strip().lower()
512 ohash = fco.data().strip().lower()
509 if (ohash != ahash and
513 if (ohash != ahash and
510 ohash != dhash and
514 ohash != dhash and
511 (dhash == ahash or
515 (dhash == ahash or
512 repo.ui.promptchoice(
516 repo.ui.promptchoice(
513 _('largefile %s has a merge conflict\nancestor was %s\n'
517 _('largefile %s has a merge conflict\nancestor was %s\n'
514 'keep (l)ocal %s or\ntake (o)ther %s?'
518 'keep (l)ocal %s or\ntake (o)ther %s?'
515 '$$ &Local $$ &Other') %
519 '$$ &Local $$ &Other') %
516 (lfutil.splitstandin(orig), ahash, dhash, ohash),
520 (lfutil.splitstandin(orig), ahash, dhash, ohash),
517 0) == 1)):
521 0) == 1)):
518 repo.wwrite(fcd.path(), fco.data(), fco.flags())
522 repo.wwrite(fcd.path(), fco.data(), fco.flags())
519 return 0
523 return 0
520
524
521 # Copy first changes the matchers to match standins instead of
525 # Copy first changes the matchers to match standins instead of
522 # largefiles. Then it overrides util.copyfile in that function it
526 # largefiles. Then it overrides util.copyfile in that function it
523 # checks if the destination largefile already exists. It also keeps a
527 # checks if the destination largefile already exists. It also keeps a
524 # list of copied files so that the largefiles can be copied and the
528 # list of copied files so that the largefiles can be copied and the
525 # dirstate updated.
529 # dirstate updated.
526 def overridecopy(orig, ui, repo, pats, opts, rename=False):
530 def overridecopy(orig, ui, repo, pats, opts, rename=False):
527 # doesn't remove largefile on rename
531 # doesn't remove largefile on rename
528 if len(pats) < 2:
532 if len(pats) < 2:
529 # this isn't legal, let the original function deal with it
533 # this isn't legal, let the original function deal with it
530 return orig(ui, repo, pats, opts, rename)
534 return orig(ui, repo, pats, opts, rename)
531
535
532 def makestandin(relpath):
536 def makestandin(relpath):
533 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
537 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
534 return os.path.join(repo.wjoin(lfutil.standin(path)))
538 return os.path.join(repo.wjoin(lfutil.standin(path)))
535
539
536 fullpats = scmutil.expandpats(pats)
540 fullpats = scmutil.expandpats(pats)
537 dest = fullpats[-1]
541 dest = fullpats[-1]
538
542
539 if os.path.isdir(dest):
543 if os.path.isdir(dest):
540 if not os.path.isdir(makestandin(dest)):
544 if not os.path.isdir(makestandin(dest)):
541 os.makedirs(makestandin(dest))
545 os.makedirs(makestandin(dest))
542 # This could copy both lfiles and normal files in one command,
546 # This could copy both lfiles and normal files in one command,
543 # but we don't want to do that. First replace their matcher to
547 # but we don't want to do that. First replace their matcher to
544 # only match normal files and run it, then replace it to just
548 # only match normal files and run it, then replace it to just
545 # match largefiles and run it again.
549 # match largefiles and run it again.
546 nonormalfiles = False
550 nonormalfiles = False
547 nolfiles = False
551 nolfiles = False
548 installnormalfilesmatchfn(repo[None].manifest())
552 installnormalfilesmatchfn(repo[None].manifest())
549 try:
553 try:
550 try:
554 try:
551 result = orig(ui, repo, pats, opts, rename)
555 result = orig(ui, repo, pats, opts, rename)
552 except util.Abort, e:
556 except util.Abort, e:
553 if str(e) != _('no files to copy'):
557 if str(e) != _('no files to copy'):
554 raise e
558 raise e
555 else:
559 else:
556 nonormalfiles = True
560 nonormalfiles = True
557 result = 0
561 result = 0
558 finally:
562 finally:
559 restorematchfn()
563 restorematchfn()
560
564
561 # The first rename can cause our current working directory to be removed.
565 # The first rename can cause our current working directory to be removed.
562 # In that case there is nothing left to copy/rename so just quit.
566 # In that case there is nothing left to copy/rename so just quit.
563 try:
567 try:
564 repo.getcwd()
568 repo.getcwd()
565 except OSError:
569 except OSError:
566 return result
570 return result
567
571
568 try:
572 try:
569 try:
573 try:
570 # When we call orig below it creates the standins but we don't add
574 # When we call orig below it creates the standins but we don't add
571 # them to the dir state until later so lock during that time.
575 # them to the dir state until later so lock during that time.
572 wlock = repo.wlock()
576 wlock = repo.wlock()
573
577
574 manifest = repo[None].manifest()
578 manifest = repo[None].manifest()
575 def overridematch(ctx, pats=[], opts={}, globbed=False,
579 def overridematch(ctx, pats=[], opts={}, globbed=False,
576 default='relpath'):
580 default='relpath'):
577 newpats = []
581 newpats = []
578 # The patterns were previously mangled to add the standin
582 # The patterns were previously mangled to add the standin
579 # directory; we need to remove that now
583 # directory; we need to remove that now
580 for pat in pats:
584 for pat in pats:
581 if match_.patkind(pat) is None and lfutil.shortname in pat:
585 if match_.patkind(pat) is None and lfutil.shortname in pat:
582 newpats.append(pat.replace(lfutil.shortname, ''))
586 newpats.append(pat.replace(lfutil.shortname, ''))
583 else:
587 else:
584 newpats.append(pat)
588 newpats.append(pat)
585 match = oldmatch(ctx, newpats, opts, globbed, default)
589 match = oldmatch(ctx, newpats, opts, globbed, default)
586 m = copy.copy(match)
590 m = copy.copy(match)
587 lfile = lambda f: lfutil.standin(f) in manifest
591 lfile = lambda f: lfutil.standin(f) in manifest
588 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
592 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
589 m._fmap = set(m._files)
593 m._fmap = set(m._files)
590 origmatchfn = m.matchfn
594 origmatchfn = m.matchfn
591 m.matchfn = lambda f: (lfutil.isstandin(f) and
595 m.matchfn = lambda f: (lfutil.isstandin(f) and
592 (f in manifest) and
596 (f in manifest) and
593 origmatchfn(lfutil.splitstandin(f)) or
597 origmatchfn(lfutil.splitstandin(f)) or
594 None)
598 None)
595 return m
599 return m
596 oldmatch = installmatchfn(overridematch)
600 oldmatch = installmatchfn(overridematch)
597 listpats = []
601 listpats = []
598 for pat in pats:
602 for pat in pats:
599 if match_.patkind(pat) is not None:
603 if match_.patkind(pat) is not None:
600 listpats.append(pat)
604 listpats.append(pat)
601 else:
605 else:
602 listpats.append(makestandin(pat))
606 listpats.append(makestandin(pat))
603
607
604 try:
608 try:
605 origcopyfile = util.copyfile
609 origcopyfile = util.copyfile
606 copiedfiles = []
610 copiedfiles = []
607 def overridecopyfile(src, dest):
611 def overridecopyfile(src, dest):
608 if (lfutil.shortname in src and
612 if (lfutil.shortname in src and
609 dest.startswith(repo.wjoin(lfutil.shortname))):
613 dest.startswith(repo.wjoin(lfutil.shortname))):
610 destlfile = dest.replace(lfutil.shortname, '')
614 destlfile = dest.replace(lfutil.shortname, '')
611 if not opts['force'] and os.path.exists(destlfile):
615 if not opts['force'] and os.path.exists(destlfile):
612 raise IOError('',
616 raise IOError('',
613 _('destination largefile already exists'))
617 _('destination largefile already exists'))
614 copiedfiles.append((src, dest))
618 copiedfiles.append((src, dest))
615 origcopyfile(src, dest)
619 origcopyfile(src, dest)
616
620
617 util.copyfile = overridecopyfile
621 util.copyfile = overridecopyfile
618 result += orig(ui, repo, listpats, opts, rename)
622 result += orig(ui, repo, listpats, opts, rename)
619 finally:
623 finally:
620 util.copyfile = origcopyfile
624 util.copyfile = origcopyfile
621
625
622 lfdirstate = lfutil.openlfdirstate(ui, repo)
626 lfdirstate = lfutil.openlfdirstate(ui, repo)
623 for (src, dest) in copiedfiles:
627 for (src, dest) in copiedfiles:
624 if (lfutil.shortname in src and
628 if (lfutil.shortname in src and
625 dest.startswith(repo.wjoin(lfutil.shortname))):
629 dest.startswith(repo.wjoin(lfutil.shortname))):
626 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
630 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
627 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
631 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
628 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
632 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
629 if not os.path.isdir(destlfiledir):
633 if not os.path.isdir(destlfiledir):
630 os.makedirs(destlfiledir)
634 os.makedirs(destlfiledir)
631 if rename:
635 if rename:
632 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
636 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
633
637
634 # The file is gone, but this deletes any empty parent
638 # The file is gone, but this deletes any empty parent
635 # directories as a side-effect.
639 # directories as a side-effect.
636 util.unlinkpath(repo.wjoin(srclfile), True)
640 util.unlinkpath(repo.wjoin(srclfile), True)
637 lfdirstate.remove(srclfile)
641 lfdirstate.remove(srclfile)
638 else:
642 else:
639 util.copyfile(repo.wjoin(srclfile),
643 util.copyfile(repo.wjoin(srclfile),
640 repo.wjoin(destlfile))
644 repo.wjoin(destlfile))
641
645
642 lfdirstate.add(destlfile)
646 lfdirstate.add(destlfile)
643 lfdirstate.write()
647 lfdirstate.write()
644 except util.Abort, e:
648 except util.Abort, e:
645 if str(e) != _('no files to copy'):
649 if str(e) != _('no files to copy'):
646 raise e
650 raise e
647 else:
651 else:
648 nolfiles = True
652 nolfiles = True
649 finally:
653 finally:
650 restorematchfn()
654 restorematchfn()
651 wlock.release()
655 wlock.release()
652
656
653 if nolfiles and nonormalfiles:
657 if nolfiles and nonormalfiles:
654 raise util.Abort(_('no files to copy'))
658 raise util.Abort(_('no files to copy'))
655
659
656 return result
660 return result
657
661
658 # When the user calls revert, we have to be careful to not revert any
662 # When the user calls revert, we have to be careful to not revert any
659 # changes to other largefiles accidentally. This means we have to keep
663 # changes to other largefiles accidentally. This means we have to keep
660 # track of the largefiles that are being reverted so we only pull down
664 # track of the largefiles that are being reverted so we only pull down
661 # the necessary largefiles.
665 # the necessary largefiles.
662 #
666 #
663 # Standins are only updated (to match the hash of largefiles) before
667 # Standins are only updated (to match the hash of largefiles) before
664 # commits. Update the standins then run the original revert, changing
668 # commits. Update the standins then run the original revert, changing
665 # the matcher to hit standins instead of largefiles. Based on the
669 # the matcher to hit standins instead of largefiles. Based on the
666 # resulting standins update the largefiles.
670 # resulting standins update the largefiles.
667 def overriderevert(orig, ui, repo, *pats, **opts):
671 def overriderevert(orig, ui, repo, *pats, **opts):
668 # Because we put the standins in a bad state (by updating them)
672 # Because we put the standins in a bad state (by updating them)
669 # and then return them to a correct state we need to lock to
673 # and then return them to a correct state we need to lock to
670 # prevent others from changing them in their incorrect state.
674 # prevent others from changing them in their incorrect state.
671 wlock = repo.wlock()
675 wlock = repo.wlock()
672 try:
676 try:
673 lfdirstate = lfutil.openlfdirstate(ui, repo)
677 lfdirstate = lfutil.openlfdirstate(ui, repo)
674 s = lfutil.lfdirstatestatus(lfdirstate, repo)
678 s = lfutil.lfdirstatestatus(lfdirstate, repo)
675 lfdirstate.write()
679 lfdirstate.write()
676 for lfile in s.modified:
680 for lfile in s.modified:
677 lfutil.updatestandin(repo, lfutil.standin(lfile))
681 lfutil.updatestandin(repo, lfutil.standin(lfile))
678 for lfile in s.deleted:
682 for lfile in s.deleted:
679 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
683 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
680 os.unlink(repo.wjoin(lfutil.standin(lfile)))
684 os.unlink(repo.wjoin(lfutil.standin(lfile)))
681
685
682 oldstandins = lfutil.getstandinsstate(repo)
686 oldstandins = lfutil.getstandinsstate(repo)
683
687
684 def overridematch(ctx, pats=[], opts={}, globbed=False,
688 def overridematch(ctx, pats=[], opts={}, globbed=False,
685 default='relpath'):
689 default='relpath'):
686 match = oldmatch(ctx, pats, opts, globbed, default)
690 match = oldmatch(ctx, pats, opts, globbed, default)
687 m = copy.copy(match)
691 m = copy.copy(match)
688 def tostandin(f):
692 def tostandin(f):
689 if lfutil.standin(f) in ctx:
693 if lfutil.standin(f) in ctx:
690 return lfutil.standin(f)
694 return lfutil.standin(f)
691 elif lfutil.standin(f) in repo[None]:
695 elif lfutil.standin(f) in repo[None]:
692 return None
696 return None
693 return f
697 return f
694 m._files = [tostandin(f) for f in m._files]
698 m._files = [tostandin(f) for f in m._files]
695 m._files = [f for f in m._files if f is not None]
699 m._files = [f for f in m._files if f is not None]
696 m._fmap = set(m._files)
700 m._fmap = set(m._files)
697 origmatchfn = m.matchfn
701 origmatchfn = m.matchfn
698 def matchfn(f):
702 def matchfn(f):
699 if lfutil.isstandin(f):
703 if lfutil.isstandin(f):
700 return (origmatchfn(lfutil.splitstandin(f)) and
704 return (origmatchfn(lfutil.splitstandin(f)) and
701 (f in repo[None] or f in ctx))
705 (f in repo[None] or f in ctx))
702 return origmatchfn(f)
706 return origmatchfn(f)
703 m.matchfn = matchfn
707 m.matchfn = matchfn
704 return m
708 return m
705 oldmatch = installmatchfn(overridematch)
709 oldmatch = installmatchfn(overridematch)
706 try:
710 try:
707 orig(ui, repo, *pats, **opts)
711 orig(ui, repo, *pats, **opts)
708 finally:
712 finally:
709 restorematchfn()
713 restorematchfn()
710
714
711 newstandins = lfutil.getstandinsstate(repo)
715 newstandins = lfutil.getstandinsstate(repo)
712 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
716 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
713 # lfdirstate should be 'normallookup'-ed for updated files,
717 # lfdirstate should be 'normallookup'-ed for updated files,
714 # because reverting doesn't touch dirstate for 'normal' files
718 # because reverting doesn't touch dirstate for 'normal' files
715 # when target revision is explicitly specified: in such case,
719 # when target revision is explicitly specified: in such case,
716 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
720 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
717 # of target (standin) file.
721 # of target (standin) file.
718 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
722 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
719 normallookup=True)
723 normallookup=True)
720
724
721 finally:
725 finally:
722 wlock.release()
726 wlock.release()
723
727
724 # after pulling changesets, we need to take some extra care to get
728 # after pulling changesets, we need to take some extra care to get
725 # largefiles updated remotely
729 # largefiles updated remotely
726 def overridepull(orig, ui, repo, source=None, **opts):
730 def overridepull(orig, ui, repo, source=None, **opts):
727 revsprepull = len(repo)
731 revsprepull = len(repo)
728 if not source:
732 if not source:
729 source = 'default'
733 source = 'default'
730 repo.lfpullsource = source
734 repo.lfpullsource = source
731 result = orig(ui, repo, source, **opts)
735 result = orig(ui, repo, source, **opts)
732 revspostpull = len(repo)
736 revspostpull = len(repo)
733 lfrevs = opts.get('lfrev', [])
737 lfrevs = opts.get('lfrev', [])
734 if opts.get('all_largefiles'):
738 if opts.get('all_largefiles'):
735 lfrevs.append('pulled()')
739 lfrevs.append('pulled()')
736 if lfrevs and revspostpull > revsprepull:
740 if lfrevs and revspostpull > revsprepull:
737 numcached = 0
741 numcached = 0
738 repo.firstpulled = revsprepull # for pulled() revset expression
742 repo.firstpulled = revsprepull # for pulled() revset expression
739 try:
743 try:
740 for rev in scmutil.revrange(repo, lfrevs):
744 for rev in scmutil.revrange(repo, lfrevs):
741 ui.note(_('pulling largefiles for revision %s\n') % rev)
745 ui.note(_('pulling largefiles for revision %s\n') % rev)
742 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
746 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
743 numcached += len(cached)
747 numcached += len(cached)
744 finally:
748 finally:
745 del repo.firstpulled
749 del repo.firstpulled
746 ui.status(_("%d largefiles cached\n") % numcached)
750 ui.status(_("%d largefiles cached\n") % numcached)
747 return result
751 return result
748
752
749 def pulledrevsetsymbol(repo, subset, x):
753 def pulledrevsetsymbol(repo, subset, x):
750 """``pulled()``
754 """``pulled()``
751 Changesets that just has been pulled.
755 Changesets that just has been pulled.
752
756
753 Only available with largefiles from pull --lfrev expressions.
757 Only available with largefiles from pull --lfrev expressions.
754
758
755 .. container:: verbose
759 .. container:: verbose
756
760
757 Some examples:
761 Some examples:
758
762
759 - pull largefiles for all new changesets::
763 - pull largefiles for all new changesets::
760
764
761 hg pull -lfrev "pulled()"
765 hg pull -lfrev "pulled()"
762
766
763 - pull largefiles for all new branch heads::
767 - pull largefiles for all new branch heads::
764
768
765 hg pull -lfrev "head(pulled()) and not closed()"
769 hg pull -lfrev "head(pulled()) and not closed()"
766
770
767 """
771 """
768
772
769 try:
773 try:
770 firstpulled = repo.firstpulled
774 firstpulled = repo.firstpulled
771 except AttributeError:
775 except AttributeError:
772 raise util.Abort(_("pulled() only available in --lfrev"))
776 raise util.Abort(_("pulled() only available in --lfrev"))
773 return revset.baseset([r for r in subset if r >= firstpulled])
777 return revset.baseset([r for r in subset if r >= firstpulled])
774
778
775 def overrideclone(orig, ui, source, dest=None, **opts):
779 def overrideclone(orig, ui, source, dest=None, **opts):
776 d = dest
780 d = dest
777 if d is None:
781 if d is None:
778 d = hg.defaultdest(source)
782 d = hg.defaultdest(source)
779 if opts.get('all_largefiles') and not hg.islocal(d):
783 if opts.get('all_largefiles') and not hg.islocal(d):
780 raise util.Abort(_(
784 raise util.Abort(_(
781 '--all-largefiles is incompatible with non-local destination %s') %
785 '--all-largefiles is incompatible with non-local destination %s') %
782 d)
786 d)
783
787
784 return orig(ui, source, dest, **opts)
788 return orig(ui, source, dest, **opts)
785
789
786 def hgclone(orig, ui, opts, *args, **kwargs):
790 def hgclone(orig, ui, opts, *args, **kwargs):
787 result = orig(ui, opts, *args, **kwargs)
791 result = orig(ui, opts, *args, **kwargs)
788
792
789 if result is not None:
793 if result is not None:
790 sourcerepo, destrepo = result
794 sourcerepo, destrepo = result
791 repo = destrepo.local()
795 repo = destrepo.local()
792
796
793 # Caching is implicitly limited to 'rev' option, since the dest repo was
797 # Caching is implicitly limited to 'rev' option, since the dest repo was
794 # truncated at that point. The user may expect a download count with
798 # truncated at that point. The user may expect a download count with
795 # this option, so attempt whether or not this is a largefile repo.
799 # this option, so attempt whether or not this is a largefile repo.
796 if opts.get('all_largefiles'):
800 if opts.get('all_largefiles'):
797 success, missing = lfcommands.downloadlfiles(ui, repo, None)
801 success, missing = lfcommands.downloadlfiles(ui, repo, None)
798
802
799 if missing != 0:
803 if missing != 0:
800 return None
804 return None
801
805
802 return result
806 return result
803
807
804 def overriderebase(orig, ui, repo, **opts):
808 def overriderebase(orig, ui, repo, **opts):
805 resuming = opts.get('continue')
809 resuming = opts.get('continue')
806 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
810 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
807 repo._lfstatuswriters.append(lambda *msg, **opts: None)
811 repo._lfstatuswriters.append(lambda *msg, **opts: None)
808 try:
812 try:
809 return orig(ui, repo, **opts)
813 return orig(ui, repo, **opts)
810 finally:
814 finally:
811 repo._lfstatuswriters.pop()
815 repo._lfstatuswriters.pop()
812 repo._lfcommithooks.pop()
816 repo._lfcommithooks.pop()
813
817
814 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
818 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
815 prefix=None, mtime=None, subrepos=None):
819 prefix=None, mtime=None, subrepos=None):
816 # No need to lock because we are only reading history and
820 # No need to lock because we are only reading history and
817 # largefile caches, neither of which are modified.
821 # largefile caches, neither of which are modified.
818 lfcommands.cachelfiles(repo.ui, repo, node)
822 lfcommands.cachelfiles(repo.ui, repo, node)
819
823
820 if kind not in archival.archivers:
824 if kind not in archival.archivers:
821 raise util.Abort(_("unknown archive type '%s'") % kind)
825 raise util.Abort(_("unknown archive type '%s'") % kind)
822
826
823 ctx = repo[node]
827 ctx = repo[node]
824
828
825 if kind == 'files':
829 if kind == 'files':
826 if prefix:
830 if prefix:
827 raise util.Abort(
831 raise util.Abort(
828 _('cannot give prefix when archiving to files'))
832 _('cannot give prefix when archiving to files'))
829 else:
833 else:
830 prefix = archival.tidyprefix(dest, kind, prefix)
834 prefix = archival.tidyprefix(dest, kind, prefix)
831
835
832 def write(name, mode, islink, getdata):
836 def write(name, mode, islink, getdata):
833 if matchfn and not matchfn(name):
837 if matchfn and not matchfn(name):
834 return
838 return
835 data = getdata()
839 data = getdata()
836 if decode:
840 if decode:
837 data = repo.wwritedata(name, data)
841 data = repo.wwritedata(name, data)
838 archiver.addfile(prefix + name, mode, islink, data)
842 archiver.addfile(prefix + name, mode, islink, data)
839
843
840 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
844 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
841
845
842 if repo.ui.configbool("ui", "archivemeta", True):
846 if repo.ui.configbool("ui", "archivemeta", True):
843 def metadata():
847 def metadata():
844 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
848 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
845 hex(repo.changelog.node(0)), hex(node), ctx.branch())
849 hex(repo.changelog.node(0)), hex(node), ctx.branch())
846
850
847 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
851 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
848 if repo.tagtype(t) == 'global')
852 if repo.tagtype(t) == 'global')
849 if not tags:
853 if not tags:
850 repo.ui.pushbuffer()
854 repo.ui.pushbuffer()
851 opts = {'template': '{latesttag}\n{latesttagdistance}',
855 opts = {'template': '{latesttag}\n{latesttagdistance}',
852 'style': '', 'patch': None, 'git': None}
856 'style': '', 'patch': None, 'git': None}
853 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
857 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
854 ltags, dist = repo.ui.popbuffer().split('\n')
858 ltags, dist = repo.ui.popbuffer().split('\n')
855 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
859 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
856 tags += 'latesttagdistance: %s\n' % dist
860 tags += 'latesttagdistance: %s\n' % dist
857
861
858 return base + tags
862 return base + tags
859
863
860 write('.hg_archival.txt', 0644, False, metadata)
864 write('.hg_archival.txt', 0644, False, metadata)
861
865
862 for f in ctx:
866 for f in ctx:
863 ff = ctx.flags(f)
867 ff = ctx.flags(f)
864 getdata = ctx[f].data
868 getdata = ctx[f].data
865 if lfutil.isstandin(f):
869 if lfutil.isstandin(f):
866 path = lfutil.findfile(repo, getdata().strip())
870 path = lfutil.findfile(repo, getdata().strip())
867 if path is None:
871 if path is None:
868 raise util.Abort(
872 raise util.Abort(
869 _('largefile %s not found in repo store or system cache')
873 _('largefile %s not found in repo store or system cache')
870 % lfutil.splitstandin(f))
874 % lfutil.splitstandin(f))
871 f = lfutil.splitstandin(f)
875 f = lfutil.splitstandin(f)
872
876
873 def getdatafn():
877 def getdatafn():
874 fd = None
878 fd = None
875 try:
879 try:
876 fd = open(path, 'rb')
880 fd = open(path, 'rb')
877 return fd.read()
881 return fd.read()
878 finally:
882 finally:
879 if fd:
883 if fd:
880 fd.close()
884 fd.close()
881
885
882 getdata = getdatafn
886 getdata = getdatafn
883 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
887 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
884
888
885 if subrepos:
889 if subrepos:
886 for subpath in sorted(ctx.substate):
890 for subpath in sorted(ctx.substate):
887 sub = ctx.sub(subpath)
891 sub = ctx.sub(subpath)
888 submatch = match_.narrowmatcher(subpath, matchfn)
892 submatch = match_.narrowmatcher(subpath, matchfn)
889 sub.archive(repo.ui, archiver, prefix, submatch)
893 sub.archive(repo.ui, archiver, prefix, submatch)
890
894
891 archiver.done()
895 archiver.done()
892
896
893 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
897 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
894 repo._get(repo._state + ('hg',))
898 repo._get(repo._state + ('hg',))
895 rev = repo._state[1]
899 rev = repo._state[1]
896 ctx = repo._repo[rev]
900 ctx = repo._repo[rev]
897
901
898 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
902 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
899
903
900 def write(name, mode, islink, getdata):
904 def write(name, mode, islink, getdata):
901 # At this point, the standin has been replaced with the largefile name,
905 # At this point, the standin has been replaced with the largefile name,
902 # so the normal matcher works here without the lfutil variants.
906 # so the normal matcher works here without the lfutil variants.
903 if match and not match(f):
907 if match and not match(f):
904 return
908 return
905 data = getdata()
909 data = getdata()
906
910
907 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
911 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
908
912
909 for f in ctx:
913 for f in ctx:
910 ff = ctx.flags(f)
914 ff = ctx.flags(f)
911 getdata = ctx[f].data
915 getdata = ctx[f].data
912 if lfutil.isstandin(f):
916 if lfutil.isstandin(f):
913 path = lfutil.findfile(repo._repo, getdata().strip())
917 path = lfutil.findfile(repo._repo, getdata().strip())
914 if path is None:
918 if path is None:
915 raise util.Abort(
919 raise util.Abort(
916 _('largefile %s not found in repo store or system cache')
920 _('largefile %s not found in repo store or system cache')
917 % lfutil.splitstandin(f))
921 % lfutil.splitstandin(f))
918 f = lfutil.splitstandin(f)
922 f = lfutil.splitstandin(f)
919
923
920 def getdatafn():
924 def getdatafn():
921 fd = None
925 fd = None
922 try:
926 try:
923 fd = open(os.path.join(prefix, path), 'rb')
927 fd = open(os.path.join(prefix, path), 'rb')
924 return fd.read()
928 return fd.read()
925 finally:
929 finally:
926 if fd:
930 if fd:
927 fd.close()
931 fd.close()
928
932
929 getdata = getdatafn
933 getdata = getdatafn
930
934
931 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
935 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
932
936
933 for subpath in sorted(ctx.substate):
937 for subpath in sorted(ctx.substate):
934 sub = ctx.sub(subpath)
938 sub = ctx.sub(subpath)
935 submatch = match_.narrowmatcher(subpath, match)
939 submatch = match_.narrowmatcher(subpath, match)
936 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
940 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
937 submatch)
941 submatch)
938
942
939 # If a largefile is modified, the change is not reflected in its
943 # If a largefile is modified, the change is not reflected in its
940 # standin until a commit. cmdutil.bailifchanged() raises an exception
944 # standin until a commit. cmdutil.bailifchanged() raises an exception
941 # if the repo has uncommitted changes. Wrap it to also check if
945 # if the repo has uncommitted changes. Wrap it to also check if
942 # largefiles were changed. This is used by bisect, backout and fetch.
946 # largefiles were changed. This is used by bisect, backout and fetch.
943 def overridebailifchanged(orig, repo):
947 def overridebailifchanged(orig, repo):
944 orig(repo)
948 orig(repo)
945 repo.lfstatus = True
949 repo.lfstatus = True
946 s = repo.status()
950 s = repo.status()
947 repo.lfstatus = False
951 repo.lfstatus = False
948 if s.modified or s.added or s.removed or s.deleted:
952 if s.modified or s.added or s.removed or s.deleted:
949 raise util.Abort(_('uncommitted changes'))
953 raise util.Abort(_('uncommitted changes'))
950
954
951 def overrideforget(orig, ui, repo, *pats, **opts):
955 def overrideforget(orig, ui, repo, *pats, **opts):
952 installnormalfilesmatchfn(repo[None].manifest())
956 installnormalfilesmatchfn(repo[None].manifest())
953 result = orig(ui, repo, *pats, **opts)
957 result = orig(ui, repo, *pats, **opts)
954 restorematchfn()
958 restorematchfn()
955 m = scmutil.match(repo[None], pats, opts)
959 m = scmutil.match(repo[None], pats, opts)
956
960
957 try:
961 try:
958 repo.lfstatus = True
962 repo.lfstatus = True
959 s = repo.status(match=m, clean=True)
963 s = repo.status(match=m, clean=True)
960 finally:
964 finally:
961 repo.lfstatus = False
965 repo.lfstatus = False
962 forget = sorted(s.modified + s.added + s.deleted + s.clean)
966 forget = sorted(s.modified + s.added + s.deleted + s.clean)
963 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
967 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
964
968
965 for f in forget:
969 for f in forget:
966 if lfutil.standin(f) not in repo.dirstate and not \
970 if lfutil.standin(f) not in repo.dirstate and not \
967 os.path.isdir(m.rel(lfutil.standin(f))):
971 os.path.isdir(m.rel(lfutil.standin(f))):
968 ui.warn(_('not removing %s: file is already untracked\n')
972 ui.warn(_('not removing %s: file is already untracked\n')
969 % m.rel(f))
973 % m.rel(f))
970 result = 1
974 result = 1
971
975
972 for f in forget:
976 for f in forget:
973 if ui.verbose or not m.exact(f):
977 if ui.verbose or not m.exact(f):
974 ui.status(_('removing %s\n') % m.rel(f))
978 ui.status(_('removing %s\n') % m.rel(f))
975
979
976 # Need to lock because standin files are deleted then removed from the
980 # Need to lock because standin files are deleted then removed from the
977 # repository and we could race in-between.
981 # repository and we could race in-between.
978 wlock = repo.wlock()
982 wlock = repo.wlock()
979 try:
983 try:
980 lfdirstate = lfutil.openlfdirstate(ui, repo)
984 lfdirstate = lfutil.openlfdirstate(ui, repo)
981 for f in forget:
985 for f in forget:
982 if lfdirstate[f] == 'a':
986 if lfdirstate[f] == 'a':
983 lfdirstate.drop(f)
987 lfdirstate.drop(f)
984 else:
988 else:
985 lfdirstate.remove(f)
989 lfdirstate.remove(f)
986 lfdirstate.write()
990 lfdirstate.write()
987 standins = [lfutil.standin(f) for f in forget]
991 standins = [lfutil.standin(f) for f in forget]
988 for f in standins:
992 for f in standins:
989 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
993 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
990 repo[None].forget(standins)
994 repo[None].forget(standins)
991 finally:
995 finally:
992 wlock.release()
996 wlock.release()
993
997
994 return result
998 return result
995
999
996 def _getoutgoings(repo, other, missing, addfunc):
1000 def _getoutgoings(repo, other, missing, addfunc):
997 """get pairs of filename and largefile hash in outgoing revisions
1001 """get pairs of filename and largefile hash in outgoing revisions
998 in 'missing'.
1002 in 'missing'.
999
1003
1000 largefiles already existing on 'other' repository are ignored.
1004 largefiles already existing on 'other' repository are ignored.
1001
1005
1002 'addfunc' is invoked with each unique pairs of filename and
1006 'addfunc' is invoked with each unique pairs of filename and
1003 largefile hash value.
1007 largefile hash value.
1004 """
1008 """
1005 knowns = set()
1009 knowns = set()
1006 lfhashes = set()
1010 lfhashes = set()
1007 def dedup(fn, lfhash):
1011 def dedup(fn, lfhash):
1008 k = (fn, lfhash)
1012 k = (fn, lfhash)
1009 if k not in knowns:
1013 if k not in knowns:
1010 knowns.add(k)
1014 knowns.add(k)
1011 lfhashes.add(lfhash)
1015 lfhashes.add(lfhash)
1012 lfutil.getlfilestoupload(repo, missing, dedup)
1016 lfutil.getlfilestoupload(repo, missing, dedup)
1013 if lfhashes:
1017 if lfhashes:
1014 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1018 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1015 for fn, lfhash in knowns:
1019 for fn, lfhash in knowns:
1016 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1020 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1017 addfunc(fn, lfhash)
1021 addfunc(fn, lfhash)
1018
1022
1019 def outgoinghook(ui, repo, other, opts, missing):
1023 def outgoinghook(ui, repo, other, opts, missing):
1020 if opts.pop('large', None):
1024 if opts.pop('large', None):
1021 lfhashes = set()
1025 lfhashes = set()
1022 if ui.debugflag:
1026 if ui.debugflag:
1023 toupload = {}
1027 toupload = {}
1024 def addfunc(fn, lfhash):
1028 def addfunc(fn, lfhash):
1025 if fn not in toupload:
1029 if fn not in toupload:
1026 toupload[fn] = []
1030 toupload[fn] = []
1027 toupload[fn].append(lfhash)
1031 toupload[fn].append(lfhash)
1028 lfhashes.add(lfhash)
1032 lfhashes.add(lfhash)
1029 def showhashes(fn):
1033 def showhashes(fn):
1030 for lfhash in sorted(toupload[fn]):
1034 for lfhash in sorted(toupload[fn]):
1031 ui.debug(' %s\n' % (lfhash))
1035 ui.debug(' %s\n' % (lfhash))
1032 else:
1036 else:
1033 toupload = set()
1037 toupload = set()
1034 def addfunc(fn, lfhash):
1038 def addfunc(fn, lfhash):
1035 toupload.add(fn)
1039 toupload.add(fn)
1036 lfhashes.add(lfhash)
1040 lfhashes.add(lfhash)
1037 def showhashes(fn):
1041 def showhashes(fn):
1038 pass
1042 pass
1039 _getoutgoings(repo, other, missing, addfunc)
1043 _getoutgoings(repo, other, missing, addfunc)
1040
1044
1041 if not toupload:
1045 if not toupload:
1042 ui.status(_('largefiles: no files to upload\n'))
1046 ui.status(_('largefiles: no files to upload\n'))
1043 else:
1047 else:
1044 ui.status(_('largefiles to upload (%d entities):\n')
1048 ui.status(_('largefiles to upload (%d entities):\n')
1045 % (len(lfhashes)))
1049 % (len(lfhashes)))
1046 for file in sorted(toupload):
1050 for file in sorted(toupload):
1047 ui.status(lfutil.splitstandin(file) + '\n')
1051 ui.status(lfutil.splitstandin(file) + '\n')
1048 showhashes(file)
1052 showhashes(file)
1049 ui.status('\n')
1053 ui.status('\n')
1050
1054
1051 def summaryremotehook(ui, repo, opts, changes):
1055 def summaryremotehook(ui, repo, opts, changes):
1052 largeopt = opts.get('large', False)
1056 largeopt = opts.get('large', False)
1053 if changes is None:
1057 if changes is None:
1054 if largeopt:
1058 if largeopt:
1055 return (False, True) # only outgoing check is needed
1059 return (False, True) # only outgoing check is needed
1056 else:
1060 else:
1057 return (False, False)
1061 return (False, False)
1058 elif largeopt:
1062 elif largeopt:
1059 url, branch, peer, outgoing = changes[1]
1063 url, branch, peer, outgoing = changes[1]
1060 if peer is None:
1064 if peer is None:
1061 # i18n: column positioning for "hg summary"
1065 # i18n: column positioning for "hg summary"
1062 ui.status(_('largefiles: (no remote repo)\n'))
1066 ui.status(_('largefiles: (no remote repo)\n'))
1063 return
1067 return
1064
1068
1065 toupload = set()
1069 toupload = set()
1066 lfhashes = set()
1070 lfhashes = set()
1067 def addfunc(fn, lfhash):
1071 def addfunc(fn, lfhash):
1068 toupload.add(fn)
1072 toupload.add(fn)
1069 lfhashes.add(lfhash)
1073 lfhashes.add(lfhash)
1070 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1074 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1071
1075
1072 if not toupload:
1076 if not toupload:
1073 # i18n: column positioning for "hg summary"
1077 # i18n: column positioning for "hg summary"
1074 ui.status(_('largefiles: (no files to upload)\n'))
1078 ui.status(_('largefiles: (no files to upload)\n'))
1075 else:
1079 else:
1076 # i18n: column positioning for "hg summary"
1080 # i18n: column positioning for "hg summary"
1077 ui.status(_('largefiles: %d entities for %d files to upload\n')
1081 ui.status(_('largefiles: %d entities for %d files to upload\n')
1078 % (len(lfhashes), len(toupload)))
1082 % (len(lfhashes), len(toupload)))
1079
1083
1080 def overridesummary(orig, ui, repo, *pats, **opts):
1084 def overridesummary(orig, ui, repo, *pats, **opts):
1081 try:
1085 try:
1082 repo.lfstatus = True
1086 repo.lfstatus = True
1083 orig(ui, repo, *pats, **opts)
1087 orig(ui, repo, *pats, **opts)
1084 finally:
1088 finally:
1085 repo.lfstatus = False
1089 repo.lfstatus = False
1086
1090
1087 def scmutiladdremove(orig, repo, matcher, prefix, opts={}, dry_run=None,
1091 def scmutiladdremove(orig, repo, matcher, prefix, opts={}, dry_run=None,
1088 similarity=None):
1092 similarity=None):
1089 if not lfutil.islfilesrepo(repo):
1093 if not lfutil.islfilesrepo(repo):
1090 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1094 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1091 # Get the list of missing largefiles so we can remove them
1095 # Get the list of missing largefiles so we can remove them
1092 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1096 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1093 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1097 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1094 False, False, False)
1098 False, False, False)
1095
1099
1096 # Call into the normal remove code, but the removing of the standin, we want
1100 # Call into the normal remove code, but the removing of the standin, we want
1097 # to have handled by original addremove. Monkey patching here makes sure
1101 # to have handled by original addremove. Monkey patching here makes sure
1098 # we don't remove the standin in the largefiles code, preventing a very
1102 # we don't remove the standin in the largefiles code, preventing a very
1099 # confused state later.
1103 # confused state later.
1100 if s.deleted:
1104 if s.deleted:
1101 m = [repo.wjoin(f) for f in s.deleted]
1105 m = [repo.wjoin(f) for f in s.deleted]
1102 removelargefiles(repo.ui, repo, True, *m, **opts)
1106 removelargefiles(repo.ui, repo, True, *m, **opts)
1103 # Call into the normal add code, and any files that *should* be added as
1107 # Call into the normal add code, and any files that *should* be added as
1104 # largefiles will be
1108 # largefiles will be
1105 addlargefiles(repo.ui, repo, matcher, **opts)
1109 addlargefiles(repo.ui, repo, matcher, **opts)
1106 # Now that we've handled largefiles, hand off to the original addremove
1110 # Now that we've handled largefiles, hand off to the original addremove
1107 # function to take care of the rest. Make sure it doesn't do anything with
1111 # function to take care of the rest. Make sure it doesn't do anything with
1108 # largefiles by passing a matcher that will ignore them.
1112 # largefiles by passing a matcher that will ignore them.
1109 matcher = composenormalfilematcher(matcher, repo[None].manifest())
1113 matcher = composenormalfilematcher(matcher, repo[None].manifest())
1110 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1114 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1111
1115
1112 # Calling purge with --all will cause the largefiles to be deleted.
1116 # Calling purge with --all will cause the largefiles to be deleted.
1113 # Override repo.status to prevent this from happening.
1117 # Override repo.status to prevent this from happening.
1114 def overridepurge(orig, ui, repo, *dirs, **opts):
1118 def overridepurge(orig, ui, repo, *dirs, **opts):
1115 oldstatus = repo.status
1119 oldstatus = repo.status
1116 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1120 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1117 clean=False, unknown=False, listsubrepos=False):
1121 clean=False, unknown=False, listsubrepos=False):
1118 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1122 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1119 listsubrepos)
1123 listsubrepos)
1120 lfdirstate = lfutil.openlfdirstate(ui, repo)
1124 lfdirstate = lfutil.openlfdirstate(ui, repo)
1121 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1125 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1122 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1126 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1123 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1127 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1124 unknown, ignored, r.clean)
1128 unknown, ignored, r.clean)
1125 repo.status = overridestatus
1129 repo.status = overridestatus
1126 orig(ui, repo, *dirs, **opts)
1130 orig(ui, repo, *dirs, **opts)
1127 repo.status = oldstatus
1131 repo.status = oldstatus
1128 def overriderollback(orig, ui, repo, **opts):
1132 def overriderollback(orig, ui, repo, **opts):
1129 wlock = repo.wlock()
1133 wlock = repo.wlock()
1130 try:
1134 try:
1131 before = repo.dirstate.parents()
1135 before = repo.dirstate.parents()
1132 orphans = set(f for f in repo.dirstate
1136 orphans = set(f for f in repo.dirstate
1133 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1137 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1134 result = orig(ui, repo, **opts)
1138 result = orig(ui, repo, **opts)
1135 after = repo.dirstate.parents()
1139 after = repo.dirstate.parents()
1136 if before == after:
1140 if before == after:
1137 return result # no need to restore standins
1141 return result # no need to restore standins
1138
1142
1139 pctx = repo['.']
1143 pctx = repo['.']
1140 for f in repo.dirstate:
1144 for f in repo.dirstate:
1141 if lfutil.isstandin(f):
1145 if lfutil.isstandin(f):
1142 orphans.discard(f)
1146 orphans.discard(f)
1143 if repo.dirstate[f] == 'r':
1147 if repo.dirstate[f] == 'r':
1144 repo.wvfs.unlinkpath(f, ignoremissing=True)
1148 repo.wvfs.unlinkpath(f, ignoremissing=True)
1145 elif f in pctx:
1149 elif f in pctx:
1146 fctx = pctx[f]
1150 fctx = pctx[f]
1147 repo.wwrite(f, fctx.data(), fctx.flags())
1151 repo.wwrite(f, fctx.data(), fctx.flags())
1148 else:
1152 else:
1149 # content of standin is not so important in 'a',
1153 # content of standin is not so important in 'a',
1150 # 'm' or 'n' (coming from the 2nd parent) cases
1154 # 'm' or 'n' (coming from the 2nd parent) cases
1151 lfutil.writestandin(repo, f, '', False)
1155 lfutil.writestandin(repo, f, '', False)
1152 for standin in orphans:
1156 for standin in orphans:
1153 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1157 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1154
1158
1155 lfdirstate = lfutil.openlfdirstate(ui, repo)
1159 lfdirstate = lfutil.openlfdirstate(ui, repo)
1156 orphans = set(lfdirstate)
1160 orphans = set(lfdirstate)
1157 lfiles = lfutil.listlfiles(repo)
1161 lfiles = lfutil.listlfiles(repo)
1158 for file in lfiles:
1162 for file in lfiles:
1159 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1163 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1160 orphans.discard(file)
1164 orphans.discard(file)
1161 for lfile in orphans:
1165 for lfile in orphans:
1162 lfdirstate.drop(lfile)
1166 lfdirstate.drop(lfile)
1163 lfdirstate.write()
1167 lfdirstate.write()
1164 finally:
1168 finally:
1165 wlock.release()
1169 wlock.release()
1166 return result
1170 return result
1167
1171
1168 def overridetransplant(orig, ui, repo, *revs, **opts):
1172 def overridetransplant(orig, ui, repo, *revs, **opts):
1169 resuming = opts.get('continue')
1173 resuming = opts.get('continue')
1170 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1174 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1171 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1175 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1172 try:
1176 try:
1173 result = orig(ui, repo, *revs, **opts)
1177 result = orig(ui, repo, *revs, **opts)
1174 finally:
1178 finally:
1175 repo._lfstatuswriters.pop()
1179 repo._lfstatuswriters.pop()
1176 repo._lfcommithooks.pop()
1180 repo._lfcommithooks.pop()
1177 return result
1181 return result
1178
1182
1179 def overridecat(orig, ui, repo, file1, *pats, **opts):
1183 def overridecat(orig, ui, repo, file1, *pats, **opts):
1180 ctx = scmutil.revsingle(repo, opts.get('rev'))
1184 ctx = scmutil.revsingle(repo, opts.get('rev'))
1181 err = 1
1185 err = 1
1182 notbad = set()
1186 notbad = set()
1183 m = scmutil.match(ctx, (file1,) + pats, opts)
1187 m = scmutil.match(ctx, (file1,) + pats, opts)
1184 origmatchfn = m.matchfn
1188 origmatchfn = m.matchfn
1185 def lfmatchfn(f):
1189 def lfmatchfn(f):
1186 if origmatchfn(f):
1190 if origmatchfn(f):
1187 return True
1191 return True
1188 lf = lfutil.splitstandin(f)
1192 lf = lfutil.splitstandin(f)
1189 if lf is None:
1193 if lf is None:
1190 return False
1194 return False
1191 notbad.add(lf)
1195 notbad.add(lf)
1192 return origmatchfn(lf)
1196 return origmatchfn(lf)
1193 m.matchfn = lfmatchfn
1197 m.matchfn = lfmatchfn
1194 origbadfn = m.bad
1198 origbadfn = m.bad
1195 def lfbadfn(f, msg):
1199 def lfbadfn(f, msg):
1196 if not f in notbad:
1200 if not f in notbad:
1197 origbadfn(f, msg)
1201 origbadfn(f, msg)
1198 m.bad = lfbadfn
1202 m.bad = lfbadfn
1199 for f in ctx.walk(m):
1203 for f in ctx.walk(m):
1200 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1204 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1201 pathname=f)
1205 pathname=f)
1202 lf = lfutil.splitstandin(f)
1206 lf = lfutil.splitstandin(f)
1203 if lf is None or origmatchfn(f):
1207 if lf is None or origmatchfn(f):
1204 # duplicating unreachable code from commands.cat
1208 # duplicating unreachable code from commands.cat
1205 data = ctx[f].data()
1209 data = ctx[f].data()
1206 if opts.get('decode'):
1210 if opts.get('decode'):
1207 data = repo.wwritedata(f, data)
1211 data = repo.wwritedata(f, data)
1208 fp.write(data)
1212 fp.write(data)
1209 else:
1213 else:
1210 hash = lfutil.readstandin(repo, lf, ctx.rev())
1214 hash = lfutil.readstandin(repo, lf, ctx.rev())
1211 if not lfutil.inusercache(repo.ui, hash):
1215 if not lfutil.inusercache(repo.ui, hash):
1212 store = basestore._openstore(repo)
1216 store = basestore._openstore(repo)
1213 success, missing = store.get([(lf, hash)])
1217 success, missing = store.get([(lf, hash)])
1214 if len(success) != 1:
1218 if len(success) != 1:
1215 raise util.Abort(
1219 raise util.Abort(
1216 _('largefile %s is not in cache and could not be '
1220 _('largefile %s is not in cache and could not be '
1217 'downloaded') % lf)
1221 'downloaded') % lf)
1218 path = lfutil.usercachepath(repo.ui, hash)
1222 path = lfutil.usercachepath(repo.ui, hash)
1219 fpin = open(path, "rb")
1223 fpin = open(path, "rb")
1220 for chunk in util.filechunkiter(fpin, 128 * 1024):
1224 for chunk in util.filechunkiter(fpin, 128 * 1024):
1221 fp.write(chunk)
1225 fp.write(chunk)
1222 fpin.close()
1226 fpin.close()
1223 fp.close()
1227 fp.close()
1224 err = 0
1228 err = 0
1225 return err
1229 return err
1226
1230
1227 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1231 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1228 *args, **kwargs):
1232 *args, **kwargs):
1229 wlock = repo.wlock()
1233 wlock = repo.wlock()
1230 try:
1234 try:
1231 # branch | | |
1235 # branch | | |
1232 # merge | force | partial | action
1236 # merge | force | partial | action
1233 # -------+-------+---------+--------------
1237 # -------+-------+---------+--------------
1234 # x | x | x | linear-merge
1238 # x | x | x | linear-merge
1235 # o | x | x | branch-merge
1239 # o | x | x | branch-merge
1236 # x | o | x | overwrite (as clean update)
1240 # x | o | x | overwrite (as clean update)
1237 # o | o | x | force-branch-merge (*1)
1241 # o | o | x | force-branch-merge (*1)
1238 # x | x | o | (*)
1242 # x | x | o | (*)
1239 # o | x | o | (*)
1243 # o | x | o | (*)
1240 # x | o | o | overwrite (as revert)
1244 # x | o | o | overwrite (as revert)
1241 # o | o | o | (*)
1245 # o | o | o | (*)
1242 #
1246 #
1243 # (*) don't care
1247 # (*) don't care
1244 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1248 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1245
1249
1246 linearmerge = not branchmerge and not force and not partial
1250 linearmerge = not branchmerge and not force and not partial
1247
1251
1248 if linearmerge or (branchmerge and force and not partial):
1252 if linearmerge or (branchmerge and force and not partial):
1249 # update standins for linear-merge or force-branch-merge,
1253 # update standins for linear-merge or force-branch-merge,
1250 # because largefiles in the working directory may be modified
1254 # because largefiles in the working directory may be modified
1251 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1255 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1252 unsure, s = lfdirstate.status(match_.always(repo.root,
1256 unsure, s = lfdirstate.status(match_.always(repo.root,
1253 repo.getcwd()),
1257 repo.getcwd()),
1254 [], False, False, False)
1258 [], False, False, False)
1255 for lfile in unsure + s.modified + s.added:
1259 for lfile in unsure + s.modified + s.added:
1256 lfutil.updatestandin(repo, lfutil.standin(lfile))
1260 lfutil.updatestandin(repo, lfutil.standin(lfile))
1257
1261
1258 if linearmerge:
1262 if linearmerge:
1259 # Only call updatelfiles on the standins that have changed
1263 # Only call updatelfiles on the standins that have changed
1260 # to save time
1264 # to save time
1261 oldstandins = lfutil.getstandinsstate(repo)
1265 oldstandins = lfutil.getstandinsstate(repo)
1262
1266
1263 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1267 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1264
1268
1265 filelist = None
1269 filelist = None
1266 if linearmerge:
1270 if linearmerge:
1267 newstandins = lfutil.getstandinsstate(repo)
1271 newstandins = lfutil.getstandinsstate(repo)
1268 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1272 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1269
1273
1270 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1274 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1271 normallookup=partial)
1275 normallookup=partial)
1272
1276
1273 return result
1277 return result
1274 finally:
1278 finally:
1275 wlock.release()
1279 wlock.release()
1276
1280
1277 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1281 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1278 result = orig(repo, files, *args, **kwargs)
1282 result = orig(repo, files, *args, **kwargs)
1279
1283
1280 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1284 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1281 if filelist:
1285 if filelist:
1282 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1286 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1283 printmessage=False, normallookup=True)
1287 printmessage=False, normallookup=True)
1284
1288
1285 return result
1289 return result
@@ -1,1179 +1,1179 b''
1 # merge.py - directory-level update/merge handling for Mercurial
1 # merge.py - directory-level update/merge handling for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import struct
8 import struct
9
9
10 from node import nullid, nullrev, hex, bin
10 from node import nullid, nullrev, hex, bin
11 from i18n import _
11 from i18n import _
12 from mercurial import obsolete
12 from mercurial import obsolete
13 import error as errormod, util, filemerge, copies, subrepo, worker
13 import error as errormod, util, filemerge, copies, subrepo, worker
14 import errno, os, shutil
14 import errno, os, shutil
15
15
16 _pack = struct.pack
16 _pack = struct.pack
17 _unpack = struct.unpack
17 _unpack = struct.unpack
18
18
19 def _droponode(data):
19 def _droponode(data):
20 # used for compatibility for v1
20 # used for compatibility for v1
21 bits = data.split('\0')
21 bits = data.split('\0')
22 bits = bits[:-2] + bits[-1:]
22 bits = bits[:-2] + bits[-1:]
23 return '\0'.join(bits)
23 return '\0'.join(bits)
24
24
25 class mergestate(object):
25 class mergestate(object):
26 '''track 3-way merge state of individual files
26 '''track 3-way merge state of individual files
27
27
28 it is stored on disk when needed. Two file are used, one with an old
28 it is stored on disk when needed. Two file are used, one with an old
29 format, one with a new format. Both contains similar data, but the new
29 format, one with a new format. Both contains similar data, but the new
30 format can store new kind of field.
30 format can store new kind of field.
31
31
32 Current new format is a list of arbitrary record of the form:
32 Current new format is a list of arbitrary record of the form:
33
33
34 [type][length][content]
34 [type][length][content]
35
35
36 Type is a single character, length is a 4 bytes integer, content is an
36 Type is a single character, length is a 4 bytes integer, content is an
37 arbitrary suites of bytes of length `length`.
37 arbitrary suites of bytes of length `length`.
38
38
39 Type should be a letter. Capital letter are mandatory record, Mercurial
39 Type should be a letter. Capital letter are mandatory record, Mercurial
40 should abort if they are unknown. lower case record can be safely ignored.
40 should abort if they are unknown. lower case record can be safely ignored.
41
41
42 Currently known record:
42 Currently known record:
43
43
44 L: the node of the "local" part of the merge (hexified version)
44 L: the node of the "local" part of the merge (hexified version)
45 O: the node of the "other" part of the merge (hexified version)
45 O: the node of the "other" part of the merge (hexified version)
46 F: a file to be merged entry
46 F: a file to be merged entry
47 '''
47 '''
48 statepathv1 = 'merge/state'
48 statepathv1 = 'merge/state'
49 statepathv2 = 'merge/state2'
49 statepathv2 = 'merge/state2'
50
50
51 def __init__(self, repo):
51 def __init__(self, repo):
52 self._repo = repo
52 self._repo = repo
53 self._dirty = False
53 self._dirty = False
54 self._read()
54 self._read()
55
55
56 def reset(self, node=None, other=None):
56 def reset(self, node=None, other=None):
57 self._state = {}
57 self._state = {}
58 self._local = None
58 self._local = None
59 self._other = None
59 self._other = None
60 if node:
60 if node:
61 self._local = node
61 self._local = node
62 self._other = other
62 self._other = other
63 shutil.rmtree(self._repo.join('merge'), True)
63 shutil.rmtree(self._repo.join('merge'), True)
64 self._dirty = False
64 self._dirty = False
65
65
66 def _read(self):
66 def _read(self):
67 """Analyse each record content to restore a serialized state from disk
67 """Analyse each record content to restore a serialized state from disk
68
68
69 This function process "record" entry produced by the de-serialization
69 This function process "record" entry produced by the de-serialization
70 of on disk file.
70 of on disk file.
71 """
71 """
72 self._state = {}
72 self._state = {}
73 self._local = None
73 self._local = None
74 self._other = None
74 self._other = None
75 records = self._readrecords()
75 records = self._readrecords()
76 for rtype, record in records:
76 for rtype, record in records:
77 if rtype == 'L':
77 if rtype == 'L':
78 self._local = bin(record)
78 self._local = bin(record)
79 elif rtype == 'O':
79 elif rtype == 'O':
80 self._other = bin(record)
80 self._other = bin(record)
81 elif rtype == 'F':
81 elif rtype == 'F':
82 bits = record.split('\0')
82 bits = record.split('\0')
83 self._state[bits[0]] = bits[1:]
83 self._state[bits[0]] = bits[1:]
84 elif not rtype.islower():
84 elif not rtype.islower():
85 raise util.Abort(_('unsupported merge state record: %s')
85 raise util.Abort(_('unsupported merge state record: %s')
86 % rtype)
86 % rtype)
87 self._dirty = False
87 self._dirty = False
88
88
89 def _readrecords(self):
89 def _readrecords(self):
90 """Read merge state from disk and return a list of record (TYPE, data)
90 """Read merge state from disk and return a list of record (TYPE, data)
91
91
92 We read data from both v1 and v2 files and decide which one to use.
92 We read data from both v1 and v2 files and decide which one to use.
93
93
94 V1 has been used by version prior to 2.9.1 and contains less data than
94 V1 has been used by version prior to 2.9.1 and contains less data than
95 v2. We read both versions and check if no data in v2 contradicts
95 v2. We read both versions and check if no data in v2 contradicts
96 v1. If there is not contradiction we can safely assume that both v1
96 v1. If there is not contradiction we can safely assume that both v1
97 and v2 were written at the same time and use the extract data in v2. If
97 and v2 were written at the same time and use the extract data in v2. If
98 there is contradiction we ignore v2 content as we assume an old version
98 there is contradiction we ignore v2 content as we assume an old version
99 of Mercurial has overwritten the mergestate file and left an old v2
99 of Mercurial has overwritten the mergestate file and left an old v2
100 file around.
100 file around.
101
101
102 returns list of record [(TYPE, data), ...]"""
102 returns list of record [(TYPE, data), ...]"""
103 v1records = self._readrecordsv1()
103 v1records = self._readrecordsv1()
104 v2records = self._readrecordsv2()
104 v2records = self._readrecordsv2()
105 oldv2 = set() # old format version of v2 record
105 oldv2 = set() # old format version of v2 record
106 for rec in v2records:
106 for rec in v2records:
107 if rec[0] == 'L':
107 if rec[0] == 'L':
108 oldv2.add(rec)
108 oldv2.add(rec)
109 elif rec[0] == 'F':
109 elif rec[0] == 'F':
110 # drop the onode data (not contained in v1)
110 # drop the onode data (not contained in v1)
111 oldv2.add(('F', _droponode(rec[1])))
111 oldv2.add(('F', _droponode(rec[1])))
112 for rec in v1records:
112 for rec in v1records:
113 if rec not in oldv2:
113 if rec not in oldv2:
114 # v1 file is newer than v2 file, use it
114 # v1 file is newer than v2 file, use it
115 # we have to infer the "other" changeset of the merge
115 # we have to infer the "other" changeset of the merge
116 # we cannot do better than that with v1 of the format
116 # we cannot do better than that with v1 of the format
117 mctx = self._repo[None].parents()[-1]
117 mctx = self._repo[None].parents()[-1]
118 v1records.append(('O', mctx.hex()))
118 v1records.append(('O', mctx.hex()))
119 # add place holder "other" file node information
119 # add place holder "other" file node information
120 # nobody is using it yet so we do no need to fetch the data
120 # nobody is using it yet so we do no need to fetch the data
121 # if mctx was wrong `mctx[bits[-2]]` may fails.
121 # if mctx was wrong `mctx[bits[-2]]` may fails.
122 for idx, r in enumerate(v1records):
122 for idx, r in enumerate(v1records):
123 if r[0] == 'F':
123 if r[0] == 'F':
124 bits = r[1].split('\0')
124 bits = r[1].split('\0')
125 bits.insert(-2, '')
125 bits.insert(-2, '')
126 v1records[idx] = (r[0], '\0'.join(bits))
126 v1records[idx] = (r[0], '\0'.join(bits))
127 return v1records
127 return v1records
128 else:
128 else:
129 return v2records
129 return v2records
130
130
131 def _readrecordsv1(self):
131 def _readrecordsv1(self):
132 """read on disk merge state for version 1 file
132 """read on disk merge state for version 1 file
133
133
134 returns list of record [(TYPE, data), ...]
134 returns list of record [(TYPE, data), ...]
135
135
136 Note: the "F" data from this file are one entry short
136 Note: the "F" data from this file are one entry short
137 (no "other file node" entry)
137 (no "other file node" entry)
138 """
138 """
139 records = []
139 records = []
140 try:
140 try:
141 f = self._repo.opener(self.statepathv1)
141 f = self._repo.opener(self.statepathv1)
142 for i, l in enumerate(f):
142 for i, l in enumerate(f):
143 if i == 0:
143 if i == 0:
144 records.append(('L', l[:-1]))
144 records.append(('L', l[:-1]))
145 else:
145 else:
146 records.append(('F', l[:-1]))
146 records.append(('F', l[:-1]))
147 f.close()
147 f.close()
148 except IOError, err:
148 except IOError, err:
149 if err.errno != errno.ENOENT:
149 if err.errno != errno.ENOENT:
150 raise
150 raise
151 return records
151 return records
152
152
153 def _readrecordsv2(self):
153 def _readrecordsv2(self):
154 """read on disk merge state for version 2 file
154 """read on disk merge state for version 2 file
155
155
156 returns list of record [(TYPE, data), ...]
156 returns list of record [(TYPE, data), ...]
157 """
157 """
158 records = []
158 records = []
159 try:
159 try:
160 f = self._repo.opener(self.statepathv2)
160 f = self._repo.opener(self.statepathv2)
161 data = f.read()
161 data = f.read()
162 off = 0
162 off = 0
163 end = len(data)
163 end = len(data)
164 while off < end:
164 while off < end:
165 rtype = data[off]
165 rtype = data[off]
166 off += 1
166 off += 1
167 length = _unpack('>I', data[off:(off + 4)])[0]
167 length = _unpack('>I', data[off:(off + 4)])[0]
168 off += 4
168 off += 4
169 record = data[off:(off + length)]
169 record = data[off:(off + length)]
170 off += length
170 off += length
171 records.append((rtype, record))
171 records.append((rtype, record))
172 f.close()
172 f.close()
173 except IOError, err:
173 except IOError, err:
174 if err.errno != errno.ENOENT:
174 if err.errno != errno.ENOENT:
175 raise
175 raise
176 return records
176 return records
177
177
178 def active(self):
178 def active(self):
179 """Whether mergestate is active.
179 """Whether mergestate is active.
180
180
181 Returns True if there appears to be mergestate. This is a rough proxy
181 Returns True if there appears to be mergestate. This is a rough proxy
182 for "is a merge in progress."
182 for "is a merge in progress."
183 """
183 """
184 # Check local variables before looking at filesystem for performance
184 # Check local variables before looking at filesystem for performance
185 # reasons.
185 # reasons.
186 return bool(self._local) or bool(self._state) or \
186 return bool(self._local) or bool(self._state) or \
187 self._repo.opener.exists(self.statepathv1) or \
187 self._repo.opener.exists(self.statepathv1) or \
188 self._repo.opener.exists(self.statepathv2)
188 self._repo.opener.exists(self.statepathv2)
189
189
190 def commit(self):
190 def commit(self):
191 """Write current state on disk (if necessary)"""
191 """Write current state on disk (if necessary)"""
192 if self._dirty:
192 if self._dirty:
193 records = []
193 records = []
194 records.append(('L', hex(self._local)))
194 records.append(('L', hex(self._local)))
195 records.append(('O', hex(self._other)))
195 records.append(('O', hex(self._other)))
196 for d, v in self._state.iteritems():
196 for d, v in self._state.iteritems():
197 records.append(('F', '\0'.join([d] + v)))
197 records.append(('F', '\0'.join([d] + v)))
198 self._writerecords(records)
198 self._writerecords(records)
199 self._dirty = False
199 self._dirty = False
200
200
201 def _writerecords(self, records):
201 def _writerecords(self, records):
202 """Write current state on disk (both v1 and v2)"""
202 """Write current state on disk (both v1 and v2)"""
203 self._writerecordsv1(records)
203 self._writerecordsv1(records)
204 self._writerecordsv2(records)
204 self._writerecordsv2(records)
205
205
206 def _writerecordsv1(self, records):
206 def _writerecordsv1(self, records):
207 """Write current state on disk in a version 1 file"""
207 """Write current state on disk in a version 1 file"""
208 f = self._repo.opener(self.statepathv1, 'w')
208 f = self._repo.opener(self.statepathv1, 'w')
209 irecords = iter(records)
209 irecords = iter(records)
210 lrecords = irecords.next()
210 lrecords = irecords.next()
211 assert lrecords[0] == 'L'
211 assert lrecords[0] == 'L'
212 f.write(hex(self._local) + '\n')
212 f.write(hex(self._local) + '\n')
213 for rtype, data in irecords:
213 for rtype, data in irecords:
214 if rtype == 'F':
214 if rtype == 'F':
215 f.write('%s\n' % _droponode(data))
215 f.write('%s\n' % _droponode(data))
216 f.close()
216 f.close()
217
217
218 def _writerecordsv2(self, records):
218 def _writerecordsv2(self, records):
219 """Write current state on disk in a version 2 file"""
219 """Write current state on disk in a version 2 file"""
220 f = self._repo.opener(self.statepathv2, 'w')
220 f = self._repo.opener(self.statepathv2, 'w')
221 for key, data in records:
221 for key, data in records:
222 assert len(key) == 1
222 assert len(key) == 1
223 format = '>sI%is' % len(data)
223 format = '>sI%is' % len(data)
224 f.write(_pack(format, key, len(data), data))
224 f.write(_pack(format, key, len(data), data))
225 f.close()
225 f.close()
226
226
227 def add(self, fcl, fco, fca, fd):
227 def add(self, fcl, fco, fca, fd):
228 """add a new (potentially?) conflicting file the merge state
228 """add a new (potentially?) conflicting file the merge state
229 fcl: file context for local,
229 fcl: file context for local,
230 fco: file context for remote,
230 fco: file context for remote,
231 fca: file context for ancestors,
231 fca: file context for ancestors,
232 fd: file path of the resulting merge.
232 fd: file path of the resulting merge.
233
233
234 note: also write the local version to the `.hg/merge` directory.
234 note: also write the local version to the `.hg/merge` directory.
235 """
235 """
236 hash = util.sha1(fcl.path()).hexdigest()
236 hash = util.sha1(fcl.path()).hexdigest()
237 self._repo.opener.write('merge/' + hash, fcl.data())
237 self._repo.opener.write('merge/' + hash, fcl.data())
238 self._state[fd] = ['u', hash, fcl.path(),
238 self._state[fd] = ['u', hash, fcl.path(),
239 fca.path(), hex(fca.filenode()),
239 fca.path(), hex(fca.filenode()),
240 fco.path(), hex(fco.filenode()),
240 fco.path(), hex(fco.filenode()),
241 fcl.flags()]
241 fcl.flags()]
242 self._dirty = True
242 self._dirty = True
243
243
244 def __contains__(self, dfile):
244 def __contains__(self, dfile):
245 return dfile in self._state
245 return dfile in self._state
246
246
247 def __getitem__(self, dfile):
247 def __getitem__(self, dfile):
248 return self._state[dfile][0]
248 return self._state[dfile][0]
249
249
250 def __iter__(self):
250 def __iter__(self):
251 return iter(sorted(self._state))
251 return iter(sorted(self._state))
252
252
253 def files(self):
253 def files(self):
254 return self._state.keys()
254 return self._state.keys()
255
255
256 def mark(self, dfile, state):
256 def mark(self, dfile, state):
257 self._state[dfile][0] = state
257 self._state[dfile][0] = state
258 self._dirty = True
258 self._dirty = True
259
259
260 def unresolved(self):
260 def unresolved(self):
261 """Obtain the paths of unresolved files."""
261 """Obtain the paths of unresolved files."""
262
262
263 for f, entry in self._state.items():
263 for f, entry in self._state.items():
264 if entry[0] == 'u':
264 if entry[0] == 'u':
265 yield f
265 yield f
266
266
267 def resolve(self, dfile, wctx, labels=None):
267 def resolve(self, dfile, wctx, labels=None):
268 """rerun merge process for file path `dfile`"""
268 """rerun merge process for file path `dfile`"""
269 if self[dfile] == 'r':
269 if self[dfile] == 'r':
270 return 0
270 return 0
271 stateentry = self._state[dfile]
271 stateentry = self._state[dfile]
272 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
272 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
273 octx = self._repo[self._other]
273 octx = self._repo[self._other]
274 fcd = wctx[dfile]
274 fcd = wctx[dfile]
275 fco = octx[ofile]
275 fco = octx[ofile]
276 fca = self._repo.filectx(afile, fileid=anode)
276 fca = self._repo.filectx(afile, fileid=anode)
277 # "premerge" x flags
277 # "premerge" x flags
278 flo = fco.flags()
278 flo = fco.flags()
279 fla = fca.flags()
279 fla = fca.flags()
280 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
280 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
281 if fca.node() == nullid:
281 if fca.node() == nullid:
282 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
282 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
283 afile)
283 afile)
284 elif flags == fla:
284 elif flags == fla:
285 flags = flo
285 flags = flo
286 # restore local
286 # restore local
287 f = self._repo.opener('merge/' + hash)
287 f = self._repo.opener('merge/' + hash)
288 self._repo.wwrite(dfile, f.read(), flags)
288 self._repo.wwrite(dfile, f.read(), flags)
289 f.close()
289 f.close()
290 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca,
290 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca,
291 labels=labels)
291 labels=labels)
292 if r is None:
292 if r is None:
293 # no real conflict
293 # no real conflict
294 del self._state[dfile]
294 del self._state[dfile]
295 self._dirty = True
295 self._dirty = True
296 elif not r:
296 elif not r:
297 self.mark(dfile, 'r')
297 self.mark(dfile, 'r')
298 return r
298 return r
299
299
300 def _checkunknownfile(repo, wctx, mctx, f):
300 def _checkunknownfile(repo, wctx, mctx, f):
301 return (os.path.isfile(repo.wjoin(f))
301 return (os.path.isfile(repo.wjoin(f))
302 and repo.wopener.audit.check(f)
302 and repo.wopener.audit.check(f)
303 and repo.dirstate.normalize(f) not in repo.dirstate
303 and repo.dirstate.normalize(f) not in repo.dirstate
304 and mctx[f].cmp(wctx[f]))
304 and mctx[f].cmp(wctx[f]))
305
305
306 def _forgetremoved(wctx, mctx, branchmerge):
306 def _forgetremoved(wctx, mctx, branchmerge):
307 """
307 """
308 Forget removed files
308 Forget removed files
309
309
310 If we're jumping between revisions (as opposed to merging), and if
310 If we're jumping between revisions (as opposed to merging), and if
311 neither the working directory nor the target rev has the file,
311 neither the working directory nor the target rev has the file,
312 then we need to remove it from the dirstate, to prevent the
312 then we need to remove it from the dirstate, to prevent the
313 dirstate from listing the file when it is no longer in the
313 dirstate from listing the file when it is no longer in the
314 manifest.
314 manifest.
315
315
316 If we're merging, and the other revision has removed a file
316 If we're merging, and the other revision has removed a file
317 that is not present in the working directory, we need to mark it
317 that is not present in the working directory, we need to mark it
318 as removed.
318 as removed.
319 """
319 """
320
320
321 ractions = []
321 ractions = []
322 factions = xactions = []
322 factions = xactions = []
323 if branchmerge:
323 if branchmerge:
324 xactions = ractions
324 xactions = ractions
325 for f in wctx.deleted():
325 for f in wctx.deleted():
326 if f not in mctx:
326 if f not in mctx:
327 xactions.append((f, None, "forget deleted"))
327 xactions.append((f, None, "forget deleted"))
328
328
329 if not branchmerge:
329 if not branchmerge:
330 for f in wctx.removed():
330 for f in wctx.removed():
331 if f not in mctx:
331 if f not in mctx:
332 factions.append((f, None, "forget removed"))
332 factions.append((f, None, "forget removed"))
333
333
334 return ractions, factions
334 return ractions, factions
335
335
336 def _checkcollision(repo, wmf, actions):
336 def _checkcollision(repo, wmf, actions):
337 # build provisional merged manifest up
337 # build provisional merged manifest up
338 pmmf = set(wmf)
338 pmmf = set(wmf)
339
339
340 if actions:
340 if actions:
341 # k, dr, e and rd are no-op
341 # k, dr, e and rd are no-op
342 for m in 'a', 'f', 'g', 'cd', 'dc':
342 for m in 'a', 'f', 'g', 'cd', 'dc':
343 for f, args, msg in actions[m]:
343 for f, args, msg in actions[m]:
344 pmmf.add(f)
344 pmmf.add(f)
345 for f, args, msg in actions['r']:
345 for f, args, msg in actions['r']:
346 pmmf.discard(f)
346 pmmf.discard(f)
347 for f, args, msg in actions['dm']:
347 for f, args, msg in actions['dm']:
348 f2, flags = args
348 f2, flags = args
349 pmmf.discard(f2)
349 pmmf.discard(f2)
350 pmmf.add(f)
350 pmmf.add(f)
351 for f, args, msg in actions['dg']:
351 for f, args, msg in actions['dg']:
352 pmmf.add(f)
352 pmmf.add(f)
353 for f, args, msg in actions['m']:
353 for f, args, msg in actions['m']:
354 f1, f2, fa, move, anc = args
354 f1, f2, fa, move, anc = args
355 if move:
355 if move:
356 pmmf.discard(f1)
356 pmmf.discard(f1)
357 pmmf.add(f)
357 pmmf.add(f)
358
358
359 # check case-folding collision in provisional merged manifest
359 # check case-folding collision in provisional merged manifest
360 foldmap = {}
360 foldmap = {}
361 for f in sorted(pmmf):
361 for f in sorted(pmmf):
362 fold = util.normcase(f)
362 fold = util.normcase(f)
363 if fold in foldmap:
363 if fold in foldmap:
364 raise util.Abort(_("case-folding collision between %s and %s")
364 raise util.Abort(_("case-folding collision between %s and %s")
365 % (f, foldmap[fold]))
365 % (f, foldmap[fold]))
366 foldmap[fold] = f
366 foldmap[fold] = f
367
367
368 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
368 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
369 acceptremote, followcopies):
369 acceptremote, followcopies):
370 """
370 """
371 Merge p1 and p2 with ancestor pa and generate merge action list
371 Merge p1 and p2 with ancestor pa and generate merge action list
372
372
373 branchmerge and force are as passed in to update
373 branchmerge and force are as passed in to update
374 partial = function to filter file lists
374 partial = function to filter file lists
375 acceptremote = accept the incoming changes without prompting
375 acceptremote = accept the incoming changes without prompting
376 """
376 """
377
377
378 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
378 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
379 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
379 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
380
380
381 # manifests fetched in order are going to be faster, so prime the caches
381 # manifests fetched in order are going to be faster, so prime the caches
382 [x.manifest() for x in
382 [x.manifest() for x in
383 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
383 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
384
384
385 if followcopies:
385 if followcopies:
386 ret = copies.mergecopies(repo, wctx, p2, pa)
386 ret = copies.mergecopies(repo, wctx, p2, pa)
387 copy, movewithdir, diverge, renamedelete = ret
387 copy, movewithdir, diverge, renamedelete = ret
388
388
389 repo.ui.note(_("resolving manifests\n"))
389 repo.ui.note(_("resolving manifests\n"))
390 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
390 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
391 % (bool(branchmerge), bool(force), bool(partial)))
391 % (bool(branchmerge), bool(force), bool(partial)))
392 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
392 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
393
393
394 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
394 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
395 copied = set(copy.values())
395 copied = set(copy.values())
396 copied.update(movewithdir.values())
396 copied.update(movewithdir.values())
397
397
398 if '.hgsubstate' in m1:
398 if '.hgsubstate' in m1:
399 # check whether sub state is modified
399 # check whether sub state is modified
400 for s in sorted(wctx.substate):
400 for s in sorted(wctx.substate):
401 if wctx.sub(s).dirty():
401 if wctx.sub(s).dirty():
402 m1['.hgsubstate'] += '+'
402 m1['.hgsubstate'] += '+'
403 break
403 break
404
404
405 aborts = []
405 aborts = []
406 # Compare manifests
406 # Compare manifests
407 diff = m1.diff(m2)
407 diff = m1.diff(m2)
408
408
409 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
409 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
410 if partial and not partial(f):
410 if partial and not partial(f):
411 continue
411 continue
412 if n1 and n2: # file exists on both local and remote side
412 if n1 and n2: # file exists on both local and remote side
413 if f not in ma:
413 if f not in ma:
414 fa = copy.get(f, None)
414 fa = copy.get(f, None)
415 if fa is not None:
415 if fa is not None:
416 actions['m'].append((f, (f, f, fa, False, pa.node()),
416 actions['m'].append((f, (f, f, fa, False, pa.node()),
417 "both renamed from " + fa))
417 "both renamed from " + fa))
418 else:
418 else:
419 actions['m'].append((f, (f, f, None, False, pa.node()),
419 actions['m'].append((f, (f, f, None, False, pa.node()),
420 "both created"))
420 "both created"))
421 else:
421 else:
422 a = ma[f]
422 a = ma[f]
423 fla = ma.flags(f)
423 fla = ma.flags(f)
424 nol = 'l' not in fl1 + fl2 + fla
424 nol = 'l' not in fl1 + fl2 + fla
425 if n2 == a and fl2 == fla:
425 if n2 == a and fl2 == fla:
426 actions['k'].append((f, (), "remote unchanged"))
426 actions['k'].append((f, (), "remote unchanged"))
427 elif n1 == a and fl1 == fla: # local unchanged - use remote
427 elif n1 == a and fl1 == fla: # local unchanged - use remote
428 if n1 == n2: # optimization: keep local content
428 if n1 == n2: # optimization: keep local content
429 actions['e'].append((f, (fl2,), "update permissions"))
429 actions['e'].append((f, (fl2,), "update permissions"))
430 else:
430 else:
431 actions['g'].append((f, (fl2,), "remote is newer"))
431 actions['g'].append((f, (fl2,), "remote is newer"))
432 elif nol and n2 == a: # remote only changed 'x'
432 elif nol and n2 == a: # remote only changed 'x'
433 actions['e'].append((f, (fl2,), "update permissions"))
433 actions['e'].append((f, (fl2,), "update permissions"))
434 elif nol and n1 == a: # local only changed 'x'
434 elif nol and n1 == a: # local only changed 'x'
435 actions['g'].append((f, (fl1,), "remote is newer"))
435 actions['g'].append((f, (fl1,), "remote is newer"))
436 else: # both changed something
436 else: # both changed something
437 actions['m'].append((f, (f, f, f, False, pa.node()),
437 actions['m'].append((f, (f, f, f, False, pa.node()),
438 "versions differ"))
438 "versions differ"))
439 elif n1: # file exists only on local side
439 elif n1: # file exists only on local side
440 if f in copied:
440 if f in copied:
441 pass # we'll deal with it on m2 side
441 pass # we'll deal with it on m2 side
442 elif f in movewithdir: # directory rename, move local
442 elif f in movewithdir: # directory rename, move local
443 f2 = movewithdir[f]
443 f2 = movewithdir[f]
444 if f2 in m2:
444 if f2 in m2:
445 actions['m'].append((f2, (f, f2, None, True, pa.node()),
445 actions['m'].append((f2, (f, f2, None, True, pa.node()),
446 "remote directory rename, both created"))
446 "remote directory rename, both created"))
447 else:
447 else:
448 actions['dm'].append((f2, (f, fl1),
448 actions['dm'].append((f2, (f, fl1),
449 "remote directory rename - move from " + f))
449 "remote directory rename - move from " + f))
450 elif f in copy:
450 elif f in copy:
451 f2 = copy[f]
451 f2 = copy[f]
452 actions['m'].append((f, (f, f2, f2, False, pa.node()),
452 actions['m'].append((f, (f, f2, f2, False, pa.node()),
453 "local copied/moved from " + f2))
453 "local copied/moved from " + f2))
454 elif f in ma: # clean, a different, no remote
454 elif f in ma: # clean, a different, no remote
455 if n1 != ma[f]:
455 if n1 != ma[f]:
456 if acceptremote:
456 if acceptremote:
457 actions['r'].append((f, None, "remote delete"))
457 actions['r'].append((f, None, "remote delete"))
458 else:
458 else:
459 actions['cd'].append((f, None,
459 actions['cd'].append((f, None,
460 "prompt changed/deleted"))
460 "prompt changed/deleted"))
461 elif n1[20:] == 'a':
461 elif n1[20:] == 'a':
462 # This extra 'a' is added by working copy manifest to mark
462 # This extra 'a' is added by working copy manifest to mark
463 # the file as locally added. We should forget it instead of
463 # the file as locally added. We should forget it instead of
464 # deleting it.
464 # deleting it.
465 actions['f'].append((f, None, "remote deleted"))
465 actions['f'].append((f, None, "remote deleted"))
466 else:
466 else:
467 actions['r'].append((f, None, "other deleted"))
467 actions['r'].append((f, None, "other deleted"))
468 elif n2: # file exists only on remote side
468 elif n2: # file exists only on remote side
469 if f in copied:
469 if f in copied:
470 pass # we'll deal with it on m1 side
470 pass # we'll deal with it on m1 side
471 elif f in movewithdir:
471 elif f in movewithdir:
472 f2 = movewithdir[f]
472 f2 = movewithdir[f]
473 if f2 in m1:
473 if f2 in m1:
474 actions['m'].append((f2, (f2, f, None, False, pa.node()),
474 actions['m'].append((f2, (f2, f, None, False, pa.node()),
475 "local directory rename, both created"))
475 "local directory rename, both created"))
476 else:
476 else:
477 actions['dg'].append((f2, (f, fl2),
477 actions['dg'].append((f2, (f, fl2),
478 "local directory rename - get from " + f))
478 "local directory rename - get from " + f))
479 elif f in copy:
479 elif f in copy:
480 f2 = copy[f]
480 f2 = copy[f]
481 if f2 in m2:
481 if f2 in m2:
482 actions['m'].append((f, (f2, f, f2, False, pa.node()),
482 actions['m'].append((f, (f2, f, f2, False, pa.node()),
483 "remote copied from " + f2))
483 "remote copied from " + f2))
484 else:
484 else:
485 actions['m'].append((f, (f2, f, f2, True, pa.node()),
485 actions['m'].append((f, (f2, f, f2, True, pa.node()),
486 "remote moved from " + f2))
486 "remote moved from " + f2))
487 elif f not in ma:
487 elif f not in ma:
488 # local unknown, remote created: the logic is described by the
488 # local unknown, remote created: the logic is described by the
489 # following table:
489 # following table:
490 #
490 #
491 # force branchmerge different | action
491 # force branchmerge different | action
492 # n * n | get
492 # n * n | get
493 # n * y | abort
493 # n * y | abort
494 # y n * | get
494 # y n * | get
495 # y y n | get
495 # y y n | get
496 # y y y | merge
496 # y y y | merge
497 #
497 #
498 # Checking whether the files are different is expensive, so we
498 # Checking whether the files are different is expensive, so we
499 # don't do that when we can avoid it.
499 # don't do that when we can avoid it.
500 if force and not branchmerge:
500 if force and not branchmerge:
501 actions['g'].append((f, (fl2,), "remote created"))
501 actions['g'].append((f, (fl2,), "remote created"))
502 else:
502 else:
503 different = _checkunknownfile(repo, wctx, p2, f)
503 different = _checkunknownfile(repo, wctx, p2, f)
504 if force and branchmerge and different:
504 if force and branchmerge and different:
505 actions['m'].append((f, (f, f, None, False, pa.node()),
505 actions['m'].append((f, (f, f, None, False, pa.node()),
506 "remote differs from untracked local"))
506 "remote differs from untracked local"))
507 elif not force and different:
507 elif not force and different:
508 aborts.append((f, 'ud'))
508 aborts.append((f, 'ud'))
509 else:
509 else:
510 actions['g'].append((f, (fl2,), "remote created"))
510 actions['g'].append((f, (fl2,), "remote created"))
511 elif n2 != ma[f]:
511 elif n2 != ma[f]:
512 different = _checkunknownfile(repo, wctx, p2, f)
512 different = _checkunknownfile(repo, wctx, p2, f)
513 if not force and different:
513 if not force and different:
514 aborts.append((f, 'ud'))
514 aborts.append((f, 'ud'))
515 else:
515 else:
516 if acceptremote:
516 if acceptremote:
517 actions['g'].append((f, (fl2,), "remote recreating"))
517 actions['g'].append((f, (fl2,), "remote recreating"))
518 else:
518 else:
519 actions['dc'].append((f, (fl2,),
519 actions['dc'].append((f, (fl2,),
520 "prompt deleted/changed"))
520 "prompt deleted/changed"))
521
521
522 for f, m in sorted(aborts):
522 for f, m in sorted(aborts):
523 if m == 'ud':
523 if m == 'ud':
524 repo.ui.warn(_("%s: untracked file differs\n") % f)
524 repo.ui.warn(_("%s: untracked file differs\n") % f)
525 else: assert False, m
525 else: assert False, m
526 if aborts:
526 if aborts:
527 raise util.Abort(_("untracked files in working directory differ "
527 raise util.Abort(_("untracked files in working directory differ "
528 "from files in requested revision"))
528 "from files in requested revision"))
529
529
530 if not util.checkcase(repo.path):
530 if not util.checkcase(repo.path):
531 # check collision between files only in p2 for clean update
531 # check collision between files only in p2 for clean update
532 if (not branchmerge and
532 if (not branchmerge and
533 (force or not wctx.dirty(missing=True, branch=False))):
533 (force or not wctx.dirty(missing=True, branch=False))):
534 _checkcollision(repo, m2, None)
534 _checkcollision(repo, m2, None)
535 else:
535 else:
536 _checkcollision(repo, m1, actions)
536 _checkcollision(repo, m1, actions)
537
537
538 return actions, diverge, renamedelete
538 return actions, diverge, renamedelete
539
539
540 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
540 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
541 """Resolves false conflicts where the nodeid changed but the content
541 """Resolves false conflicts where the nodeid changed but the content
542 remained the same."""
542 remained the same."""
543
543
544 cdactions = []
544 cdactions = []
545 for action in actions['cd']:
545 for action in actions['cd']:
546 f = action[0]
546 f = action[0]
547 if f in ancestor and not wctx[f].cmp(ancestor[f]):
547 if f in ancestor and not wctx[f].cmp(ancestor[f]):
548 # local did change but ended up with same content
548 # local did change but ended up with same content
549 actions['r'].append((f, None, "prompt same"))
549 actions['r'].append((f, None, "prompt same"))
550 else:
550 else:
551 cdactions.append(action)
551 cdactions.append(action)
552 actions['cd'] = cdactions
552 actions['cd'] = cdactions
553
553
554 dcactions = []
554 dcactions = []
555 for action in actions['dc']:
555 for action in actions['dc']:
556 f = action[0]
556 f = action[0]
557 if f in ancestor and not mctx[f].cmp(ancestor[f]):
557 if f in ancestor and not mctx[f].cmp(ancestor[f]):
558 # remote did change but ended up with same content
558 # remote did change but ended up with same content
559 pass # don't get = keep local deleted
559 pass # don't get = keep local deleted
560 else:
560 else:
561 dcactions.append(action)
561 dcactions.append(action)
562 actions['dc'] = dcactions
562 actions['dc'] = dcactions
563
563
564 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
564 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
565 acceptremote, followcopies):
565 acceptremote, followcopies):
566 "Calculate the actions needed to merge mctx into wctx using ancestors"
566 "Calculate the actions needed to merge mctx into wctx using ancestors"
567
567
568 if len(ancestors) == 1: # default
568 if len(ancestors) == 1: # default
569 actions, diverge, renamedelete = manifestmerge(
569 actions, diverge, renamedelete = manifestmerge(
570 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
570 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
571 acceptremote, followcopies)
571 acceptremote, followcopies)
572
572
573 else: # only when merge.preferancestor=* - the default
573 else: # only when merge.preferancestor=* - the default
574 repo.ui.note(
574 repo.ui.note(
575 _("note: merging %s and %s using bids from ancestors %s\n") %
575 _("note: merging %s and %s using bids from ancestors %s\n") %
576 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
576 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
577
577
578 # Call for bids
578 # Call for bids
579 fbids = {} # mapping filename to bids (action method to list af actions)
579 fbids = {} # mapping filename to bids (action method to list af actions)
580 diverge, renamedelete = None, None
580 diverge, renamedelete = None, None
581 for ancestor in ancestors:
581 for ancestor in ancestors:
582 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
582 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
583 actions, diverge1, renamedelete1 = manifestmerge(
583 actions, diverge1, renamedelete1 = manifestmerge(
584 repo, wctx, mctx, ancestor, branchmerge, force, partial,
584 repo, wctx, mctx, ancestor, branchmerge, force, partial,
585 acceptremote, followcopies)
585 acceptremote, followcopies)
586 if diverge is None: # and renamedelete is None.
586 if diverge is None: # and renamedelete is None.
587 # Arbitrarily pick warnings from first iteration
587 # Arbitrarily pick warnings from first iteration
588 diverge = diverge1
588 diverge = diverge1
589 renamedelete = renamedelete1
589 renamedelete = renamedelete1
590 for m, l in sorted(actions.items()):
590 for m, l in sorted(actions.items()):
591 for a in l:
591 for a in l:
592 f, args, msg = a
592 f, args, msg = a
593 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
593 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
594 if f in fbids:
594 if f in fbids:
595 d = fbids[f]
595 d = fbids[f]
596 if m in d:
596 if m in d:
597 d[m].append(a)
597 d[m].append(a)
598 else:
598 else:
599 d[m] = [a]
599 d[m] = [a]
600 else:
600 else:
601 fbids[f] = {m: [a]}
601 fbids[f] = {m: [a]}
602
602
603 # Pick the best bid for each file
603 # Pick the best bid for each file
604 repo.ui.note(_('\nauction for merging merge bids\n'))
604 repo.ui.note(_('\nauction for merging merge bids\n'))
605 actions = dict((m, []) for m in actions.keys())
605 actions = dict((m, []) for m in actions.keys())
606 for f, bids in sorted(fbids.items()):
606 for f, bids in sorted(fbids.items()):
607 # bids is a mapping from action method to list af actions
607 # bids is a mapping from action method to list af actions
608 # Consensus?
608 # Consensus?
609 if len(bids) == 1: # all bids are the same kind of method
609 if len(bids) == 1: # all bids are the same kind of method
610 m, l = bids.items()[0]
610 m, l = bids.items()[0]
611 if util.all(a == l[0] for a in l[1:]): # len(bids) is > 1
611 if util.all(a == l[0] for a in l[1:]): # len(bids) is > 1
612 repo.ui.note(" %s: consensus for %s\n" % (f, m))
612 repo.ui.note(" %s: consensus for %s\n" % (f, m))
613 actions[m].append(l[0])
613 actions[m].append(l[0])
614 continue
614 continue
615 # If keep is an option, just do it.
615 # If keep is an option, just do it.
616 if 'k' in bids:
616 if 'k' in bids:
617 repo.ui.note(" %s: picking 'keep' action\n" % f)
617 repo.ui.note(" %s: picking 'keep' action\n" % f)
618 actions['k'].append(bids['k'][0])
618 actions['k'].append(bids['k'][0])
619 continue
619 continue
620 # If there are gets and they all agree [how could they not?], do it.
620 # If there are gets and they all agree [how could they not?], do it.
621 if 'g' in bids:
621 if 'g' in bids:
622 ga0 = bids['g'][0]
622 ga0 = bids['g'][0]
623 if util.all(a == ga0 for a in bids['g'][1:]):
623 if util.all(a == ga0 for a in bids['g'][1:]):
624 repo.ui.note(" %s: picking 'get' action\n" % f)
624 repo.ui.note(" %s: picking 'get' action\n" % f)
625 actions['g'].append(ga0)
625 actions['g'].append(ga0)
626 continue
626 continue
627 # TODO: Consider other simple actions such as mode changes
627 # TODO: Consider other simple actions such as mode changes
628 # Handle inefficient democrazy.
628 # Handle inefficient democrazy.
629 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
629 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
630 for m, l in sorted(bids.items()):
630 for m, l in sorted(bids.items()):
631 for _f, args, msg in l:
631 for _f, args, msg in l:
632 repo.ui.note(' %s -> %s\n' % (msg, m))
632 repo.ui.note(' %s -> %s\n' % (msg, m))
633 # Pick random action. TODO: Instead, prompt user when resolving
633 # Pick random action. TODO: Instead, prompt user when resolving
634 m, l = bids.items()[0]
634 m, l = bids.items()[0]
635 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
635 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
636 (f, m))
636 (f, m))
637 actions[m].append(l[0])
637 actions[m].append(l[0])
638 continue
638 continue
639 repo.ui.note(_('end of auction\n\n'))
639 repo.ui.note(_('end of auction\n\n'))
640
640
641 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
641 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
642
642
643 # Prompt and create actions. TODO: Move this towards resolve phase.
644 for f, args, msg in sorted(actions['cd']):
645 if repo.ui.promptchoice(
646 _("local changed %s which remote deleted\n"
647 "use (c)hanged version or (d)elete?"
648 "$$ &Changed $$ &Delete") % f, 0):
649 actions['r'].append((f, None, "prompt delete"))
650 else:
651 actions['a'].append((f, None, "prompt keep"))
652 del actions['cd'][:]
653
654 for f, args, msg in sorted(actions['dc']):
655 flags, = args
656 if repo.ui.promptchoice(
657 _("remote changed %s which local deleted\n"
658 "use (c)hanged version or leave (d)eleted?"
659 "$$ &Changed $$ &Deleted") % f, 0) == 0:
660 actions['g'].append((f, (flags,), "prompt recreating"))
661 del actions['dc'][:]
662
663 if wctx.rev() is None:
643 if wctx.rev() is None:
664 ractions, factions = _forgetremoved(wctx, mctx, branchmerge)
644 ractions, factions = _forgetremoved(wctx, mctx, branchmerge)
665 actions['r'].extend(ractions)
645 actions['r'].extend(ractions)
666 actions['f'].extend(factions)
646 actions['f'].extend(factions)
667
647
668 return actions, diverge, renamedelete
648 return actions, diverge, renamedelete
669
649
670 def batchremove(repo, actions):
650 def batchremove(repo, actions):
671 """apply removes to the working directory
651 """apply removes to the working directory
672
652
673 yields tuples for progress updates
653 yields tuples for progress updates
674 """
654 """
675 verbose = repo.ui.verbose
655 verbose = repo.ui.verbose
676 unlink = util.unlinkpath
656 unlink = util.unlinkpath
677 wjoin = repo.wjoin
657 wjoin = repo.wjoin
678 audit = repo.wopener.audit
658 audit = repo.wopener.audit
679 i = 0
659 i = 0
680 for f, args, msg in actions:
660 for f, args, msg in actions:
681 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
661 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
682 if verbose:
662 if verbose:
683 repo.ui.note(_("removing %s\n") % f)
663 repo.ui.note(_("removing %s\n") % f)
684 audit(f)
664 audit(f)
685 try:
665 try:
686 unlink(wjoin(f), ignoremissing=True)
666 unlink(wjoin(f), ignoremissing=True)
687 except OSError, inst:
667 except OSError, inst:
688 repo.ui.warn(_("update failed to remove %s: %s!\n") %
668 repo.ui.warn(_("update failed to remove %s: %s!\n") %
689 (f, inst.strerror))
669 (f, inst.strerror))
690 if i == 100:
670 if i == 100:
691 yield i, f
671 yield i, f
692 i = 0
672 i = 0
693 i += 1
673 i += 1
694 if i > 0:
674 if i > 0:
695 yield i, f
675 yield i, f
696
676
697 def batchget(repo, mctx, actions):
677 def batchget(repo, mctx, actions):
698 """apply gets to the working directory
678 """apply gets to the working directory
699
679
700 mctx is the context to get from
680 mctx is the context to get from
701
681
702 yields tuples for progress updates
682 yields tuples for progress updates
703 """
683 """
704 verbose = repo.ui.verbose
684 verbose = repo.ui.verbose
705 fctx = mctx.filectx
685 fctx = mctx.filectx
706 wwrite = repo.wwrite
686 wwrite = repo.wwrite
707 i = 0
687 i = 0
708 for f, args, msg in actions:
688 for f, args, msg in actions:
709 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
689 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
710 if verbose:
690 if verbose:
711 repo.ui.note(_("getting %s\n") % f)
691 repo.ui.note(_("getting %s\n") % f)
712 wwrite(f, fctx(f).data(), args[0])
692 wwrite(f, fctx(f).data(), args[0])
713 if i == 100:
693 if i == 100:
714 yield i, f
694 yield i, f
715 i = 0
695 i = 0
716 i += 1
696 i += 1
717 if i > 0:
697 if i > 0:
718 yield i, f
698 yield i, f
719
699
720 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
700 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
721 """apply the merge action list to the working directory
701 """apply the merge action list to the working directory
722
702
723 wctx is the working copy context
703 wctx is the working copy context
724 mctx is the context to be merged into the working copy
704 mctx is the context to be merged into the working copy
725
705
726 Return a tuple of counts (updated, merged, removed, unresolved) that
706 Return a tuple of counts (updated, merged, removed, unresolved) that
727 describes how many files were affected by the update.
707 describes how many files were affected by the update.
728 """
708 """
729
709
730 updated, merged, removed, unresolved = 0, 0, 0, 0
710 updated, merged, removed, unresolved = 0, 0, 0, 0
731 ms = mergestate(repo)
711 ms = mergestate(repo)
732 ms.reset(wctx.p1().node(), mctx.node())
712 ms.reset(wctx.p1().node(), mctx.node())
733 moves = []
713 moves = []
734 for m, l in actions.items():
714 for m, l in actions.items():
735 l.sort()
715 l.sort()
736
716
737 # prescan for merges
717 # prescan for merges
738 for f, args, msg in actions['m']:
718 for f, args, msg in actions['m']:
739 f1, f2, fa, move, anc = args
719 f1, f2, fa, move, anc = args
740 if f == '.hgsubstate': # merged internally
720 if f == '.hgsubstate': # merged internally
741 continue
721 continue
742 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
722 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
743 fcl = wctx[f1]
723 fcl = wctx[f1]
744 fco = mctx[f2]
724 fco = mctx[f2]
745 actx = repo[anc]
725 actx = repo[anc]
746 if fa in actx:
726 if fa in actx:
747 fca = actx[fa]
727 fca = actx[fa]
748 else:
728 else:
749 fca = repo.filectx(f1, fileid=nullrev)
729 fca = repo.filectx(f1, fileid=nullrev)
750 ms.add(fcl, fco, fca, f)
730 ms.add(fcl, fco, fca, f)
751 if f1 != f and move:
731 if f1 != f and move:
752 moves.append(f1)
732 moves.append(f1)
753
733
754 audit = repo.wopener.audit
734 audit = repo.wopener.audit
755 _updating = _('updating')
735 _updating = _('updating')
756 _files = _('files')
736 _files = _('files')
757 progress = repo.ui.progress
737 progress = repo.ui.progress
758
738
759 # remove renamed files after safely stored
739 # remove renamed files after safely stored
760 for f in moves:
740 for f in moves:
761 if os.path.lexists(repo.wjoin(f)):
741 if os.path.lexists(repo.wjoin(f)):
762 repo.ui.debug("removing %s\n" % f)
742 repo.ui.debug("removing %s\n" % f)
763 audit(f)
743 audit(f)
764 util.unlinkpath(repo.wjoin(f))
744 util.unlinkpath(repo.wjoin(f))
765
745
766 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
746 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
767
747
768 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
748 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
769 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
749 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
770
750
771 # remove in parallel (must come first)
751 # remove in parallel (must come first)
772 z = 0
752 z = 0
773 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
753 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
774 for i, item in prog:
754 for i, item in prog:
775 z += i
755 z += i
776 progress(_updating, z, item=item, total=numupdates, unit=_files)
756 progress(_updating, z, item=item, total=numupdates, unit=_files)
777 removed = len(actions['r'])
757 removed = len(actions['r'])
778
758
779 # get in parallel
759 # get in parallel
780 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
760 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
781 for i, item in prog:
761 for i, item in prog:
782 z += i
762 z += i
783 progress(_updating, z, item=item, total=numupdates, unit=_files)
763 progress(_updating, z, item=item, total=numupdates, unit=_files)
784 updated = len(actions['g'])
764 updated = len(actions['g'])
785
765
786 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
766 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
787 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
767 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
788
768
789 # forget (manifest only, just log it) (must come first)
769 # forget (manifest only, just log it) (must come first)
790 for f, args, msg in actions['f']:
770 for f, args, msg in actions['f']:
791 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
771 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
792 z += 1
772 z += 1
793 progress(_updating, z, item=f, total=numupdates, unit=_files)
773 progress(_updating, z, item=f, total=numupdates, unit=_files)
794
774
795 # re-add (manifest only, just log it)
775 # re-add (manifest only, just log it)
796 for f, args, msg in actions['a']:
776 for f, args, msg in actions['a']:
797 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
777 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
798 z += 1
778 z += 1
799 progress(_updating, z, item=f, total=numupdates, unit=_files)
779 progress(_updating, z, item=f, total=numupdates, unit=_files)
800
780
801 # keep (noop, just log it)
781 # keep (noop, just log it)
802 for f, args, msg in actions['k']:
782 for f, args, msg in actions['k']:
803 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
783 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
804 # no progress
784 # no progress
805
785
806 # merge
786 # merge
807 for f, args, msg in actions['m']:
787 for f, args, msg in actions['m']:
808 repo.ui.debug(" %s: %s -> m\n" % (f, msg))
788 repo.ui.debug(" %s: %s -> m\n" % (f, msg))
809 z += 1
789 z += 1
810 progress(_updating, z, item=f, total=numupdates, unit=_files)
790 progress(_updating, z, item=f, total=numupdates, unit=_files)
811 if f == '.hgsubstate': # subrepo states need updating
791 if f == '.hgsubstate': # subrepo states need updating
812 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
792 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
813 overwrite)
793 overwrite)
814 continue
794 continue
815 audit(f)
795 audit(f)
816 r = ms.resolve(f, wctx, labels=labels)
796 r = ms.resolve(f, wctx, labels=labels)
817 if r is not None and r > 0:
797 if r is not None and r > 0:
818 unresolved += 1
798 unresolved += 1
819 else:
799 else:
820 if r is None:
800 if r is None:
821 updated += 1
801 updated += 1
822 else:
802 else:
823 merged += 1
803 merged += 1
824
804
825 # directory rename, move local
805 # directory rename, move local
826 for f, args, msg in actions['dm']:
806 for f, args, msg in actions['dm']:
827 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
807 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
828 z += 1
808 z += 1
829 progress(_updating, z, item=f, total=numupdates, unit=_files)
809 progress(_updating, z, item=f, total=numupdates, unit=_files)
830 f0, flags = args
810 f0, flags = args
831 repo.ui.note(_("moving %s to %s\n") % (f0, f))
811 repo.ui.note(_("moving %s to %s\n") % (f0, f))
832 audit(f)
812 audit(f)
833 repo.wwrite(f, wctx.filectx(f0).data(), flags)
813 repo.wwrite(f, wctx.filectx(f0).data(), flags)
834 util.unlinkpath(repo.wjoin(f0))
814 util.unlinkpath(repo.wjoin(f0))
835 updated += 1
815 updated += 1
836
816
837 # local directory rename, get
817 # local directory rename, get
838 for f, args, msg in actions['dg']:
818 for f, args, msg in actions['dg']:
839 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
819 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
840 z += 1
820 z += 1
841 progress(_updating, z, item=f, total=numupdates, unit=_files)
821 progress(_updating, z, item=f, total=numupdates, unit=_files)
842 f0, flags = args
822 f0, flags = args
843 repo.ui.note(_("getting %s to %s\n") % (f0, f))
823 repo.ui.note(_("getting %s to %s\n") % (f0, f))
844 repo.wwrite(f, mctx.filectx(f0).data(), flags)
824 repo.wwrite(f, mctx.filectx(f0).data(), flags)
845 updated += 1
825 updated += 1
846
826
847 # exec
827 # exec
848 for f, args, msg in actions['e']:
828 for f, args, msg in actions['e']:
849 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
829 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
850 z += 1
830 z += 1
851 progress(_updating, z, item=f, total=numupdates, unit=_files)
831 progress(_updating, z, item=f, total=numupdates, unit=_files)
852 flags, = args
832 flags, = args
853 audit(f)
833 audit(f)
854 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
834 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
855 updated += 1
835 updated += 1
856
836
857 ms.commit()
837 ms.commit()
858 progress(_updating, None, total=numupdates, unit=_files)
838 progress(_updating, None, total=numupdates, unit=_files)
859
839
860 return updated, merged, removed, unresolved
840 return updated, merged, removed, unresolved
861
841
862 def recordupdates(repo, actions, branchmerge):
842 def recordupdates(repo, actions, branchmerge):
863 "record merge actions to the dirstate"
843 "record merge actions to the dirstate"
864 # remove (must come first)
844 # remove (must come first)
865 for f, args, msg in actions['r']:
845 for f, args, msg in actions['r']:
866 if branchmerge:
846 if branchmerge:
867 repo.dirstate.remove(f)
847 repo.dirstate.remove(f)
868 else:
848 else:
869 repo.dirstate.drop(f)
849 repo.dirstate.drop(f)
870
850
871 # forget (must come first)
851 # forget (must come first)
872 for f, args, msg in actions['f']:
852 for f, args, msg in actions['f']:
873 repo.dirstate.drop(f)
853 repo.dirstate.drop(f)
874
854
875 # re-add
855 # re-add
876 for f, args, msg in actions['a']:
856 for f, args, msg in actions['a']:
877 if not branchmerge:
857 if not branchmerge:
878 repo.dirstate.add(f)
858 repo.dirstate.add(f)
879
859
880 # exec change
860 # exec change
881 for f, args, msg in actions['e']:
861 for f, args, msg in actions['e']:
882 repo.dirstate.normallookup(f)
862 repo.dirstate.normallookup(f)
883
863
884 # keep
864 # keep
885 for f, args, msg in actions['k']:
865 for f, args, msg in actions['k']:
886 pass
866 pass
887
867
888 # get
868 # get
889 for f, args, msg in actions['g']:
869 for f, args, msg in actions['g']:
890 if branchmerge:
870 if branchmerge:
891 repo.dirstate.otherparent(f)
871 repo.dirstate.otherparent(f)
892 else:
872 else:
893 repo.dirstate.normal(f)
873 repo.dirstate.normal(f)
894
874
895 # merge
875 # merge
896 for f, args, msg in actions['m']:
876 for f, args, msg in actions['m']:
897 f1, f2, fa, move, anc = args
877 f1, f2, fa, move, anc = args
898 if branchmerge:
878 if branchmerge:
899 # We've done a branch merge, mark this file as merged
879 # We've done a branch merge, mark this file as merged
900 # so that we properly record the merger later
880 # so that we properly record the merger later
901 repo.dirstate.merge(f)
881 repo.dirstate.merge(f)
902 if f1 != f2: # copy/rename
882 if f1 != f2: # copy/rename
903 if move:
883 if move:
904 repo.dirstate.remove(f1)
884 repo.dirstate.remove(f1)
905 if f1 != f:
885 if f1 != f:
906 repo.dirstate.copy(f1, f)
886 repo.dirstate.copy(f1, f)
907 else:
887 else:
908 repo.dirstate.copy(f2, f)
888 repo.dirstate.copy(f2, f)
909 else:
889 else:
910 # We've update-merged a locally modified file, so
890 # We've update-merged a locally modified file, so
911 # we set the dirstate to emulate a normal checkout
891 # we set the dirstate to emulate a normal checkout
912 # of that file some time in the past. Thus our
892 # of that file some time in the past. Thus our
913 # merge will appear as a normal local file
893 # merge will appear as a normal local file
914 # modification.
894 # modification.
915 if f2 == f: # file not locally copied/moved
895 if f2 == f: # file not locally copied/moved
916 repo.dirstate.normallookup(f)
896 repo.dirstate.normallookup(f)
917 if move:
897 if move:
918 repo.dirstate.drop(f1)
898 repo.dirstate.drop(f1)
919
899
920 # directory rename, move local
900 # directory rename, move local
921 for f, args, msg in actions['dm']:
901 for f, args, msg in actions['dm']:
922 f0, flag = args
902 f0, flag = args
923 if branchmerge:
903 if branchmerge:
924 repo.dirstate.add(f)
904 repo.dirstate.add(f)
925 repo.dirstate.remove(f0)
905 repo.dirstate.remove(f0)
926 repo.dirstate.copy(f0, f)
906 repo.dirstate.copy(f0, f)
927 else:
907 else:
928 repo.dirstate.normal(f)
908 repo.dirstate.normal(f)
929 repo.dirstate.drop(f0)
909 repo.dirstate.drop(f0)
930
910
931 # directory rename, get
911 # directory rename, get
932 for f, args, msg in actions['dg']:
912 for f, args, msg in actions['dg']:
933 f0, flag = args
913 f0, flag = args
934 if branchmerge:
914 if branchmerge:
935 repo.dirstate.add(f)
915 repo.dirstate.add(f)
936 repo.dirstate.copy(f0, f)
916 repo.dirstate.copy(f0, f)
937 else:
917 else:
938 repo.dirstate.normal(f)
918 repo.dirstate.normal(f)
939
919
940 def update(repo, node, branchmerge, force, partial, ancestor=None,
920 def update(repo, node, branchmerge, force, partial, ancestor=None,
941 mergeancestor=False, labels=None):
921 mergeancestor=False, labels=None):
942 """
922 """
943 Perform a merge between the working directory and the given node
923 Perform a merge between the working directory and the given node
944
924
945 node = the node to update to, or None if unspecified
925 node = the node to update to, or None if unspecified
946 branchmerge = whether to merge between branches
926 branchmerge = whether to merge between branches
947 force = whether to force branch merging or file overwriting
927 force = whether to force branch merging or file overwriting
948 partial = a function to filter file lists (dirstate not updated)
928 partial = a function to filter file lists (dirstate not updated)
949 mergeancestor = whether it is merging with an ancestor. If true,
929 mergeancestor = whether it is merging with an ancestor. If true,
950 we should accept the incoming changes for any prompts that occur.
930 we should accept the incoming changes for any prompts that occur.
951 If false, merging with an ancestor (fast-forward) is only allowed
931 If false, merging with an ancestor (fast-forward) is only allowed
952 between different named branches. This flag is used by rebase extension
932 between different named branches. This flag is used by rebase extension
953 as a temporary fix and should be avoided in general.
933 as a temporary fix and should be avoided in general.
954
934
955 The table below shows all the behaviors of the update command
935 The table below shows all the behaviors of the update command
956 given the -c and -C or no options, whether the working directory
936 given the -c and -C or no options, whether the working directory
957 is dirty, whether a revision is specified, and the relationship of
937 is dirty, whether a revision is specified, and the relationship of
958 the parent rev to the target rev (linear, on the same named
938 the parent rev to the target rev (linear, on the same named
959 branch, or on another named branch).
939 branch, or on another named branch).
960
940
961 This logic is tested by test-update-branches.t.
941 This logic is tested by test-update-branches.t.
962
942
963 -c -C dirty rev | linear same cross
943 -c -C dirty rev | linear same cross
964 n n n n | ok (1) x
944 n n n n | ok (1) x
965 n n n y | ok ok ok
945 n n n y | ok ok ok
966 n n y n | merge (2) (2)
946 n n y n | merge (2) (2)
967 n n y y | merge (3) (3)
947 n n y y | merge (3) (3)
968 n y * * | --- discard ---
948 n y * * | --- discard ---
969 y n y * | --- (4) ---
949 y n y * | --- (4) ---
970 y n n * | --- ok ---
950 y n n * | --- ok ---
971 y y * * | --- (5) ---
951 y y * * | --- (5) ---
972
952
973 x = can't happen
953 x = can't happen
974 * = don't-care
954 * = don't-care
975 1 = abort: not a linear update (merge or update --check to force update)
955 1 = abort: not a linear update (merge or update --check to force update)
976 2 = abort: uncommitted changes (commit and merge, or update --clean to
956 2 = abort: uncommitted changes (commit and merge, or update --clean to
977 discard changes)
957 discard changes)
978 3 = abort: uncommitted changes (commit or update --clean to discard changes)
958 3 = abort: uncommitted changes (commit or update --clean to discard changes)
979 4 = abort: uncommitted changes (checked in commands.py)
959 4 = abort: uncommitted changes (checked in commands.py)
980 5 = incompatible options (checked in commands.py)
960 5 = incompatible options (checked in commands.py)
981
961
982 Return the same tuple as applyupdates().
962 Return the same tuple as applyupdates().
983 """
963 """
984
964
985 onode = node
965 onode = node
986 wlock = repo.wlock()
966 wlock = repo.wlock()
987 try:
967 try:
988 wc = repo[None]
968 wc = repo[None]
989 pl = wc.parents()
969 pl = wc.parents()
990 p1 = pl[0]
970 p1 = pl[0]
991 pas = [None]
971 pas = [None]
992 if ancestor is not None:
972 if ancestor is not None:
993 pas = [repo[ancestor]]
973 pas = [repo[ancestor]]
994
974
995 if node is None:
975 if node is None:
996 # Here is where we should consider bookmarks, divergent bookmarks,
976 # Here is where we should consider bookmarks, divergent bookmarks,
997 # foreground changesets (successors), and tip of current branch;
977 # foreground changesets (successors), and tip of current branch;
998 # but currently we are only checking the branch tips.
978 # but currently we are only checking the branch tips.
999 try:
979 try:
1000 node = repo.branchtip(wc.branch())
980 node = repo.branchtip(wc.branch())
1001 except errormod.RepoLookupError:
981 except errormod.RepoLookupError:
1002 if wc.branch() == 'default': # no default branch!
982 if wc.branch() == 'default': # no default branch!
1003 node = repo.lookup('tip') # update to tip
983 node = repo.lookup('tip') # update to tip
1004 else:
984 else:
1005 raise util.Abort(_("branch %s not found") % wc.branch())
985 raise util.Abort(_("branch %s not found") % wc.branch())
1006
986
1007 if p1.obsolete() and not p1.children():
987 if p1.obsolete() and not p1.children():
1008 # allow updating to successors
988 # allow updating to successors
1009 successors = obsolete.successorssets(repo, p1.node())
989 successors = obsolete.successorssets(repo, p1.node())
1010
990
1011 # behavior of certain cases is as follows,
991 # behavior of certain cases is as follows,
1012 #
992 #
1013 # divergent changesets: update to highest rev, similar to what
993 # divergent changesets: update to highest rev, similar to what
1014 # is currently done when there are more than one head
994 # is currently done when there are more than one head
1015 # (i.e. 'tip')
995 # (i.e. 'tip')
1016 #
996 #
1017 # replaced changesets: same as divergent except we know there
997 # replaced changesets: same as divergent except we know there
1018 # is no conflict
998 # is no conflict
1019 #
999 #
1020 # pruned changeset: no update is done; though, we could
1000 # pruned changeset: no update is done; though, we could
1021 # consider updating to the first non-obsolete parent,
1001 # consider updating to the first non-obsolete parent,
1022 # similar to what is current done for 'hg prune'
1002 # similar to what is current done for 'hg prune'
1023
1003
1024 if successors:
1004 if successors:
1025 # flatten the list here handles both divergent (len > 1)
1005 # flatten the list here handles both divergent (len > 1)
1026 # and the usual case (len = 1)
1006 # and the usual case (len = 1)
1027 successors = [n for sub in successors for n in sub]
1007 successors = [n for sub in successors for n in sub]
1028
1008
1029 # get the max revision for the given successors set,
1009 # get the max revision for the given successors set,
1030 # i.e. the 'tip' of a set
1010 # i.e. the 'tip' of a set
1031 node = repo.revs('max(%ln)', successors).first()
1011 node = repo.revs('max(%ln)', successors).first()
1032 pas = [p1]
1012 pas = [p1]
1033
1013
1034 overwrite = force and not branchmerge
1014 overwrite = force and not branchmerge
1035
1015
1036 p2 = repo[node]
1016 p2 = repo[node]
1037 if pas[0] is None:
1017 if pas[0] is None:
1038 if repo.ui.config('merge', 'preferancestor', '*') == '*':
1018 if repo.ui.config('merge', 'preferancestor', '*') == '*':
1039 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1019 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1040 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1020 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1041 else:
1021 else:
1042 pas = [p1.ancestor(p2, warn=branchmerge)]
1022 pas = [p1.ancestor(p2, warn=branchmerge)]
1043
1023
1044 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1024 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1045
1025
1046 ### check phase
1026 ### check phase
1047 if not overwrite and len(pl) > 1:
1027 if not overwrite and len(pl) > 1:
1048 raise util.Abort(_("outstanding uncommitted merge"))
1028 raise util.Abort(_("outstanding uncommitted merge"))
1049 if branchmerge:
1029 if branchmerge:
1050 if pas == [p2]:
1030 if pas == [p2]:
1051 raise util.Abort(_("merging with a working directory ancestor"
1031 raise util.Abort(_("merging with a working directory ancestor"
1052 " has no effect"))
1032 " has no effect"))
1053 elif pas == [p1]:
1033 elif pas == [p1]:
1054 if not mergeancestor and p1.branch() == p2.branch():
1034 if not mergeancestor and p1.branch() == p2.branch():
1055 raise util.Abort(_("nothing to merge"),
1035 raise util.Abort(_("nothing to merge"),
1056 hint=_("use 'hg update' "
1036 hint=_("use 'hg update' "
1057 "or check 'hg heads'"))
1037 "or check 'hg heads'"))
1058 if not force and (wc.files() or wc.deleted()):
1038 if not force and (wc.files() or wc.deleted()):
1059 raise util.Abort(_("uncommitted changes"),
1039 raise util.Abort(_("uncommitted changes"),
1060 hint=_("use 'hg status' to list changes"))
1040 hint=_("use 'hg status' to list changes"))
1061 for s in sorted(wc.substate):
1041 for s in sorted(wc.substate):
1062 if wc.sub(s).dirty():
1042 if wc.sub(s).dirty():
1063 raise util.Abort(_("uncommitted changes in "
1043 raise util.Abort(_("uncommitted changes in "
1064 "subrepository '%s'") % s)
1044 "subrepository '%s'") % s)
1065
1045
1066 elif not overwrite:
1046 elif not overwrite:
1067 if p1 == p2: # no-op update
1047 if p1 == p2: # no-op update
1068 # call the hooks and exit early
1048 # call the hooks and exit early
1069 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1049 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1070 repo.hook('update', parent1=xp2, parent2='', error=0)
1050 repo.hook('update', parent1=xp2, parent2='', error=0)
1071 return 0, 0, 0, 0
1051 return 0, 0, 0, 0
1072
1052
1073 if pas not in ([p1], [p2]): # nonlinear
1053 if pas not in ([p1], [p2]): # nonlinear
1074 dirty = wc.dirty(missing=True)
1054 dirty = wc.dirty(missing=True)
1075 if dirty or onode is None:
1055 if dirty or onode is None:
1076 # Branching is a bit strange to ensure we do the minimal
1056 # Branching is a bit strange to ensure we do the minimal
1077 # amount of call to obsolete.background.
1057 # amount of call to obsolete.background.
1078 foreground = obsolete.foreground(repo, [p1.node()])
1058 foreground = obsolete.foreground(repo, [p1.node()])
1079 # note: the <node> variable contains a random identifier
1059 # note: the <node> variable contains a random identifier
1080 if repo[node].node() in foreground:
1060 if repo[node].node() in foreground:
1081 pas = [p1] # allow updating to successors
1061 pas = [p1] # allow updating to successors
1082 elif dirty:
1062 elif dirty:
1083 msg = _("uncommitted changes")
1063 msg = _("uncommitted changes")
1084 if onode is None:
1064 if onode is None:
1085 hint = _("commit and merge, or update --clean to"
1065 hint = _("commit and merge, or update --clean to"
1086 " discard changes")
1066 " discard changes")
1087 else:
1067 else:
1088 hint = _("commit or update --clean to discard"
1068 hint = _("commit or update --clean to discard"
1089 " changes")
1069 " changes")
1090 raise util.Abort(msg, hint=hint)
1070 raise util.Abort(msg, hint=hint)
1091 else: # node is none
1071 else: # node is none
1092 msg = _("not a linear update")
1072 msg = _("not a linear update")
1093 hint = _("merge or update --check to force update")
1073 hint = _("merge or update --check to force update")
1094 raise util.Abort(msg, hint=hint)
1074 raise util.Abort(msg, hint=hint)
1095 else:
1075 else:
1096 # Allow jumping branches if clean and specific rev given
1076 # Allow jumping branches if clean and specific rev given
1097 pas = [p1]
1077 pas = [p1]
1098
1078
1099 followcopies = False
1079 followcopies = False
1100 if overwrite:
1080 if overwrite:
1101 pas = [wc]
1081 pas = [wc]
1102 elif pas == [p2]: # backwards
1082 elif pas == [p2]: # backwards
1103 pas = [wc.p1()]
1083 pas = [wc.p1()]
1104 elif not branchmerge and not wc.dirty(missing=True):
1084 elif not branchmerge and not wc.dirty(missing=True):
1105 pass
1085 pass
1106 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1086 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1107 followcopies = True
1087 followcopies = True
1108
1088
1109 ### calculate phase
1089 ### calculate phase
1110 actions, diverge, renamedelete = calculateupdates(
1090 actions, diverge, renamedelete = calculateupdates(
1111 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1091 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1112 followcopies)
1092 followcopies)
1113
1093
1094 # Prompt and create actions. TODO: Move this towards resolve phase.
1095 for f, args, msg in sorted(actions['cd']):
1096 if repo.ui.promptchoice(
1097 _("local changed %s which remote deleted\n"
1098 "use (c)hanged version or (d)elete?"
1099 "$$ &Changed $$ &Delete") % f, 0):
1100 actions['r'].append((f, None, "prompt delete"))
1101 else:
1102 actions['a'].append((f, None, "prompt keep"))
1103 del actions['cd'][:]
1104
1105 for f, args, msg in sorted(actions['dc']):
1106 flags, = args
1107 if repo.ui.promptchoice(
1108 _("remote changed %s which local deleted\n"
1109 "use (c)hanged version or leave (d)eleted?"
1110 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1111 actions['g'].append((f, (flags,), "prompt recreating"))
1112 del actions['dc'][:]
1113
1114 ### apply phase
1114 ### apply phase
1115 if not branchmerge: # just jump to the new rev
1115 if not branchmerge: # just jump to the new rev
1116 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1116 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1117 if not partial:
1117 if not partial:
1118 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1118 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1119 # note that we're in the middle of an update
1119 # note that we're in the middle of an update
1120 repo.vfs.write('updatestate', p2.hex())
1120 repo.vfs.write('updatestate', p2.hex())
1121
1121
1122 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1122 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1123
1123
1124 # divergent renames
1124 # divergent renames
1125 for f, fl in sorted(diverge.iteritems()):
1125 for f, fl in sorted(diverge.iteritems()):
1126 repo.ui.warn(_("note: possible conflict - %s was renamed "
1126 repo.ui.warn(_("note: possible conflict - %s was renamed "
1127 "multiple times to:\n") % f)
1127 "multiple times to:\n") % f)
1128 for nf in fl:
1128 for nf in fl:
1129 repo.ui.warn(" %s\n" % nf)
1129 repo.ui.warn(" %s\n" % nf)
1130
1130
1131 # rename and delete
1131 # rename and delete
1132 for f, fl in sorted(renamedelete.iteritems()):
1132 for f, fl in sorted(renamedelete.iteritems()):
1133 repo.ui.warn(_("note: possible conflict - %s was deleted "
1133 repo.ui.warn(_("note: possible conflict - %s was deleted "
1134 "and renamed to:\n") % f)
1134 "and renamed to:\n") % f)
1135 for nf in fl:
1135 for nf in fl:
1136 repo.ui.warn(" %s\n" % nf)
1136 repo.ui.warn(" %s\n" % nf)
1137
1137
1138 if not partial:
1138 if not partial:
1139 repo.dirstate.beginparentchange()
1139 repo.dirstate.beginparentchange()
1140 repo.setparents(fp1, fp2)
1140 repo.setparents(fp1, fp2)
1141 recordupdates(repo, actions, branchmerge)
1141 recordupdates(repo, actions, branchmerge)
1142 # update completed, clear state
1142 # update completed, clear state
1143 util.unlink(repo.join('updatestate'))
1143 util.unlink(repo.join('updatestate'))
1144
1144
1145 if not branchmerge:
1145 if not branchmerge:
1146 repo.dirstate.setbranch(p2.branch())
1146 repo.dirstate.setbranch(p2.branch())
1147 repo.dirstate.endparentchange()
1147 repo.dirstate.endparentchange()
1148 finally:
1148 finally:
1149 wlock.release()
1149 wlock.release()
1150
1150
1151 if not partial:
1151 if not partial:
1152 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1152 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1153 return stats
1153 return stats
1154
1154
1155 def graft(repo, ctx, pctx, labels):
1155 def graft(repo, ctx, pctx, labels):
1156 """Do a graft-like merge.
1156 """Do a graft-like merge.
1157
1157
1158 This is a merge where the merge ancestor is chosen such that one
1158 This is a merge where the merge ancestor is chosen such that one
1159 or more changesets are grafted onto the current changeset. In
1159 or more changesets are grafted onto the current changeset. In
1160 addition to the merge, this fixes up the dirstate to include only
1160 addition to the merge, this fixes up the dirstate to include only
1161 a single parent and tries to duplicate any renames/copies
1161 a single parent and tries to duplicate any renames/copies
1162 appropriately.
1162 appropriately.
1163
1163
1164 ctx - changeset to rebase
1164 ctx - changeset to rebase
1165 pctx - merge base, usually ctx.p1()
1165 pctx - merge base, usually ctx.p1()
1166 labels - merge labels eg ['local', 'graft']
1166 labels - merge labels eg ['local', 'graft']
1167
1167
1168 """
1168 """
1169
1169
1170 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1170 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1171 labels=labels)
1171 labels=labels)
1172 # drop the second merge parent
1172 # drop the second merge parent
1173 repo.dirstate.beginparentchange()
1173 repo.dirstate.beginparentchange()
1174 repo.setparents(repo['.'].node(), nullid)
1174 repo.setparents(repo['.'].node(), nullid)
1175 repo.dirstate.write()
1175 repo.dirstate.write()
1176 # fix up dirstate for copies and renames
1176 # fix up dirstate for copies and renames
1177 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1177 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1178 repo.dirstate.endparentchange()
1178 repo.dirstate.endparentchange()
1179 return stats
1179 return stats
@@ -1,449 +1,415 b''
1
1
2 $ echo "[extensions]" >> $HGRCPATH
2 $ echo "[extensions]" >> $HGRCPATH
3 $ echo "largefiles =" >> $HGRCPATH
3 $ echo "largefiles =" >> $HGRCPATH
4
4
5 Create the repository outside $HOME since largefiles write to
5 Create the repository outside $HOME since largefiles write to
6 $HOME/.cache/largefiles.
6 $HOME/.cache/largefiles.
7
7
8 $ hg init test
8 $ hg init test
9 $ cd test
9 $ cd test
10 $ echo "root" > root
10 $ echo "root" > root
11 $ hg add root
11 $ hg add root
12 $ hg commit -m "Root commit" --config extensions.largefiles=!
12 $ hg commit -m "Root commit" --config extensions.largefiles=!
13
13
14 Ensure that .hg/largefiles isn't created before largefiles are added
14 Ensure that .hg/largefiles isn't created before largefiles are added
15 #if unix-permissions
15 #if unix-permissions
16 $ chmod 555 .hg
16 $ chmod 555 .hg
17 #endif
17 #endif
18 $ hg status
18 $ hg status
19 #if unix-permissions
19 #if unix-permissions
20 $ chmod 755 .hg
20 $ chmod 755 .hg
21 #endif
21 #endif
22
22
23 $ test -f .hg/largefiles
23 $ test -f .hg/largefiles
24 [1]
24 [1]
25
25
26 $ echo "large" > foo
26 $ echo "large" > foo
27 $ hg add --large foo
27 $ hg add --large foo
28 $ hg commit -m "Add foo as a largefile"
28 $ hg commit -m "Add foo as a largefile"
29
29
30 $ hg update -r 0
30 $ hg update -r 0
31 getting changed largefiles
31 getting changed largefiles
32 0 largefiles updated, 1 removed
32 0 largefiles updated, 1 removed
33 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
33 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
34
34
35 $ echo "normal" > foo
35 $ echo "normal" > foo
36 $ hg add foo
36 $ hg add foo
37 $ hg commit -m "Add foo as normal file"
37 $ hg commit -m "Add foo as normal file"
38 created new head
38 created new head
39
39
40 Normal file in the working copy, keeping the normal version:
40 Normal file in the working copy, keeping the normal version:
41
41
42 $ echo "n" | hg merge --config ui.interactive=Yes
42 $ echo "n" | hg merge --config ui.interactive=Yes
43 remote turned local normal file foo into a largefile
43 remote turned local normal file foo into a largefile
44 use (l)argefile or keep (n)ormal file? n
44 use (l)argefile or keep (n)ormal file? n
45 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
45 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 (branch merge, don't forget to commit)
46 (branch merge, don't forget to commit)
47
47
48 $ hg status
48 $ hg status
49 $ cat foo
49 $ cat foo
50 normal
50 normal
51
51
52 Normal file in the working copy, keeping the largefile version:
52 Normal file in the working copy, keeping the largefile version:
53
53
54 $ hg update -q -C
54 $ hg update -q -C
55 $ echo "l" | hg merge --config ui.interactive=Yes
55 $ echo "l" | hg merge --config ui.interactive=Yes
56 remote turned local normal file foo into a largefile
56 remote turned local normal file foo into a largefile
57 use (l)argefile or keep (n)ormal file? l
57 use (l)argefile or keep (n)ormal file? l
58 getting changed largefiles
58 getting changed largefiles
59 1 largefiles updated, 0 removed
59 1 largefiles updated, 0 removed
60 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
60 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
61 (branch merge, don't forget to commit)
61 (branch merge, don't forget to commit)
62
62
63 $ hg status
63 $ hg status
64 M foo
64 M foo
65
65
66 $ hg diff --nodates
66 $ hg diff --nodates
67 diff -r fa129ab6b5a7 .hglf/foo
67 diff -r fa129ab6b5a7 .hglf/foo
68 --- /dev/null
68 --- /dev/null
69 +++ b/.hglf/foo
69 +++ b/.hglf/foo
70 @@ -0,0 +1,1 @@
70 @@ -0,0 +1,1 @@
71 +7f7097b041ccf68cc5561e9600da4655d21c6d18
71 +7f7097b041ccf68cc5561e9600da4655d21c6d18
72 diff -r fa129ab6b5a7 foo
72 diff -r fa129ab6b5a7 foo
73 --- a/foo
73 --- a/foo
74 +++ /dev/null
74 +++ /dev/null
75 @@ -1,1 +0,0 @@
75 @@ -1,1 +0,0 @@
76 -normal
76 -normal
77
77
78 $ cat foo
78 $ cat foo
79 large
79 large
80
80
81 Largefile in the working copy, keeping the normal version:
81 Largefile in the working copy, keeping the normal version:
82
82
83 $ hg update -q -C -r 1
83 $ hg update -q -C -r 1
84 $ echo "n" | hg merge --config ui.interactive=Yes
84 $ echo "n" | hg merge --config ui.interactive=Yes
85 remote turned local largefile foo into a normal file
85 remote turned local largefile foo into a normal file
86 keep (l)argefile or use (n)ormal file? n
86 keep (l)argefile or use (n)ormal file? n
87 getting changed largefiles
87 getting changed largefiles
88 0 largefiles updated, 0 removed
88 0 largefiles updated, 0 removed
89 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
89 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
90 (branch merge, don't forget to commit)
90 (branch merge, don't forget to commit)
91
91
92 $ hg status
92 $ hg status
93 M foo
93 M foo
94
94
95 $ hg diff --nodates
95 $ hg diff --nodates
96 diff -r ff521236428a .hglf/foo
96 diff -r ff521236428a .hglf/foo
97 --- a/.hglf/foo
97 --- a/.hglf/foo
98 +++ /dev/null
98 +++ /dev/null
99 @@ -1,1 +0,0 @@
99 @@ -1,1 +0,0 @@
100 -7f7097b041ccf68cc5561e9600da4655d21c6d18
100 -7f7097b041ccf68cc5561e9600da4655d21c6d18
101 diff -r ff521236428a foo
101 diff -r ff521236428a foo
102 --- /dev/null
102 --- /dev/null
103 +++ b/foo
103 +++ b/foo
104 @@ -0,0 +1,1 @@
104 @@ -0,0 +1,1 @@
105 +normal
105 +normal
106
106
107 $ cat foo
107 $ cat foo
108 normal
108 normal
109
109
110 Largefile in the working copy, keeping the largefile version:
110 Largefile in the working copy, keeping the largefile version:
111
111
112 $ hg update -q -C -r 1
112 $ hg update -q -C -r 1
113 $ echo "l" | hg merge --config ui.interactive=Yes
113 $ echo "l" | hg merge --config ui.interactive=Yes
114 remote turned local largefile foo into a normal file
114 remote turned local largefile foo into a normal file
115 keep (l)argefile or use (n)ormal file? l
115 keep (l)argefile or use (n)ormal file? l
116 getting changed largefiles
116 getting changed largefiles
117 0 largefiles updated, 0 removed
117 0 largefiles updated, 0 removed
118 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
119 (branch merge, don't forget to commit)
119 (branch merge, don't forget to commit)
120
120
121 $ hg status
121 $ hg status
122
122
123 $ cat foo
123 $ cat foo
124 large
124 large
125
125
126 Whatever ... commit something so we can invoke merge when updating
126 Whatever ... commit something so we can invoke merge when updating
127
127
128 $ hg commit -m '3: Merge'
128 $ hg commit -m '3: Merge'
129
129
130 Updating from largefile to normal - no reason to prompt
130 Updating from largefile to normal - no reason to prompt
131
131
132 $ hg up -r 2
132 $ hg up -r 2
133 getting changed largefiles
133 getting changed largefiles
134 0 largefiles updated, 0 removed
134 0 largefiles updated, 0 removed
135 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
135 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
136 $ cat foo
136 $ cat foo
137 normal
137 normal
138
138
139 (the update above used to leave the working dir in a very weird state - clean it
139 (the update above used to leave the working dir in a very weird state - clean it
140 $ hg up -qr null
140 $ hg up -qr null
141 $ hg up -qr 2
141 $ hg up -qr 2
142 )
142 )
143
143
144 Updating from normal to largefile - no reason to prompt
144 Updating from normal to largefile - no reason to prompt
145
145
146 $ hg up -r 3
146 $ hg up -r 3
147 getting changed largefiles
147 getting changed largefiles
148 1 largefiles updated, 0 removed
148 1 largefiles updated, 0 removed
149 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
149 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
150 $ cat foo
150 $ cat foo
151 large
151 large
152
152
153 $ cd ..
153 $ cd ..
154
154
155
155
156 Systematic testing of merges involving largefiles:
156 Systematic testing of merges involving largefiles:
157
157
158 Ancestor: normal Parent: normal-id Parent: large result: large
158 Ancestor: normal Parent: normal-id Parent: large result: large
159 Ancestor: normal Parent: normal2 Parent: large result: ?
159 Ancestor: normal Parent: normal2 Parent: large result: ?
160 Ancestor: large Parent: large-id Parent: normal result: normal
160 Ancestor: large Parent: large-id Parent: normal result: normal
161 Ancestor: large Parent: large2 Parent: normal result: ?
161 Ancestor: large Parent: large2 Parent: normal result: ?
162
162
163 All cases should try merging both ways.
163 All cases should try merging both ways.
164
164
165 Prepare test repo:
165 Prepare test repo:
166
166
167 $ hg init merges
167 $ hg init merges
168 $ cd merges
168 $ cd merges
169
169
170 prepare cases with "normal" ancestor:
170 prepare cases with "normal" ancestor:
171
171
172 $ hg up -qr null
172 $ hg up -qr null
173 $ echo normal > f
173 $ echo normal > f
174 $ hg ci -Aqm "normal-ancestor"
174 $ hg ci -Aqm "normal-ancestor"
175 $ hg tag -l "normal-ancestor"
175 $ hg tag -l "normal-ancestor"
176 $ touch f2
176 $ touch f2
177 $ hg ci -Aqm "normal-id"
177 $ hg ci -Aqm "normal-id"
178 $ hg tag -l "normal-id"
178 $ hg tag -l "normal-id"
179 $ echo normal2 > f
179 $ echo normal2 > f
180 $ hg ci -m "normal2"
180 $ hg ci -m "normal2"
181 $ hg tag -l "normal2"
181 $ hg tag -l "normal2"
182 $ echo normal > f
182 $ echo normal > f
183 $ hg ci -Aqm "normal-same"
183 $ hg ci -Aqm "normal-same"
184 $ hg tag -l "normal-same"
184 $ hg tag -l "normal-same"
185 $ hg up -qr "normal-ancestor"
185 $ hg up -qr "normal-ancestor"
186 $ hg rm f
186 $ hg rm f
187 $ echo large > f
187 $ echo large > f
188 $ hg add --large f
188 $ hg add --large f
189 $ hg ci -qm "large"
189 $ hg ci -qm "large"
190 $ hg tag -l "large"
190 $ hg tag -l "large"
191
191
192 prepare cases with "large" ancestor:
192 prepare cases with "large" ancestor:
193
193
194 $ hg up -qr null
194 $ hg up -qr null
195 $ echo large > f
195 $ echo large > f
196 $ hg add --large f
196 $ hg add --large f
197 $ hg ci -qm "large-ancestor"
197 $ hg ci -qm "large-ancestor"
198 $ hg tag -l "large-ancestor"
198 $ hg tag -l "large-ancestor"
199 $ touch f2
199 $ touch f2
200 $ hg ci -Aqm "large-id"
200 $ hg ci -Aqm "large-id"
201 $ hg tag -l "large-id"
201 $ hg tag -l "large-id"
202 $ echo large2 > f
202 $ echo large2 > f
203 $ hg ci -m "large2"
203 $ hg ci -m "large2"
204 $ hg tag -l "large2"
204 $ hg tag -l "large2"
205 $ echo large > f
205 $ echo large > f
206 $ hg ci -Aqm "large-same"
206 $ hg ci -Aqm "large-same"
207 $ hg tag -l "large-same"
207 $ hg tag -l "large-same"
208 $ hg up -qr "large-ancestor"
208 $ hg up -qr "large-ancestor"
209 $ hg rm f
209 $ hg rm f
210 $ echo normal > f
210 $ echo normal > f
211 $ hg ci -qAm "normal"
211 $ hg ci -qAm "normal"
212 $ hg tag -l "normal"
212 $ hg tag -l "normal"
213
213
214 $ hg log -GT '{tags}'
214 $ hg log -GT '{tags}'
215 @ normal tip
215 @ normal tip
216 |
216 |
217 | o large-same
217 | o large-same
218 | |
218 | |
219 | o large2
219 | o large2
220 | |
220 | |
221 | o large-id
221 | o large-id
222 |/
222 |/
223 o large-ancestor
223 o large-ancestor
224
224
225 o large
225 o large
226 |
226 |
227 | o normal-same
227 | o normal-same
228 | |
228 | |
229 | o normal2
229 | o normal2
230 | |
230 | |
231 | o normal-id
231 | o normal-id
232 |/
232 |/
233 o normal-ancestor
233 o normal-ancestor
234
234
235
235
236
236
237 Ancestor: normal Parent: normal-id Parent: large result: large
237 Ancestor: normal Parent: normal-id Parent: large result: large
238
238
239 $ hg up -Cqr normal-id
239 $ hg up -Cqr normal-id
240 $ hg merge -r large
240 $ hg merge -r large
241 getting changed largefiles
241 getting changed largefiles
242 1 largefiles updated, 0 removed
242 1 largefiles updated, 0 removed
243 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
243 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
244 (branch merge, don't forget to commit)
244 (branch merge, don't forget to commit)
245 $ cat f
245 $ cat f
246 large
246 large
247
247
248 swap
248 swap
249
249
250 $ hg up -Cqr large
250 $ hg up -Cqr large
251 $ hg merge -r normal-id
251 $ hg merge -r normal-id
252 getting changed largefiles
252 getting changed largefiles
253 0 largefiles updated, 0 removed
253 0 largefiles updated, 0 removed
254 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
254 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
255 (branch merge, don't forget to commit)
255 (branch merge, don't forget to commit)
256 $ cat f
256 $ cat f
257 large
257 large
258
258
259 Ancestor: normal Parent: normal-same Parent: large result: large
259 Ancestor: normal Parent: normal-same Parent: large result: large
260
260
261 $ hg up -Cqr normal-same
261 $ hg up -Cqr normal-same
262 $ hg merge -r large
262 $ hg merge -r large
263 getting changed largefiles
263 getting changed largefiles
264 1 largefiles updated, 0 removed
264 1 largefiles updated, 0 removed
265 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
265 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
266 (branch merge, don't forget to commit)
266 (branch merge, don't forget to commit)
267 $ cat f
267 $ cat f
268 large
268 large
269
269
270 swap
270 swap
271
271
272 $ hg up -Cqr large
272 $ hg up -Cqr large
273 $ hg merge -r normal-same
273 $ hg merge -r normal-same
274 getting changed largefiles
274 getting changed largefiles
275 0 largefiles updated, 0 removed
275 0 largefiles updated, 0 removed
276 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
276 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
277 (branch merge, don't forget to commit)
277 (branch merge, don't forget to commit)
278 $ cat f
278 $ cat f
279 large
279 large
280
280
281 Ancestor: normal Parent: normal2 Parent: large result: ?
281 Ancestor: normal Parent: normal2 Parent: large result: ?
282 (annoying extra prompt ... but it do not do any serious harm)
282 (annoying extra prompt ... but it do not do any serious harm)
283
283
284 $ hg up -Cqr normal2
284 $ hg up -Cqr normal2
285 $ hg merge -r large
285 $ hg merge -r large
286 local changed f which remote deleted
287 use (c)hanged version or (d)elete? c
288 remote turned local normal file f into a largefile
286 remote turned local normal file f into a largefile
289 use (l)argefile or keep (n)ormal file? l
287 use (l)argefile or keep (n)ormal file? l
290 getting changed largefiles
288 getting changed largefiles
291 1 largefiles updated, 0 removed
289 1 largefiles updated, 0 removed
292 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
290 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
293 (branch merge, don't forget to commit)
291 (branch merge, don't forget to commit)
294 $ cat f
292 $ cat f
295 large
293 large
296
294
297 $ hg up -Cqr normal2
295 $ hg up -Cqr normal2
298 $ ( echo c; echo n ) | hg merge -r large --config ui.interactive=Yes
296 $ echo n | hg merge -r large --config ui.interactive=Yes
299 local changed f which remote deleted
300 use (c)hanged version or (d)elete? c
301 remote turned local normal file f into a largefile
297 remote turned local normal file f into a largefile
302 use (l)argefile or keep (n)ormal file? n
298 use (l)argefile or keep (n)ormal file? n
303 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
299 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
304 (branch merge, don't forget to commit)
300 (branch merge, don't forget to commit)
305 $ cat f
301 $ cat f
306 normal2
302 normal2
307
303
308 $ hg up -Cqr normal2
309 $ echo d | hg merge -r large --config ui.interactive=Yes
310 local changed f which remote deleted
311 use (c)hanged version or (d)elete? d
312 getting changed largefiles
313 1 largefiles updated, 0 removed
314 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
315 (branch merge, don't forget to commit)
316 $ cat f
317 large
318
319 swap
304 swap
320
305
321 $ hg up -Cqr large
306 $ hg up -Cqr large
322 $ hg merge -r normal2
307 $ hg merge -r normal2
323 remote changed f which local deleted
324 use (c)hanged version or leave (d)eleted? c
325 remote turned local largefile f into a normal file
308 remote turned local largefile f into a normal file
326 keep (l)argefile or use (n)ormal file? l
309 keep (l)argefile or use (n)ormal file? l
327 getting changed largefiles
310 getting changed largefiles
328 0 largefiles updated, 0 removed
311 0 largefiles updated, 0 removed
329 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
312 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
330 (branch merge, don't forget to commit)
313 (branch merge, don't forget to commit)
331 $ cat f
314 $ cat f
332 large
315 large
333
316
334 $ hg up -Cqr large
317 $ hg up -Cqr large
335 $ ( echo c; echo n ) | hg merge -r normal2 --config ui.interactive=Yes
318 $ echo n | hg merge -r normal2 --config ui.interactive=Yes
336 remote changed f which local deleted
337 use (c)hanged version or leave (d)eleted? c
338 remote turned local largefile f into a normal file
319 remote turned local largefile f into a normal file
339 keep (l)argefile or use (n)ormal file? n
320 keep (l)argefile or use (n)ormal file? n
340 getting changed largefiles
321 getting changed largefiles
341 0 largefiles updated, 0 removed
322 0 largefiles updated, 0 removed
342 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
323 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
343 (branch merge, don't forget to commit)
324 (branch merge, don't forget to commit)
344 $ cat f
325 $ cat f
345 normal2
326 normal2
346
327
347 $ hg up -Cqr large
348 $ echo d | hg merge -r normal2 --config ui.interactive=Yes
349 remote changed f which local deleted
350 use (c)hanged version or leave (d)eleted? d
351 getting changed largefiles
352 0 largefiles updated, 0 removed
353 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
354 (branch merge, don't forget to commit)
355 $ cat f
356 large
357
358 Ancestor: large Parent: large-id Parent: normal result: normal
328 Ancestor: large Parent: large-id Parent: normal result: normal
359
329
360 $ hg up -Cqr large-id
330 $ hg up -Cqr large-id
361 $ hg merge -r normal
331 $ hg merge -r normal
362 getting changed largefiles
332 getting changed largefiles
363 0 largefiles updated, 0 removed
333 0 largefiles updated, 0 removed
364 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
334 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
365 (branch merge, don't forget to commit)
335 (branch merge, don't forget to commit)
366 $ cat f
336 $ cat f
367 normal
337 normal
368
338
369 swap
339 swap
370
340
371 $ hg up -Cqr normal
341 $ hg up -Cqr normal
372 $ hg merge -r large-id
342 $ hg merge -r large-id
373 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
343 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
374 (branch merge, don't forget to commit)
344 (branch merge, don't forget to commit)
375 $ cat f
345 $ cat f
376 normal
346 normal
377
347
378 Ancestor: large Parent: large-same Parent: normal result: normal
348 Ancestor: large Parent: large-same Parent: normal result: normal
379
349
380 $ hg up -Cqr large-same
350 $ hg up -Cqr large-same
381 $ hg merge -r normal
351 $ hg merge -r normal
382 getting changed largefiles
352 getting changed largefiles
383 0 largefiles updated, 0 removed
353 0 largefiles updated, 0 removed
384 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
354 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
385 (branch merge, don't forget to commit)
355 (branch merge, don't forget to commit)
386 $ cat f
356 $ cat f
387 normal
357 normal
388
358
389 swap
359 swap
390
360
391 $ hg up -Cqr normal
361 $ hg up -Cqr normal
392 $ hg merge -r large-same
362 $ hg merge -r large-same
393 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
363 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
394 (branch merge, don't forget to commit)
364 (branch merge, don't forget to commit)
395 $ cat f
365 $ cat f
396 normal
366 normal
397
367
398 Ancestor: large Parent: large2 Parent: normal result: ?
368 Ancestor: large Parent: large2 Parent: normal result: ?
399 (annoying extra prompt ... but it do not do any serious harm)
369 (annoying extra prompt ... but it do not do any serious harm)
400
370
401 $ hg up -Cqr large2
371 $ hg up -Cqr large2
402 $ hg merge -r normal
372 $ hg merge -r normal
403 local changed .hglf/f which remote deleted
404 use (c)hanged version or (d)elete? c
405 remote turned local largefile f into a normal file
373 remote turned local largefile f into a normal file
406 keep (l)argefile or use (n)ormal file? l
374 keep (l)argefile or use (n)ormal file? l
407 getting changed largefiles
375 getting changed largefiles
408 0 largefiles updated, 0 removed
376 0 largefiles updated, 0 removed
409 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
377 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
410 (branch merge, don't forget to commit)
378 (branch merge, don't forget to commit)
411 $ cat f
379 $ cat f
412 large2
380 large2
413
381
414 $ hg up -Cqr large2
382 $ hg up -Cqr large2
415 $ echo d | hg merge -r normal --config ui.interactive=Yes
383 $ echo n | hg merge -r normal --config ui.interactive=Yes
416 local changed .hglf/f which remote deleted
384 remote turned local largefile f into a normal file
417 use (c)hanged version or (d)elete? d
385 keep (l)argefile or use (n)ormal file? n
418 getting changed largefiles
386 getting changed largefiles
419 0 largefiles updated, 0 removed
387 0 largefiles updated, 0 removed
420 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
388 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
421 (branch merge, don't forget to commit)
389 (branch merge, don't forget to commit)
422 $ cat f
390 $ cat f
423 normal
391 normal
424
392
425 swap
393 swap
426
394
427 $ hg up -Cqr normal
395 $ hg up -Cqr normal
428 $ hg merge -r large2
396 $ hg merge -r large2
429 remote changed .hglf/f which local deleted
430 use (c)hanged version or leave (d)eleted? c
431 remote turned local normal file f into a largefile
397 remote turned local normal file f into a largefile
432 use (l)argefile or keep (n)ormal file? l
398 use (l)argefile or keep (n)ormal file? l
433 getting changed largefiles
399 getting changed largefiles
434 1 largefiles updated, 0 removed
400 1 largefiles updated, 0 removed
435 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
401 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
436 (branch merge, don't forget to commit)
402 (branch merge, don't forget to commit)
437 $ cat f
403 $ cat f
438 large2
404 large2
439
405
440 $ hg up -Cqr normal
406 $ hg up -Cqr normal
441 $ echo d | hg merge -r large2 --config ui.interactive=Yes
407 $ echo n | hg merge -r large2 --config ui.interactive=Yes
442 remote changed .hglf/f which local deleted
408 remote turned local normal file f into a largefile
443 use (c)hanged version or leave (d)eleted? d
409 use (l)argefile or keep (n)ormal file? n
444 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
410 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
445 (branch merge, don't forget to commit)
411 (branch merge, don't forget to commit)
446 $ cat f
412 $ cat f
447 normal
413 normal
448
414
449 $ cd ..
415 $ cd ..
@@ -1,656 +1,647 b''
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 getting changed largefiles
39 getting changed largefiles
40 1 largefiles updated, 0 removed
40 1 largefiles updated, 0 removed
41 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
41 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
42 (branch merge, don't forget to commit)
42 (branch merge, don't forget to commit)
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? o
67 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
68 merging normal1
68 merging normal1
69 warning: conflicts during merge.
69 warning: conflicts during merge.
70 merging normal1 incomplete! (edit conflicts, then use 'hg resolve --mark')
70 merging normal1 incomplete! (edit conflicts, then use 'hg resolve --mark')
71 getting changed largefiles
71 getting changed largefiles
72 1 largefiles updated, 0 removed
72 1 largefiles updated, 0 removed
73 0 files updated, 1 files merged, 0 files removed, 1 files unresolved
73 0 files updated, 1 files merged, 0 files removed, 1 files unresolved
74 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
74 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
75 [1]
75 [1]
76 $ hg status -A large1
76 $ hg status -A large1
77 M large1
77 M large1
78 $ cat large1
78 $ cat large1
79 large1 in #1
79 large1 in #1
80 $ cat .hglf/large1
80 $ cat .hglf/large1
81 58e24f733a964da346e2407a2bee99d9001184f5
81 58e24f733a964da346e2407a2bee99d9001184f5
82
82
83 Test that "hg revert -r REV" updates largefiles from "REV" correctly
83 Test that "hg revert -r REV" updates largefiles from "REV" correctly
84
84
85 $ hg update -q -C 3
85 $ hg update -q -C 3
86 $ hg status -A large1
86 $ hg status -A large1
87 C large1
87 C large1
88 $ cat large1
88 $ cat large1
89 large1 in #3
89 large1 in #3
90 $ cat .hglf/large1
90 $ cat .hglf/large1
91 e5bb990443d6a92aaf7223813720f7566c9dd05b
91 e5bb990443d6a92aaf7223813720f7566c9dd05b
92 $ hg diff -c 1 --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
92 $ hg diff -c 1 --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
93 -4669e532d5b2c093a78eca010077e708a071bb64
93 -4669e532d5b2c093a78eca010077e708a071bb64
94 +58e24f733a964da346e2407a2bee99d9001184f5
94 +58e24f733a964da346e2407a2bee99d9001184f5
95 $ hg revert --no-backup -r 1 --config debug.dirstate.delaywrite=2 large1
95 $ hg revert --no-backup -r 1 --config debug.dirstate.delaywrite=2 large1
96 $ hg status -A large1
96 $ hg status -A large1
97 M large1
97 M large1
98 $ cat large1
98 $ cat large1
99 large1 in #1
99 large1 in #1
100 $ cat .hglf/large1
100 $ cat .hglf/large1
101 58e24f733a964da346e2407a2bee99d9001184f5
101 58e24f733a964da346e2407a2bee99d9001184f5
102
102
103 Test that "hg rollback" restores status of largefiles correctly
103 Test that "hg rollback" restores status of largefiles correctly
104
104
105 $ hg update -C -q
105 $ hg update -C -q
106 $ hg remove large1
106 $ hg remove large1
107 $ test -f .hglf/large1
107 $ test -f .hglf/large1
108 [1]
108 [1]
109 $ hg forget large2
109 $ hg forget large2
110 $ test -f .hglf/large2
110 $ test -f .hglf/large2
111 [1]
111 [1]
112 $ echo largeX > largeX
112 $ echo largeX > largeX
113 $ hg add --large largeX
113 $ hg add --large largeX
114 $ cat .hglf/largeX
114 $ cat .hglf/largeX
115
115
116 $ hg commit -m 'will be rollback-ed soon'
116 $ hg commit -m 'will be rollback-ed soon'
117 $ echo largeY > largeY
117 $ echo largeY > largeY
118 $ hg add --large largeY
118 $ hg add --large largeY
119 #if windows
119 #if windows
120 $ hg status -A large1
120 $ hg status -A large1
121 large1: * (glob)
121 large1: * (glob)
122 #else
122 #else
123 $ hg status -A large1
123 $ hg status -A large1
124 large1: No such file or directory
124 large1: No such file or directory
125 #endif
125 #endif
126 $ hg status -A large2
126 $ hg status -A large2
127 ? large2
127 ? large2
128 $ hg status -A largeX
128 $ hg status -A largeX
129 C largeX
129 C largeX
130 $ hg status -A largeY
130 $ hg status -A largeY
131 A largeY
131 A largeY
132 $ hg rollback
132 $ hg rollback
133 repository tip rolled back to revision 3 (undo commit)
133 repository tip rolled back to revision 3 (undo commit)
134 working directory now based on revision 3
134 working directory now based on revision 3
135 $ hg status -A large1
135 $ hg status -A large1
136 R large1
136 R large1
137 $ test -f .hglf/large1
137 $ test -f .hglf/large1
138 [1]
138 [1]
139 $ hg status -A large2
139 $ hg status -A large2
140 R large2
140 R large2
141 $ test -f .hglf/large2
141 $ test -f .hglf/large2
142 [1]
142 [1]
143 $ hg status -A largeX
143 $ hg status -A largeX
144 A largeX
144 A largeX
145 $ cat .hglf/largeX
145 $ cat .hglf/largeX
146
146
147 $ hg status -A largeY
147 $ hg status -A largeY
148 ? largeY
148 ? largeY
149 $ test -f .hglf/largeY
149 $ test -f .hglf/largeY
150 [1]
150 [1]
151
151
152 Test that "hg rollback" restores standins correctly
152 Test that "hg rollback" restores standins correctly
153
153
154 $ hg commit -m 'will be rollback-ed soon'
154 $ hg commit -m 'will be rollback-ed soon'
155 $ hg update -q -C 2
155 $ hg update -q -C 2
156 $ cat large1
156 $ cat large1
157 large1
157 large1
158 $ cat .hglf/large1
158 $ cat .hglf/large1
159 4669e532d5b2c093a78eca010077e708a071bb64
159 4669e532d5b2c093a78eca010077e708a071bb64
160 $ cat large2
160 $ cat large2
161 large2 in #2
161 large2 in #2
162 $ cat .hglf/large2
162 $ cat .hglf/large2
163 3cfce6277e7668985707b6887ce56f9f62f6ccd9
163 3cfce6277e7668985707b6887ce56f9f62f6ccd9
164
164
165 $ hg rollback -q -f
165 $ hg rollback -q -f
166 $ cat large1
166 $ cat large1
167 large1
167 large1
168 $ cat .hglf/large1
168 $ cat .hglf/large1
169 4669e532d5b2c093a78eca010077e708a071bb64
169 4669e532d5b2c093a78eca010077e708a071bb64
170 $ cat large2
170 $ cat large2
171 large2 in #2
171 large2 in #2
172 $ cat .hglf/large2
172 $ cat .hglf/large2
173 3cfce6277e7668985707b6887ce56f9f62f6ccd9
173 3cfce6277e7668985707b6887ce56f9f62f6ccd9
174
174
175 (rollback the parent of the working directory, when the parent of it
175 (rollback the parent of the working directory, when the parent of it
176 is not branch-tip)
176 is not branch-tip)
177
177
178 $ hg update -q -C 1
178 $ hg update -q -C 1
179 $ cat .hglf/large1
179 $ cat .hglf/large1
180 58e24f733a964da346e2407a2bee99d9001184f5
180 58e24f733a964da346e2407a2bee99d9001184f5
181 $ cat .hglf/large2
181 $ cat .hglf/large2
182 1deebade43c8c498a3c8daddac0244dc55d1331d
182 1deebade43c8c498a3c8daddac0244dc55d1331d
183
183
184 $ echo normalX > normalX
184 $ echo normalX > normalX
185 $ hg add normalX
185 $ hg add normalX
186 $ hg commit -m 'will be rollback-ed soon'
186 $ hg commit -m 'will be rollback-ed soon'
187 $ hg rollback -q
187 $ hg rollback -q
188
188
189 $ cat .hglf/large1
189 $ cat .hglf/large1
190 58e24f733a964da346e2407a2bee99d9001184f5
190 58e24f733a964da346e2407a2bee99d9001184f5
191 $ cat .hglf/large2
191 $ cat .hglf/large2
192 1deebade43c8c498a3c8daddac0244dc55d1331d
192 1deebade43c8c498a3c8daddac0244dc55d1331d
193
193
194 Test that "hg status" shows status of largefiles correctly just after
194 Test that "hg status" shows status of largefiles correctly just after
195 automated commit like rebase/transplant
195 automated commit like rebase/transplant
196
196
197 $ cat >> .hg/hgrc <<EOF
197 $ cat >> .hg/hgrc <<EOF
198 > [extensions]
198 > [extensions]
199 > rebase =
199 > rebase =
200 > strip =
200 > strip =
201 > transplant =
201 > transplant =
202 > EOF
202 > EOF
203 $ hg update -q -C 1
203 $ hg update -q -C 1
204 $ hg remove large1
204 $ hg remove large1
205 $ echo largeX > largeX
205 $ echo largeX > largeX
206 $ hg add --large largeX
206 $ hg add --large largeX
207 $ hg commit -m '#4'
207 $ hg commit -m '#4'
208
208
209 $ hg rebase -s 1 -d 2 --keep
209 $ hg rebase -s 1 -d 2 --keep
210 rebasing 1:72518492caa6 "#1"
210 rebasing 1:72518492caa6 "#1"
211 rebasing 4:07d6153b5c04 "#4" (tip)
211 rebasing 4:07d6153b5c04 "#4" (tip)
212 #if windows
212 #if windows
213 $ hg status -A large1
213 $ hg status -A large1
214 large1: * (glob)
214 large1: * (glob)
215 #else
215 #else
216 $ hg status -A large1
216 $ hg status -A large1
217 large1: No such file or directory
217 large1: No such file or directory
218 #endif
218 #endif
219 $ hg status -A largeX
219 $ hg status -A largeX
220 C largeX
220 C largeX
221 $ hg strip -q 5
221 $ hg strip -q 5
222
222
223 $ hg update -q -C 2
223 $ hg update -q -C 2
224 $ hg transplant -q 1 4
224 $ hg transplant -q 1 4
225 #if windows
225 #if windows
226 $ hg status -A large1
226 $ hg status -A large1
227 large1: * (glob)
227 large1: * (glob)
228 #else
228 #else
229 $ hg status -A large1
229 $ hg status -A large1
230 large1: No such file or directory
230 large1: No such file or directory
231 #endif
231 #endif
232 $ hg status -A largeX
232 $ hg status -A largeX
233 C largeX
233 C largeX
234 $ hg strip -q 5
234 $ hg strip -q 5
235
235
236 $ hg update -q -C 2
236 $ hg update -q -C 2
237 $ hg transplant -q --merge 1 --merge 4
237 $ hg transplant -q --merge 1 --merge 4
238 #if windows
238 #if windows
239 $ hg status -A large1
239 $ hg status -A large1
240 large1: * (glob)
240 large1: * (glob)
241 #else
241 #else
242 $ hg status -A large1
242 $ hg status -A large1
243 large1: No such file or directory
243 large1: No such file or directory
244 #endif
244 #endif
245 $ hg status -A largeX
245 $ hg status -A largeX
246 C largeX
246 C largeX
247 $ hg strip -q 5
247 $ hg strip -q 5
248
248
249 Test that linear merge can detect modification (and conflict) correctly
249 Test that linear merge can detect modification (and conflict) correctly
250
250
251 (linear merge without conflict)
251 (linear merge without conflict)
252
252
253 $ echo 'large2 for linear merge (no conflict)' > large2
253 $ echo 'large2 for linear merge (no conflict)' > large2
254 $ hg update 3 --config debug.dirstate.delaywrite=2
254 $ hg update 3 --config debug.dirstate.delaywrite=2
255 getting changed largefiles
255 getting changed largefiles
256 1 largefiles updated, 0 removed
256 1 largefiles updated, 0 removed
257 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
257 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
258 $ hg status -A large2
258 $ hg status -A large2
259 M large2
259 M large2
260 $ cat large2
260 $ cat large2
261 large2 for linear merge (no conflict)
261 large2 for linear merge (no conflict)
262 $ cat .hglf/large2
262 $ cat .hglf/large2
263 9c4bf8f1b33536d6e5f89447e10620cfe52ea710
263 9c4bf8f1b33536d6e5f89447e10620cfe52ea710
264
264
265 (linear merge with conflict, choosing "other")
265 (linear merge with conflict, choosing "other")
266
266
267 $ hg update -q -C 2
267 $ hg update -q -C 2
268 $ echo 'large1 for linear merge (conflict)' > large1
268 $ echo 'large1 for linear merge (conflict)' > large1
269 $ hg update 3 --config ui.interactive=True <<EOF
269 $ hg update 3 --config ui.interactive=True <<EOF
270 > o
270 > o
271 > EOF
271 > EOF
272 largefile large1 has a merge conflict
272 largefile large1 has a merge conflict
273 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
273 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
274 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
274 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
275 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? o
275 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? o
276 getting changed largefiles
276 getting changed largefiles
277 1 largefiles updated, 0 removed
277 1 largefiles updated, 0 removed
278 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
278 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
279 $ hg status -A large1
279 $ hg status -A large1
280 C large1
280 C large1
281 $ cat large1
281 $ cat large1
282 large1 in #3
282 large1 in #3
283 $ cat .hglf/large1
283 $ cat .hglf/large1
284 e5bb990443d6a92aaf7223813720f7566c9dd05b
284 e5bb990443d6a92aaf7223813720f7566c9dd05b
285
285
286 (linear merge with conflict, choosing "local")
286 (linear merge with conflict, choosing "local")
287
287
288 $ hg update -q -C 2
288 $ hg update -q -C 2
289 $ echo 'large1 for linear merge (conflict)' > large1
289 $ echo 'large1 for linear merge (conflict)' > large1
290 $ hg update 3 --config debug.dirstate.delaywrite=2
290 $ hg update 3 --config debug.dirstate.delaywrite=2
291 largefile large1 has a merge conflict
291 largefile large1 has a merge conflict
292 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
292 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
293 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
293 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
294 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
294 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
295 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
295 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
296 $ hg status -A large1
296 $ hg status -A large1
297 M large1
297 M large1
298 $ cat large1
298 $ cat large1
299 large1 for linear merge (conflict)
299 large1 for linear merge (conflict)
300 $ cat .hglf/large1
300 $ cat .hglf/large1
301 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
301 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
302
302
303 Test a linear merge to a revision containing same-name normal file
303 Test a linear merge to a revision containing same-name normal file
304
304
305 $ hg update -q -C 3
305 $ hg update -q -C 3
306 $ hg remove large2
306 $ hg remove large2
307 $ echo 'large2 as normal file' > large2
307 $ echo 'large2 as normal file' > large2
308 $ hg add large2
308 $ hg add large2
309 $ echo 'large3 as normal file' > large3
309 $ echo 'large3 as normal file' > large3
310 $ hg add large3
310 $ hg add large3
311 $ hg commit -m '#5'
311 $ hg commit -m '#5'
312 $ hg manifest
312 $ hg manifest
313 .hglf/large1
313 .hglf/large1
314 large2
314 large2
315 large3
315 large3
316 normal1
316 normal1
317
317
318 (modified largefile is already switched to normal)
318 (modified largefile is already switched to normal)
319
319
320 $ hg update -q -C 2
320 $ hg update -q -C 2
321 $ echo 'modified large2 for linear merge' > large2
321 $ echo 'modified large2 for linear merge' > large2
322 $ hg update -q 5
322 $ hg update -q 5
323 local changed .hglf/large2 which remote deleted
324 use (c)hanged version or (d)elete? c
325 remote turned local largefile large2 into a normal file
323 remote turned local largefile large2 into a normal file
326 keep (l)argefile or use (n)ormal file? l
324 keep (l)argefile or use (n)ormal file? l
327 $ hg debugdirstate --nodates | grep large2
325 $ hg debugdirstate --nodates | grep large2
328 a 0 -1 .hglf/large2
326 a 0 -1 .hglf/large2
329 r 0 0 large2
327 r 0 0 large2
330 $ hg status -A large2
328 $ hg status -A large2
331 A large2
329 A large2
332 $ cat large2
330 $ cat large2
333 modified large2 for linear merge
331 modified large2 for linear merge
334
332
335 (added largefile is already committed as normal)
333 (added largefile is already committed as normal)
336
334
337 $ hg update -q -C 2
335 $ hg update -q -C 2
338 $ echo 'large3 as large file for linear merge' > large3
336 $ echo 'large3 as large file for linear merge' > large3
339 $ hg add --large large3
337 $ hg add --large large3
340 $ hg update -q 5
338 $ hg update -q 5
341 remote turned local largefile large3 into a normal file
339 remote turned local largefile large3 into a normal file
342 keep (l)argefile or use (n)ormal file? l
340 keep (l)argefile or use (n)ormal file? l
343 $ hg debugdirstate --nodates | grep large3
341 $ hg debugdirstate --nodates | grep large3
344 a 0 -1 .hglf/large3
342 a 0 -1 .hglf/large3
345 r 0 0 large3
343 r 0 0 large3
346 $ hg status -A large3
344 $ hg status -A large3
347 A large3
345 A large3
348 $ cat large3
346 $ cat large3
349 large3 as large file for linear merge
347 large3 as large file for linear merge
350 $ rm -f large3 .hglf/large3
348 $ rm -f large3 .hglf/large3
351
349
352 Test that the internal linear merging works correctly
350 Test that the internal linear merging works correctly
353 (both heads are stripped to keep pairing of revision number and commit log)
351 (both heads are stripped to keep pairing of revision number and commit log)
354
352
355 $ hg update -q -C 2
353 $ hg update -q -C 2
356 $ hg strip 3 4
354 $ hg strip 3 4
357 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/9530e27857f7-backup.hg (glob)
355 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/9530e27857f7-backup.hg (glob)
358 $ mv .hg/strip-backup/9530e27857f7-backup.hg $TESTTMP
356 $ mv .hg/strip-backup/9530e27857f7-backup.hg $TESTTMP
359
357
360 (internal linear merging at "hg pull --update")
358 (internal linear merging at "hg pull --update")
361
359
362 $ echo 'large1 for linear merge (conflict)' > large1
360 $ echo 'large1 for linear merge (conflict)' > large1
363 $ echo 'large2 for linear merge (conflict with normal file)' > large2
361 $ echo 'large2 for linear merge (conflict with normal file)' > large2
364 $ hg pull --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-backup.hg
362 $ hg pull --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-backup.hg
365 pulling from $TESTTMP/9530e27857f7-backup.hg (glob)
363 pulling from $TESTTMP/9530e27857f7-backup.hg (glob)
366 searching for changes
364 searching for changes
367 adding changesets
365 adding changesets
368 adding manifests
366 adding manifests
369 adding file changes
367 adding file changes
370 added 3 changesets with 5 changes to 5 files
368 added 3 changesets with 5 changes to 5 files
371 local changed .hglf/large2 which remote deleted
372 use (c)hanged version or (d)elete? c
373 remote turned local largefile large2 into a normal file
369 remote turned local largefile large2 into a normal file
374 keep (l)argefile or use (n)ormal file? l
370 keep (l)argefile or use (n)ormal file? l
375 largefile large1 has a merge conflict
371 largefile large1 has a merge conflict
376 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
372 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
377 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
373 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
378 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
374 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
379 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
375 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
380
376
381 $ hg status -A large1
377 $ hg status -A large1
382 M large1
378 M large1
383 $ cat large1
379 $ cat large1
384 large1 for linear merge (conflict)
380 large1 for linear merge (conflict)
385 $ cat .hglf/large1
381 $ cat .hglf/large1
386 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
382 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
387 $ hg status -A large2
383 $ hg status -A large2
388 A large2
384 A large2
389 $ cat large2
385 $ cat large2
390 large2 for linear merge (conflict with normal file)
386 large2 for linear merge (conflict with normal file)
391 $ cat .hglf/large2
387 $ cat .hglf/large2
392 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
388 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
393
389
394 (internal linear merging at "hg unbundle --update")
390 (internal linear merging at "hg unbundle --update")
395
391
396 $ hg update -q -C 2
392 $ hg update -q -C 2
397 $ hg rollback -q
393 $ hg rollback -q
398
394
399 $ echo 'large1 for linear merge (conflict)' > large1
395 $ echo 'large1 for linear merge (conflict)' > large1
400 $ echo 'large2 for linear merge (conflict with normal file)' > large2
396 $ echo 'large2 for linear merge (conflict with normal file)' > large2
401 $ hg unbundle --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-backup.hg
397 $ hg unbundle --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-backup.hg
402 adding changesets
398 adding changesets
403 adding manifests
399 adding manifests
404 adding file changes
400 adding file changes
405 added 3 changesets with 5 changes to 5 files
401 added 3 changesets with 5 changes to 5 files
406 local changed .hglf/large2 which remote deleted
407 use (c)hanged version or (d)elete? c
408 remote turned local largefile large2 into a normal file
402 remote turned local largefile large2 into a normal file
409 keep (l)argefile or use (n)ormal file? l
403 keep (l)argefile or use (n)ormal file? l
410 largefile large1 has a merge conflict
404 largefile large1 has a merge conflict
411 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
405 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
412 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
406 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
413 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
407 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
414 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
408 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
415
409
416 $ hg status -A large1
410 $ hg status -A large1
417 M large1
411 M large1
418 $ cat large1
412 $ cat large1
419 large1 for linear merge (conflict)
413 large1 for linear merge (conflict)
420 $ cat .hglf/large1
414 $ cat .hglf/large1
421 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
415 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
422 $ hg status -A large2
416 $ hg status -A large2
423 A large2
417 A large2
424 $ cat large2
418 $ cat large2
425 large2 for linear merge (conflict with normal file)
419 large2 for linear merge (conflict with normal file)
426 $ cat .hglf/large2
420 $ cat .hglf/large2
427 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
421 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
428
422
429 (internal linear merging in subrepo at "hg update")
423 (internal linear merging in subrepo at "hg update")
430
424
431 $ cd ..
425 $ cd ..
432 $ hg init subparent
426 $ hg init subparent
433 $ cd subparent
427 $ cd subparent
434
428
435 $ hg clone -q -u 2 ../repo sub
429 $ hg clone -q -u 2 ../repo sub
436 $ cat > .hgsub <<EOF
430 $ cat > .hgsub <<EOF
437 > sub = sub
431 > sub = sub
438 > EOF
432 > EOF
439 $ hg add .hgsub
433 $ hg add .hgsub
440 $ hg commit -m '#0@parent'
434 $ hg commit -m '#0@parent'
441 $ cat .hgsubstate
435 $ cat .hgsubstate
442 f74e50bd9e5594b7cf1e6c5cbab86ddd25f3ca2f sub
436 f74e50bd9e5594b7cf1e6c5cbab86ddd25f3ca2f sub
443 $ hg -R sub update -q
437 $ hg -R sub update -q
444 $ hg commit -m '#1@parent'
438 $ hg commit -m '#1@parent'
445 $ cat .hgsubstate
439 $ cat .hgsubstate
446 d65e59e952a9638e2ce863b41a420ca723dd3e8d sub
440 d65e59e952a9638e2ce863b41a420ca723dd3e8d sub
447 $ hg update -q 0
441 $ hg update -q 0
448
442
449 $ echo 'large1 for linear merge (conflict)' > sub/large1
443 $ echo 'large1 for linear merge (conflict)' > sub/large1
450 $ echo 'large2 for linear merge (conflict with normal file)' > sub/large2
444 $ echo 'large2 for linear merge (conflict with normal file)' > sub/large2
451 $ hg update --config ui.interactive=True --config debug.dirstate.delaywrite=2 <<EOF
445 $ hg update --config ui.interactive=True --config debug.dirstate.delaywrite=2 <<EOF
452 > m
446 > m
453 > r
447 > r
454 > c
455 > l
448 > l
456 > l
449 > l
457 > EOF
450 > EOF
458 subrepository sub diverged (local revision: f74e50bd9e55, remote revision: d65e59e952a9)
451 subrepository sub diverged (local revision: f74e50bd9e55, remote revision: d65e59e952a9)
459 (M)erge, keep (l)ocal or keep (r)emote? m
452 (M)erge, keep (l)ocal or keep (r)emote? m
460 subrepository sources for sub differ (in checked out version)
453 subrepository sources for sub differ (in checked out version)
461 use (l)ocal source (f74e50bd9e55) or (r)emote source (d65e59e952a9)? r
454 use (l)ocal source (f74e50bd9e55) or (r)emote source (d65e59e952a9)? r
462 local changed .hglf/large2 which remote deleted
463 use (c)hanged version or (d)elete? c
464 remote turned local largefile large2 into a normal file
455 remote turned local largefile large2 into a normal file
465 keep (l)argefile or use (n)ormal file? l
456 keep (l)argefile or use (n)ormal file? l
466 largefile large1 has a merge conflict
457 largefile large1 has a merge conflict
467 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
458 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
468 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
459 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
469 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
460 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
470 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
461 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
471 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
462 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
472
463
473 $ hg -R sub status -A sub/large1
464 $ hg -R sub status -A sub/large1
474 M sub/large1
465 M sub/large1
475 $ cat sub/large1
466 $ cat sub/large1
476 large1 for linear merge (conflict)
467 large1 for linear merge (conflict)
477 $ cat sub/.hglf/large1
468 $ cat sub/.hglf/large1
478 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
469 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
479 $ hg -R sub status -A sub/large2
470 $ hg -R sub status -A sub/large2
480 A sub/large2
471 A sub/large2
481 $ cat sub/large2
472 $ cat sub/large2
482 large2 for linear merge (conflict with normal file)
473 large2 for linear merge (conflict with normal file)
483 $ cat sub/.hglf/large2
474 $ cat sub/.hglf/large2
484 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
475 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
485
476
486 $ cd ..
477 $ cd ..
487 $ cd repo
478 $ cd repo
488
479
489 Test that rebase updates largefiles in the working directory even if
480 Test that rebase updates largefiles in the working directory even if
490 it is aborted by conflict.
481 it is aborted by conflict.
491
482
492 $ hg update -q -C 3
483 $ hg update -q -C 3
493 $ cat .hglf/large1
484 $ cat .hglf/large1
494 e5bb990443d6a92aaf7223813720f7566c9dd05b
485 e5bb990443d6a92aaf7223813720f7566c9dd05b
495 $ cat large1
486 $ cat large1
496 large1 in #3
487 large1 in #3
497 $ hg rebase -s 1 -d 3 --keep --config ui.interactive=True <<EOF
488 $ hg rebase -s 1 -d 3 --keep --config ui.interactive=True <<EOF
498 > o
489 > o
499 > EOF
490 > EOF
500 rebasing 1:72518492caa6 "#1"
491 rebasing 1:72518492caa6 "#1"
501 largefile large1 has a merge conflict
492 largefile large1 has a merge conflict
502 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
493 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
503 keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or
494 keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or
504 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
495 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
505 merging normal1
496 merging normal1
506 warning: conflicts during merge.
497 warning: conflicts during merge.
507 merging normal1 incomplete! (edit conflicts, then use 'hg resolve --mark')
498 merging normal1 incomplete! (edit conflicts, then use 'hg resolve --mark')
508 unresolved conflicts (see hg resolve, then hg rebase --continue)
499 unresolved conflicts (see hg resolve, then hg rebase --continue)
509 [1]
500 [1]
510 $ cat .hglf/large1
501 $ cat .hglf/large1
511 58e24f733a964da346e2407a2bee99d9001184f5
502 58e24f733a964da346e2407a2bee99d9001184f5
512 $ cat large1
503 $ cat large1
513 large1 in #1
504 large1 in #1
514
505
515 Test that rebase updates standins for manually modified largefiles at
506 Test that rebase updates standins for manually modified largefiles at
516 the 1st commit of resuming.
507 the 1st commit of resuming.
517
508
518 $ echo "manually modified before 'hg rebase --continue'" > large1
509 $ echo "manually modified before 'hg rebase --continue'" > large1
519 $ hg resolve -m normal1
510 $ hg resolve -m normal1
520 (no more unresolved files)
511 (no more unresolved files)
521 $ hg rebase --continue --config ui.interactive=True <<EOF
512 $ hg rebase --continue --config ui.interactive=True <<EOF
522 > c
513 > c
523 > EOF
514 > EOF
524 rebasing 1:72518492caa6 "#1"
515 rebasing 1:72518492caa6 "#1"
525 rebasing 4:07d6153b5c04 "#4"
516 rebasing 4:07d6153b5c04 "#4"
526 local changed .hglf/large1 which remote deleted
517 local changed .hglf/large1 which remote deleted
527 use (c)hanged version or (d)elete? c
518 use (c)hanged version or (d)elete? c
528
519
529 $ hg diff -c "tip~1" --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
520 $ hg diff -c "tip~1" --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
530 -e5bb990443d6a92aaf7223813720f7566c9dd05b
521 -e5bb990443d6a92aaf7223813720f7566c9dd05b
531 +8a4f783556e7dea21139ca0466eafce954c75c13
522 +8a4f783556e7dea21139ca0466eafce954c75c13
532 $ rm -f large1
523 $ rm -f large1
533 $ hg update -q -C tip
524 $ hg update -q -C tip
534 $ cat large1
525 $ cat large1
535 manually modified before 'hg rebase --continue'
526 manually modified before 'hg rebase --continue'
536
527
537 Test that transplant updates largefiles, of which standins are safely
528 Test that transplant updates largefiles, of which standins are safely
538 changed, even if it is aborted by conflict of other.
529 changed, even if it is aborted by conflict of other.
539
530
540 $ hg update -q -C 5
531 $ hg update -q -C 5
541 $ cat .hglf/large1
532 $ cat .hglf/large1
542 e5bb990443d6a92aaf7223813720f7566c9dd05b
533 e5bb990443d6a92aaf7223813720f7566c9dd05b
543 $ cat large1
534 $ cat large1
544 large1 in #3
535 large1 in #3
545 $ hg diff -c 4 .hglf/largeX | grep '^[+-][0-9a-z]'
536 $ hg diff -c 4 .hglf/largeX | grep '^[+-][0-9a-z]'
546 +fa44618ea25181aff4f48b70428294790cec9f61
537 +fa44618ea25181aff4f48b70428294790cec9f61
547 $ hg transplant 4
538 $ hg transplant 4
548 applying 07d6153b5c04
539 applying 07d6153b5c04
549 patching file .hglf/large1
540 patching file .hglf/large1
550 Hunk #1 FAILED at 0
541 Hunk #1 FAILED at 0
551 1 out of 1 hunks FAILED -- saving rejects to file .hglf/large1.rej
542 1 out of 1 hunks FAILED -- saving rejects to file .hglf/large1.rej
552 patch failed to apply
543 patch failed to apply
553 abort: fix up the merge and run hg transplant --continue
544 abort: fix up the merge and run hg transplant --continue
554 [255]
545 [255]
555 $ hg status -A large1
546 $ hg status -A large1
556 C large1
547 C large1
557 $ cat .hglf/large1
548 $ cat .hglf/large1
558 e5bb990443d6a92aaf7223813720f7566c9dd05b
549 e5bb990443d6a92aaf7223813720f7566c9dd05b
559 $ cat large1
550 $ cat large1
560 large1 in #3
551 large1 in #3
561 $ hg status -A largeX
552 $ hg status -A largeX
562 A largeX
553 A largeX
563 $ cat .hglf/largeX
554 $ cat .hglf/largeX
564 fa44618ea25181aff4f48b70428294790cec9f61
555 fa44618ea25181aff4f48b70428294790cec9f61
565 $ cat largeX
556 $ cat largeX
566 largeX
557 largeX
567
558
568 Test that transplant updates standins for manually modified largefiles
559 Test that transplant updates standins for manually modified largefiles
569 at the 1st commit of resuming.
560 at the 1st commit of resuming.
570
561
571 $ echo "manually modified before 'hg transplant --continue'" > large1
562 $ echo "manually modified before 'hg transplant --continue'" > large1
572 $ hg transplant --continue
563 $ hg transplant --continue
573 07d6153b5c04 transplanted as f1bf30eb88cc
564 07d6153b5c04 transplanted as f1bf30eb88cc
574 $ hg diff -c tip .hglf/large1 | grep '^[+-][0-9a-z]'
565 $ hg diff -c tip .hglf/large1 | grep '^[+-][0-9a-z]'
575 -e5bb990443d6a92aaf7223813720f7566c9dd05b
566 -e5bb990443d6a92aaf7223813720f7566c9dd05b
576 +6a4f36d4075fbe0f30ec1d26ca44e63c05903671
567 +6a4f36d4075fbe0f30ec1d26ca44e63c05903671
577 $ rm -f large1
568 $ rm -f large1
578 $ hg update -q -C tip
569 $ hg update -q -C tip
579 $ cat large1
570 $ cat large1
580 manually modified before 'hg transplant --continue'
571 manually modified before 'hg transplant --continue'
581
572
582 Test that "hg status" doesn't show removal of largefiles not managed
573 Test that "hg status" doesn't show removal of largefiles not managed
583 in the target context.
574 in the target context.
584
575
585 $ hg update -q -C 4
576 $ hg update -q -C 4
586 $ hg remove largeX
577 $ hg remove largeX
587 $ hg status -A largeX
578 $ hg status -A largeX
588 R largeX
579 R largeX
589 $ hg status -A --rev '.^1' largeX
580 $ hg status -A --rev '.^1' largeX
590
581
591 #if execbit
582 #if execbit
592
583
593 Test that "hg status" against revisions other than parent notices exec
584 Test that "hg status" against revisions other than parent notices exec
594 bit changes of largefiles.
585 bit changes of largefiles.
595
586
596 $ hg update -q -C 4
587 $ hg update -q -C 4
597
588
598 (the case that large2 doesn't have exec bit in the target context but
589 (the case that large2 doesn't have exec bit in the target context but
599 in the working context)
590 in the working context)
600
591
601 $ chmod +x large2
592 $ chmod +x large2
602 $ hg status -A --rev 0 large2
593 $ hg status -A --rev 0 large2
603 M large2
594 M large2
604 $ hg commit -m 'chmod +x large2'
595 $ hg commit -m 'chmod +x large2'
605
596
606 (the case that large2 has exec bit in the target context but not in
597 (the case that large2 has exec bit in the target context but not in
607 the working context)
598 the working context)
608
599
609 $ echo dummy > dummy
600 $ echo dummy > dummy
610 $ hg add dummy
601 $ hg add dummy
611 $ hg commit -m 'revision for separation'
602 $ hg commit -m 'revision for separation'
612 $ chmod -x large2
603 $ chmod -x large2
613 $ hg status -A --rev '.^1' large2
604 $ hg status -A --rev '.^1' large2
614 M large2
605 M large2
615
606
616 #else
607 #else
617
608
618 Test that "hg status" against revisions other than parent ignores exec
609 Test that "hg status" against revisions other than parent ignores exec
619 bit correctly on the platform being unaware of it.
610 bit correctly on the platform being unaware of it.
620
611
621 $ hg update -q -C 4
612 $ hg update -q -C 4
622
613
623 $ cat > exec-bit.patch <<EOF
614 $ cat > exec-bit.patch <<EOF
624 > # HG changeset patch
615 > # HG changeset patch
625 > # User test
616 > # User test
626 > # Date 0 0
617 > # Date 0 0
627 > # Thu Jan 01 00:00:00 1970 +0000
618 > # Thu Jan 01 00:00:00 1970 +0000
628 > # Node ID be1b433a65b12b27b5519d92213e14f7e1769b90
619 > # Node ID be1b433a65b12b27b5519d92213e14f7e1769b90
629 > # Parent 07d6153b5c04313efb75deec9ba577de7faeb727
620 > # Parent 07d6153b5c04313efb75deec9ba577de7faeb727
630 > chmod +x large2
621 > chmod +x large2
631 >
622 >
632 > diff --git a/.hglf/large2 b/.hglf/large2
623 > diff --git a/.hglf/large2 b/.hglf/large2
633 > old mode 100644
624 > old mode 100644
634 > new mode 100755
625 > new mode 100755
635 > EOF
626 > EOF
636 $ hg import --exact --bypass exec-bit.patch
627 $ hg import --exact --bypass exec-bit.patch
637 applying exec-bit.patch
628 applying exec-bit.patch
638 $ hg status -A --rev tip large2
629 $ hg status -A --rev tip large2
639 C large2
630 C large2
640
631
641 #endif
632 #endif
642
633
643 $ cd ..
634 $ cd ..
644
635
645 Test that "hg convert" avoids copying largefiles from the working
636 Test that "hg convert" avoids copying largefiles from the working
646 directory into store, because "hg convert" doesn't update largefiles
637 directory into store, because "hg convert" doesn't update largefiles
647 in the working directory (removing files under ".cache/largefiles"
638 in the working directory (removing files under ".cache/largefiles"
648 forces "hg convert" to copy corresponding largefiles)
639 forces "hg convert" to copy corresponding largefiles)
649
640
650 $ cat >> $HGRCPATH <<EOF
641 $ cat >> $HGRCPATH <<EOF
651 > [extensions]
642 > [extensions]
652 > convert =
643 > convert =
653 > EOF
644 > EOF
654
645
655 $ rm $TESTTMP/.cache/largefiles/6a4f36d4075fbe0f30ec1d26ca44e63c05903671
646 $ rm $TESTTMP/.cache/largefiles/6a4f36d4075fbe0f30ec1d26ca44e63c05903671
656 $ hg convert -q repo repo.converted
647 $ hg convert -q repo repo.converted
General Comments 0
You need to be logged in to leave comments. Login now