##// END OF EJS Templates
rewriteutil: also consider pending obsoletes when updating hashes in messages...
Matt Harbison -
r45994:f7e293e0 default
parent child Browse files
Show More
@@ -1,115 +1,125 b''
1 # rewriteutil.py - utility functions for rewriting changesets
1 # rewriteutil.py - utility functions for rewriting changesets
2 #
2 #
3 # Copyright 2017 Octobus <contact@octobus.net>
3 # Copyright 2017 Octobus <contact@octobus.net>
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 import re
10 import re
11
11
12 from .i18n import _
12 from .i18n import _
13
13
14 from . import (
14 from . import (
15 error,
15 error,
16 node,
16 node,
17 obsolete,
17 obsolete,
18 obsutil,
18 obsutil,
19 revset,
19 revset,
20 scmutil,
20 scmutil,
21 )
21 )
22
22
23
23
24 sha1re = re.compile(br'\b[0-9a-f]{6,40}\b')
24 sha1re = re.compile(br'\b[0-9a-f]{6,40}\b')
25
25
26
26
27 def precheck(repo, revs, action=b'rewrite'):
27 def precheck(repo, revs, action=b'rewrite'):
28 """check if revs can be rewritten
28 """check if revs can be rewritten
29 action is used to control the error message.
29 action is used to control the error message.
30
30
31 Make sure this function is called after taking the lock.
31 Make sure this function is called after taking the lock.
32 """
32 """
33 if node.nullrev in revs:
33 if node.nullrev in revs:
34 msg = _(b"cannot %s null changeset") % action
34 msg = _(b"cannot %s null changeset") % action
35 hint = _(b"no changeset checked out")
35 hint = _(b"no changeset checked out")
36 raise error.Abort(msg, hint=hint)
36 raise error.Abort(msg, hint=hint)
37
37
38 if len(repo[None].parents()) > 1:
38 if len(repo[None].parents()) > 1:
39 raise error.Abort(_(b"cannot %s while merging") % action)
39 raise error.Abort(_(b"cannot %s while merging") % action)
40
40
41 publicrevs = repo.revs(b'%ld and public()', revs)
41 publicrevs = repo.revs(b'%ld and public()', revs)
42 if publicrevs:
42 if publicrevs:
43 msg = _(b"cannot %s public changesets") % action
43 msg = _(b"cannot %s public changesets") % action
44 hint = _(b"see 'hg help phases' for details")
44 hint = _(b"see 'hg help phases' for details")
45 raise error.Abort(msg, hint=hint)
45 raise error.Abort(msg, hint=hint)
46
46
47 newunstable = disallowednewunstable(repo, revs)
47 newunstable = disallowednewunstable(repo, revs)
48 if newunstable:
48 if newunstable:
49 raise error.Abort(_(b"cannot %s changeset with children") % action)
49 raise error.Abort(_(b"cannot %s changeset with children") % action)
50
50
51
51
52 def disallowednewunstable(repo, revs):
52 def disallowednewunstable(repo, revs):
53 """Checks whether editing the revs will create new unstable changesets and
53 """Checks whether editing the revs will create new unstable changesets and
54 are we allowed to create them.
54 are we allowed to create them.
55
55
56 To allow new unstable changesets, set the config:
56 To allow new unstable changesets, set the config:
57 `experimental.evolution.allowunstable=True`
57 `experimental.evolution.allowunstable=True`
58 """
58 """
59 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
59 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
60 if allowunstable:
60 if allowunstable:
61 return revset.baseset()
61 return revset.baseset()
62 return repo.revs(b"(%ld::) - %ld", revs, revs)
62 return repo.revs(b"(%ld::) - %ld", revs, revs)
63
63
64
64
65 def skip_empty_successor(ui, command):
65 def skip_empty_successor(ui, command):
66 empty_successor = ui.config(b'rewrite', b'empty-successor')
66 empty_successor = ui.config(b'rewrite', b'empty-successor')
67 if empty_successor == b'skip':
67 if empty_successor == b'skip':
68 return True
68 return True
69 elif empty_successor == b'keep':
69 elif empty_successor == b'keep':
70 return False
70 return False
71 else:
71 else:
72 raise error.ConfigError(
72 raise error.ConfigError(
73 _(
73 _(
74 b"%s doesn't know how to handle config "
74 b"%s doesn't know how to handle config "
75 b"rewrite.empty-successor=%s (only 'skip' and 'keep' are "
75 b"rewrite.empty-successor=%s (only 'skip' and 'keep' are "
76 b"supported)"
76 b"supported)"
77 )
77 )
78 % (command, empty_successor)
78 % (command, empty_successor)
79 )
79 )
80
80
81
81
82 def update_hash_refs(repo, commitmsg):
82 def update_hash_refs(repo, commitmsg, pending=None):
83 """Replace all obsolete commit hashes in the message with the current hash.
83 """Replace all obsolete commit hashes in the message with the current hash.
84
84
85 If the obsolete commit was split or is divergent, the hash is not replaced
85 If the obsolete commit was split or is divergent, the hash is not replaced
86 as there's no way to know which successor to choose.
86 as there's no way to know which successor to choose.
87
88 For commands that update a series of commits in the current transaction, the
89 new obsolete markers can be considered by setting ``pending`` to a mapping
90 of ``pending[oldnode] = [successor_node1, successor_node2,..]``.
87 """
91 """
92 if not pending:
93 pending = {}
88 cache = {}
94 cache = {}
89 sha1s = re.findall(sha1re, commitmsg)
95 sha1s = re.findall(sha1re, commitmsg)
90 unfi = repo.unfiltered()
96 unfi = repo.unfiltered()
91 for sha1 in sha1s:
97 for sha1 in sha1s:
92 fullnode = scmutil.resolvehexnodeidprefix(unfi, sha1)
98 fullnode = scmutil.resolvehexnodeidprefix(unfi, sha1)
93 if fullnode is None:
99 if fullnode is None:
94 continue
100 continue
95 ctx = unfi[fullnode]
101 ctx = unfi[fullnode]
96 if not ctx.obsolete():
102 if not ctx.obsolete():
97 continue
103 successors = pending.get(fullnode)
98
104 if successors is None:
99 successors = obsutil.successorssets(repo, ctx.node(), cache=cache)
105 continue
106 # obsutil.successorssets() returns a list of list of nodes
107 successors = [successors]
108 else:
109 successors = obsutil.successorssets(repo, ctx.node(), cache=cache)
100
110
101 # We can't make any assumptions about how to update the hash if the
111 # We can't make any assumptions about how to update the hash if the
102 # cset in question was split or diverged.
112 # cset in question was split or diverged.
103 if len(successors) == 1 and len(successors[0]) == 1:
113 if len(successors) == 1 and len(successors[0]) == 1:
104 newsha1 = node.hex(successors[0][0])
114 newsha1 = node.hex(successors[0][0])
105 commitmsg = commitmsg.replace(sha1, newsha1[: len(sha1)])
115 commitmsg = commitmsg.replace(sha1, newsha1[: len(sha1)])
106 else:
116 else:
107 repo.ui.note(
117 repo.ui.note(
108 _(
118 _(
109 b'The stale commit message reference to %s could '
119 b'The stale commit message reference to %s could '
110 b'not be updated\n'
120 b'not be updated\n'
111 )
121 )
112 % sha1
122 % sha1
113 )
123 )
114
124
115 return commitmsg
125 return commitmsg
General Comments 0
You need to be logged in to leave comments. Login now