##// END OF EJS Templates
merge: refactor unknown file conflict checking...
Matt Mackall -
r16093:7e30f5f2 default
parent child Browse files
Show More
@@ -1,964 +1,961 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
10
10
11 import os
11 import os
12 import copy
12 import copy
13
13
14 from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
14 from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
15 node, archival, error, merge
15 node, archival, error, merge
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17 from mercurial.node import hex
17 from mercurial.node import hex
18 from hgext import rebase
18 from hgext import rebase
19
19
20 import lfutil
20 import lfutil
21 import lfcommands
21 import lfcommands
22
22
23 # -- Utility functions: commonly/repeatedly needed functionality ---------------
23 # -- Utility functions: commonly/repeatedly needed functionality ---------------
24
24
25 def installnormalfilesmatchfn(manifest):
25 def installnormalfilesmatchfn(manifest):
26 '''overrides scmutil.match so that the matcher it returns will ignore all
26 '''overrides scmutil.match so that the matcher it returns will ignore all
27 largefiles'''
27 largefiles'''
28 oldmatch = None # for the closure
28 oldmatch = None # for the closure
29 def override_match(ctx, pats=[], opts={}, globbed=False,
29 def override_match(ctx, pats=[], opts={}, globbed=False,
30 default='relpath'):
30 default='relpath'):
31 match = oldmatch(ctx, pats, opts, globbed, default)
31 match = oldmatch(ctx, pats, opts, globbed, default)
32 m = copy.copy(match)
32 m = copy.copy(match)
33 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
33 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
34 manifest)
34 manifest)
35 m._files = filter(notlfile, m._files)
35 m._files = filter(notlfile, m._files)
36 m._fmap = set(m._files)
36 m._fmap = set(m._files)
37 orig_matchfn = m.matchfn
37 orig_matchfn = m.matchfn
38 m.matchfn = lambda f: notlfile(f) and orig_matchfn(f) or None
38 m.matchfn = lambda f: notlfile(f) and orig_matchfn(f) or None
39 return m
39 return m
40 oldmatch = installmatchfn(override_match)
40 oldmatch = installmatchfn(override_match)
41
41
42 def installmatchfn(f):
42 def installmatchfn(f):
43 oldmatch = scmutil.match
43 oldmatch = scmutil.match
44 setattr(f, 'oldmatch', oldmatch)
44 setattr(f, 'oldmatch', oldmatch)
45 scmutil.match = f
45 scmutil.match = f
46 return oldmatch
46 return oldmatch
47
47
48 def restorematchfn():
48 def restorematchfn():
49 '''restores scmutil.match to what it was before installnormalfilesmatchfn
49 '''restores scmutil.match to what it was before installnormalfilesmatchfn
50 was called. no-op if scmutil.match is its original function.
50 was called. no-op if scmutil.match is its original function.
51
51
52 Note that n calls to installnormalfilesmatchfn will require n calls to
52 Note that n calls to installnormalfilesmatchfn will require n calls to
53 restore matchfn to reverse'''
53 restore matchfn to reverse'''
54 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
54 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
55
55
56 def add_largefiles(ui, repo, *pats, **opts):
56 def add_largefiles(ui, repo, *pats, **opts):
57 large = opts.pop('large', None)
57 large = opts.pop('large', None)
58 lfsize = lfutil.getminsize(
58 lfsize = lfutil.getminsize(
59 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
59 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
60
60
61 lfmatcher = None
61 lfmatcher = None
62 if lfutil.islfilesrepo(repo):
62 if lfutil.islfilesrepo(repo):
63 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
63 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
64 if lfpats:
64 if lfpats:
65 lfmatcher = match_.match(repo.root, '', list(lfpats))
65 lfmatcher = match_.match(repo.root, '', list(lfpats))
66
66
67 lfnames = []
67 lfnames = []
68 m = scmutil.match(repo[None], pats, opts)
68 m = scmutil.match(repo[None], pats, opts)
69 m.bad = lambda x, y: None
69 m.bad = lambda x, y: None
70 wctx = repo[None]
70 wctx = repo[None]
71 for f in repo.walk(m):
71 for f in repo.walk(m):
72 exact = m.exact(f)
72 exact = m.exact(f)
73 lfile = lfutil.standin(f) in wctx
73 lfile = lfutil.standin(f) in wctx
74 nfile = f in wctx
74 nfile = f in wctx
75 exists = lfile or nfile
75 exists = lfile or nfile
76
76
77 # Don't warn the user when they attempt to add a normal tracked file.
77 # Don't warn the user when they attempt to add a normal tracked file.
78 # The normal add code will do that for us.
78 # The normal add code will do that for us.
79 if exact and exists:
79 if exact and exists:
80 if lfile:
80 if lfile:
81 ui.warn(_('%s already a largefile\n') % f)
81 ui.warn(_('%s already a largefile\n') % f)
82 continue
82 continue
83
83
84 if exact or not exists:
84 if exact or not exists:
85 abovemin = (lfsize and
85 abovemin = (lfsize and
86 os.lstat(repo.wjoin(f)).st_size >= lfsize * 1024 * 1024)
86 os.lstat(repo.wjoin(f)).st_size >= lfsize * 1024 * 1024)
87 if large or abovemin or (lfmatcher and lfmatcher(f)):
87 if large or abovemin or (lfmatcher and lfmatcher(f)):
88 lfnames.append(f)
88 lfnames.append(f)
89 if ui.verbose or not exact:
89 if ui.verbose or not exact:
90 ui.status(_('adding %s as a largefile\n') % m.rel(f))
90 ui.status(_('adding %s as a largefile\n') % m.rel(f))
91
91
92 bad = []
92 bad = []
93 standins = []
93 standins = []
94
94
95 # Need to lock, otherwise there could be a race condition between
95 # Need to lock, otherwise there could be a race condition between
96 # when standins are created and added to the repo.
96 # when standins are created and added to the repo.
97 wlock = repo.wlock()
97 wlock = repo.wlock()
98 try:
98 try:
99 if not opts.get('dry_run'):
99 if not opts.get('dry_run'):
100 lfdirstate = lfutil.openlfdirstate(ui, repo)
100 lfdirstate = lfutil.openlfdirstate(ui, repo)
101 for f in lfnames:
101 for f in lfnames:
102 standinname = lfutil.standin(f)
102 standinname = lfutil.standin(f)
103 lfutil.writestandin(repo, standinname, hash='',
103 lfutil.writestandin(repo, standinname, hash='',
104 executable=lfutil.getexecutable(repo.wjoin(f)))
104 executable=lfutil.getexecutable(repo.wjoin(f)))
105 standins.append(standinname)
105 standins.append(standinname)
106 if lfdirstate[f] == 'r':
106 if lfdirstate[f] == 'r':
107 lfdirstate.normallookup(f)
107 lfdirstate.normallookup(f)
108 else:
108 else:
109 lfdirstate.add(f)
109 lfdirstate.add(f)
110 lfdirstate.write()
110 lfdirstate.write()
111 bad += [lfutil.splitstandin(f)
111 bad += [lfutil.splitstandin(f)
112 for f in lfutil.repo_add(repo, standins)
112 for f in lfutil.repo_add(repo, standins)
113 if f in m.files()]
113 if f in m.files()]
114 finally:
114 finally:
115 wlock.release()
115 wlock.release()
116 return bad
116 return bad
117
117
118 def remove_largefiles(ui, repo, *pats, **opts):
118 def remove_largefiles(ui, repo, *pats, **opts):
119 after = opts.get('after')
119 after = opts.get('after')
120 if not pats and not after:
120 if not pats and not after:
121 raise util.Abort(_('no files specified'))
121 raise util.Abort(_('no files specified'))
122 m = scmutil.match(repo[None], pats, opts)
122 m = scmutil.match(repo[None], pats, opts)
123 try:
123 try:
124 repo.lfstatus = True
124 repo.lfstatus = True
125 s = repo.status(match=m, clean=True)
125 s = repo.status(match=m, clean=True)
126 finally:
126 finally:
127 repo.lfstatus = False
127 repo.lfstatus = False
128 manifest = repo[None].manifest()
128 manifest = repo[None].manifest()
129 modified, added, deleted, clean = [[f for f in list
129 modified, added, deleted, clean = [[f for f in list
130 if lfutil.standin(f) in manifest]
130 if lfutil.standin(f) in manifest]
131 for list in [s[0], s[1], s[3], s[6]]]
131 for list in [s[0], s[1], s[3], s[6]]]
132
132
133 def warn(files, reason):
133 def warn(files, reason):
134 for f in files:
134 for f in files:
135 ui.warn(_('not removing %s: %s (use forget to undo)\n')
135 ui.warn(_('not removing %s: %s (use forget to undo)\n')
136 % (m.rel(f), reason))
136 % (m.rel(f), reason))
137
137
138 if after:
138 if after:
139 remove, forget = deleted, []
139 remove, forget = deleted, []
140 warn(modified + added + clean, _('file still exists'))
140 warn(modified + added + clean, _('file still exists'))
141 else:
141 else:
142 remove, forget = deleted + clean, []
142 remove, forget = deleted + clean, []
143 warn(modified, _('file is modified'))
143 warn(modified, _('file is modified'))
144 warn(added, _('file has been marked for add'))
144 warn(added, _('file has been marked for add'))
145
145
146 for f in sorted(remove + forget):
146 for f in sorted(remove + forget):
147 if ui.verbose or not m.exact(f):
147 if ui.verbose or not m.exact(f):
148 ui.status(_('removing %s\n') % m.rel(f))
148 ui.status(_('removing %s\n') % m.rel(f))
149
149
150 # Need to lock because standin files are deleted then removed from the
150 # Need to lock because standin files are deleted then removed from the
151 # repository and we could race inbetween.
151 # repository and we could race inbetween.
152 wlock = repo.wlock()
152 wlock = repo.wlock()
153 try:
153 try:
154 lfdirstate = lfutil.openlfdirstate(ui, repo)
154 lfdirstate = lfutil.openlfdirstate(ui, repo)
155 for f in remove:
155 for f in remove:
156 if not after:
156 if not after:
157 # If this is being called by addremove, notify the user that we
157 # If this is being called by addremove, notify the user that we
158 # are removing the file.
158 # are removing the file.
159 if getattr(repo, "_isaddremove", False):
159 if getattr(repo, "_isaddremove", False):
160 ui.status(_('removing %s\n' % f))
160 ui.status(_('removing %s\n' % f))
161 if os.path.exists(repo.wjoin(f)):
161 if os.path.exists(repo.wjoin(f)):
162 util.unlinkpath(repo.wjoin(f))
162 util.unlinkpath(repo.wjoin(f))
163 lfdirstate.remove(f)
163 lfdirstate.remove(f)
164 lfdirstate.write()
164 lfdirstate.write()
165 forget = [lfutil.standin(f) for f in forget]
165 forget = [lfutil.standin(f) for f in forget]
166 remove = [lfutil.standin(f) for f in remove]
166 remove = [lfutil.standin(f) for f in remove]
167 lfutil.repo_forget(repo, forget)
167 lfutil.repo_forget(repo, forget)
168 # If this is being called by addremove, let the original addremove
168 # If this is being called by addremove, let the original addremove
169 # function handle this.
169 # function handle this.
170 if not getattr(repo, "_isaddremove", False):
170 if not getattr(repo, "_isaddremove", False):
171 lfutil.repo_remove(repo, remove, unlink=True)
171 lfutil.repo_remove(repo, remove, unlink=True)
172 finally:
172 finally:
173 wlock.release()
173 wlock.release()
174
174
175 # -- Wrappers: modify existing commands --------------------------------
175 # -- Wrappers: modify existing commands --------------------------------
176
176
177 # Add works by going through the files that the user wanted to add and
177 # Add works by going through the files that the user wanted to add and
178 # checking if they should be added as largefiles. Then it makes a new
178 # checking if they should be added as largefiles. Then it makes a new
179 # matcher which matches only the normal files and runs the original
179 # matcher which matches only the normal files and runs the original
180 # version of add.
180 # version of add.
181 def override_add(orig, ui, repo, *pats, **opts):
181 def override_add(orig, ui, repo, *pats, **opts):
182 normal = opts.pop('normal')
182 normal = opts.pop('normal')
183 if normal:
183 if normal:
184 if opts.get('large'):
184 if opts.get('large'):
185 raise util.Abort(_('--normal cannot be used with --large'))
185 raise util.Abort(_('--normal cannot be used with --large'))
186 return orig(ui, repo, *pats, **opts)
186 return orig(ui, repo, *pats, **opts)
187 bad = add_largefiles(ui, repo, *pats, **opts)
187 bad = add_largefiles(ui, repo, *pats, **opts)
188 installnormalfilesmatchfn(repo[None].manifest())
188 installnormalfilesmatchfn(repo[None].manifest())
189 result = orig(ui, repo, *pats, **opts)
189 result = orig(ui, repo, *pats, **opts)
190 restorematchfn()
190 restorematchfn()
191
191
192 return (result == 1 or bad) and 1 or 0
192 return (result == 1 or bad) and 1 or 0
193
193
194 def override_remove(orig, ui, repo, *pats, **opts):
194 def override_remove(orig, ui, repo, *pats, **opts):
195 installnormalfilesmatchfn(repo[None].manifest())
195 installnormalfilesmatchfn(repo[None].manifest())
196 orig(ui, repo, *pats, **opts)
196 orig(ui, repo, *pats, **opts)
197 restorematchfn()
197 restorematchfn()
198 remove_largefiles(ui, repo, *pats, **opts)
198 remove_largefiles(ui, repo, *pats, **opts)
199
199
200 def override_status(orig, ui, repo, *pats, **opts):
200 def override_status(orig, ui, repo, *pats, **opts):
201 try:
201 try:
202 repo.lfstatus = True
202 repo.lfstatus = True
203 return orig(ui, repo, *pats, **opts)
203 return orig(ui, repo, *pats, **opts)
204 finally:
204 finally:
205 repo.lfstatus = False
205 repo.lfstatus = False
206
206
207 def override_log(orig, ui, repo, *pats, **opts):
207 def override_log(orig, ui, repo, *pats, **opts):
208 try:
208 try:
209 repo.lfstatus = True
209 repo.lfstatus = True
210 orig(ui, repo, *pats, **opts)
210 orig(ui, repo, *pats, **opts)
211 finally:
211 finally:
212 repo.lfstatus = False
212 repo.lfstatus = False
213
213
214 def override_verify(orig, ui, repo, *pats, **opts):
214 def override_verify(orig, ui, repo, *pats, **opts):
215 large = opts.pop('large', False)
215 large = opts.pop('large', False)
216 all = opts.pop('lfa', False)
216 all = opts.pop('lfa', False)
217 contents = opts.pop('lfc', False)
217 contents = opts.pop('lfc', False)
218
218
219 result = orig(ui, repo, *pats, **opts)
219 result = orig(ui, repo, *pats, **opts)
220 if large:
220 if large:
221 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
221 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
222 return result
222 return result
223
223
224 # Override needs to refresh standins so that update's normal merge
224 # Override needs to refresh standins so that update's normal merge
225 # will go through properly. Then the other update hook (overriding repo.update)
225 # will go through properly. Then the other update hook (overriding repo.update)
226 # will get the new files. Filemerge is also overriden so that the merge
226 # will get the new files. Filemerge is also overriden so that the merge
227 # will merge standins correctly.
227 # will merge standins correctly.
228 def override_update(orig, ui, repo, *pats, **opts):
228 def override_update(orig, ui, repo, *pats, **opts):
229 lfdirstate = lfutil.openlfdirstate(ui, repo)
229 lfdirstate = lfutil.openlfdirstate(ui, repo)
230 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
230 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
231 False, False)
231 False, False)
232 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
232 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
233
233
234 # Need to lock between the standins getting updated and their
234 # Need to lock between the standins getting updated and their
235 # largefiles getting updated
235 # largefiles getting updated
236 wlock = repo.wlock()
236 wlock = repo.wlock()
237 try:
237 try:
238 if opts['check']:
238 if opts['check']:
239 mod = len(modified) > 0
239 mod = len(modified) > 0
240 for lfile in unsure:
240 for lfile in unsure:
241 standin = lfutil.standin(lfile)
241 standin = lfutil.standin(lfile)
242 if repo['.'][standin].data().strip() != \
242 if repo['.'][standin].data().strip() != \
243 lfutil.hashfile(repo.wjoin(lfile)):
243 lfutil.hashfile(repo.wjoin(lfile)):
244 mod = True
244 mod = True
245 else:
245 else:
246 lfdirstate.normal(lfile)
246 lfdirstate.normal(lfile)
247 lfdirstate.write()
247 lfdirstate.write()
248 if mod:
248 if mod:
249 raise util.Abort(_('uncommitted local changes'))
249 raise util.Abort(_('uncommitted local changes'))
250 # XXX handle removed differently
250 # XXX handle removed differently
251 if not opts['clean']:
251 if not opts['clean']:
252 for lfile in unsure + modified + added:
252 for lfile in unsure + modified + added:
253 lfutil.updatestandin(repo, lfutil.standin(lfile))
253 lfutil.updatestandin(repo, lfutil.standin(lfile))
254 finally:
254 finally:
255 wlock.release()
255 wlock.release()
256 return orig(ui, repo, *pats, **opts)
256 return orig(ui, repo, *pats, **opts)
257
257
258 # Before starting the manifest merge, merge.updates will call
258 # Before starting the manifest merge, merge.updates will call
259 # _checkunknown to check if there are any files in the merged-in
259 # _checkunknown to check if there are any files in the merged-in
260 # changeset that collide with unknown files in the working copy.
260 # changeset that collide with unknown files in the working copy.
261 #
261 #
262 # The largefiles are seen as unknown, so this prevents us from merging
262 # The largefiles are seen as unknown, so this prevents us from merging
263 # in a file 'foo' if we already have a largefile with the same name.
263 # in a file 'foo' if we already have a largefile with the same name.
264 #
264 #
265 # The overridden function filters the unknown files by removing any
265 # The overridden function filters the unknown files by removing any
266 # largefiles. This makes the merge proceed and we can then handle this
266 # largefiles. This makes the merge proceed and we can then handle this
267 # case further in the overridden manifestmerge function below.
267 # case further in the overridden manifestmerge function below.
268 def override_checkunknown(origfn, wctx, mctx, folding):
268 def override_checkunknownfile(origfn, repo, wctx, mctx, f):
269 origunknown = wctx.unknown()
269 if lfutil.standin(f) in wctx:
270 wctx._unknown = filter(lambda f: lfutil.standin(f) not in wctx, origunknown)
270 return False
271 try:
271 return origfn(repo, wctx, mctx, f)
272 return origfn(wctx, mctx, folding)
273 finally:
274 wctx._unknown = origunknown
275
272
276 # The manifest merge handles conflicts on the manifest level. We want
273 # The manifest merge handles conflicts on the manifest level. We want
277 # to handle changes in largefile-ness of files at this level too.
274 # to handle changes in largefile-ness of files at this level too.
278 #
275 #
279 # The strategy is to run the original manifestmerge and then process
276 # The strategy is to run the original manifestmerge and then process
280 # the action list it outputs. There are two cases we need to deal with:
277 # the action list it outputs. There are two cases we need to deal with:
281 #
278 #
282 # 1. Normal file in p1, largefile in p2. Here the largefile is
279 # 1. Normal file in p1, largefile in p2. Here the largefile is
283 # detected via its standin file, which will enter the working copy
280 # detected via its standin file, which will enter the working copy
284 # with a "get" action. It is not "merge" since the standin is all
281 # with a "get" action. It is not "merge" since the standin is all
285 # Mercurial is concerned with at this level -- the link to the
282 # Mercurial is concerned with at this level -- the link to the
286 # existing normal file is not relevant here.
283 # existing normal file is not relevant here.
287 #
284 #
288 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
285 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
289 # since the largefile will be present in the working copy and
286 # since the largefile will be present in the working copy and
290 # different from the normal file in p2. Mercurial therefore
287 # different from the normal file in p2. Mercurial therefore
291 # triggers a merge action.
288 # triggers a merge action.
292 #
289 #
293 # In both cases, we prompt the user and emit new actions to either
290 # In both cases, we prompt the user and emit new actions to either
294 # remove the standin (if the normal file was kept) or to remove the
291 # remove the standin (if the normal file was kept) or to remove the
295 # normal file and get the standin (if the largefile was kept). The
292 # normal file and get the standin (if the largefile was kept). The
296 # default prompt answer is to use the largefile version since it was
293 # default prompt answer is to use the largefile version since it was
297 # presumably changed on purpose.
294 # presumably changed on purpose.
298 #
295 #
299 # Finally, the merge.applyupdates function will then take care of
296 # Finally, the merge.applyupdates function will then take care of
300 # writing the files into the working copy and lfcommands.updatelfiles
297 # writing the files into the working copy and lfcommands.updatelfiles
301 # will update the largefiles.
298 # will update the largefiles.
302 def override_manifestmerge(origfn, repo, p1, p2, pa, overwrite, partial):
299 def override_manifestmerge(origfn, repo, p1, p2, pa, overwrite, partial):
303 actions = origfn(repo, p1, p2, pa, overwrite, partial)
300 actions = origfn(repo, p1, p2, pa, overwrite, partial)
304 processed = []
301 processed = []
305
302
306 for action in actions:
303 for action in actions:
307 if overwrite:
304 if overwrite:
308 processed.append(action)
305 processed.append(action)
309 continue
306 continue
310 f, m = action[:2]
307 f, m = action[:2]
311
308
312 choices = (_('&Largefile'), _('&Normal file'))
309 choices = (_('&Largefile'), _('&Normal file'))
313 if m == "g" and lfutil.splitstandin(f) in p1 and f in p2:
310 if m == "g" and lfutil.splitstandin(f) in p1 and f in p2:
314 # Case 1: normal file in the working copy, largefile in
311 # Case 1: normal file in the working copy, largefile in
315 # the second parent
312 # the second parent
316 lfile = lfutil.splitstandin(f)
313 lfile = lfutil.splitstandin(f)
317 standin = f
314 standin = f
318 msg = _('%s has been turned into a largefile\n'
315 msg = _('%s has been turned into a largefile\n'
319 'use (l)argefile or keep as (n)ormal file?') % lfile
316 'use (l)argefile or keep as (n)ormal file?') % lfile
320 if repo.ui.promptchoice(msg, choices, 0) == 0:
317 if repo.ui.promptchoice(msg, choices, 0) == 0:
321 processed.append((lfile, "r"))
318 processed.append((lfile, "r"))
322 processed.append((standin, "g", p2.flags(standin)))
319 processed.append((standin, "g", p2.flags(standin)))
323 else:
320 else:
324 processed.append((standin, "r"))
321 processed.append((standin, "r"))
325 elif m == "m" and lfutil.standin(f) in p1 and f in p2:
322 elif m == "m" and lfutil.standin(f) in p1 and f in p2:
326 # Case 2: largefile in the working copy, normal file in
323 # Case 2: largefile in the working copy, normal file in
327 # the second parent
324 # the second parent
328 standin = lfutil.standin(f)
325 standin = lfutil.standin(f)
329 lfile = f
326 lfile = f
330 msg = _('%s has been turned into a normal file\n'
327 msg = _('%s has been turned into a normal file\n'
331 'keep as (l)argefile or use (n)ormal file?') % lfile
328 'keep as (l)argefile or use (n)ormal file?') % lfile
332 if repo.ui.promptchoice(msg, choices, 0) == 0:
329 if repo.ui.promptchoice(msg, choices, 0) == 0:
333 processed.append((lfile, "r"))
330 processed.append((lfile, "r"))
334 else:
331 else:
335 processed.append((standin, "r"))
332 processed.append((standin, "r"))
336 processed.append((lfile, "g", p2.flags(lfile)))
333 processed.append((lfile, "g", p2.flags(lfile)))
337 else:
334 else:
338 processed.append(action)
335 processed.append(action)
339
336
340 return processed
337 return processed
341
338
342 # Override filemerge to prompt the user about how they wish to merge
339 # Override filemerge to prompt the user about how they wish to merge
343 # largefiles. This will handle identical edits, and copy/rename +
340 # largefiles. This will handle identical edits, and copy/rename +
344 # edit without prompting the user.
341 # edit without prompting the user.
345 def override_filemerge(origfn, repo, mynode, orig, fcd, fco, fca):
342 def override_filemerge(origfn, repo, mynode, orig, fcd, fco, fca):
346 # Use better variable names here. Because this is a wrapper we cannot
343 # Use better variable names here. Because this is a wrapper we cannot
347 # change the variable names in the function declaration.
344 # change the variable names in the function declaration.
348 fcdest, fcother, fcancestor = fcd, fco, fca
345 fcdest, fcother, fcancestor = fcd, fco, fca
349 if not lfutil.isstandin(orig):
346 if not lfutil.isstandin(orig):
350 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
347 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
351 else:
348 else:
352 if not fcother.cmp(fcdest): # files identical?
349 if not fcother.cmp(fcdest): # files identical?
353 return None
350 return None
354
351
355 # backwards, use working dir parent as ancestor
352 # backwards, use working dir parent as ancestor
356 if fcancestor == fcother:
353 if fcancestor == fcother:
357 fcancestor = fcdest.parents()[0]
354 fcancestor = fcdest.parents()[0]
358
355
359 if orig != fcother.path():
356 if orig != fcother.path():
360 repo.ui.status(_('merging %s and %s to %s\n')
357 repo.ui.status(_('merging %s and %s to %s\n')
361 % (lfutil.splitstandin(orig),
358 % (lfutil.splitstandin(orig),
362 lfutil.splitstandin(fcother.path()),
359 lfutil.splitstandin(fcother.path()),
363 lfutil.splitstandin(fcdest.path())))
360 lfutil.splitstandin(fcdest.path())))
364 else:
361 else:
365 repo.ui.status(_('merging %s\n')
362 repo.ui.status(_('merging %s\n')
366 % lfutil.splitstandin(fcdest.path()))
363 % lfutil.splitstandin(fcdest.path()))
367
364
368 if fcancestor.path() != fcother.path() and fcother.data() == \
365 if fcancestor.path() != fcother.path() and fcother.data() == \
369 fcancestor.data():
366 fcancestor.data():
370 return 0
367 return 0
371 if fcancestor.path() != fcdest.path() and fcdest.data() == \
368 if fcancestor.path() != fcdest.path() and fcdest.data() == \
372 fcancestor.data():
369 fcancestor.data():
373 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
370 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
374 return 0
371 return 0
375
372
376 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
373 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
377 'keep (l)ocal or take (o)ther?') %
374 'keep (l)ocal or take (o)ther?') %
378 lfutil.splitstandin(orig),
375 lfutil.splitstandin(orig),
379 (_('&Local'), _('&Other')), 0) == 0:
376 (_('&Local'), _('&Other')), 0) == 0:
380 return 0
377 return 0
381 else:
378 else:
382 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
379 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
383 return 0
380 return 0
384
381
385 # Copy first changes the matchers to match standins instead of
382 # Copy first changes the matchers to match standins instead of
386 # largefiles. Then it overrides util.copyfile in that function it
383 # largefiles. Then it overrides util.copyfile in that function it
387 # checks if the destination largefile already exists. It also keeps a
384 # checks if the destination largefile already exists. It also keeps a
388 # list of copied files so that the largefiles can be copied and the
385 # list of copied files so that the largefiles can be copied and the
389 # dirstate updated.
386 # dirstate updated.
390 def override_copy(orig, ui, repo, pats, opts, rename=False):
387 def override_copy(orig, ui, repo, pats, opts, rename=False):
391 # doesn't remove largefile on rename
388 # doesn't remove largefile on rename
392 if len(pats) < 2:
389 if len(pats) < 2:
393 # this isn't legal, let the original function deal with it
390 # this isn't legal, let the original function deal with it
394 return orig(ui, repo, pats, opts, rename)
391 return orig(ui, repo, pats, opts, rename)
395
392
396 def makestandin(relpath):
393 def makestandin(relpath):
397 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
394 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
398 return os.path.join(repo.wjoin(lfutil.standin(path)))
395 return os.path.join(repo.wjoin(lfutil.standin(path)))
399
396
400 fullpats = scmutil.expandpats(pats)
397 fullpats = scmutil.expandpats(pats)
401 dest = fullpats[-1]
398 dest = fullpats[-1]
402
399
403 if os.path.isdir(dest):
400 if os.path.isdir(dest):
404 if not os.path.isdir(makestandin(dest)):
401 if not os.path.isdir(makestandin(dest)):
405 os.makedirs(makestandin(dest))
402 os.makedirs(makestandin(dest))
406 # This could copy both lfiles and normal files in one command,
403 # This could copy both lfiles and normal files in one command,
407 # but we don't want to do that. First replace their matcher to
404 # but we don't want to do that. First replace their matcher to
408 # only match normal files and run it, then replace it to just
405 # only match normal files and run it, then replace it to just
409 # match largefiles and run it again.
406 # match largefiles and run it again.
410 nonormalfiles = False
407 nonormalfiles = False
411 nolfiles = False
408 nolfiles = False
412 try:
409 try:
413 try:
410 try:
414 installnormalfilesmatchfn(repo[None].manifest())
411 installnormalfilesmatchfn(repo[None].manifest())
415 result = orig(ui, repo, pats, opts, rename)
412 result = orig(ui, repo, pats, opts, rename)
416 except util.Abort, e:
413 except util.Abort, e:
417 if str(e) != 'no files to copy':
414 if str(e) != 'no files to copy':
418 raise e
415 raise e
419 else:
416 else:
420 nonormalfiles = True
417 nonormalfiles = True
421 result = 0
418 result = 0
422 finally:
419 finally:
423 restorematchfn()
420 restorematchfn()
424
421
425 # The first rename can cause our current working directory to be removed.
422 # The first rename can cause our current working directory to be removed.
426 # In that case there is nothing left to copy/rename so just quit.
423 # In that case there is nothing left to copy/rename so just quit.
427 try:
424 try:
428 repo.getcwd()
425 repo.getcwd()
429 except OSError:
426 except OSError:
430 return result
427 return result
431
428
432 try:
429 try:
433 try:
430 try:
434 # When we call orig below it creates the standins but we don't add them
431 # When we call orig below it creates the standins but we don't add them
435 # to the dir state until later so lock during that time.
432 # to the dir state until later so lock during that time.
436 wlock = repo.wlock()
433 wlock = repo.wlock()
437
434
438 manifest = repo[None].manifest()
435 manifest = repo[None].manifest()
439 oldmatch = None # for the closure
436 oldmatch = None # for the closure
440 def override_match(ctx, pats=[], opts={}, globbed=False,
437 def override_match(ctx, pats=[], opts={}, globbed=False,
441 default='relpath'):
438 default='relpath'):
442 newpats = []
439 newpats = []
443 # The patterns were previously mangled to add the standin
440 # The patterns were previously mangled to add the standin
444 # directory; we need to remove that now
441 # directory; we need to remove that now
445 for pat in pats:
442 for pat in pats:
446 if match_.patkind(pat) is None and lfutil.shortname in pat:
443 if match_.patkind(pat) is None and lfutil.shortname in pat:
447 newpats.append(pat.replace(lfutil.shortname, ''))
444 newpats.append(pat.replace(lfutil.shortname, ''))
448 else:
445 else:
449 newpats.append(pat)
446 newpats.append(pat)
450 match = oldmatch(ctx, newpats, opts, globbed, default)
447 match = oldmatch(ctx, newpats, opts, globbed, default)
451 m = copy.copy(match)
448 m = copy.copy(match)
452 lfile = lambda f: lfutil.standin(f) in manifest
449 lfile = lambda f: lfutil.standin(f) in manifest
453 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
450 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
454 m._fmap = set(m._files)
451 m._fmap = set(m._files)
455 orig_matchfn = m.matchfn
452 orig_matchfn = m.matchfn
456 m.matchfn = lambda f: (lfutil.isstandin(f) and
453 m.matchfn = lambda f: (lfutil.isstandin(f) and
457 (f in manifest) and
454 (f in manifest) and
458 orig_matchfn(lfutil.splitstandin(f)) or
455 orig_matchfn(lfutil.splitstandin(f)) or
459 None)
456 None)
460 return m
457 return m
461 oldmatch = installmatchfn(override_match)
458 oldmatch = installmatchfn(override_match)
462 listpats = []
459 listpats = []
463 for pat in pats:
460 for pat in pats:
464 if match_.patkind(pat) is not None:
461 if match_.patkind(pat) is not None:
465 listpats.append(pat)
462 listpats.append(pat)
466 else:
463 else:
467 listpats.append(makestandin(pat))
464 listpats.append(makestandin(pat))
468
465
469 try:
466 try:
470 origcopyfile = util.copyfile
467 origcopyfile = util.copyfile
471 copiedfiles = []
468 copiedfiles = []
472 def override_copyfile(src, dest):
469 def override_copyfile(src, dest):
473 if (lfutil.shortname in src and
470 if (lfutil.shortname in src and
474 dest.startswith(repo.wjoin(lfutil.shortname))):
471 dest.startswith(repo.wjoin(lfutil.shortname))):
475 destlfile = dest.replace(lfutil.shortname, '')
472 destlfile = dest.replace(lfutil.shortname, '')
476 if not opts['force'] and os.path.exists(destlfile):
473 if not opts['force'] and os.path.exists(destlfile):
477 raise IOError('',
474 raise IOError('',
478 _('destination largefile already exists'))
475 _('destination largefile already exists'))
479 copiedfiles.append((src, dest))
476 copiedfiles.append((src, dest))
480 origcopyfile(src, dest)
477 origcopyfile(src, dest)
481
478
482 util.copyfile = override_copyfile
479 util.copyfile = override_copyfile
483 result += orig(ui, repo, listpats, opts, rename)
480 result += orig(ui, repo, listpats, opts, rename)
484 finally:
481 finally:
485 util.copyfile = origcopyfile
482 util.copyfile = origcopyfile
486
483
487 lfdirstate = lfutil.openlfdirstate(ui, repo)
484 lfdirstate = lfutil.openlfdirstate(ui, repo)
488 for (src, dest) in copiedfiles:
485 for (src, dest) in copiedfiles:
489 if (lfutil.shortname in src and
486 if (lfutil.shortname in src and
490 dest.startswith(repo.wjoin(lfutil.shortname))):
487 dest.startswith(repo.wjoin(lfutil.shortname))):
491 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
488 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
492 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
489 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
493 destlfiledir = os.path.dirname(destlfile) or '.'
490 destlfiledir = os.path.dirname(destlfile) or '.'
494 if not os.path.isdir(destlfiledir):
491 if not os.path.isdir(destlfiledir):
495 os.makedirs(destlfiledir)
492 os.makedirs(destlfiledir)
496 if rename:
493 if rename:
497 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
494 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
498 lfdirstate.remove(srclfile)
495 lfdirstate.remove(srclfile)
499 else:
496 else:
500 util.copyfile(srclfile, destlfile)
497 util.copyfile(srclfile, destlfile)
501 lfdirstate.add(destlfile)
498 lfdirstate.add(destlfile)
502 lfdirstate.write()
499 lfdirstate.write()
503 except util.Abort, e:
500 except util.Abort, e:
504 if str(e) != 'no files to copy':
501 if str(e) != 'no files to copy':
505 raise e
502 raise e
506 else:
503 else:
507 nolfiles = True
504 nolfiles = True
508 finally:
505 finally:
509 restorematchfn()
506 restorematchfn()
510 wlock.release()
507 wlock.release()
511
508
512 if nolfiles and nonormalfiles:
509 if nolfiles and nonormalfiles:
513 raise util.Abort(_('no files to copy'))
510 raise util.Abort(_('no files to copy'))
514
511
515 return result
512 return result
516
513
517 # When the user calls revert, we have to be careful to not revert any
514 # When the user calls revert, we have to be careful to not revert any
518 # changes to other largefiles accidentally. This means we have to keep
515 # changes to other largefiles accidentally. This means we have to keep
519 # track of the largefiles that are being reverted so we only pull down
516 # track of the largefiles that are being reverted so we only pull down
520 # the necessary largefiles.
517 # the necessary largefiles.
521 #
518 #
522 # Standins are only updated (to match the hash of largefiles) before
519 # Standins are only updated (to match the hash of largefiles) before
523 # commits. Update the standins then run the original revert, changing
520 # commits. Update the standins then run the original revert, changing
524 # the matcher to hit standins instead of largefiles. Based on the
521 # the matcher to hit standins instead of largefiles. Based on the
525 # resulting standins update the largefiles. Then return the standins
522 # resulting standins update the largefiles. Then return the standins
526 # to their proper state
523 # to their proper state
527 def override_revert(orig, ui, repo, *pats, **opts):
524 def override_revert(orig, ui, repo, *pats, **opts):
528 # Because we put the standins in a bad state (by updating them)
525 # Because we put the standins in a bad state (by updating them)
529 # and then return them to a correct state we need to lock to
526 # and then return them to a correct state we need to lock to
530 # prevent others from changing them in their incorrect state.
527 # prevent others from changing them in their incorrect state.
531 wlock = repo.wlock()
528 wlock = repo.wlock()
532 try:
529 try:
533 lfdirstate = lfutil.openlfdirstate(ui, repo)
530 lfdirstate = lfutil.openlfdirstate(ui, repo)
534 (modified, added, removed, missing, unknown, ignored, clean) = \
531 (modified, added, removed, missing, unknown, ignored, clean) = \
535 lfutil.lfdirstate_status(lfdirstate, repo, repo['.'].rev())
532 lfutil.lfdirstate_status(lfdirstate, repo, repo['.'].rev())
536 for lfile in modified:
533 for lfile in modified:
537 lfutil.updatestandin(repo, lfutil.standin(lfile))
534 lfutil.updatestandin(repo, lfutil.standin(lfile))
538 for lfile in missing:
535 for lfile in missing:
539 os.unlink(repo.wjoin(lfutil.standin(lfile)))
536 os.unlink(repo.wjoin(lfutil.standin(lfile)))
540
537
541 try:
538 try:
542 ctx = repo[opts.get('rev')]
539 ctx = repo[opts.get('rev')]
543 oldmatch = None # for the closure
540 oldmatch = None # for the closure
544 def override_match(ctx, pats=[], opts={}, globbed=False,
541 def override_match(ctx, pats=[], opts={}, globbed=False,
545 default='relpath'):
542 default='relpath'):
546 match = oldmatch(ctx, pats, opts, globbed, default)
543 match = oldmatch(ctx, pats, opts, globbed, default)
547 m = copy.copy(match)
544 m = copy.copy(match)
548 def tostandin(f):
545 def tostandin(f):
549 if lfutil.standin(f) in ctx:
546 if lfutil.standin(f) in ctx:
550 return lfutil.standin(f)
547 return lfutil.standin(f)
551 elif lfutil.standin(f) in repo[None]:
548 elif lfutil.standin(f) in repo[None]:
552 return None
549 return None
553 return f
550 return f
554 m._files = [tostandin(f) for f in m._files]
551 m._files = [tostandin(f) for f in m._files]
555 m._files = [f for f in m._files if f is not None]
552 m._files = [f for f in m._files if f is not None]
556 m._fmap = set(m._files)
553 m._fmap = set(m._files)
557 orig_matchfn = m.matchfn
554 orig_matchfn = m.matchfn
558 def matchfn(f):
555 def matchfn(f):
559 if lfutil.isstandin(f):
556 if lfutil.isstandin(f):
560 # We need to keep track of what largefiles are being
557 # We need to keep track of what largefiles are being
561 # matched so we know which ones to update later --
558 # matched so we know which ones to update later --
562 # otherwise we accidentally revert changes to other
559 # otherwise we accidentally revert changes to other
563 # largefiles. This is repo-specific, so duckpunch the
560 # largefiles. This is repo-specific, so duckpunch the
564 # repo object to keep the list of largefiles for us
561 # repo object to keep the list of largefiles for us
565 # later.
562 # later.
566 if orig_matchfn(lfutil.splitstandin(f)) and \
563 if orig_matchfn(lfutil.splitstandin(f)) and \
567 (f in repo[None] or f in ctx):
564 (f in repo[None] or f in ctx):
568 lfileslist = getattr(repo, '_lfilestoupdate', [])
565 lfileslist = getattr(repo, '_lfilestoupdate', [])
569 lfileslist.append(lfutil.splitstandin(f))
566 lfileslist.append(lfutil.splitstandin(f))
570 repo._lfilestoupdate = lfileslist
567 repo._lfilestoupdate = lfileslist
571 return True
568 return True
572 else:
569 else:
573 return False
570 return False
574 return orig_matchfn(f)
571 return orig_matchfn(f)
575 m.matchfn = matchfn
572 m.matchfn = matchfn
576 return m
573 return m
577 oldmatch = installmatchfn(override_match)
574 oldmatch = installmatchfn(override_match)
578 scmutil.match
575 scmutil.match
579 matches = override_match(repo[None], pats, opts)
576 matches = override_match(repo[None], pats, opts)
580 orig(ui, repo, *pats, **opts)
577 orig(ui, repo, *pats, **opts)
581 finally:
578 finally:
582 restorematchfn()
579 restorematchfn()
583 lfileslist = getattr(repo, '_lfilestoupdate', [])
580 lfileslist = getattr(repo, '_lfilestoupdate', [])
584 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
581 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
585 printmessage=False)
582 printmessage=False)
586
583
587 # empty out the largefiles list so we start fresh next time
584 # empty out the largefiles list so we start fresh next time
588 repo._lfilestoupdate = []
585 repo._lfilestoupdate = []
589 for lfile in modified:
586 for lfile in modified:
590 if lfile in lfileslist:
587 if lfile in lfileslist:
591 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
588 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
592 in repo['.']:
589 in repo['.']:
593 lfutil.writestandin(repo, lfutil.standin(lfile),
590 lfutil.writestandin(repo, lfutil.standin(lfile),
594 repo['.'][lfile].data().strip(),
591 repo['.'][lfile].data().strip(),
595 'x' in repo['.'][lfile].flags())
592 'x' in repo['.'][lfile].flags())
596 lfdirstate = lfutil.openlfdirstate(ui, repo)
593 lfdirstate = lfutil.openlfdirstate(ui, repo)
597 for lfile in added:
594 for lfile in added:
598 standin = lfutil.standin(lfile)
595 standin = lfutil.standin(lfile)
599 if standin not in ctx and (standin in matches or opts.get('all')):
596 if standin not in ctx and (standin in matches or opts.get('all')):
600 if lfile in lfdirstate:
597 if lfile in lfdirstate:
601 lfdirstate.drop(lfile)
598 lfdirstate.drop(lfile)
602 util.unlinkpath(repo.wjoin(standin))
599 util.unlinkpath(repo.wjoin(standin))
603 lfdirstate.write()
600 lfdirstate.write()
604 finally:
601 finally:
605 wlock.release()
602 wlock.release()
606
603
607 def hg_update(orig, repo, node):
604 def hg_update(orig, repo, node):
608 result = orig(repo, node)
605 result = orig(repo, node)
609 lfcommands.updatelfiles(repo.ui, repo)
606 lfcommands.updatelfiles(repo.ui, repo)
610 return result
607 return result
611
608
612 def hg_clean(orig, repo, node, show_stats=True):
609 def hg_clean(orig, repo, node, show_stats=True):
613 result = orig(repo, node, show_stats)
610 result = orig(repo, node, show_stats)
614 lfcommands.updatelfiles(repo.ui, repo)
611 lfcommands.updatelfiles(repo.ui, repo)
615 return result
612 return result
616
613
617 def hg_merge(orig, repo, node, force=None, remind=True):
614 def hg_merge(orig, repo, node, force=None, remind=True):
618 # Mark the repo as being in the middle of a merge, so that
615 # Mark the repo as being in the middle of a merge, so that
619 # updatelfiles() will know that it needs to trust the standins in
616 # updatelfiles() will know that it needs to trust the standins in
620 # the working copy, not in the standins in the current node
617 # the working copy, not in the standins in the current node
621 repo._ismerging = True
618 repo._ismerging = True
622 try:
619 try:
623 result = orig(repo, node, force, remind)
620 result = orig(repo, node, force, remind)
624 lfcommands.updatelfiles(repo.ui, repo)
621 lfcommands.updatelfiles(repo.ui, repo)
625 finally:
622 finally:
626 repo._ismerging = False
623 repo._ismerging = False
627 return result
624 return result
628
625
629 # When we rebase a repository with remotely changed largefiles, we need to
626 # When we rebase a repository with remotely changed largefiles, we need to
630 # take some extra care so that the largefiles are correctly updated in the
627 # take some extra care so that the largefiles are correctly updated in the
631 # working copy
628 # working copy
632 def override_pull(orig, ui, repo, source=None, **opts):
629 def override_pull(orig, ui, repo, source=None, **opts):
633 if opts.get('rebase', False):
630 if opts.get('rebase', False):
634 repo._isrebasing = True
631 repo._isrebasing = True
635 try:
632 try:
636 if opts.get('update'):
633 if opts.get('update'):
637 del opts['update']
634 del opts['update']
638 ui.debug('--update and --rebase are not compatible, ignoring '
635 ui.debug('--update and --rebase are not compatible, ignoring '
639 'the update flag\n')
636 'the update flag\n')
640 del opts['rebase']
637 del opts['rebase']
641 cmdutil.bailifchanged(repo)
638 cmdutil.bailifchanged(repo)
642 revsprepull = len(repo)
639 revsprepull = len(repo)
643 origpostincoming = commands.postincoming
640 origpostincoming = commands.postincoming
644 def _dummy(*args, **kwargs):
641 def _dummy(*args, **kwargs):
645 pass
642 pass
646 commands.postincoming = _dummy
643 commands.postincoming = _dummy
647 repo.lfpullsource = source
644 repo.lfpullsource = source
648 if not source:
645 if not source:
649 source = 'default'
646 source = 'default'
650 try:
647 try:
651 result = commands.pull(ui, repo, source, **opts)
648 result = commands.pull(ui, repo, source, **opts)
652 finally:
649 finally:
653 commands.postincoming = origpostincoming
650 commands.postincoming = origpostincoming
654 revspostpull = len(repo)
651 revspostpull = len(repo)
655 if revspostpull > revsprepull:
652 if revspostpull > revsprepull:
656 result = result or rebase.rebase(ui, repo)
653 result = result or rebase.rebase(ui, repo)
657 finally:
654 finally:
658 repo._isrebasing = False
655 repo._isrebasing = False
659 else:
656 else:
660 repo.lfpullsource = source
657 repo.lfpullsource = source
661 if not source:
658 if not source:
662 source = 'default'
659 source = 'default'
663 result = orig(ui, repo, source, **opts)
660 result = orig(ui, repo, source, **opts)
664 # If we do not have the new largefiles for any new heads we pulled, we
661 # If we do not have the new largefiles for any new heads we pulled, we
665 # will run into a problem later if we try to merge or rebase with one of
662 # will run into a problem later if we try to merge or rebase with one of
666 # these heads, so cache the largefiles now direclty into the system
663 # these heads, so cache the largefiles now direclty into the system
667 # cache.
664 # cache.
668 ui.status(_("caching new largefiles\n"))
665 ui.status(_("caching new largefiles\n"))
669 numcached = 0
666 numcached = 0
670 branches = repo.branchmap()
667 branches = repo.branchmap()
671 for branch in branches:
668 for branch in branches:
672 heads = repo.branchheads(branch)
669 heads = repo.branchheads(branch)
673 for head in heads:
670 for head in heads:
674 (cached, missing) = lfcommands.cachelfiles(ui, repo, head)
671 (cached, missing) = lfcommands.cachelfiles(ui, repo, head)
675 numcached += len(cached)
672 numcached += len(cached)
676 ui.status(_("%d largefiles cached\n" % numcached))
673 ui.status(_("%d largefiles cached\n" % numcached))
677 return result
674 return result
678
675
679 def override_rebase(orig, ui, repo, **opts):
676 def override_rebase(orig, ui, repo, **opts):
680 repo._isrebasing = True
677 repo._isrebasing = True
681 try:
678 try:
682 orig(ui, repo, **opts)
679 orig(ui, repo, **opts)
683 finally:
680 finally:
684 repo._isrebasing = False
681 repo._isrebasing = False
685
682
686 def override_archive(orig, repo, dest, node, kind, decode=True, matchfn=None,
683 def override_archive(orig, repo, dest, node, kind, decode=True, matchfn=None,
687 prefix=None, mtime=None, subrepos=None):
684 prefix=None, mtime=None, subrepos=None):
688 # No need to lock because we are only reading history and
685 # No need to lock because we are only reading history and
689 # largefile caches, neither of which are modified.
686 # largefile caches, neither of which are modified.
690 lfcommands.cachelfiles(repo.ui, repo, node)
687 lfcommands.cachelfiles(repo.ui, repo, node)
691
688
692 if kind not in archival.archivers:
689 if kind not in archival.archivers:
693 raise util.Abort(_("unknown archive type '%s'") % kind)
690 raise util.Abort(_("unknown archive type '%s'") % kind)
694
691
695 ctx = repo[node]
692 ctx = repo[node]
696
693
697 if kind == 'files':
694 if kind == 'files':
698 if prefix:
695 if prefix:
699 raise util.Abort(
696 raise util.Abort(
700 _('cannot give prefix when archiving to files'))
697 _('cannot give prefix when archiving to files'))
701 else:
698 else:
702 prefix = archival.tidyprefix(dest, kind, prefix)
699 prefix = archival.tidyprefix(dest, kind, prefix)
703
700
704 def write(name, mode, islink, getdata):
701 def write(name, mode, islink, getdata):
705 if matchfn and not matchfn(name):
702 if matchfn and not matchfn(name):
706 return
703 return
707 data = getdata()
704 data = getdata()
708 if decode:
705 if decode:
709 data = repo.wwritedata(name, data)
706 data = repo.wwritedata(name, data)
710 archiver.addfile(prefix + name, mode, islink, data)
707 archiver.addfile(prefix + name, mode, islink, data)
711
708
712 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
709 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
713
710
714 if repo.ui.configbool("ui", "archivemeta", True):
711 if repo.ui.configbool("ui", "archivemeta", True):
715 def metadata():
712 def metadata():
716 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
713 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
717 hex(repo.changelog.node(0)), hex(node), ctx.branch())
714 hex(repo.changelog.node(0)), hex(node), ctx.branch())
718
715
719 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
716 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
720 if repo.tagtype(t) == 'global')
717 if repo.tagtype(t) == 'global')
721 if not tags:
718 if not tags:
722 repo.ui.pushbuffer()
719 repo.ui.pushbuffer()
723 opts = {'template': '{latesttag}\n{latesttagdistance}',
720 opts = {'template': '{latesttag}\n{latesttagdistance}',
724 'style': '', 'patch': None, 'git': None}
721 'style': '', 'patch': None, 'git': None}
725 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
722 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
726 ltags, dist = repo.ui.popbuffer().split('\n')
723 ltags, dist = repo.ui.popbuffer().split('\n')
727 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
724 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
728 tags += 'latesttagdistance: %s\n' % dist
725 tags += 'latesttagdistance: %s\n' % dist
729
726
730 return base + tags
727 return base + tags
731
728
732 write('.hg_archival.txt', 0644, False, metadata)
729 write('.hg_archival.txt', 0644, False, metadata)
733
730
734 for f in ctx:
731 for f in ctx:
735 ff = ctx.flags(f)
732 ff = ctx.flags(f)
736 getdata = ctx[f].data
733 getdata = ctx[f].data
737 if lfutil.isstandin(f):
734 if lfutil.isstandin(f):
738 path = lfutil.findfile(repo, getdata().strip())
735 path = lfutil.findfile(repo, getdata().strip())
739 if path is None:
736 if path is None:
740 raise util.Abort(
737 raise util.Abort(
741 _('largefile %s not found in repo store or system cache')
738 _('largefile %s not found in repo store or system cache')
742 % lfutil.splitstandin(f))
739 % lfutil.splitstandin(f))
743 f = lfutil.splitstandin(f)
740 f = lfutil.splitstandin(f)
744
741
745 def getdatafn():
742 def getdatafn():
746 fd = None
743 fd = None
747 try:
744 try:
748 fd = open(path, 'rb')
745 fd = open(path, 'rb')
749 return fd.read()
746 return fd.read()
750 finally:
747 finally:
751 if fd:
748 if fd:
752 fd.close()
749 fd.close()
753
750
754 getdata = getdatafn
751 getdata = getdatafn
755 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
752 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
756
753
757 if subrepos:
754 if subrepos:
758 for subpath in ctx.substate:
755 for subpath in ctx.substate:
759 sub = ctx.sub(subpath)
756 sub = ctx.sub(subpath)
760 sub.archive(repo.ui, archiver, prefix)
757 sub.archive(repo.ui, archiver, prefix)
761
758
762 archiver.done()
759 archiver.done()
763
760
764 # If a largefile is modified, the change is not reflected in its
761 # If a largefile is modified, the change is not reflected in its
765 # standin until a commit. cmdutil.bailifchanged() raises an exception
762 # standin until a commit. cmdutil.bailifchanged() raises an exception
766 # if the repo has uncommitted changes. Wrap it to also check if
763 # if the repo has uncommitted changes. Wrap it to also check if
767 # largefiles were changed. This is used by bisect and backout.
764 # largefiles were changed. This is used by bisect and backout.
768 def override_bailifchanged(orig, repo):
765 def override_bailifchanged(orig, repo):
769 orig(repo)
766 orig(repo)
770 repo.lfstatus = True
767 repo.lfstatus = True
771 modified, added, removed, deleted = repo.status()[:4]
768 modified, added, removed, deleted = repo.status()[:4]
772 repo.lfstatus = False
769 repo.lfstatus = False
773 if modified or added or removed or deleted:
770 if modified or added or removed or deleted:
774 raise util.Abort(_('outstanding uncommitted changes'))
771 raise util.Abort(_('outstanding uncommitted changes'))
775
772
776 # Fetch doesn't use cmdutil.bail_if_changed so override it to add the check
773 # Fetch doesn't use cmdutil.bail_if_changed so override it to add the check
777 def override_fetch(orig, ui, repo, *pats, **opts):
774 def override_fetch(orig, ui, repo, *pats, **opts):
778 repo.lfstatus = True
775 repo.lfstatus = True
779 modified, added, removed, deleted = repo.status()[:4]
776 modified, added, removed, deleted = repo.status()[:4]
780 repo.lfstatus = False
777 repo.lfstatus = False
781 if modified or added or removed or deleted:
778 if modified or added or removed or deleted:
782 raise util.Abort(_('outstanding uncommitted changes'))
779 raise util.Abort(_('outstanding uncommitted changes'))
783 return orig(ui, repo, *pats, **opts)
780 return orig(ui, repo, *pats, **opts)
784
781
785 def override_forget(orig, ui, repo, *pats, **opts):
782 def override_forget(orig, ui, repo, *pats, **opts):
786 installnormalfilesmatchfn(repo[None].manifest())
783 installnormalfilesmatchfn(repo[None].manifest())
787 orig(ui, repo, *pats, **opts)
784 orig(ui, repo, *pats, **opts)
788 restorematchfn()
785 restorematchfn()
789 m = scmutil.match(repo[None], pats, opts)
786 m = scmutil.match(repo[None], pats, opts)
790
787
791 try:
788 try:
792 repo.lfstatus = True
789 repo.lfstatus = True
793 s = repo.status(match=m, clean=True)
790 s = repo.status(match=m, clean=True)
794 finally:
791 finally:
795 repo.lfstatus = False
792 repo.lfstatus = False
796 forget = sorted(s[0] + s[1] + s[3] + s[6])
793 forget = sorted(s[0] + s[1] + s[3] + s[6])
797 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
794 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
798
795
799 for f in forget:
796 for f in forget:
800 if lfutil.standin(f) not in repo.dirstate and not \
797 if lfutil.standin(f) not in repo.dirstate and not \
801 os.path.isdir(m.rel(lfutil.standin(f))):
798 os.path.isdir(m.rel(lfutil.standin(f))):
802 ui.warn(_('not removing %s: file is already untracked\n')
799 ui.warn(_('not removing %s: file is already untracked\n')
803 % m.rel(f))
800 % m.rel(f))
804
801
805 for f in forget:
802 for f in forget:
806 if ui.verbose or not m.exact(f):
803 if ui.verbose or not m.exact(f):
807 ui.status(_('removing %s\n') % m.rel(f))
804 ui.status(_('removing %s\n') % m.rel(f))
808
805
809 # Need to lock because standin files are deleted then removed from the
806 # Need to lock because standin files are deleted then removed from the
810 # repository and we could race inbetween.
807 # repository and we could race inbetween.
811 wlock = repo.wlock()
808 wlock = repo.wlock()
812 try:
809 try:
813 lfdirstate = lfutil.openlfdirstate(ui, repo)
810 lfdirstate = lfutil.openlfdirstate(ui, repo)
814 for f in forget:
811 for f in forget:
815 if lfdirstate[f] == 'a':
812 if lfdirstate[f] == 'a':
816 lfdirstate.drop(f)
813 lfdirstate.drop(f)
817 else:
814 else:
818 lfdirstate.remove(f)
815 lfdirstate.remove(f)
819 lfdirstate.write()
816 lfdirstate.write()
820 lfutil.repo_remove(repo, [lfutil.standin(f) for f in forget],
817 lfutil.repo_remove(repo, [lfutil.standin(f) for f in forget],
821 unlink=True)
818 unlink=True)
822 finally:
819 finally:
823 wlock.release()
820 wlock.release()
824
821
825 def getoutgoinglfiles(ui, repo, dest=None, **opts):
822 def getoutgoinglfiles(ui, repo, dest=None, **opts):
826 dest = ui.expandpath(dest or 'default-push', dest or 'default')
823 dest = ui.expandpath(dest or 'default-push', dest or 'default')
827 dest, branches = hg.parseurl(dest, opts.get('branch'))
824 dest, branches = hg.parseurl(dest, opts.get('branch'))
828 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
825 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
829 if revs:
826 if revs:
830 revs = [repo.lookup(rev) for rev in revs]
827 revs = [repo.lookup(rev) for rev in revs]
831
828
832 remoteui = hg.remoteui
829 remoteui = hg.remoteui
833
830
834 try:
831 try:
835 remote = hg.repository(remoteui(repo, opts), dest)
832 remote = hg.repository(remoteui(repo, opts), dest)
836 except error.RepoError:
833 except error.RepoError:
837 return None
834 return None
838 o = lfutil.findoutgoing(repo, remote, False)
835 o = lfutil.findoutgoing(repo, remote, False)
839 if not o:
836 if not o:
840 return None
837 return None
841 o = repo.changelog.nodesbetween(o, revs)[0]
838 o = repo.changelog.nodesbetween(o, revs)[0]
842 if opts.get('newest_first'):
839 if opts.get('newest_first'):
843 o.reverse()
840 o.reverse()
844
841
845 toupload = set()
842 toupload = set()
846 for n in o:
843 for n in o:
847 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
844 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
848 ctx = repo[n]
845 ctx = repo[n]
849 files = set(ctx.files())
846 files = set(ctx.files())
850 if len(parents) == 2:
847 if len(parents) == 2:
851 mc = ctx.manifest()
848 mc = ctx.manifest()
852 mp1 = ctx.parents()[0].manifest()
849 mp1 = ctx.parents()[0].manifest()
853 mp2 = ctx.parents()[1].manifest()
850 mp2 = ctx.parents()[1].manifest()
854 for f in mp1:
851 for f in mp1:
855 if f not in mc:
852 if f not in mc:
856 files.add(f)
853 files.add(f)
857 for f in mp2:
854 for f in mp2:
858 if f not in mc:
855 if f not in mc:
859 files.add(f)
856 files.add(f)
860 for f in mc:
857 for f in mc:
861 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
858 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
862 files.add(f)
859 files.add(f)
863 toupload = toupload.union(
860 toupload = toupload.union(
864 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
861 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
865 return toupload
862 return toupload
866
863
867 def override_outgoing(orig, ui, repo, dest=None, **opts):
864 def override_outgoing(orig, ui, repo, dest=None, **opts):
868 orig(ui, repo, dest, **opts)
865 orig(ui, repo, dest, **opts)
869
866
870 if opts.pop('large', None):
867 if opts.pop('large', None):
871 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
868 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
872 if toupload is None:
869 if toupload is None:
873 ui.status(_('largefiles: No remote repo\n'))
870 ui.status(_('largefiles: No remote repo\n'))
874 else:
871 else:
875 ui.status(_('largefiles to upload:\n'))
872 ui.status(_('largefiles to upload:\n'))
876 for file in toupload:
873 for file in toupload:
877 ui.status(lfutil.splitstandin(file) + '\n')
874 ui.status(lfutil.splitstandin(file) + '\n')
878 ui.status('\n')
875 ui.status('\n')
879
876
880 def override_summary(orig, ui, repo, *pats, **opts):
877 def override_summary(orig, ui, repo, *pats, **opts):
881 try:
878 try:
882 repo.lfstatus = True
879 repo.lfstatus = True
883 orig(ui, repo, *pats, **opts)
880 orig(ui, repo, *pats, **opts)
884 finally:
881 finally:
885 repo.lfstatus = False
882 repo.lfstatus = False
886
883
887 if opts.pop('large', None):
884 if opts.pop('large', None):
888 toupload = getoutgoinglfiles(ui, repo, None, **opts)
885 toupload = getoutgoinglfiles(ui, repo, None, **opts)
889 if toupload is None:
886 if toupload is None:
890 ui.status(_('largefiles: No remote repo\n'))
887 ui.status(_('largefiles: No remote repo\n'))
891 else:
888 else:
892 ui.status(_('largefiles: %d to upload\n') % len(toupload))
889 ui.status(_('largefiles: %d to upload\n') % len(toupload))
893
890
894 def override_addremove(orig, ui, repo, *pats, **opts):
891 def override_addremove(orig, ui, repo, *pats, **opts):
895 # Get the list of missing largefiles so we can remove them
892 # Get the list of missing largefiles so we can remove them
896 lfdirstate = lfutil.openlfdirstate(ui, repo)
893 lfdirstate = lfutil.openlfdirstate(ui, repo)
897 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
894 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
898 False, False)
895 False, False)
899 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
896 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
900
897
901 # Call into the normal remove code, but the removing of the standin, we want
898 # Call into the normal remove code, but the removing of the standin, we want
902 # to have handled by original addremove. Monkey patching here makes sure
899 # to have handled by original addremove. Monkey patching here makes sure
903 # we don't remove the standin in the largefiles code, preventing a very
900 # we don't remove the standin in the largefiles code, preventing a very
904 # confused state later.
901 # confused state later.
905 if missing:
902 if missing:
906 repo._isaddremove = True
903 repo._isaddremove = True
907 remove_largefiles(ui, repo, *missing, **opts)
904 remove_largefiles(ui, repo, *missing, **opts)
908 repo._isaddremove = False
905 repo._isaddremove = False
909 # Call into the normal add code, and any files that *should* be added as
906 # Call into the normal add code, and any files that *should* be added as
910 # largefiles will be
907 # largefiles will be
911 add_largefiles(ui, repo, *pats, **opts)
908 add_largefiles(ui, repo, *pats, **opts)
912 # Now that we've handled largefiles, hand off to the original addremove
909 # Now that we've handled largefiles, hand off to the original addremove
913 # function to take care of the rest. Make sure it doesn't do anything with
910 # function to take care of the rest. Make sure it doesn't do anything with
914 # largefiles by installing a matcher that will ignore them.
911 # largefiles by installing a matcher that will ignore them.
915 installnormalfilesmatchfn(repo[None].manifest())
912 installnormalfilesmatchfn(repo[None].manifest())
916 result = orig(ui, repo, *pats, **opts)
913 result = orig(ui, repo, *pats, **opts)
917 restorematchfn()
914 restorematchfn()
918 return result
915 return result
919
916
920 # Calling purge with --all will cause the largefiles to be deleted.
917 # Calling purge with --all will cause the largefiles to be deleted.
921 # Override repo.status to prevent this from happening.
918 # Override repo.status to prevent this from happening.
922 def override_purge(orig, ui, repo, *dirs, **opts):
919 def override_purge(orig, ui, repo, *dirs, **opts):
923 oldstatus = repo.status
920 oldstatus = repo.status
924 def override_status(node1='.', node2=None, match=None, ignored=False,
921 def override_status(node1='.', node2=None, match=None, ignored=False,
925 clean=False, unknown=False, listsubrepos=False):
922 clean=False, unknown=False, listsubrepos=False):
926 r = oldstatus(node1, node2, match, ignored, clean, unknown,
923 r = oldstatus(node1, node2, match, ignored, clean, unknown,
927 listsubrepos)
924 listsubrepos)
928 lfdirstate = lfutil.openlfdirstate(ui, repo)
925 lfdirstate = lfutil.openlfdirstate(ui, repo)
929 modified, added, removed, deleted, unknown, ignored, clean = r
926 modified, added, removed, deleted, unknown, ignored, clean = r
930 unknown = [f for f in unknown if lfdirstate[f] == '?']
927 unknown = [f for f in unknown if lfdirstate[f] == '?']
931 ignored = [f for f in ignored if lfdirstate[f] == '?']
928 ignored = [f for f in ignored if lfdirstate[f] == '?']
932 return modified, added, removed, deleted, unknown, ignored, clean
929 return modified, added, removed, deleted, unknown, ignored, clean
933 repo.status = override_status
930 repo.status = override_status
934 orig(ui, repo, *dirs, **opts)
931 orig(ui, repo, *dirs, **opts)
935 repo.status = oldstatus
932 repo.status = oldstatus
936
933
937 def override_rollback(orig, ui, repo, **opts):
934 def override_rollback(orig, ui, repo, **opts):
938 result = orig(ui, repo, **opts)
935 result = orig(ui, repo, **opts)
939 merge.update(repo, node=None, branchmerge=False, force=True,
936 merge.update(repo, node=None, branchmerge=False, force=True,
940 partial=lfutil.isstandin)
937 partial=lfutil.isstandin)
941 wlock = repo.wlock()
938 wlock = repo.wlock()
942 try:
939 try:
943 lfdirstate = lfutil.openlfdirstate(ui, repo)
940 lfdirstate = lfutil.openlfdirstate(ui, repo)
944 lfiles = lfutil.listlfiles(repo)
941 lfiles = lfutil.listlfiles(repo)
945 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
942 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
946 for file in lfiles:
943 for file in lfiles:
947 if file in oldlfiles:
944 if file in oldlfiles:
948 lfdirstate.normallookup(file)
945 lfdirstate.normallookup(file)
949 else:
946 else:
950 lfdirstate.add(file)
947 lfdirstate.add(file)
951 lfdirstate.write()
948 lfdirstate.write()
952 finally:
949 finally:
953 wlock.release()
950 wlock.release()
954 return result
951 return result
955
952
956 def override_transplant(orig, ui, repo, *revs, **opts):
953 def override_transplant(orig, ui, repo, *revs, **opts):
957 try:
954 try:
958 repo._istransplanting = True
955 repo._istransplanting = True
959 result = orig(ui, repo, *revs, **opts)
956 result = orig(ui, repo, *revs, **opts)
960 lfcommands.updatelfiles(ui, repo, filelist=None,
957 lfcommands.updatelfiles(ui, repo, filelist=None,
961 printmessage=False)
958 printmessage=False)
962 finally:
959 finally:
963 repo._istransplanting = False
960 repo._istransplanting = False
964 return result
961 return result
@@ -1,143 +1,143 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''setup for largefiles extension: uisetup'''
9 '''setup for largefiles extension: uisetup'''
10
10
11 from mercurial import archival, cmdutil, commands, extensions, filemerge, hg, \
11 from mercurial import archival, cmdutil, commands, extensions, filemerge, hg, \
12 httprepo, localrepo, merge, sshrepo, sshserver, wireproto
12 httprepo, localrepo, merge, sshrepo, sshserver, wireproto
13 from mercurial.i18n import _
13 from mercurial.i18n import _
14 from mercurial.hgweb import hgweb_mod, protocol
14 from mercurial.hgweb import hgweb_mod, protocol
15
15
16 import overrides
16 import overrides
17 import proto
17 import proto
18
18
19 def uisetup(ui):
19 def uisetup(ui):
20 # Disable auto-status for some commands which assume that all
20 # Disable auto-status for some commands which assume that all
21 # files in the result are under Mercurial's control
21 # files in the result are under Mercurial's control
22
22
23 entry = extensions.wrapcommand(commands.table, 'add',
23 entry = extensions.wrapcommand(commands.table, 'add',
24 overrides.override_add)
24 overrides.override_add)
25 addopt = [('', 'large', None, _('add as largefile')),
25 addopt = [('', 'large', None, _('add as largefile')),
26 ('', 'normal', None, _('add as normal file')),
26 ('', 'normal', None, _('add as normal file')),
27 ('', 'lfsize', '', _('add all files above this size '
27 ('', 'lfsize', '', _('add all files above this size '
28 '(in megabytes) as largefiles '
28 '(in megabytes) as largefiles '
29 '(default: 10)'))]
29 '(default: 10)'))]
30 entry[1].extend(addopt)
30 entry[1].extend(addopt)
31
31
32 entry = extensions.wrapcommand(commands.table, 'addremove',
32 entry = extensions.wrapcommand(commands.table, 'addremove',
33 overrides.override_addremove)
33 overrides.override_addremove)
34 entry = extensions.wrapcommand(commands.table, 'remove',
34 entry = extensions.wrapcommand(commands.table, 'remove',
35 overrides.override_remove)
35 overrides.override_remove)
36 entry = extensions.wrapcommand(commands.table, 'forget',
36 entry = extensions.wrapcommand(commands.table, 'forget',
37 overrides.override_forget)
37 overrides.override_forget)
38 entry = extensions.wrapcommand(commands.table, 'status',
38 entry = extensions.wrapcommand(commands.table, 'status',
39 overrides.override_status)
39 overrides.override_status)
40 entry = extensions.wrapcommand(commands.table, 'log',
40 entry = extensions.wrapcommand(commands.table, 'log',
41 overrides.override_log)
41 overrides.override_log)
42 entry = extensions.wrapcommand(commands.table, 'rollback',
42 entry = extensions.wrapcommand(commands.table, 'rollback',
43 overrides.override_rollback)
43 overrides.override_rollback)
44 entry = extensions.wrapcommand(commands.table, 'verify',
44 entry = extensions.wrapcommand(commands.table, 'verify',
45 overrides.override_verify)
45 overrides.override_verify)
46
46
47 verifyopt = [('', 'large', None, _('verify largefiles')),
47 verifyopt = [('', 'large', None, _('verify largefiles')),
48 ('', 'lfa', None,
48 ('', 'lfa', None,
49 _('verify all revisions of largefiles not just current')),
49 _('verify all revisions of largefiles not just current')),
50 ('', 'lfc', None,
50 ('', 'lfc', None,
51 _('verify largefile contents not just existence'))]
51 _('verify largefile contents not just existence'))]
52 entry[1].extend(verifyopt)
52 entry[1].extend(verifyopt)
53
53
54 entry = extensions.wrapcommand(commands.table, 'outgoing',
54 entry = extensions.wrapcommand(commands.table, 'outgoing',
55 overrides.override_outgoing)
55 overrides.override_outgoing)
56 outgoingopt = [('', 'large', None, _('display outgoing largefiles'))]
56 outgoingopt = [('', 'large', None, _('display outgoing largefiles'))]
57 entry[1].extend(outgoingopt)
57 entry[1].extend(outgoingopt)
58 entry = extensions.wrapcommand(commands.table, 'summary',
58 entry = extensions.wrapcommand(commands.table, 'summary',
59 overrides.override_summary)
59 overrides.override_summary)
60 summaryopt = [('', 'large', None, _('display outgoing largefiles'))]
60 summaryopt = [('', 'large', None, _('display outgoing largefiles'))]
61 entry[1].extend(summaryopt)
61 entry[1].extend(summaryopt)
62
62
63 entry = extensions.wrapcommand(commands.table, 'update',
63 entry = extensions.wrapcommand(commands.table, 'update',
64 overrides.override_update)
64 overrides.override_update)
65 entry = extensions.wrapcommand(commands.table, 'pull',
65 entry = extensions.wrapcommand(commands.table, 'pull',
66 overrides.override_pull)
66 overrides.override_pull)
67 entry = extensions.wrapfunction(merge, '_checkunknown',
67 entry = extensions.wrapfunction(merge, '_checkunknownfile',
68 overrides.override_checkunknown)
68 overrides.override_checkunknownfile)
69 entry = extensions.wrapfunction(merge, 'manifestmerge',
69 entry = extensions.wrapfunction(merge, 'manifestmerge',
70 overrides.override_manifestmerge)
70 overrides.override_manifestmerge)
71 entry = extensions.wrapfunction(filemerge, 'filemerge',
71 entry = extensions.wrapfunction(filemerge, 'filemerge',
72 overrides.override_filemerge)
72 overrides.override_filemerge)
73 entry = extensions.wrapfunction(cmdutil, 'copy',
73 entry = extensions.wrapfunction(cmdutil, 'copy',
74 overrides.override_copy)
74 overrides.override_copy)
75
75
76 # Backout calls revert so we need to override both the command and the
76 # Backout calls revert so we need to override both the command and the
77 # function
77 # function
78 entry = extensions.wrapcommand(commands.table, 'revert',
78 entry = extensions.wrapcommand(commands.table, 'revert',
79 overrides.override_revert)
79 overrides.override_revert)
80 entry = extensions.wrapfunction(commands, 'revert',
80 entry = extensions.wrapfunction(commands, 'revert',
81 overrides.override_revert)
81 overrides.override_revert)
82
82
83 # clone uses hg._update instead of hg.update even though they are the
83 # clone uses hg._update instead of hg.update even though they are the
84 # same function... so wrap both of them)
84 # same function... so wrap both of them)
85 extensions.wrapfunction(hg, 'update', overrides.hg_update)
85 extensions.wrapfunction(hg, 'update', overrides.hg_update)
86 extensions.wrapfunction(hg, '_update', overrides.hg_update)
86 extensions.wrapfunction(hg, '_update', overrides.hg_update)
87 extensions.wrapfunction(hg, 'clean', overrides.hg_clean)
87 extensions.wrapfunction(hg, 'clean', overrides.hg_clean)
88 extensions.wrapfunction(hg, 'merge', overrides.hg_merge)
88 extensions.wrapfunction(hg, 'merge', overrides.hg_merge)
89
89
90 extensions.wrapfunction(archival, 'archive', overrides.override_archive)
90 extensions.wrapfunction(archival, 'archive', overrides.override_archive)
91 extensions.wrapfunction(cmdutil, 'bailifchanged',
91 extensions.wrapfunction(cmdutil, 'bailifchanged',
92 overrides.override_bailifchanged)
92 overrides.override_bailifchanged)
93
93
94 # create the new wireproto commands ...
94 # create the new wireproto commands ...
95 wireproto.commands['putlfile'] = (proto.putlfile, 'sha')
95 wireproto.commands['putlfile'] = (proto.putlfile, 'sha')
96 wireproto.commands['getlfile'] = (proto.getlfile, 'sha')
96 wireproto.commands['getlfile'] = (proto.getlfile, 'sha')
97 wireproto.commands['statlfile'] = (proto.statlfile, 'sha')
97 wireproto.commands['statlfile'] = (proto.statlfile, 'sha')
98
98
99 # ... and wrap some existing ones
99 # ... and wrap some existing ones
100 wireproto.commands['capabilities'] = (proto.capabilities, '')
100 wireproto.commands['capabilities'] = (proto.capabilities, '')
101 wireproto.commands['heads'] = (proto.heads, '')
101 wireproto.commands['heads'] = (proto.heads, '')
102 wireproto.commands['lheads'] = (wireproto.heads, '')
102 wireproto.commands['lheads'] = (wireproto.heads, '')
103
103
104 # make putlfile behave the same as push and {get,stat}lfile behave
104 # make putlfile behave the same as push and {get,stat}lfile behave
105 # the same as pull w.r.t. permissions checks
105 # the same as pull w.r.t. permissions checks
106 hgweb_mod.perms['putlfile'] = 'push'
106 hgweb_mod.perms['putlfile'] = 'push'
107 hgweb_mod.perms['getlfile'] = 'pull'
107 hgweb_mod.perms['getlfile'] = 'pull'
108 hgweb_mod.perms['statlfile'] = 'pull'
108 hgweb_mod.perms['statlfile'] = 'pull'
109
109
110 # the hello wireproto command uses wireproto.capabilities, so it won't see
110 # the hello wireproto command uses wireproto.capabilities, so it won't see
111 # our largefiles capability unless we replace the actual function as well.
111 # our largefiles capability unless we replace the actual function as well.
112 proto.capabilities_orig = wireproto.capabilities
112 proto.capabilities_orig = wireproto.capabilities
113 wireproto.capabilities = proto.capabilities
113 wireproto.capabilities = proto.capabilities
114
114
115 # these let us reject non-largefiles clients and make them display
115 # these let us reject non-largefiles clients and make them display
116 # our error messages
116 # our error messages
117 protocol.webproto.refuseclient = proto.webproto_refuseclient
117 protocol.webproto.refuseclient = proto.webproto_refuseclient
118 sshserver.sshserver.refuseclient = proto.sshproto_refuseclient
118 sshserver.sshserver.refuseclient = proto.sshproto_refuseclient
119
119
120 # can't do this in reposetup because it needs to have happened before
120 # can't do this in reposetup because it needs to have happened before
121 # wirerepo.__init__ is called
121 # wirerepo.__init__ is called
122 proto.ssh_oldcallstream = sshrepo.sshrepository._callstream
122 proto.ssh_oldcallstream = sshrepo.sshrepository._callstream
123 proto.http_oldcallstream = httprepo.httprepository._callstream
123 proto.http_oldcallstream = httprepo.httprepository._callstream
124 sshrepo.sshrepository._callstream = proto.sshrepo_callstream
124 sshrepo.sshrepository._callstream = proto.sshrepo_callstream
125 httprepo.httprepository._callstream = proto.httprepo_callstream
125 httprepo.httprepository._callstream = proto.httprepo_callstream
126
126
127 # don't die on seeing a repo with the largefiles requirement
127 # don't die on seeing a repo with the largefiles requirement
128 localrepo.localrepository.supported |= set(['largefiles'])
128 localrepo.localrepository.supported |= set(['largefiles'])
129
129
130 # override some extensions' stuff as well
130 # override some extensions' stuff as well
131 for name, module in extensions.extensions():
131 for name, module in extensions.extensions():
132 if name == 'fetch':
132 if name == 'fetch':
133 extensions.wrapcommand(getattr(module, 'cmdtable'), 'fetch',
133 extensions.wrapcommand(getattr(module, 'cmdtable'), 'fetch',
134 overrides.override_fetch)
134 overrides.override_fetch)
135 if name == 'purge':
135 if name == 'purge':
136 extensions.wrapcommand(getattr(module, 'cmdtable'), 'purge',
136 extensions.wrapcommand(getattr(module, 'cmdtable'), 'purge',
137 overrides.override_purge)
137 overrides.override_purge)
138 if name == 'rebase':
138 if name == 'rebase':
139 extensions.wrapcommand(getattr(module, 'cmdtable'), 'rebase',
139 extensions.wrapcommand(getattr(module, 'cmdtable'), 'rebase',
140 overrides.override_rebase)
140 overrides.override_rebase)
141 if name == 'transplant':
141 if name == 'transplant':
142 extensions.wrapcommand(getattr(module, 'cmdtable'), 'transplant',
142 extensions.wrapcommand(getattr(module, 'cmdtable'), 'transplant',
143 overrides.override_transplant)
143 overrides.override_transplant)
@@ -1,592 +1,589 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 from node import nullid, nullrev, hex, bin
8 from node import nullid, nullrev, hex, bin
9 from i18n import _
9 from i18n import _
10 import scmutil, util, filemerge, copies, subrepo
10 import scmutil, util, filemerge, copies, subrepo
11 import errno, os, shutil
11 import errno, os, shutil
12
12
13 class mergestate(object):
13 class mergestate(object):
14 '''track 3-way merge state of individual files'''
14 '''track 3-way merge state of individual files'''
15 def __init__(self, repo):
15 def __init__(self, repo):
16 self._repo = repo
16 self._repo = repo
17 self._dirty = False
17 self._dirty = False
18 self._read()
18 self._read()
19 def reset(self, node=None):
19 def reset(self, node=None):
20 self._state = {}
20 self._state = {}
21 if node:
21 if node:
22 self._local = node
22 self._local = node
23 shutil.rmtree(self._repo.join("merge"), True)
23 shutil.rmtree(self._repo.join("merge"), True)
24 self._dirty = False
24 self._dirty = False
25 def _read(self):
25 def _read(self):
26 self._state = {}
26 self._state = {}
27 try:
27 try:
28 f = self._repo.opener("merge/state")
28 f = self._repo.opener("merge/state")
29 for i, l in enumerate(f):
29 for i, l in enumerate(f):
30 if i == 0:
30 if i == 0:
31 self._local = bin(l[:-1])
31 self._local = bin(l[:-1])
32 else:
32 else:
33 bits = l[:-1].split("\0")
33 bits = l[:-1].split("\0")
34 self._state[bits[0]] = bits[1:]
34 self._state[bits[0]] = bits[1:]
35 f.close()
35 f.close()
36 except IOError, err:
36 except IOError, err:
37 if err.errno != errno.ENOENT:
37 if err.errno != errno.ENOENT:
38 raise
38 raise
39 self._dirty = False
39 self._dirty = False
40 def commit(self):
40 def commit(self):
41 if self._dirty:
41 if self._dirty:
42 f = self._repo.opener("merge/state", "w")
42 f = self._repo.opener("merge/state", "w")
43 f.write(hex(self._local) + "\n")
43 f.write(hex(self._local) + "\n")
44 for d, v in self._state.iteritems():
44 for d, v in self._state.iteritems():
45 f.write("\0".join([d] + v) + "\n")
45 f.write("\0".join([d] + v) + "\n")
46 f.close()
46 f.close()
47 self._dirty = False
47 self._dirty = False
48 def add(self, fcl, fco, fca, fd, flags):
48 def add(self, fcl, fco, fca, fd, flags):
49 hash = util.sha1(fcl.path()).hexdigest()
49 hash = util.sha1(fcl.path()).hexdigest()
50 self._repo.opener.write("merge/" + hash, fcl.data())
50 self._repo.opener.write("merge/" + hash, fcl.data())
51 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
51 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
52 hex(fca.filenode()), fco.path(), flags]
52 hex(fca.filenode()), fco.path(), flags]
53 self._dirty = True
53 self._dirty = True
54 def __contains__(self, dfile):
54 def __contains__(self, dfile):
55 return dfile in self._state
55 return dfile in self._state
56 def __getitem__(self, dfile):
56 def __getitem__(self, dfile):
57 return self._state[dfile][0]
57 return self._state[dfile][0]
58 def __iter__(self):
58 def __iter__(self):
59 l = self._state.keys()
59 l = self._state.keys()
60 l.sort()
60 l.sort()
61 for f in l:
61 for f in l:
62 yield f
62 yield f
63 def mark(self, dfile, state):
63 def mark(self, dfile, state):
64 self._state[dfile][0] = state
64 self._state[dfile][0] = state
65 self._dirty = True
65 self._dirty = True
66 def resolve(self, dfile, wctx, octx):
66 def resolve(self, dfile, wctx, octx):
67 if self[dfile] == 'r':
67 if self[dfile] == 'r':
68 return 0
68 return 0
69 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
69 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
70 f = self._repo.opener("merge/" + hash)
70 f = self._repo.opener("merge/" + hash)
71 self._repo.wwrite(dfile, f.read(), flags)
71 self._repo.wwrite(dfile, f.read(), flags)
72 f.close()
72 f.close()
73 fcd = wctx[dfile]
73 fcd = wctx[dfile]
74 fco = octx[ofile]
74 fco = octx[ofile]
75 fca = self._repo.filectx(afile, fileid=anode)
75 fca = self._repo.filectx(afile, fileid=anode)
76 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
76 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
77 if r is None:
77 if r is None:
78 # no real conflict
78 # no real conflict
79 del self._state[dfile]
79 del self._state[dfile]
80 elif not r:
80 elif not r:
81 self.mark(dfile, 'r')
81 self.mark(dfile, 'r')
82 return r
82 return r
83
83
84 def _checkunknown(wctx, mctx, folding):
84 def _checkunknownfile(repo, wctx, mctx, f):
85 return (not repo.dirstate._ignore(f)
86 and os.path.exists(repo.wjoin(f))
87 and mctx[f].cmp(wctx[f]))
88
89 def _checkunknown(repo, wctx, mctx):
85 "check for collisions between unknown files and files in mctx"
90 "check for collisions between unknown files and files in mctx"
86 if folding:
87 foldf = util.normcase
88 else:
89 foldf = lambda fn: fn
90 folded = {}
91 for fn in mctx:
92 folded[foldf(fn)] = fn
93
91
94 error = False
92 error = False
95 for fn in wctx.unknown():
93 for f in mctx:
96 f = foldf(fn)
94 if f not in wctx and _checkunknownfile(repo, wctx, mctx, f):
97 if f in folded and mctx[folded[f]].cmp(wctx[f]):
98 error = True
95 error = True
99 wctx._repo.ui.warn(_("%s: untracked file differs\n") % fn)
96 wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
100 if error:
97 if error:
101 raise util.Abort(_("untracked files in working directory differ "
98 raise util.Abort(_("untracked files in working directory differ "
102 "from files in requested revision"))
99 "from files in requested revision"))
103
100
104 def _checkcollision(mctx, wctx):
101 def _checkcollision(mctx, wctx):
105 "check for case folding collisions in the destination context"
102 "check for case folding collisions in the destination context"
106 folded = {}
103 folded = {}
107 for fn in mctx:
104 for fn in mctx:
108 fold = util.normcase(fn)
105 fold = util.normcase(fn)
109 if fold in folded:
106 if fold in folded:
110 raise util.Abort(_("case-folding collision between %s and %s")
107 raise util.Abort(_("case-folding collision between %s and %s")
111 % (fn, folded[fold]))
108 % (fn, folded[fold]))
112 folded[fold] = fn
109 folded[fold] = fn
113
110
114 if wctx:
111 if wctx:
115 for fn in wctx:
112 for fn in wctx:
116 fold = util.normcase(fn)
113 fold = util.normcase(fn)
117 mfn = folded.get(fold, None)
114 mfn = folded.get(fold, None)
118 if mfn and (mfn != fn):
115 if mfn and (mfn != fn):
119 raise util.Abort(_("case-folding collision between %s and %s")
116 raise util.Abort(_("case-folding collision between %s and %s")
120 % (mfn, fn))
117 % (mfn, fn))
121
118
122 def _forgetremoved(wctx, mctx, branchmerge):
119 def _forgetremoved(wctx, mctx, branchmerge):
123 """
120 """
124 Forget removed files
121 Forget removed files
125
122
126 If we're jumping between revisions (as opposed to merging), and if
123 If we're jumping between revisions (as opposed to merging), and if
127 neither the working directory nor the target rev has the file,
124 neither the working directory nor the target rev has the file,
128 then we need to remove it from the dirstate, to prevent the
125 then we need to remove it from the dirstate, to prevent the
129 dirstate from listing the file when it is no longer in the
126 dirstate from listing the file when it is no longer in the
130 manifest.
127 manifest.
131
128
132 If we're merging, and the other revision has removed a file
129 If we're merging, and the other revision has removed a file
133 that is not present in the working directory, we need to mark it
130 that is not present in the working directory, we need to mark it
134 as removed.
131 as removed.
135 """
132 """
136
133
137 action = []
134 action = []
138 state = branchmerge and 'r' or 'f'
135 state = branchmerge and 'r' or 'f'
139 for f in wctx.deleted():
136 for f in wctx.deleted():
140 if f not in mctx:
137 if f not in mctx:
141 action.append((f, state))
138 action.append((f, state))
142
139
143 if not branchmerge:
140 if not branchmerge:
144 for f in wctx.removed():
141 for f in wctx.removed():
145 if f not in mctx:
142 if f not in mctx:
146 action.append((f, "f"))
143 action.append((f, "f"))
147
144
148 return action
145 return action
149
146
150 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
147 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
151 """
148 """
152 Merge p1 and p2 with ancestor pa and generate merge action list
149 Merge p1 and p2 with ancestor pa and generate merge action list
153
150
154 overwrite = whether we clobber working files
151 overwrite = whether we clobber working files
155 partial = function to filter file lists
152 partial = function to filter file lists
156 """
153 """
157
154
158 def fmerge(f, f2, fa):
155 def fmerge(f, f2, fa):
159 """merge flags"""
156 """merge flags"""
160 a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
157 a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
161 if m == n: # flags agree
158 if m == n: # flags agree
162 return m # unchanged
159 return m # unchanged
163 if m and n and not a: # flags set, don't agree, differ from parent
160 if m and n and not a: # flags set, don't agree, differ from parent
164 r = repo.ui.promptchoice(
161 r = repo.ui.promptchoice(
165 _(" conflicting flags for %s\n"
162 _(" conflicting flags for %s\n"
166 "(n)one, e(x)ec or sym(l)ink?") % f,
163 "(n)one, e(x)ec or sym(l)ink?") % f,
167 (_("&None"), _("E&xec"), _("Sym&link")), 0)
164 (_("&None"), _("E&xec"), _("Sym&link")), 0)
168 if r == 1:
165 if r == 1:
169 return "x" # Exec
166 return "x" # Exec
170 if r == 2:
167 if r == 2:
171 return "l" # Symlink
168 return "l" # Symlink
172 return ""
169 return ""
173 if m and m != a: # changed from a to m
170 if m and m != a: # changed from a to m
174 return m
171 return m
175 if n and n != a: # changed from a to n
172 if n and n != a: # changed from a to n
176 if n == 'l' or a == 'l':
173 if n == 'l' or a == 'l':
177 # can't automatically merge symlink flag change here, let
174 # can't automatically merge symlink flag change here, let
178 # filemerge take care of it
175 # filemerge take care of it
179 return m
176 return m
180 return n
177 return n
181 return '' # flag was cleared
178 return '' # flag was cleared
182
179
183 def act(msg, m, f, *args):
180 def act(msg, m, f, *args):
184 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
181 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
185 action.append((f, m) + args)
182 action.append((f, m) + args)
186
183
187 action, copy = [], {}
184 action, copy = [], {}
188
185
189 if overwrite:
186 if overwrite:
190 pa = p1
187 pa = p1
191 elif pa == p2: # backwards
188 elif pa == p2: # backwards
192 pa = p1.p1()
189 pa = p1.p1()
193 elif pa and repo.ui.configbool("merge", "followcopies", True):
190 elif pa and repo.ui.configbool("merge", "followcopies", True):
194 dirs = repo.ui.configbool("merge", "followdirs", True)
191 dirs = repo.ui.configbool("merge", "followdirs", True)
195 copy, diverge = copies.mergecopies(repo, p1, p2, pa, dirs)
192 copy, diverge = copies.mergecopies(repo, p1, p2, pa, dirs)
196 for of, fl in diverge.iteritems():
193 for of, fl in diverge.iteritems():
197 act("divergent renames", "dr", of, fl)
194 act("divergent renames", "dr", of, fl)
198
195
199 repo.ui.note(_("resolving manifests\n"))
196 repo.ui.note(_("resolving manifests\n"))
200 repo.ui.debug(" overwrite: %s, partial: %s\n"
197 repo.ui.debug(" overwrite: %s, partial: %s\n"
201 % (bool(overwrite), bool(partial)))
198 % (bool(overwrite), bool(partial)))
202 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, p1, p2))
199 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, p1, p2))
203
200
204 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
201 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
205 copied = set(copy.values())
202 copied = set(copy.values())
206
203
207 if '.hgsubstate' in m1:
204 if '.hgsubstate' in m1:
208 # check whether sub state is modified
205 # check whether sub state is modified
209 for s in p1.substate:
206 for s in p1.substate:
210 if p1.sub(s).dirty():
207 if p1.sub(s).dirty():
211 m1['.hgsubstate'] += "+"
208 m1['.hgsubstate'] += "+"
212 break
209 break
213
210
214 # Compare manifests
211 # Compare manifests
215 for f, n in m1.iteritems():
212 for f, n in m1.iteritems():
216 if partial and not partial(f):
213 if partial and not partial(f):
217 continue
214 continue
218 if f in m2:
215 if f in m2:
219 rflags = fmerge(f, f, f)
216 rflags = fmerge(f, f, f)
220 a = ma.get(f, nullid)
217 a = ma.get(f, nullid)
221 if n == m2[f] or m2[f] == a: # same or local newer
218 if n == m2[f] or m2[f] == a: # same or local newer
222 # is file locally modified or flags need changing?
219 # is file locally modified or flags need changing?
223 # dirstate flags may need to be made current
220 # dirstate flags may need to be made current
224 if m1.flags(f) != rflags or n[20:]:
221 if m1.flags(f) != rflags or n[20:]:
225 act("update permissions", "e", f, rflags)
222 act("update permissions", "e", f, rflags)
226 elif n == a: # remote newer
223 elif n == a: # remote newer
227 act("remote is newer", "g", f, rflags)
224 act("remote is newer", "g", f, rflags)
228 else: # both changed
225 else: # both changed
229 act("versions differ", "m", f, f, f, rflags, False)
226 act("versions differ", "m", f, f, f, rflags, False)
230 elif f in copied: # files we'll deal with on m2 side
227 elif f in copied: # files we'll deal with on m2 side
231 pass
228 pass
232 elif f in copy:
229 elif f in copy:
233 f2 = copy[f]
230 f2 = copy[f]
234 if f2 not in m2: # directory rename
231 if f2 not in m2: # directory rename
235 act("remote renamed directory to " + f2, "d",
232 act("remote renamed directory to " + f2, "d",
236 f, None, f2, m1.flags(f))
233 f, None, f2, m1.flags(f))
237 else: # case 2 A,B/B/B or case 4,21 A/B/B
234 else: # case 2 A,B/B/B or case 4,21 A/B/B
238 act("local copied/moved to " + f2, "m",
235 act("local copied/moved to " + f2, "m",
239 f, f2, f, fmerge(f, f2, f2), False)
236 f, f2, f, fmerge(f, f2, f2), False)
240 elif f in ma: # clean, a different, no remote
237 elif f in ma: # clean, a different, no remote
241 if n != ma[f]:
238 if n != ma[f]:
242 if repo.ui.promptchoice(
239 if repo.ui.promptchoice(
243 _(" local changed %s which remote deleted\n"
240 _(" local changed %s which remote deleted\n"
244 "use (c)hanged version or (d)elete?") % f,
241 "use (c)hanged version or (d)elete?") % f,
245 (_("&Changed"), _("&Delete")), 0):
242 (_("&Changed"), _("&Delete")), 0):
246 act("prompt delete", "r", f)
243 act("prompt delete", "r", f)
247 else:
244 else:
248 act("prompt keep", "a", f)
245 act("prompt keep", "a", f)
249 elif n[20:] == "a": # added, no remote
246 elif n[20:] == "a": # added, no remote
250 act("remote deleted", "f", f)
247 act("remote deleted", "f", f)
251 elif n[20:] != "u":
248 elif n[20:] != "u":
252 act("other deleted", "r", f)
249 act("other deleted", "r", f)
253
250
254 for f, n in m2.iteritems():
251 for f, n in m2.iteritems():
255 if partial and not partial(f):
252 if partial and not partial(f):
256 continue
253 continue
257 if f in m1 or f in copied: # files already visited
254 if f in m1 or f in copied: # files already visited
258 continue
255 continue
259 if f in copy:
256 if f in copy:
260 f2 = copy[f]
257 f2 = copy[f]
261 if f2 not in m1: # directory rename
258 if f2 not in m1: # directory rename
262 act("local renamed directory to " + f2, "d",
259 act("local renamed directory to " + f2, "d",
263 None, f, f2, m2.flags(f))
260 None, f, f2, m2.flags(f))
264 elif f2 in m2: # rename case 1, A/A,B/A
261 elif f2 in m2: # rename case 1, A/A,B/A
265 act("remote copied to " + f, "m",
262 act("remote copied to " + f, "m",
266 f2, f, f, fmerge(f2, f, f2), False)
263 f2, f, f, fmerge(f2, f, f2), False)
267 else: # case 3,20 A/B/A
264 else: # case 3,20 A/B/A
268 act("remote moved to " + f, "m",
265 act("remote moved to " + f, "m",
269 f2, f, f, fmerge(f2, f, f2), True)
266 f2, f, f, fmerge(f2, f, f2), True)
270 elif f not in ma:
267 elif f not in ma:
271 act("remote created", "g", f, m2.flags(f))
268 act("remote created", "g", f, m2.flags(f))
272 elif n != ma[f]:
269 elif n != ma[f]:
273 if repo.ui.promptchoice(
270 if repo.ui.promptchoice(
274 _("remote changed %s which local deleted\n"
271 _("remote changed %s which local deleted\n"
275 "use (c)hanged version or leave (d)eleted?") % f,
272 "use (c)hanged version or leave (d)eleted?") % f,
276 (_("&Changed"), _("&Deleted")), 0) == 0:
273 (_("&Changed"), _("&Deleted")), 0) == 0:
277 act("prompt recreating", "g", f, m2.flags(f))
274 act("prompt recreating", "g", f, m2.flags(f))
278
275
279 return action
276 return action
280
277
281 def actionkey(a):
278 def actionkey(a):
282 return a[1] == 'r' and -1 or 0, a
279 return a[1] == 'r' and -1 or 0, a
283
280
284 def applyupdates(repo, action, wctx, mctx, actx, overwrite):
281 def applyupdates(repo, action, wctx, mctx, actx, overwrite):
285 """apply the merge action list to the working directory
282 """apply the merge action list to the working directory
286
283
287 wctx is the working copy context
284 wctx is the working copy context
288 mctx is the context to be merged into the working copy
285 mctx is the context to be merged into the working copy
289 actx is the context of the common ancestor
286 actx is the context of the common ancestor
290
287
291 Return a tuple of counts (updated, merged, removed, unresolved) that
288 Return a tuple of counts (updated, merged, removed, unresolved) that
292 describes how many files were affected by the update.
289 describes how many files were affected by the update.
293 """
290 """
294
291
295 updated, merged, removed, unresolved = 0, 0, 0, 0
292 updated, merged, removed, unresolved = 0, 0, 0, 0
296 ms = mergestate(repo)
293 ms = mergestate(repo)
297 ms.reset(wctx.p1().node())
294 ms.reset(wctx.p1().node())
298 moves = []
295 moves = []
299 action.sort(key=actionkey)
296 action.sort(key=actionkey)
300
297
301 # prescan for merges
298 # prescan for merges
302 for a in action:
299 for a in action:
303 f, m = a[:2]
300 f, m = a[:2]
304 if m == 'm': # merge
301 if m == 'm': # merge
305 f2, fd, flags, move = a[2:]
302 f2, fd, flags, move = a[2:]
306 if f == '.hgsubstate': # merged internally
303 if f == '.hgsubstate': # merged internally
307 continue
304 continue
308 repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd))
305 repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd))
309 fcl = wctx[f]
306 fcl = wctx[f]
310 fco = mctx[f2]
307 fco = mctx[f2]
311 if mctx == actx: # backwards, use working dir parent as ancestor
308 if mctx == actx: # backwards, use working dir parent as ancestor
312 if fcl.parents():
309 if fcl.parents():
313 fca = fcl.p1()
310 fca = fcl.p1()
314 else:
311 else:
315 fca = repo.filectx(f, fileid=nullrev)
312 fca = repo.filectx(f, fileid=nullrev)
316 else:
313 else:
317 fca = fcl.ancestor(fco, actx)
314 fca = fcl.ancestor(fco, actx)
318 if not fca:
315 if not fca:
319 fca = repo.filectx(f, fileid=nullrev)
316 fca = repo.filectx(f, fileid=nullrev)
320 ms.add(fcl, fco, fca, fd, flags)
317 ms.add(fcl, fco, fca, fd, flags)
321 if f != fd and move:
318 if f != fd and move:
322 moves.append(f)
319 moves.append(f)
323
320
324 audit = scmutil.pathauditor(repo.root)
321 audit = scmutil.pathauditor(repo.root)
325
322
326 # remove renamed files after safely stored
323 # remove renamed files after safely stored
327 for f in moves:
324 for f in moves:
328 if os.path.lexists(repo.wjoin(f)):
325 if os.path.lexists(repo.wjoin(f)):
329 repo.ui.debug("removing %s\n" % f)
326 repo.ui.debug("removing %s\n" % f)
330 audit(f)
327 audit(f)
331 os.unlink(repo.wjoin(f))
328 os.unlink(repo.wjoin(f))
332
329
333 numupdates = len(action)
330 numupdates = len(action)
334 for i, a in enumerate(action):
331 for i, a in enumerate(action):
335 f, m = a[:2]
332 f, m = a[:2]
336 repo.ui.progress(_('updating'), i + 1, item=f, total=numupdates,
333 repo.ui.progress(_('updating'), i + 1, item=f, total=numupdates,
337 unit=_('files'))
334 unit=_('files'))
338 if f and f[0] == "/":
335 if f and f[0] == "/":
339 continue
336 continue
340 if m == "r": # remove
337 if m == "r": # remove
341 repo.ui.note(_("removing %s\n") % f)
338 repo.ui.note(_("removing %s\n") % f)
342 audit(f)
339 audit(f)
343 if f == '.hgsubstate': # subrepo states need updating
340 if f == '.hgsubstate': # subrepo states need updating
344 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
341 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
345 try:
342 try:
346 util.unlinkpath(repo.wjoin(f))
343 util.unlinkpath(repo.wjoin(f))
347 except OSError, inst:
344 except OSError, inst:
348 if inst.errno != errno.ENOENT:
345 if inst.errno != errno.ENOENT:
349 repo.ui.warn(_("update failed to remove %s: %s!\n") %
346 repo.ui.warn(_("update failed to remove %s: %s!\n") %
350 (f, inst.strerror))
347 (f, inst.strerror))
351 removed += 1
348 removed += 1
352 elif m == "m": # merge
349 elif m == "m": # merge
353 if f == '.hgsubstate': # subrepo states need updating
350 if f == '.hgsubstate': # subrepo states need updating
354 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx), overwrite)
351 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx), overwrite)
355 continue
352 continue
356 f2, fd, flags, move = a[2:]
353 f2, fd, flags, move = a[2:]
357 repo.wopener.audit(fd)
354 repo.wopener.audit(fd)
358 r = ms.resolve(fd, wctx, mctx)
355 r = ms.resolve(fd, wctx, mctx)
359 if r is not None and r > 0:
356 if r is not None and r > 0:
360 unresolved += 1
357 unresolved += 1
361 else:
358 else:
362 if r is None:
359 if r is None:
363 updated += 1
360 updated += 1
364 else:
361 else:
365 merged += 1
362 merged += 1
366 if (move and repo.dirstate.normalize(fd) != f
363 if (move and repo.dirstate.normalize(fd) != f
367 and os.path.lexists(repo.wjoin(f))):
364 and os.path.lexists(repo.wjoin(f))):
368 repo.ui.debug("removing %s\n" % f)
365 repo.ui.debug("removing %s\n" % f)
369 audit(f)
366 audit(f)
370 os.unlink(repo.wjoin(f))
367 os.unlink(repo.wjoin(f))
371 elif m == "g": # get
368 elif m == "g": # get
372 flags = a[2]
369 flags = a[2]
373 repo.ui.note(_("getting %s\n") % f)
370 repo.ui.note(_("getting %s\n") % f)
374 t = mctx.filectx(f).data()
371 t = mctx.filectx(f).data()
375 repo.wwrite(f, t, flags)
372 repo.wwrite(f, t, flags)
376 t = None
373 t = None
377 updated += 1
374 updated += 1
378 if f == '.hgsubstate': # subrepo states need updating
375 if f == '.hgsubstate': # subrepo states need updating
379 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
376 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
380 elif m == "d": # directory rename
377 elif m == "d": # directory rename
381 f2, fd, flags = a[2:]
378 f2, fd, flags = a[2:]
382 if f:
379 if f:
383 repo.ui.note(_("moving %s to %s\n") % (f, fd))
380 repo.ui.note(_("moving %s to %s\n") % (f, fd))
384 audit(f)
381 audit(f)
385 t = wctx.filectx(f).data()
382 t = wctx.filectx(f).data()
386 repo.wwrite(fd, t, flags)
383 repo.wwrite(fd, t, flags)
387 util.unlinkpath(repo.wjoin(f))
384 util.unlinkpath(repo.wjoin(f))
388 if f2:
385 if f2:
389 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
386 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
390 t = mctx.filectx(f2).data()
387 t = mctx.filectx(f2).data()
391 repo.wwrite(fd, t, flags)
388 repo.wwrite(fd, t, flags)
392 updated += 1
389 updated += 1
393 elif m == "dr": # divergent renames
390 elif m == "dr": # divergent renames
394 fl = a[2]
391 fl = a[2]
395 repo.ui.warn(_("note: possible conflict - %s was renamed "
392 repo.ui.warn(_("note: possible conflict - %s was renamed "
396 "multiple times to:\n") % f)
393 "multiple times to:\n") % f)
397 for nf in fl:
394 for nf in fl:
398 repo.ui.warn(" %s\n" % nf)
395 repo.ui.warn(" %s\n" % nf)
399 elif m == "e": # exec
396 elif m == "e": # exec
400 flags = a[2]
397 flags = a[2]
401 repo.wopener.audit(f)
398 repo.wopener.audit(f)
402 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
399 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
403 ms.commit()
400 ms.commit()
404 repo.ui.progress(_('updating'), None, total=numupdates, unit=_('files'))
401 repo.ui.progress(_('updating'), None, total=numupdates, unit=_('files'))
405
402
406 return updated, merged, removed, unresolved
403 return updated, merged, removed, unresolved
407
404
408 def recordupdates(repo, action, branchmerge):
405 def recordupdates(repo, action, branchmerge):
409 "record merge actions to the dirstate"
406 "record merge actions to the dirstate"
410
407
411 for a in action:
408 for a in action:
412 f, m = a[:2]
409 f, m = a[:2]
413 if m == "r": # remove
410 if m == "r": # remove
414 if branchmerge:
411 if branchmerge:
415 repo.dirstate.remove(f)
412 repo.dirstate.remove(f)
416 else:
413 else:
417 repo.dirstate.drop(f)
414 repo.dirstate.drop(f)
418 elif m == "a": # re-add
415 elif m == "a": # re-add
419 if not branchmerge:
416 if not branchmerge:
420 repo.dirstate.add(f)
417 repo.dirstate.add(f)
421 elif m == "f": # forget
418 elif m == "f": # forget
422 repo.dirstate.drop(f)
419 repo.dirstate.drop(f)
423 elif m == "e": # exec change
420 elif m == "e": # exec change
424 repo.dirstate.normallookup(f)
421 repo.dirstate.normallookup(f)
425 elif m == "g": # get
422 elif m == "g": # get
426 if branchmerge:
423 if branchmerge:
427 repo.dirstate.otherparent(f)
424 repo.dirstate.otherparent(f)
428 else:
425 else:
429 repo.dirstate.normal(f)
426 repo.dirstate.normal(f)
430 elif m == "m": # merge
427 elif m == "m": # merge
431 f2, fd, flag, move = a[2:]
428 f2, fd, flag, move = a[2:]
432 if branchmerge:
429 if branchmerge:
433 # We've done a branch merge, mark this file as merged
430 # We've done a branch merge, mark this file as merged
434 # so that we properly record the merger later
431 # so that we properly record the merger later
435 repo.dirstate.merge(fd)
432 repo.dirstate.merge(fd)
436 if f != f2: # copy/rename
433 if f != f2: # copy/rename
437 if move:
434 if move:
438 repo.dirstate.remove(f)
435 repo.dirstate.remove(f)
439 if f != fd:
436 if f != fd:
440 repo.dirstate.copy(f, fd)
437 repo.dirstate.copy(f, fd)
441 else:
438 else:
442 repo.dirstate.copy(f2, fd)
439 repo.dirstate.copy(f2, fd)
443 else:
440 else:
444 # We've update-merged a locally modified file, so
441 # We've update-merged a locally modified file, so
445 # we set the dirstate to emulate a normal checkout
442 # we set the dirstate to emulate a normal checkout
446 # of that file some time in the past. Thus our
443 # of that file some time in the past. Thus our
447 # merge will appear as a normal local file
444 # merge will appear as a normal local file
448 # modification.
445 # modification.
449 if f2 == fd: # file not locally copied/moved
446 if f2 == fd: # file not locally copied/moved
450 repo.dirstate.normallookup(fd)
447 repo.dirstate.normallookup(fd)
451 if move:
448 if move:
452 repo.dirstate.drop(f)
449 repo.dirstate.drop(f)
453 elif m == "d": # directory rename
450 elif m == "d": # directory rename
454 f2, fd, flag = a[2:]
451 f2, fd, flag = a[2:]
455 if not f2 and f not in repo.dirstate:
452 if not f2 and f not in repo.dirstate:
456 # untracked file moved
453 # untracked file moved
457 continue
454 continue
458 if branchmerge:
455 if branchmerge:
459 repo.dirstate.add(fd)
456 repo.dirstate.add(fd)
460 if f:
457 if f:
461 repo.dirstate.remove(f)
458 repo.dirstate.remove(f)
462 repo.dirstate.copy(f, fd)
459 repo.dirstate.copy(f, fd)
463 if f2:
460 if f2:
464 repo.dirstate.copy(f2, fd)
461 repo.dirstate.copy(f2, fd)
465 else:
462 else:
466 repo.dirstate.normal(fd)
463 repo.dirstate.normal(fd)
467 if f:
464 if f:
468 repo.dirstate.drop(f)
465 repo.dirstate.drop(f)
469
466
470 def update(repo, node, branchmerge, force, partial, ancestor=None):
467 def update(repo, node, branchmerge, force, partial, ancestor=None):
471 """
468 """
472 Perform a merge between the working directory and the given node
469 Perform a merge between the working directory and the given node
473
470
474 node = the node to update to, or None if unspecified
471 node = the node to update to, or None if unspecified
475 branchmerge = whether to merge between branches
472 branchmerge = whether to merge between branches
476 force = whether to force branch merging or file overwriting
473 force = whether to force branch merging or file overwriting
477 partial = a function to filter file lists (dirstate not updated)
474 partial = a function to filter file lists (dirstate not updated)
478
475
479 The table below shows all the behaviors of the update command
476 The table below shows all the behaviors of the update command
480 given the -c and -C or no options, whether the working directory
477 given the -c and -C or no options, whether the working directory
481 is dirty, whether a revision is specified, and the relationship of
478 is dirty, whether a revision is specified, and the relationship of
482 the parent rev to the target rev (linear, on the same named
479 the parent rev to the target rev (linear, on the same named
483 branch, or on another named branch).
480 branch, or on another named branch).
484
481
485 This logic is tested by test-update-branches.t.
482 This logic is tested by test-update-branches.t.
486
483
487 -c -C dirty rev | linear same cross
484 -c -C dirty rev | linear same cross
488 n n n n | ok (1) x
485 n n n n | ok (1) x
489 n n n y | ok ok ok
486 n n n y | ok ok ok
490 n n y * | merge (2) (2)
487 n n y * | merge (2) (2)
491 n y * * | --- discard ---
488 n y * * | --- discard ---
492 y n y * | --- (3) ---
489 y n y * | --- (3) ---
493 y n n * | --- ok ---
490 y n n * | --- ok ---
494 y y * * | --- (4) ---
491 y y * * | --- (4) ---
495
492
496 x = can't happen
493 x = can't happen
497 * = don't-care
494 * = don't-care
498 1 = abort: crosses branches (use 'hg merge' or 'hg update -c')
495 1 = abort: crosses branches (use 'hg merge' or 'hg update -c')
499 2 = abort: crosses branches (use 'hg merge' to merge or
496 2 = abort: crosses branches (use 'hg merge' to merge or
500 use 'hg update -C' to discard changes)
497 use 'hg update -C' to discard changes)
501 3 = abort: uncommitted local changes
498 3 = abort: uncommitted local changes
502 4 = incompatible options (checked in commands.py)
499 4 = incompatible options (checked in commands.py)
503
500
504 Return the same tuple as applyupdates().
501 Return the same tuple as applyupdates().
505 """
502 """
506
503
507 onode = node
504 onode = node
508 wlock = repo.wlock()
505 wlock = repo.wlock()
509 try:
506 try:
510 wc = repo[None]
507 wc = repo[None]
511 if node is None:
508 if node is None:
512 # tip of current branch
509 # tip of current branch
513 try:
510 try:
514 node = repo.branchtags()[wc.branch()]
511 node = repo.branchtags()[wc.branch()]
515 except KeyError:
512 except KeyError:
516 if wc.branch() == "default": # no default branch!
513 if wc.branch() == "default": # no default branch!
517 node = repo.lookup("tip") # update to tip
514 node = repo.lookup("tip") # update to tip
518 else:
515 else:
519 raise util.Abort(_("branch %s not found") % wc.branch())
516 raise util.Abort(_("branch %s not found") % wc.branch())
520 overwrite = force and not branchmerge
517 overwrite = force and not branchmerge
521 pl = wc.parents()
518 pl = wc.parents()
522 p1, p2 = pl[0], repo[node]
519 p1, p2 = pl[0], repo[node]
523 if ancestor:
520 if ancestor:
524 pa = repo[ancestor]
521 pa = repo[ancestor]
525 else:
522 else:
526 pa = p1.ancestor(p2)
523 pa = p1.ancestor(p2)
527
524
528 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
525 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
529
526
530 ### check phase
527 ### check phase
531 if not overwrite and len(pl) > 1:
528 if not overwrite and len(pl) > 1:
532 raise util.Abort(_("outstanding uncommitted merges"))
529 raise util.Abort(_("outstanding uncommitted merges"))
533 if branchmerge:
530 if branchmerge:
534 if pa == p2:
531 if pa == p2:
535 raise util.Abort(_("merging with a working directory ancestor"
532 raise util.Abort(_("merging with a working directory ancestor"
536 " has no effect"))
533 " has no effect"))
537 elif pa == p1:
534 elif pa == p1:
538 if p1.branch() == p2.branch():
535 if p1.branch() == p2.branch():
539 raise util.Abort(_("nothing to merge"),
536 raise util.Abort(_("nothing to merge"),
540 hint=_("use 'hg update' "
537 hint=_("use 'hg update' "
541 "or check 'hg heads'"))
538 "or check 'hg heads'"))
542 if not force and (wc.files() or wc.deleted()):
539 if not force and (wc.files() or wc.deleted()):
543 raise util.Abort(_("outstanding uncommitted changes"),
540 raise util.Abort(_("outstanding uncommitted changes"),
544 hint=_("use 'hg status' to list changes"))
541 hint=_("use 'hg status' to list changes"))
545 for s in wc.substate:
542 for s in wc.substate:
546 if wc.sub(s).dirty():
543 if wc.sub(s).dirty():
547 raise util.Abort(_("outstanding uncommitted changes in "
544 raise util.Abort(_("outstanding uncommitted changes in "
548 "subrepository '%s'") % s)
545 "subrepository '%s'") % s)
549
546
550 elif not overwrite:
547 elif not overwrite:
551 if pa == p1 or pa == p2: # linear
548 if pa == p1 or pa == p2: # linear
552 pass # all good
549 pass # all good
553 elif wc.dirty(missing=True):
550 elif wc.dirty(missing=True):
554 raise util.Abort(_("crosses branches (merge branches or use"
551 raise util.Abort(_("crosses branches (merge branches or use"
555 " --clean to discard changes)"))
552 " --clean to discard changes)"))
556 elif onode is None:
553 elif onode is None:
557 raise util.Abort(_("crosses branches (merge branches or update"
554 raise util.Abort(_("crosses branches (merge branches or update"
558 " --check to force update)"))
555 " --check to force update)"))
559 else:
556 else:
560 # Allow jumping branches if clean and specific rev given
557 # Allow jumping branches if clean and specific rev given
561 pa = p1
558 pa = p1
562
559
563 ### calculate phase
560 ### calculate phase
564 action = []
561 action = []
565 wc.status(unknown=True) # prime cache
562 wc.status(unknown=True) # prime cache
566 folding = not util.checkcase(repo.path)
563 folding = not util.checkcase(repo.path)
567 if not force:
564 if not force:
568 _checkunknown(wc, p2, folding)
565 _checkunknown(repo, wc, p2)
569 if folding:
566 if folding:
570 _checkcollision(p2, branchmerge and p1)
567 _checkcollision(p2, branchmerge and p1)
571 action += _forgetremoved(wc, p2, branchmerge)
568 action += _forgetremoved(wc, p2, branchmerge)
572 action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
569 action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
573
570
574 ### apply phase
571 ### apply phase
575 if not branchmerge: # just jump to the new rev
572 if not branchmerge: # just jump to the new rev
576 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
573 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
577 if not partial:
574 if not partial:
578 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
575 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
579
576
580 stats = applyupdates(repo, action, wc, p2, pa, overwrite)
577 stats = applyupdates(repo, action, wc, p2, pa, overwrite)
581
578
582 if not partial:
579 if not partial:
583 repo.dirstate.setparents(fp1, fp2)
580 repo.dirstate.setparents(fp1, fp2)
584 recordupdates(repo, action, branchmerge)
581 recordupdates(repo, action, branchmerge)
585 if not branchmerge:
582 if not branchmerge:
586 repo.dirstate.setbranch(p2.branch())
583 repo.dirstate.setbranch(p2.branch())
587 finally:
584 finally:
588 wlock.release()
585 wlock.release()
589
586
590 if not partial:
587 if not partial:
591 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
588 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
592 return stats
589 return stats
General Comments 0
You need to be logged in to leave comments. Login now