##// END OF EJS Templates
destutil: extract all 'mergedest' abort messages into a dictionary...
Pierre-Yves David -
r28102:bd74b5e0 default
parent child Browse files
Show More
@@ -1,252 +1,282
1 # destutil.py - Mercurial utility function for command destination
1 # destutil.py - Mercurial utility function for command destination
2 #
2 #
3 # Copyright Matt Mackall <mpm@selenic.com> and other
3 # Copyright Matt Mackall <mpm@selenic.com> and other
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 __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 from .i18n import _
10 from .i18n import _
11 from . import (
11 from . import (
12 bookmarks,
12 bookmarks,
13 error,
13 error,
14 obsolete,
14 obsolete,
15 )
15 )
16
16
17 def _destupdatevalidate(repo, rev, clean, check):
17 def _destupdatevalidate(repo, rev, clean, check):
18 """validate that the destination comply to various rules
18 """validate that the destination comply to various rules
19
19
20 This exists as its own function to help wrapping from extensions."""
20 This exists as its own function to help wrapping from extensions."""
21 wc = repo[None]
21 wc = repo[None]
22 p1 = wc.p1()
22 p1 = wc.p1()
23 if not clean:
23 if not clean:
24 # Check that the update is linear.
24 # Check that the update is linear.
25 #
25 #
26 # Mercurial do not allow update-merge for non linear pattern
26 # Mercurial do not allow update-merge for non linear pattern
27 # (that would be technically possible but was considered too confusing
27 # (that would be technically possible but was considered too confusing
28 # for user a long time ago)
28 # for user a long time ago)
29 #
29 #
30 # See mercurial.merge.update for details
30 # See mercurial.merge.update for details
31 if p1.rev() not in repo.changelog.ancestors([rev], inclusive=True):
31 if p1.rev() not in repo.changelog.ancestors([rev], inclusive=True):
32 dirty = wc.dirty(missing=True)
32 dirty = wc.dirty(missing=True)
33 foreground = obsolete.foreground(repo, [p1.node()])
33 foreground = obsolete.foreground(repo, [p1.node()])
34 if not repo[rev].node() in foreground:
34 if not repo[rev].node() in foreground:
35 if dirty:
35 if dirty:
36 msg = _("uncommitted changes")
36 msg = _("uncommitted changes")
37 hint = _("commit and merge, or update --clean to"
37 hint = _("commit and merge, or update --clean to"
38 " discard changes")
38 " discard changes")
39 raise error.UpdateAbort(msg, hint=hint)
39 raise error.UpdateAbort(msg, hint=hint)
40 elif not check: # destination is not a descendant.
40 elif not check: # destination is not a descendant.
41 msg = _("not a linear update")
41 msg = _("not a linear update")
42 hint = _("merge or update --check to force update")
42 hint = _("merge or update --check to force update")
43 raise error.UpdateAbort(msg, hint=hint)
43 raise error.UpdateAbort(msg, hint=hint)
44
44
45 def _destupdateobs(repo, clean, check):
45 def _destupdateobs(repo, clean, check):
46 """decide of an update destination from obsolescence markers"""
46 """decide of an update destination from obsolescence markers"""
47 node = None
47 node = None
48 wc = repo[None]
48 wc = repo[None]
49 p1 = wc.p1()
49 p1 = wc.p1()
50 movemark = None
50 movemark = None
51
51
52 if p1.obsolete() and not p1.children():
52 if p1.obsolete() and not p1.children():
53 # allow updating to successors
53 # allow updating to successors
54 successors = obsolete.successorssets(repo, p1.node())
54 successors = obsolete.successorssets(repo, p1.node())
55
55
56 # behavior of certain cases is as follows,
56 # behavior of certain cases is as follows,
57 #
57 #
58 # divergent changesets: update to highest rev, similar to what
58 # divergent changesets: update to highest rev, similar to what
59 # is currently done when there are more than one head
59 # is currently done when there are more than one head
60 # (i.e. 'tip')
60 # (i.e. 'tip')
61 #
61 #
62 # replaced changesets: same as divergent except we know there
62 # replaced changesets: same as divergent except we know there
63 # is no conflict
63 # is no conflict
64 #
64 #
65 # pruned changeset: no update is done; though, we could
65 # pruned changeset: no update is done; though, we could
66 # consider updating to the first non-obsolete parent,
66 # consider updating to the first non-obsolete parent,
67 # similar to what is current done for 'hg prune'
67 # similar to what is current done for 'hg prune'
68
68
69 if successors:
69 if successors:
70 # flatten the list here handles both divergent (len > 1)
70 # flatten the list here handles both divergent (len > 1)
71 # and the usual case (len = 1)
71 # and the usual case (len = 1)
72 successors = [n for sub in successors for n in sub]
72 successors = [n for sub in successors for n in sub]
73
73
74 # get the max revision for the given successors set,
74 # get the max revision for the given successors set,
75 # i.e. the 'tip' of a set
75 # i.e. the 'tip' of a set
76 node = repo.revs('max(%ln)', successors).first()
76 node = repo.revs('max(%ln)', successors).first()
77 if bookmarks.isactivewdirparent(repo):
77 if bookmarks.isactivewdirparent(repo):
78 movemark = repo['.'].node()
78 movemark = repo['.'].node()
79 return node, movemark, None
79 return node, movemark, None
80
80
81 def _destupdatebook(repo, clean, check):
81 def _destupdatebook(repo, clean, check):
82 """decide on an update destination from active bookmark"""
82 """decide on an update destination from active bookmark"""
83 # we also move the active bookmark, if any
83 # we also move the active bookmark, if any
84 activemark = None
84 activemark = None
85 node, movemark = bookmarks.calculateupdate(repo.ui, repo, None)
85 node, movemark = bookmarks.calculateupdate(repo.ui, repo, None)
86 if node is not None:
86 if node is not None:
87 activemark = node
87 activemark = node
88 return node, movemark, activemark
88 return node, movemark, activemark
89
89
90 def _destupdatebranch(repo, clean, check):
90 def _destupdatebranch(repo, clean, check):
91 """decide on an update destination from current branch"""
91 """decide on an update destination from current branch"""
92 wc = repo[None]
92 wc = repo[None]
93 movemark = node = None
93 movemark = node = None
94 try:
94 try:
95 node = repo.revs('max(.::(head() and branch(%s)))'
95 node = repo.revs('max(.::(head() and branch(%s)))'
96 , wc.branch()).first()
96 , wc.branch()).first()
97 if bookmarks.isactivewdirparent(repo):
97 if bookmarks.isactivewdirparent(repo):
98 movemark = repo['.'].node()
98 movemark = repo['.'].node()
99 except error.RepoLookupError:
99 except error.RepoLookupError:
100 if wc.branch() == 'default': # no default branch!
100 if wc.branch() == 'default': # no default branch!
101 node = repo.lookup('tip') # update to tip
101 node = repo.lookup('tip') # update to tip
102 else:
102 else:
103 raise error.Abort(_("branch %s not found") % wc.branch())
103 raise error.Abort(_("branch %s not found") % wc.branch())
104 return node, movemark, None
104 return node, movemark, None
105
105
106 # order in which each step should be evalutated
106 # order in which each step should be evalutated
107 # steps are run until one finds a destination
107 # steps are run until one finds a destination
108 destupdatesteps = ['evolution', 'bookmark', 'branch']
108 destupdatesteps = ['evolution', 'bookmark', 'branch']
109 # mapping to ease extension overriding steps.
109 # mapping to ease extension overriding steps.
110 destupdatestepmap = {'evolution': _destupdateobs,
110 destupdatestepmap = {'evolution': _destupdateobs,
111 'bookmark': _destupdatebook,
111 'bookmark': _destupdatebook,
112 'branch': _destupdatebranch,
112 'branch': _destupdatebranch,
113 }
113 }
114
114
115 def destupdate(repo, clean=False, check=False):
115 def destupdate(repo, clean=False, check=False):
116 """destination for bare update operation
116 """destination for bare update operation
117
117
118 return (rev, movemark, activemark)
118 return (rev, movemark, activemark)
119
119
120 - rev: the revision to update to,
120 - rev: the revision to update to,
121 - movemark: node to move the active bookmark from
121 - movemark: node to move the active bookmark from
122 (cf bookmark.calculate update),
122 (cf bookmark.calculate update),
123 - activemark: a bookmark to activate at the end of the update.
123 - activemark: a bookmark to activate at the end of the update.
124 """
124 """
125 node = movemark = activemark = None
125 node = movemark = activemark = None
126
126
127 for step in destupdatesteps:
127 for step in destupdatesteps:
128 node, movemark, activemark = destupdatestepmap[step](repo, clean, check)
128 node, movemark, activemark = destupdatestepmap[step](repo, clean, check)
129 if node is not None:
129 if node is not None:
130 break
130 break
131 rev = repo[node].rev()
131 rev = repo[node].rev()
132
132
133 _destupdatevalidate(repo, rev, clean, check)
133 _destupdatevalidate(repo, rev, clean, check)
134
134
135 return rev, movemark, activemark
135 return rev, movemark, activemark
136
136
137 msgdestmerge = {
138 # too many matching divergent bookmark
139 'toomanybookmarks':
140 (_("multiple matching bookmarks to merge -"
141 " please merge with an explicit rev or bookmark"),
142 _("run 'hg heads' to see all heads")),
143 # no other matching divergent bookmark
144 'nootherbookmarks':
145 (_("no matching bookmark to merge - "
146 "please merge with an explicit rev or bookmark"),
147 _("run 'hg heads' to see all heads")),
148 # branch have too many unbookmarked heads, no obvious destination
149 'toomanyheads':
150 (_("branch '%s' has %d heads - please merge with an explicit rev"),
151 _("run 'hg heads .' to see heads")),
152 # branch have no other unbookmarked heads
153 'bookmarkedheads':
154 (_("heads are bookmarked - please merge with an explicit rev"),
155 _("run 'hg heads' to see all heads")),
156 # branch have just a single heads, but there is other branches
157 'nootherbranchheads':
158 (_("branch '%s' has one head - please merge with an explicit rev"),
159 _("run 'hg heads' to see all heads")),
160 # repository have a single head
161 'nootherheads':
162 (_('nothing to merge'),
163 None),
164 # repository have a single head and we are not on it
165 'nootherheadsbehind':
166 (_('nothing to merge'),
167 _("use 'hg update' instead")),
168 # We are not on a head
169 'notatheads':
170 (_('working directory not at a head revision'),
171 _("use 'hg update' or merge with an explicit revision"))
172 }
173
137 def _destmergebook(repo):
174 def _destmergebook(repo):
138 """find merge destination in the active bookmark case"""
175 """find merge destination in the active bookmark case"""
139 node = None
176 node = None
140 bmheads = repo.bookmarkheads(repo._activebookmark)
177 bmheads = repo.bookmarkheads(repo._activebookmark)
141 curhead = repo[repo._activebookmark].node()
178 curhead = repo[repo._activebookmark].node()
142 if len(bmheads) == 2:
179 if len(bmheads) == 2:
143 if curhead == bmheads[0]:
180 if curhead == bmheads[0]:
144 node = bmheads[1]
181 node = bmheads[1]
145 else:
182 else:
146 node = bmheads[0]
183 node = bmheads[0]
147 elif len(bmheads) > 2:
184 elif len(bmheads) > 2:
148 raise error.Abort(_("multiple matching bookmarks to merge - "
185 msg, hint = msgdestmerge['toomanybookmarks']
149 "please merge with an explicit rev or bookmark"),
186 raise error.Abort(msg, hint=hint)
150 hint=_("run 'hg heads' to see all heads"))
151 elif len(bmheads) <= 1:
187 elif len(bmheads) <= 1:
152 raise error.Abort(_("no matching bookmark to merge - "
188 msg, hint = msgdestmerge['nootherbookmarks']
153 "please merge with an explicit rev or bookmark"),
189 raise error.Abort(msg, hint=hint)
154 hint=_("run 'hg heads' to see all heads"))
155 assert node is not None
190 assert node is not None
156 return node
191 return node
157
192
158 def _destmergebranch(repo):
193 def _destmergebranch(repo):
159 """find merge destination based on branch heads"""
194 """find merge destination based on branch heads"""
160 node = None
195 node = None
161 branch = repo[None].branch()
196 branch = repo[None].branch()
162 bheads = repo.branchheads(branch)
197 bheads = repo.branchheads(branch)
163 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
198 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
164
199
165 if len(nbhs) > 2:
200 if len(nbhs) > 2:
166 raise error.Abort(_("branch '%s' has %d heads - "
201 msg, hint = msgdestmerge['toomanyheads']
167 "please merge with an explicit rev")
202 msg %= (branch, len(bheads))
168 % (branch, len(bheads)),
203 raise error.Abort(msg, hint=hint)
169 hint=_("run 'hg heads .' to see heads"))
170
204
171 parent = repo.dirstate.p1()
205 parent = repo.dirstate.p1()
172 if len(nbhs) <= 1:
206 if len(nbhs) <= 1:
173 if len(bheads) > 1:
207 if len(bheads) > 1:
174 raise error.Abort(_("heads are bookmarked - "
208 msg, hint = msgdestmerge['bookmarkedheads']
175 "please merge with an explicit rev"),
209 elif len(repo.heads()) > 1:
176 hint=_("run 'hg heads' to see all heads"))
210 msg, hint = msgdestmerge['nootherbranchheads']
177 if len(repo.heads()) > 1:
211 msg %= branch
178 raise error.Abort(_("branch '%s' has one head - "
212 elif parent != repo.lookup(branch):
179 "please merge with an explicit rev")
213 msg, hint = msgdestmerge['nootherheadsbehind']
180 % branch,
214 else:
181 hint=_("run 'hg heads' to see all heads"))
215 msg, hint = msgdestmerge['nootherheads']
182 msg, hint = _('nothing to merge'), None
183 if parent != repo.lookup(branch):
184 hint = _("use 'hg update' instead")
185 raise error.Abort(msg, hint=hint)
216 raise error.Abort(msg, hint=hint)
186
217
187 if parent not in bheads:
218 if parent not in bheads:
188 raise error.Abort(_('working directory not at a head revision'),
219 msg, hint = msgdestmerge['notatheads']
189 hint=_("use 'hg update' or merge with an "
220 raise error.Abort(msg, hint=hint)
190 "explicit revision"))
191 if parent == nbhs[0]:
221 if parent == nbhs[0]:
192 node = nbhs[-1]
222 node = nbhs[-1]
193 else:
223 else:
194 node = nbhs[0]
224 node = nbhs[0]
195 assert node is not None
225 assert node is not None
196 return node
226 return node
197
227
198 def destmerge(repo):
228 def destmerge(repo):
199 if repo._activebookmark:
229 if repo._activebookmark:
200 node = _destmergebook(repo)
230 node = _destmergebook(repo)
201 else:
231 else:
202 node = _destmergebranch(repo)
232 node = _destmergebranch(repo)
203 return repo[node].rev()
233 return repo[node].rev()
204
234
205 histeditdefaultrevset = 'reverse(only(.) and not public() and not ::merge())'
235 histeditdefaultrevset = 'reverse(only(.) and not public() and not ::merge())'
206
236
207 def desthistedit(ui, repo):
237 def desthistedit(ui, repo):
208 """Default base revision to edit for `hg histedit`."""
238 """Default base revision to edit for `hg histedit`."""
209 # Avoid cycle: scmutil -> revset -> destutil
239 # Avoid cycle: scmutil -> revset -> destutil
210 from . import scmutil
240 from . import scmutil
211
241
212 default = ui.config('histedit', 'defaultrev', histeditdefaultrevset)
242 default = ui.config('histedit', 'defaultrev', histeditdefaultrevset)
213 if default:
243 if default:
214 revs = scmutil.revrange(repo, [default])
244 revs = scmutil.revrange(repo, [default])
215 if revs:
245 if revs:
216 # The revset supplied by the user may not be in ascending order nor
246 # The revset supplied by the user may not be in ascending order nor
217 # take the first revision. So do this manually.
247 # take the first revision. So do this manually.
218 revs.sort()
248 revs.sort()
219 return revs.first()
249 return revs.first()
220
250
221 return None
251 return None
222
252
223 def _statusotherbook(ui, repo):
253 def _statusotherbook(ui, repo):
224 bmheads = repo.bookmarkheads(repo._activebookmark)
254 bmheads = repo.bookmarkheads(repo._activebookmark)
225 curhead = repo[repo._activebookmark].node()
255 curhead = repo[repo._activebookmark].node()
226 if repo.revs('%n and parents()', curhead):
256 if repo.revs('%n and parents()', curhead):
227 # we are on the active bookmark
257 # we are on the active bookmark
228 bmheads = [b for b in bmheads if curhead != b]
258 bmheads = [b for b in bmheads if curhead != b]
229 if bmheads:
259 if bmheads:
230 msg = _('%i other divergent bookmarks for "%s"\n')
260 msg = _('%i other divergent bookmarks for "%s"\n')
231 ui.status(msg % (len(bmheads), repo._activebookmark))
261 ui.status(msg % (len(bmheads), repo._activebookmark))
232
262
233 def _statusotherbranchheads(ui, repo):
263 def _statusotherbranchheads(ui, repo):
234 currentbranch = repo.dirstate.branch()
264 currentbranch = repo.dirstate.branch()
235 heads = repo.branchheads(currentbranch)
265 heads = repo.branchheads(currentbranch)
236 l = len(heads)
266 l = len(heads)
237 if repo.revs('%ln and parents()', heads):
267 if repo.revs('%ln and parents()', heads):
238 # we are on a head
268 # we are on a head
239 heads = repo.revs('%ln - parents()', heads)
269 heads = repo.revs('%ln - parents()', heads)
240 if heads and l != len(heads):
270 if heads and l != len(heads):
241 ui.status(_('%i other heads for branch "%s"\n') %
271 ui.status(_('%i other heads for branch "%s"\n') %
242 (len(heads), currentbranch))
272 (len(heads), currentbranch))
243
273
244 def statusotherdests(ui, repo):
274 def statusotherdests(ui, repo):
245 """Print message about other head"""
275 """Print message about other head"""
246 # XXX we should probably include a hint:
276 # XXX we should probably include a hint:
247 # - about what to do
277 # - about what to do
248 # - how to see such heads
278 # - how to see such heads
249 if repo._activebookmark:
279 if repo._activebookmark:
250 _statusotherbook(ui, repo)
280 _statusotherbook(ui, repo)
251 else:
281 else:
252 _statusotherbranchheads(ui, repo)
282 _statusotherbranchheads(ui, repo)
General Comments 0
You need to be logged in to leave comments. Login now