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