##// END OF EJS Templates
histedit: use single quotes in use warning
timeless -
r29970:5ad16469 default
parent child Browse files
Show More
@@ -1,1605 +1,1605 b''
1 # histedit.py - interactive history editing for mercurial
1 # histedit.py - interactive history editing for mercurial
2 #
2 #
3 # Copyright 2009 Augie Fackler <raf@durin42.com>
3 # Copyright 2009 Augie Fackler <raf@durin42.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 """interactive history editing
7 """interactive history editing
8
8
9 With this extension installed, Mercurial gains one new command: histedit. Usage
9 With this extension installed, Mercurial gains one new command: histedit. Usage
10 is as follows, assuming the following history::
10 is as follows, assuming the following history::
11
11
12 @ 3[tip] 7c2fd3b9020c 2009-04-27 18:04 -0500 durin42
12 @ 3[tip] 7c2fd3b9020c 2009-04-27 18:04 -0500 durin42
13 | Add delta
13 | Add delta
14 |
14 |
15 o 2 030b686bedc4 2009-04-27 18:04 -0500 durin42
15 o 2 030b686bedc4 2009-04-27 18:04 -0500 durin42
16 | Add gamma
16 | Add gamma
17 |
17 |
18 o 1 c561b4e977df 2009-04-27 18:04 -0500 durin42
18 o 1 c561b4e977df 2009-04-27 18:04 -0500 durin42
19 | Add beta
19 | Add beta
20 |
20 |
21 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
21 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
22 Add alpha
22 Add alpha
23
23
24 If you were to run ``hg histedit c561b4e977df``, you would see the following
24 If you were to run ``hg histedit c561b4e977df``, you would see the following
25 file open in your editor::
25 file open in your editor::
26
26
27 pick c561b4e977df Add beta
27 pick c561b4e977df Add beta
28 pick 030b686bedc4 Add gamma
28 pick 030b686bedc4 Add gamma
29 pick 7c2fd3b9020c Add delta
29 pick 7c2fd3b9020c Add delta
30
30
31 # Edit history between c561b4e977df and 7c2fd3b9020c
31 # Edit history between c561b4e977df and 7c2fd3b9020c
32 #
32 #
33 # Commits are listed from least to most recent
33 # Commits are listed from least to most recent
34 #
34 #
35 # Commands:
35 # Commands:
36 # p, pick = use commit
36 # p, pick = use commit
37 # e, edit = use commit, but stop for amending
37 # e, edit = use commit, but stop for amending
38 # f, fold = use commit, but combine it with the one above
38 # f, fold = use commit, but combine it with the one above
39 # r, roll = like fold, but discard this commit's description
39 # r, roll = like fold, but discard this commit's description
40 # d, drop = remove commit from history
40 # d, drop = remove commit from history
41 # m, mess = edit commit message without changing commit content
41 # m, mess = edit commit message without changing commit content
42 #
42 #
43
43
44 In this file, lines beginning with ``#`` are ignored. You must specify a rule
44 In this file, lines beginning with ``#`` are ignored. You must specify a rule
45 for each revision in your history. For example, if you had meant to add gamma
45 for each revision in your history. For example, if you had meant to add gamma
46 before beta, and then wanted to add delta in the same revision as beta, you
46 before beta, and then wanted to add delta in the same revision as beta, you
47 would reorganize the file to look like this::
47 would reorganize the file to look like this::
48
48
49 pick 030b686bedc4 Add gamma
49 pick 030b686bedc4 Add gamma
50 pick c561b4e977df Add beta
50 pick c561b4e977df Add beta
51 fold 7c2fd3b9020c Add delta
51 fold 7c2fd3b9020c Add delta
52
52
53 # Edit history between c561b4e977df and 7c2fd3b9020c
53 # Edit history between c561b4e977df and 7c2fd3b9020c
54 #
54 #
55 # Commits are listed from least to most recent
55 # Commits are listed from least to most recent
56 #
56 #
57 # Commands:
57 # Commands:
58 # p, pick = use commit
58 # p, pick = use commit
59 # e, edit = use commit, but stop for amending
59 # e, edit = use commit, but stop for amending
60 # f, fold = use commit, but combine it with the one above
60 # f, fold = use commit, but combine it with the one above
61 # r, roll = like fold, but discard this commit's description
61 # r, roll = like fold, but discard this commit's description
62 # d, drop = remove commit from history
62 # d, drop = remove commit from history
63 # m, mess = edit commit message without changing commit content
63 # m, mess = edit commit message without changing commit content
64 #
64 #
65
65
66 At which point you close the editor and ``histedit`` starts working. When you
66 At which point you close the editor and ``histedit`` starts working. When you
67 specify a ``fold`` operation, ``histedit`` will open an editor when it folds
67 specify a ``fold`` operation, ``histedit`` will open an editor when it folds
68 those revisions together, offering you a chance to clean up the commit message::
68 those revisions together, offering you a chance to clean up the commit message::
69
69
70 Add beta
70 Add beta
71 ***
71 ***
72 Add delta
72 Add delta
73
73
74 Edit the commit message to your liking, then close the editor. For
74 Edit the commit message to your liking, then close the editor. For
75 this example, let's assume that the commit message was changed to
75 this example, let's assume that the commit message was changed to
76 ``Add beta and delta.`` After histedit has run and had a chance to
76 ``Add beta and delta.`` After histedit has run and had a chance to
77 remove any old or temporary revisions it needed, the history looks
77 remove any old or temporary revisions it needed, the history looks
78 like this::
78 like this::
79
79
80 @ 2[tip] 989b4d060121 2009-04-27 18:04 -0500 durin42
80 @ 2[tip] 989b4d060121 2009-04-27 18:04 -0500 durin42
81 | Add beta and delta.
81 | Add beta and delta.
82 |
82 |
83 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
83 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
84 | Add gamma
84 | Add gamma
85 |
85 |
86 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
86 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
87 Add alpha
87 Add alpha
88
88
89 Note that ``histedit`` does *not* remove any revisions (even its own temporary
89 Note that ``histedit`` does *not* remove any revisions (even its own temporary
90 ones) until after it has completed all the editing operations, so it will
90 ones) until after it has completed all the editing operations, so it will
91 probably perform several strip operations when it's done. For the above example,
91 probably perform several strip operations when it's done. For the above example,
92 it had to run strip twice. Strip can be slow depending on a variety of factors,
92 it had to run strip twice. Strip can be slow depending on a variety of factors,
93 so you might need to be a little patient. You can choose to keep the original
93 so you might need to be a little patient. You can choose to keep the original
94 revisions by passing the ``--keep`` flag.
94 revisions by passing the ``--keep`` flag.
95
95
96 The ``edit`` operation will drop you back to a command prompt,
96 The ``edit`` operation will drop you back to a command prompt,
97 allowing you to edit files freely, or even use ``hg record`` to commit
97 allowing you to edit files freely, or even use ``hg record`` to commit
98 some changes as a separate commit. When you're done, any remaining
98 some changes as a separate commit. When you're done, any remaining
99 uncommitted changes will be committed as well. When done, run ``hg
99 uncommitted changes will be committed as well. When done, run ``hg
100 histedit --continue`` to finish this step. You'll be prompted for a
100 histedit --continue`` to finish this step. You'll be prompted for a
101 new commit message, but the default commit message will be the
101 new commit message, but the default commit message will be the
102 original message for the ``edit`` ed revision.
102 original message for the ``edit`` ed revision.
103
103
104 The ``message`` operation will give you a chance to revise a commit
104 The ``message`` operation will give you a chance to revise a commit
105 message without changing the contents. It's a shortcut for doing
105 message without changing the contents. It's a shortcut for doing
106 ``edit`` immediately followed by `hg histedit --continue``.
106 ``edit`` immediately followed by `hg histedit --continue``.
107
107
108 If ``histedit`` encounters a conflict when moving a revision (while
108 If ``histedit`` encounters a conflict when moving a revision (while
109 handling ``pick`` or ``fold``), it'll stop in a similar manner to
109 handling ``pick`` or ``fold``), it'll stop in a similar manner to
110 ``edit`` with the difference that it won't prompt you for a commit
110 ``edit`` with the difference that it won't prompt you for a commit
111 message when done. If you decide at this point that you don't like how
111 message when done. If you decide at this point that you don't like how
112 much work it will be to rearrange history, or that you made a mistake,
112 much work it will be to rearrange history, or that you made a mistake,
113 you can use ``hg histedit --abort`` to abandon the new changes you
113 you can use ``hg histedit --abort`` to abandon the new changes you
114 have made and return to the state before you attempted to edit your
114 have made and return to the state before you attempted to edit your
115 history.
115 history.
116
116
117 If we clone the histedit-ed example repository above and add four more
117 If we clone the histedit-ed example repository above and add four more
118 changes, such that we have the following history::
118 changes, such that we have the following history::
119
119
120 @ 6[tip] 038383181893 2009-04-27 18:04 -0500 stefan
120 @ 6[tip] 038383181893 2009-04-27 18:04 -0500 stefan
121 | Add theta
121 | Add theta
122 |
122 |
123 o 5 140988835471 2009-04-27 18:04 -0500 stefan
123 o 5 140988835471 2009-04-27 18:04 -0500 stefan
124 | Add eta
124 | Add eta
125 |
125 |
126 o 4 122930637314 2009-04-27 18:04 -0500 stefan
126 o 4 122930637314 2009-04-27 18:04 -0500 stefan
127 | Add zeta
127 | Add zeta
128 |
128 |
129 o 3 836302820282 2009-04-27 18:04 -0500 stefan
129 o 3 836302820282 2009-04-27 18:04 -0500 stefan
130 | Add epsilon
130 | Add epsilon
131 |
131 |
132 o 2 989b4d060121 2009-04-27 18:04 -0500 durin42
132 o 2 989b4d060121 2009-04-27 18:04 -0500 durin42
133 | Add beta and delta.
133 | Add beta and delta.
134 |
134 |
135 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
135 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
136 | Add gamma
136 | Add gamma
137 |
137 |
138 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
138 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
139 Add alpha
139 Add alpha
140
140
141 If you run ``hg histedit --outgoing`` on the clone then it is the same
141 If you run ``hg histedit --outgoing`` on the clone then it is the same
142 as running ``hg histedit 836302820282``. If you need plan to push to a
142 as running ``hg histedit 836302820282``. If you need plan to push to a
143 repository that Mercurial does not detect to be related to the source
143 repository that Mercurial does not detect to be related to the source
144 repo, you can add a ``--force`` option.
144 repo, you can add a ``--force`` option.
145
145
146 Config
146 Config
147 ------
147 ------
148
148
149 Histedit rule lines are truncated to 80 characters by default. You
149 Histedit rule lines are truncated to 80 characters by default. You
150 can customize this behavior by setting a different length in your
150 can customize this behavior by setting a different length in your
151 configuration file::
151 configuration file::
152
152
153 [histedit]
153 [histedit]
154 linelen = 120 # truncate rule lines at 120 characters
154 linelen = 120 # truncate rule lines at 120 characters
155
155
156 ``hg histedit`` attempts to automatically choose an appropriate base
156 ``hg histedit`` attempts to automatically choose an appropriate base
157 revision to use. To change which base revision is used, define a
157 revision to use. To change which base revision is used, define a
158 revset in your configuration file::
158 revset in your configuration file::
159
159
160 [histedit]
160 [histedit]
161 defaultrev = only(.) & draft()
161 defaultrev = only(.) & draft()
162
162
163 By default each edited revision needs to be present in histedit commands.
163 By default each edited revision needs to be present in histedit commands.
164 To remove revision you need to use ``drop`` operation. You can configure
164 To remove revision you need to use ``drop`` operation. You can configure
165 the drop to be implicit for missing commits by adding::
165 the drop to be implicit for missing commits by adding::
166
166
167 [histedit]
167 [histedit]
168 dropmissing = True
168 dropmissing = True
169
169
170 """
170 """
171
171
172 from __future__ import absolute_import
172 from __future__ import absolute_import
173
173
174 import errno
174 import errno
175 import os
175 import os
176 import sys
176 import sys
177
177
178 from mercurial.i18n import _
178 from mercurial.i18n import _
179 from mercurial import (
179 from mercurial import (
180 bundle2,
180 bundle2,
181 cmdutil,
181 cmdutil,
182 context,
182 context,
183 copies,
183 copies,
184 destutil,
184 destutil,
185 discovery,
185 discovery,
186 error,
186 error,
187 exchange,
187 exchange,
188 extensions,
188 extensions,
189 hg,
189 hg,
190 lock,
190 lock,
191 merge as mergemod,
191 merge as mergemod,
192 node,
192 node,
193 obsolete,
193 obsolete,
194 repair,
194 repair,
195 scmutil,
195 scmutil,
196 util,
196 util,
197 )
197 )
198
198
199 pickle = util.pickle
199 pickle = util.pickle
200 release = lock.release
200 release = lock.release
201 cmdtable = {}
201 cmdtable = {}
202 command = cmdutil.command(cmdtable)
202 command = cmdutil.command(cmdtable)
203
203
204 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
204 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
205 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
205 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
206 # be specifying the version(s) of Mercurial they are tested with, or
206 # be specifying the version(s) of Mercurial they are tested with, or
207 # leave the attribute unspecified.
207 # leave the attribute unspecified.
208 testedwith = 'ships-with-hg-core'
208 testedwith = 'ships-with-hg-core'
209
209
210 actiontable = {}
210 actiontable = {}
211 primaryactions = set()
211 primaryactions = set()
212 secondaryactions = set()
212 secondaryactions = set()
213 tertiaryactions = set()
213 tertiaryactions = set()
214 internalactions = set()
214 internalactions = set()
215
215
216 def geteditcomment(ui, first, last):
216 def geteditcomment(ui, first, last):
217 """ construct the editor comment
217 """ construct the editor comment
218 The comment includes::
218 The comment includes::
219 - an intro
219 - an intro
220 - sorted primary commands
220 - sorted primary commands
221 - sorted short commands
221 - sorted short commands
222 - sorted long commands
222 - sorted long commands
223 - additional hints
223 - additional hints
224
224
225 Commands are only included once.
225 Commands are only included once.
226 """
226 """
227 intro = _("""Edit history between %s and %s
227 intro = _("""Edit history between %s and %s
228
228
229 Commits are listed from least to most recent
229 Commits are listed from least to most recent
230
230
231 You can reorder changesets by reordering the lines
231 You can reorder changesets by reordering the lines
232
232
233 Commands:
233 Commands:
234 """)
234 """)
235 actions = []
235 actions = []
236 def addverb(v):
236 def addverb(v):
237 a = actiontable[v]
237 a = actiontable[v]
238 lines = a.message.split("\n")
238 lines = a.message.split("\n")
239 if len(a.verbs):
239 if len(a.verbs):
240 v = ', '.join(sorted(a.verbs, key=lambda v: len(v)))
240 v = ', '.join(sorted(a.verbs, key=lambda v: len(v)))
241 actions.append(" %s = %s" % (v, lines[0]))
241 actions.append(" %s = %s" % (v, lines[0]))
242 actions.extend([' %s' for l in lines[1:]])
242 actions.extend([' %s' for l in lines[1:]])
243
243
244 for v in (
244 for v in (
245 sorted(primaryactions) +
245 sorted(primaryactions) +
246 sorted(secondaryactions) +
246 sorted(secondaryactions) +
247 sorted(tertiaryactions)
247 sorted(tertiaryactions)
248 ):
248 ):
249 addverb(v)
249 addverb(v)
250 actions.append('')
250 actions.append('')
251
251
252 hints = []
252 hints = []
253 if ui.configbool('histedit', 'dropmissing'):
253 if ui.configbool('histedit', 'dropmissing'):
254 hints.append("Deleting a changeset from the list "
254 hints.append("Deleting a changeset from the list "
255 "will DISCARD it from the edited history!")
255 "will DISCARD it from the edited history!")
256
256
257 lines = (intro % (first, last)).split('\n') + actions + hints
257 lines = (intro % (first, last)).split('\n') + actions + hints
258
258
259 return ''.join(['# %s\n' % l if l else '#\n' for l in lines])
259 return ''.join(['# %s\n' % l if l else '#\n' for l in lines])
260
260
261 class histeditstate(object):
261 class histeditstate(object):
262 def __init__(self, repo, parentctxnode=None, actions=None, keep=None,
262 def __init__(self, repo, parentctxnode=None, actions=None, keep=None,
263 topmost=None, replacements=None, lock=None, wlock=None):
263 topmost=None, replacements=None, lock=None, wlock=None):
264 self.repo = repo
264 self.repo = repo
265 self.actions = actions
265 self.actions = actions
266 self.keep = keep
266 self.keep = keep
267 self.topmost = topmost
267 self.topmost = topmost
268 self.parentctxnode = parentctxnode
268 self.parentctxnode = parentctxnode
269 self.lock = lock
269 self.lock = lock
270 self.wlock = wlock
270 self.wlock = wlock
271 self.backupfile = None
271 self.backupfile = None
272 if replacements is None:
272 if replacements is None:
273 self.replacements = []
273 self.replacements = []
274 else:
274 else:
275 self.replacements = replacements
275 self.replacements = replacements
276
276
277 def read(self):
277 def read(self):
278 """Load histedit state from disk and set fields appropriately."""
278 """Load histedit state from disk and set fields appropriately."""
279 try:
279 try:
280 state = self.repo.vfs.read('histedit-state')
280 state = self.repo.vfs.read('histedit-state')
281 except IOError as err:
281 except IOError as err:
282 if err.errno != errno.ENOENT:
282 if err.errno != errno.ENOENT:
283 raise
283 raise
284 cmdutil.wrongtooltocontinue(self.repo, _('histedit'))
284 cmdutil.wrongtooltocontinue(self.repo, _('histedit'))
285
285
286 if state.startswith('v1\n'):
286 if state.startswith('v1\n'):
287 data = self._load()
287 data = self._load()
288 parentctxnode, rules, keep, topmost, replacements, backupfile = data
288 parentctxnode, rules, keep, topmost, replacements, backupfile = data
289 else:
289 else:
290 data = pickle.loads(state)
290 data = pickle.loads(state)
291 parentctxnode, rules, keep, topmost, replacements = data
291 parentctxnode, rules, keep, topmost, replacements = data
292 backupfile = None
292 backupfile = None
293
293
294 self.parentctxnode = parentctxnode
294 self.parentctxnode = parentctxnode
295 rules = "\n".join(["%s %s" % (verb, rest) for [verb, rest] in rules])
295 rules = "\n".join(["%s %s" % (verb, rest) for [verb, rest] in rules])
296 actions = parserules(rules, self)
296 actions = parserules(rules, self)
297 self.actions = actions
297 self.actions = actions
298 self.keep = keep
298 self.keep = keep
299 self.topmost = topmost
299 self.topmost = topmost
300 self.replacements = replacements
300 self.replacements = replacements
301 self.backupfile = backupfile
301 self.backupfile = backupfile
302
302
303 def write(self):
303 def write(self):
304 fp = self.repo.vfs('histedit-state', 'w')
304 fp = self.repo.vfs('histedit-state', 'w')
305 fp.write('v1\n')
305 fp.write('v1\n')
306 fp.write('%s\n' % node.hex(self.parentctxnode))
306 fp.write('%s\n' % node.hex(self.parentctxnode))
307 fp.write('%s\n' % node.hex(self.topmost))
307 fp.write('%s\n' % node.hex(self.topmost))
308 fp.write('%s\n' % self.keep)
308 fp.write('%s\n' % self.keep)
309 fp.write('%d\n' % len(self.actions))
309 fp.write('%d\n' % len(self.actions))
310 for action in self.actions:
310 for action in self.actions:
311 fp.write('%s\n' % action.tostate())
311 fp.write('%s\n' % action.tostate())
312 fp.write('%d\n' % len(self.replacements))
312 fp.write('%d\n' % len(self.replacements))
313 for replacement in self.replacements:
313 for replacement in self.replacements:
314 fp.write('%s%s\n' % (node.hex(replacement[0]), ''.join(node.hex(r)
314 fp.write('%s%s\n' % (node.hex(replacement[0]), ''.join(node.hex(r)
315 for r in replacement[1])))
315 for r in replacement[1])))
316 backupfile = self.backupfile
316 backupfile = self.backupfile
317 if not backupfile:
317 if not backupfile:
318 backupfile = ''
318 backupfile = ''
319 fp.write('%s\n' % backupfile)
319 fp.write('%s\n' % backupfile)
320 fp.close()
320 fp.close()
321
321
322 def _load(self):
322 def _load(self):
323 fp = self.repo.vfs('histedit-state', 'r')
323 fp = self.repo.vfs('histedit-state', 'r')
324 lines = [l[:-1] for l in fp.readlines()]
324 lines = [l[:-1] for l in fp.readlines()]
325
325
326 index = 0
326 index = 0
327 lines[index] # version number
327 lines[index] # version number
328 index += 1
328 index += 1
329
329
330 parentctxnode = node.bin(lines[index])
330 parentctxnode = node.bin(lines[index])
331 index += 1
331 index += 1
332
332
333 topmost = node.bin(lines[index])
333 topmost = node.bin(lines[index])
334 index += 1
334 index += 1
335
335
336 keep = lines[index] == 'True'
336 keep = lines[index] == 'True'
337 index += 1
337 index += 1
338
338
339 # Rules
339 # Rules
340 rules = []
340 rules = []
341 rulelen = int(lines[index])
341 rulelen = int(lines[index])
342 index += 1
342 index += 1
343 for i in xrange(rulelen):
343 for i in xrange(rulelen):
344 ruleaction = lines[index]
344 ruleaction = lines[index]
345 index += 1
345 index += 1
346 rule = lines[index]
346 rule = lines[index]
347 index += 1
347 index += 1
348 rules.append((ruleaction, rule))
348 rules.append((ruleaction, rule))
349
349
350 # Replacements
350 # Replacements
351 replacements = []
351 replacements = []
352 replacementlen = int(lines[index])
352 replacementlen = int(lines[index])
353 index += 1
353 index += 1
354 for i in xrange(replacementlen):
354 for i in xrange(replacementlen):
355 replacement = lines[index]
355 replacement = lines[index]
356 original = node.bin(replacement[:40])
356 original = node.bin(replacement[:40])
357 succ = [node.bin(replacement[i:i + 40]) for i in
357 succ = [node.bin(replacement[i:i + 40]) for i in
358 range(40, len(replacement), 40)]
358 range(40, len(replacement), 40)]
359 replacements.append((original, succ))
359 replacements.append((original, succ))
360 index += 1
360 index += 1
361
361
362 backupfile = lines[index]
362 backupfile = lines[index]
363 index += 1
363 index += 1
364
364
365 fp.close()
365 fp.close()
366
366
367 return parentctxnode, rules, keep, topmost, replacements, backupfile
367 return parentctxnode, rules, keep, topmost, replacements, backupfile
368
368
369 def clear(self):
369 def clear(self):
370 if self.inprogress():
370 if self.inprogress():
371 self.repo.vfs.unlink('histedit-state')
371 self.repo.vfs.unlink('histedit-state')
372
372
373 def inprogress(self):
373 def inprogress(self):
374 return self.repo.vfs.exists('histedit-state')
374 return self.repo.vfs.exists('histedit-state')
375
375
376
376
377 class histeditaction(object):
377 class histeditaction(object):
378 def __init__(self, state, node):
378 def __init__(self, state, node):
379 self.state = state
379 self.state = state
380 self.repo = state.repo
380 self.repo = state.repo
381 self.node = node
381 self.node = node
382
382
383 @classmethod
383 @classmethod
384 def fromrule(cls, state, rule):
384 def fromrule(cls, state, rule):
385 """Parses the given rule, returning an instance of the histeditaction.
385 """Parses the given rule, returning an instance of the histeditaction.
386 """
386 """
387 rulehash = rule.strip().split(' ', 1)[0]
387 rulehash = rule.strip().split(' ', 1)[0]
388 try:
388 try:
389 rev = node.bin(rulehash)
389 rev = node.bin(rulehash)
390 except TypeError:
390 except TypeError:
391 raise error.ParseError("invalid changeset %s" % rulehash)
391 raise error.ParseError("invalid changeset %s" % rulehash)
392 return cls(state, rev)
392 return cls(state, rev)
393
393
394 def verify(self, prev, expected, seen):
394 def verify(self, prev, expected, seen):
395 """ Verifies semantic correctness of the rule"""
395 """ Verifies semantic correctness of the rule"""
396 repo = self.repo
396 repo = self.repo
397 ha = node.hex(self.node)
397 ha = node.hex(self.node)
398 try:
398 try:
399 self.node = repo[ha].node()
399 self.node = repo[ha].node()
400 except error.RepoError:
400 except error.RepoError:
401 raise error.ParseError(_('unknown changeset %s listed')
401 raise error.ParseError(_('unknown changeset %s listed')
402 % ha[:12])
402 % ha[:12])
403 if self.node is not None:
403 if self.node is not None:
404 self._verifynodeconstraints(prev, expected, seen)
404 self._verifynodeconstraints(prev, expected, seen)
405
405
406 def _verifynodeconstraints(self, prev, expected, seen):
406 def _verifynodeconstraints(self, prev, expected, seen):
407 # by default command need a node in the edited list
407 # by default command need a node in the edited list
408 if self.node not in expected:
408 if self.node not in expected:
409 raise error.ParseError(_('%s "%s" changeset was not a candidate')
409 raise error.ParseError(_('%s "%s" changeset was not a candidate')
410 % (self.verb, node.short(self.node)),
410 % (self.verb, node.short(self.node)),
411 hint=_('only use listed changesets'))
411 hint=_('only use listed changesets'))
412 # and only one command per node
412 # and only one command per node
413 if self.node in seen:
413 if self.node in seen:
414 raise error.ParseError(_('duplicated command for changeset %s') %
414 raise error.ParseError(_('duplicated command for changeset %s') %
415 node.short(self.node))
415 node.short(self.node))
416
416
417 def torule(self):
417 def torule(self):
418 """build a histedit rule line for an action
418 """build a histedit rule line for an action
419
419
420 by default lines are in the form:
420 by default lines are in the form:
421 <hash> <rev> <summary>
421 <hash> <rev> <summary>
422 """
422 """
423 ctx = self.repo[self.node]
423 ctx = self.repo[self.node]
424 summary = _getsummary(ctx)
424 summary = _getsummary(ctx)
425 line = '%s %s %d %s' % (self.verb, ctx, ctx.rev(), summary)
425 line = '%s %s %d %s' % (self.verb, ctx, ctx.rev(), summary)
426 # trim to 75 columns by default so it's not stupidly wide in my editor
426 # trim to 75 columns by default so it's not stupidly wide in my editor
427 # (the 5 more are left for verb)
427 # (the 5 more are left for verb)
428 maxlen = self.repo.ui.configint('histedit', 'linelen', default=80)
428 maxlen = self.repo.ui.configint('histedit', 'linelen', default=80)
429 maxlen = max(maxlen, 22) # avoid truncating hash
429 maxlen = max(maxlen, 22) # avoid truncating hash
430 return util.ellipsis(line, maxlen)
430 return util.ellipsis(line, maxlen)
431
431
432 def tostate(self):
432 def tostate(self):
433 """Print an action in format used by histedit state files
433 """Print an action in format used by histedit state files
434 (the first line is a verb, the remainder is the second)
434 (the first line is a verb, the remainder is the second)
435 """
435 """
436 return "%s\n%s" % (self.verb, node.hex(self.node))
436 return "%s\n%s" % (self.verb, node.hex(self.node))
437
437
438 def run(self):
438 def run(self):
439 """Runs the action. The default behavior is simply apply the action's
439 """Runs the action. The default behavior is simply apply the action's
440 rulectx onto the current parentctx."""
440 rulectx onto the current parentctx."""
441 self.applychange()
441 self.applychange()
442 self.continuedirty()
442 self.continuedirty()
443 return self.continueclean()
443 return self.continueclean()
444
444
445 def applychange(self):
445 def applychange(self):
446 """Applies the changes from this action's rulectx onto the current
446 """Applies the changes from this action's rulectx onto the current
447 parentctx, but does not commit them."""
447 parentctx, but does not commit them."""
448 repo = self.repo
448 repo = self.repo
449 rulectx = repo[self.node]
449 rulectx = repo[self.node]
450 repo.ui.pushbuffer(error=True, labeled=True)
450 repo.ui.pushbuffer(error=True, labeled=True)
451 hg.update(repo, self.state.parentctxnode, quietempty=True)
451 hg.update(repo, self.state.parentctxnode, quietempty=True)
452 stats = applychanges(repo.ui, repo, rulectx, {})
452 stats = applychanges(repo.ui, repo, rulectx, {})
453 if stats and stats[3] > 0:
453 if stats and stats[3] > 0:
454 buf = repo.ui.popbuffer()
454 buf = repo.ui.popbuffer()
455 repo.ui.write(*buf)
455 repo.ui.write(*buf)
456 raise error.InterventionRequired(
456 raise error.InterventionRequired(
457 _('Fix up the change (%s %s)') %
457 _('Fix up the change (%s %s)') %
458 (self.verb, node.short(self.node)),
458 (self.verb, node.short(self.node)),
459 hint=_('hg histedit --continue to resume'))
459 hint=_('hg histedit --continue to resume'))
460 else:
460 else:
461 repo.ui.popbuffer()
461 repo.ui.popbuffer()
462
462
463 def continuedirty(self):
463 def continuedirty(self):
464 """Continues the action when changes have been applied to the working
464 """Continues the action when changes have been applied to the working
465 copy. The default behavior is to commit the dirty changes."""
465 copy. The default behavior is to commit the dirty changes."""
466 repo = self.repo
466 repo = self.repo
467 rulectx = repo[self.node]
467 rulectx = repo[self.node]
468
468
469 editor = self.commiteditor()
469 editor = self.commiteditor()
470 commit = commitfuncfor(repo, rulectx)
470 commit = commitfuncfor(repo, rulectx)
471
471
472 commit(text=rulectx.description(), user=rulectx.user(),
472 commit(text=rulectx.description(), user=rulectx.user(),
473 date=rulectx.date(), extra=rulectx.extra(), editor=editor)
473 date=rulectx.date(), extra=rulectx.extra(), editor=editor)
474
474
475 def commiteditor(self):
475 def commiteditor(self):
476 """The editor to be used to edit the commit message."""
476 """The editor to be used to edit the commit message."""
477 return False
477 return False
478
478
479 def continueclean(self):
479 def continueclean(self):
480 """Continues the action when the working copy is clean. The default
480 """Continues the action when the working copy is clean. The default
481 behavior is to accept the current commit as the new version of the
481 behavior is to accept the current commit as the new version of the
482 rulectx."""
482 rulectx."""
483 ctx = self.repo['.']
483 ctx = self.repo['.']
484 if ctx.node() == self.state.parentctxnode:
484 if ctx.node() == self.state.parentctxnode:
485 self.repo.ui.warn(_('%s: skipping changeset (no changes)\n') %
485 self.repo.ui.warn(_('%s: skipping changeset (no changes)\n') %
486 node.short(self.node))
486 node.short(self.node))
487 return ctx, [(self.node, tuple())]
487 return ctx, [(self.node, tuple())]
488 if ctx.node() == self.node:
488 if ctx.node() == self.node:
489 # Nothing changed
489 # Nothing changed
490 return ctx, []
490 return ctx, []
491 return ctx, [(self.node, (ctx.node(),))]
491 return ctx, [(self.node, (ctx.node(),))]
492
492
493 def commitfuncfor(repo, src):
493 def commitfuncfor(repo, src):
494 """Build a commit function for the replacement of <src>
494 """Build a commit function for the replacement of <src>
495
495
496 This function ensure we apply the same treatment to all changesets.
496 This function ensure we apply the same treatment to all changesets.
497
497
498 - Add a 'histedit_source' entry in extra.
498 - Add a 'histedit_source' entry in extra.
499
499
500 Note that fold has its own separated logic because its handling is a bit
500 Note that fold has its own separated logic because its handling is a bit
501 different and not easily factored out of the fold method.
501 different and not easily factored out of the fold method.
502 """
502 """
503 phasemin = src.phase()
503 phasemin = src.phase()
504 def commitfunc(**kwargs):
504 def commitfunc(**kwargs):
505 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
505 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
506 try:
506 try:
507 repo.ui.setconfig('phases', 'new-commit', phasemin,
507 repo.ui.setconfig('phases', 'new-commit', phasemin,
508 'histedit')
508 'histedit')
509 extra = kwargs.get('extra', {}).copy()
509 extra = kwargs.get('extra', {}).copy()
510 extra['histedit_source'] = src.hex()
510 extra['histedit_source'] = src.hex()
511 kwargs['extra'] = extra
511 kwargs['extra'] = extra
512 return repo.commit(**kwargs)
512 return repo.commit(**kwargs)
513 finally:
513 finally:
514 repo.ui.restoreconfig(phasebackup)
514 repo.ui.restoreconfig(phasebackup)
515 return commitfunc
515 return commitfunc
516
516
517 def applychanges(ui, repo, ctx, opts):
517 def applychanges(ui, repo, ctx, opts):
518 """Merge changeset from ctx (only) in the current working directory"""
518 """Merge changeset from ctx (only) in the current working directory"""
519 wcpar = repo.dirstate.parents()[0]
519 wcpar = repo.dirstate.parents()[0]
520 if ctx.p1().node() == wcpar:
520 if ctx.p1().node() == wcpar:
521 # edits are "in place" we do not need to make any merge,
521 # edits are "in place" we do not need to make any merge,
522 # just applies changes on parent for editing
522 # just applies changes on parent for editing
523 cmdutil.revert(ui, repo, ctx, (wcpar, node.nullid), all=True)
523 cmdutil.revert(ui, repo, ctx, (wcpar, node.nullid), all=True)
524 stats = None
524 stats = None
525 else:
525 else:
526 try:
526 try:
527 # ui.forcemerge is an internal variable, do not document
527 # ui.forcemerge is an internal variable, do not document
528 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
528 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
529 'histedit')
529 'histedit')
530 stats = mergemod.graft(repo, ctx, ctx.p1(), ['local', 'histedit'])
530 stats = mergemod.graft(repo, ctx, ctx.p1(), ['local', 'histedit'])
531 finally:
531 finally:
532 repo.ui.setconfig('ui', 'forcemerge', '', 'histedit')
532 repo.ui.setconfig('ui', 'forcemerge', '', 'histedit')
533 return stats
533 return stats
534
534
535 def collapse(repo, first, last, commitopts, skipprompt=False):
535 def collapse(repo, first, last, commitopts, skipprompt=False):
536 """collapse the set of revisions from first to last as new one.
536 """collapse the set of revisions from first to last as new one.
537
537
538 Expected commit options are:
538 Expected commit options are:
539 - message
539 - message
540 - date
540 - date
541 - username
541 - username
542 Commit message is edited in all cases.
542 Commit message is edited in all cases.
543
543
544 This function works in memory."""
544 This function works in memory."""
545 ctxs = list(repo.set('%d::%d', first, last))
545 ctxs = list(repo.set('%d::%d', first, last))
546 if not ctxs:
546 if not ctxs:
547 return None
547 return None
548 for c in ctxs:
548 for c in ctxs:
549 if not c.mutable():
549 if not c.mutable():
550 raise error.ParseError(
550 raise error.ParseError(
551 _("cannot fold into public change %s") % node.short(c.node()))
551 _("cannot fold into public change %s") % node.short(c.node()))
552 base = first.parents()[0]
552 base = first.parents()[0]
553
553
554 # commit a new version of the old changeset, including the update
554 # commit a new version of the old changeset, including the update
555 # collect all files which might be affected
555 # collect all files which might be affected
556 files = set()
556 files = set()
557 for ctx in ctxs:
557 for ctx in ctxs:
558 files.update(ctx.files())
558 files.update(ctx.files())
559
559
560 # Recompute copies (avoid recording a -> b -> a)
560 # Recompute copies (avoid recording a -> b -> a)
561 copied = copies.pathcopies(base, last)
561 copied = copies.pathcopies(base, last)
562
562
563 # prune files which were reverted by the updates
563 # prune files which were reverted by the updates
564 files = [f for f in files if not cmdutil.samefile(f, last, base)]
564 files = [f for f in files if not cmdutil.samefile(f, last, base)]
565 # commit version of these files as defined by head
565 # commit version of these files as defined by head
566 headmf = last.manifest()
566 headmf = last.manifest()
567 def filectxfn(repo, ctx, path):
567 def filectxfn(repo, ctx, path):
568 if path in headmf:
568 if path in headmf:
569 fctx = last[path]
569 fctx = last[path]
570 flags = fctx.flags()
570 flags = fctx.flags()
571 mctx = context.memfilectx(repo,
571 mctx = context.memfilectx(repo,
572 fctx.path(), fctx.data(),
572 fctx.path(), fctx.data(),
573 islink='l' in flags,
573 islink='l' in flags,
574 isexec='x' in flags,
574 isexec='x' in flags,
575 copied=copied.get(path))
575 copied=copied.get(path))
576 return mctx
576 return mctx
577 return None
577 return None
578
578
579 if commitopts.get('message'):
579 if commitopts.get('message'):
580 message = commitopts['message']
580 message = commitopts['message']
581 else:
581 else:
582 message = first.description()
582 message = first.description()
583 user = commitopts.get('user')
583 user = commitopts.get('user')
584 date = commitopts.get('date')
584 date = commitopts.get('date')
585 extra = commitopts.get('extra')
585 extra = commitopts.get('extra')
586
586
587 parents = (first.p1().node(), first.p2().node())
587 parents = (first.p1().node(), first.p2().node())
588 editor = None
588 editor = None
589 if not skipprompt:
589 if not skipprompt:
590 editor = cmdutil.getcommiteditor(edit=True, editform='histedit.fold')
590 editor = cmdutil.getcommiteditor(edit=True, editform='histedit.fold')
591 new = context.memctx(repo,
591 new = context.memctx(repo,
592 parents=parents,
592 parents=parents,
593 text=message,
593 text=message,
594 files=files,
594 files=files,
595 filectxfn=filectxfn,
595 filectxfn=filectxfn,
596 user=user,
596 user=user,
597 date=date,
597 date=date,
598 extra=extra,
598 extra=extra,
599 editor=editor)
599 editor=editor)
600 return repo.commitctx(new)
600 return repo.commitctx(new)
601
601
602 def _isdirtywc(repo):
602 def _isdirtywc(repo):
603 return repo[None].dirty(missing=True)
603 return repo[None].dirty(missing=True)
604
604
605 def abortdirty():
605 def abortdirty():
606 raise error.Abort(_('working copy has pending changes'),
606 raise error.Abort(_('working copy has pending changes'),
607 hint=_('amend, commit, or revert them and run histedit '
607 hint=_('amend, commit, or revert them and run histedit '
608 '--continue, or abort with histedit --abort'))
608 '--continue, or abort with histedit --abort'))
609
609
610 def action(verbs, message, priority=False, internal=False):
610 def action(verbs, message, priority=False, internal=False):
611 def wrap(cls):
611 def wrap(cls):
612 assert not priority or not internal
612 assert not priority or not internal
613 verb = verbs[0]
613 verb = verbs[0]
614 if priority:
614 if priority:
615 primaryactions.add(verb)
615 primaryactions.add(verb)
616 elif internal:
616 elif internal:
617 internalactions.add(verb)
617 internalactions.add(verb)
618 elif len(verbs) > 1:
618 elif len(verbs) > 1:
619 secondaryactions.add(verb)
619 secondaryactions.add(verb)
620 else:
620 else:
621 tertiaryactions.add(verb)
621 tertiaryactions.add(verb)
622
622
623 cls.verb = verb
623 cls.verb = verb
624 cls.verbs = verbs
624 cls.verbs = verbs
625 cls.message = message
625 cls.message = message
626 for verb in verbs:
626 for verb in verbs:
627 actiontable[verb] = cls
627 actiontable[verb] = cls
628 return cls
628 return cls
629 return wrap
629 return wrap
630
630
631 @action(['pick', 'p'],
631 @action(['pick', 'p'],
632 _('use commit'),
632 _('use commit'),
633 priority=True)
633 priority=True)
634 class pick(histeditaction):
634 class pick(histeditaction):
635 def run(self):
635 def run(self):
636 rulectx = self.repo[self.node]
636 rulectx = self.repo[self.node]
637 if rulectx.parents()[0].node() == self.state.parentctxnode:
637 if rulectx.parents()[0].node() == self.state.parentctxnode:
638 self.repo.ui.debug('node %s unchanged\n' % node.short(self.node))
638 self.repo.ui.debug('node %s unchanged\n' % node.short(self.node))
639 return rulectx, []
639 return rulectx, []
640
640
641 return super(pick, self).run()
641 return super(pick, self).run()
642
642
643 @action(['edit', 'e'],
643 @action(['edit', 'e'],
644 _('use commit, but stop for amending'),
644 _('use commit, but stop for amending'),
645 priority=True)
645 priority=True)
646 class edit(histeditaction):
646 class edit(histeditaction):
647 def run(self):
647 def run(self):
648 repo = self.repo
648 repo = self.repo
649 rulectx = repo[self.node]
649 rulectx = repo[self.node]
650 hg.update(repo, self.state.parentctxnode, quietempty=True)
650 hg.update(repo, self.state.parentctxnode, quietempty=True)
651 applychanges(repo.ui, repo, rulectx, {})
651 applychanges(repo.ui, repo, rulectx, {})
652 raise error.InterventionRequired(
652 raise error.InterventionRequired(
653 _('Editing (%s), you may commit or record as needed now.')
653 _('Editing (%s), you may commit or record as needed now.')
654 % node.short(self.node),
654 % node.short(self.node),
655 hint=_('hg histedit --continue to resume'))
655 hint=_('hg histedit --continue to resume'))
656
656
657 def commiteditor(self):
657 def commiteditor(self):
658 return cmdutil.getcommiteditor(edit=True, editform='histedit.edit')
658 return cmdutil.getcommiteditor(edit=True, editform='histedit.edit')
659
659
660 @action(['fold', 'f'],
660 @action(['fold', 'f'],
661 _('use commit, but combine it with the one above'))
661 _('use commit, but combine it with the one above'))
662 class fold(histeditaction):
662 class fold(histeditaction):
663 def verify(self, prev, expected, seen):
663 def verify(self, prev, expected, seen):
664 """ Verifies semantic correctness of the fold rule"""
664 """ Verifies semantic correctness of the fold rule"""
665 super(fold, self).verify(prev, expected, seen)
665 super(fold, self).verify(prev, expected, seen)
666 repo = self.repo
666 repo = self.repo
667 if not prev:
667 if not prev:
668 c = repo[self.node].parents()[0]
668 c = repo[self.node].parents()[0]
669 elif not prev.verb in ('pick', 'base'):
669 elif not prev.verb in ('pick', 'base'):
670 return
670 return
671 else:
671 else:
672 c = repo[prev.node]
672 c = repo[prev.node]
673 if not c.mutable():
673 if not c.mutable():
674 raise error.ParseError(
674 raise error.ParseError(
675 _("cannot fold into public change %s") % node.short(c.node()))
675 _("cannot fold into public change %s") % node.short(c.node()))
676
676
677
677
678 def continuedirty(self):
678 def continuedirty(self):
679 repo = self.repo
679 repo = self.repo
680 rulectx = repo[self.node]
680 rulectx = repo[self.node]
681
681
682 commit = commitfuncfor(repo, rulectx)
682 commit = commitfuncfor(repo, rulectx)
683 commit(text='fold-temp-revision %s' % node.short(self.node),
683 commit(text='fold-temp-revision %s' % node.short(self.node),
684 user=rulectx.user(), date=rulectx.date(),
684 user=rulectx.user(), date=rulectx.date(),
685 extra=rulectx.extra())
685 extra=rulectx.extra())
686
686
687 def continueclean(self):
687 def continueclean(self):
688 repo = self.repo
688 repo = self.repo
689 ctx = repo['.']
689 ctx = repo['.']
690 rulectx = repo[self.node]
690 rulectx = repo[self.node]
691 parentctxnode = self.state.parentctxnode
691 parentctxnode = self.state.parentctxnode
692 if ctx.node() == parentctxnode:
692 if ctx.node() == parentctxnode:
693 repo.ui.warn(_('%s: empty changeset\n') %
693 repo.ui.warn(_('%s: empty changeset\n') %
694 node.short(self.node))
694 node.short(self.node))
695 return ctx, [(self.node, (parentctxnode,))]
695 return ctx, [(self.node, (parentctxnode,))]
696
696
697 parentctx = repo[parentctxnode]
697 parentctx = repo[parentctxnode]
698 newcommits = set(c.node() for c in repo.set('(%d::. - %d)', parentctx,
698 newcommits = set(c.node() for c in repo.set('(%d::. - %d)', parentctx,
699 parentctx))
699 parentctx))
700 if not newcommits:
700 if not newcommits:
701 repo.ui.warn(_('%s: cannot fold - working copy is not a '
701 repo.ui.warn(_('%s: cannot fold - working copy is not a '
702 'descendant of previous commit %s\n') %
702 'descendant of previous commit %s\n') %
703 (node.short(self.node), node.short(parentctxnode)))
703 (node.short(self.node), node.short(parentctxnode)))
704 return ctx, [(self.node, (ctx.node(),))]
704 return ctx, [(self.node, (ctx.node(),))]
705
705
706 middlecommits = newcommits.copy()
706 middlecommits = newcommits.copy()
707 middlecommits.discard(ctx.node())
707 middlecommits.discard(ctx.node())
708
708
709 return self.finishfold(repo.ui, repo, parentctx, rulectx, ctx.node(),
709 return self.finishfold(repo.ui, repo, parentctx, rulectx, ctx.node(),
710 middlecommits)
710 middlecommits)
711
711
712 def skipprompt(self):
712 def skipprompt(self):
713 """Returns true if the rule should skip the message editor.
713 """Returns true if the rule should skip the message editor.
714
714
715 For example, 'fold' wants to show an editor, but 'rollup'
715 For example, 'fold' wants to show an editor, but 'rollup'
716 doesn't want to.
716 doesn't want to.
717 """
717 """
718 return False
718 return False
719
719
720 def mergedescs(self):
720 def mergedescs(self):
721 """Returns true if the rule should merge messages of multiple changes.
721 """Returns true if the rule should merge messages of multiple changes.
722
722
723 This exists mainly so that 'rollup' rules can be a subclass of
723 This exists mainly so that 'rollup' rules can be a subclass of
724 'fold'.
724 'fold'.
725 """
725 """
726 return True
726 return True
727
727
728 def finishfold(self, ui, repo, ctx, oldctx, newnode, internalchanges):
728 def finishfold(self, ui, repo, ctx, oldctx, newnode, internalchanges):
729 parent = ctx.parents()[0].node()
729 parent = ctx.parents()[0].node()
730 repo.ui.pushbuffer()
730 repo.ui.pushbuffer()
731 hg.update(repo, parent)
731 hg.update(repo, parent)
732 repo.ui.popbuffer()
732 repo.ui.popbuffer()
733 ### prepare new commit data
733 ### prepare new commit data
734 commitopts = {}
734 commitopts = {}
735 commitopts['user'] = ctx.user()
735 commitopts['user'] = ctx.user()
736 # commit message
736 # commit message
737 if not self.mergedescs():
737 if not self.mergedescs():
738 newmessage = ctx.description()
738 newmessage = ctx.description()
739 else:
739 else:
740 newmessage = '\n***\n'.join(
740 newmessage = '\n***\n'.join(
741 [ctx.description()] +
741 [ctx.description()] +
742 [repo[r].description() for r in internalchanges] +
742 [repo[r].description() for r in internalchanges] +
743 [oldctx.description()]) + '\n'
743 [oldctx.description()]) + '\n'
744 commitopts['message'] = newmessage
744 commitopts['message'] = newmessage
745 # date
745 # date
746 commitopts['date'] = max(ctx.date(), oldctx.date())
746 commitopts['date'] = max(ctx.date(), oldctx.date())
747 extra = ctx.extra().copy()
747 extra = ctx.extra().copy()
748 # histedit_source
748 # histedit_source
749 # note: ctx is likely a temporary commit but that the best we can do
749 # note: ctx is likely a temporary commit but that the best we can do
750 # here. This is sufficient to solve issue3681 anyway.
750 # here. This is sufficient to solve issue3681 anyway.
751 extra['histedit_source'] = '%s,%s' % (ctx.hex(), oldctx.hex())
751 extra['histedit_source'] = '%s,%s' % (ctx.hex(), oldctx.hex())
752 commitopts['extra'] = extra
752 commitopts['extra'] = extra
753 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
753 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
754 try:
754 try:
755 phasemin = max(ctx.phase(), oldctx.phase())
755 phasemin = max(ctx.phase(), oldctx.phase())
756 repo.ui.setconfig('phases', 'new-commit', phasemin, 'histedit')
756 repo.ui.setconfig('phases', 'new-commit', phasemin, 'histedit')
757 n = collapse(repo, ctx, repo[newnode], commitopts,
757 n = collapse(repo, ctx, repo[newnode], commitopts,
758 skipprompt=self.skipprompt())
758 skipprompt=self.skipprompt())
759 finally:
759 finally:
760 repo.ui.restoreconfig(phasebackup)
760 repo.ui.restoreconfig(phasebackup)
761 if n is None:
761 if n is None:
762 return ctx, []
762 return ctx, []
763 repo.ui.pushbuffer()
763 repo.ui.pushbuffer()
764 hg.update(repo, n)
764 hg.update(repo, n)
765 repo.ui.popbuffer()
765 repo.ui.popbuffer()
766 replacements = [(oldctx.node(), (newnode,)),
766 replacements = [(oldctx.node(), (newnode,)),
767 (ctx.node(), (n,)),
767 (ctx.node(), (n,)),
768 (newnode, (n,)),
768 (newnode, (n,)),
769 ]
769 ]
770 for ich in internalchanges:
770 for ich in internalchanges:
771 replacements.append((ich, (n,)))
771 replacements.append((ich, (n,)))
772 return repo[n], replacements
772 return repo[n], replacements
773
773
774 class base(histeditaction):
774 class base(histeditaction):
775
775
776 def run(self):
776 def run(self):
777 if self.repo['.'].node() != self.node:
777 if self.repo['.'].node() != self.node:
778 mergemod.update(self.repo, self.node, False, True)
778 mergemod.update(self.repo, self.node, False, True)
779 # branchmerge, force)
779 # branchmerge, force)
780 return self.continueclean()
780 return self.continueclean()
781
781
782 def continuedirty(self):
782 def continuedirty(self):
783 abortdirty()
783 abortdirty()
784
784
785 def continueclean(self):
785 def continueclean(self):
786 basectx = self.repo['.']
786 basectx = self.repo['.']
787 return basectx, []
787 return basectx, []
788
788
789 def _verifynodeconstraints(self, prev, expected, seen):
789 def _verifynodeconstraints(self, prev, expected, seen):
790 # base can only be use with a node not in the edited set
790 # base can only be use with a node not in the edited set
791 if self.node in expected:
791 if self.node in expected:
792 msg = _('%s "%s" changeset was an edited list candidate')
792 msg = _('%s "%s" changeset was an edited list candidate')
793 raise error.ParseError(
793 raise error.ParseError(
794 msg % (self.verb, node.short(self.node)),
794 msg % (self.verb, node.short(self.node)),
795 hint=_('base must only use unlisted changesets'))
795 hint=_('base must only use unlisted changesets'))
796
796
797 @action(['_multifold'],
797 @action(['_multifold'],
798 _(
798 _(
799 """fold subclass used for when multiple folds happen in a row
799 """fold subclass used for when multiple folds happen in a row
800
800
801 We only want to fire the editor for the folded message once when
801 We only want to fire the editor for the folded message once when
802 (say) four changes are folded down into a single change. This is
802 (say) four changes are folded down into a single change. This is
803 similar to rollup, but we should preserve both messages so that
803 similar to rollup, but we should preserve both messages so that
804 when the last fold operation runs we can show the user all the
804 when the last fold operation runs we can show the user all the
805 commit messages in their editor.
805 commit messages in their editor.
806 """),
806 """),
807 internal=True)
807 internal=True)
808 class _multifold(fold):
808 class _multifold(fold):
809 def skipprompt(self):
809 def skipprompt(self):
810 return True
810 return True
811
811
812 @action(["roll", "r"],
812 @action(["roll", "r"],
813 _("like fold, but discard this commit's description"))
813 _("like fold, but discard this commit's description"))
814 class rollup(fold):
814 class rollup(fold):
815 def mergedescs(self):
815 def mergedescs(self):
816 return False
816 return False
817
817
818 def skipprompt(self):
818 def skipprompt(self):
819 return True
819 return True
820
820
821 @action(["drop", "d"],
821 @action(["drop", "d"],
822 _('remove commit from history'))
822 _('remove commit from history'))
823 class drop(histeditaction):
823 class drop(histeditaction):
824 def run(self):
824 def run(self):
825 parentctx = self.repo[self.state.parentctxnode]
825 parentctx = self.repo[self.state.parentctxnode]
826 return parentctx, [(self.node, tuple())]
826 return parentctx, [(self.node, tuple())]
827
827
828 @action(["mess", "m"],
828 @action(["mess", "m"],
829 _('edit commit message without changing commit content'),
829 _('edit commit message without changing commit content'),
830 priority=True)
830 priority=True)
831 class message(histeditaction):
831 class message(histeditaction):
832 def commiteditor(self):
832 def commiteditor(self):
833 return cmdutil.getcommiteditor(edit=True, editform='histedit.mess')
833 return cmdutil.getcommiteditor(edit=True, editform='histedit.mess')
834
834
835 def findoutgoing(ui, repo, remote=None, force=False, opts=None):
835 def findoutgoing(ui, repo, remote=None, force=False, opts=None):
836 """utility function to find the first outgoing changeset
836 """utility function to find the first outgoing changeset
837
837
838 Used by initialization code"""
838 Used by initialization code"""
839 if opts is None:
839 if opts is None:
840 opts = {}
840 opts = {}
841 dest = ui.expandpath(remote or 'default-push', remote or 'default')
841 dest = ui.expandpath(remote or 'default-push', remote or 'default')
842 dest, revs = hg.parseurl(dest, None)[:2]
842 dest, revs = hg.parseurl(dest, None)[:2]
843 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
843 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
844
844
845 revs, checkout = hg.addbranchrevs(repo, repo, revs, None)
845 revs, checkout = hg.addbranchrevs(repo, repo, revs, None)
846 other = hg.peer(repo, opts, dest)
846 other = hg.peer(repo, opts, dest)
847
847
848 if revs:
848 if revs:
849 revs = [repo.lookup(rev) for rev in revs]
849 revs = [repo.lookup(rev) for rev in revs]
850
850
851 outgoing = discovery.findcommonoutgoing(repo, other, revs, force=force)
851 outgoing = discovery.findcommonoutgoing(repo, other, revs, force=force)
852 if not outgoing.missing:
852 if not outgoing.missing:
853 raise error.Abort(_('no outgoing ancestors'))
853 raise error.Abort(_('no outgoing ancestors'))
854 roots = list(repo.revs("roots(%ln)", outgoing.missing))
854 roots = list(repo.revs("roots(%ln)", outgoing.missing))
855 if 1 < len(roots):
855 if 1 < len(roots):
856 msg = _('there are ambiguous outgoing revisions')
856 msg = _('there are ambiguous outgoing revisions')
857 hint = _('see "hg help histedit" for more detail')
857 hint = _("see 'hg help histedit' for more detail")
858 raise error.Abort(msg, hint=hint)
858 raise error.Abort(msg, hint=hint)
859 return repo.lookup(roots[0])
859 return repo.lookup(roots[0])
860
860
861
861
862 @command('histedit',
862 @command('histedit',
863 [('', 'commands', '',
863 [('', 'commands', '',
864 _('read history edits from the specified file'), _('FILE')),
864 _('read history edits from the specified file'), _('FILE')),
865 ('c', 'continue', False, _('continue an edit already in progress')),
865 ('c', 'continue', False, _('continue an edit already in progress')),
866 ('', 'edit-plan', False, _('edit remaining actions list')),
866 ('', 'edit-plan', False, _('edit remaining actions list')),
867 ('k', 'keep', False,
867 ('k', 'keep', False,
868 _("don't strip old nodes after edit is complete")),
868 _("don't strip old nodes after edit is complete")),
869 ('', 'abort', False, _('abort an edit in progress')),
869 ('', 'abort', False, _('abort an edit in progress')),
870 ('o', 'outgoing', False, _('changesets not found in destination')),
870 ('o', 'outgoing', False, _('changesets not found in destination')),
871 ('f', 'force', False,
871 ('f', 'force', False,
872 _('force outgoing even for unrelated repositories')),
872 _('force outgoing even for unrelated repositories')),
873 ('r', 'rev', [], _('first revision to be edited'), _('REV'))],
873 ('r', 'rev', [], _('first revision to be edited'), _('REV'))],
874 _("[OPTIONS] ([ANCESTOR] | --outgoing [URL])"))
874 _("[OPTIONS] ([ANCESTOR] | --outgoing [URL])"))
875 def histedit(ui, repo, *freeargs, **opts):
875 def histedit(ui, repo, *freeargs, **opts):
876 """interactively edit changeset history
876 """interactively edit changeset history
877
877
878 This command lets you edit a linear series of changesets (up to
878 This command lets you edit a linear series of changesets (up to
879 and including the working directory, which should be clean).
879 and including the working directory, which should be clean).
880 You can:
880 You can:
881
881
882 - `pick` to [re]order a changeset
882 - `pick` to [re]order a changeset
883
883
884 - `drop` to omit changeset
884 - `drop` to omit changeset
885
885
886 - `mess` to reword the changeset commit message
886 - `mess` to reword the changeset commit message
887
887
888 - `fold` to combine it with the preceding changeset
888 - `fold` to combine it with the preceding changeset
889
889
890 - `roll` like fold, but discarding this commit's description
890 - `roll` like fold, but discarding this commit's description
891
891
892 - `edit` to edit this changeset
892 - `edit` to edit this changeset
893
893
894 There are a number of ways to select the root changeset:
894 There are a number of ways to select the root changeset:
895
895
896 - Specify ANCESTOR directly
896 - Specify ANCESTOR directly
897
897
898 - Use --outgoing -- it will be the first linear changeset not
898 - Use --outgoing -- it will be the first linear changeset not
899 included in destination. (See :hg:`help config.paths.default-push`)
899 included in destination. (See :hg:`help config.paths.default-push`)
900
900
901 - Otherwise, the value from the "histedit.defaultrev" config option
901 - Otherwise, the value from the "histedit.defaultrev" config option
902 is used as a revset to select the base revision when ANCESTOR is not
902 is used as a revset to select the base revision when ANCESTOR is not
903 specified. The first revision returned by the revset is used. By
903 specified. The first revision returned by the revset is used. By
904 default, this selects the editable history that is unique to the
904 default, this selects the editable history that is unique to the
905 ancestry of the working directory.
905 ancestry of the working directory.
906
906
907 .. container:: verbose
907 .. container:: verbose
908
908
909 If you use --outgoing, this command will abort if there are ambiguous
909 If you use --outgoing, this command will abort if there are ambiguous
910 outgoing revisions. For example, if there are multiple branches
910 outgoing revisions. For example, if there are multiple branches
911 containing outgoing revisions.
911 containing outgoing revisions.
912
912
913 Use "min(outgoing() and ::.)" or similar revset specification
913 Use "min(outgoing() and ::.)" or similar revset specification
914 instead of --outgoing to specify edit target revision exactly in
914 instead of --outgoing to specify edit target revision exactly in
915 such ambiguous situation. See :hg:`help revsets` for detail about
915 such ambiguous situation. See :hg:`help revsets` for detail about
916 selecting revisions.
916 selecting revisions.
917
917
918 .. container:: verbose
918 .. container:: verbose
919
919
920 Examples:
920 Examples:
921
921
922 - A number of changes have been made.
922 - A number of changes have been made.
923 Revision 3 is no longer needed.
923 Revision 3 is no longer needed.
924
924
925 Start history editing from revision 3::
925 Start history editing from revision 3::
926
926
927 hg histedit -r 3
927 hg histedit -r 3
928
928
929 An editor opens, containing the list of revisions,
929 An editor opens, containing the list of revisions,
930 with specific actions specified::
930 with specific actions specified::
931
931
932 pick 5339bf82f0ca 3 Zworgle the foobar
932 pick 5339bf82f0ca 3 Zworgle the foobar
933 pick 8ef592ce7cc4 4 Bedazzle the zerlog
933 pick 8ef592ce7cc4 4 Bedazzle the zerlog
934 pick 0a9639fcda9d 5 Morgify the cromulancy
934 pick 0a9639fcda9d 5 Morgify the cromulancy
935
935
936 Additional information about the possible actions
936 Additional information about the possible actions
937 to take appears below the list of revisions.
937 to take appears below the list of revisions.
938
938
939 To remove revision 3 from the history,
939 To remove revision 3 from the history,
940 its action (at the beginning of the relevant line)
940 its action (at the beginning of the relevant line)
941 is changed to 'drop'::
941 is changed to 'drop'::
942
942
943 drop 5339bf82f0ca 3 Zworgle the foobar
943 drop 5339bf82f0ca 3 Zworgle the foobar
944 pick 8ef592ce7cc4 4 Bedazzle the zerlog
944 pick 8ef592ce7cc4 4 Bedazzle the zerlog
945 pick 0a9639fcda9d 5 Morgify the cromulancy
945 pick 0a9639fcda9d 5 Morgify the cromulancy
946
946
947 - A number of changes have been made.
947 - A number of changes have been made.
948 Revision 2 and 4 need to be swapped.
948 Revision 2 and 4 need to be swapped.
949
949
950 Start history editing from revision 2::
950 Start history editing from revision 2::
951
951
952 hg histedit -r 2
952 hg histedit -r 2
953
953
954 An editor opens, containing the list of revisions,
954 An editor opens, containing the list of revisions,
955 with specific actions specified::
955 with specific actions specified::
956
956
957 pick 252a1af424ad 2 Blorb a morgwazzle
957 pick 252a1af424ad 2 Blorb a morgwazzle
958 pick 5339bf82f0ca 3 Zworgle the foobar
958 pick 5339bf82f0ca 3 Zworgle the foobar
959 pick 8ef592ce7cc4 4 Bedazzle the zerlog
959 pick 8ef592ce7cc4 4 Bedazzle the zerlog
960
960
961 To swap revision 2 and 4, its lines are swapped
961 To swap revision 2 and 4, its lines are swapped
962 in the editor::
962 in the editor::
963
963
964 pick 8ef592ce7cc4 4 Bedazzle the zerlog
964 pick 8ef592ce7cc4 4 Bedazzle the zerlog
965 pick 5339bf82f0ca 3 Zworgle the foobar
965 pick 5339bf82f0ca 3 Zworgle the foobar
966 pick 252a1af424ad 2 Blorb a morgwazzle
966 pick 252a1af424ad 2 Blorb a morgwazzle
967
967
968 Returns 0 on success, 1 if user intervention is required (not only
968 Returns 0 on success, 1 if user intervention is required (not only
969 for intentional "edit" command, but also for resolving unexpected
969 for intentional "edit" command, but also for resolving unexpected
970 conflicts).
970 conflicts).
971 """
971 """
972 state = histeditstate(repo)
972 state = histeditstate(repo)
973 try:
973 try:
974 state.wlock = repo.wlock()
974 state.wlock = repo.wlock()
975 state.lock = repo.lock()
975 state.lock = repo.lock()
976 _histedit(ui, repo, state, *freeargs, **opts)
976 _histedit(ui, repo, state, *freeargs, **opts)
977 finally:
977 finally:
978 release(state.lock, state.wlock)
978 release(state.lock, state.wlock)
979
979
980 goalcontinue = 'continue'
980 goalcontinue = 'continue'
981 goalabort = 'abort'
981 goalabort = 'abort'
982 goaleditplan = 'edit-plan'
982 goaleditplan = 'edit-plan'
983 goalnew = 'new'
983 goalnew = 'new'
984
984
985 def _getgoal(opts):
985 def _getgoal(opts):
986 if opts.get('continue'):
986 if opts.get('continue'):
987 return goalcontinue
987 return goalcontinue
988 if opts.get('abort'):
988 if opts.get('abort'):
989 return goalabort
989 return goalabort
990 if opts.get('edit_plan'):
990 if opts.get('edit_plan'):
991 return goaleditplan
991 return goaleditplan
992 return goalnew
992 return goalnew
993
993
994 def _readfile(path):
994 def _readfile(path):
995 if path == '-':
995 if path == '-':
996 return sys.stdin.read()
996 return sys.stdin.read()
997 else:
997 else:
998 with open(path, 'rb') as f:
998 with open(path, 'rb') as f:
999 return f.read()
999 return f.read()
1000
1000
1001 def _validateargs(ui, repo, state, freeargs, opts, goal, rules, revs):
1001 def _validateargs(ui, repo, state, freeargs, opts, goal, rules, revs):
1002 # TODO only abort if we try to histedit mq patches, not just
1002 # TODO only abort if we try to histedit mq patches, not just
1003 # blanket if mq patches are applied somewhere
1003 # blanket if mq patches are applied somewhere
1004 mq = getattr(repo, 'mq', None)
1004 mq = getattr(repo, 'mq', None)
1005 if mq and mq.applied:
1005 if mq and mq.applied:
1006 raise error.Abort(_('source has mq patches applied'))
1006 raise error.Abort(_('source has mq patches applied'))
1007
1007
1008 # basic argument incompatibility processing
1008 # basic argument incompatibility processing
1009 outg = opts.get('outgoing')
1009 outg = opts.get('outgoing')
1010 editplan = opts.get('edit_plan')
1010 editplan = opts.get('edit_plan')
1011 abort = opts.get('abort')
1011 abort = opts.get('abort')
1012 force = opts.get('force')
1012 force = opts.get('force')
1013 if force and not outg:
1013 if force and not outg:
1014 raise error.Abort(_('--force only allowed with --outgoing'))
1014 raise error.Abort(_('--force only allowed with --outgoing'))
1015 if goal == 'continue':
1015 if goal == 'continue':
1016 if any((outg, abort, revs, freeargs, rules, editplan)):
1016 if any((outg, abort, revs, freeargs, rules, editplan)):
1017 raise error.Abort(_('no arguments allowed with --continue'))
1017 raise error.Abort(_('no arguments allowed with --continue'))
1018 elif goal == 'abort':
1018 elif goal == 'abort':
1019 if any((outg, revs, freeargs, rules, editplan)):
1019 if any((outg, revs, freeargs, rules, editplan)):
1020 raise error.Abort(_('no arguments allowed with --abort'))
1020 raise error.Abort(_('no arguments allowed with --abort'))
1021 elif goal == 'edit-plan':
1021 elif goal == 'edit-plan':
1022 if any((outg, revs, freeargs)):
1022 if any((outg, revs, freeargs)):
1023 raise error.Abort(_('only --commands argument allowed with '
1023 raise error.Abort(_('only --commands argument allowed with '
1024 '--edit-plan'))
1024 '--edit-plan'))
1025 else:
1025 else:
1026 if os.path.exists(os.path.join(repo.path, 'histedit-state')):
1026 if os.path.exists(os.path.join(repo.path, 'histedit-state')):
1027 raise error.Abort(_('history edit already in progress, try '
1027 raise error.Abort(_('history edit already in progress, try '
1028 '--continue or --abort'))
1028 '--continue or --abort'))
1029 if outg:
1029 if outg:
1030 if revs:
1030 if revs:
1031 raise error.Abort(_('no revisions allowed with --outgoing'))
1031 raise error.Abort(_('no revisions allowed with --outgoing'))
1032 if len(freeargs) > 1:
1032 if len(freeargs) > 1:
1033 raise error.Abort(
1033 raise error.Abort(
1034 _('only one repo argument allowed with --outgoing'))
1034 _('only one repo argument allowed with --outgoing'))
1035 else:
1035 else:
1036 revs.extend(freeargs)
1036 revs.extend(freeargs)
1037 if len(revs) == 0:
1037 if len(revs) == 0:
1038 defaultrev = destutil.desthistedit(ui, repo)
1038 defaultrev = destutil.desthistedit(ui, repo)
1039 if defaultrev is not None:
1039 if defaultrev is not None:
1040 revs.append(defaultrev)
1040 revs.append(defaultrev)
1041
1041
1042 if len(revs) != 1:
1042 if len(revs) != 1:
1043 raise error.Abort(
1043 raise error.Abort(
1044 _('histedit requires exactly one ancestor revision'))
1044 _('histedit requires exactly one ancestor revision'))
1045
1045
1046 def _histedit(ui, repo, state, *freeargs, **opts):
1046 def _histedit(ui, repo, state, *freeargs, **opts):
1047 goal = _getgoal(opts)
1047 goal = _getgoal(opts)
1048 revs = opts.get('rev', [])
1048 revs = opts.get('rev', [])
1049 rules = opts.get('commands', '')
1049 rules = opts.get('commands', '')
1050 state.keep = opts.get('keep', False)
1050 state.keep = opts.get('keep', False)
1051
1051
1052 _validateargs(ui, repo, state, freeargs, opts, goal, rules, revs)
1052 _validateargs(ui, repo, state, freeargs, opts, goal, rules, revs)
1053
1053
1054 # rebuild state
1054 # rebuild state
1055 if goal == goalcontinue:
1055 if goal == goalcontinue:
1056 state.read()
1056 state.read()
1057 state = bootstrapcontinue(ui, state, opts)
1057 state = bootstrapcontinue(ui, state, opts)
1058 elif goal == goaleditplan:
1058 elif goal == goaleditplan:
1059 _edithisteditplan(ui, repo, state, rules)
1059 _edithisteditplan(ui, repo, state, rules)
1060 return
1060 return
1061 elif goal == goalabort:
1061 elif goal == goalabort:
1062 _aborthistedit(ui, repo, state)
1062 _aborthistedit(ui, repo, state)
1063 return
1063 return
1064 else:
1064 else:
1065 # goal == goalnew
1065 # goal == goalnew
1066 _newhistedit(ui, repo, state, revs, freeargs, opts)
1066 _newhistedit(ui, repo, state, revs, freeargs, opts)
1067
1067
1068 _continuehistedit(ui, repo, state)
1068 _continuehistedit(ui, repo, state)
1069 _finishhistedit(ui, repo, state)
1069 _finishhistedit(ui, repo, state)
1070
1070
1071 def _continuehistedit(ui, repo, state):
1071 def _continuehistedit(ui, repo, state):
1072 """This function runs after either:
1072 """This function runs after either:
1073 - bootstrapcontinue (if the goal is 'continue')
1073 - bootstrapcontinue (if the goal is 'continue')
1074 - _newhistedit (if the goal is 'new')
1074 - _newhistedit (if the goal is 'new')
1075 """
1075 """
1076 # preprocess rules so that we can hide inner folds from the user
1076 # preprocess rules so that we can hide inner folds from the user
1077 # and only show one editor
1077 # and only show one editor
1078 actions = state.actions[:]
1078 actions = state.actions[:]
1079 for idx, (action, nextact) in enumerate(
1079 for idx, (action, nextact) in enumerate(
1080 zip(actions, actions[1:] + [None])):
1080 zip(actions, actions[1:] + [None])):
1081 if action.verb == 'fold' and nextact and nextact.verb == 'fold':
1081 if action.verb == 'fold' and nextact and nextact.verb == 'fold':
1082 state.actions[idx].__class__ = _multifold
1082 state.actions[idx].__class__ = _multifold
1083
1083
1084 total = len(state.actions)
1084 total = len(state.actions)
1085 pos = 0
1085 pos = 0
1086 while state.actions:
1086 while state.actions:
1087 state.write()
1087 state.write()
1088 actobj = state.actions.pop(0)
1088 actobj = state.actions.pop(0)
1089 pos += 1
1089 pos += 1
1090 ui.progress(_("editing"), pos, actobj.torule(),
1090 ui.progress(_("editing"), pos, actobj.torule(),
1091 _('changes'), total)
1091 _('changes'), total)
1092 ui.debug('histedit: processing %s %s\n' % (actobj.verb,\
1092 ui.debug('histedit: processing %s %s\n' % (actobj.verb,\
1093 actobj.torule()))
1093 actobj.torule()))
1094 parentctx, replacement_ = actobj.run()
1094 parentctx, replacement_ = actobj.run()
1095 state.parentctxnode = parentctx.node()
1095 state.parentctxnode = parentctx.node()
1096 state.replacements.extend(replacement_)
1096 state.replacements.extend(replacement_)
1097 state.write()
1097 state.write()
1098 ui.progress(_("editing"), None)
1098 ui.progress(_("editing"), None)
1099
1099
1100 def _finishhistedit(ui, repo, state):
1100 def _finishhistedit(ui, repo, state):
1101 """This action runs when histedit is finishing its session"""
1101 """This action runs when histedit is finishing its session"""
1102 repo.ui.pushbuffer()
1102 repo.ui.pushbuffer()
1103 hg.update(repo, state.parentctxnode, quietempty=True)
1103 hg.update(repo, state.parentctxnode, quietempty=True)
1104 repo.ui.popbuffer()
1104 repo.ui.popbuffer()
1105
1105
1106 mapping, tmpnodes, created, ntm = processreplacement(state)
1106 mapping, tmpnodes, created, ntm = processreplacement(state)
1107 if mapping:
1107 if mapping:
1108 for prec, succs in mapping.iteritems():
1108 for prec, succs in mapping.iteritems():
1109 if not succs:
1109 if not succs:
1110 ui.debug('histedit: %s is dropped\n' % node.short(prec))
1110 ui.debug('histedit: %s is dropped\n' % node.short(prec))
1111 else:
1111 else:
1112 ui.debug('histedit: %s is replaced by %s\n' % (
1112 ui.debug('histedit: %s is replaced by %s\n' % (
1113 node.short(prec), node.short(succs[0])))
1113 node.short(prec), node.short(succs[0])))
1114 if len(succs) > 1:
1114 if len(succs) > 1:
1115 m = 'histedit: %s'
1115 m = 'histedit: %s'
1116 for n in succs[1:]:
1116 for n in succs[1:]:
1117 ui.debug(m % node.short(n))
1117 ui.debug(m % node.short(n))
1118
1118
1119 supportsmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
1119 supportsmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
1120 if supportsmarkers:
1120 if supportsmarkers:
1121 # Only create markers if the temp nodes weren't already removed.
1121 # Only create markers if the temp nodes weren't already removed.
1122 obsolete.createmarkers(repo, ((repo[t],()) for t in sorted(tmpnodes)
1122 obsolete.createmarkers(repo, ((repo[t],()) for t in sorted(tmpnodes)
1123 if t in repo))
1123 if t in repo))
1124 else:
1124 else:
1125 cleanupnode(ui, repo, 'temp', tmpnodes)
1125 cleanupnode(ui, repo, 'temp', tmpnodes)
1126
1126
1127 if not state.keep:
1127 if not state.keep:
1128 if mapping:
1128 if mapping:
1129 movebookmarks(ui, repo, mapping, state.topmost, ntm)
1129 movebookmarks(ui, repo, mapping, state.topmost, ntm)
1130 # TODO update mq state
1130 # TODO update mq state
1131 if supportsmarkers:
1131 if supportsmarkers:
1132 markers = []
1132 markers = []
1133 # sort by revision number because it sound "right"
1133 # sort by revision number because it sound "right"
1134 for prec in sorted(mapping, key=repo.changelog.rev):
1134 for prec in sorted(mapping, key=repo.changelog.rev):
1135 succs = mapping[prec]
1135 succs = mapping[prec]
1136 markers.append((repo[prec],
1136 markers.append((repo[prec],
1137 tuple(repo[s] for s in succs)))
1137 tuple(repo[s] for s in succs)))
1138 if markers:
1138 if markers:
1139 obsolete.createmarkers(repo, markers)
1139 obsolete.createmarkers(repo, markers)
1140 else:
1140 else:
1141 cleanupnode(ui, repo, 'replaced', mapping)
1141 cleanupnode(ui, repo, 'replaced', mapping)
1142
1142
1143 state.clear()
1143 state.clear()
1144 if os.path.exists(repo.sjoin('undo')):
1144 if os.path.exists(repo.sjoin('undo')):
1145 os.unlink(repo.sjoin('undo'))
1145 os.unlink(repo.sjoin('undo'))
1146 if repo.vfs.exists('histedit-last-edit.txt'):
1146 if repo.vfs.exists('histedit-last-edit.txt'):
1147 repo.vfs.unlink('histedit-last-edit.txt')
1147 repo.vfs.unlink('histedit-last-edit.txt')
1148
1148
1149 def _aborthistedit(ui, repo, state):
1149 def _aborthistedit(ui, repo, state):
1150 try:
1150 try:
1151 state.read()
1151 state.read()
1152 __, leafs, tmpnodes, __ = processreplacement(state)
1152 __, leafs, tmpnodes, __ = processreplacement(state)
1153 ui.debug('restore wc to old parent %s\n'
1153 ui.debug('restore wc to old parent %s\n'
1154 % node.short(state.topmost))
1154 % node.short(state.topmost))
1155
1155
1156 # Recover our old commits if necessary
1156 # Recover our old commits if necessary
1157 if not state.topmost in repo and state.backupfile:
1157 if not state.topmost in repo and state.backupfile:
1158 backupfile = repo.join(state.backupfile)
1158 backupfile = repo.join(state.backupfile)
1159 f = hg.openpath(ui, backupfile)
1159 f = hg.openpath(ui, backupfile)
1160 gen = exchange.readbundle(ui, f, backupfile)
1160 gen = exchange.readbundle(ui, f, backupfile)
1161 with repo.transaction('histedit.abort') as tr:
1161 with repo.transaction('histedit.abort') as tr:
1162 if not isinstance(gen, bundle2.unbundle20):
1162 if not isinstance(gen, bundle2.unbundle20):
1163 gen.apply(repo, 'histedit', 'bundle:' + backupfile)
1163 gen.apply(repo, 'histedit', 'bundle:' + backupfile)
1164 if isinstance(gen, bundle2.unbundle20):
1164 if isinstance(gen, bundle2.unbundle20):
1165 bundle2.applybundle(repo, gen, tr,
1165 bundle2.applybundle(repo, gen, tr,
1166 source='histedit',
1166 source='histedit',
1167 url='bundle:' + backupfile)
1167 url='bundle:' + backupfile)
1168
1168
1169 os.remove(backupfile)
1169 os.remove(backupfile)
1170
1170
1171 # check whether we should update away
1171 # check whether we should update away
1172 if repo.unfiltered().revs('parents() and (%n or %ln::)',
1172 if repo.unfiltered().revs('parents() and (%n or %ln::)',
1173 state.parentctxnode, leafs | tmpnodes):
1173 state.parentctxnode, leafs | tmpnodes):
1174 hg.clean(repo, state.topmost, show_stats=True, quietempty=True)
1174 hg.clean(repo, state.topmost, show_stats=True, quietempty=True)
1175 cleanupnode(ui, repo, 'created', tmpnodes)
1175 cleanupnode(ui, repo, 'created', tmpnodes)
1176 cleanupnode(ui, repo, 'temp', leafs)
1176 cleanupnode(ui, repo, 'temp', leafs)
1177 except Exception:
1177 except Exception:
1178 if state.inprogress():
1178 if state.inprogress():
1179 ui.warn(_('warning: encountered an exception during histedit '
1179 ui.warn(_('warning: encountered an exception during histedit '
1180 '--abort; the repository may not have been completely '
1180 '--abort; the repository may not have been completely '
1181 'cleaned up\n'))
1181 'cleaned up\n'))
1182 raise
1182 raise
1183 finally:
1183 finally:
1184 state.clear()
1184 state.clear()
1185
1185
1186 def _edithisteditplan(ui, repo, state, rules):
1186 def _edithisteditplan(ui, repo, state, rules):
1187 state.read()
1187 state.read()
1188 if not rules:
1188 if not rules:
1189 comment = geteditcomment(ui,
1189 comment = geteditcomment(ui,
1190 node.short(state.parentctxnode),
1190 node.short(state.parentctxnode),
1191 node.short(state.topmost))
1191 node.short(state.topmost))
1192 rules = ruleeditor(repo, ui, state.actions, comment)
1192 rules = ruleeditor(repo, ui, state.actions, comment)
1193 else:
1193 else:
1194 rules = _readfile(rules)
1194 rules = _readfile(rules)
1195 actions = parserules(rules, state)
1195 actions = parserules(rules, state)
1196 ctxs = [repo[act.node] \
1196 ctxs = [repo[act.node] \
1197 for act in state.actions if act.node]
1197 for act in state.actions if act.node]
1198 warnverifyactions(ui, repo, actions, state, ctxs)
1198 warnverifyactions(ui, repo, actions, state, ctxs)
1199 state.actions = actions
1199 state.actions = actions
1200 state.write()
1200 state.write()
1201
1201
1202 def _newhistedit(ui, repo, state, revs, freeargs, opts):
1202 def _newhistedit(ui, repo, state, revs, freeargs, opts):
1203 outg = opts.get('outgoing')
1203 outg = opts.get('outgoing')
1204 rules = opts.get('commands', '')
1204 rules = opts.get('commands', '')
1205 force = opts.get('force')
1205 force = opts.get('force')
1206
1206
1207 cmdutil.checkunfinished(repo)
1207 cmdutil.checkunfinished(repo)
1208 cmdutil.bailifchanged(repo)
1208 cmdutil.bailifchanged(repo)
1209
1209
1210 topmost, empty = repo.dirstate.parents()
1210 topmost, empty = repo.dirstate.parents()
1211 if outg:
1211 if outg:
1212 if freeargs:
1212 if freeargs:
1213 remote = freeargs[0]
1213 remote = freeargs[0]
1214 else:
1214 else:
1215 remote = None
1215 remote = None
1216 root = findoutgoing(ui, repo, remote, force, opts)
1216 root = findoutgoing(ui, repo, remote, force, opts)
1217 else:
1217 else:
1218 rr = list(repo.set('roots(%ld)', scmutil.revrange(repo, revs)))
1218 rr = list(repo.set('roots(%ld)', scmutil.revrange(repo, revs)))
1219 if len(rr) != 1:
1219 if len(rr) != 1:
1220 raise error.Abort(_('The specified revisions must have '
1220 raise error.Abort(_('The specified revisions must have '
1221 'exactly one common root'))
1221 'exactly one common root'))
1222 root = rr[0].node()
1222 root = rr[0].node()
1223
1223
1224 revs = between(repo, root, topmost, state.keep)
1224 revs = between(repo, root, topmost, state.keep)
1225 if not revs:
1225 if not revs:
1226 raise error.Abort(_('%s is not an ancestor of working directory') %
1226 raise error.Abort(_('%s is not an ancestor of working directory') %
1227 node.short(root))
1227 node.short(root))
1228
1228
1229 ctxs = [repo[r] for r in revs]
1229 ctxs = [repo[r] for r in revs]
1230 if not rules:
1230 if not rules:
1231 comment = geteditcomment(ui, node.short(root), node.short(topmost))
1231 comment = geteditcomment(ui, node.short(root), node.short(topmost))
1232 actions = [pick(state, r) for r in revs]
1232 actions = [pick(state, r) for r in revs]
1233 rules = ruleeditor(repo, ui, actions, comment)
1233 rules = ruleeditor(repo, ui, actions, comment)
1234 else:
1234 else:
1235 rules = _readfile(rules)
1235 rules = _readfile(rules)
1236 actions = parserules(rules, state)
1236 actions = parserules(rules, state)
1237 warnverifyactions(ui, repo, actions, state, ctxs)
1237 warnverifyactions(ui, repo, actions, state, ctxs)
1238
1238
1239 parentctxnode = repo[root].parents()[0].node()
1239 parentctxnode = repo[root].parents()[0].node()
1240
1240
1241 state.parentctxnode = parentctxnode
1241 state.parentctxnode = parentctxnode
1242 state.actions = actions
1242 state.actions = actions
1243 state.topmost = topmost
1243 state.topmost = topmost
1244 state.replacements = []
1244 state.replacements = []
1245
1245
1246 # Create a backup so we can always abort completely.
1246 # Create a backup so we can always abort completely.
1247 backupfile = None
1247 backupfile = None
1248 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1248 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1249 backupfile = repair._bundle(repo, [parentctxnode], [topmost], root,
1249 backupfile = repair._bundle(repo, [parentctxnode], [topmost], root,
1250 'histedit')
1250 'histedit')
1251 state.backupfile = backupfile
1251 state.backupfile = backupfile
1252
1252
1253 def _getsummary(ctx):
1253 def _getsummary(ctx):
1254 # a common pattern is to extract the summary but default to the empty
1254 # a common pattern is to extract the summary but default to the empty
1255 # string
1255 # string
1256 summary = ctx.description() or ''
1256 summary = ctx.description() or ''
1257 if summary:
1257 if summary:
1258 summary = summary.splitlines()[0]
1258 summary = summary.splitlines()[0]
1259 return summary
1259 return summary
1260
1260
1261 def bootstrapcontinue(ui, state, opts):
1261 def bootstrapcontinue(ui, state, opts):
1262 repo = state.repo
1262 repo = state.repo
1263 if state.actions:
1263 if state.actions:
1264 actobj = state.actions.pop(0)
1264 actobj = state.actions.pop(0)
1265
1265
1266 if _isdirtywc(repo):
1266 if _isdirtywc(repo):
1267 actobj.continuedirty()
1267 actobj.continuedirty()
1268 if _isdirtywc(repo):
1268 if _isdirtywc(repo):
1269 abortdirty()
1269 abortdirty()
1270
1270
1271 parentctx, replacements = actobj.continueclean()
1271 parentctx, replacements = actobj.continueclean()
1272
1272
1273 state.parentctxnode = parentctx.node()
1273 state.parentctxnode = parentctx.node()
1274 state.replacements.extend(replacements)
1274 state.replacements.extend(replacements)
1275
1275
1276 return state
1276 return state
1277
1277
1278 def between(repo, old, new, keep):
1278 def between(repo, old, new, keep):
1279 """select and validate the set of revision to edit
1279 """select and validate the set of revision to edit
1280
1280
1281 When keep is false, the specified set can't have children."""
1281 When keep is false, the specified set can't have children."""
1282 ctxs = list(repo.set('%n::%n', old, new))
1282 ctxs = list(repo.set('%n::%n', old, new))
1283 if ctxs and not keep:
1283 if ctxs and not keep:
1284 if (not obsolete.isenabled(repo, obsolete.allowunstableopt) and
1284 if (not obsolete.isenabled(repo, obsolete.allowunstableopt) and
1285 repo.revs('(%ld::) - (%ld)', ctxs, ctxs)):
1285 repo.revs('(%ld::) - (%ld)', ctxs, ctxs)):
1286 raise error.Abort(_('can only histedit a changeset together '
1286 raise error.Abort(_('can only histedit a changeset together '
1287 'with all its descendants'))
1287 'with all its descendants'))
1288 if repo.revs('(%ld) and merge()', ctxs):
1288 if repo.revs('(%ld) and merge()', ctxs):
1289 raise error.Abort(_('cannot edit history that contains merges'))
1289 raise error.Abort(_('cannot edit history that contains merges'))
1290 root = ctxs[0] # list is already sorted by repo.set
1290 root = ctxs[0] # list is already sorted by repo.set
1291 if not root.mutable():
1291 if not root.mutable():
1292 raise error.Abort(_('cannot edit public changeset: %s') % root,
1292 raise error.Abort(_('cannot edit public changeset: %s') % root,
1293 hint=_('see "hg help phases" for details'))
1293 hint=_("see 'hg help phases' for details"))
1294 return [c.node() for c in ctxs]
1294 return [c.node() for c in ctxs]
1295
1295
1296 def ruleeditor(repo, ui, actions, editcomment=""):
1296 def ruleeditor(repo, ui, actions, editcomment=""):
1297 """open an editor to edit rules
1297 """open an editor to edit rules
1298
1298
1299 rules are in the format [ [act, ctx], ...] like in state.rules
1299 rules are in the format [ [act, ctx], ...] like in state.rules
1300 """
1300 """
1301 if repo.ui.configbool("experimental", "histedit.autoverb"):
1301 if repo.ui.configbool("experimental", "histedit.autoverb"):
1302 newact = util.sortdict()
1302 newact = util.sortdict()
1303 for act in actions:
1303 for act in actions:
1304 ctx = repo[act.node]
1304 ctx = repo[act.node]
1305 summary = _getsummary(ctx)
1305 summary = _getsummary(ctx)
1306 fword = summary.split(' ', 1)[0].lower()
1306 fword = summary.split(' ', 1)[0].lower()
1307 added = False
1307 added = False
1308
1308
1309 # if it doesn't end with the special character '!' just skip this
1309 # if it doesn't end with the special character '!' just skip this
1310 if fword.endswith('!'):
1310 if fword.endswith('!'):
1311 fword = fword[:-1]
1311 fword = fword[:-1]
1312 if fword in primaryactions | secondaryactions | tertiaryactions:
1312 if fword in primaryactions | secondaryactions | tertiaryactions:
1313 act.verb = fword
1313 act.verb = fword
1314 # get the target summary
1314 # get the target summary
1315 tsum = summary[len(fword) + 1:].lstrip()
1315 tsum = summary[len(fword) + 1:].lstrip()
1316 # safe but slow: reverse iterate over the actions so we
1316 # safe but slow: reverse iterate over the actions so we
1317 # don't clash on two commits having the same summary
1317 # don't clash on two commits having the same summary
1318 for na, l in reversed(list(newact.iteritems())):
1318 for na, l in reversed(list(newact.iteritems())):
1319 actx = repo[na.node]
1319 actx = repo[na.node]
1320 asum = _getsummary(actx)
1320 asum = _getsummary(actx)
1321 if asum == tsum:
1321 if asum == tsum:
1322 added = True
1322 added = True
1323 l.append(act)
1323 l.append(act)
1324 break
1324 break
1325
1325
1326 if not added:
1326 if not added:
1327 newact[act] = []
1327 newact[act] = []
1328
1328
1329 # copy over and flatten the new list
1329 # copy over and flatten the new list
1330 actions = []
1330 actions = []
1331 for na, l in newact.iteritems():
1331 for na, l in newact.iteritems():
1332 actions.append(na)
1332 actions.append(na)
1333 actions += l
1333 actions += l
1334
1334
1335 rules = '\n'.join([act.torule() for act in actions])
1335 rules = '\n'.join([act.torule() for act in actions])
1336 rules += '\n\n'
1336 rules += '\n\n'
1337 rules += editcomment
1337 rules += editcomment
1338 rules = ui.edit(rules, ui.username(), {'prefix': 'histedit'})
1338 rules = ui.edit(rules, ui.username(), {'prefix': 'histedit'})
1339
1339
1340 # Save edit rules in .hg/histedit-last-edit.txt in case
1340 # Save edit rules in .hg/histedit-last-edit.txt in case
1341 # the user needs to ask for help after something
1341 # the user needs to ask for help after something
1342 # surprising happens.
1342 # surprising happens.
1343 f = open(repo.join('histedit-last-edit.txt'), 'w')
1343 f = open(repo.join('histedit-last-edit.txt'), 'w')
1344 f.write(rules)
1344 f.write(rules)
1345 f.close()
1345 f.close()
1346
1346
1347 return rules
1347 return rules
1348
1348
1349 def parserules(rules, state):
1349 def parserules(rules, state):
1350 """Read the histedit rules string and return list of action objects """
1350 """Read the histedit rules string and return list of action objects """
1351 rules = [l for l in (r.strip() for r in rules.splitlines())
1351 rules = [l for l in (r.strip() for r in rules.splitlines())
1352 if l and not l.startswith('#')]
1352 if l and not l.startswith('#')]
1353 actions = []
1353 actions = []
1354 for r in rules:
1354 for r in rules:
1355 if ' ' not in r:
1355 if ' ' not in r:
1356 raise error.ParseError(_('malformed line "%s"') % r)
1356 raise error.ParseError(_('malformed line "%s"') % r)
1357 verb, rest = r.split(' ', 1)
1357 verb, rest = r.split(' ', 1)
1358
1358
1359 if verb not in actiontable:
1359 if verb not in actiontable:
1360 raise error.ParseError(_('unknown action "%s"') % verb)
1360 raise error.ParseError(_('unknown action "%s"') % verb)
1361
1361
1362 action = actiontable[verb].fromrule(state, rest)
1362 action = actiontable[verb].fromrule(state, rest)
1363 actions.append(action)
1363 actions.append(action)
1364 return actions
1364 return actions
1365
1365
1366 def warnverifyactions(ui, repo, actions, state, ctxs):
1366 def warnverifyactions(ui, repo, actions, state, ctxs):
1367 try:
1367 try:
1368 verifyactions(actions, state, ctxs)
1368 verifyactions(actions, state, ctxs)
1369 except error.ParseError:
1369 except error.ParseError:
1370 if repo.vfs.exists('histedit-last-edit.txt'):
1370 if repo.vfs.exists('histedit-last-edit.txt'):
1371 ui.warn(_('warning: histedit rules saved '
1371 ui.warn(_('warning: histedit rules saved '
1372 'to: .hg/histedit-last-edit.txt\n'))
1372 'to: .hg/histedit-last-edit.txt\n'))
1373 raise
1373 raise
1374
1374
1375 def verifyactions(actions, state, ctxs):
1375 def verifyactions(actions, state, ctxs):
1376 """Verify that there exists exactly one action per given changeset and
1376 """Verify that there exists exactly one action per given changeset and
1377 other constraints.
1377 other constraints.
1378
1378
1379 Will abort if there are to many or too few rules, a malformed rule,
1379 Will abort if there are to many or too few rules, a malformed rule,
1380 or a rule on a changeset outside of the user-given range.
1380 or a rule on a changeset outside of the user-given range.
1381 """
1381 """
1382 expected = set(c.node() for c in ctxs)
1382 expected = set(c.node() for c in ctxs)
1383 seen = set()
1383 seen = set()
1384 prev = None
1384 prev = None
1385 for action in actions:
1385 for action in actions:
1386 action.verify(prev, expected, seen)
1386 action.verify(prev, expected, seen)
1387 prev = action
1387 prev = action
1388 if action.node is not None:
1388 if action.node is not None:
1389 seen.add(action.node)
1389 seen.add(action.node)
1390 missing = sorted(expected - seen) # sort to stabilize output
1390 missing = sorted(expected - seen) # sort to stabilize output
1391
1391
1392 if state.repo.ui.configbool('histedit', 'dropmissing'):
1392 if state.repo.ui.configbool('histedit', 'dropmissing'):
1393 if len(actions) == 0:
1393 if len(actions) == 0:
1394 raise error.ParseError(_('no rules provided'),
1394 raise error.ParseError(_('no rules provided'),
1395 hint=_('use strip extension to remove commits'))
1395 hint=_('use strip extension to remove commits'))
1396
1396
1397 drops = [drop(state, n) for n in missing]
1397 drops = [drop(state, n) for n in missing]
1398 # put the in the beginning so they execute immediately and
1398 # put the in the beginning so they execute immediately and
1399 # don't show in the edit-plan in the future
1399 # don't show in the edit-plan in the future
1400 actions[:0] = drops
1400 actions[:0] = drops
1401 elif missing:
1401 elif missing:
1402 raise error.ParseError(_('missing rules for changeset %s') %
1402 raise error.ParseError(_('missing rules for changeset %s') %
1403 node.short(missing[0]),
1403 node.short(missing[0]),
1404 hint=_('use "drop %s" to discard, see also: '
1404 hint=_('use "drop %s" to discard, see also: '
1405 '"hg help -e histedit.config"')
1405 "'hg help -e histedit.config'")
1406 % node.short(missing[0]))
1406 % node.short(missing[0]))
1407
1407
1408 def adjustreplacementsfrommarkers(repo, oldreplacements):
1408 def adjustreplacementsfrommarkers(repo, oldreplacements):
1409 """Adjust replacements from obsolescense markers
1409 """Adjust replacements from obsolescense markers
1410
1410
1411 Replacements structure is originally generated based on
1411 Replacements structure is originally generated based on
1412 histedit's state and does not account for changes that are
1412 histedit's state and does not account for changes that are
1413 not recorded there. This function fixes that by adding
1413 not recorded there. This function fixes that by adding
1414 data read from obsolescense markers"""
1414 data read from obsolescense markers"""
1415 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1415 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1416 return oldreplacements
1416 return oldreplacements
1417
1417
1418 unfi = repo.unfiltered()
1418 unfi = repo.unfiltered()
1419 nm = unfi.changelog.nodemap
1419 nm = unfi.changelog.nodemap
1420 obsstore = repo.obsstore
1420 obsstore = repo.obsstore
1421 newreplacements = list(oldreplacements)
1421 newreplacements = list(oldreplacements)
1422 oldsuccs = [r[1] for r in oldreplacements]
1422 oldsuccs = [r[1] for r in oldreplacements]
1423 # successors that have already been added to succstocheck once
1423 # successors that have already been added to succstocheck once
1424 seensuccs = set().union(*oldsuccs) # create a set from an iterable of tuples
1424 seensuccs = set().union(*oldsuccs) # create a set from an iterable of tuples
1425 succstocheck = list(seensuccs)
1425 succstocheck = list(seensuccs)
1426 while succstocheck:
1426 while succstocheck:
1427 n = succstocheck.pop()
1427 n = succstocheck.pop()
1428 missing = nm.get(n) is None
1428 missing = nm.get(n) is None
1429 markers = obsstore.successors.get(n, ())
1429 markers = obsstore.successors.get(n, ())
1430 if missing and not markers:
1430 if missing and not markers:
1431 # dead end, mark it as such
1431 # dead end, mark it as such
1432 newreplacements.append((n, ()))
1432 newreplacements.append((n, ()))
1433 for marker in markers:
1433 for marker in markers:
1434 nsuccs = marker[1]
1434 nsuccs = marker[1]
1435 newreplacements.append((n, nsuccs))
1435 newreplacements.append((n, nsuccs))
1436 for nsucc in nsuccs:
1436 for nsucc in nsuccs:
1437 if nsucc not in seensuccs:
1437 if nsucc not in seensuccs:
1438 seensuccs.add(nsucc)
1438 seensuccs.add(nsucc)
1439 succstocheck.append(nsucc)
1439 succstocheck.append(nsucc)
1440
1440
1441 return newreplacements
1441 return newreplacements
1442
1442
1443 def processreplacement(state):
1443 def processreplacement(state):
1444 """process the list of replacements to return
1444 """process the list of replacements to return
1445
1445
1446 1) the final mapping between original and created nodes
1446 1) the final mapping between original and created nodes
1447 2) the list of temporary node created by histedit
1447 2) the list of temporary node created by histedit
1448 3) the list of new commit created by histedit"""
1448 3) the list of new commit created by histedit"""
1449 replacements = adjustreplacementsfrommarkers(state.repo, state.replacements)
1449 replacements = adjustreplacementsfrommarkers(state.repo, state.replacements)
1450 allsuccs = set()
1450 allsuccs = set()
1451 replaced = set()
1451 replaced = set()
1452 fullmapping = {}
1452 fullmapping = {}
1453 # initialize basic set
1453 # initialize basic set
1454 # fullmapping records all operations recorded in replacement
1454 # fullmapping records all operations recorded in replacement
1455 for rep in replacements:
1455 for rep in replacements:
1456 allsuccs.update(rep[1])
1456 allsuccs.update(rep[1])
1457 replaced.add(rep[0])
1457 replaced.add(rep[0])
1458 fullmapping.setdefault(rep[0], set()).update(rep[1])
1458 fullmapping.setdefault(rep[0], set()).update(rep[1])
1459 new = allsuccs - replaced
1459 new = allsuccs - replaced
1460 tmpnodes = allsuccs & replaced
1460 tmpnodes = allsuccs & replaced
1461 # Reduce content fullmapping into direct relation between original nodes
1461 # Reduce content fullmapping into direct relation between original nodes
1462 # and final node created during history edition
1462 # and final node created during history edition
1463 # Dropped changeset are replaced by an empty list
1463 # Dropped changeset are replaced by an empty list
1464 toproceed = set(fullmapping)
1464 toproceed = set(fullmapping)
1465 final = {}
1465 final = {}
1466 while toproceed:
1466 while toproceed:
1467 for x in list(toproceed):
1467 for x in list(toproceed):
1468 succs = fullmapping[x]
1468 succs = fullmapping[x]
1469 for s in list(succs):
1469 for s in list(succs):
1470 if s in toproceed:
1470 if s in toproceed:
1471 # non final node with unknown closure
1471 # non final node with unknown closure
1472 # We can't process this now
1472 # We can't process this now
1473 break
1473 break
1474 elif s in final:
1474 elif s in final:
1475 # non final node, replace with closure
1475 # non final node, replace with closure
1476 succs.remove(s)
1476 succs.remove(s)
1477 succs.update(final[s])
1477 succs.update(final[s])
1478 else:
1478 else:
1479 final[x] = succs
1479 final[x] = succs
1480 toproceed.remove(x)
1480 toproceed.remove(x)
1481 # remove tmpnodes from final mapping
1481 # remove tmpnodes from final mapping
1482 for n in tmpnodes:
1482 for n in tmpnodes:
1483 del final[n]
1483 del final[n]
1484 # we expect all changes involved in final to exist in the repo
1484 # we expect all changes involved in final to exist in the repo
1485 # turn `final` into list (topologically sorted)
1485 # turn `final` into list (topologically sorted)
1486 nm = state.repo.changelog.nodemap
1486 nm = state.repo.changelog.nodemap
1487 for prec, succs in final.items():
1487 for prec, succs in final.items():
1488 final[prec] = sorted(succs, key=nm.get)
1488 final[prec] = sorted(succs, key=nm.get)
1489
1489
1490 # computed topmost element (necessary for bookmark)
1490 # computed topmost element (necessary for bookmark)
1491 if new:
1491 if new:
1492 newtopmost = sorted(new, key=state.repo.changelog.rev)[-1]
1492 newtopmost = sorted(new, key=state.repo.changelog.rev)[-1]
1493 elif not final:
1493 elif not final:
1494 # Nothing rewritten at all. we won't need `newtopmost`
1494 # Nothing rewritten at all. we won't need `newtopmost`
1495 # It is the same as `oldtopmost` and `processreplacement` know it
1495 # It is the same as `oldtopmost` and `processreplacement` know it
1496 newtopmost = None
1496 newtopmost = None
1497 else:
1497 else:
1498 # every body died. The newtopmost is the parent of the root.
1498 # every body died. The newtopmost is the parent of the root.
1499 r = state.repo.changelog.rev
1499 r = state.repo.changelog.rev
1500 newtopmost = state.repo[sorted(final, key=r)[0]].p1().node()
1500 newtopmost = state.repo[sorted(final, key=r)[0]].p1().node()
1501
1501
1502 return final, tmpnodes, new, newtopmost
1502 return final, tmpnodes, new, newtopmost
1503
1503
1504 def movebookmarks(ui, repo, mapping, oldtopmost, newtopmost):
1504 def movebookmarks(ui, repo, mapping, oldtopmost, newtopmost):
1505 """Move bookmark from old to newly created node"""
1505 """Move bookmark from old to newly created node"""
1506 if not mapping:
1506 if not mapping:
1507 # if nothing got rewritten there is not purpose for this function
1507 # if nothing got rewritten there is not purpose for this function
1508 return
1508 return
1509 moves = []
1509 moves = []
1510 for bk, old in sorted(repo._bookmarks.iteritems()):
1510 for bk, old in sorted(repo._bookmarks.iteritems()):
1511 if old == oldtopmost:
1511 if old == oldtopmost:
1512 # special case ensure bookmark stay on tip.
1512 # special case ensure bookmark stay on tip.
1513 #
1513 #
1514 # This is arguably a feature and we may only want that for the
1514 # This is arguably a feature and we may only want that for the
1515 # active bookmark. But the behavior is kept compatible with the old
1515 # active bookmark. But the behavior is kept compatible with the old
1516 # version for now.
1516 # version for now.
1517 moves.append((bk, newtopmost))
1517 moves.append((bk, newtopmost))
1518 continue
1518 continue
1519 base = old
1519 base = old
1520 new = mapping.get(base, None)
1520 new = mapping.get(base, None)
1521 if new is None:
1521 if new is None:
1522 continue
1522 continue
1523 while not new:
1523 while not new:
1524 # base is killed, trying with parent
1524 # base is killed, trying with parent
1525 base = repo[base].p1().node()
1525 base = repo[base].p1().node()
1526 new = mapping.get(base, (base,))
1526 new = mapping.get(base, (base,))
1527 # nothing to move
1527 # nothing to move
1528 moves.append((bk, new[-1]))
1528 moves.append((bk, new[-1]))
1529 if moves:
1529 if moves:
1530 lock = tr = None
1530 lock = tr = None
1531 try:
1531 try:
1532 lock = repo.lock()
1532 lock = repo.lock()
1533 tr = repo.transaction('histedit')
1533 tr = repo.transaction('histedit')
1534 marks = repo._bookmarks
1534 marks = repo._bookmarks
1535 for mark, new in moves:
1535 for mark, new in moves:
1536 old = marks[mark]
1536 old = marks[mark]
1537 ui.note(_('histedit: moving bookmarks %s from %s to %s\n')
1537 ui.note(_('histedit: moving bookmarks %s from %s to %s\n')
1538 % (mark, node.short(old), node.short(new)))
1538 % (mark, node.short(old), node.short(new)))
1539 marks[mark] = new
1539 marks[mark] = new
1540 marks.recordchange(tr)
1540 marks.recordchange(tr)
1541 tr.close()
1541 tr.close()
1542 finally:
1542 finally:
1543 release(tr, lock)
1543 release(tr, lock)
1544
1544
1545 def cleanupnode(ui, repo, name, nodes):
1545 def cleanupnode(ui, repo, name, nodes):
1546 """strip a group of nodes from the repository
1546 """strip a group of nodes from the repository
1547
1547
1548 The set of node to strip may contains unknown nodes."""
1548 The set of node to strip may contains unknown nodes."""
1549 ui.debug('should strip %s nodes %s\n' %
1549 ui.debug('should strip %s nodes %s\n' %
1550 (name, ', '.join([node.short(n) for n in nodes])))
1550 (name, ', '.join([node.short(n) for n in nodes])))
1551 with repo.lock():
1551 with repo.lock():
1552 # do not let filtering get in the way of the cleanse
1552 # do not let filtering get in the way of the cleanse
1553 # we should probably get rid of obsolescence marker created during the
1553 # we should probably get rid of obsolescence marker created during the
1554 # histedit, but we currently do not have such information.
1554 # histedit, but we currently do not have such information.
1555 repo = repo.unfiltered()
1555 repo = repo.unfiltered()
1556 # Find all nodes that need to be stripped
1556 # Find all nodes that need to be stripped
1557 # (we use %lr instead of %ln to silently ignore unknown items)
1557 # (we use %lr instead of %ln to silently ignore unknown items)
1558 nm = repo.changelog.nodemap
1558 nm = repo.changelog.nodemap
1559 nodes = sorted(n for n in nodes if n in nm)
1559 nodes = sorted(n for n in nodes if n in nm)
1560 roots = [c.node() for c in repo.set("roots(%ln)", nodes)]
1560 roots = [c.node() for c in repo.set("roots(%ln)", nodes)]
1561 for c in roots:
1561 for c in roots:
1562 # We should process node in reverse order to strip tip most first.
1562 # We should process node in reverse order to strip tip most first.
1563 # but this trigger a bug in changegroup hook.
1563 # but this trigger a bug in changegroup hook.
1564 # This would reduce bundle overhead
1564 # This would reduce bundle overhead
1565 repair.strip(ui, repo, c)
1565 repair.strip(ui, repo, c)
1566
1566
1567 def stripwrapper(orig, ui, repo, nodelist, *args, **kwargs):
1567 def stripwrapper(orig, ui, repo, nodelist, *args, **kwargs):
1568 if isinstance(nodelist, str):
1568 if isinstance(nodelist, str):
1569 nodelist = [nodelist]
1569 nodelist = [nodelist]
1570 if os.path.exists(os.path.join(repo.path, 'histedit-state')):
1570 if os.path.exists(os.path.join(repo.path, 'histedit-state')):
1571 state = histeditstate(repo)
1571 state = histeditstate(repo)
1572 state.read()
1572 state.read()
1573 histedit_nodes = set([action.node for action
1573 histedit_nodes = set([action.node for action
1574 in state.actions if action.node])
1574 in state.actions if action.node])
1575 strip_nodes = set([repo[n].node() for n in nodelist])
1575 strip_nodes = set([repo[n].node() for n in nodelist])
1576 common_nodes = histedit_nodes & strip_nodes
1576 common_nodes = histedit_nodes & strip_nodes
1577 if common_nodes:
1577 if common_nodes:
1578 raise error.Abort(_("histedit in progress, can't strip %s")
1578 raise error.Abort(_("histedit in progress, can't strip %s")
1579 % ', '.join(node.short(x) for x in common_nodes))
1579 % ', '.join(node.short(x) for x in common_nodes))
1580 return orig(ui, repo, nodelist, *args, **kwargs)
1580 return orig(ui, repo, nodelist, *args, **kwargs)
1581
1581
1582 extensions.wrapfunction(repair, 'strip', stripwrapper)
1582 extensions.wrapfunction(repair, 'strip', stripwrapper)
1583
1583
1584 def summaryhook(ui, repo):
1584 def summaryhook(ui, repo):
1585 if not os.path.exists(repo.join('histedit-state')):
1585 if not os.path.exists(repo.join('histedit-state')):
1586 return
1586 return
1587 state = histeditstate(repo)
1587 state = histeditstate(repo)
1588 state.read()
1588 state.read()
1589 if state.actions:
1589 if state.actions:
1590 # i18n: column positioning for "hg summary"
1590 # i18n: column positioning for "hg summary"
1591 ui.write(_('hist: %s (histedit --continue)\n') %
1591 ui.write(_('hist: %s (histedit --continue)\n') %
1592 (ui.label(_('%d remaining'), 'histedit.remaining') %
1592 (ui.label(_('%d remaining'), 'histedit.remaining') %
1593 len(state.actions)))
1593 len(state.actions)))
1594
1594
1595 def extsetup(ui):
1595 def extsetup(ui):
1596 cmdutil.summaryhooks.add('histedit', summaryhook)
1596 cmdutil.summaryhooks.add('histedit', summaryhook)
1597 cmdutil.unfinishedstates.append(
1597 cmdutil.unfinishedstates.append(
1598 ['histedit-state', False, True, _('histedit in progress'),
1598 ['histedit-state', False, True, _('histedit in progress'),
1599 _("use 'hg histedit --continue' or 'hg histedit --abort'")])
1599 _("use 'hg histedit --continue' or 'hg histedit --abort'")])
1600 cmdutil.afterresolvedstates.append(
1600 cmdutil.afterresolvedstates.append(
1601 ['histedit-state', _('hg histedit --continue')])
1601 ['histedit-state', _('hg histedit --continue')])
1602 if ui.configbool("experimental", "histeditng"):
1602 if ui.configbool("experimental", "histeditng"):
1603 globals()['base'] = action(['base', 'b'],
1603 globals()['base'] = action(['base', 'b'],
1604 _('checkout changeset and apply further changesets from there')
1604 _('checkout changeset and apply further changesets from there')
1605 )(base)
1605 )(base)
@@ -1,550 +1,550 b''
1 Test argument handling and various data parsing
1 Test argument handling and various data parsing
2 ==================================================
2 ==================================================
3
3
4
4
5 Enable extensions used by this test.
5 Enable extensions used by this test.
6 $ cat >>$HGRCPATH <<EOF
6 $ cat >>$HGRCPATH <<EOF
7 > [extensions]
7 > [extensions]
8 > histedit=
8 > histedit=
9 > EOF
9 > EOF
10
10
11 Repo setup.
11 Repo setup.
12 $ hg init foo
12 $ hg init foo
13 $ cd foo
13 $ cd foo
14 $ echo alpha >> alpha
14 $ echo alpha >> alpha
15 $ hg addr
15 $ hg addr
16 adding alpha
16 adding alpha
17 $ hg ci -m one
17 $ hg ci -m one
18 $ echo alpha >> alpha
18 $ echo alpha >> alpha
19 $ hg ci -m two
19 $ hg ci -m two
20 $ echo alpha >> alpha
20 $ echo alpha >> alpha
21 $ hg ci -m three
21 $ hg ci -m three
22 $ echo alpha >> alpha
22 $ echo alpha >> alpha
23 $ hg ci -m four
23 $ hg ci -m four
24 $ echo alpha >> alpha
24 $ echo alpha >> alpha
25 $ hg ci -m five
25 $ hg ci -m five
26
26
27 $ hg log --style compact --graph
27 $ hg log --style compact --graph
28 @ 4[tip] 08d98a8350f3 1970-01-01 00:00 +0000 test
28 @ 4[tip] 08d98a8350f3 1970-01-01 00:00 +0000 test
29 | five
29 | five
30 |
30 |
31 o 3 c8e68270e35a 1970-01-01 00:00 +0000 test
31 o 3 c8e68270e35a 1970-01-01 00:00 +0000 test
32 | four
32 | four
33 |
33 |
34 o 2 eb57da33312f 1970-01-01 00:00 +0000 test
34 o 2 eb57da33312f 1970-01-01 00:00 +0000 test
35 | three
35 | three
36 |
36 |
37 o 1 579e40513370 1970-01-01 00:00 +0000 test
37 o 1 579e40513370 1970-01-01 00:00 +0000 test
38 | two
38 | two
39 |
39 |
40 o 0 6058cbb6cfd7 1970-01-01 00:00 +0000 test
40 o 0 6058cbb6cfd7 1970-01-01 00:00 +0000 test
41 one
41 one
42
42
43
43
44 histedit --continue/--abort with no existing state
44 histedit --continue/--abort with no existing state
45 --------------------------------------------------
45 --------------------------------------------------
46
46
47 $ hg histedit --continue
47 $ hg histedit --continue
48 abort: no histedit in progress
48 abort: no histedit in progress
49 [255]
49 [255]
50 $ hg histedit --abort
50 $ hg histedit --abort
51 abort: no histedit in progress
51 abort: no histedit in progress
52 [255]
52 [255]
53
53
54 Run a dummy edit to make sure we get tip^^ correctly via revsingle.
54 Run a dummy edit to make sure we get tip^^ correctly via revsingle.
55 --------------------------------------------------------------------
55 --------------------------------------------------------------------
56
56
57 $ HGEDITOR=cat hg histedit "tip^^"
57 $ HGEDITOR=cat hg histedit "tip^^"
58 pick eb57da33312f 2 three
58 pick eb57da33312f 2 three
59 pick c8e68270e35a 3 four
59 pick c8e68270e35a 3 four
60 pick 08d98a8350f3 4 five
60 pick 08d98a8350f3 4 five
61
61
62 # Edit history between eb57da33312f and 08d98a8350f3
62 # Edit history between eb57da33312f and 08d98a8350f3
63 #
63 #
64 # Commits are listed from least to most recent
64 # Commits are listed from least to most recent
65 #
65 #
66 # You can reorder changesets by reordering the lines
66 # You can reorder changesets by reordering the lines
67 #
67 #
68 # Commands:
68 # Commands:
69 #
69 #
70 # e, edit = use commit, but stop for amending
70 # e, edit = use commit, but stop for amending
71 # m, mess = edit commit message without changing commit content
71 # m, mess = edit commit message without changing commit content
72 # p, pick = use commit
72 # p, pick = use commit
73 # d, drop = remove commit from history
73 # d, drop = remove commit from history
74 # f, fold = use commit, but combine it with the one above
74 # f, fold = use commit, but combine it with the one above
75 # r, roll = like fold, but discard this commit's description
75 # r, roll = like fold, but discard this commit's description
76 #
76 #
77
77
78 Run on a revision not ancestors of the current working directory.
78 Run on a revision not ancestors of the current working directory.
79 --------------------------------------------------------------------
79 --------------------------------------------------------------------
80
80
81 $ hg up 2
81 $ hg up 2
82 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
82 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
83 $ hg histedit -r 4
83 $ hg histedit -r 4
84 abort: 08d98a8350f3 is not an ancestor of working directory
84 abort: 08d98a8350f3 is not an ancestor of working directory
85 [255]
85 [255]
86 $ hg up --quiet
86 $ hg up --quiet
87
87
88
88
89 Test that we pick the minimum of a revrange
89 Test that we pick the minimum of a revrange
90 ---------------------------------------
90 ---------------------------------------
91
91
92 $ HGEDITOR=cat hg histedit '2::' --commands - << EOF
92 $ HGEDITOR=cat hg histedit '2::' --commands - << EOF
93 > pick eb57da33312f 2 three
93 > pick eb57da33312f 2 three
94 > pick c8e68270e35a 3 four
94 > pick c8e68270e35a 3 four
95 > pick 08d98a8350f3 4 five
95 > pick 08d98a8350f3 4 five
96 > EOF
96 > EOF
97 $ hg up --quiet
97 $ hg up --quiet
98
98
99 $ HGEDITOR=cat hg histedit 'tip:2' --commands - << EOF
99 $ HGEDITOR=cat hg histedit 'tip:2' --commands - << EOF
100 > pick eb57da33312f 2 three
100 > pick eb57da33312f 2 three
101 > pick c8e68270e35a 3 four
101 > pick c8e68270e35a 3 four
102 > pick 08d98a8350f3 4 five
102 > pick 08d98a8350f3 4 five
103 > EOF
103 > EOF
104 $ hg up --quiet
104 $ hg up --quiet
105
105
106 Test config specified default
106 Test config specified default
107 -----------------------------
107 -----------------------------
108
108
109 $ HGEDITOR=cat hg histedit --config "histedit.defaultrev=only(.) - ::eb57da33312f" --commands - << EOF
109 $ HGEDITOR=cat hg histedit --config "histedit.defaultrev=only(.) - ::eb57da33312f" --commands - << EOF
110 > pick c8e68270e35a 3 four
110 > pick c8e68270e35a 3 four
111 > pick 08d98a8350f3 4 five
111 > pick 08d98a8350f3 4 five
112 > EOF
112 > EOF
113
113
114 Run on a revision not descendants of the initial parent
114 Run on a revision not descendants of the initial parent
115 --------------------------------------------------------------------
115 --------------------------------------------------------------------
116
116
117 Test the message shown for inconsistent histedit state, which may be
117 Test the message shown for inconsistent histedit state, which may be
118 created (and forgotten) by Mercurial earlier than 2.7. This emulates
118 created (and forgotten) by Mercurial earlier than 2.7. This emulates
119 Mercurial earlier than 2.7 by renaming ".hg/histedit-state"
119 Mercurial earlier than 2.7 by renaming ".hg/histedit-state"
120 temporarily.
120 temporarily.
121
121
122 $ hg log -G -T '{rev} {shortest(node)} {desc}\n' -r 2::
122 $ hg log -G -T '{rev} {shortest(node)} {desc}\n' -r 2::
123 @ 4 08d9 five
123 @ 4 08d9 five
124 |
124 |
125 o 3 c8e6 four
125 o 3 c8e6 four
126 |
126 |
127 o 2 eb57 three
127 o 2 eb57 three
128 |
128 |
129 ~
129 ~
130 $ HGEDITOR=cat hg histedit -r 4 --commands - << EOF
130 $ HGEDITOR=cat hg histedit -r 4 --commands - << EOF
131 > edit 08d98a8350f3 4 five
131 > edit 08d98a8350f3 4 five
132 > EOF
132 > EOF
133 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
133 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
134 reverting alpha
134 reverting alpha
135 Editing (08d98a8350f3), you may commit or record as needed now.
135 Editing (08d98a8350f3), you may commit or record as needed now.
136 (hg histedit --continue to resume)
136 (hg histedit --continue to resume)
137 [1]
137 [1]
138
138
139 $ hg graft --continue
139 $ hg graft --continue
140 abort: no graft in progress
140 abort: no graft in progress
141 (continue: hg histedit --continue)
141 (continue: hg histedit --continue)
142 [255]
142 [255]
143
143
144 $ mv .hg/histedit-state .hg/histedit-state.back
144 $ mv .hg/histedit-state .hg/histedit-state.back
145 $ hg update --quiet --clean 2
145 $ hg update --quiet --clean 2
146 $ echo alpha >> alpha
146 $ echo alpha >> alpha
147 $ mv .hg/histedit-state.back .hg/histedit-state
147 $ mv .hg/histedit-state.back .hg/histedit-state
148
148
149 $ hg histedit --continue
149 $ hg histedit --continue
150 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/08d98a8350f3-02594089-backup.hg (glob)
150 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/08d98a8350f3-02594089-backup.hg (glob)
151 $ hg log -G -T '{rev} {shortest(node)} {desc}\n' -r 2::
151 $ hg log -G -T '{rev} {shortest(node)} {desc}\n' -r 2::
152 @ 4 f5ed five
152 @ 4 f5ed five
153 |
153 |
154 | o 3 c8e6 four
154 | o 3 c8e6 four
155 |/
155 |/
156 o 2 eb57 three
156 o 2 eb57 three
157 |
157 |
158 ~
158 ~
159
159
160 $ hg unbundle -q $TESTTMP/foo/.hg/strip-backup/08d98a8350f3-02594089-backup.hg
160 $ hg unbundle -q $TESTTMP/foo/.hg/strip-backup/08d98a8350f3-02594089-backup.hg
161 $ hg strip -q -r f5ed --config extensions.strip=
161 $ hg strip -q -r f5ed --config extensions.strip=
162 $ hg up -q 08d98a8350f3
162 $ hg up -q 08d98a8350f3
163
163
164 Test that missing revisions are detected
164 Test that missing revisions are detected
165 ---------------------------------------
165 ---------------------------------------
166
166
167 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
167 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
168 > pick eb57da33312f 2 three
168 > pick eb57da33312f 2 three
169 > pick 08d98a8350f3 4 five
169 > pick 08d98a8350f3 4 five
170 > EOF
170 > EOF
171 hg: parse error: missing rules for changeset c8e68270e35a
171 hg: parse error: missing rules for changeset c8e68270e35a
172 (use "drop c8e68270e35a" to discard, see also: "hg help -e histedit.config")
172 (use "drop c8e68270e35a" to discard, see also: 'hg help -e histedit.config')
173 [255]
173 [255]
174
174
175 Test that extra revisions are detected
175 Test that extra revisions are detected
176 ---------------------------------------
176 ---------------------------------------
177
177
178 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
178 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
179 > pick 6058cbb6cfd7 0 one
179 > pick 6058cbb6cfd7 0 one
180 > pick c8e68270e35a 3 four
180 > pick c8e68270e35a 3 four
181 > pick 08d98a8350f3 4 five
181 > pick 08d98a8350f3 4 five
182 > EOF
182 > EOF
183 hg: parse error: pick "6058cbb6cfd7" changeset was not a candidate
183 hg: parse error: pick "6058cbb6cfd7" changeset was not a candidate
184 (only use listed changesets)
184 (only use listed changesets)
185 [255]
185 [255]
186
186
187 Test malformed line
187 Test malformed line
188 ---------------------------------------
188 ---------------------------------------
189
189
190 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
190 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
191 > pickeb57da33312f2three
191 > pickeb57da33312f2three
192 > pick c8e68270e35a 3 four
192 > pick c8e68270e35a 3 four
193 > pick 08d98a8350f3 4 five
193 > pick 08d98a8350f3 4 five
194 > EOF
194 > EOF
195 hg: parse error: malformed line "pickeb57da33312f2three"
195 hg: parse error: malformed line "pickeb57da33312f2three"
196 [255]
196 [255]
197
197
198 Test unknown changeset
198 Test unknown changeset
199 ---------------------------------------
199 ---------------------------------------
200
200
201 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
201 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
202 > pick 0123456789ab 2 three
202 > pick 0123456789ab 2 three
203 > pick c8e68270e35a 3 four
203 > pick c8e68270e35a 3 four
204 > pick 08d98a8350f3 4 five
204 > pick 08d98a8350f3 4 five
205 > EOF
205 > EOF
206 hg: parse error: unknown changeset 0123456789ab listed
206 hg: parse error: unknown changeset 0123456789ab listed
207 [255]
207 [255]
208
208
209 Test unknown command
209 Test unknown command
210 ---------------------------------------
210 ---------------------------------------
211
211
212 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
212 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
213 > coin eb57da33312f 2 three
213 > coin eb57da33312f 2 three
214 > pick c8e68270e35a 3 four
214 > pick c8e68270e35a 3 four
215 > pick 08d98a8350f3 4 five
215 > pick 08d98a8350f3 4 five
216 > EOF
216 > EOF
217 hg: parse error: unknown action "coin"
217 hg: parse error: unknown action "coin"
218 [255]
218 [255]
219
219
220 Test duplicated changeset
220 Test duplicated changeset
221 ---------------------------------------
221 ---------------------------------------
222
222
223 So one is missing and one appear twice.
223 So one is missing and one appear twice.
224
224
225 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
225 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
226 > pick eb57da33312f 2 three
226 > pick eb57da33312f 2 three
227 > pick eb57da33312f 2 three
227 > pick eb57da33312f 2 three
228 > pick 08d98a8350f3 4 five
228 > pick 08d98a8350f3 4 five
229 > EOF
229 > EOF
230 hg: parse error: duplicated command for changeset eb57da33312f
230 hg: parse error: duplicated command for changeset eb57da33312f
231 [255]
231 [255]
232
232
233 Test bogus rev
233 Test bogus rev
234 ---------------------------------------
234 ---------------------------------------
235
235
236 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
236 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
237 > pick eb57da33312f 2 three
237 > pick eb57da33312f 2 three
238 > pick 0
238 > pick 0
239 > pick 08d98a8350f3 4 five
239 > pick 08d98a8350f3 4 five
240 > EOF
240 > EOF
241 hg: parse error: invalid changeset 0
241 hg: parse error: invalid changeset 0
242 [255]
242 [255]
243
243
244 Test short version of command
244 Test short version of command
245 ---------------------------------------
245 ---------------------------------------
246
246
247 Note: we use varying amounts of white space between command name and changeset
247 Note: we use varying amounts of white space between command name and changeset
248 short hash. This tests issue3893.
248 short hash. This tests issue3893.
249
249
250 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
250 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
251 > pick eb57da33312f 2 three
251 > pick eb57da33312f 2 three
252 > p c8e68270e35a 3 four
252 > p c8e68270e35a 3 four
253 > f 08d98a8350f3 4 five
253 > f 08d98a8350f3 4 five
254 > EOF
254 > EOF
255 four
255 four
256 ***
256 ***
257 five
257 five
258
258
259
259
260
260
261 HG: Enter commit message. Lines beginning with 'HG:' are removed.
261 HG: Enter commit message. Lines beginning with 'HG:' are removed.
262 HG: Leave message empty to abort commit.
262 HG: Leave message empty to abort commit.
263 HG: --
263 HG: --
264 HG: user: test
264 HG: user: test
265 HG: branch 'default'
265 HG: branch 'default'
266 HG: changed alpha
266 HG: changed alpha
267 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/*-backup.hg (glob)
267 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/*-backup.hg (glob)
268 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/*-backup.hg (glob)
268 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/*-backup.hg (glob)
269
269
270 $ hg update -q 2
270 $ hg update -q 2
271 $ echo x > x
271 $ echo x > x
272 $ hg add x
272 $ hg add x
273 $ hg commit -m'x' x
273 $ hg commit -m'x' x
274 created new head
274 created new head
275 $ hg histedit -r 'heads(all())'
275 $ hg histedit -r 'heads(all())'
276 abort: The specified revisions must have exactly one common root
276 abort: The specified revisions must have exactly one common root
277 [255]
277 [255]
278
278
279 Test that trimming description using multi-byte characters
279 Test that trimming description using multi-byte characters
280 --------------------------------------------------------------------
280 --------------------------------------------------------------------
281
281
282 $ python <<EOF
282 $ python <<EOF
283 > fp = open('logfile', 'w')
283 > fp = open('logfile', 'w')
284 > fp.write('12345678901234567890123456789012345678901234567890' +
284 > fp.write('12345678901234567890123456789012345678901234567890' +
285 > '12345') # there are 5 more columns for 80 columns
285 > '12345') # there are 5 more columns for 80 columns
286 >
286 >
287 > # 2 x 4 = 8 columns, but 3 x 4 = 12 bytes
287 > # 2 x 4 = 8 columns, but 3 x 4 = 12 bytes
288 > fp.write(u'\u3042\u3044\u3046\u3048'.encode('utf-8'))
288 > fp.write(u'\u3042\u3044\u3046\u3048'.encode('utf-8'))
289 >
289 >
290 > fp.close()
290 > fp.close()
291 > EOF
291 > EOF
292 $ echo xx >> x
292 $ echo xx >> x
293 $ hg --encoding utf-8 commit --logfile logfile
293 $ hg --encoding utf-8 commit --logfile logfile
294
294
295 $ HGEDITOR=cat hg --encoding utf-8 histedit tip
295 $ HGEDITOR=cat hg --encoding utf-8 histedit tip
296 pick 3d3ea1f3a10b 5 1234567890123456789012345678901234567890123456789012345\xe3\x81\x82... (esc)
296 pick 3d3ea1f3a10b 5 1234567890123456789012345678901234567890123456789012345\xe3\x81\x82... (esc)
297
297
298 # Edit history between 3d3ea1f3a10b and 3d3ea1f3a10b
298 # Edit history between 3d3ea1f3a10b and 3d3ea1f3a10b
299 #
299 #
300 # Commits are listed from least to most recent
300 # Commits are listed from least to most recent
301 #
301 #
302 # You can reorder changesets by reordering the lines
302 # You can reorder changesets by reordering the lines
303 #
303 #
304 # Commands:
304 # Commands:
305 #
305 #
306 # e, edit = use commit, but stop for amending
306 # e, edit = use commit, but stop for amending
307 # m, mess = edit commit message without changing commit content
307 # m, mess = edit commit message without changing commit content
308 # p, pick = use commit
308 # p, pick = use commit
309 # d, drop = remove commit from history
309 # d, drop = remove commit from history
310 # f, fold = use commit, but combine it with the one above
310 # f, fold = use commit, but combine it with the one above
311 # r, roll = like fold, but discard this commit's description
311 # r, roll = like fold, but discard this commit's description
312 #
312 #
313
313
314 Test --continue with --keep
314 Test --continue with --keep
315
315
316 $ hg strip -q -r . --config extensions.strip=
316 $ hg strip -q -r . --config extensions.strip=
317 $ hg histedit '.^' -q --keep --commands - << EOF
317 $ hg histedit '.^' -q --keep --commands - << EOF
318 > edit eb57da33312f 2 three
318 > edit eb57da33312f 2 three
319 > pick f3cfcca30c44 4 x
319 > pick f3cfcca30c44 4 x
320 > EOF
320 > EOF
321 Editing (eb57da33312f), you may commit or record as needed now.
321 Editing (eb57da33312f), you may commit or record as needed now.
322 (hg histedit --continue to resume)
322 (hg histedit --continue to resume)
323 [1]
323 [1]
324 $ echo edit >> alpha
324 $ echo edit >> alpha
325 $ hg histedit -q --continue
325 $ hg histedit -q --continue
326 $ hg log -G -T '{rev}:{node|short} {desc}'
326 $ hg log -G -T '{rev}:{node|short} {desc}'
327 @ 6:8fda0c726bf2 x
327 @ 6:8fda0c726bf2 x
328 |
328 |
329 o 5:63379946892c three
329 o 5:63379946892c three
330 |
330 |
331 | o 4:f3cfcca30c44 x
331 | o 4:f3cfcca30c44 x
332 | |
332 | |
333 | | o 3:2a30f3cfee78 four
333 | | o 3:2a30f3cfee78 four
334 | |/ ***
334 | |/ ***
335 | | five
335 | | five
336 | o 2:eb57da33312f three
336 | o 2:eb57da33312f three
337 |/
337 |/
338 o 1:579e40513370 two
338 o 1:579e40513370 two
339 |
339 |
340 o 0:6058cbb6cfd7 one
340 o 0:6058cbb6cfd7 one
341
341
342
342
343 Test that abort fails gracefully on exception
343 Test that abort fails gracefully on exception
344 ----------------------------------------------
344 ----------------------------------------------
345 $ hg histedit . -q --commands - << EOF
345 $ hg histedit . -q --commands - << EOF
346 > edit 8fda0c726bf2 6 x
346 > edit 8fda0c726bf2 6 x
347 > EOF
347 > EOF
348 Editing (8fda0c726bf2), you may commit or record as needed now.
348 Editing (8fda0c726bf2), you may commit or record as needed now.
349 (hg histedit --continue to resume)
349 (hg histedit --continue to resume)
350 [1]
350 [1]
351 Corrupt histedit state file
351 Corrupt histedit state file
352 $ sed 's/8fda0c726bf2/123456789012/' .hg/histedit-state > ../corrupt-histedit
352 $ sed 's/8fda0c726bf2/123456789012/' .hg/histedit-state > ../corrupt-histedit
353 $ mv ../corrupt-histedit .hg/histedit-state
353 $ mv ../corrupt-histedit .hg/histedit-state
354 $ hg histedit --abort
354 $ hg histedit --abort
355 warning: encountered an exception during histedit --abort; the repository may not have been completely cleaned up
355 warning: encountered an exception during histedit --abort; the repository may not have been completely cleaned up
356 abort: .*(No such file or directory:|The system cannot find the file specified).* (re)
356 abort: .*(No such file or directory:|The system cannot find the file specified).* (re)
357 [255]
357 [255]
358 Histedit state has been exited
358 Histedit state has been exited
359 $ hg summary -q
359 $ hg summary -q
360 parent: 5:63379946892c
360 parent: 5:63379946892c
361 commit: 1 added, 1 unknown (new branch head)
361 commit: 1 added, 1 unknown (new branch head)
362 update: 4 new changesets (update)
362 update: 4 new changesets (update)
363
363
364 $ cd ..
364 $ cd ..
365
365
366 Set up default base revision tests
366 Set up default base revision tests
367
367
368 $ hg init defaultbase
368 $ hg init defaultbase
369 $ cd defaultbase
369 $ cd defaultbase
370 $ touch foo
370 $ touch foo
371 $ hg -q commit -A -m root
371 $ hg -q commit -A -m root
372 $ echo 1 > foo
372 $ echo 1 > foo
373 $ hg commit -m 'public 1'
373 $ hg commit -m 'public 1'
374 $ hg phase --force --public -r .
374 $ hg phase --force --public -r .
375 $ echo 2 > foo
375 $ echo 2 > foo
376 $ hg commit -m 'draft after public'
376 $ hg commit -m 'draft after public'
377 $ hg -q up -r 1
377 $ hg -q up -r 1
378 $ echo 3 > foo
378 $ echo 3 > foo
379 $ hg commit -m 'head 1 public'
379 $ hg commit -m 'head 1 public'
380 created new head
380 created new head
381 $ hg phase --force --public -r .
381 $ hg phase --force --public -r .
382 $ echo 4 > foo
382 $ echo 4 > foo
383 $ hg commit -m 'head 1 draft 1'
383 $ hg commit -m 'head 1 draft 1'
384 $ echo 5 > foo
384 $ echo 5 > foo
385 $ hg commit -m 'head 1 draft 2'
385 $ hg commit -m 'head 1 draft 2'
386 $ hg -q up -r 2
386 $ hg -q up -r 2
387 $ echo 6 > foo
387 $ echo 6 > foo
388 $ hg commit -m 'head 2 commit 1'
388 $ hg commit -m 'head 2 commit 1'
389 $ echo 7 > foo
389 $ echo 7 > foo
390 $ hg commit -m 'head 2 commit 2'
390 $ hg commit -m 'head 2 commit 2'
391 $ hg -q up -r 2
391 $ hg -q up -r 2
392 $ echo 8 > foo
392 $ echo 8 > foo
393 $ hg commit -m 'head 3'
393 $ hg commit -m 'head 3'
394 created new head
394 created new head
395 $ hg -q up -r 2
395 $ hg -q up -r 2
396 $ echo 9 > foo
396 $ echo 9 > foo
397 $ hg commit -m 'head 4'
397 $ hg commit -m 'head 4'
398 created new head
398 created new head
399 $ hg merge --tool :local -r 8
399 $ hg merge --tool :local -r 8
400 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
400 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
401 (branch merge, don't forget to commit)
401 (branch merge, don't forget to commit)
402 $ hg commit -m 'merge head 3 into head 4'
402 $ hg commit -m 'merge head 3 into head 4'
403 $ echo 11 > foo
403 $ echo 11 > foo
404 $ hg commit -m 'commit 1 after merge'
404 $ hg commit -m 'commit 1 after merge'
405 $ echo 12 > foo
405 $ echo 12 > foo
406 $ hg commit -m 'commit 2 after merge'
406 $ hg commit -m 'commit 2 after merge'
407
407
408 $ hg log -G -T '{rev}:{node|short} {phase} {desc}\n'
408 $ hg log -G -T '{rev}:{node|short} {phase} {desc}\n'
409 @ 12:8cde254db839 draft commit 2 after merge
409 @ 12:8cde254db839 draft commit 2 after merge
410 |
410 |
411 o 11:6f2f0241f119 draft commit 1 after merge
411 o 11:6f2f0241f119 draft commit 1 after merge
412 |
412 |
413 o 10:90506cc76b00 draft merge head 3 into head 4
413 o 10:90506cc76b00 draft merge head 3 into head 4
414 |\
414 |\
415 | o 9:f8607a373a97 draft head 4
415 | o 9:f8607a373a97 draft head 4
416 | |
416 | |
417 o | 8:0da92be05148 draft head 3
417 o | 8:0da92be05148 draft head 3
418 |/
418 |/
419 | o 7:4c35cdf97d5e draft head 2 commit 2
419 | o 7:4c35cdf97d5e draft head 2 commit 2
420 | |
420 | |
421 | o 6:931820154288 draft head 2 commit 1
421 | o 6:931820154288 draft head 2 commit 1
422 |/
422 |/
423 | o 5:8cdc02b9bc63 draft head 1 draft 2
423 | o 5:8cdc02b9bc63 draft head 1 draft 2
424 | |
424 | |
425 | o 4:463b8c0d2973 draft head 1 draft 1
425 | o 4:463b8c0d2973 draft head 1 draft 1
426 | |
426 | |
427 | o 3:23a0c4eefcbf public head 1 public
427 | o 3:23a0c4eefcbf public head 1 public
428 | |
428 | |
429 o | 2:4117331c3abb draft draft after public
429 o | 2:4117331c3abb draft draft after public
430 |/
430 |/
431 o 1:4426d359ea59 public public 1
431 o 1:4426d359ea59 public public 1
432 |
432 |
433 o 0:54136a8ddf32 public root
433 o 0:54136a8ddf32 public root
434
434
435
435
436 Default base revision should stop at public changesets
436 Default base revision should stop at public changesets
437
437
438 $ hg -q up 8cdc02b9bc63
438 $ hg -q up 8cdc02b9bc63
439 $ hg histedit --commands - <<EOF
439 $ hg histedit --commands - <<EOF
440 > pick 463b8c0d2973
440 > pick 463b8c0d2973
441 > pick 8cdc02b9bc63
441 > pick 8cdc02b9bc63
442 > EOF
442 > EOF
443
443
444 Default base revision should stop at branchpoint
444 Default base revision should stop at branchpoint
445
445
446 $ hg -q up 4c35cdf97d5e
446 $ hg -q up 4c35cdf97d5e
447 $ hg histedit --commands - <<EOF
447 $ hg histedit --commands - <<EOF
448 > pick 931820154288
448 > pick 931820154288
449 > pick 4c35cdf97d5e
449 > pick 4c35cdf97d5e
450 > EOF
450 > EOF
451
451
452 Default base revision should stop at merge commit
452 Default base revision should stop at merge commit
453
453
454 $ hg -q up 8cde254db839
454 $ hg -q up 8cde254db839
455 $ hg histedit --commands - <<EOF
455 $ hg histedit --commands - <<EOF
456 > pick 6f2f0241f119
456 > pick 6f2f0241f119
457 > pick 8cde254db839
457 > pick 8cde254db839
458 > EOF
458 > EOF
459
459
460 commit --amend should abort if histedit is in progress
460 commit --amend should abort if histedit is in progress
461 (issue4800) and markers are not being created.
461 (issue4800) and markers are not being created.
462 Eventually, histedit could perhaps look at `source` extra,
462 Eventually, histedit could perhaps look at `source` extra,
463 in which case this test should be revisited.
463 in which case this test should be revisited.
464
464
465 $ hg -q up 8cde254db839
465 $ hg -q up 8cde254db839
466 $ hg histedit 6f2f0241f119 --commands - <<EOF
466 $ hg histedit 6f2f0241f119 --commands - <<EOF
467 > pick 8cde254db839
467 > pick 8cde254db839
468 > edit 6f2f0241f119
468 > edit 6f2f0241f119
469 > EOF
469 > EOF
470 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
470 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
471 merging foo
471 merging foo
472 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
472 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
473 Fix up the change (pick 8cde254db839)
473 Fix up the change (pick 8cde254db839)
474 (hg histedit --continue to resume)
474 (hg histedit --continue to resume)
475 [1]
475 [1]
476 $ hg resolve -m --all
476 $ hg resolve -m --all
477 (no more unresolved files)
477 (no more unresolved files)
478 continue: hg histedit --continue
478 continue: hg histedit --continue
479 $ hg histedit --cont
479 $ hg histedit --cont
480 merging foo
480 merging foo
481 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
481 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
482 Editing (6f2f0241f119), you may commit or record as needed now.
482 Editing (6f2f0241f119), you may commit or record as needed now.
483 (hg histedit --continue to resume)
483 (hg histedit --continue to resume)
484 [1]
484 [1]
485 $ hg resolve -m --all
485 $ hg resolve -m --all
486 (no more unresolved files)
486 (no more unresolved files)
487 continue: hg histedit --continue
487 continue: hg histedit --continue
488 $ hg commit --amend -m 'reject this fold'
488 $ hg commit --amend -m 'reject this fold'
489 abort: histedit in progress
489 abort: histedit in progress
490 (use 'hg histedit --continue' or 'hg histedit --abort')
490 (use 'hg histedit --continue' or 'hg histedit --abort')
491 [255]
491 [255]
492
492
493 With markers enabled, histedit does not get confused, and
493 With markers enabled, histedit does not get confused, and
494 amend should not be blocked by the ongoing histedit.
494 amend should not be blocked by the ongoing histedit.
495
495
496 $ cat >>$HGRCPATH <<EOF
496 $ cat >>$HGRCPATH <<EOF
497 > [experimental]
497 > [experimental]
498 > evolution=createmarkers,allowunstable
498 > evolution=createmarkers,allowunstable
499 > EOF
499 > EOF
500 $ hg commit --amend -m 'allow this fold'
500 $ hg commit --amend -m 'allow this fold'
501 $ hg histedit --continue
501 $ hg histedit --continue
502
502
503 $ cd ..
503 $ cd ..
504
504
505 Test autoverb feature
505 Test autoverb feature
506
506
507 $ hg init autoverb
507 $ hg init autoverb
508 $ cd autoverb
508 $ cd autoverb
509 $ echo alpha >> alpha
509 $ echo alpha >> alpha
510 $ hg ci -qAm one
510 $ hg ci -qAm one
511 $ echo alpha >> alpha
511 $ echo alpha >> alpha
512 $ hg ci -qm two
512 $ hg ci -qm two
513 $ echo beta >> beta
513 $ echo beta >> beta
514 $ hg ci -qAm "roll! one"
514 $ hg ci -qAm "roll! one"
515
515
516 $ hg log --style compact --graph
516 $ hg log --style compact --graph
517 @ 2[tip] 4f34d0f8b5fa 1970-01-01 00:00 +0000 test
517 @ 2[tip] 4f34d0f8b5fa 1970-01-01 00:00 +0000 test
518 | roll! one
518 | roll! one
519 |
519 |
520 o 1 579e40513370 1970-01-01 00:00 +0000 test
520 o 1 579e40513370 1970-01-01 00:00 +0000 test
521 | two
521 | two
522 |
522 |
523 o 0 6058cbb6cfd7 1970-01-01 00:00 +0000 test
523 o 0 6058cbb6cfd7 1970-01-01 00:00 +0000 test
524 one
524 one
525
525
526
526
527 Check that 'roll' is selected by default
527 Check that 'roll' is selected by default
528
528
529 $ HGEDITOR=cat hg histedit 0 --config experimental.histedit.autoverb=True
529 $ HGEDITOR=cat hg histedit 0 --config experimental.histedit.autoverb=True
530 pick 6058cbb6cfd7 0 one
530 pick 6058cbb6cfd7 0 one
531 roll 4f34d0f8b5fa 2 roll! one
531 roll 4f34d0f8b5fa 2 roll! one
532 pick 579e40513370 1 two
532 pick 579e40513370 1 two
533
533
534 # Edit history between 6058cbb6cfd7 and 4f34d0f8b5fa
534 # Edit history between 6058cbb6cfd7 and 4f34d0f8b5fa
535 #
535 #
536 # Commits are listed from least to most recent
536 # Commits are listed from least to most recent
537 #
537 #
538 # You can reorder changesets by reordering the lines
538 # You can reorder changesets by reordering the lines
539 #
539 #
540 # Commands:
540 # Commands:
541 #
541 #
542 # e, edit = use commit, but stop for amending
542 # e, edit = use commit, but stop for amending
543 # m, mess = edit commit message without changing commit content
543 # m, mess = edit commit message without changing commit content
544 # p, pick = use commit
544 # p, pick = use commit
545 # d, drop = remove commit from history
545 # d, drop = remove commit from history
546 # f, fold = use commit, but combine it with the one above
546 # f, fold = use commit, but combine it with the one above
547 # r, roll = like fold, but discard this commit's description
547 # r, roll = like fold, but discard this commit's description
548 #
548 #
549
549
550 $ cd ..
550 $ cd ..
@@ -1,174 +1,174 b''
1 $ . "$TESTDIR/histedit-helpers.sh"
1 $ . "$TESTDIR/histedit-helpers.sh"
2
2
3 $ cat >> $HGRCPATH <<EOF
3 $ cat >> $HGRCPATH <<EOF
4 > [extensions]
4 > [extensions]
5 > histedit=
5 > histedit=
6 > EOF
6 > EOF
7
7
8 $ initrepo ()
8 $ initrepo ()
9 > {
9 > {
10 > hg init r
10 > hg init r
11 > cd r
11 > cd r
12 > for x in a b c d e f ; do
12 > for x in a b c d e f ; do
13 > echo $x > $x
13 > echo $x > $x
14 > hg add $x
14 > hg add $x
15 > hg ci -m $x
15 > hg ci -m $x
16 > done
16 > done
17 > }
17 > }
18
18
19 $ initrepo
19 $ initrepo
20
20
21 log before edit
21 log before edit
22 $ hg log --graph
22 $ hg log --graph
23 @ changeset: 5:652413bf663e
23 @ changeset: 5:652413bf663e
24 | tag: tip
24 | tag: tip
25 | user: test
25 | user: test
26 | date: Thu Jan 01 00:00:00 1970 +0000
26 | date: Thu Jan 01 00:00:00 1970 +0000
27 | summary: f
27 | summary: f
28 |
28 |
29 o changeset: 4:e860deea161a
29 o changeset: 4:e860deea161a
30 | user: test
30 | user: test
31 | date: Thu Jan 01 00:00:00 1970 +0000
31 | date: Thu Jan 01 00:00:00 1970 +0000
32 | summary: e
32 | summary: e
33 |
33 |
34 o changeset: 3:055a42cdd887
34 o changeset: 3:055a42cdd887
35 | user: test
35 | user: test
36 | date: Thu Jan 01 00:00:00 1970 +0000
36 | date: Thu Jan 01 00:00:00 1970 +0000
37 | summary: d
37 | summary: d
38 |
38 |
39 o changeset: 2:177f92b77385
39 o changeset: 2:177f92b77385
40 | user: test
40 | user: test
41 | date: Thu Jan 01 00:00:00 1970 +0000
41 | date: Thu Jan 01 00:00:00 1970 +0000
42 | summary: c
42 | summary: c
43 |
43 |
44 o changeset: 1:d2ae7f538514
44 o changeset: 1:d2ae7f538514
45 | user: test
45 | user: test
46 | date: Thu Jan 01 00:00:00 1970 +0000
46 | date: Thu Jan 01 00:00:00 1970 +0000
47 | summary: b
47 | summary: b
48 |
48 |
49 o changeset: 0:cb9a9f314b8b
49 o changeset: 0:cb9a9f314b8b
50 user: test
50 user: test
51 date: Thu Jan 01 00:00:00 1970 +0000
51 date: Thu Jan 01 00:00:00 1970 +0000
52 summary: a
52 summary: a
53
53
54
54
55 edit the history
55 edit the history
56 $ hg histedit 177f92b77385 --commands - 2>&1 << EOF | fixbundle
56 $ hg histedit 177f92b77385 --commands - 2>&1 << EOF | fixbundle
57 > drop 177f92b77385 c
57 > drop 177f92b77385 c
58 > pick e860deea161a e
58 > pick e860deea161a e
59 > pick 652413bf663e f
59 > pick 652413bf663e f
60 > pick 055a42cdd887 d
60 > pick 055a42cdd887 d
61 > EOF
61 > EOF
62
62
63 log after edit
63 log after edit
64 $ hg log --graph
64 $ hg log --graph
65 @ changeset: 4:f518305ce889
65 @ changeset: 4:f518305ce889
66 | tag: tip
66 | tag: tip
67 | user: test
67 | user: test
68 | date: Thu Jan 01 00:00:00 1970 +0000
68 | date: Thu Jan 01 00:00:00 1970 +0000
69 | summary: d
69 | summary: d
70 |
70 |
71 o changeset: 3:a4f7421b80f7
71 o changeset: 3:a4f7421b80f7
72 | user: test
72 | user: test
73 | date: Thu Jan 01 00:00:00 1970 +0000
73 | date: Thu Jan 01 00:00:00 1970 +0000
74 | summary: f
74 | summary: f
75 |
75 |
76 o changeset: 2:ee283cb5f2d5
76 o changeset: 2:ee283cb5f2d5
77 | user: test
77 | user: test
78 | date: Thu Jan 01 00:00:00 1970 +0000
78 | date: Thu Jan 01 00:00:00 1970 +0000
79 | summary: e
79 | summary: e
80 |
80 |
81 o changeset: 1:d2ae7f538514
81 o changeset: 1:d2ae7f538514
82 | user: test
82 | user: test
83 | date: Thu Jan 01 00:00:00 1970 +0000
83 | date: Thu Jan 01 00:00:00 1970 +0000
84 | summary: b
84 | summary: b
85 |
85 |
86 o changeset: 0:cb9a9f314b8b
86 o changeset: 0:cb9a9f314b8b
87 user: test
87 user: test
88 date: Thu Jan 01 00:00:00 1970 +0000
88 date: Thu Jan 01 00:00:00 1970 +0000
89 summary: a
89 summary: a
90
90
91
91
92 Check histedit_source
92 Check histedit_source
93
93
94 $ hg log --debug --rev f518305ce889
94 $ hg log --debug --rev f518305ce889
95 changeset: 4:f518305ce889c07cb5bd05522176d75590ef3324
95 changeset: 4:f518305ce889c07cb5bd05522176d75590ef3324
96 tag: tip
96 tag: tip
97 phase: draft
97 phase: draft
98 parent: 3:a4f7421b80f79fcc59fff01bcbf4a53d127dd6d3
98 parent: 3:a4f7421b80f79fcc59fff01bcbf4a53d127dd6d3
99 parent: -1:0000000000000000000000000000000000000000
99 parent: -1:0000000000000000000000000000000000000000
100 manifest: 4:d3d4f51c157ff242c32ff745d4799aaa26ccda44
100 manifest: 4:d3d4f51c157ff242c32ff745d4799aaa26ccda44
101 user: test
101 user: test
102 date: Thu Jan 01 00:00:00 1970 +0000
102 date: Thu Jan 01 00:00:00 1970 +0000
103 files+: d
103 files+: d
104 extra: branch=default
104 extra: branch=default
105 extra: histedit_source=055a42cdd88768532f9cf79daa407fc8d138de9b
105 extra: histedit_source=055a42cdd88768532f9cf79daa407fc8d138de9b
106 description:
106 description:
107 d
107 d
108
108
109
109
110
110
111 manifest after edit
111 manifest after edit
112 $ hg manifest
112 $ hg manifest
113 a
113 a
114 b
114 b
115 d
115 d
116 e
116 e
117 f
117 f
118
118
119 Drop the last changeset
119 Drop the last changeset
120
120
121 $ hg histedit ee283cb5f2d5 --commands - 2>&1 << EOF | fixbundle
121 $ hg histedit ee283cb5f2d5 --commands - 2>&1 << EOF | fixbundle
122 > pick ee283cb5f2d5 e
122 > pick ee283cb5f2d5 e
123 > pick a4f7421b80f7 f
123 > pick a4f7421b80f7 f
124 > drop f518305ce889 d
124 > drop f518305ce889 d
125 > EOF
125 > EOF
126 $ hg log --graph
126 $ hg log --graph
127 @ changeset: 3:a4f7421b80f7
127 @ changeset: 3:a4f7421b80f7
128 | tag: tip
128 | tag: tip
129 | user: test
129 | user: test
130 | date: Thu Jan 01 00:00:00 1970 +0000
130 | date: Thu Jan 01 00:00:00 1970 +0000
131 | summary: f
131 | summary: f
132 |
132 |
133 o changeset: 2:ee283cb5f2d5
133 o changeset: 2:ee283cb5f2d5
134 | user: test
134 | user: test
135 | date: Thu Jan 01 00:00:00 1970 +0000
135 | date: Thu Jan 01 00:00:00 1970 +0000
136 | summary: e
136 | summary: e
137 |
137 |
138 o changeset: 1:d2ae7f538514
138 o changeset: 1:d2ae7f538514
139 | user: test
139 | user: test
140 | date: Thu Jan 01 00:00:00 1970 +0000
140 | date: Thu Jan 01 00:00:00 1970 +0000
141 | summary: b
141 | summary: b
142 |
142 |
143 o changeset: 0:cb9a9f314b8b
143 o changeset: 0:cb9a9f314b8b
144 user: test
144 user: test
145 date: Thu Jan 01 00:00:00 1970 +0000
145 date: Thu Jan 01 00:00:00 1970 +0000
146 summary: a
146 summary: a
147
147
148
148
149 $ hg histedit cb9a9f314b8b --commands - 2>&1 << EOF | fixbundle
149 $ hg histedit cb9a9f314b8b --commands - 2>&1 << EOF | fixbundle
150 > pick cb9a9f314b8b a
150 > pick cb9a9f314b8b a
151 > pick ee283cb5f2d5 e
151 > pick ee283cb5f2d5 e
152 > EOF
152 > EOF
153 hg: parse error: missing rules for changeset a4f7421b80f7
153 hg: parse error: missing rules for changeset a4f7421b80f7
154 (use "drop a4f7421b80f7" to discard, see also: "hg help -e histedit.config")
154 (use "drop a4f7421b80f7" to discard, see also: 'hg help -e histedit.config')
155 $ hg --config histedit.dropmissing=True histedit cb9a9f314b8b --commands - 2>&1 << EOF | fixbundle
155 $ hg --config histedit.dropmissing=True histedit cb9a9f314b8b --commands - 2>&1 << EOF | fixbundle
156 > EOF
156 > EOF
157 hg: parse error: no rules provided
157 hg: parse error: no rules provided
158 (use strip extension to remove commits)
158 (use strip extension to remove commits)
159 $ hg --config histedit.dropmissing=True histedit cb9a9f314b8b --commands - 2>&1 << EOF | fixbundle
159 $ hg --config histedit.dropmissing=True histedit cb9a9f314b8b --commands - 2>&1 << EOF | fixbundle
160 > pick cb9a9f314b8b a
160 > pick cb9a9f314b8b a
161 > pick ee283cb5f2d5 e
161 > pick ee283cb5f2d5 e
162 > EOF
162 > EOF
163 $ hg log --graph
163 $ hg log --graph
164 @ changeset: 1:e99c679bf03e
164 @ changeset: 1:e99c679bf03e
165 | tag: tip
165 | tag: tip
166 | user: test
166 | user: test
167 | date: Thu Jan 01 00:00:00 1970 +0000
167 | date: Thu Jan 01 00:00:00 1970 +0000
168 | summary: e
168 | summary: e
169 |
169 |
170 o changeset: 0:cb9a9f314b8b
170 o changeset: 0:cb9a9f314b8b
171 user: test
171 user: test
172 date: Thu Jan 01 00:00:00 1970 +0000
172 date: Thu Jan 01 00:00:00 1970 +0000
173 summary: a
173 summary: a
174
174
@@ -1,505 +1,505 b''
1 $ . "$TESTDIR/histedit-helpers.sh"
1 $ . "$TESTDIR/histedit-helpers.sh"
2
2
3 Enable obsolete
3 Enable obsolete
4
4
5 $ cat >> $HGRCPATH << EOF
5 $ cat >> $HGRCPATH << EOF
6 > [ui]
6 > [ui]
7 > logtemplate= {rev}:{node|short} {desc|firstline}
7 > logtemplate= {rev}:{node|short} {desc|firstline}
8 > [phases]
8 > [phases]
9 > publish=False
9 > publish=False
10 > [experimental]
10 > [experimental]
11 > evolution=createmarkers,allowunstable
11 > evolution=createmarkers,allowunstable
12 > [extensions]
12 > [extensions]
13 > histedit=
13 > histedit=
14 > rebase=
14 > rebase=
15 > EOF
15 > EOF
16
16
17 Test that histedit learns about obsolescence not stored in histedit state
17 Test that histedit learns about obsolescence not stored in histedit state
18 $ hg init boo
18 $ hg init boo
19 $ cd boo
19 $ cd boo
20 $ echo a > a
20 $ echo a > a
21 $ hg ci -Am a
21 $ hg ci -Am a
22 adding a
22 adding a
23 $ echo a > b
23 $ echo a > b
24 $ echo a > c
24 $ echo a > c
25 $ echo a > c
25 $ echo a > c
26 $ hg ci -Am b
26 $ hg ci -Am b
27 adding b
27 adding b
28 adding c
28 adding c
29 $ echo a > d
29 $ echo a > d
30 $ hg ci -Am c
30 $ hg ci -Am c
31 adding d
31 adding d
32 $ echo "pick `hg log -r 0 -T '{node|short}'`" > plan
32 $ echo "pick `hg log -r 0 -T '{node|short}'`" > plan
33 $ echo "pick `hg log -r 2 -T '{node|short}'`" >> plan
33 $ echo "pick `hg log -r 2 -T '{node|short}'`" >> plan
34 $ echo "edit `hg log -r 1 -T '{node|short}'`" >> plan
34 $ echo "edit `hg log -r 1 -T '{node|short}'`" >> plan
35 $ hg histedit -r 'all()' --commands plan
35 $ hg histedit -r 'all()' --commands plan
36 Editing (1b2d564fad96), you may commit or record as needed now.
36 Editing (1b2d564fad96), you may commit or record as needed now.
37 (hg histedit --continue to resume)
37 (hg histedit --continue to resume)
38 [1]
38 [1]
39 $ hg st
39 $ hg st
40 A b
40 A b
41 A c
41 A c
42 ? plan
42 ? plan
43 $ hg commit --amend b
43 $ hg commit --amend b
44 $ hg histedit --continue
44 $ hg histedit --continue
45 $ hg log -G
45 $ hg log -G
46 @ 6:46abc7c4d873 b
46 @ 6:46abc7c4d873 b
47 |
47 |
48 o 5:49d44ab2be1b c
48 o 5:49d44ab2be1b c
49 |
49 |
50 o 0:cb9a9f314b8b a
50 o 0:cb9a9f314b8b a
51
51
52 $ hg debugobsolete
52 $ hg debugobsolete
53 e72d22b19f8ecf4150ab4f91d0973fd9955d3ddf 49d44ab2be1b67a79127568a67c9c99430633b48 0 (*) {'user': 'test'} (glob)
53 e72d22b19f8ecf4150ab4f91d0973fd9955d3ddf 49d44ab2be1b67a79127568a67c9c99430633b48 0 (*) {'user': 'test'} (glob)
54 3e30a45cf2f719e96ab3922dfe039cfd047956ce 0 {e72d22b19f8ecf4150ab4f91d0973fd9955d3ddf} (*) {'user': 'test'} (glob)
54 3e30a45cf2f719e96ab3922dfe039cfd047956ce 0 {e72d22b19f8ecf4150ab4f91d0973fd9955d3ddf} (*) {'user': 'test'} (glob)
55 1b2d564fad96311b45362f17c2aa855150efb35f 46abc7c4d8738e8563e577f7889e1b6db3da4199 0 (*) {'user': 'test'} (glob)
55 1b2d564fad96311b45362f17c2aa855150efb35f 46abc7c4d8738e8563e577f7889e1b6db3da4199 0 (*) {'user': 'test'} (glob)
56 114f4176969ef342759a8a57e6bccefc4234829b 49d44ab2be1b67a79127568a67c9c99430633b48 0 (*) {'user': 'test'} (glob)
56 114f4176969ef342759a8a57e6bccefc4234829b 49d44ab2be1b67a79127568a67c9c99430633b48 0 (*) {'user': 'test'} (glob)
57
57
58 With some node gone missing during the edit.
58 With some node gone missing during the edit.
59
59
60 $ echo "pick `hg log -r 0 -T '{node|short}'`" > plan
60 $ echo "pick `hg log -r 0 -T '{node|short}'`" > plan
61 $ echo "pick `hg log -r 6 -T '{node|short}'`" >> plan
61 $ echo "pick `hg log -r 6 -T '{node|short}'`" >> plan
62 $ echo "edit `hg log -r 5 -T '{node|short}'`" >> plan
62 $ echo "edit `hg log -r 5 -T '{node|short}'`" >> plan
63 $ hg histedit -r 'all()' --commands plan
63 $ hg histedit -r 'all()' --commands plan
64 Editing (49d44ab2be1b), you may commit or record as needed now.
64 Editing (49d44ab2be1b), you may commit or record as needed now.
65 (hg histedit --continue to resume)
65 (hg histedit --continue to resume)
66 [1]
66 [1]
67 $ hg st
67 $ hg st
68 A b
68 A b
69 A d
69 A d
70 ? plan
70 ? plan
71 $ hg commit --amend -X . -m XXXXXX
71 $ hg commit --amend -X . -m XXXXXX
72 $ hg commit --amend -X . -m b2
72 $ hg commit --amend -X . -m b2
73 $ hg --hidden --config extensions.strip= strip 'desc(XXXXXX)' --no-backup
73 $ hg --hidden --config extensions.strip= strip 'desc(XXXXXX)' --no-backup
74 $ hg histedit --continue
74 $ hg histedit --continue
75 $ hg log -G
75 $ hg log -G
76 @ 9:273c1f3b8626 c
76 @ 9:273c1f3b8626 c
77 |
77 |
78 o 8:aba7da937030 b2
78 o 8:aba7da937030 b2
79 |
79 |
80 o 0:cb9a9f314b8b a
80 o 0:cb9a9f314b8b a
81
81
82 $ hg debugobsolete
82 $ hg debugobsolete
83 e72d22b19f8ecf4150ab4f91d0973fd9955d3ddf 49d44ab2be1b67a79127568a67c9c99430633b48 0 (*) {'user': 'test'} (glob)
83 e72d22b19f8ecf4150ab4f91d0973fd9955d3ddf 49d44ab2be1b67a79127568a67c9c99430633b48 0 (*) {'user': 'test'} (glob)
84 3e30a45cf2f719e96ab3922dfe039cfd047956ce 0 {e72d22b19f8ecf4150ab4f91d0973fd9955d3ddf} (*) {'user': 'test'} (glob)
84 3e30a45cf2f719e96ab3922dfe039cfd047956ce 0 {e72d22b19f8ecf4150ab4f91d0973fd9955d3ddf} (*) {'user': 'test'} (glob)
85 1b2d564fad96311b45362f17c2aa855150efb35f 46abc7c4d8738e8563e577f7889e1b6db3da4199 0 (*) {'user': 'test'} (glob)
85 1b2d564fad96311b45362f17c2aa855150efb35f 46abc7c4d8738e8563e577f7889e1b6db3da4199 0 (*) {'user': 'test'} (glob)
86 114f4176969ef342759a8a57e6bccefc4234829b 49d44ab2be1b67a79127568a67c9c99430633b48 0 (*) {'user': 'test'} (glob)
86 114f4176969ef342759a8a57e6bccefc4234829b 49d44ab2be1b67a79127568a67c9c99430633b48 0 (*) {'user': 'test'} (glob)
87 76f72745eac0643d16530e56e2f86e36e40631f1 2ca853e48edbd6453a0674dc0fe28a0974c51b9c 0 (*) {'user': 'test'} (glob)
87 76f72745eac0643d16530e56e2f86e36e40631f1 2ca853e48edbd6453a0674dc0fe28a0974c51b9c 0 (*) {'user': 'test'} (glob)
88 2ca853e48edbd6453a0674dc0fe28a0974c51b9c aba7da93703075eec9fb1dbaf143ff2bc1c49d46 0 (*) {'user': 'test'} (glob)
88 2ca853e48edbd6453a0674dc0fe28a0974c51b9c aba7da93703075eec9fb1dbaf143ff2bc1c49d46 0 (*) {'user': 'test'} (glob)
89 49d44ab2be1b67a79127568a67c9c99430633b48 273c1f3b86267ed3ec684bb13af1fa4d6ba56e02 0 (*) {'user': 'test'} (glob)
89 49d44ab2be1b67a79127568a67c9c99430633b48 273c1f3b86267ed3ec684bb13af1fa4d6ba56e02 0 (*) {'user': 'test'} (glob)
90 46abc7c4d8738e8563e577f7889e1b6db3da4199 aba7da93703075eec9fb1dbaf143ff2bc1c49d46 0 (*) {'user': 'test'} (glob)
90 46abc7c4d8738e8563e577f7889e1b6db3da4199 aba7da93703075eec9fb1dbaf143ff2bc1c49d46 0 (*) {'user': 'test'} (glob)
91 $ cd ..
91 $ cd ..
92
92
93 Base setup for the rest of the testing
93 Base setup for the rest of the testing
94 ======================================
94 ======================================
95
95
96 $ hg init base
96 $ hg init base
97 $ cd base
97 $ cd base
98
98
99 $ for x in a b c d e f ; do
99 $ for x in a b c d e f ; do
100 > echo $x > $x
100 > echo $x > $x
101 > hg add $x
101 > hg add $x
102 > hg ci -m $x
102 > hg ci -m $x
103 > done
103 > done
104
104
105 $ hg log --graph
105 $ hg log --graph
106 @ 5:652413bf663e f
106 @ 5:652413bf663e f
107 |
107 |
108 o 4:e860deea161a e
108 o 4:e860deea161a e
109 |
109 |
110 o 3:055a42cdd887 d
110 o 3:055a42cdd887 d
111 |
111 |
112 o 2:177f92b77385 c
112 o 2:177f92b77385 c
113 |
113 |
114 o 1:d2ae7f538514 b
114 o 1:d2ae7f538514 b
115 |
115 |
116 o 0:cb9a9f314b8b a
116 o 0:cb9a9f314b8b a
117
117
118
118
119 $ HGEDITOR=cat hg histedit 1
119 $ HGEDITOR=cat hg histedit 1
120 pick d2ae7f538514 1 b
120 pick d2ae7f538514 1 b
121 pick 177f92b77385 2 c
121 pick 177f92b77385 2 c
122 pick 055a42cdd887 3 d
122 pick 055a42cdd887 3 d
123 pick e860deea161a 4 e
123 pick e860deea161a 4 e
124 pick 652413bf663e 5 f
124 pick 652413bf663e 5 f
125
125
126 # Edit history between d2ae7f538514 and 652413bf663e
126 # Edit history between d2ae7f538514 and 652413bf663e
127 #
127 #
128 # Commits are listed from least to most recent
128 # Commits are listed from least to most recent
129 #
129 #
130 # You can reorder changesets by reordering the lines
130 # You can reorder changesets by reordering the lines
131 #
131 #
132 # Commands:
132 # Commands:
133 #
133 #
134 # e, edit = use commit, but stop for amending
134 # e, edit = use commit, but stop for amending
135 # m, mess = edit commit message without changing commit content
135 # m, mess = edit commit message without changing commit content
136 # p, pick = use commit
136 # p, pick = use commit
137 # d, drop = remove commit from history
137 # d, drop = remove commit from history
138 # f, fold = use commit, but combine it with the one above
138 # f, fold = use commit, but combine it with the one above
139 # r, roll = like fold, but discard this commit's description
139 # r, roll = like fold, but discard this commit's description
140 #
140 #
141 $ hg histedit 1 --commands - --verbose <<EOF | grep histedit
141 $ hg histedit 1 --commands - --verbose <<EOF | grep histedit
142 > pick 177f92b77385 2 c
142 > pick 177f92b77385 2 c
143 > drop d2ae7f538514 1 b
143 > drop d2ae7f538514 1 b
144 > pick 055a42cdd887 3 d
144 > pick 055a42cdd887 3 d
145 > fold e860deea161a 4 e
145 > fold e860deea161a 4 e
146 > pick 652413bf663e 5 f
146 > pick 652413bf663e 5 f
147 > EOF
147 > EOF
148 [1]
148 [1]
149 $ hg log --graph --hidden
149 $ hg log --graph --hidden
150 @ 10:cacdfd884a93 f
150 @ 10:cacdfd884a93 f
151 |
151 |
152 o 9:59d9f330561f d
152 o 9:59d9f330561f d
153 |
153 |
154 | x 8:b558abc46d09 fold-temp-revision e860deea161a
154 | x 8:b558abc46d09 fold-temp-revision e860deea161a
155 | |
155 | |
156 | x 7:96e494a2d553 d
156 | x 7:96e494a2d553 d
157 |/
157 |/
158 o 6:b346ab9a313d c
158 o 6:b346ab9a313d c
159 |
159 |
160 | x 5:652413bf663e f
160 | x 5:652413bf663e f
161 | |
161 | |
162 | x 4:e860deea161a e
162 | x 4:e860deea161a e
163 | |
163 | |
164 | x 3:055a42cdd887 d
164 | x 3:055a42cdd887 d
165 | |
165 | |
166 | x 2:177f92b77385 c
166 | x 2:177f92b77385 c
167 | |
167 | |
168 | x 1:d2ae7f538514 b
168 | x 1:d2ae7f538514 b
169 |/
169 |/
170 o 0:cb9a9f314b8b a
170 o 0:cb9a9f314b8b a
171
171
172 $ hg debugobsolete
172 $ hg debugobsolete
173 96e494a2d553dd05902ba1cee1d94d4cb7b8faed 0 {b346ab9a313db8537ecf96fca3ca3ca984ef3bd7} (*) {'user': 'test'} (glob)
173 96e494a2d553dd05902ba1cee1d94d4cb7b8faed 0 {b346ab9a313db8537ecf96fca3ca3ca984ef3bd7} (*) {'user': 'test'} (glob)
174 b558abc46d09c30f57ac31e85a8a3d64d2e906e4 0 {96e494a2d553dd05902ba1cee1d94d4cb7b8faed} (*) {'user': 'test'} (glob)
174 b558abc46d09c30f57ac31e85a8a3d64d2e906e4 0 {96e494a2d553dd05902ba1cee1d94d4cb7b8faed} (*) {'user': 'test'} (glob)
175 d2ae7f538514cd87c17547b0de4cea71fe1af9fb 0 {cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b} (*) {'user': 'test'} (glob)
175 d2ae7f538514cd87c17547b0de4cea71fe1af9fb 0 {cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b} (*) {'user': 'test'} (glob)
176 177f92b773850b59254aa5e923436f921b55483b b346ab9a313db8537ecf96fca3ca3ca984ef3bd7 0 (*) {'user': 'test'} (glob)
176 177f92b773850b59254aa5e923436f921b55483b b346ab9a313db8537ecf96fca3ca3ca984ef3bd7 0 (*) {'user': 'test'} (glob)
177 055a42cdd88768532f9cf79daa407fc8d138de9b 59d9f330561fd6c88b1a6b32f0e45034d88db784 0 (*) {'user': 'test'} (glob)
177 055a42cdd88768532f9cf79daa407fc8d138de9b 59d9f330561fd6c88b1a6b32f0e45034d88db784 0 (*) {'user': 'test'} (glob)
178 e860deea161a2f77de56603b340ebbb4536308ae 59d9f330561fd6c88b1a6b32f0e45034d88db784 0 (*) {'user': 'test'} (glob)
178 e860deea161a2f77de56603b340ebbb4536308ae 59d9f330561fd6c88b1a6b32f0e45034d88db784 0 (*) {'user': 'test'} (glob)
179 652413bf663ef2a641cab26574e46d5f5a64a55a cacdfd884a9321ec4e1de275ef3949fa953a1f83 0 (*) {'user': 'test'} (glob)
179 652413bf663ef2a641cab26574e46d5f5a64a55a cacdfd884a9321ec4e1de275ef3949fa953a1f83 0 (*) {'user': 'test'} (glob)
180
180
181
181
182 Ensure hidden revision does not prevent histedit
182 Ensure hidden revision does not prevent histedit
183 -------------------------------------------------
183 -------------------------------------------------
184
184
185 create an hidden revision
185 create an hidden revision
186
186
187 $ hg histedit 6 --commands - << EOF
187 $ hg histedit 6 --commands - << EOF
188 > pick b346ab9a313d 6 c
188 > pick b346ab9a313d 6 c
189 > drop 59d9f330561f 7 d
189 > drop 59d9f330561f 7 d
190 > pick cacdfd884a93 8 f
190 > pick cacdfd884a93 8 f
191 > EOF
191 > EOF
192 $ hg log --graph
192 $ hg log --graph
193 @ 11:c13eb81022ca f
193 @ 11:c13eb81022ca f
194 |
194 |
195 o 6:b346ab9a313d c
195 o 6:b346ab9a313d c
196 |
196 |
197 o 0:cb9a9f314b8b a
197 o 0:cb9a9f314b8b a
198
198
199 check hidden revision are ignored (6 have hidden children 7 and 8)
199 check hidden revision are ignored (6 have hidden children 7 and 8)
200
200
201 $ hg histedit 6 --commands - << EOF
201 $ hg histedit 6 --commands - << EOF
202 > pick b346ab9a313d 6 c
202 > pick b346ab9a313d 6 c
203 > pick c13eb81022ca 8 f
203 > pick c13eb81022ca 8 f
204 > EOF
204 > EOF
205
205
206
206
207
207
208 Test that rewriting leaving instability behind is allowed
208 Test that rewriting leaving instability behind is allowed
209 ---------------------------------------------------------------------
209 ---------------------------------------------------------------------
210
210
211 $ hg up '.^'
211 $ hg up '.^'
212 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
212 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
213 $ hg log -r 'children(.)'
213 $ hg log -r 'children(.)'
214 11:c13eb81022ca f (no-eol)
214 11:c13eb81022ca f (no-eol)
215 $ hg histedit -r '.' --commands - <<EOF
215 $ hg histedit -r '.' --commands - <<EOF
216 > edit b346ab9a313d 6 c
216 > edit b346ab9a313d 6 c
217 > EOF
217 > EOF
218 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
218 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
219 adding c
219 adding c
220 Editing (b346ab9a313d), you may commit or record as needed now.
220 Editing (b346ab9a313d), you may commit or record as needed now.
221 (hg histedit --continue to resume)
221 (hg histedit --continue to resume)
222 [1]
222 [1]
223 $ echo c >> c
223 $ echo c >> c
224 $ hg histedit --continue
224 $ hg histedit --continue
225
225
226 $ hg log -r 'unstable()'
226 $ hg log -r 'unstable()'
227 11:c13eb81022ca f (no-eol)
227 11:c13eb81022ca f (no-eol)
228
228
229 stabilise
229 stabilise
230
230
231 $ hg rebase -r 'unstable()' -d .
231 $ hg rebase -r 'unstable()' -d .
232 rebasing 11:c13eb81022ca "f"
232 rebasing 11:c13eb81022ca "f"
233 $ hg up tip -q
233 $ hg up tip -q
234
234
235 Test dropping of changeset on the top of the stack
235 Test dropping of changeset on the top of the stack
236 -------------------------------------------------------
236 -------------------------------------------------------
237
237
238 Nothing is rewritten below, the working directory parent must be change for the
238 Nothing is rewritten below, the working directory parent must be change for the
239 dropped changeset to be hidden.
239 dropped changeset to be hidden.
240
240
241 $ cd ..
241 $ cd ..
242 $ hg clone base droplast
242 $ hg clone base droplast
243 updating to branch default
243 updating to branch default
244 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
244 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
245 $ cd droplast
245 $ cd droplast
246 $ hg histedit -r '40db8afa467b' --commands - << EOF
246 $ hg histedit -r '40db8afa467b' --commands - << EOF
247 > pick 40db8afa467b 10 c
247 > pick 40db8afa467b 10 c
248 > drop b449568bf7fc 11 f
248 > drop b449568bf7fc 11 f
249 > EOF
249 > EOF
250 $ hg log -G
250 $ hg log -G
251 @ 12:40db8afa467b c
251 @ 12:40db8afa467b c
252 |
252 |
253 o 0:cb9a9f314b8b a
253 o 0:cb9a9f314b8b a
254
254
255
255
256 With rewritten ancestors
256 With rewritten ancestors
257
257
258 $ echo e > e
258 $ echo e > e
259 $ hg add e
259 $ hg add e
260 $ hg commit -m g
260 $ hg commit -m g
261 $ echo f > f
261 $ echo f > f
262 $ hg add f
262 $ hg add f
263 $ hg commit -m h
263 $ hg commit -m h
264 $ hg histedit -r '40db8afa467b' --commands - << EOF
264 $ hg histedit -r '40db8afa467b' --commands - << EOF
265 > pick 47a8561c0449 12 g
265 > pick 47a8561c0449 12 g
266 > pick 40db8afa467b 10 c
266 > pick 40db8afa467b 10 c
267 > drop 1b3b05f35ff0 13 h
267 > drop 1b3b05f35ff0 13 h
268 > EOF
268 > EOF
269 $ hg log -G
269 $ hg log -G
270 @ 17:ee6544123ab8 c
270 @ 17:ee6544123ab8 c
271 |
271 |
272 o 16:269e713e9eae g
272 o 16:269e713e9eae g
273 |
273 |
274 o 0:cb9a9f314b8b a
274 o 0:cb9a9f314b8b a
275
275
276 $ cd ../base
276 $ cd ../base
277
277
278
278
279
279
280 Test phases support
280 Test phases support
281 ===========================================
281 ===========================================
282
282
283 Check that histedit respect immutability
283 Check that histedit respect immutability
284 -------------------------------------------
284 -------------------------------------------
285
285
286 $ cat >> $HGRCPATH << EOF
286 $ cat >> $HGRCPATH << EOF
287 > [ui]
287 > [ui]
288 > logtemplate= {rev}:{node|short} ({phase}) {desc|firstline}\n
288 > logtemplate= {rev}:{node|short} ({phase}) {desc|firstline}\n
289 > EOF
289 > EOF
290
290
291 $ hg ph -pv '.^'
291 $ hg ph -pv '.^'
292 phase changed for 2 changesets
292 phase changed for 2 changesets
293 $ hg log -G
293 $ hg log -G
294 @ 13:b449568bf7fc (draft) f
294 @ 13:b449568bf7fc (draft) f
295 |
295 |
296 o 12:40db8afa467b (public) c
296 o 12:40db8afa467b (public) c
297 |
297 |
298 o 0:cb9a9f314b8b (public) a
298 o 0:cb9a9f314b8b (public) a
299
299
300 $ hg histedit -r '.~2'
300 $ hg histedit -r '.~2'
301 abort: cannot edit public changeset: cb9a9f314b8b
301 abort: cannot edit public changeset: cb9a9f314b8b
302 (see "hg help phases" for details)
302 (see 'hg help phases' for details)
303 [255]
303 [255]
304
304
305
305
306 Prepare further testing
306 Prepare further testing
307 -------------------------------------------
307 -------------------------------------------
308
308
309 $ for x in g h i j k ; do
309 $ for x in g h i j k ; do
310 > echo $x > $x
310 > echo $x > $x
311 > hg add $x
311 > hg add $x
312 > hg ci -m $x
312 > hg ci -m $x
313 > done
313 > done
314 $ hg phase --force --secret .~2
314 $ hg phase --force --secret .~2
315 $ hg log -G
315 $ hg log -G
316 @ 18:ee118ab9fa44 (secret) k
316 @ 18:ee118ab9fa44 (secret) k
317 |
317 |
318 o 17:3a6c53ee7f3d (secret) j
318 o 17:3a6c53ee7f3d (secret) j
319 |
319 |
320 o 16:b605fb7503f2 (secret) i
320 o 16:b605fb7503f2 (secret) i
321 |
321 |
322 o 15:7395e1ff83bd (draft) h
322 o 15:7395e1ff83bd (draft) h
323 |
323 |
324 o 14:6b70183d2492 (draft) g
324 o 14:6b70183d2492 (draft) g
325 |
325 |
326 o 13:b449568bf7fc (draft) f
326 o 13:b449568bf7fc (draft) f
327 |
327 |
328 o 12:40db8afa467b (public) c
328 o 12:40db8afa467b (public) c
329 |
329 |
330 o 0:cb9a9f314b8b (public) a
330 o 0:cb9a9f314b8b (public) a
331
331
332 $ cd ..
332 $ cd ..
333
333
334 simple phase conservation
334 simple phase conservation
335 -------------------------------------------
335 -------------------------------------------
336
336
337 Resulting changeset should conserve the phase of the original one whatever the
337 Resulting changeset should conserve the phase of the original one whatever the
338 phases.new-commit option is.
338 phases.new-commit option is.
339
339
340 New-commit as draft (default)
340 New-commit as draft (default)
341
341
342 $ cp -r base simple-draft
342 $ cp -r base simple-draft
343 $ cd simple-draft
343 $ cd simple-draft
344 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
344 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
345 > edit b449568bf7fc 11 f
345 > edit b449568bf7fc 11 f
346 > pick 6b70183d2492 12 g
346 > pick 6b70183d2492 12 g
347 > pick 7395e1ff83bd 13 h
347 > pick 7395e1ff83bd 13 h
348 > pick b605fb7503f2 14 i
348 > pick b605fb7503f2 14 i
349 > pick 3a6c53ee7f3d 15 j
349 > pick 3a6c53ee7f3d 15 j
350 > pick ee118ab9fa44 16 k
350 > pick ee118ab9fa44 16 k
351 > EOF
351 > EOF
352 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
352 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
353 adding f
353 adding f
354 Editing (b449568bf7fc), you may commit or record as needed now.
354 Editing (b449568bf7fc), you may commit or record as needed now.
355 (hg histedit --continue to resume)
355 (hg histedit --continue to resume)
356 [1]
356 [1]
357 $ echo f >> f
357 $ echo f >> f
358 $ hg histedit --continue
358 $ hg histedit --continue
359 $ hg log -G
359 $ hg log -G
360 @ 24:12e89af74238 (secret) k
360 @ 24:12e89af74238 (secret) k
361 |
361 |
362 o 23:636a8687b22e (secret) j
362 o 23:636a8687b22e (secret) j
363 |
363 |
364 o 22:ccaf0a38653f (secret) i
364 o 22:ccaf0a38653f (secret) i
365 |
365 |
366 o 21:11a89d1c2613 (draft) h
366 o 21:11a89d1c2613 (draft) h
367 |
367 |
368 o 20:c1dec7ca82ea (draft) g
368 o 20:c1dec7ca82ea (draft) g
369 |
369 |
370 o 19:087281e68428 (draft) f
370 o 19:087281e68428 (draft) f
371 |
371 |
372 o 12:40db8afa467b (public) c
372 o 12:40db8afa467b (public) c
373 |
373 |
374 o 0:cb9a9f314b8b (public) a
374 o 0:cb9a9f314b8b (public) a
375
375
376 $ cd ..
376 $ cd ..
377
377
378
378
379 New-commit as secret (config)
379 New-commit as secret (config)
380
380
381 $ cp -r base simple-secret
381 $ cp -r base simple-secret
382 $ cd simple-secret
382 $ cd simple-secret
383 $ cat >> .hg/hgrc << EOF
383 $ cat >> .hg/hgrc << EOF
384 > [phases]
384 > [phases]
385 > new-commit=secret
385 > new-commit=secret
386 > EOF
386 > EOF
387 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
387 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
388 > edit b449568bf7fc 11 f
388 > edit b449568bf7fc 11 f
389 > pick 6b70183d2492 12 g
389 > pick 6b70183d2492 12 g
390 > pick 7395e1ff83bd 13 h
390 > pick 7395e1ff83bd 13 h
391 > pick b605fb7503f2 14 i
391 > pick b605fb7503f2 14 i
392 > pick 3a6c53ee7f3d 15 j
392 > pick 3a6c53ee7f3d 15 j
393 > pick ee118ab9fa44 16 k
393 > pick ee118ab9fa44 16 k
394 > EOF
394 > EOF
395 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
395 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
396 adding f
396 adding f
397 Editing (b449568bf7fc), you may commit or record as needed now.
397 Editing (b449568bf7fc), you may commit or record as needed now.
398 (hg histedit --continue to resume)
398 (hg histedit --continue to resume)
399 [1]
399 [1]
400 $ echo f >> f
400 $ echo f >> f
401 $ hg histedit --continue
401 $ hg histedit --continue
402 $ hg log -G
402 $ hg log -G
403 @ 24:12e89af74238 (secret) k
403 @ 24:12e89af74238 (secret) k
404 |
404 |
405 o 23:636a8687b22e (secret) j
405 o 23:636a8687b22e (secret) j
406 |
406 |
407 o 22:ccaf0a38653f (secret) i
407 o 22:ccaf0a38653f (secret) i
408 |
408 |
409 o 21:11a89d1c2613 (draft) h
409 o 21:11a89d1c2613 (draft) h
410 |
410 |
411 o 20:c1dec7ca82ea (draft) g
411 o 20:c1dec7ca82ea (draft) g
412 |
412 |
413 o 19:087281e68428 (draft) f
413 o 19:087281e68428 (draft) f
414 |
414 |
415 o 12:40db8afa467b (public) c
415 o 12:40db8afa467b (public) c
416 |
416 |
417 o 0:cb9a9f314b8b (public) a
417 o 0:cb9a9f314b8b (public) a
418
418
419 $ cd ..
419 $ cd ..
420
420
421
421
422 Changeset reordering
422 Changeset reordering
423 -------------------------------------------
423 -------------------------------------------
424
424
425 If a secret changeset is put before a draft one, all descendant should be secret.
425 If a secret changeset is put before a draft one, all descendant should be secret.
426 It seems more important to present the secret phase.
426 It seems more important to present the secret phase.
427
427
428 $ cp -r base reorder
428 $ cp -r base reorder
429 $ cd reorder
429 $ cd reorder
430 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
430 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
431 > pick b449568bf7fc 11 f
431 > pick b449568bf7fc 11 f
432 > pick 3a6c53ee7f3d 15 j
432 > pick 3a6c53ee7f3d 15 j
433 > pick 6b70183d2492 12 g
433 > pick 6b70183d2492 12 g
434 > pick b605fb7503f2 14 i
434 > pick b605fb7503f2 14 i
435 > pick 7395e1ff83bd 13 h
435 > pick 7395e1ff83bd 13 h
436 > pick ee118ab9fa44 16 k
436 > pick ee118ab9fa44 16 k
437 > EOF
437 > EOF
438 $ hg log -G
438 $ hg log -G
439 @ 23:558246857888 (secret) k
439 @ 23:558246857888 (secret) k
440 |
440 |
441 o 22:28bd44768535 (secret) h
441 o 22:28bd44768535 (secret) h
442 |
442 |
443 o 21:d5395202aeb9 (secret) i
443 o 21:d5395202aeb9 (secret) i
444 |
444 |
445 o 20:21edda8e341b (secret) g
445 o 20:21edda8e341b (secret) g
446 |
446 |
447 o 19:5ab64f3a4832 (secret) j
447 o 19:5ab64f3a4832 (secret) j
448 |
448 |
449 o 13:b449568bf7fc (draft) f
449 o 13:b449568bf7fc (draft) f
450 |
450 |
451 o 12:40db8afa467b (public) c
451 o 12:40db8afa467b (public) c
452 |
452 |
453 o 0:cb9a9f314b8b (public) a
453 o 0:cb9a9f314b8b (public) a
454
454
455 $ cd ..
455 $ cd ..
456
456
457 Changeset folding
457 Changeset folding
458 -------------------------------------------
458 -------------------------------------------
459
459
460 Folding a secret changeset with a draft one turn the result secret (again,
460 Folding a secret changeset with a draft one turn the result secret (again,
461 better safe than sorry). Folding between same phase changeset still works
461 better safe than sorry). Folding between same phase changeset still works
462
462
463 Note that there is a few reordering in this series for more extensive test
463 Note that there is a few reordering in this series for more extensive test
464
464
465 $ cp -r base folding
465 $ cp -r base folding
466 $ cd folding
466 $ cd folding
467 $ cat >> .hg/hgrc << EOF
467 $ cat >> .hg/hgrc << EOF
468 > [phases]
468 > [phases]
469 > new-commit=secret
469 > new-commit=secret
470 > EOF
470 > EOF
471 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
471 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
472 > pick 7395e1ff83bd 13 h
472 > pick 7395e1ff83bd 13 h
473 > fold b449568bf7fc 11 f
473 > fold b449568bf7fc 11 f
474 > pick 6b70183d2492 12 g
474 > pick 6b70183d2492 12 g
475 > fold 3a6c53ee7f3d 15 j
475 > fold 3a6c53ee7f3d 15 j
476 > pick b605fb7503f2 14 i
476 > pick b605fb7503f2 14 i
477 > fold ee118ab9fa44 16 k
477 > fold ee118ab9fa44 16 k
478 > EOF
478 > EOF
479 $ hg log -G
479 $ hg log -G
480 @ 27:f9daec13fb98 (secret) i
480 @ 27:f9daec13fb98 (secret) i
481 |
481 |
482 o 24:49807617f46a (secret) g
482 o 24:49807617f46a (secret) g
483 |
483 |
484 o 21:050280826e04 (draft) h
484 o 21:050280826e04 (draft) h
485 |
485 |
486 o 12:40db8afa467b (public) c
486 o 12:40db8afa467b (public) c
487 |
487 |
488 o 0:cb9a9f314b8b (public) a
488 o 0:cb9a9f314b8b (public) a
489
489
490 $ hg co 49807617f46a
490 $ hg co 49807617f46a
491 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
491 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
492 $ echo wat >> wat
492 $ echo wat >> wat
493 $ hg add wat
493 $ hg add wat
494 $ hg ci -m 'add wat'
494 $ hg ci -m 'add wat'
495 created new head
495 created new head
496 $ hg merge f9daec13fb98
496 $ hg merge f9daec13fb98
497 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
497 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
498 (branch merge, don't forget to commit)
498 (branch merge, don't forget to commit)
499 $ hg ci -m 'merge'
499 $ hg ci -m 'merge'
500 $ echo not wat > wat
500 $ echo not wat > wat
501 $ hg ci -m 'modify wat'
501 $ hg ci -m 'modify wat'
502 $ hg histedit 050280826e04
502 $ hg histedit 050280826e04
503 abort: cannot edit history that contains merges
503 abort: cannot edit history that contains merges
504 [255]
504 [255]
505 $ cd ..
505 $ cd ..
@@ -1,153 +1,153 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > histedit=
3 > histedit=
4 > EOF
4 > EOF
5
5
6 $ initrepos ()
6 $ initrepos ()
7 > {
7 > {
8 > hg init r
8 > hg init r
9 > cd r
9 > cd r
10 > for x in a b c ; do
10 > for x in a b c ; do
11 > echo $x > $x
11 > echo $x > $x
12 > hg add $x
12 > hg add $x
13 > hg ci -m $x
13 > hg ci -m $x
14 > done
14 > done
15 > cd ..
15 > cd ..
16 > hg clone r r2 | grep -v updating
16 > hg clone r r2 | grep -v updating
17 > cd r2
17 > cd r2
18 > for x in d e f ; do
18 > for x in d e f ; do
19 > echo $x > $x
19 > echo $x > $x
20 > hg add $x
20 > hg add $x
21 > hg ci -m $x
21 > hg ci -m $x
22 > done
22 > done
23 > cd ..
23 > cd ..
24 > hg init r3
24 > hg init r3
25 > cd r3
25 > cd r3
26 > for x in g h i ; do
26 > for x in g h i ; do
27 > echo $x > $x
27 > echo $x > $x
28 > hg add $x
28 > hg add $x
29 > hg ci -m $x
29 > hg ci -m $x
30 > done
30 > done
31 > cd ..
31 > cd ..
32 > }
32 > }
33
33
34 $ initrepos
34 $ initrepos
35 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
35 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
36
36
37 show the edit commands offered by outgoing
37 show the edit commands offered by outgoing
38 $ cd r2
38 $ cd r2
39 $ HGEDITOR=cat hg histedit --outgoing ../r | grep -v comparing | grep -v searching
39 $ HGEDITOR=cat hg histedit --outgoing ../r | grep -v comparing | grep -v searching
40 pick 055a42cdd887 3 d
40 pick 055a42cdd887 3 d
41 pick e860deea161a 4 e
41 pick e860deea161a 4 e
42 pick 652413bf663e 5 f
42 pick 652413bf663e 5 f
43
43
44 # Edit history between 055a42cdd887 and 652413bf663e
44 # Edit history between 055a42cdd887 and 652413bf663e
45 #
45 #
46 # Commits are listed from least to most recent
46 # Commits are listed from least to most recent
47 #
47 #
48 # You can reorder changesets by reordering the lines
48 # You can reorder changesets by reordering the lines
49 #
49 #
50 # Commands:
50 # Commands:
51 #
51 #
52 # e, edit = use commit, but stop for amending
52 # e, edit = use commit, but stop for amending
53 # m, mess = edit commit message without changing commit content
53 # m, mess = edit commit message without changing commit content
54 # p, pick = use commit
54 # p, pick = use commit
55 # d, drop = remove commit from history
55 # d, drop = remove commit from history
56 # f, fold = use commit, but combine it with the one above
56 # f, fold = use commit, but combine it with the one above
57 # r, roll = like fold, but discard this commit's description
57 # r, roll = like fold, but discard this commit's description
58 #
58 #
59 $ cd ..
59 $ cd ..
60
60
61 show the error from unrelated repos
61 show the error from unrelated repos
62 $ cd r3
62 $ cd r3
63 $ HGEDITOR=cat hg histedit --outgoing ../r | grep -v comparing | grep -v searching
63 $ HGEDITOR=cat hg histedit --outgoing ../r | grep -v comparing | grep -v searching
64 abort: repository is unrelated
64 abort: repository is unrelated
65 [1]
65 [1]
66 $ cd ..
66 $ cd ..
67
67
68 show the error from unrelated repos
68 show the error from unrelated repos
69 $ cd r3
69 $ cd r3
70 $ HGEDITOR=cat hg histedit --force --outgoing ../r
70 $ HGEDITOR=cat hg histedit --force --outgoing ../r
71 comparing with ../r
71 comparing with ../r
72 searching for changes
72 searching for changes
73 warning: repository is unrelated
73 warning: repository is unrelated
74 pick 2a4042b45417 0 g
74 pick 2a4042b45417 0 g
75 pick 68c46b4927ce 1 h
75 pick 68c46b4927ce 1 h
76 pick 51281e65ba79 2 i
76 pick 51281e65ba79 2 i
77
77
78 # Edit history between 2a4042b45417 and 51281e65ba79
78 # Edit history between 2a4042b45417 and 51281e65ba79
79 #
79 #
80 # Commits are listed from least to most recent
80 # Commits are listed from least to most recent
81 #
81 #
82 # You can reorder changesets by reordering the lines
82 # You can reorder changesets by reordering the lines
83 #
83 #
84 # Commands:
84 # Commands:
85 #
85 #
86 # e, edit = use commit, but stop for amending
86 # e, edit = use commit, but stop for amending
87 # m, mess = edit commit message without changing commit content
87 # m, mess = edit commit message without changing commit content
88 # p, pick = use commit
88 # p, pick = use commit
89 # d, drop = remove commit from history
89 # d, drop = remove commit from history
90 # f, fold = use commit, but combine it with the one above
90 # f, fold = use commit, but combine it with the one above
91 # r, roll = like fold, but discard this commit's description
91 # r, roll = like fold, but discard this commit's description
92 #
92 #
93 $ cd ..
93 $ cd ..
94
94
95 test sensitivity to branch in URL:
95 test sensitivity to branch in URL:
96
96
97 $ cd r2
97 $ cd r2
98 $ hg -q update 2
98 $ hg -q update 2
99 $ hg -q branch foo
99 $ hg -q branch foo
100 $ hg commit -m 'create foo branch'
100 $ hg commit -m 'create foo branch'
101 $ HGEDITOR=cat hg histedit --outgoing '../r#foo' | grep -v comparing | grep -v searching
101 $ HGEDITOR=cat hg histedit --outgoing '../r#foo' | grep -v comparing | grep -v searching
102 pick f26599ee3441 6 create foo branch
102 pick f26599ee3441 6 create foo branch
103
103
104 # Edit history between f26599ee3441 and f26599ee3441
104 # Edit history between f26599ee3441 and f26599ee3441
105 #
105 #
106 # Commits are listed from least to most recent
106 # Commits are listed from least to most recent
107 #
107 #
108 # You can reorder changesets by reordering the lines
108 # You can reorder changesets by reordering the lines
109 #
109 #
110 # Commands:
110 # Commands:
111 #
111 #
112 # e, edit = use commit, but stop for amending
112 # e, edit = use commit, but stop for amending
113 # m, mess = edit commit message without changing commit content
113 # m, mess = edit commit message without changing commit content
114 # p, pick = use commit
114 # p, pick = use commit
115 # d, drop = remove commit from history
115 # d, drop = remove commit from history
116 # f, fold = use commit, but combine it with the one above
116 # f, fold = use commit, but combine it with the one above
117 # r, roll = like fold, but discard this commit's description
117 # r, roll = like fold, but discard this commit's description
118 #
118 #
119
119
120 test to check number of roots in outgoing revisions
120 test to check number of roots in outgoing revisions
121
121
122 $ hg -q outgoing -G --template '{node|short}({branch})' '../r'
122 $ hg -q outgoing -G --template '{node|short}({branch})' '../r'
123 @ f26599ee3441(foo)
123 @ f26599ee3441(foo)
124
124
125 o 652413bf663e(default)
125 o 652413bf663e(default)
126 |
126 |
127 o e860deea161a(default)
127 o e860deea161a(default)
128 |
128 |
129 o 055a42cdd887(default)
129 o 055a42cdd887(default)
130
130
131 $ HGEDITOR=cat hg -q histedit --outgoing '../r'
131 $ HGEDITOR=cat hg -q histedit --outgoing '../r'
132 abort: there are ambiguous outgoing revisions
132 abort: there are ambiguous outgoing revisions
133 (see "hg help histedit" for more detail)
133 (see 'hg help histedit' for more detail)
134 [255]
134 [255]
135
135
136 $ hg -q update -C 2
136 $ hg -q update -C 2
137 $ echo aa >> a
137 $ echo aa >> a
138 $ hg -q commit -m 'another head on default'
138 $ hg -q commit -m 'another head on default'
139 $ hg -q outgoing -G --template '{node|short}({branch})' '../r#default'
139 $ hg -q outgoing -G --template '{node|short}({branch})' '../r#default'
140 @ 3879dc049647(default)
140 @ 3879dc049647(default)
141
141
142 o 652413bf663e(default)
142 o 652413bf663e(default)
143 |
143 |
144 o e860deea161a(default)
144 o e860deea161a(default)
145 |
145 |
146 o 055a42cdd887(default)
146 o 055a42cdd887(default)
147
147
148 $ HGEDITOR=cat hg -q histedit --outgoing '../r#default'
148 $ HGEDITOR=cat hg -q histedit --outgoing '../r#default'
149 abort: there are ambiguous outgoing revisions
149 abort: there are ambiguous outgoing revisions
150 (see "hg help histedit" for more detail)
150 (see 'hg help histedit' for more detail)
151 [255]
151 [255]
152
152
153 $ cd ..
153 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now