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