##// END OF EJS Templates
histedit: tweak `edit` message to try and guide users to our workflow...
Augie Fackler -
r46719:3f82a915 default
parent child Browse files
Show More
@@ -1,2654 +1,2655 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 and date
39 # r, roll = like fold, but discard this commit's description and date
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 # b, base = checkout changeset and apply further changesets from there
42 # b, base = checkout changeset and apply further changesets from there
43 #
43 #
44
44
45 In this file, lines beginning with ``#`` are ignored. You must specify a rule
45 In this file, lines beginning with ``#`` are ignored. You must specify a rule
46 for each revision in your history. For example, if you had meant to add gamma
46 for each revision in your history. For example, if you had meant to add gamma
47 before beta, and then wanted to add delta in the same revision as beta, you
47 before beta, and then wanted to add delta in the same revision as beta, you
48 would reorganize the file to look like this::
48 would reorganize the file to look like this::
49
49
50 pick 030b686bedc4 Add gamma
50 pick 030b686bedc4 Add gamma
51 pick c561b4e977df Add beta
51 pick c561b4e977df Add beta
52 fold 7c2fd3b9020c Add delta
52 fold 7c2fd3b9020c Add delta
53
53
54 # Edit history between c561b4e977df and 7c2fd3b9020c
54 # Edit history between c561b4e977df and 7c2fd3b9020c
55 #
55 #
56 # Commits are listed from least to most recent
56 # Commits are listed from least to most recent
57 #
57 #
58 # Commands:
58 # Commands:
59 # p, pick = use commit
59 # p, pick = use commit
60 # e, edit = use commit, but stop for amending
60 # e, edit = use commit, but stop for amending
61 # f, fold = use commit, but combine it with the one above
61 # f, fold = use commit, but combine it with the one above
62 # r, roll = like fold, but discard this commit's description and date
62 # r, roll = like fold, but discard this commit's description and date
63 # d, drop = remove commit from history
63 # d, drop = remove commit from history
64 # m, mess = edit commit message without changing commit content
64 # m, mess = edit commit message without changing commit content
65 # b, base = checkout changeset and apply further changesets from there
65 # b, base = checkout changeset and apply further changesets from there
66 #
66 #
67
67
68 At which point you close the editor and ``histedit`` starts working. When you
68 At which point you close the editor and ``histedit`` starts working. When you
69 specify a ``fold`` operation, ``histedit`` will open an editor when it folds
69 specify a ``fold`` operation, ``histedit`` will open an editor when it folds
70 those revisions together, offering you a chance to clean up the commit message::
70 those revisions together, offering you a chance to clean up the commit message::
71
71
72 Add beta
72 Add beta
73 ***
73 ***
74 Add delta
74 Add delta
75
75
76 Edit the commit message to your liking, then close the editor. The date used
76 Edit the commit message to your liking, then close the editor. The date used
77 for the commit will be the later of the two commits' dates. For this example,
77 for the commit will be the later of the two commits' dates. For this example,
78 let's assume that the commit message was changed to ``Add beta and delta.``
78 let's assume that the commit message was changed to ``Add beta and delta.``
79 After histedit has run and had a chance to remove any old or temporary
79 After histedit has run and had a chance to remove any old or temporary
80 revisions it needed, the history looks like this::
80 revisions it needed, the history looks like this::
81
81
82 @ 2[tip] 989b4d060121 2009-04-27 18:04 -0500 durin42
82 @ 2[tip] 989b4d060121 2009-04-27 18:04 -0500 durin42
83 | Add beta and delta.
83 | Add beta and delta.
84 |
84 |
85 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
85 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
86 | Add gamma
86 | Add gamma
87 |
87 |
88 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
88 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
89 Add alpha
89 Add alpha
90
90
91 Note that ``histedit`` does *not* remove any revisions (even its own temporary
91 Note that ``histedit`` does *not* remove any revisions (even its own temporary
92 ones) until after it has completed all the editing operations, so it will
92 ones) until after it has completed all the editing operations, so it will
93 probably perform several strip operations when it's done. For the above example,
93 probably perform several strip operations when it's done. For the above example,
94 it had to run strip twice. Strip can be slow depending on a variety of factors,
94 it had to run strip twice. Strip can be slow depending on a variety of factors,
95 so you might need to be a little patient. You can choose to keep the original
95 so you might need to be a little patient. You can choose to keep the original
96 revisions by passing the ``--keep`` flag.
96 revisions by passing the ``--keep`` flag.
97
97
98 The ``edit`` operation will drop you back to a command prompt,
98 The ``edit`` operation will drop you back to a command prompt,
99 allowing you to edit files freely, or even use ``hg record`` to commit
99 allowing you to edit files freely, or even use ``hg record`` to commit
100 some changes as a separate commit. When you're done, any remaining
100 some changes as a separate commit. When you're done, any remaining
101 uncommitted changes will be committed as well. When done, run ``hg
101 uncommitted changes will be committed as well. When done, run ``hg
102 histedit --continue`` to finish this step. If there are uncommitted
102 histedit --continue`` to finish this step. If there are uncommitted
103 changes, you'll be prompted for a new commit message, but the default
103 changes, you'll be prompted for a new commit message, but the default
104 commit message will be the original message for the ``edit`` ed
104 commit message will be the original message for the ``edit`` ed
105 revision, and the date of the original commit will be preserved.
105 revision, and the date of the original commit will be preserved.
106
106
107 The ``message`` operation will give you a chance to revise a commit
107 The ``message`` operation will give you a chance to revise a commit
108 message without changing the contents. It's a shortcut for doing
108 message without changing the contents. It's a shortcut for doing
109 ``edit`` immediately followed by `hg histedit --continue``.
109 ``edit`` immediately followed by `hg histedit --continue``.
110
110
111 If ``histedit`` encounters a conflict when moving a revision (while
111 If ``histedit`` encounters a conflict when moving a revision (while
112 handling ``pick`` or ``fold``), it'll stop in a similar manner to
112 handling ``pick`` or ``fold``), it'll stop in a similar manner to
113 ``edit`` with the difference that it won't prompt you for a commit
113 ``edit`` with the difference that it won't prompt you for a commit
114 message when done. If you decide at this point that you don't like how
114 message when done. If you decide at this point that you don't like how
115 much work it will be to rearrange history, or that you made a mistake,
115 much work it will be to rearrange history, or that you made a mistake,
116 you can use ``hg histedit --abort`` to abandon the new changes you
116 you can use ``hg histedit --abort`` to abandon the new changes you
117 have made and return to the state before you attempted to edit your
117 have made and return to the state before you attempted to edit your
118 history.
118 history.
119
119
120 If we clone the histedit-ed example repository above and add four more
120 If we clone the histedit-ed example repository above and add four more
121 changes, such that we have the following history::
121 changes, such that we have the following history::
122
122
123 @ 6[tip] 038383181893 2009-04-27 18:04 -0500 stefan
123 @ 6[tip] 038383181893 2009-04-27 18:04 -0500 stefan
124 | Add theta
124 | Add theta
125 |
125 |
126 o 5 140988835471 2009-04-27 18:04 -0500 stefan
126 o 5 140988835471 2009-04-27 18:04 -0500 stefan
127 | Add eta
127 | Add eta
128 |
128 |
129 o 4 122930637314 2009-04-27 18:04 -0500 stefan
129 o 4 122930637314 2009-04-27 18:04 -0500 stefan
130 | Add zeta
130 | Add zeta
131 |
131 |
132 o 3 836302820282 2009-04-27 18:04 -0500 stefan
132 o 3 836302820282 2009-04-27 18:04 -0500 stefan
133 | Add epsilon
133 | Add epsilon
134 |
134 |
135 o 2 989b4d060121 2009-04-27 18:04 -0500 durin42
135 o 2 989b4d060121 2009-04-27 18:04 -0500 durin42
136 | Add beta and delta.
136 | Add beta and delta.
137 |
137 |
138 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
138 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
139 | Add gamma
139 | Add gamma
140 |
140 |
141 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
141 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
142 Add alpha
142 Add alpha
143
143
144 If you run ``hg histedit --outgoing`` on the clone then it is the same
144 If you run ``hg histedit --outgoing`` on the clone then it is the same
145 as running ``hg histedit 836302820282``. If you need plan to push to a
145 as running ``hg histedit 836302820282``. If you need plan to push to a
146 repository that Mercurial does not detect to be related to the source
146 repository that Mercurial does not detect to be related to the source
147 repo, you can add a ``--force`` option.
147 repo, you can add a ``--force`` option.
148
148
149 Config
149 Config
150 ------
150 ------
151
151
152 Histedit rule lines are truncated to 80 characters by default. You
152 Histedit rule lines are truncated to 80 characters by default. You
153 can customize this behavior by setting a different length in your
153 can customize this behavior by setting a different length in your
154 configuration file::
154 configuration file::
155
155
156 [histedit]
156 [histedit]
157 linelen = 120 # truncate rule lines at 120 characters
157 linelen = 120 # truncate rule lines at 120 characters
158
158
159 The summary of a change can be customized as well::
159 The summary of a change can be customized as well::
160
160
161 [histedit]
161 [histedit]
162 summary-template = '{rev} {bookmarks} {desc|firstline}'
162 summary-template = '{rev} {bookmarks} {desc|firstline}'
163
163
164 The customized summary should be kept short enough that rule lines
164 The customized summary should be kept short enough that rule lines
165 will fit in the configured line length. See above if that requires
165 will fit in the configured line length. See above if that requires
166 customization.
166 customization.
167
167
168 ``hg histedit`` attempts to automatically choose an appropriate base
168 ``hg histedit`` attempts to automatically choose an appropriate base
169 revision to use. To change which base revision is used, define a
169 revision to use. To change which base revision is used, define a
170 revset in your configuration file::
170 revset in your configuration file::
171
171
172 [histedit]
172 [histedit]
173 defaultrev = only(.) & draft()
173 defaultrev = only(.) & draft()
174
174
175 By default each edited revision needs to be present in histedit commands.
175 By default each edited revision needs to be present in histedit commands.
176 To remove revision you need to use ``drop`` operation. You can configure
176 To remove revision you need to use ``drop`` operation. You can configure
177 the drop to be implicit for missing commits by adding::
177 the drop to be implicit for missing commits by adding::
178
178
179 [histedit]
179 [histedit]
180 dropmissing = True
180 dropmissing = True
181
181
182 By default, histedit will close the transaction after each action. For
182 By default, histedit will close the transaction after each action. For
183 performance purposes, you can configure histedit to use a single transaction
183 performance purposes, you can configure histedit to use a single transaction
184 across the entire histedit. WARNING: This setting introduces a significant risk
184 across the entire histedit. WARNING: This setting introduces a significant risk
185 of losing the work you've done in a histedit if the histedit aborts
185 of losing the work you've done in a histedit if the histedit aborts
186 unexpectedly::
186 unexpectedly::
187
187
188 [histedit]
188 [histedit]
189 singletransaction = True
189 singletransaction = True
190
190
191 """
191 """
192
192
193 from __future__ import absolute_import
193 from __future__ import absolute_import
194
194
195 # chistedit dependencies that are not available everywhere
195 # chistedit dependencies that are not available everywhere
196 try:
196 try:
197 import fcntl
197 import fcntl
198 import termios
198 import termios
199 except ImportError:
199 except ImportError:
200 fcntl = None
200 fcntl = None
201 termios = None
201 termios = None
202
202
203 import functools
203 import functools
204 import os
204 import os
205 import struct
205 import struct
206
206
207 from mercurial.i18n import _
207 from mercurial.i18n import _
208 from mercurial.pycompat import (
208 from mercurial.pycompat import (
209 getattr,
209 getattr,
210 open,
210 open,
211 )
211 )
212 from mercurial import (
212 from mercurial import (
213 bundle2,
213 bundle2,
214 cmdutil,
214 cmdutil,
215 context,
215 context,
216 copies,
216 copies,
217 destutil,
217 destutil,
218 discovery,
218 discovery,
219 encoding,
219 encoding,
220 error,
220 error,
221 exchange,
221 exchange,
222 extensions,
222 extensions,
223 hg,
223 hg,
224 logcmdutil,
224 logcmdutil,
225 merge as mergemod,
225 merge as mergemod,
226 mergestate as mergestatemod,
226 mergestate as mergestatemod,
227 mergeutil,
227 mergeutil,
228 node,
228 node,
229 obsolete,
229 obsolete,
230 pycompat,
230 pycompat,
231 registrar,
231 registrar,
232 repair,
232 repair,
233 rewriteutil,
233 rewriteutil,
234 scmutil,
234 scmutil,
235 state as statemod,
235 state as statemod,
236 util,
236 util,
237 )
237 )
238 from mercurial.utils import (
238 from mercurial.utils import (
239 dateutil,
239 dateutil,
240 stringutil,
240 stringutil,
241 )
241 )
242
242
243 pickle = util.pickle
243 pickle = util.pickle
244 cmdtable = {}
244 cmdtable = {}
245 command = registrar.command(cmdtable)
245 command = registrar.command(cmdtable)
246
246
247 configtable = {}
247 configtable = {}
248 configitem = registrar.configitem(configtable)
248 configitem = registrar.configitem(configtable)
249 configitem(
249 configitem(
250 b'experimental',
250 b'experimental',
251 b'histedit.autoverb',
251 b'histedit.autoverb',
252 default=False,
252 default=False,
253 )
253 )
254 configitem(
254 configitem(
255 b'histedit',
255 b'histedit',
256 b'defaultrev',
256 b'defaultrev',
257 default=None,
257 default=None,
258 )
258 )
259 configitem(
259 configitem(
260 b'histedit',
260 b'histedit',
261 b'dropmissing',
261 b'dropmissing',
262 default=False,
262 default=False,
263 )
263 )
264 configitem(
264 configitem(
265 b'histedit',
265 b'histedit',
266 b'linelen',
266 b'linelen',
267 default=80,
267 default=80,
268 )
268 )
269 configitem(
269 configitem(
270 b'histedit',
270 b'histedit',
271 b'singletransaction',
271 b'singletransaction',
272 default=False,
272 default=False,
273 )
273 )
274 configitem(
274 configitem(
275 b'ui',
275 b'ui',
276 b'interface.histedit',
276 b'interface.histedit',
277 default=None,
277 default=None,
278 )
278 )
279 configitem(b'histedit', b'summary-template', default=b'{rev} {desc|firstline}')
279 configitem(b'histedit', b'summary-template', default=b'{rev} {desc|firstline}')
280
280
281 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
281 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
282 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
282 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
283 # be specifying the version(s) of Mercurial they are tested with, or
283 # be specifying the version(s) of Mercurial they are tested with, or
284 # leave the attribute unspecified.
284 # leave the attribute unspecified.
285 testedwith = b'ships-with-hg-core'
285 testedwith = b'ships-with-hg-core'
286
286
287 actiontable = {}
287 actiontable = {}
288 primaryactions = set()
288 primaryactions = set()
289 secondaryactions = set()
289 secondaryactions = set()
290 tertiaryactions = set()
290 tertiaryactions = set()
291 internalactions = set()
291 internalactions = set()
292
292
293
293
294 def geteditcomment(ui, first, last):
294 def geteditcomment(ui, first, last):
295 """construct the editor comment
295 """construct the editor comment
296 The comment includes::
296 The comment includes::
297 - an intro
297 - an intro
298 - sorted primary commands
298 - sorted primary commands
299 - sorted short commands
299 - sorted short commands
300 - sorted long commands
300 - sorted long commands
301 - additional hints
301 - additional hints
302
302
303 Commands are only included once.
303 Commands are only included once.
304 """
304 """
305 intro = _(
305 intro = _(
306 b"""Edit history between %s and %s
306 b"""Edit history between %s and %s
307
307
308 Commits are listed from least to most recent
308 Commits are listed from least to most recent
309
309
310 You can reorder changesets by reordering the lines
310 You can reorder changesets by reordering the lines
311
311
312 Commands:
312 Commands:
313 """
313 """
314 )
314 )
315 actions = []
315 actions = []
316
316
317 def addverb(v):
317 def addverb(v):
318 a = actiontable[v]
318 a = actiontable[v]
319 lines = a.message.split(b"\n")
319 lines = a.message.split(b"\n")
320 if len(a.verbs):
320 if len(a.verbs):
321 v = b', '.join(sorted(a.verbs, key=lambda v: len(v)))
321 v = b', '.join(sorted(a.verbs, key=lambda v: len(v)))
322 actions.append(b" %s = %s" % (v, lines[0]))
322 actions.append(b" %s = %s" % (v, lines[0]))
323 actions.extend([b' %s'] * (len(lines) - 1))
323 actions.extend([b' %s'] * (len(lines) - 1))
324
324
325 for v in (
325 for v in (
326 sorted(primaryactions)
326 sorted(primaryactions)
327 + sorted(secondaryactions)
327 + sorted(secondaryactions)
328 + sorted(tertiaryactions)
328 + sorted(tertiaryactions)
329 ):
329 ):
330 addverb(v)
330 addverb(v)
331 actions.append(b'')
331 actions.append(b'')
332
332
333 hints = []
333 hints = []
334 if ui.configbool(b'histedit', b'dropmissing'):
334 if ui.configbool(b'histedit', b'dropmissing'):
335 hints.append(
335 hints.append(
336 b"Deleting a changeset from the list "
336 b"Deleting a changeset from the list "
337 b"will DISCARD it from the edited history!"
337 b"will DISCARD it from the edited history!"
338 )
338 )
339
339
340 lines = (intro % (first, last)).split(b'\n') + actions + hints
340 lines = (intro % (first, last)).split(b'\n') + actions + hints
341
341
342 return b''.join([b'# %s\n' % l if l else b'#\n' for l in lines])
342 return b''.join([b'# %s\n' % l if l else b'#\n' for l in lines])
343
343
344
344
345 class histeditstate(object):
345 class histeditstate(object):
346 def __init__(self, repo):
346 def __init__(self, repo):
347 self.repo = repo
347 self.repo = repo
348 self.actions = None
348 self.actions = None
349 self.keep = None
349 self.keep = None
350 self.topmost = None
350 self.topmost = None
351 self.parentctxnode = None
351 self.parentctxnode = None
352 self.lock = None
352 self.lock = None
353 self.wlock = None
353 self.wlock = None
354 self.backupfile = None
354 self.backupfile = None
355 self.stateobj = statemod.cmdstate(repo, b'histedit-state')
355 self.stateobj = statemod.cmdstate(repo, b'histedit-state')
356 self.replacements = []
356 self.replacements = []
357
357
358 def read(self):
358 def read(self):
359 """Load histedit state from disk and set fields appropriately."""
359 """Load histedit state from disk and set fields appropriately."""
360 if not self.stateobj.exists():
360 if not self.stateobj.exists():
361 cmdutil.wrongtooltocontinue(self.repo, _(b'histedit'))
361 cmdutil.wrongtooltocontinue(self.repo, _(b'histedit'))
362
362
363 data = self._read()
363 data = self._read()
364
364
365 self.parentctxnode = data[b'parentctxnode']
365 self.parentctxnode = data[b'parentctxnode']
366 actions = parserules(data[b'rules'], self)
366 actions = parserules(data[b'rules'], self)
367 self.actions = actions
367 self.actions = actions
368 self.keep = data[b'keep']
368 self.keep = data[b'keep']
369 self.topmost = data[b'topmost']
369 self.topmost = data[b'topmost']
370 self.replacements = data[b'replacements']
370 self.replacements = data[b'replacements']
371 self.backupfile = data[b'backupfile']
371 self.backupfile = data[b'backupfile']
372
372
373 def _read(self):
373 def _read(self):
374 fp = self.repo.vfs.read(b'histedit-state')
374 fp = self.repo.vfs.read(b'histedit-state')
375 if fp.startswith(b'v1\n'):
375 if fp.startswith(b'v1\n'):
376 data = self._load()
376 data = self._load()
377 parentctxnode, rules, keep, topmost, replacements, backupfile = data
377 parentctxnode, rules, keep, topmost, replacements, backupfile = data
378 else:
378 else:
379 data = pickle.loads(fp)
379 data = pickle.loads(fp)
380 parentctxnode, rules, keep, topmost, replacements = data
380 parentctxnode, rules, keep, topmost, replacements = data
381 backupfile = None
381 backupfile = None
382 rules = b"\n".join([b"%s %s" % (verb, rest) for [verb, rest] in rules])
382 rules = b"\n".join([b"%s %s" % (verb, rest) for [verb, rest] in rules])
383
383
384 return {
384 return {
385 b'parentctxnode': parentctxnode,
385 b'parentctxnode': parentctxnode,
386 b"rules": rules,
386 b"rules": rules,
387 b"keep": keep,
387 b"keep": keep,
388 b"topmost": topmost,
388 b"topmost": topmost,
389 b"replacements": replacements,
389 b"replacements": replacements,
390 b"backupfile": backupfile,
390 b"backupfile": backupfile,
391 }
391 }
392
392
393 def write(self, tr=None):
393 def write(self, tr=None):
394 if tr:
394 if tr:
395 tr.addfilegenerator(
395 tr.addfilegenerator(
396 b'histedit-state',
396 b'histedit-state',
397 (b'histedit-state',),
397 (b'histedit-state',),
398 self._write,
398 self._write,
399 location=b'plain',
399 location=b'plain',
400 )
400 )
401 else:
401 else:
402 with self.repo.vfs(b"histedit-state", b"w") as f:
402 with self.repo.vfs(b"histedit-state", b"w") as f:
403 self._write(f)
403 self._write(f)
404
404
405 def _write(self, fp):
405 def _write(self, fp):
406 fp.write(b'v1\n')
406 fp.write(b'v1\n')
407 fp.write(b'%s\n' % node.hex(self.parentctxnode))
407 fp.write(b'%s\n' % node.hex(self.parentctxnode))
408 fp.write(b'%s\n' % node.hex(self.topmost))
408 fp.write(b'%s\n' % node.hex(self.topmost))
409 fp.write(b'%s\n' % (b'True' if self.keep else b'False'))
409 fp.write(b'%s\n' % (b'True' if self.keep else b'False'))
410 fp.write(b'%d\n' % len(self.actions))
410 fp.write(b'%d\n' % len(self.actions))
411 for action in self.actions:
411 for action in self.actions:
412 fp.write(b'%s\n' % action.tostate())
412 fp.write(b'%s\n' % action.tostate())
413 fp.write(b'%d\n' % len(self.replacements))
413 fp.write(b'%d\n' % len(self.replacements))
414 for replacement in self.replacements:
414 for replacement in self.replacements:
415 fp.write(
415 fp.write(
416 b'%s%s\n'
416 b'%s%s\n'
417 % (
417 % (
418 node.hex(replacement[0]),
418 node.hex(replacement[0]),
419 b''.join(node.hex(r) for r in replacement[1]),
419 b''.join(node.hex(r) for r in replacement[1]),
420 )
420 )
421 )
421 )
422 backupfile = self.backupfile
422 backupfile = self.backupfile
423 if not backupfile:
423 if not backupfile:
424 backupfile = b''
424 backupfile = b''
425 fp.write(b'%s\n' % backupfile)
425 fp.write(b'%s\n' % backupfile)
426
426
427 def _load(self):
427 def _load(self):
428 fp = self.repo.vfs(b'histedit-state', b'r')
428 fp = self.repo.vfs(b'histedit-state', b'r')
429 lines = [l[:-1] for l in fp.readlines()]
429 lines = [l[:-1] for l in fp.readlines()]
430
430
431 index = 0
431 index = 0
432 lines[index] # version number
432 lines[index] # version number
433 index += 1
433 index += 1
434
434
435 parentctxnode = node.bin(lines[index])
435 parentctxnode = node.bin(lines[index])
436 index += 1
436 index += 1
437
437
438 topmost = node.bin(lines[index])
438 topmost = node.bin(lines[index])
439 index += 1
439 index += 1
440
440
441 keep = lines[index] == b'True'
441 keep = lines[index] == b'True'
442 index += 1
442 index += 1
443
443
444 # Rules
444 # Rules
445 rules = []
445 rules = []
446 rulelen = int(lines[index])
446 rulelen = int(lines[index])
447 index += 1
447 index += 1
448 for i in pycompat.xrange(rulelen):
448 for i in pycompat.xrange(rulelen):
449 ruleaction = lines[index]
449 ruleaction = lines[index]
450 index += 1
450 index += 1
451 rule = lines[index]
451 rule = lines[index]
452 index += 1
452 index += 1
453 rules.append((ruleaction, rule))
453 rules.append((ruleaction, rule))
454
454
455 # Replacements
455 # Replacements
456 replacements = []
456 replacements = []
457 replacementlen = int(lines[index])
457 replacementlen = int(lines[index])
458 index += 1
458 index += 1
459 for i in pycompat.xrange(replacementlen):
459 for i in pycompat.xrange(replacementlen):
460 replacement = lines[index]
460 replacement = lines[index]
461 original = node.bin(replacement[:40])
461 original = node.bin(replacement[:40])
462 succ = [
462 succ = [
463 node.bin(replacement[i : i + 40])
463 node.bin(replacement[i : i + 40])
464 for i in range(40, len(replacement), 40)
464 for i in range(40, len(replacement), 40)
465 ]
465 ]
466 replacements.append((original, succ))
466 replacements.append((original, succ))
467 index += 1
467 index += 1
468
468
469 backupfile = lines[index]
469 backupfile = lines[index]
470 index += 1
470 index += 1
471
471
472 fp.close()
472 fp.close()
473
473
474 return parentctxnode, rules, keep, topmost, replacements, backupfile
474 return parentctxnode, rules, keep, topmost, replacements, backupfile
475
475
476 def clear(self):
476 def clear(self):
477 if self.inprogress():
477 if self.inprogress():
478 self.repo.vfs.unlink(b'histedit-state')
478 self.repo.vfs.unlink(b'histedit-state')
479
479
480 def inprogress(self):
480 def inprogress(self):
481 return self.repo.vfs.exists(b'histedit-state')
481 return self.repo.vfs.exists(b'histedit-state')
482
482
483
483
484 class histeditaction(object):
484 class histeditaction(object):
485 def __init__(self, state, node):
485 def __init__(self, state, node):
486 self.state = state
486 self.state = state
487 self.repo = state.repo
487 self.repo = state.repo
488 self.node = node
488 self.node = node
489
489
490 @classmethod
490 @classmethod
491 def fromrule(cls, state, rule):
491 def fromrule(cls, state, rule):
492 """Parses the given rule, returning an instance of the histeditaction."""
492 """Parses the given rule, returning an instance of the histeditaction."""
493 ruleid = rule.strip().split(b' ', 1)[0]
493 ruleid = rule.strip().split(b' ', 1)[0]
494 # ruleid can be anything from rev numbers, hashes, "bookmarks" etc
494 # ruleid can be anything from rev numbers, hashes, "bookmarks" etc
495 # Check for validation of rule ids and get the rulehash
495 # Check for validation of rule ids and get the rulehash
496 try:
496 try:
497 rev = node.bin(ruleid)
497 rev = node.bin(ruleid)
498 except TypeError:
498 except TypeError:
499 try:
499 try:
500 _ctx = scmutil.revsingle(state.repo, ruleid)
500 _ctx = scmutil.revsingle(state.repo, ruleid)
501 rulehash = _ctx.hex()
501 rulehash = _ctx.hex()
502 rev = node.bin(rulehash)
502 rev = node.bin(rulehash)
503 except error.RepoLookupError:
503 except error.RepoLookupError:
504 raise error.ParseError(_(b"invalid changeset %s") % ruleid)
504 raise error.ParseError(_(b"invalid changeset %s") % ruleid)
505 return cls(state, rev)
505 return cls(state, rev)
506
506
507 def verify(self, prev, expected, seen):
507 def verify(self, prev, expected, seen):
508 """ Verifies semantic correctness of the rule"""
508 """ Verifies semantic correctness of the rule"""
509 repo = self.repo
509 repo = self.repo
510 ha = node.hex(self.node)
510 ha = node.hex(self.node)
511 self.node = scmutil.resolvehexnodeidprefix(repo, ha)
511 self.node = scmutil.resolvehexnodeidprefix(repo, ha)
512 if self.node is None:
512 if self.node is None:
513 raise error.ParseError(_(b'unknown changeset %s listed') % ha[:12])
513 raise error.ParseError(_(b'unknown changeset %s listed') % ha[:12])
514 self._verifynodeconstraints(prev, expected, seen)
514 self._verifynodeconstraints(prev, expected, seen)
515
515
516 def _verifynodeconstraints(self, prev, expected, seen):
516 def _verifynodeconstraints(self, prev, expected, seen):
517 # by default command need a node in the edited list
517 # by default command need a node in the edited list
518 if self.node not in expected:
518 if self.node not in expected:
519 raise error.ParseError(
519 raise error.ParseError(
520 _(b'%s "%s" changeset was not a candidate')
520 _(b'%s "%s" changeset was not a candidate')
521 % (self.verb, node.short(self.node)),
521 % (self.verb, node.short(self.node)),
522 hint=_(b'only use listed changesets'),
522 hint=_(b'only use listed changesets'),
523 )
523 )
524 # and only one command per node
524 # and only one command per node
525 if self.node in seen:
525 if self.node in seen:
526 raise error.ParseError(
526 raise error.ParseError(
527 _(b'duplicated command for changeset %s')
527 _(b'duplicated command for changeset %s')
528 % node.short(self.node)
528 % node.short(self.node)
529 )
529 )
530
530
531 def torule(self):
531 def torule(self):
532 """build a histedit rule line for an action
532 """build a histedit rule line for an action
533
533
534 by default lines are in the form:
534 by default lines are in the form:
535 <hash> <rev> <summary>
535 <hash> <rev> <summary>
536 """
536 """
537 ctx = self.repo[self.node]
537 ctx = self.repo[self.node]
538 ui = self.repo.ui
538 ui = self.repo.ui
539 # We don't want color codes in the commit message template, so
539 # We don't want color codes in the commit message template, so
540 # disable the label() template function while we render it.
540 # disable the label() template function while we render it.
541 with ui.configoverride(
541 with ui.configoverride(
542 {(b'templatealias', b'label(l,x)'): b"x"}, b'histedit'
542 {(b'templatealias', b'label(l,x)'): b"x"}, b'histedit'
543 ):
543 ):
544 summary = cmdutil.rendertemplate(
544 summary = cmdutil.rendertemplate(
545 ctx, ui.config(b'histedit', b'summary-template')
545 ctx, ui.config(b'histedit', b'summary-template')
546 )
546 )
547 # Handle the fact that `''.splitlines() => []`
547 # Handle the fact that `''.splitlines() => []`
548 summary = summary.splitlines()[0] if summary else b''
548 summary = summary.splitlines()[0] if summary else b''
549 line = b'%s %s %s' % (self.verb, ctx, summary)
549 line = b'%s %s %s' % (self.verb, ctx, summary)
550 # trim to 75 columns by default so it's not stupidly wide in my editor
550 # trim to 75 columns by default so it's not stupidly wide in my editor
551 # (the 5 more are left for verb)
551 # (the 5 more are left for verb)
552 maxlen = self.repo.ui.configint(b'histedit', b'linelen')
552 maxlen = self.repo.ui.configint(b'histedit', b'linelen')
553 maxlen = max(maxlen, 22) # avoid truncating hash
553 maxlen = max(maxlen, 22) # avoid truncating hash
554 return stringutil.ellipsis(line, maxlen)
554 return stringutil.ellipsis(line, maxlen)
555
555
556 def tostate(self):
556 def tostate(self):
557 """Print an action in format used by histedit state files
557 """Print an action in format used by histedit state files
558 (the first line is a verb, the remainder is the second)
558 (the first line is a verb, the remainder is the second)
559 """
559 """
560 return b"%s\n%s" % (self.verb, node.hex(self.node))
560 return b"%s\n%s" % (self.verb, node.hex(self.node))
561
561
562 def run(self):
562 def run(self):
563 """Runs the action. The default behavior is simply apply the action's
563 """Runs the action. The default behavior is simply apply the action's
564 rulectx onto the current parentctx."""
564 rulectx onto the current parentctx."""
565 self.applychange()
565 self.applychange()
566 self.continuedirty()
566 self.continuedirty()
567 return self.continueclean()
567 return self.continueclean()
568
568
569 def applychange(self):
569 def applychange(self):
570 """Applies the changes from this action's rulectx onto the current
570 """Applies the changes from this action's rulectx onto the current
571 parentctx, but does not commit them."""
571 parentctx, but does not commit them."""
572 repo = self.repo
572 repo = self.repo
573 rulectx = repo[self.node]
573 rulectx = repo[self.node]
574 repo.ui.pushbuffer(error=True, labeled=True)
574 repo.ui.pushbuffer(error=True, labeled=True)
575 hg.update(repo, self.state.parentctxnode, quietempty=True)
575 hg.update(repo, self.state.parentctxnode, quietempty=True)
576 repo.ui.popbuffer()
576 repo.ui.popbuffer()
577 stats = applychanges(repo.ui, repo, rulectx, {})
577 stats = applychanges(repo.ui, repo, rulectx, {})
578 repo.dirstate.setbranch(rulectx.branch())
578 repo.dirstate.setbranch(rulectx.branch())
579 if stats.unresolvedcount:
579 if stats.unresolvedcount:
580 raise error.InterventionRequired(
580 raise error.InterventionRequired(
581 _(b'Fix up the change (%s %s)')
581 _(b'Fix up the change (%s %s)')
582 % (self.verb, node.short(self.node)),
582 % (self.verb, node.short(self.node)),
583 hint=_(b'hg histedit --continue to resume'),
583 hint=_(b'hg histedit --continue to resume'),
584 )
584 )
585
585
586 def continuedirty(self):
586 def continuedirty(self):
587 """Continues the action when changes have been applied to the working
587 """Continues the action when changes have been applied to the working
588 copy. The default behavior is to commit the dirty changes."""
588 copy. The default behavior is to commit the dirty changes."""
589 repo = self.repo
589 repo = self.repo
590 rulectx = repo[self.node]
590 rulectx = repo[self.node]
591
591
592 editor = self.commiteditor()
592 editor = self.commiteditor()
593 commit = commitfuncfor(repo, rulectx)
593 commit = commitfuncfor(repo, rulectx)
594 if repo.ui.configbool(b'rewrite', b'update-timestamp'):
594 if repo.ui.configbool(b'rewrite', b'update-timestamp'):
595 date = dateutil.makedate()
595 date = dateutil.makedate()
596 else:
596 else:
597 date = rulectx.date()
597 date = rulectx.date()
598 commit(
598 commit(
599 text=rulectx.description(),
599 text=rulectx.description(),
600 user=rulectx.user(),
600 user=rulectx.user(),
601 date=date,
601 date=date,
602 extra=rulectx.extra(),
602 extra=rulectx.extra(),
603 editor=editor,
603 editor=editor,
604 )
604 )
605
605
606 def commiteditor(self):
606 def commiteditor(self):
607 """The editor to be used to edit the commit message."""
607 """The editor to be used to edit the commit message."""
608 return False
608 return False
609
609
610 def continueclean(self):
610 def continueclean(self):
611 """Continues the action when the working copy is clean. The default
611 """Continues the action when the working copy is clean. The default
612 behavior is to accept the current commit as the new version of the
612 behavior is to accept the current commit as the new version of the
613 rulectx."""
613 rulectx."""
614 ctx = self.repo[b'.']
614 ctx = self.repo[b'.']
615 if ctx.node() == self.state.parentctxnode:
615 if ctx.node() == self.state.parentctxnode:
616 self.repo.ui.warn(
616 self.repo.ui.warn(
617 _(b'%s: skipping changeset (no changes)\n')
617 _(b'%s: skipping changeset (no changes)\n')
618 % node.short(self.node)
618 % node.short(self.node)
619 )
619 )
620 return ctx, [(self.node, tuple())]
620 return ctx, [(self.node, tuple())]
621 if ctx.node() == self.node:
621 if ctx.node() == self.node:
622 # Nothing changed
622 # Nothing changed
623 return ctx, []
623 return ctx, []
624 return ctx, [(self.node, (ctx.node(),))]
624 return ctx, [(self.node, (ctx.node(),))]
625
625
626
626
627 def commitfuncfor(repo, src):
627 def commitfuncfor(repo, src):
628 """Build a commit function for the replacement of <src>
628 """Build a commit function for the replacement of <src>
629
629
630 This function ensure we apply the same treatment to all changesets.
630 This function ensure we apply the same treatment to all changesets.
631
631
632 - Add a 'histedit_source' entry in extra.
632 - Add a 'histedit_source' entry in extra.
633
633
634 Note that fold has its own separated logic because its handling is a bit
634 Note that fold has its own separated logic because its handling is a bit
635 different and not easily factored out of the fold method.
635 different and not easily factored out of the fold method.
636 """
636 """
637 phasemin = src.phase()
637 phasemin = src.phase()
638
638
639 def commitfunc(**kwargs):
639 def commitfunc(**kwargs):
640 overrides = {(b'phases', b'new-commit'): phasemin}
640 overrides = {(b'phases', b'new-commit'): phasemin}
641 with repo.ui.configoverride(overrides, b'histedit'):
641 with repo.ui.configoverride(overrides, b'histedit'):
642 extra = kwargs.get('extra', {}).copy()
642 extra = kwargs.get('extra', {}).copy()
643 extra[b'histedit_source'] = src.hex()
643 extra[b'histedit_source'] = src.hex()
644 kwargs['extra'] = extra
644 kwargs['extra'] = extra
645 return repo.commit(**kwargs)
645 return repo.commit(**kwargs)
646
646
647 return commitfunc
647 return commitfunc
648
648
649
649
650 def applychanges(ui, repo, ctx, opts):
650 def applychanges(ui, repo, ctx, opts):
651 """Merge changeset from ctx (only) in the current working directory"""
651 """Merge changeset from ctx (only) in the current working directory"""
652 if ctx.p1().node() == repo.dirstate.p1():
652 if ctx.p1().node() == repo.dirstate.p1():
653 # edits are "in place" we do not need to make any merge,
653 # edits are "in place" we do not need to make any merge,
654 # just applies changes on parent for editing
654 # just applies changes on parent for editing
655 ui.pushbuffer()
655 ui.pushbuffer()
656 cmdutil.revert(ui, repo, ctx, all=True)
656 cmdutil.revert(ui, repo, ctx, all=True)
657 stats = mergemod.updateresult(0, 0, 0, 0)
657 stats = mergemod.updateresult(0, 0, 0, 0)
658 ui.popbuffer()
658 ui.popbuffer()
659 else:
659 else:
660 try:
660 try:
661 # ui.forcemerge is an internal variable, do not document
661 # ui.forcemerge is an internal variable, do not document
662 repo.ui.setconfig(
662 repo.ui.setconfig(
663 b'ui', b'forcemerge', opts.get(b'tool', b''), b'histedit'
663 b'ui', b'forcemerge', opts.get(b'tool', b''), b'histedit'
664 )
664 )
665 stats = mergemod.graft(repo, ctx, labels=[b'local', b'histedit'])
665 stats = mergemod.graft(repo, ctx, labels=[b'local', b'histedit'])
666 finally:
666 finally:
667 repo.ui.setconfig(b'ui', b'forcemerge', b'', b'histedit')
667 repo.ui.setconfig(b'ui', b'forcemerge', b'', b'histedit')
668 return stats
668 return stats
669
669
670
670
671 def collapse(repo, firstctx, lastctx, commitopts, skipprompt=False):
671 def collapse(repo, firstctx, lastctx, commitopts, skipprompt=False):
672 """collapse the set of revisions from first to last as new one.
672 """collapse the set of revisions from first to last as new one.
673
673
674 Expected commit options are:
674 Expected commit options are:
675 - message
675 - message
676 - date
676 - date
677 - username
677 - username
678 Commit message is edited in all cases.
678 Commit message is edited in all cases.
679
679
680 This function works in memory."""
680 This function works in memory."""
681 ctxs = list(repo.set(b'%d::%d', firstctx.rev(), lastctx.rev()))
681 ctxs = list(repo.set(b'%d::%d', firstctx.rev(), lastctx.rev()))
682 if not ctxs:
682 if not ctxs:
683 return None
683 return None
684 for c in ctxs:
684 for c in ctxs:
685 if not c.mutable():
685 if not c.mutable():
686 raise error.ParseError(
686 raise error.ParseError(
687 _(b"cannot fold into public change %s") % node.short(c.node())
687 _(b"cannot fold into public change %s") % node.short(c.node())
688 )
688 )
689 base = firstctx.p1()
689 base = firstctx.p1()
690
690
691 # commit a new version of the old changeset, including the update
691 # commit a new version of the old changeset, including the update
692 # collect all files which might be affected
692 # collect all files which might be affected
693 files = set()
693 files = set()
694 for ctx in ctxs:
694 for ctx in ctxs:
695 files.update(ctx.files())
695 files.update(ctx.files())
696
696
697 # Recompute copies (avoid recording a -> b -> a)
697 # Recompute copies (avoid recording a -> b -> a)
698 copied = copies.pathcopies(base, lastctx)
698 copied = copies.pathcopies(base, lastctx)
699
699
700 # prune files which were reverted by the updates
700 # prune files which were reverted by the updates
701 files = [f for f in files if not cmdutil.samefile(f, lastctx, base)]
701 files = [f for f in files if not cmdutil.samefile(f, lastctx, base)]
702 # commit version of these files as defined by head
702 # commit version of these files as defined by head
703 headmf = lastctx.manifest()
703 headmf = lastctx.manifest()
704
704
705 def filectxfn(repo, ctx, path):
705 def filectxfn(repo, ctx, path):
706 if path in headmf:
706 if path in headmf:
707 fctx = lastctx[path]
707 fctx = lastctx[path]
708 flags = fctx.flags()
708 flags = fctx.flags()
709 mctx = context.memfilectx(
709 mctx = context.memfilectx(
710 repo,
710 repo,
711 ctx,
711 ctx,
712 fctx.path(),
712 fctx.path(),
713 fctx.data(),
713 fctx.data(),
714 islink=b'l' in flags,
714 islink=b'l' in flags,
715 isexec=b'x' in flags,
715 isexec=b'x' in flags,
716 copysource=copied.get(path),
716 copysource=copied.get(path),
717 )
717 )
718 return mctx
718 return mctx
719 return None
719 return None
720
720
721 if commitopts.get(b'message'):
721 if commitopts.get(b'message'):
722 message = commitopts[b'message']
722 message = commitopts[b'message']
723 else:
723 else:
724 message = firstctx.description()
724 message = firstctx.description()
725 user = commitopts.get(b'user')
725 user = commitopts.get(b'user')
726 date = commitopts.get(b'date')
726 date = commitopts.get(b'date')
727 extra = commitopts.get(b'extra')
727 extra = commitopts.get(b'extra')
728
728
729 parents = (firstctx.p1().node(), firstctx.p2().node())
729 parents = (firstctx.p1().node(), firstctx.p2().node())
730 editor = None
730 editor = None
731 if not skipprompt:
731 if not skipprompt:
732 editor = cmdutil.getcommiteditor(edit=True, editform=b'histedit.fold')
732 editor = cmdutil.getcommiteditor(edit=True, editform=b'histedit.fold')
733 new = context.memctx(
733 new = context.memctx(
734 repo,
734 repo,
735 parents=parents,
735 parents=parents,
736 text=message,
736 text=message,
737 files=files,
737 files=files,
738 filectxfn=filectxfn,
738 filectxfn=filectxfn,
739 user=user,
739 user=user,
740 date=date,
740 date=date,
741 extra=extra,
741 extra=extra,
742 editor=editor,
742 editor=editor,
743 )
743 )
744 return repo.commitctx(new)
744 return repo.commitctx(new)
745
745
746
746
747 def _isdirtywc(repo):
747 def _isdirtywc(repo):
748 return repo[None].dirty(missing=True)
748 return repo[None].dirty(missing=True)
749
749
750
750
751 def abortdirty():
751 def abortdirty():
752 raise error.Abort(
752 raise error.Abort(
753 _(b'working copy has pending changes'),
753 _(b'working copy has pending changes'),
754 hint=_(
754 hint=_(
755 b'amend, commit, or revert them and run histedit '
755 b'amend, commit, or revert them and run histedit '
756 b'--continue, or abort with histedit --abort'
756 b'--continue, or abort with histedit --abort'
757 ),
757 ),
758 )
758 )
759
759
760
760
761 def action(verbs, message, priority=False, internal=False):
761 def action(verbs, message, priority=False, internal=False):
762 def wrap(cls):
762 def wrap(cls):
763 assert not priority or not internal
763 assert not priority or not internal
764 verb = verbs[0]
764 verb = verbs[0]
765 if priority:
765 if priority:
766 primaryactions.add(verb)
766 primaryactions.add(verb)
767 elif internal:
767 elif internal:
768 internalactions.add(verb)
768 internalactions.add(verb)
769 elif len(verbs) > 1:
769 elif len(verbs) > 1:
770 secondaryactions.add(verb)
770 secondaryactions.add(verb)
771 else:
771 else:
772 tertiaryactions.add(verb)
772 tertiaryactions.add(verb)
773
773
774 cls.verb = verb
774 cls.verb = verb
775 cls.verbs = verbs
775 cls.verbs = verbs
776 cls.message = message
776 cls.message = message
777 for verb in verbs:
777 for verb in verbs:
778 actiontable[verb] = cls
778 actiontable[verb] = cls
779 return cls
779 return cls
780
780
781 return wrap
781 return wrap
782
782
783
783
784 @action([b'pick', b'p'], _(b'use commit'), priority=True)
784 @action([b'pick', b'p'], _(b'use commit'), priority=True)
785 class pick(histeditaction):
785 class pick(histeditaction):
786 def run(self):
786 def run(self):
787 rulectx = self.repo[self.node]
787 rulectx = self.repo[self.node]
788 if rulectx.p1().node() == self.state.parentctxnode:
788 if rulectx.p1().node() == self.state.parentctxnode:
789 self.repo.ui.debug(b'node %s unchanged\n' % node.short(self.node))
789 self.repo.ui.debug(b'node %s unchanged\n' % node.short(self.node))
790 return rulectx, []
790 return rulectx, []
791
791
792 return super(pick, self).run()
792 return super(pick, self).run()
793
793
794
794
795 @action([b'edit', b'e'], _(b'use commit, but stop for amending'), priority=True)
795 @action([b'edit', b'e'], _(b'use commit, but stop for amending'), priority=True)
796 class edit(histeditaction):
796 class edit(histeditaction):
797 def run(self):
797 def run(self):
798 repo = self.repo
798 repo = self.repo
799 rulectx = repo[self.node]
799 rulectx = repo[self.node]
800 hg.update(repo, self.state.parentctxnode, quietempty=True)
800 hg.update(repo, self.state.parentctxnode, quietempty=True)
801 applychanges(repo.ui, repo, rulectx, {})
801 applychanges(repo.ui, repo, rulectx, {})
802 hint = _(b'to edit %s, `hg histedit --continue` after making changes')
802 raise error.InterventionRequired(
803 raise error.InterventionRequired(
803 _(b'Editing (%s), you may commit or record as needed now.')
804 _(b'Editing (%s), commit as needed now to split the change')
804 % node.short(self.node),
805 % node.short(self.node),
805 hint=_(b'hg histedit --continue to resume'),
806 hint=hint % node.short(self.node),
806 )
807 )
807
808
808 def commiteditor(self):
809 def commiteditor(self):
809 return cmdutil.getcommiteditor(edit=True, editform=b'histedit.edit')
810 return cmdutil.getcommiteditor(edit=True, editform=b'histedit.edit')
810
811
811
812
812 @action([b'fold', b'f'], _(b'use commit, but combine it with the one above'))
813 @action([b'fold', b'f'], _(b'use commit, but combine it with the one above'))
813 class fold(histeditaction):
814 class fold(histeditaction):
814 def verify(self, prev, expected, seen):
815 def verify(self, prev, expected, seen):
815 """ Verifies semantic correctness of the fold rule"""
816 """ Verifies semantic correctness of the fold rule"""
816 super(fold, self).verify(prev, expected, seen)
817 super(fold, self).verify(prev, expected, seen)
817 repo = self.repo
818 repo = self.repo
818 if not prev:
819 if not prev:
819 c = repo[self.node].p1()
820 c = repo[self.node].p1()
820 elif not prev.verb in (b'pick', b'base'):
821 elif not prev.verb in (b'pick', b'base'):
821 return
822 return
822 else:
823 else:
823 c = repo[prev.node]
824 c = repo[prev.node]
824 if not c.mutable():
825 if not c.mutable():
825 raise error.ParseError(
826 raise error.ParseError(
826 _(b"cannot fold into public change %s") % node.short(c.node())
827 _(b"cannot fold into public change %s") % node.short(c.node())
827 )
828 )
828
829
829 def continuedirty(self):
830 def continuedirty(self):
830 repo = self.repo
831 repo = self.repo
831 rulectx = repo[self.node]
832 rulectx = repo[self.node]
832
833
833 commit = commitfuncfor(repo, rulectx)
834 commit = commitfuncfor(repo, rulectx)
834 commit(
835 commit(
835 text=b'fold-temp-revision %s' % node.short(self.node),
836 text=b'fold-temp-revision %s' % node.short(self.node),
836 user=rulectx.user(),
837 user=rulectx.user(),
837 date=rulectx.date(),
838 date=rulectx.date(),
838 extra=rulectx.extra(),
839 extra=rulectx.extra(),
839 )
840 )
840
841
841 def continueclean(self):
842 def continueclean(self):
842 repo = self.repo
843 repo = self.repo
843 ctx = repo[b'.']
844 ctx = repo[b'.']
844 rulectx = repo[self.node]
845 rulectx = repo[self.node]
845 parentctxnode = self.state.parentctxnode
846 parentctxnode = self.state.parentctxnode
846 if ctx.node() == parentctxnode:
847 if ctx.node() == parentctxnode:
847 repo.ui.warn(_(b'%s: empty changeset\n') % node.short(self.node))
848 repo.ui.warn(_(b'%s: empty changeset\n') % node.short(self.node))
848 return ctx, [(self.node, (parentctxnode,))]
849 return ctx, [(self.node, (parentctxnode,))]
849
850
850 parentctx = repo[parentctxnode]
851 parentctx = repo[parentctxnode]
851 newcommits = {
852 newcommits = {
852 c.node()
853 c.node()
853 for c in repo.set(b'(%d::. - %d)', parentctx.rev(), parentctx.rev())
854 for c in repo.set(b'(%d::. - %d)', parentctx.rev(), parentctx.rev())
854 }
855 }
855 if not newcommits:
856 if not newcommits:
856 repo.ui.warn(
857 repo.ui.warn(
857 _(
858 _(
858 b'%s: cannot fold - working copy is not a '
859 b'%s: cannot fold - working copy is not a '
859 b'descendant of previous commit %s\n'
860 b'descendant of previous commit %s\n'
860 )
861 )
861 % (node.short(self.node), node.short(parentctxnode))
862 % (node.short(self.node), node.short(parentctxnode))
862 )
863 )
863 return ctx, [(self.node, (ctx.node(),))]
864 return ctx, [(self.node, (ctx.node(),))]
864
865
865 middlecommits = newcommits.copy()
866 middlecommits = newcommits.copy()
866 middlecommits.discard(ctx.node())
867 middlecommits.discard(ctx.node())
867
868
868 return self.finishfold(
869 return self.finishfold(
869 repo.ui, repo, parentctx, rulectx, ctx.node(), middlecommits
870 repo.ui, repo, parentctx, rulectx, ctx.node(), middlecommits
870 )
871 )
871
872
872 def skipprompt(self):
873 def skipprompt(self):
873 """Returns true if the rule should skip the message editor.
874 """Returns true if the rule should skip the message editor.
874
875
875 For example, 'fold' wants to show an editor, but 'rollup'
876 For example, 'fold' wants to show an editor, but 'rollup'
876 doesn't want to.
877 doesn't want to.
877 """
878 """
878 return False
879 return False
879
880
880 def mergedescs(self):
881 def mergedescs(self):
881 """Returns true if the rule should merge messages of multiple changes.
882 """Returns true if the rule should merge messages of multiple changes.
882
883
883 This exists mainly so that 'rollup' rules can be a subclass of
884 This exists mainly so that 'rollup' rules can be a subclass of
884 'fold'.
885 'fold'.
885 """
886 """
886 return True
887 return True
887
888
888 def firstdate(self):
889 def firstdate(self):
889 """Returns true if the rule should preserve the date of the first
890 """Returns true if the rule should preserve the date of the first
890 change.
891 change.
891
892
892 This exists mainly so that 'rollup' rules can be a subclass of
893 This exists mainly so that 'rollup' rules can be a subclass of
893 'fold'.
894 'fold'.
894 """
895 """
895 return False
896 return False
896
897
897 def finishfold(self, ui, repo, ctx, oldctx, newnode, internalchanges):
898 def finishfold(self, ui, repo, ctx, oldctx, newnode, internalchanges):
898 mergemod.update(ctx.p1())
899 mergemod.update(ctx.p1())
899 ### prepare new commit data
900 ### prepare new commit data
900 commitopts = {}
901 commitopts = {}
901 commitopts[b'user'] = ctx.user()
902 commitopts[b'user'] = ctx.user()
902 # commit message
903 # commit message
903 if not self.mergedescs():
904 if not self.mergedescs():
904 newmessage = ctx.description()
905 newmessage = ctx.description()
905 else:
906 else:
906 newmessage = (
907 newmessage = (
907 b'\n***\n'.join(
908 b'\n***\n'.join(
908 [ctx.description()]
909 [ctx.description()]
909 + [repo[r].description() for r in internalchanges]
910 + [repo[r].description() for r in internalchanges]
910 + [oldctx.description()]
911 + [oldctx.description()]
911 )
912 )
912 + b'\n'
913 + b'\n'
913 )
914 )
914 commitopts[b'message'] = newmessage
915 commitopts[b'message'] = newmessage
915 # date
916 # date
916 if self.firstdate():
917 if self.firstdate():
917 commitopts[b'date'] = ctx.date()
918 commitopts[b'date'] = ctx.date()
918 else:
919 else:
919 commitopts[b'date'] = max(ctx.date(), oldctx.date())
920 commitopts[b'date'] = max(ctx.date(), oldctx.date())
920 # if date is to be updated to current
921 # if date is to be updated to current
921 if ui.configbool(b'rewrite', b'update-timestamp'):
922 if ui.configbool(b'rewrite', b'update-timestamp'):
922 commitopts[b'date'] = dateutil.makedate()
923 commitopts[b'date'] = dateutil.makedate()
923
924
924 extra = ctx.extra().copy()
925 extra = ctx.extra().copy()
925 # histedit_source
926 # histedit_source
926 # note: ctx is likely a temporary commit but that the best we can do
927 # note: ctx is likely a temporary commit but that the best we can do
927 # here. This is sufficient to solve issue3681 anyway.
928 # here. This is sufficient to solve issue3681 anyway.
928 extra[b'histedit_source'] = b'%s,%s' % (ctx.hex(), oldctx.hex())
929 extra[b'histedit_source'] = b'%s,%s' % (ctx.hex(), oldctx.hex())
929 commitopts[b'extra'] = extra
930 commitopts[b'extra'] = extra
930 phasemin = max(ctx.phase(), oldctx.phase())
931 phasemin = max(ctx.phase(), oldctx.phase())
931 overrides = {(b'phases', b'new-commit'): phasemin}
932 overrides = {(b'phases', b'new-commit'): phasemin}
932 with repo.ui.configoverride(overrides, b'histedit'):
933 with repo.ui.configoverride(overrides, b'histedit'):
933 n = collapse(
934 n = collapse(
934 repo,
935 repo,
935 ctx,
936 ctx,
936 repo[newnode],
937 repo[newnode],
937 commitopts,
938 commitopts,
938 skipprompt=self.skipprompt(),
939 skipprompt=self.skipprompt(),
939 )
940 )
940 if n is None:
941 if n is None:
941 return ctx, []
942 return ctx, []
942 mergemod.update(repo[n])
943 mergemod.update(repo[n])
943 replacements = [
944 replacements = [
944 (oldctx.node(), (newnode,)),
945 (oldctx.node(), (newnode,)),
945 (ctx.node(), (n,)),
946 (ctx.node(), (n,)),
946 (newnode, (n,)),
947 (newnode, (n,)),
947 ]
948 ]
948 for ich in internalchanges:
949 for ich in internalchanges:
949 replacements.append((ich, (n,)))
950 replacements.append((ich, (n,)))
950 return repo[n], replacements
951 return repo[n], replacements
951
952
952
953
953 @action(
954 @action(
954 [b'base', b'b'],
955 [b'base', b'b'],
955 _(b'checkout changeset and apply further changesets from there'),
956 _(b'checkout changeset and apply further changesets from there'),
956 )
957 )
957 class base(histeditaction):
958 class base(histeditaction):
958 def run(self):
959 def run(self):
959 if self.repo[b'.'].node() != self.node:
960 if self.repo[b'.'].node() != self.node:
960 mergemod.clean_update(self.repo[self.node])
961 mergemod.clean_update(self.repo[self.node])
961 return self.continueclean()
962 return self.continueclean()
962
963
963 def continuedirty(self):
964 def continuedirty(self):
964 abortdirty()
965 abortdirty()
965
966
966 def continueclean(self):
967 def continueclean(self):
967 basectx = self.repo[b'.']
968 basectx = self.repo[b'.']
968 return basectx, []
969 return basectx, []
969
970
970 def _verifynodeconstraints(self, prev, expected, seen):
971 def _verifynodeconstraints(self, prev, expected, seen):
971 # base can only be use with a node not in the edited set
972 # base can only be use with a node not in the edited set
972 if self.node in expected:
973 if self.node in expected:
973 msg = _(b'%s "%s" changeset was an edited list candidate')
974 msg = _(b'%s "%s" changeset was an edited list candidate')
974 raise error.ParseError(
975 raise error.ParseError(
975 msg % (self.verb, node.short(self.node)),
976 msg % (self.verb, node.short(self.node)),
976 hint=_(b'base must only use unlisted changesets'),
977 hint=_(b'base must only use unlisted changesets'),
977 )
978 )
978
979
979
980
980 @action(
981 @action(
981 [b'_multifold'],
982 [b'_multifold'],
982 _(
983 _(
983 """fold subclass used for when multiple folds happen in a row
984 """fold subclass used for when multiple folds happen in a row
984
985
985 We only want to fire the editor for the folded message once when
986 We only want to fire the editor for the folded message once when
986 (say) four changes are folded down into a single change. This is
987 (say) four changes are folded down into a single change. This is
987 similar to rollup, but we should preserve both messages so that
988 similar to rollup, but we should preserve both messages so that
988 when the last fold operation runs we can show the user all the
989 when the last fold operation runs we can show the user all the
989 commit messages in their editor.
990 commit messages in their editor.
990 """
991 """
991 ),
992 ),
992 internal=True,
993 internal=True,
993 )
994 )
994 class _multifold(fold):
995 class _multifold(fold):
995 def skipprompt(self):
996 def skipprompt(self):
996 return True
997 return True
997
998
998
999
999 @action(
1000 @action(
1000 [b"roll", b"r"],
1001 [b"roll", b"r"],
1001 _(b"like fold, but discard this commit's description and date"),
1002 _(b"like fold, but discard this commit's description and date"),
1002 )
1003 )
1003 class rollup(fold):
1004 class rollup(fold):
1004 def mergedescs(self):
1005 def mergedescs(self):
1005 return False
1006 return False
1006
1007
1007 def skipprompt(self):
1008 def skipprompt(self):
1008 return True
1009 return True
1009
1010
1010 def firstdate(self):
1011 def firstdate(self):
1011 return True
1012 return True
1012
1013
1013
1014
1014 @action([b"drop", b"d"], _(b'remove commit from history'))
1015 @action([b"drop", b"d"], _(b'remove commit from history'))
1015 class drop(histeditaction):
1016 class drop(histeditaction):
1016 def run(self):
1017 def run(self):
1017 parentctx = self.repo[self.state.parentctxnode]
1018 parentctx = self.repo[self.state.parentctxnode]
1018 return parentctx, [(self.node, tuple())]
1019 return parentctx, [(self.node, tuple())]
1019
1020
1020
1021
1021 @action(
1022 @action(
1022 [b"mess", b"m"],
1023 [b"mess", b"m"],
1023 _(b'edit commit message without changing commit content'),
1024 _(b'edit commit message without changing commit content'),
1024 priority=True,
1025 priority=True,
1025 )
1026 )
1026 class message(histeditaction):
1027 class message(histeditaction):
1027 def commiteditor(self):
1028 def commiteditor(self):
1028 return cmdutil.getcommiteditor(edit=True, editform=b'histedit.mess')
1029 return cmdutil.getcommiteditor(edit=True, editform=b'histedit.mess')
1029
1030
1030
1031
1031 def findoutgoing(ui, repo, remote=None, force=False, opts=None):
1032 def findoutgoing(ui, repo, remote=None, force=False, opts=None):
1032 """utility function to find the first outgoing changeset
1033 """utility function to find the first outgoing changeset
1033
1034
1034 Used by initialization code"""
1035 Used by initialization code"""
1035 if opts is None:
1036 if opts is None:
1036 opts = {}
1037 opts = {}
1037 dest = ui.expandpath(remote or b'default-push', remote or b'default')
1038 dest = ui.expandpath(remote or b'default-push', remote or b'default')
1038 dest, branches = hg.parseurl(dest, None)[:2]
1039 dest, branches = hg.parseurl(dest, None)[:2]
1039 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
1040 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
1040
1041
1041 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
1042 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
1042 other = hg.peer(repo, opts, dest)
1043 other = hg.peer(repo, opts, dest)
1043
1044
1044 if revs:
1045 if revs:
1045 revs = [repo.lookup(rev) for rev in revs]
1046 revs = [repo.lookup(rev) for rev in revs]
1046
1047
1047 outgoing = discovery.findcommonoutgoing(repo, other, revs, force=force)
1048 outgoing = discovery.findcommonoutgoing(repo, other, revs, force=force)
1048 if not outgoing.missing:
1049 if not outgoing.missing:
1049 raise error.Abort(_(b'no outgoing ancestors'))
1050 raise error.Abort(_(b'no outgoing ancestors'))
1050 roots = list(repo.revs(b"roots(%ln)", outgoing.missing))
1051 roots = list(repo.revs(b"roots(%ln)", outgoing.missing))
1051 if len(roots) > 1:
1052 if len(roots) > 1:
1052 msg = _(b'there are ambiguous outgoing revisions')
1053 msg = _(b'there are ambiguous outgoing revisions')
1053 hint = _(b"see 'hg help histedit' for more detail")
1054 hint = _(b"see 'hg help histedit' for more detail")
1054 raise error.Abort(msg, hint=hint)
1055 raise error.Abort(msg, hint=hint)
1055 return repo[roots[0]].node()
1056 return repo[roots[0]].node()
1056
1057
1057
1058
1058 # Curses Support
1059 # Curses Support
1059 try:
1060 try:
1060 import curses
1061 import curses
1061 except ImportError:
1062 except ImportError:
1062 curses = None
1063 curses = None
1063
1064
1064 KEY_LIST = [b'pick', b'edit', b'fold', b'drop', b'mess', b'roll']
1065 KEY_LIST = [b'pick', b'edit', b'fold', b'drop', b'mess', b'roll']
1065 ACTION_LABELS = {
1066 ACTION_LABELS = {
1066 b'fold': b'^fold',
1067 b'fold': b'^fold',
1067 b'roll': b'^roll',
1068 b'roll': b'^roll',
1068 }
1069 }
1069
1070
1070 COLOR_HELP, COLOR_SELECTED, COLOR_OK, COLOR_WARN, COLOR_CURRENT = 1, 2, 3, 4, 5
1071 COLOR_HELP, COLOR_SELECTED, COLOR_OK, COLOR_WARN, COLOR_CURRENT = 1, 2, 3, 4, 5
1071 COLOR_DIFF_ADD_LINE, COLOR_DIFF_DEL_LINE, COLOR_DIFF_OFFSET = 6, 7, 8
1072 COLOR_DIFF_ADD_LINE, COLOR_DIFF_DEL_LINE, COLOR_DIFF_OFFSET = 6, 7, 8
1072 COLOR_ROLL, COLOR_ROLL_CURRENT, COLOR_ROLL_SELECTED = 9, 10, 11
1073 COLOR_ROLL, COLOR_ROLL_CURRENT, COLOR_ROLL_SELECTED = 9, 10, 11
1073
1074
1074 E_QUIT, E_HISTEDIT = 1, 2
1075 E_QUIT, E_HISTEDIT = 1, 2
1075 E_PAGEDOWN, E_PAGEUP, E_LINEUP, E_LINEDOWN, E_RESIZE = 3, 4, 5, 6, 7
1076 E_PAGEDOWN, E_PAGEUP, E_LINEUP, E_LINEDOWN, E_RESIZE = 3, 4, 5, 6, 7
1076 MODE_INIT, MODE_PATCH, MODE_RULES, MODE_HELP = 0, 1, 2, 3
1077 MODE_INIT, MODE_PATCH, MODE_RULES, MODE_HELP = 0, 1, 2, 3
1077
1078
1078 KEYTABLE = {
1079 KEYTABLE = {
1079 b'global': {
1080 b'global': {
1080 b'h': b'next-action',
1081 b'h': b'next-action',
1081 b'KEY_RIGHT': b'next-action',
1082 b'KEY_RIGHT': b'next-action',
1082 b'l': b'prev-action',
1083 b'l': b'prev-action',
1083 b'KEY_LEFT': b'prev-action',
1084 b'KEY_LEFT': b'prev-action',
1084 b'q': b'quit',
1085 b'q': b'quit',
1085 b'c': b'histedit',
1086 b'c': b'histedit',
1086 b'C': b'histedit',
1087 b'C': b'histedit',
1087 b'v': b'showpatch',
1088 b'v': b'showpatch',
1088 b'?': b'help',
1089 b'?': b'help',
1089 },
1090 },
1090 MODE_RULES: {
1091 MODE_RULES: {
1091 b'd': b'action-drop',
1092 b'd': b'action-drop',
1092 b'e': b'action-edit',
1093 b'e': b'action-edit',
1093 b'f': b'action-fold',
1094 b'f': b'action-fold',
1094 b'm': b'action-mess',
1095 b'm': b'action-mess',
1095 b'p': b'action-pick',
1096 b'p': b'action-pick',
1096 b'r': b'action-roll',
1097 b'r': b'action-roll',
1097 b' ': b'select',
1098 b' ': b'select',
1098 b'j': b'down',
1099 b'j': b'down',
1099 b'k': b'up',
1100 b'k': b'up',
1100 b'KEY_DOWN': b'down',
1101 b'KEY_DOWN': b'down',
1101 b'KEY_UP': b'up',
1102 b'KEY_UP': b'up',
1102 b'J': b'move-down',
1103 b'J': b'move-down',
1103 b'K': b'move-up',
1104 b'K': b'move-up',
1104 b'KEY_NPAGE': b'move-down',
1105 b'KEY_NPAGE': b'move-down',
1105 b'KEY_PPAGE': b'move-up',
1106 b'KEY_PPAGE': b'move-up',
1106 b'0': b'goto', # Used for 0..9
1107 b'0': b'goto', # Used for 0..9
1107 },
1108 },
1108 MODE_PATCH: {
1109 MODE_PATCH: {
1109 b' ': b'page-down',
1110 b' ': b'page-down',
1110 b'KEY_NPAGE': b'page-down',
1111 b'KEY_NPAGE': b'page-down',
1111 b'KEY_PPAGE': b'page-up',
1112 b'KEY_PPAGE': b'page-up',
1112 b'j': b'line-down',
1113 b'j': b'line-down',
1113 b'k': b'line-up',
1114 b'k': b'line-up',
1114 b'KEY_DOWN': b'line-down',
1115 b'KEY_DOWN': b'line-down',
1115 b'KEY_UP': b'line-up',
1116 b'KEY_UP': b'line-up',
1116 b'J': b'down',
1117 b'J': b'down',
1117 b'K': b'up',
1118 b'K': b'up',
1118 },
1119 },
1119 MODE_HELP: {},
1120 MODE_HELP: {},
1120 }
1121 }
1121
1122
1122
1123
1123 def screen_size():
1124 def screen_size():
1124 return struct.unpack(b'hh', fcntl.ioctl(1, termios.TIOCGWINSZ, b' '))
1125 return struct.unpack(b'hh', fcntl.ioctl(1, termios.TIOCGWINSZ, b' '))
1125
1126
1126
1127
1127 class histeditrule(object):
1128 class histeditrule(object):
1128 def __init__(self, ui, ctx, pos, action=b'pick'):
1129 def __init__(self, ui, ctx, pos, action=b'pick'):
1129 self.ui = ui
1130 self.ui = ui
1130 self.ctx = ctx
1131 self.ctx = ctx
1131 self.action = action
1132 self.action = action
1132 self.origpos = pos
1133 self.origpos = pos
1133 self.pos = pos
1134 self.pos = pos
1134 self.conflicts = []
1135 self.conflicts = []
1135
1136
1136 def __bytes__(self):
1137 def __bytes__(self):
1137 # Example display of several histeditrules:
1138 # Example display of several histeditrules:
1138 #
1139 #
1139 # #10 pick 316392:06a16c25c053 add option to skip tests
1140 # #10 pick 316392:06a16c25c053 add option to skip tests
1140 # #11 ^roll 316393:71313c964cc5 <RED>oops a fixup commit</RED>
1141 # #11 ^roll 316393:71313c964cc5 <RED>oops a fixup commit</RED>
1141 # #12 pick 316394:ab31f3973b0d include mfbt for mozilla-config.h
1142 # #12 pick 316394:ab31f3973b0d include mfbt for mozilla-config.h
1142 # #13 ^fold 316395:14ce5803f4c3 fix warnings
1143 # #13 ^fold 316395:14ce5803f4c3 fix warnings
1143 #
1144 #
1144 # The carets point to the changeset being folded into ("roll this
1145 # The carets point to the changeset being folded into ("roll this
1145 # changeset into the changeset above").
1146 # changeset into the changeset above").
1146 return b'%s%s' % (self.prefix, self.desc)
1147 return b'%s%s' % (self.prefix, self.desc)
1147
1148
1148 __str__ = encoding.strmethod(__bytes__)
1149 __str__ = encoding.strmethod(__bytes__)
1149
1150
1150 @property
1151 @property
1151 def prefix(self):
1152 def prefix(self):
1152 # Some actions ('fold' and 'roll') combine a patch with a
1153 # Some actions ('fold' and 'roll') combine a patch with a
1153 # previous one. Add a marker showing which patch they apply
1154 # previous one. Add a marker showing which patch they apply
1154 # to.
1155 # to.
1155 action = ACTION_LABELS.get(self.action, self.action)
1156 action = ACTION_LABELS.get(self.action, self.action)
1156
1157
1157 h = self.ctx.hex()[0:12]
1158 h = self.ctx.hex()[0:12]
1158 r = self.ctx.rev()
1159 r = self.ctx.rev()
1159
1160
1160 return b"#%s %s %d:%s " % (
1161 return b"#%s %s %d:%s " % (
1161 (b'%d' % self.origpos).ljust(2),
1162 (b'%d' % self.origpos).ljust(2),
1162 action.ljust(6),
1163 action.ljust(6),
1163 r,
1164 r,
1164 h,
1165 h,
1165 )
1166 )
1166
1167
1167 @util.propertycache
1168 @util.propertycache
1168 def desc(self):
1169 def desc(self):
1169 summary = cmdutil.rendertemplate(
1170 summary = cmdutil.rendertemplate(
1170 self.ctx, self.ui.config(b'histedit', b'summary-template')
1171 self.ctx, self.ui.config(b'histedit', b'summary-template')
1171 )
1172 )
1172 if summary:
1173 if summary:
1173 return summary
1174 return summary
1174 # This is split off from the prefix property so that we can
1175 # This is split off from the prefix property so that we can
1175 # separately make the description for 'roll' red (since it
1176 # separately make the description for 'roll' red (since it
1176 # will get discarded).
1177 # will get discarded).
1177 return self.ctx.description().splitlines()[0].strip()
1178 return self.ctx.description().splitlines()[0].strip()
1178
1179
1179 def checkconflicts(self, other):
1180 def checkconflicts(self, other):
1180 if other.pos > self.pos and other.origpos <= self.origpos:
1181 if other.pos > self.pos and other.origpos <= self.origpos:
1181 if set(other.ctx.files()) & set(self.ctx.files()) != set():
1182 if set(other.ctx.files()) & set(self.ctx.files()) != set():
1182 self.conflicts.append(other)
1183 self.conflicts.append(other)
1183 return self.conflicts
1184 return self.conflicts
1184
1185
1185 if other in self.conflicts:
1186 if other in self.conflicts:
1186 self.conflicts.remove(other)
1187 self.conflicts.remove(other)
1187 return self.conflicts
1188 return self.conflicts
1188
1189
1189
1190
1190 # ============ EVENTS ===============
1191 # ============ EVENTS ===============
1191 def movecursor(state, oldpos, newpos):
1192 def movecursor(state, oldpos, newpos):
1192 """Change the rule/changeset that the cursor is pointing to, regardless of
1193 """Change the rule/changeset that the cursor is pointing to, regardless of
1193 current mode (you can switch between patches from the view patch window)."""
1194 current mode (you can switch between patches from the view patch window)."""
1194 state[b'pos'] = newpos
1195 state[b'pos'] = newpos
1195
1196
1196 mode, _ = state[b'mode']
1197 mode, _ = state[b'mode']
1197 if mode == MODE_RULES:
1198 if mode == MODE_RULES:
1198 # Scroll through the list by updating the view for MODE_RULES, so that
1199 # Scroll through the list by updating the view for MODE_RULES, so that
1199 # even if we are not currently viewing the rules, switching back will
1200 # even if we are not currently viewing the rules, switching back will
1200 # result in the cursor's rule being visible.
1201 # result in the cursor's rule being visible.
1201 modestate = state[b'modes'][MODE_RULES]
1202 modestate = state[b'modes'][MODE_RULES]
1202 if newpos < modestate[b'line_offset']:
1203 if newpos < modestate[b'line_offset']:
1203 modestate[b'line_offset'] = newpos
1204 modestate[b'line_offset'] = newpos
1204 elif newpos > modestate[b'line_offset'] + state[b'page_height'] - 1:
1205 elif newpos > modestate[b'line_offset'] + state[b'page_height'] - 1:
1205 modestate[b'line_offset'] = newpos - state[b'page_height'] + 1
1206 modestate[b'line_offset'] = newpos - state[b'page_height'] + 1
1206
1207
1207 # Reset the patch view region to the top of the new patch.
1208 # Reset the patch view region to the top of the new patch.
1208 state[b'modes'][MODE_PATCH][b'line_offset'] = 0
1209 state[b'modes'][MODE_PATCH][b'line_offset'] = 0
1209
1210
1210
1211
1211 def changemode(state, mode):
1212 def changemode(state, mode):
1212 curmode, _ = state[b'mode']
1213 curmode, _ = state[b'mode']
1213 state[b'mode'] = (mode, curmode)
1214 state[b'mode'] = (mode, curmode)
1214 if mode == MODE_PATCH:
1215 if mode == MODE_PATCH:
1215 state[b'modes'][MODE_PATCH][b'patchcontents'] = patchcontents(state)
1216 state[b'modes'][MODE_PATCH][b'patchcontents'] = patchcontents(state)
1216
1217
1217
1218
1218 def makeselection(state, pos):
1219 def makeselection(state, pos):
1219 state[b'selected'] = pos
1220 state[b'selected'] = pos
1220
1221
1221
1222
1222 def swap(state, oldpos, newpos):
1223 def swap(state, oldpos, newpos):
1223 """Swap two positions and calculate necessary conflicts in
1224 """Swap two positions and calculate necessary conflicts in
1224 O(|newpos-oldpos|) time"""
1225 O(|newpos-oldpos|) time"""
1225
1226
1226 rules = state[b'rules']
1227 rules = state[b'rules']
1227 assert 0 <= oldpos < len(rules) and 0 <= newpos < len(rules)
1228 assert 0 <= oldpos < len(rules) and 0 <= newpos < len(rules)
1228
1229
1229 rules[oldpos], rules[newpos] = rules[newpos], rules[oldpos]
1230 rules[oldpos], rules[newpos] = rules[newpos], rules[oldpos]
1230
1231
1231 # TODO: swap should not know about histeditrule's internals
1232 # TODO: swap should not know about histeditrule's internals
1232 rules[newpos].pos = newpos
1233 rules[newpos].pos = newpos
1233 rules[oldpos].pos = oldpos
1234 rules[oldpos].pos = oldpos
1234
1235
1235 start = min(oldpos, newpos)
1236 start = min(oldpos, newpos)
1236 end = max(oldpos, newpos)
1237 end = max(oldpos, newpos)
1237 for r in pycompat.xrange(start, end + 1):
1238 for r in pycompat.xrange(start, end + 1):
1238 rules[newpos].checkconflicts(rules[r])
1239 rules[newpos].checkconflicts(rules[r])
1239 rules[oldpos].checkconflicts(rules[r])
1240 rules[oldpos].checkconflicts(rules[r])
1240
1241
1241 if state[b'selected']:
1242 if state[b'selected']:
1242 makeselection(state, newpos)
1243 makeselection(state, newpos)
1243
1244
1244
1245
1245 def changeaction(state, pos, action):
1246 def changeaction(state, pos, action):
1246 """Change the action state on the given position to the new action"""
1247 """Change the action state on the given position to the new action"""
1247 rules = state[b'rules']
1248 rules = state[b'rules']
1248 assert 0 <= pos < len(rules)
1249 assert 0 <= pos < len(rules)
1249 rules[pos].action = action
1250 rules[pos].action = action
1250
1251
1251
1252
1252 def cycleaction(state, pos, next=False):
1253 def cycleaction(state, pos, next=False):
1253 """Changes the action state the next or the previous action from
1254 """Changes the action state the next or the previous action from
1254 the action list"""
1255 the action list"""
1255 rules = state[b'rules']
1256 rules = state[b'rules']
1256 assert 0 <= pos < len(rules)
1257 assert 0 <= pos < len(rules)
1257 current = rules[pos].action
1258 current = rules[pos].action
1258
1259
1259 assert current in KEY_LIST
1260 assert current in KEY_LIST
1260
1261
1261 index = KEY_LIST.index(current)
1262 index = KEY_LIST.index(current)
1262 if next:
1263 if next:
1263 index += 1
1264 index += 1
1264 else:
1265 else:
1265 index -= 1
1266 index -= 1
1266 changeaction(state, pos, KEY_LIST[index % len(KEY_LIST)])
1267 changeaction(state, pos, KEY_LIST[index % len(KEY_LIST)])
1267
1268
1268
1269
1269 def changeview(state, delta, unit):
1270 def changeview(state, delta, unit):
1270 """Change the region of whatever is being viewed (a patch or the list of
1271 """Change the region of whatever is being viewed (a patch or the list of
1271 changesets). 'delta' is an amount (+/- 1) and 'unit' is 'page' or 'line'."""
1272 changesets). 'delta' is an amount (+/- 1) and 'unit' is 'page' or 'line'."""
1272 mode, _ = state[b'mode']
1273 mode, _ = state[b'mode']
1273 if mode != MODE_PATCH:
1274 if mode != MODE_PATCH:
1274 return
1275 return
1275 mode_state = state[b'modes'][mode]
1276 mode_state = state[b'modes'][mode]
1276 num_lines = len(mode_state[b'patchcontents'])
1277 num_lines = len(mode_state[b'patchcontents'])
1277 page_height = state[b'page_height']
1278 page_height = state[b'page_height']
1278 unit = page_height if unit == b'page' else 1
1279 unit = page_height if unit == b'page' else 1
1279 num_pages = 1 + (num_lines - 1) // page_height
1280 num_pages = 1 + (num_lines - 1) // page_height
1280 max_offset = (num_pages - 1) * page_height
1281 max_offset = (num_pages - 1) * page_height
1281 newline = mode_state[b'line_offset'] + delta * unit
1282 newline = mode_state[b'line_offset'] + delta * unit
1282 mode_state[b'line_offset'] = max(0, min(max_offset, newline))
1283 mode_state[b'line_offset'] = max(0, min(max_offset, newline))
1283
1284
1284
1285
1285 def event(state, ch):
1286 def event(state, ch):
1286 """Change state based on the current character input
1287 """Change state based on the current character input
1287
1288
1288 This takes the current state and based on the current character input from
1289 This takes the current state and based on the current character input from
1289 the user we change the state.
1290 the user we change the state.
1290 """
1291 """
1291 selected = state[b'selected']
1292 selected = state[b'selected']
1292 oldpos = state[b'pos']
1293 oldpos = state[b'pos']
1293 rules = state[b'rules']
1294 rules = state[b'rules']
1294
1295
1295 if ch in (curses.KEY_RESIZE, b"KEY_RESIZE"):
1296 if ch in (curses.KEY_RESIZE, b"KEY_RESIZE"):
1296 return E_RESIZE
1297 return E_RESIZE
1297
1298
1298 lookup_ch = ch
1299 lookup_ch = ch
1299 if ch is not None and b'0' <= ch <= b'9':
1300 if ch is not None and b'0' <= ch <= b'9':
1300 lookup_ch = b'0'
1301 lookup_ch = b'0'
1301
1302
1302 curmode, prevmode = state[b'mode']
1303 curmode, prevmode = state[b'mode']
1303 action = KEYTABLE[curmode].get(
1304 action = KEYTABLE[curmode].get(
1304 lookup_ch, KEYTABLE[b'global'].get(lookup_ch)
1305 lookup_ch, KEYTABLE[b'global'].get(lookup_ch)
1305 )
1306 )
1306 if action is None:
1307 if action is None:
1307 return
1308 return
1308 if action in (b'down', b'move-down'):
1309 if action in (b'down', b'move-down'):
1309 newpos = min(oldpos + 1, len(rules) - 1)
1310 newpos = min(oldpos + 1, len(rules) - 1)
1310 movecursor(state, oldpos, newpos)
1311 movecursor(state, oldpos, newpos)
1311 if selected is not None or action == b'move-down':
1312 if selected is not None or action == b'move-down':
1312 swap(state, oldpos, newpos)
1313 swap(state, oldpos, newpos)
1313 elif action in (b'up', b'move-up'):
1314 elif action in (b'up', b'move-up'):
1314 newpos = max(0, oldpos - 1)
1315 newpos = max(0, oldpos - 1)
1315 movecursor(state, oldpos, newpos)
1316 movecursor(state, oldpos, newpos)
1316 if selected is not None or action == b'move-up':
1317 if selected is not None or action == b'move-up':
1317 swap(state, oldpos, newpos)
1318 swap(state, oldpos, newpos)
1318 elif action == b'next-action':
1319 elif action == b'next-action':
1319 cycleaction(state, oldpos, next=True)
1320 cycleaction(state, oldpos, next=True)
1320 elif action == b'prev-action':
1321 elif action == b'prev-action':
1321 cycleaction(state, oldpos, next=False)
1322 cycleaction(state, oldpos, next=False)
1322 elif action == b'select':
1323 elif action == b'select':
1323 selected = oldpos if selected is None else None
1324 selected = oldpos if selected is None else None
1324 makeselection(state, selected)
1325 makeselection(state, selected)
1325 elif action == b'goto' and int(ch) < len(rules) and len(rules) <= 10:
1326 elif action == b'goto' and int(ch) < len(rules) and len(rules) <= 10:
1326 newrule = next((r for r in rules if r.origpos == int(ch)))
1327 newrule = next((r for r in rules if r.origpos == int(ch)))
1327 movecursor(state, oldpos, newrule.pos)
1328 movecursor(state, oldpos, newrule.pos)
1328 if selected is not None:
1329 if selected is not None:
1329 swap(state, oldpos, newrule.pos)
1330 swap(state, oldpos, newrule.pos)
1330 elif action.startswith(b'action-'):
1331 elif action.startswith(b'action-'):
1331 changeaction(state, oldpos, action[7:])
1332 changeaction(state, oldpos, action[7:])
1332 elif action == b'showpatch':
1333 elif action == b'showpatch':
1333 changemode(state, MODE_PATCH if curmode != MODE_PATCH else prevmode)
1334 changemode(state, MODE_PATCH if curmode != MODE_PATCH else prevmode)
1334 elif action == b'help':
1335 elif action == b'help':
1335 changemode(state, MODE_HELP if curmode != MODE_HELP else prevmode)
1336 changemode(state, MODE_HELP if curmode != MODE_HELP else prevmode)
1336 elif action == b'quit':
1337 elif action == b'quit':
1337 return E_QUIT
1338 return E_QUIT
1338 elif action == b'histedit':
1339 elif action == b'histedit':
1339 return E_HISTEDIT
1340 return E_HISTEDIT
1340 elif action == b'page-down':
1341 elif action == b'page-down':
1341 return E_PAGEDOWN
1342 return E_PAGEDOWN
1342 elif action == b'page-up':
1343 elif action == b'page-up':
1343 return E_PAGEUP
1344 return E_PAGEUP
1344 elif action == b'line-down':
1345 elif action == b'line-down':
1345 return E_LINEDOWN
1346 return E_LINEDOWN
1346 elif action == b'line-up':
1347 elif action == b'line-up':
1347 return E_LINEUP
1348 return E_LINEUP
1348
1349
1349
1350
1350 def makecommands(rules):
1351 def makecommands(rules):
1351 """Returns a list of commands consumable by histedit --commands based on
1352 """Returns a list of commands consumable by histedit --commands based on
1352 our list of rules"""
1353 our list of rules"""
1353 commands = []
1354 commands = []
1354 for rules in rules:
1355 for rules in rules:
1355 commands.append(b'%s %s\n' % (rules.action, rules.ctx))
1356 commands.append(b'%s %s\n' % (rules.action, rules.ctx))
1356 return commands
1357 return commands
1357
1358
1358
1359
1359 def addln(win, y, x, line, color=None):
1360 def addln(win, y, x, line, color=None):
1360 """Add a line to the given window left padding but 100% filled with
1361 """Add a line to the given window left padding but 100% filled with
1361 whitespace characters, so that the color appears on the whole line"""
1362 whitespace characters, so that the color appears on the whole line"""
1362 maxy, maxx = win.getmaxyx()
1363 maxy, maxx = win.getmaxyx()
1363 length = maxx - 1 - x
1364 length = maxx - 1 - x
1364 line = bytes(line).ljust(length)[:length]
1365 line = bytes(line).ljust(length)[:length]
1365 if y < 0:
1366 if y < 0:
1366 y = maxy + y
1367 y = maxy + y
1367 if x < 0:
1368 if x < 0:
1368 x = maxx + x
1369 x = maxx + x
1369 if color:
1370 if color:
1370 win.addstr(y, x, line, color)
1371 win.addstr(y, x, line, color)
1371 else:
1372 else:
1372 win.addstr(y, x, line)
1373 win.addstr(y, x, line)
1373
1374
1374
1375
1375 def _trunc_head(line, n):
1376 def _trunc_head(line, n):
1376 if len(line) <= n:
1377 if len(line) <= n:
1377 return line
1378 return line
1378 return b'> ' + line[-(n - 2) :]
1379 return b'> ' + line[-(n - 2) :]
1379
1380
1380
1381
1381 def _trunc_tail(line, n):
1382 def _trunc_tail(line, n):
1382 if len(line) <= n:
1383 if len(line) <= n:
1383 return line
1384 return line
1384 return line[: n - 2] + b' >'
1385 return line[: n - 2] + b' >'
1385
1386
1386
1387
1387 def patchcontents(state):
1388 def patchcontents(state):
1388 repo = state[b'repo']
1389 repo = state[b'repo']
1389 rule = state[b'rules'][state[b'pos']]
1390 rule = state[b'rules'][state[b'pos']]
1390 displayer = logcmdutil.changesetdisplayer(
1391 displayer = logcmdutil.changesetdisplayer(
1391 repo.ui, repo, {b"patch": True, b"template": b"status"}, buffered=True
1392 repo.ui, repo, {b"patch": True, b"template": b"status"}, buffered=True
1392 )
1393 )
1393 overrides = {(b'ui', b'verbose'): True}
1394 overrides = {(b'ui', b'verbose'): True}
1394 with repo.ui.configoverride(overrides, source=b'histedit'):
1395 with repo.ui.configoverride(overrides, source=b'histedit'):
1395 displayer.show(rule.ctx)
1396 displayer.show(rule.ctx)
1396 displayer.close()
1397 displayer.close()
1397 return displayer.hunk[rule.ctx.rev()].splitlines()
1398 return displayer.hunk[rule.ctx.rev()].splitlines()
1398
1399
1399
1400
1400 def _chisteditmain(repo, rules, stdscr):
1401 def _chisteditmain(repo, rules, stdscr):
1401 try:
1402 try:
1402 curses.use_default_colors()
1403 curses.use_default_colors()
1403 except curses.error:
1404 except curses.error:
1404 pass
1405 pass
1405
1406
1406 # initialize color pattern
1407 # initialize color pattern
1407 curses.init_pair(COLOR_HELP, curses.COLOR_WHITE, curses.COLOR_BLUE)
1408 curses.init_pair(COLOR_HELP, curses.COLOR_WHITE, curses.COLOR_BLUE)
1408 curses.init_pair(COLOR_SELECTED, curses.COLOR_BLACK, curses.COLOR_WHITE)
1409 curses.init_pair(COLOR_SELECTED, curses.COLOR_BLACK, curses.COLOR_WHITE)
1409 curses.init_pair(COLOR_WARN, curses.COLOR_BLACK, curses.COLOR_YELLOW)
1410 curses.init_pair(COLOR_WARN, curses.COLOR_BLACK, curses.COLOR_YELLOW)
1410 curses.init_pair(COLOR_OK, curses.COLOR_BLACK, curses.COLOR_GREEN)
1411 curses.init_pair(COLOR_OK, curses.COLOR_BLACK, curses.COLOR_GREEN)
1411 curses.init_pair(COLOR_CURRENT, curses.COLOR_WHITE, curses.COLOR_MAGENTA)
1412 curses.init_pair(COLOR_CURRENT, curses.COLOR_WHITE, curses.COLOR_MAGENTA)
1412 curses.init_pair(COLOR_DIFF_ADD_LINE, curses.COLOR_GREEN, -1)
1413 curses.init_pair(COLOR_DIFF_ADD_LINE, curses.COLOR_GREEN, -1)
1413 curses.init_pair(COLOR_DIFF_DEL_LINE, curses.COLOR_RED, -1)
1414 curses.init_pair(COLOR_DIFF_DEL_LINE, curses.COLOR_RED, -1)
1414 curses.init_pair(COLOR_DIFF_OFFSET, curses.COLOR_MAGENTA, -1)
1415 curses.init_pair(COLOR_DIFF_OFFSET, curses.COLOR_MAGENTA, -1)
1415 curses.init_pair(COLOR_ROLL, curses.COLOR_RED, -1)
1416 curses.init_pair(COLOR_ROLL, curses.COLOR_RED, -1)
1416 curses.init_pair(
1417 curses.init_pair(
1417 COLOR_ROLL_CURRENT, curses.COLOR_BLACK, curses.COLOR_MAGENTA
1418 COLOR_ROLL_CURRENT, curses.COLOR_BLACK, curses.COLOR_MAGENTA
1418 )
1419 )
1419 curses.init_pair(COLOR_ROLL_SELECTED, curses.COLOR_RED, curses.COLOR_WHITE)
1420 curses.init_pair(COLOR_ROLL_SELECTED, curses.COLOR_RED, curses.COLOR_WHITE)
1420
1421
1421 # don't display the cursor
1422 # don't display the cursor
1422 try:
1423 try:
1423 curses.curs_set(0)
1424 curses.curs_set(0)
1424 except curses.error:
1425 except curses.error:
1425 pass
1426 pass
1426
1427
1427 def rendercommit(win, state):
1428 def rendercommit(win, state):
1428 """Renders the commit window that shows the log of the current selected
1429 """Renders the commit window that shows the log of the current selected
1429 commit"""
1430 commit"""
1430 pos = state[b'pos']
1431 pos = state[b'pos']
1431 rules = state[b'rules']
1432 rules = state[b'rules']
1432 rule = rules[pos]
1433 rule = rules[pos]
1433
1434
1434 ctx = rule.ctx
1435 ctx = rule.ctx
1435 win.box()
1436 win.box()
1436
1437
1437 maxy, maxx = win.getmaxyx()
1438 maxy, maxx = win.getmaxyx()
1438 length = maxx - 3
1439 length = maxx - 3
1439
1440
1440 line = b"changeset: %d:%s" % (ctx.rev(), ctx.hex()[:12])
1441 line = b"changeset: %d:%s" % (ctx.rev(), ctx.hex()[:12])
1441 win.addstr(1, 1, line[:length])
1442 win.addstr(1, 1, line[:length])
1442
1443
1443 line = b"user: %s" % ctx.user()
1444 line = b"user: %s" % ctx.user()
1444 win.addstr(2, 1, line[:length])
1445 win.addstr(2, 1, line[:length])
1445
1446
1446 bms = repo.nodebookmarks(ctx.node())
1447 bms = repo.nodebookmarks(ctx.node())
1447 line = b"bookmark: %s" % b' '.join(bms)
1448 line = b"bookmark: %s" % b' '.join(bms)
1448 win.addstr(3, 1, line[:length])
1449 win.addstr(3, 1, line[:length])
1449
1450
1450 line = b"summary: %s" % (ctx.description().splitlines()[0])
1451 line = b"summary: %s" % (ctx.description().splitlines()[0])
1451 win.addstr(4, 1, line[:length])
1452 win.addstr(4, 1, line[:length])
1452
1453
1453 line = b"files: "
1454 line = b"files: "
1454 win.addstr(5, 1, line)
1455 win.addstr(5, 1, line)
1455 fnx = 1 + len(line)
1456 fnx = 1 + len(line)
1456 fnmaxx = length - fnx + 1
1457 fnmaxx = length - fnx + 1
1457 y = 5
1458 y = 5
1458 fnmaxn = maxy - (1 + y) - 1
1459 fnmaxn = maxy - (1 + y) - 1
1459 files = ctx.files()
1460 files = ctx.files()
1460 for i, line1 in enumerate(files):
1461 for i, line1 in enumerate(files):
1461 if len(files) > fnmaxn and i == fnmaxn - 1:
1462 if len(files) > fnmaxn and i == fnmaxn - 1:
1462 win.addstr(y, fnx, _trunc_tail(b','.join(files[i:]), fnmaxx))
1463 win.addstr(y, fnx, _trunc_tail(b','.join(files[i:]), fnmaxx))
1463 y = y + 1
1464 y = y + 1
1464 break
1465 break
1465 win.addstr(y, fnx, _trunc_head(line1, fnmaxx))
1466 win.addstr(y, fnx, _trunc_head(line1, fnmaxx))
1466 y = y + 1
1467 y = y + 1
1467
1468
1468 conflicts = rule.conflicts
1469 conflicts = rule.conflicts
1469 if len(conflicts) > 0:
1470 if len(conflicts) > 0:
1470 conflictstr = b','.join(map(lambda r: r.ctx.hex()[:12], conflicts))
1471 conflictstr = b','.join(map(lambda r: r.ctx.hex()[:12], conflicts))
1471 conflictstr = b"changed files overlap with %s" % conflictstr
1472 conflictstr = b"changed files overlap with %s" % conflictstr
1472 else:
1473 else:
1473 conflictstr = b'no overlap'
1474 conflictstr = b'no overlap'
1474
1475
1475 win.addstr(y, 1, conflictstr[:length])
1476 win.addstr(y, 1, conflictstr[:length])
1476 win.noutrefresh()
1477 win.noutrefresh()
1477
1478
1478 def helplines(mode):
1479 def helplines(mode):
1479 if mode == MODE_PATCH:
1480 if mode == MODE_PATCH:
1480 help = b"""\
1481 help = b"""\
1481 ?: help, k/up: line up, j/down: line down, v: stop viewing patch
1482 ?: help, k/up: line up, j/down: line down, v: stop viewing patch
1482 pgup: prev page, space/pgdn: next page, c: commit, q: abort
1483 pgup: prev page, space/pgdn: next page, c: commit, q: abort
1483 """
1484 """
1484 else:
1485 else:
1485 help = b"""\
1486 help = b"""\
1486 ?: help, k/up: move up, j/down: move down, space: select, v: view patch
1487 ?: help, k/up: move up, j/down: move down, space: select, v: view patch
1487 d: drop, e: edit, f: fold, m: mess, p: pick, r: roll
1488 d: drop, e: edit, f: fold, m: mess, p: pick, r: roll
1488 pgup/K: move patch up, pgdn/J: move patch down, c: commit, q: abort
1489 pgup/K: move patch up, pgdn/J: move patch down, c: commit, q: abort
1489 """
1490 """
1490 return help.splitlines()
1491 return help.splitlines()
1491
1492
1492 def renderhelp(win, state):
1493 def renderhelp(win, state):
1493 maxy, maxx = win.getmaxyx()
1494 maxy, maxx = win.getmaxyx()
1494 mode, _ = state[b'mode']
1495 mode, _ = state[b'mode']
1495 for y, line in enumerate(helplines(mode)):
1496 for y, line in enumerate(helplines(mode)):
1496 if y >= maxy:
1497 if y >= maxy:
1497 break
1498 break
1498 addln(win, y, 0, line, curses.color_pair(COLOR_HELP))
1499 addln(win, y, 0, line, curses.color_pair(COLOR_HELP))
1499 win.noutrefresh()
1500 win.noutrefresh()
1500
1501
1501 def renderrules(rulesscr, state):
1502 def renderrules(rulesscr, state):
1502 rules = state[b'rules']
1503 rules = state[b'rules']
1503 pos = state[b'pos']
1504 pos = state[b'pos']
1504 selected = state[b'selected']
1505 selected = state[b'selected']
1505 start = state[b'modes'][MODE_RULES][b'line_offset']
1506 start = state[b'modes'][MODE_RULES][b'line_offset']
1506
1507
1507 conflicts = [r.ctx for r in rules if r.conflicts]
1508 conflicts = [r.ctx for r in rules if r.conflicts]
1508 if len(conflicts) > 0:
1509 if len(conflicts) > 0:
1509 line = b"potential conflict in %s" % b','.join(
1510 line = b"potential conflict in %s" % b','.join(
1510 map(pycompat.bytestr, conflicts)
1511 map(pycompat.bytestr, conflicts)
1511 )
1512 )
1512 addln(rulesscr, -1, 0, line, curses.color_pair(COLOR_WARN))
1513 addln(rulesscr, -1, 0, line, curses.color_pair(COLOR_WARN))
1513
1514
1514 for y, rule in enumerate(rules[start:]):
1515 for y, rule in enumerate(rules[start:]):
1515 if y >= state[b'page_height']:
1516 if y >= state[b'page_height']:
1516 break
1517 break
1517 if len(rule.conflicts) > 0:
1518 if len(rule.conflicts) > 0:
1518 rulesscr.addstr(y, 0, b" ", curses.color_pair(COLOR_WARN))
1519 rulesscr.addstr(y, 0, b" ", curses.color_pair(COLOR_WARN))
1519 else:
1520 else:
1520 rulesscr.addstr(y, 0, b" ", curses.COLOR_BLACK)
1521 rulesscr.addstr(y, 0, b" ", curses.COLOR_BLACK)
1521
1522
1522 if y + start == selected:
1523 if y + start == selected:
1523 rollcolor = COLOR_ROLL_SELECTED
1524 rollcolor = COLOR_ROLL_SELECTED
1524 addln(rulesscr, y, 2, rule, curses.color_pair(COLOR_SELECTED))
1525 addln(rulesscr, y, 2, rule, curses.color_pair(COLOR_SELECTED))
1525 elif y + start == pos:
1526 elif y + start == pos:
1526 rollcolor = COLOR_ROLL_CURRENT
1527 rollcolor = COLOR_ROLL_CURRENT
1527 addln(
1528 addln(
1528 rulesscr,
1529 rulesscr,
1529 y,
1530 y,
1530 2,
1531 2,
1531 rule,
1532 rule,
1532 curses.color_pair(COLOR_CURRENT) | curses.A_BOLD,
1533 curses.color_pair(COLOR_CURRENT) | curses.A_BOLD,
1533 )
1534 )
1534 else:
1535 else:
1535 rollcolor = COLOR_ROLL
1536 rollcolor = COLOR_ROLL
1536 addln(rulesscr, y, 2, rule)
1537 addln(rulesscr, y, 2, rule)
1537
1538
1538 if rule.action == b'roll':
1539 if rule.action == b'roll':
1539 rulesscr.addstr(
1540 rulesscr.addstr(
1540 y,
1541 y,
1541 2 + len(rule.prefix),
1542 2 + len(rule.prefix),
1542 rule.desc,
1543 rule.desc,
1543 curses.color_pair(rollcolor),
1544 curses.color_pair(rollcolor),
1544 )
1545 )
1545
1546
1546 rulesscr.noutrefresh()
1547 rulesscr.noutrefresh()
1547
1548
1548 def renderstring(win, state, output, diffcolors=False):
1549 def renderstring(win, state, output, diffcolors=False):
1549 maxy, maxx = win.getmaxyx()
1550 maxy, maxx = win.getmaxyx()
1550 length = min(maxy - 1, len(output))
1551 length = min(maxy - 1, len(output))
1551 for y in range(0, length):
1552 for y in range(0, length):
1552 line = output[y]
1553 line = output[y]
1553 if diffcolors:
1554 if diffcolors:
1554 if line and line[0] == b'+':
1555 if line and line[0] == b'+':
1555 win.addstr(
1556 win.addstr(
1556 y, 0, line, curses.color_pair(COLOR_DIFF_ADD_LINE)
1557 y, 0, line, curses.color_pair(COLOR_DIFF_ADD_LINE)
1557 )
1558 )
1558 elif line and line[0] == b'-':
1559 elif line and line[0] == b'-':
1559 win.addstr(
1560 win.addstr(
1560 y, 0, line, curses.color_pair(COLOR_DIFF_DEL_LINE)
1561 y, 0, line, curses.color_pair(COLOR_DIFF_DEL_LINE)
1561 )
1562 )
1562 elif line.startswith(b'@@ '):
1563 elif line.startswith(b'@@ '):
1563 win.addstr(y, 0, line, curses.color_pair(COLOR_DIFF_OFFSET))
1564 win.addstr(y, 0, line, curses.color_pair(COLOR_DIFF_OFFSET))
1564 else:
1565 else:
1565 win.addstr(y, 0, line)
1566 win.addstr(y, 0, line)
1566 else:
1567 else:
1567 win.addstr(y, 0, line)
1568 win.addstr(y, 0, line)
1568 win.noutrefresh()
1569 win.noutrefresh()
1569
1570
1570 def renderpatch(win, state):
1571 def renderpatch(win, state):
1571 start = state[b'modes'][MODE_PATCH][b'line_offset']
1572 start = state[b'modes'][MODE_PATCH][b'line_offset']
1572 content = state[b'modes'][MODE_PATCH][b'patchcontents']
1573 content = state[b'modes'][MODE_PATCH][b'patchcontents']
1573 renderstring(win, state, content[start:], diffcolors=True)
1574 renderstring(win, state, content[start:], diffcolors=True)
1574
1575
1575 def layout(mode):
1576 def layout(mode):
1576 maxy, maxx = stdscr.getmaxyx()
1577 maxy, maxx = stdscr.getmaxyx()
1577 helplen = len(helplines(mode))
1578 helplen = len(helplines(mode))
1578 return {
1579 return {
1579 b'commit': (12, maxx),
1580 b'commit': (12, maxx),
1580 b'help': (helplen, maxx),
1581 b'help': (helplen, maxx),
1581 b'main': (maxy - helplen - 12, maxx),
1582 b'main': (maxy - helplen - 12, maxx),
1582 }
1583 }
1583
1584
1584 def drawvertwin(size, y, x):
1585 def drawvertwin(size, y, x):
1585 win = curses.newwin(size[0], size[1], y, x)
1586 win = curses.newwin(size[0], size[1], y, x)
1586 y += size[0]
1587 y += size[0]
1587 return win, y, x
1588 return win, y, x
1588
1589
1589 state = {
1590 state = {
1590 b'pos': 0,
1591 b'pos': 0,
1591 b'rules': rules,
1592 b'rules': rules,
1592 b'selected': None,
1593 b'selected': None,
1593 b'mode': (MODE_INIT, MODE_INIT),
1594 b'mode': (MODE_INIT, MODE_INIT),
1594 b'page_height': None,
1595 b'page_height': None,
1595 b'modes': {
1596 b'modes': {
1596 MODE_RULES: {
1597 MODE_RULES: {
1597 b'line_offset': 0,
1598 b'line_offset': 0,
1598 },
1599 },
1599 MODE_PATCH: {
1600 MODE_PATCH: {
1600 b'line_offset': 0,
1601 b'line_offset': 0,
1601 },
1602 },
1602 },
1603 },
1603 b'repo': repo,
1604 b'repo': repo,
1604 }
1605 }
1605
1606
1606 # eventloop
1607 # eventloop
1607 ch = None
1608 ch = None
1608 stdscr.clear()
1609 stdscr.clear()
1609 stdscr.refresh()
1610 stdscr.refresh()
1610 while True:
1611 while True:
1611 try:
1612 try:
1612 oldmode, _ = state[b'mode']
1613 oldmode, _ = state[b'mode']
1613 if oldmode == MODE_INIT:
1614 if oldmode == MODE_INIT:
1614 changemode(state, MODE_RULES)
1615 changemode(state, MODE_RULES)
1615 e = event(state, ch)
1616 e = event(state, ch)
1616
1617
1617 if e == E_QUIT:
1618 if e == E_QUIT:
1618 return False
1619 return False
1619 if e == E_HISTEDIT:
1620 if e == E_HISTEDIT:
1620 return state[b'rules']
1621 return state[b'rules']
1621 else:
1622 else:
1622 if e == E_RESIZE:
1623 if e == E_RESIZE:
1623 size = screen_size()
1624 size = screen_size()
1624 if size != stdscr.getmaxyx():
1625 if size != stdscr.getmaxyx():
1625 curses.resizeterm(*size)
1626 curses.resizeterm(*size)
1626
1627
1627 curmode, _ = state[b'mode']
1628 curmode, _ = state[b'mode']
1628 sizes = layout(curmode)
1629 sizes = layout(curmode)
1629 if curmode != oldmode:
1630 if curmode != oldmode:
1630 state[b'page_height'] = sizes[b'main'][0]
1631 state[b'page_height'] = sizes[b'main'][0]
1631 # Adjust the view to fit the current screen size.
1632 # Adjust the view to fit the current screen size.
1632 movecursor(state, state[b'pos'], state[b'pos'])
1633 movecursor(state, state[b'pos'], state[b'pos'])
1633
1634
1634 # Pack the windows against the top, each pane spread across the
1635 # Pack the windows against the top, each pane spread across the
1635 # full width of the screen.
1636 # full width of the screen.
1636 y, x = (0, 0)
1637 y, x = (0, 0)
1637 helpwin, y, x = drawvertwin(sizes[b'help'], y, x)
1638 helpwin, y, x = drawvertwin(sizes[b'help'], y, x)
1638 mainwin, y, x = drawvertwin(sizes[b'main'], y, x)
1639 mainwin, y, x = drawvertwin(sizes[b'main'], y, x)
1639 commitwin, y, x = drawvertwin(sizes[b'commit'], y, x)
1640 commitwin, y, x = drawvertwin(sizes[b'commit'], y, x)
1640
1641
1641 if e in (E_PAGEDOWN, E_PAGEUP, E_LINEDOWN, E_LINEUP):
1642 if e in (E_PAGEDOWN, E_PAGEUP, E_LINEDOWN, E_LINEUP):
1642 if e == E_PAGEDOWN:
1643 if e == E_PAGEDOWN:
1643 changeview(state, +1, b'page')
1644 changeview(state, +1, b'page')
1644 elif e == E_PAGEUP:
1645 elif e == E_PAGEUP:
1645 changeview(state, -1, b'page')
1646 changeview(state, -1, b'page')
1646 elif e == E_LINEDOWN:
1647 elif e == E_LINEDOWN:
1647 changeview(state, +1, b'line')
1648 changeview(state, +1, b'line')
1648 elif e == E_LINEUP:
1649 elif e == E_LINEUP:
1649 changeview(state, -1, b'line')
1650 changeview(state, -1, b'line')
1650
1651
1651 # start rendering
1652 # start rendering
1652 commitwin.erase()
1653 commitwin.erase()
1653 helpwin.erase()
1654 helpwin.erase()
1654 mainwin.erase()
1655 mainwin.erase()
1655 if curmode == MODE_PATCH:
1656 if curmode == MODE_PATCH:
1656 renderpatch(mainwin, state)
1657 renderpatch(mainwin, state)
1657 elif curmode == MODE_HELP:
1658 elif curmode == MODE_HELP:
1658 renderstring(mainwin, state, __doc__.strip().splitlines())
1659 renderstring(mainwin, state, __doc__.strip().splitlines())
1659 else:
1660 else:
1660 renderrules(mainwin, state)
1661 renderrules(mainwin, state)
1661 rendercommit(commitwin, state)
1662 rendercommit(commitwin, state)
1662 renderhelp(helpwin, state)
1663 renderhelp(helpwin, state)
1663 curses.doupdate()
1664 curses.doupdate()
1664 # done rendering
1665 # done rendering
1665 ch = encoding.strtolocal(stdscr.getkey())
1666 ch = encoding.strtolocal(stdscr.getkey())
1666 except curses.error:
1667 except curses.error:
1667 pass
1668 pass
1668
1669
1669
1670
1670 def _chistedit(ui, repo, freeargs, opts):
1671 def _chistedit(ui, repo, freeargs, opts):
1671 """interactively edit changeset history via a curses interface
1672 """interactively edit changeset history via a curses interface
1672
1673
1673 Provides a ncurses interface to histedit. Press ? in chistedit mode
1674 Provides a ncurses interface to histedit. Press ? in chistedit mode
1674 to see an extensive help. Requires python-curses to be installed."""
1675 to see an extensive help. Requires python-curses to be installed."""
1675
1676
1676 if curses is None:
1677 if curses is None:
1677 raise error.Abort(_(b"Python curses library required"))
1678 raise error.Abort(_(b"Python curses library required"))
1678
1679
1679 # disable color
1680 # disable color
1680 ui._colormode = None
1681 ui._colormode = None
1681
1682
1682 try:
1683 try:
1683 keep = opts.get(b'keep')
1684 keep = opts.get(b'keep')
1684 revs = opts.get(b'rev', [])[:]
1685 revs = opts.get(b'rev', [])[:]
1685 cmdutil.checkunfinished(repo)
1686 cmdutil.checkunfinished(repo)
1686 cmdutil.bailifchanged(repo)
1687 cmdutil.bailifchanged(repo)
1687
1688
1688 if os.path.exists(os.path.join(repo.path, b'histedit-state')):
1689 if os.path.exists(os.path.join(repo.path, b'histedit-state')):
1689 raise error.Abort(
1690 raise error.Abort(
1690 _(
1691 _(
1691 b'history edit already in progress, try '
1692 b'history edit already in progress, try '
1692 b'--continue or --abort'
1693 b'--continue or --abort'
1693 )
1694 )
1694 )
1695 )
1695 revs.extend(freeargs)
1696 revs.extend(freeargs)
1696 if not revs:
1697 if not revs:
1697 defaultrev = destutil.desthistedit(ui, repo)
1698 defaultrev = destutil.desthistedit(ui, repo)
1698 if defaultrev is not None:
1699 if defaultrev is not None:
1699 revs.append(defaultrev)
1700 revs.append(defaultrev)
1700 if len(revs) != 1:
1701 if len(revs) != 1:
1701 raise error.Abort(
1702 raise error.Abort(
1702 _(b'histedit requires exactly one ancestor revision')
1703 _(b'histedit requires exactly one ancestor revision')
1703 )
1704 )
1704
1705
1705 rr = list(repo.set(b'roots(%ld)', scmutil.revrange(repo, revs)))
1706 rr = list(repo.set(b'roots(%ld)', scmutil.revrange(repo, revs)))
1706 if len(rr) != 1:
1707 if len(rr) != 1:
1707 raise error.Abort(
1708 raise error.Abort(
1708 _(
1709 _(
1709 b'The specified revisions must have '
1710 b'The specified revisions must have '
1710 b'exactly one common root'
1711 b'exactly one common root'
1711 )
1712 )
1712 )
1713 )
1713 root = rr[0].node()
1714 root = rr[0].node()
1714
1715
1715 topmost = repo.dirstate.p1()
1716 topmost = repo.dirstate.p1()
1716 revs = between(repo, root, topmost, keep)
1717 revs = between(repo, root, topmost, keep)
1717 if not revs:
1718 if not revs:
1718 raise error.Abort(
1719 raise error.Abort(
1719 _(b'%s is not an ancestor of working directory')
1720 _(b'%s is not an ancestor of working directory')
1720 % node.short(root)
1721 % node.short(root)
1721 )
1722 )
1722
1723
1723 ctxs = []
1724 ctxs = []
1724 for i, r in enumerate(revs):
1725 for i, r in enumerate(revs):
1725 ctxs.append(histeditrule(ui, repo[r], i))
1726 ctxs.append(histeditrule(ui, repo[r], i))
1726 with util.with_lc_ctype():
1727 with util.with_lc_ctype():
1727 rc = curses.wrapper(functools.partial(_chisteditmain, repo, ctxs))
1728 rc = curses.wrapper(functools.partial(_chisteditmain, repo, ctxs))
1728 curses.echo()
1729 curses.echo()
1729 curses.endwin()
1730 curses.endwin()
1730 if rc is False:
1731 if rc is False:
1731 ui.write(_(b"histedit aborted\n"))
1732 ui.write(_(b"histedit aborted\n"))
1732 return 0
1733 return 0
1733 if type(rc) is list:
1734 if type(rc) is list:
1734 ui.status(_(b"performing changes\n"))
1735 ui.status(_(b"performing changes\n"))
1735 rules = makecommands(rc)
1736 rules = makecommands(rc)
1736 with repo.vfs(b'chistedit', b'w+') as fp:
1737 with repo.vfs(b'chistedit', b'w+') as fp:
1737 for r in rules:
1738 for r in rules:
1738 fp.write(r)
1739 fp.write(r)
1739 opts[b'commands'] = fp.name
1740 opts[b'commands'] = fp.name
1740 return _texthistedit(ui, repo, freeargs, opts)
1741 return _texthistedit(ui, repo, freeargs, opts)
1741 except KeyboardInterrupt:
1742 except KeyboardInterrupt:
1742 pass
1743 pass
1743 return -1
1744 return -1
1744
1745
1745
1746
1746 @command(
1747 @command(
1747 b'histedit',
1748 b'histedit',
1748 [
1749 [
1749 (
1750 (
1750 b'',
1751 b'',
1751 b'commands',
1752 b'commands',
1752 b'',
1753 b'',
1753 _(b'read history edits from the specified file'),
1754 _(b'read history edits from the specified file'),
1754 _(b'FILE'),
1755 _(b'FILE'),
1755 ),
1756 ),
1756 (b'c', b'continue', False, _(b'continue an edit already in progress')),
1757 (b'c', b'continue', False, _(b'continue an edit already in progress')),
1757 (b'', b'edit-plan', False, _(b'edit remaining actions list')),
1758 (b'', b'edit-plan', False, _(b'edit remaining actions list')),
1758 (
1759 (
1759 b'k',
1760 b'k',
1760 b'keep',
1761 b'keep',
1761 False,
1762 False,
1762 _(b"don't strip old nodes after edit is complete"),
1763 _(b"don't strip old nodes after edit is complete"),
1763 ),
1764 ),
1764 (b'', b'abort', False, _(b'abort an edit in progress')),
1765 (b'', b'abort', False, _(b'abort an edit in progress')),
1765 (b'o', b'outgoing', False, _(b'changesets not found in destination')),
1766 (b'o', b'outgoing', False, _(b'changesets not found in destination')),
1766 (
1767 (
1767 b'f',
1768 b'f',
1768 b'force',
1769 b'force',
1769 False,
1770 False,
1770 _(b'force outgoing even for unrelated repositories'),
1771 _(b'force outgoing even for unrelated repositories'),
1771 ),
1772 ),
1772 (b'r', b'rev', [], _(b'first revision to be edited'), _(b'REV')),
1773 (b'r', b'rev', [], _(b'first revision to be edited'), _(b'REV')),
1773 ]
1774 ]
1774 + cmdutil.formatteropts,
1775 + cmdutil.formatteropts,
1775 _(b"[OPTIONS] ([ANCESTOR] | --outgoing [URL])"),
1776 _(b"[OPTIONS] ([ANCESTOR] | --outgoing [URL])"),
1776 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
1777 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
1777 )
1778 )
1778 def histedit(ui, repo, *freeargs, **opts):
1779 def histedit(ui, repo, *freeargs, **opts):
1779 """interactively edit changeset history
1780 """interactively edit changeset history
1780
1781
1781 This command lets you edit a linear series of changesets (up to
1782 This command lets you edit a linear series of changesets (up to
1782 and including the working directory, which should be clean).
1783 and including the working directory, which should be clean).
1783 You can:
1784 You can:
1784
1785
1785 - `pick` to [re]order a changeset
1786 - `pick` to [re]order a changeset
1786
1787
1787 - `drop` to omit changeset
1788 - `drop` to omit changeset
1788
1789
1789 - `mess` to reword the changeset commit message
1790 - `mess` to reword the changeset commit message
1790
1791
1791 - `fold` to combine it with the preceding changeset (using the later date)
1792 - `fold` to combine it with the preceding changeset (using the later date)
1792
1793
1793 - `roll` like fold, but discarding this commit's description and date
1794 - `roll` like fold, but discarding this commit's description and date
1794
1795
1795 - `edit` to edit this changeset (preserving date)
1796 - `edit` to edit this changeset (preserving date)
1796
1797
1797 - `base` to checkout changeset and apply further changesets from there
1798 - `base` to checkout changeset and apply further changesets from there
1798
1799
1799 There are a number of ways to select the root changeset:
1800 There are a number of ways to select the root changeset:
1800
1801
1801 - Specify ANCESTOR directly
1802 - Specify ANCESTOR directly
1802
1803
1803 - Use --outgoing -- it will be the first linear changeset not
1804 - Use --outgoing -- it will be the first linear changeset not
1804 included in destination. (See :hg:`help config.paths.default-push`)
1805 included in destination. (See :hg:`help config.paths.default-push`)
1805
1806
1806 - Otherwise, the value from the "histedit.defaultrev" config option
1807 - Otherwise, the value from the "histedit.defaultrev" config option
1807 is used as a revset to select the base revision when ANCESTOR is not
1808 is used as a revset to select the base revision when ANCESTOR is not
1808 specified. The first revision returned by the revset is used. By
1809 specified. The first revision returned by the revset is used. By
1809 default, this selects the editable history that is unique to the
1810 default, this selects the editable history that is unique to the
1810 ancestry of the working directory.
1811 ancestry of the working directory.
1811
1812
1812 .. container:: verbose
1813 .. container:: verbose
1813
1814
1814 If you use --outgoing, this command will abort if there are ambiguous
1815 If you use --outgoing, this command will abort if there are ambiguous
1815 outgoing revisions. For example, if there are multiple branches
1816 outgoing revisions. For example, if there are multiple branches
1816 containing outgoing revisions.
1817 containing outgoing revisions.
1817
1818
1818 Use "min(outgoing() and ::.)" or similar revset specification
1819 Use "min(outgoing() and ::.)" or similar revset specification
1819 instead of --outgoing to specify edit target revision exactly in
1820 instead of --outgoing to specify edit target revision exactly in
1820 such ambiguous situation. See :hg:`help revsets` for detail about
1821 such ambiguous situation. See :hg:`help revsets` for detail about
1821 selecting revisions.
1822 selecting revisions.
1822
1823
1823 .. container:: verbose
1824 .. container:: verbose
1824
1825
1825 Examples:
1826 Examples:
1826
1827
1827 - A number of changes have been made.
1828 - A number of changes have been made.
1828 Revision 3 is no longer needed.
1829 Revision 3 is no longer needed.
1829
1830
1830 Start history editing from revision 3::
1831 Start history editing from revision 3::
1831
1832
1832 hg histedit -r 3
1833 hg histedit -r 3
1833
1834
1834 An editor opens, containing the list of revisions,
1835 An editor opens, containing the list of revisions,
1835 with specific actions specified::
1836 with specific actions specified::
1836
1837
1837 pick 5339bf82f0ca 3 Zworgle the foobar
1838 pick 5339bf82f0ca 3 Zworgle the foobar
1838 pick 8ef592ce7cc4 4 Bedazzle the zerlog
1839 pick 8ef592ce7cc4 4 Bedazzle the zerlog
1839 pick 0a9639fcda9d 5 Morgify the cromulancy
1840 pick 0a9639fcda9d 5 Morgify the cromulancy
1840
1841
1841 Additional information about the possible actions
1842 Additional information about the possible actions
1842 to take appears below the list of revisions.
1843 to take appears below the list of revisions.
1843
1844
1844 To remove revision 3 from the history,
1845 To remove revision 3 from the history,
1845 its action (at the beginning of the relevant line)
1846 its action (at the beginning of the relevant line)
1846 is changed to 'drop'::
1847 is changed to 'drop'::
1847
1848
1848 drop 5339bf82f0ca 3 Zworgle the foobar
1849 drop 5339bf82f0ca 3 Zworgle the foobar
1849 pick 8ef592ce7cc4 4 Bedazzle the zerlog
1850 pick 8ef592ce7cc4 4 Bedazzle the zerlog
1850 pick 0a9639fcda9d 5 Morgify the cromulancy
1851 pick 0a9639fcda9d 5 Morgify the cromulancy
1851
1852
1852 - A number of changes have been made.
1853 - A number of changes have been made.
1853 Revision 2 and 4 need to be swapped.
1854 Revision 2 and 4 need to be swapped.
1854
1855
1855 Start history editing from revision 2::
1856 Start history editing from revision 2::
1856
1857
1857 hg histedit -r 2
1858 hg histedit -r 2
1858
1859
1859 An editor opens, containing the list of revisions,
1860 An editor opens, containing the list of revisions,
1860 with specific actions specified::
1861 with specific actions specified::
1861
1862
1862 pick 252a1af424ad 2 Blorb a morgwazzle
1863 pick 252a1af424ad 2 Blorb a morgwazzle
1863 pick 5339bf82f0ca 3 Zworgle the foobar
1864 pick 5339bf82f0ca 3 Zworgle the foobar
1864 pick 8ef592ce7cc4 4 Bedazzle the zerlog
1865 pick 8ef592ce7cc4 4 Bedazzle the zerlog
1865
1866
1866 To swap revision 2 and 4, its lines are swapped
1867 To swap revision 2 and 4, its lines are swapped
1867 in the editor::
1868 in the editor::
1868
1869
1869 pick 8ef592ce7cc4 4 Bedazzle the zerlog
1870 pick 8ef592ce7cc4 4 Bedazzle the zerlog
1870 pick 5339bf82f0ca 3 Zworgle the foobar
1871 pick 5339bf82f0ca 3 Zworgle the foobar
1871 pick 252a1af424ad 2 Blorb a morgwazzle
1872 pick 252a1af424ad 2 Blorb a morgwazzle
1872
1873
1873 Returns 0 on success, 1 if user intervention is required (not only
1874 Returns 0 on success, 1 if user intervention is required (not only
1874 for intentional "edit" command, but also for resolving unexpected
1875 for intentional "edit" command, but also for resolving unexpected
1875 conflicts).
1876 conflicts).
1876 """
1877 """
1877 opts = pycompat.byteskwargs(opts)
1878 opts = pycompat.byteskwargs(opts)
1878
1879
1879 # kludge: _chistedit only works for starting an edit, not aborting
1880 # kludge: _chistedit only works for starting an edit, not aborting
1880 # or continuing, so fall back to regular _texthistedit for those
1881 # or continuing, so fall back to regular _texthistedit for those
1881 # operations.
1882 # operations.
1882 if ui.interface(b'histedit') == b'curses' and _getgoal(opts) == goalnew:
1883 if ui.interface(b'histedit') == b'curses' and _getgoal(opts) == goalnew:
1883 return _chistedit(ui, repo, freeargs, opts)
1884 return _chistedit(ui, repo, freeargs, opts)
1884 return _texthistedit(ui, repo, freeargs, opts)
1885 return _texthistedit(ui, repo, freeargs, opts)
1885
1886
1886
1887
1887 def _texthistedit(ui, repo, freeargs, opts):
1888 def _texthistedit(ui, repo, freeargs, opts):
1888 state = histeditstate(repo)
1889 state = histeditstate(repo)
1889 with repo.wlock() as wlock, repo.lock() as lock:
1890 with repo.wlock() as wlock, repo.lock() as lock:
1890 state.wlock = wlock
1891 state.wlock = wlock
1891 state.lock = lock
1892 state.lock = lock
1892 _histedit(ui, repo, state, freeargs, opts)
1893 _histedit(ui, repo, state, freeargs, opts)
1893
1894
1894
1895
1895 goalcontinue = b'continue'
1896 goalcontinue = b'continue'
1896 goalabort = b'abort'
1897 goalabort = b'abort'
1897 goaleditplan = b'edit-plan'
1898 goaleditplan = b'edit-plan'
1898 goalnew = b'new'
1899 goalnew = b'new'
1899
1900
1900
1901
1901 def _getgoal(opts):
1902 def _getgoal(opts):
1902 if opts.get(b'continue'):
1903 if opts.get(b'continue'):
1903 return goalcontinue
1904 return goalcontinue
1904 if opts.get(b'abort'):
1905 if opts.get(b'abort'):
1905 return goalabort
1906 return goalabort
1906 if opts.get(b'edit_plan'):
1907 if opts.get(b'edit_plan'):
1907 return goaleditplan
1908 return goaleditplan
1908 return goalnew
1909 return goalnew
1909
1910
1910
1911
1911 def _readfile(ui, path):
1912 def _readfile(ui, path):
1912 if path == b'-':
1913 if path == b'-':
1913 with ui.timeblockedsection(b'histedit'):
1914 with ui.timeblockedsection(b'histedit'):
1914 return ui.fin.read()
1915 return ui.fin.read()
1915 else:
1916 else:
1916 with open(path, b'rb') as f:
1917 with open(path, b'rb') as f:
1917 return f.read()
1918 return f.read()
1918
1919
1919
1920
1920 def _validateargs(ui, repo, state, freeargs, opts, goal, rules, revs):
1921 def _validateargs(ui, repo, state, freeargs, opts, goal, rules, revs):
1921 # TODO only abort if we try to histedit mq patches, not just
1922 # TODO only abort if we try to histedit mq patches, not just
1922 # blanket if mq patches are applied somewhere
1923 # blanket if mq patches are applied somewhere
1923 mq = getattr(repo, 'mq', None)
1924 mq = getattr(repo, 'mq', None)
1924 if mq and mq.applied:
1925 if mq and mq.applied:
1925 raise error.Abort(_(b'source has mq patches applied'))
1926 raise error.Abort(_(b'source has mq patches applied'))
1926
1927
1927 # basic argument incompatibility processing
1928 # basic argument incompatibility processing
1928 outg = opts.get(b'outgoing')
1929 outg = opts.get(b'outgoing')
1929 editplan = opts.get(b'edit_plan')
1930 editplan = opts.get(b'edit_plan')
1930 abort = opts.get(b'abort')
1931 abort = opts.get(b'abort')
1931 force = opts.get(b'force')
1932 force = opts.get(b'force')
1932 if force and not outg:
1933 if force and not outg:
1933 raise error.Abort(_(b'--force only allowed with --outgoing'))
1934 raise error.Abort(_(b'--force only allowed with --outgoing'))
1934 if goal == b'continue':
1935 if goal == b'continue':
1935 if any((outg, abort, revs, freeargs, rules, editplan)):
1936 if any((outg, abort, revs, freeargs, rules, editplan)):
1936 raise error.Abort(_(b'no arguments allowed with --continue'))
1937 raise error.Abort(_(b'no arguments allowed with --continue'))
1937 elif goal == b'abort':
1938 elif goal == b'abort':
1938 if any((outg, revs, freeargs, rules, editplan)):
1939 if any((outg, revs, freeargs, rules, editplan)):
1939 raise error.Abort(_(b'no arguments allowed with --abort'))
1940 raise error.Abort(_(b'no arguments allowed with --abort'))
1940 elif goal == b'edit-plan':
1941 elif goal == b'edit-plan':
1941 if any((outg, revs, freeargs)):
1942 if any((outg, revs, freeargs)):
1942 raise error.Abort(
1943 raise error.Abort(
1943 _(b'only --commands argument allowed with --edit-plan')
1944 _(b'only --commands argument allowed with --edit-plan')
1944 )
1945 )
1945 else:
1946 else:
1946 if state.inprogress():
1947 if state.inprogress():
1947 raise error.Abort(
1948 raise error.Abort(
1948 _(
1949 _(
1949 b'history edit already in progress, try '
1950 b'history edit already in progress, try '
1950 b'--continue or --abort'
1951 b'--continue or --abort'
1951 )
1952 )
1952 )
1953 )
1953 if outg:
1954 if outg:
1954 if revs:
1955 if revs:
1955 raise error.Abort(_(b'no revisions allowed with --outgoing'))
1956 raise error.Abort(_(b'no revisions allowed with --outgoing'))
1956 if len(freeargs) > 1:
1957 if len(freeargs) > 1:
1957 raise error.Abort(
1958 raise error.Abort(
1958 _(b'only one repo argument allowed with --outgoing')
1959 _(b'only one repo argument allowed with --outgoing')
1959 )
1960 )
1960 else:
1961 else:
1961 revs.extend(freeargs)
1962 revs.extend(freeargs)
1962 if len(revs) == 0:
1963 if len(revs) == 0:
1963 defaultrev = destutil.desthistedit(ui, repo)
1964 defaultrev = destutil.desthistedit(ui, repo)
1964 if defaultrev is not None:
1965 if defaultrev is not None:
1965 revs.append(defaultrev)
1966 revs.append(defaultrev)
1966
1967
1967 if len(revs) != 1:
1968 if len(revs) != 1:
1968 raise error.Abort(
1969 raise error.Abort(
1969 _(b'histedit requires exactly one ancestor revision')
1970 _(b'histedit requires exactly one ancestor revision')
1970 )
1971 )
1971
1972
1972
1973
1973 def _histedit(ui, repo, state, freeargs, opts):
1974 def _histedit(ui, repo, state, freeargs, opts):
1974 fm = ui.formatter(b'histedit', opts)
1975 fm = ui.formatter(b'histedit', opts)
1975 fm.startitem()
1976 fm.startitem()
1976 goal = _getgoal(opts)
1977 goal = _getgoal(opts)
1977 revs = opts.get(b'rev', [])
1978 revs = opts.get(b'rev', [])
1978 nobackup = not ui.configbool(b'rewrite', b'backup-bundle')
1979 nobackup = not ui.configbool(b'rewrite', b'backup-bundle')
1979 rules = opts.get(b'commands', b'')
1980 rules = opts.get(b'commands', b'')
1980 state.keep = opts.get(b'keep', False)
1981 state.keep = opts.get(b'keep', False)
1981
1982
1982 _validateargs(ui, repo, state, freeargs, opts, goal, rules, revs)
1983 _validateargs(ui, repo, state, freeargs, opts, goal, rules, revs)
1983
1984
1984 hastags = False
1985 hastags = False
1985 if revs:
1986 if revs:
1986 revs = scmutil.revrange(repo, revs)
1987 revs = scmutil.revrange(repo, revs)
1987 ctxs = [repo[rev] for rev in revs]
1988 ctxs = [repo[rev] for rev in revs]
1988 for ctx in ctxs:
1989 for ctx in ctxs:
1989 tags = [tag for tag in ctx.tags() if tag != b'tip']
1990 tags = [tag for tag in ctx.tags() if tag != b'tip']
1990 if not hastags:
1991 if not hastags:
1991 hastags = len(tags)
1992 hastags = len(tags)
1992 if hastags:
1993 if hastags:
1993 if ui.promptchoice(
1994 if ui.promptchoice(
1994 _(
1995 _(
1995 b'warning: tags associated with the given'
1996 b'warning: tags associated with the given'
1996 b' changeset will be lost after histedit.\n'
1997 b' changeset will be lost after histedit.\n'
1997 b'do you want to continue (yN)? $$ &Yes $$ &No'
1998 b'do you want to continue (yN)? $$ &Yes $$ &No'
1998 ),
1999 ),
1999 default=1,
2000 default=1,
2000 ):
2001 ):
2001 raise error.Abort(_(b'histedit cancelled\n'))
2002 raise error.Abort(_(b'histedit cancelled\n'))
2002 # rebuild state
2003 # rebuild state
2003 if goal == goalcontinue:
2004 if goal == goalcontinue:
2004 state.read()
2005 state.read()
2005 state = bootstrapcontinue(ui, state, opts)
2006 state = bootstrapcontinue(ui, state, opts)
2006 elif goal == goaleditplan:
2007 elif goal == goaleditplan:
2007 _edithisteditplan(ui, repo, state, rules)
2008 _edithisteditplan(ui, repo, state, rules)
2008 return
2009 return
2009 elif goal == goalabort:
2010 elif goal == goalabort:
2010 _aborthistedit(ui, repo, state, nobackup=nobackup)
2011 _aborthistedit(ui, repo, state, nobackup=nobackup)
2011 return
2012 return
2012 else:
2013 else:
2013 # goal == goalnew
2014 # goal == goalnew
2014 _newhistedit(ui, repo, state, revs, freeargs, opts)
2015 _newhistedit(ui, repo, state, revs, freeargs, opts)
2015
2016
2016 _continuehistedit(ui, repo, state)
2017 _continuehistedit(ui, repo, state)
2017 _finishhistedit(ui, repo, state, fm)
2018 _finishhistedit(ui, repo, state, fm)
2018 fm.end()
2019 fm.end()
2019
2020
2020
2021
2021 def _continuehistedit(ui, repo, state):
2022 def _continuehistedit(ui, repo, state):
2022 """This function runs after either:
2023 """This function runs after either:
2023 - bootstrapcontinue (if the goal is 'continue')
2024 - bootstrapcontinue (if the goal is 'continue')
2024 - _newhistedit (if the goal is 'new')
2025 - _newhistedit (if the goal is 'new')
2025 """
2026 """
2026 # preprocess rules so that we can hide inner folds from the user
2027 # preprocess rules so that we can hide inner folds from the user
2027 # and only show one editor
2028 # and only show one editor
2028 actions = state.actions[:]
2029 actions = state.actions[:]
2029 for idx, (action, nextact) in enumerate(zip(actions, actions[1:] + [None])):
2030 for idx, (action, nextact) in enumerate(zip(actions, actions[1:] + [None])):
2030 if action.verb == b'fold' and nextact and nextact.verb == b'fold':
2031 if action.verb == b'fold' and nextact and nextact.verb == b'fold':
2031 state.actions[idx].__class__ = _multifold
2032 state.actions[idx].__class__ = _multifold
2032
2033
2033 # Force an initial state file write, so the user can run --abort/continue
2034 # Force an initial state file write, so the user can run --abort/continue
2034 # even if there's an exception before the first transaction serialize.
2035 # even if there's an exception before the first transaction serialize.
2035 state.write()
2036 state.write()
2036
2037
2037 tr = None
2038 tr = None
2038 # Don't use singletransaction by default since it rolls the entire
2039 # Don't use singletransaction by default since it rolls the entire
2039 # transaction back if an unexpected exception happens (like a
2040 # transaction back if an unexpected exception happens (like a
2040 # pretxncommit hook throws, or the user aborts the commit msg editor).
2041 # pretxncommit hook throws, or the user aborts the commit msg editor).
2041 if ui.configbool(b"histedit", b"singletransaction"):
2042 if ui.configbool(b"histedit", b"singletransaction"):
2042 # Don't use a 'with' for the transaction, since actions may close
2043 # Don't use a 'with' for the transaction, since actions may close
2043 # and reopen a transaction. For example, if the action executes an
2044 # and reopen a transaction. For example, if the action executes an
2044 # external process it may choose to commit the transaction first.
2045 # external process it may choose to commit the transaction first.
2045 tr = repo.transaction(b'histedit')
2046 tr = repo.transaction(b'histedit')
2046 progress = ui.makeprogress(
2047 progress = ui.makeprogress(
2047 _(b"editing"), unit=_(b'changes'), total=len(state.actions)
2048 _(b"editing"), unit=_(b'changes'), total=len(state.actions)
2048 )
2049 )
2049 with progress, util.acceptintervention(tr):
2050 with progress, util.acceptintervention(tr):
2050 while state.actions:
2051 while state.actions:
2051 state.write(tr=tr)
2052 state.write(tr=tr)
2052 actobj = state.actions[0]
2053 actobj = state.actions[0]
2053 progress.increment(item=actobj.torule())
2054 progress.increment(item=actobj.torule())
2054 ui.debug(
2055 ui.debug(
2055 b'histedit: processing %s %s\n' % (actobj.verb, actobj.torule())
2056 b'histedit: processing %s %s\n' % (actobj.verb, actobj.torule())
2056 )
2057 )
2057 parentctx, replacement_ = actobj.run()
2058 parentctx, replacement_ = actobj.run()
2058 state.parentctxnode = parentctx.node()
2059 state.parentctxnode = parentctx.node()
2059 state.replacements.extend(replacement_)
2060 state.replacements.extend(replacement_)
2060 state.actions.pop(0)
2061 state.actions.pop(0)
2061
2062
2062 state.write()
2063 state.write()
2063
2064
2064
2065
2065 def _finishhistedit(ui, repo, state, fm):
2066 def _finishhistedit(ui, repo, state, fm):
2066 """This action runs when histedit is finishing its session"""
2067 """This action runs when histedit is finishing its session"""
2067 mergemod.update(repo[state.parentctxnode])
2068 mergemod.update(repo[state.parentctxnode])
2068
2069
2069 mapping, tmpnodes, created, ntm = processreplacement(state)
2070 mapping, tmpnodes, created, ntm = processreplacement(state)
2070 if mapping:
2071 if mapping:
2071 for prec, succs in pycompat.iteritems(mapping):
2072 for prec, succs in pycompat.iteritems(mapping):
2072 if not succs:
2073 if not succs:
2073 ui.debug(b'histedit: %s is dropped\n' % node.short(prec))
2074 ui.debug(b'histedit: %s is dropped\n' % node.short(prec))
2074 else:
2075 else:
2075 ui.debug(
2076 ui.debug(
2076 b'histedit: %s is replaced by %s\n'
2077 b'histedit: %s is replaced by %s\n'
2077 % (node.short(prec), node.short(succs[0]))
2078 % (node.short(prec), node.short(succs[0]))
2078 )
2079 )
2079 if len(succs) > 1:
2080 if len(succs) > 1:
2080 m = b'histedit: %s'
2081 m = b'histedit: %s'
2081 for n in succs[1:]:
2082 for n in succs[1:]:
2082 ui.debug(m % node.short(n))
2083 ui.debug(m % node.short(n))
2083
2084
2084 if not state.keep:
2085 if not state.keep:
2085 if mapping:
2086 if mapping:
2086 movetopmostbookmarks(repo, state.topmost, ntm)
2087 movetopmostbookmarks(repo, state.topmost, ntm)
2087 # TODO update mq state
2088 # TODO update mq state
2088 else:
2089 else:
2089 mapping = {}
2090 mapping = {}
2090
2091
2091 for n in tmpnodes:
2092 for n in tmpnodes:
2092 if n in repo:
2093 if n in repo:
2093 mapping[n] = ()
2094 mapping[n] = ()
2094
2095
2095 # remove entries about unknown nodes
2096 # remove entries about unknown nodes
2096 has_node = repo.unfiltered().changelog.index.has_node
2097 has_node = repo.unfiltered().changelog.index.has_node
2097 mapping = {
2098 mapping = {
2098 k: v
2099 k: v
2099 for k, v in mapping.items()
2100 for k, v in mapping.items()
2100 if has_node(k) and all(has_node(n) for n in v)
2101 if has_node(k) and all(has_node(n) for n in v)
2101 }
2102 }
2102 scmutil.cleanupnodes(repo, mapping, b'histedit')
2103 scmutil.cleanupnodes(repo, mapping, b'histedit')
2103 hf = fm.hexfunc
2104 hf = fm.hexfunc
2104 fl = fm.formatlist
2105 fl = fm.formatlist
2105 fd = fm.formatdict
2106 fd = fm.formatdict
2106 nodechanges = fd(
2107 nodechanges = fd(
2107 {
2108 {
2108 hf(oldn): fl([hf(n) for n in newn], name=b'node')
2109 hf(oldn): fl([hf(n) for n in newn], name=b'node')
2109 for oldn, newn in pycompat.iteritems(mapping)
2110 for oldn, newn in pycompat.iteritems(mapping)
2110 },
2111 },
2111 key=b"oldnode",
2112 key=b"oldnode",
2112 value=b"newnodes",
2113 value=b"newnodes",
2113 )
2114 )
2114 fm.data(nodechanges=nodechanges)
2115 fm.data(nodechanges=nodechanges)
2115
2116
2116 state.clear()
2117 state.clear()
2117 if os.path.exists(repo.sjoin(b'undo')):
2118 if os.path.exists(repo.sjoin(b'undo')):
2118 os.unlink(repo.sjoin(b'undo'))
2119 os.unlink(repo.sjoin(b'undo'))
2119 if repo.vfs.exists(b'histedit-last-edit.txt'):
2120 if repo.vfs.exists(b'histedit-last-edit.txt'):
2120 repo.vfs.unlink(b'histedit-last-edit.txt')
2121 repo.vfs.unlink(b'histedit-last-edit.txt')
2121
2122
2122
2123
2123 def _aborthistedit(ui, repo, state, nobackup=False):
2124 def _aborthistedit(ui, repo, state, nobackup=False):
2124 try:
2125 try:
2125 state.read()
2126 state.read()
2126 __, leafs, tmpnodes, __ = processreplacement(state)
2127 __, leafs, tmpnodes, __ = processreplacement(state)
2127 ui.debug(b'restore wc to old parent %s\n' % node.short(state.topmost))
2128 ui.debug(b'restore wc to old parent %s\n' % node.short(state.topmost))
2128
2129
2129 # Recover our old commits if necessary
2130 # Recover our old commits if necessary
2130 if not state.topmost in repo and state.backupfile:
2131 if not state.topmost in repo and state.backupfile:
2131 backupfile = repo.vfs.join(state.backupfile)
2132 backupfile = repo.vfs.join(state.backupfile)
2132 f = hg.openpath(ui, backupfile)
2133 f = hg.openpath(ui, backupfile)
2133 gen = exchange.readbundle(ui, f, backupfile)
2134 gen = exchange.readbundle(ui, f, backupfile)
2134 with repo.transaction(b'histedit.abort') as tr:
2135 with repo.transaction(b'histedit.abort') as tr:
2135 bundle2.applybundle(
2136 bundle2.applybundle(
2136 repo,
2137 repo,
2137 gen,
2138 gen,
2138 tr,
2139 tr,
2139 source=b'histedit',
2140 source=b'histedit',
2140 url=b'bundle:' + backupfile,
2141 url=b'bundle:' + backupfile,
2141 )
2142 )
2142
2143
2143 os.remove(backupfile)
2144 os.remove(backupfile)
2144
2145
2145 # check whether we should update away
2146 # check whether we should update away
2146 if repo.unfiltered().revs(
2147 if repo.unfiltered().revs(
2147 b'parents() and (%n or %ln::)',
2148 b'parents() and (%n or %ln::)',
2148 state.parentctxnode,
2149 state.parentctxnode,
2149 leafs | tmpnodes,
2150 leafs | tmpnodes,
2150 ):
2151 ):
2151 hg.clean(repo, state.topmost, show_stats=True, quietempty=True)
2152 hg.clean(repo, state.topmost, show_stats=True, quietempty=True)
2152 cleanupnode(ui, repo, tmpnodes, nobackup=nobackup)
2153 cleanupnode(ui, repo, tmpnodes, nobackup=nobackup)
2153 cleanupnode(ui, repo, leafs, nobackup=nobackup)
2154 cleanupnode(ui, repo, leafs, nobackup=nobackup)
2154 except Exception:
2155 except Exception:
2155 if state.inprogress():
2156 if state.inprogress():
2156 ui.warn(
2157 ui.warn(
2157 _(
2158 _(
2158 b'warning: encountered an exception during histedit '
2159 b'warning: encountered an exception during histedit '
2159 b'--abort; the repository may not have been completely '
2160 b'--abort; the repository may not have been completely '
2160 b'cleaned up\n'
2161 b'cleaned up\n'
2161 )
2162 )
2162 )
2163 )
2163 raise
2164 raise
2164 finally:
2165 finally:
2165 state.clear()
2166 state.clear()
2166
2167
2167
2168
2168 def hgaborthistedit(ui, repo):
2169 def hgaborthistedit(ui, repo):
2169 state = histeditstate(repo)
2170 state = histeditstate(repo)
2170 nobackup = not ui.configbool(b'rewrite', b'backup-bundle')
2171 nobackup = not ui.configbool(b'rewrite', b'backup-bundle')
2171 with repo.wlock() as wlock, repo.lock() as lock:
2172 with repo.wlock() as wlock, repo.lock() as lock:
2172 state.wlock = wlock
2173 state.wlock = wlock
2173 state.lock = lock
2174 state.lock = lock
2174 _aborthistedit(ui, repo, state, nobackup=nobackup)
2175 _aborthistedit(ui, repo, state, nobackup=nobackup)
2175
2176
2176
2177
2177 def _edithisteditplan(ui, repo, state, rules):
2178 def _edithisteditplan(ui, repo, state, rules):
2178 state.read()
2179 state.read()
2179 if not rules:
2180 if not rules:
2180 comment = geteditcomment(
2181 comment = geteditcomment(
2181 ui, node.short(state.parentctxnode), node.short(state.topmost)
2182 ui, node.short(state.parentctxnode), node.short(state.topmost)
2182 )
2183 )
2183 rules = ruleeditor(repo, ui, state.actions, comment)
2184 rules = ruleeditor(repo, ui, state.actions, comment)
2184 else:
2185 else:
2185 rules = _readfile(ui, rules)
2186 rules = _readfile(ui, rules)
2186 actions = parserules(rules, state)
2187 actions = parserules(rules, state)
2187 ctxs = [repo[act.node] for act in state.actions if act.node]
2188 ctxs = [repo[act.node] for act in state.actions if act.node]
2188 warnverifyactions(ui, repo, actions, state, ctxs)
2189 warnverifyactions(ui, repo, actions, state, ctxs)
2189 state.actions = actions
2190 state.actions = actions
2190 state.write()
2191 state.write()
2191
2192
2192
2193
2193 def _newhistedit(ui, repo, state, revs, freeargs, opts):
2194 def _newhistedit(ui, repo, state, revs, freeargs, opts):
2194 outg = opts.get(b'outgoing')
2195 outg = opts.get(b'outgoing')
2195 rules = opts.get(b'commands', b'')
2196 rules = opts.get(b'commands', b'')
2196 force = opts.get(b'force')
2197 force = opts.get(b'force')
2197
2198
2198 cmdutil.checkunfinished(repo)
2199 cmdutil.checkunfinished(repo)
2199 cmdutil.bailifchanged(repo)
2200 cmdutil.bailifchanged(repo)
2200
2201
2201 topmost = repo.dirstate.p1()
2202 topmost = repo.dirstate.p1()
2202 if outg:
2203 if outg:
2203 if freeargs:
2204 if freeargs:
2204 remote = freeargs[0]
2205 remote = freeargs[0]
2205 else:
2206 else:
2206 remote = None
2207 remote = None
2207 root = findoutgoing(ui, repo, remote, force, opts)
2208 root = findoutgoing(ui, repo, remote, force, opts)
2208 else:
2209 else:
2209 rr = list(repo.set(b'roots(%ld)', scmutil.revrange(repo, revs)))
2210 rr = list(repo.set(b'roots(%ld)', scmutil.revrange(repo, revs)))
2210 if len(rr) != 1:
2211 if len(rr) != 1:
2211 raise error.Abort(
2212 raise error.Abort(
2212 _(
2213 _(
2213 b'The specified revisions must have '
2214 b'The specified revisions must have '
2214 b'exactly one common root'
2215 b'exactly one common root'
2215 )
2216 )
2216 )
2217 )
2217 root = rr[0].node()
2218 root = rr[0].node()
2218
2219
2219 revs = between(repo, root, topmost, state.keep)
2220 revs = between(repo, root, topmost, state.keep)
2220 if not revs:
2221 if not revs:
2221 raise error.Abort(
2222 raise error.Abort(
2222 _(b'%s is not an ancestor of working directory') % node.short(root)
2223 _(b'%s is not an ancestor of working directory') % node.short(root)
2223 )
2224 )
2224
2225
2225 ctxs = [repo[r] for r in revs]
2226 ctxs = [repo[r] for r in revs]
2226
2227
2227 wctx = repo[None]
2228 wctx = repo[None]
2228 # Please don't ask me why `ancestors` is this value. I figured it
2229 # Please don't ask me why `ancestors` is this value. I figured it
2229 # out with print-debugging, not by actually understanding what the
2230 # out with print-debugging, not by actually understanding what the
2230 # merge code is doing. :(
2231 # merge code is doing. :(
2231 ancs = [repo[b'.']]
2232 ancs = [repo[b'.']]
2232 # Sniff-test to make sure we won't collide with untracked files in
2233 # Sniff-test to make sure we won't collide with untracked files in
2233 # the working directory. If we don't do this, we can get a
2234 # the working directory. If we don't do this, we can get a
2234 # collision after we've started histedit and backing out gets ugly
2235 # collision after we've started histedit and backing out gets ugly
2235 # for everyone, especially the user.
2236 # for everyone, especially the user.
2236 for c in [ctxs[0].p1()] + ctxs:
2237 for c in [ctxs[0].p1()] + ctxs:
2237 try:
2238 try:
2238 mergemod.calculateupdates(
2239 mergemod.calculateupdates(
2239 repo,
2240 repo,
2240 wctx,
2241 wctx,
2241 c,
2242 c,
2242 ancs,
2243 ancs,
2243 # These parameters were determined by print-debugging
2244 # These parameters were determined by print-debugging
2244 # what happens later on inside histedit.
2245 # what happens later on inside histedit.
2245 branchmerge=False,
2246 branchmerge=False,
2246 force=False,
2247 force=False,
2247 acceptremote=False,
2248 acceptremote=False,
2248 followcopies=False,
2249 followcopies=False,
2249 )
2250 )
2250 except error.Abort:
2251 except error.Abort:
2251 raise error.Abort(
2252 raise error.Abort(
2252 _(
2253 _(
2253 b"untracked files in working directory conflict with files in %s"
2254 b"untracked files in working directory conflict with files in %s"
2254 )
2255 )
2255 % c
2256 % c
2256 )
2257 )
2257
2258
2258 if not rules:
2259 if not rules:
2259 comment = geteditcomment(ui, node.short(root), node.short(topmost))
2260 comment = geteditcomment(ui, node.short(root), node.short(topmost))
2260 actions = [pick(state, r) for r in revs]
2261 actions = [pick(state, r) for r in revs]
2261 rules = ruleeditor(repo, ui, actions, comment)
2262 rules = ruleeditor(repo, ui, actions, comment)
2262 else:
2263 else:
2263 rules = _readfile(ui, rules)
2264 rules = _readfile(ui, rules)
2264 actions = parserules(rules, state)
2265 actions = parserules(rules, state)
2265 warnverifyactions(ui, repo, actions, state, ctxs)
2266 warnverifyactions(ui, repo, actions, state, ctxs)
2266
2267
2267 parentctxnode = repo[root].p1().node()
2268 parentctxnode = repo[root].p1().node()
2268
2269
2269 state.parentctxnode = parentctxnode
2270 state.parentctxnode = parentctxnode
2270 state.actions = actions
2271 state.actions = actions
2271 state.topmost = topmost
2272 state.topmost = topmost
2272 state.replacements = []
2273 state.replacements = []
2273
2274
2274 ui.log(
2275 ui.log(
2275 b"histedit",
2276 b"histedit",
2276 b"%d actions to histedit\n",
2277 b"%d actions to histedit\n",
2277 len(actions),
2278 len(actions),
2278 histedit_num_actions=len(actions),
2279 histedit_num_actions=len(actions),
2279 )
2280 )
2280
2281
2281 # Create a backup so we can always abort completely.
2282 # Create a backup so we can always abort completely.
2282 backupfile = None
2283 backupfile = None
2283 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2284 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2284 backupfile = repair.backupbundle(
2285 backupfile = repair.backupbundle(
2285 repo, [parentctxnode], [topmost], root, b'histedit'
2286 repo, [parentctxnode], [topmost], root, b'histedit'
2286 )
2287 )
2287 state.backupfile = backupfile
2288 state.backupfile = backupfile
2288
2289
2289
2290
2290 def _getsummary(ctx):
2291 def _getsummary(ctx):
2291 # a common pattern is to extract the summary but default to the empty
2292 # a common pattern is to extract the summary but default to the empty
2292 # string
2293 # string
2293 summary = ctx.description() or b''
2294 summary = ctx.description() or b''
2294 if summary:
2295 if summary:
2295 summary = summary.splitlines()[0]
2296 summary = summary.splitlines()[0]
2296 return summary
2297 return summary
2297
2298
2298
2299
2299 def bootstrapcontinue(ui, state, opts):
2300 def bootstrapcontinue(ui, state, opts):
2300 repo = state.repo
2301 repo = state.repo
2301
2302
2302 ms = mergestatemod.mergestate.read(repo)
2303 ms = mergestatemod.mergestate.read(repo)
2303 mergeutil.checkunresolved(ms)
2304 mergeutil.checkunresolved(ms)
2304
2305
2305 if state.actions:
2306 if state.actions:
2306 actobj = state.actions.pop(0)
2307 actobj = state.actions.pop(0)
2307
2308
2308 if _isdirtywc(repo):
2309 if _isdirtywc(repo):
2309 actobj.continuedirty()
2310 actobj.continuedirty()
2310 if _isdirtywc(repo):
2311 if _isdirtywc(repo):
2311 abortdirty()
2312 abortdirty()
2312
2313
2313 parentctx, replacements = actobj.continueclean()
2314 parentctx, replacements = actobj.continueclean()
2314
2315
2315 state.parentctxnode = parentctx.node()
2316 state.parentctxnode = parentctx.node()
2316 state.replacements.extend(replacements)
2317 state.replacements.extend(replacements)
2317
2318
2318 return state
2319 return state
2319
2320
2320
2321
2321 def between(repo, old, new, keep):
2322 def between(repo, old, new, keep):
2322 """select and validate the set of revision to edit
2323 """select and validate the set of revision to edit
2323
2324
2324 When keep is false, the specified set can't have children."""
2325 When keep is false, the specified set can't have children."""
2325 revs = repo.revs(b'%n::%n', old, new)
2326 revs = repo.revs(b'%n::%n', old, new)
2326 if revs and not keep:
2327 if revs and not keep:
2327 rewriteutil.precheck(repo, revs, b'edit')
2328 rewriteutil.precheck(repo, revs, b'edit')
2328 if repo.revs(b'(%ld) and merge()', revs):
2329 if repo.revs(b'(%ld) and merge()', revs):
2329 raise error.Abort(_(b'cannot edit history that contains merges'))
2330 raise error.Abort(_(b'cannot edit history that contains merges'))
2330 return pycompat.maplist(repo.changelog.node, revs)
2331 return pycompat.maplist(repo.changelog.node, revs)
2331
2332
2332
2333
2333 def ruleeditor(repo, ui, actions, editcomment=b""):
2334 def ruleeditor(repo, ui, actions, editcomment=b""):
2334 """open an editor to edit rules
2335 """open an editor to edit rules
2335
2336
2336 rules are in the format [ [act, ctx], ...] like in state.rules
2337 rules are in the format [ [act, ctx], ...] like in state.rules
2337 """
2338 """
2338 if repo.ui.configbool(b"experimental", b"histedit.autoverb"):
2339 if repo.ui.configbool(b"experimental", b"histedit.autoverb"):
2339 newact = util.sortdict()
2340 newact = util.sortdict()
2340 for act in actions:
2341 for act in actions:
2341 ctx = repo[act.node]
2342 ctx = repo[act.node]
2342 summary = _getsummary(ctx)
2343 summary = _getsummary(ctx)
2343 fword = summary.split(b' ', 1)[0].lower()
2344 fword = summary.split(b' ', 1)[0].lower()
2344 added = False
2345 added = False
2345
2346
2346 # if it doesn't end with the special character '!' just skip this
2347 # if it doesn't end with the special character '!' just skip this
2347 if fword.endswith(b'!'):
2348 if fword.endswith(b'!'):
2348 fword = fword[:-1]
2349 fword = fword[:-1]
2349 if fword in primaryactions | secondaryactions | tertiaryactions:
2350 if fword in primaryactions | secondaryactions | tertiaryactions:
2350 act.verb = fword
2351 act.verb = fword
2351 # get the target summary
2352 # get the target summary
2352 tsum = summary[len(fword) + 1 :].lstrip()
2353 tsum = summary[len(fword) + 1 :].lstrip()
2353 # safe but slow: reverse iterate over the actions so we
2354 # safe but slow: reverse iterate over the actions so we
2354 # don't clash on two commits having the same summary
2355 # don't clash on two commits having the same summary
2355 for na, l in reversed(list(pycompat.iteritems(newact))):
2356 for na, l in reversed(list(pycompat.iteritems(newact))):
2356 actx = repo[na.node]
2357 actx = repo[na.node]
2357 asum = _getsummary(actx)
2358 asum = _getsummary(actx)
2358 if asum == tsum:
2359 if asum == tsum:
2359 added = True
2360 added = True
2360 l.append(act)
2361 l.append(act)
2361 break
2362 break
2362
2363
2363 if not added:
2364 if not added:
2364 newact[act] = []
2365 newact[act] = []
2365
2366
2366 # copy over and flatten the new list
2367 # copy over and flatten the new list
2367 actions = []
2368 actions = []
2368 for na, l in pycompat.iteritems(newact):
2369 for na, l in pycompat.iteritems(newact):
2369 actions.append(na)
2370 actions.append(na)
2370 actions += l
2371 actions += l
2371
2372
2372 rules = b'\n'.join([act.torule() for act in actions])
2373 rules = b'\n'.join([act.torule() for act in actions])
2373 rules += b'\n\n'
2374 rules += b'\n\n'
2374 rules += editcomment
2375 rules += editcomment
2375 rules = ui.edit(
2376 rules = ui.edit(
2376 rules,
2377 rules,
2377 ui.username(),
2378 ui.username(),
2378 {b'prefix': b'histedit'},
2379 {b'prefix': b'histedit'},
2379 repopath=repo.path,
2380 repopath=repo.path,
2380 action=b'histedit',
2381 action=b'histedit',
2381 )
2382 )
2382
2383
2383 # Save edit rules in .hg/histedit-last-edit.txt in case
2384 # Save edit rules in .hg/histedit-last-edit.txt in case
2384 # the user needs to ask for help after something
2385 # the user needs to ask for help after something
2385 # surprising happens.
2386 # surprising happens.
2386 with repo.vfs(b'histedit-last-edit.txt', b'wb') as f:
2387 with repo.vfs(b'histedit-last-edit.txt', b'wb') as f:
2387 f.write(rules)
2388 f.write(rules)
2388
2389
2389 return rules
2390 return rules
2390
2391
2391
2392
2392 def parserules(rules, state):
2393 def parserules(rules, state):
2393 """Read the histedit rules string and return list of action objects """
2394 """Read the histedit rules string and return list of action objects """
2394 rules = [
2395 rules = [
2395 l
2396 l
2396 for l in (r.strip() for r in rules.splitlines())
2397 for l in (r.strip() for r in rules.splitlines())
2397 if l and not l.startswith(b'#')
2398 if l and not l.startswith(b'#')
2398 ]
2399 ]
2399 actions = []
2400 actions = []
2400 for r in rules:
2401 for r in rules:
2401 if b' ' not in r:
2402 if b' ' not in r:
2402 raise error.ParseError(_(b'malformed line "%s"') % r)
2403 raise error.ParseError(_(b'malformed line "%s"') % r)
2403 verb, rest = r.split(b' ', 1)
2404 verb, rest = r.split(b' ', 1)
2404
2405
2405 if verb not in actiontable:
2406 if verb not in actiontable:
2406 raise error.ParseError(_(b'unknown action "%s"') % verb)
2407 raise error.ParseError(_(b'unknown action "%s"') % verb)
2407
2408
2408 action = actiontable[verb].fromrule(state, rest)
2409 action = actiontable[verb].fromrule(state, rest)
2409 actions.append(action)
2410 actions.append(action)
2410 return actions
2411 return actions
2411
2412
2412
2413
2413 def warnverifyactions(ui, repo, actions, state, ctxs):
2414 def warnverifyactions(ui, repo, actions, state, ctxs):
2414 try:
2415 try:
2415 verifyactions(actions, state, ctxs)
2416 verifyactions(actions, state, ctxs)
2416 except error.ParseError:
2417 except error.ParseError:
2417 if repo.vfs.exists(b'histedit-last-edit.txt'):
2418 if repo.vfs.exists(b'histedit-last-edit.txt'):
2418 ui.warn(
2419 ui.warn(
2419 _(
2420 _(
2420 b'warning: histedit rules saved '
2421 b'warning: histedit rules saved '
2421 b'to: .hg/histedit-last-edit.txt\n'
2422 b'to: .hg/histedit-last-edit.txt\n'
2422 )
2423 )
2423 )
2424 )
2424 raise
2425 raise
2425
2426
2426
2427
2427 def verifyactions(actions, state, ctxs):
2428 def verifyactions(actions, state, ctxs):
2428 """Verify that there exists exactly one action per given changeset and
2429 """Verify that there exists exactly one action per given changeset and
2429 other constraints.
2430 other constraints.
2430
2431
2431 Will abort if there are to many or too few rules, a malformed rule,
2432 Will abort if there are to many or too few rules, a malformed rule,
2432 or a rule on a changeset outside of the user-given range.
2433 or a rule on a changeset outside of the user-given range.
2433 """
2434 """
2434 expected = {c.node() for c in ctxs}
2435 expected = {c.node() for c in ctxs}
2435 seen = set()
2436 seen = set()
2436 prev = None
2437 prev = None
2437
2438
2438 if actions and actions[0].verb in [b'roll', b'fold']:
2439 if actions and actions[0].verb in [b'roll', b'fold']:
2439 raise error.ParseError(
2440 raise error.ParseError(
2440 _(b'first changeset cannot use verb "%s"') % actions[0].verb
2441 _(b'first changeset cannot use verb "%s"') % actions[0].verb
2441 )
2442 )
2442
2443
2443 for action in actions:
2444 for action in actions:
2444 action.verify(prev, expected, seen)
2445 action.verify(prev, expected, seen)
2445 prev = action
2446 prev = action
2446 if action.node is not None:
2447 if action.node is not None:
2447 seen.add(action.node)
2448 seen.add(action.node)
2448 missing = sorted(expected - seen) # sort to stabilize output
2449 missing = sorted(expected - seen) # sort to stabilize output
2449
2450
2450 if state.repo.ui.configbool(b'histedit', b'dropmissing'):
2451 if state.repo.ui.configbool(b'histedit', b'dropmissing'):
2451 if len(actions) == 0:
2452 if len(actions) == 0:
2452 raise error.ParseError(
2453 raise error.ParseError(
2453 _(b'no rules provided'),
2454 _(b'no rules provided'),
2454 hint=_(b'use strip extension to remove commits'),
2455 hint=_(b'use strip extension to remove commits'),
2455 )
2456 )
2456
2457
2457 drops = [drop(state, n) for n in missing]
2458 drops = [drop(state, n) for n in missing]
2458 # put the in the beginning so they execute immediately and
2459 # put the in the beginning so they execute immediately and
2459 # don't show in the edit-plan in the future
2460 # don't show in the edit-plan in the future
2460 actions[:0] = drops
2461 actions[:0] = drops
2461 elif missing:
2462 elif missing:
2462 raise error.ParseError(
2463 raise error.ParseError(
2463 _(b'missing rules for changeset %s') % node.short(missing[0]),
2464 _(b'missing rules for changeset %s') % node.short(missing[0]),
2464 hint=_(
2465 hint=_(
2465 b'use "drop %s" to discard, see also: '
2466 b'use "drop %s" to discard, see also: '
2466 b"'hg help -e histedit.config'"
2467 b"'hg help -e histedit.config'"
2467 )
2468 )
2468 % node.short(missing[0]),
2469 % node.short(missing[0]),
2469 )
2470 )
2470
2471
2471
2472
2472 def adjustreplacementsfrommarkers(repo, oldreplacements):
2473 def adjustreplacementsfrommarkers(repo, oldreplacements):
2473 """Adjust replacements from obsolescence markers
2474 """Adjust replacements from obsolescence markers
2474
2475
2475 Replacements structure is originally generated based on
2476 Replacements structure is originally generated based on
2476 histedit's state and does not account for changes that are
2477 histedit's state and does not account for changes that are
2477 not recorded there. This function fixes that by adding
2478 not recorded there. This function fixes that by adding
2478 data read from obsolescence markers"""
2479 data read from obsolescence markers"""
2479 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2480 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2480 return oldreplacements
2481 return oldreplacements
2481
2482
2482 unfi = repo.unfiltered()
2483 unfi = repo.unfiltered()
2483 get_rev = unfi.changelog.index.get_rev
2484 get_rev = unfi.changelog.index.get_rev
2484 obsstore = repo.obsstore
2485 obsstore = repo.obsstore
2485 newreplacements = list(oldreplacements)
2486 newreplacements = list(oldreplacements)
2486 oldsuccs = [r[1] for r in oldreplacements]
2487 oldsuccs = [r[1] for r in oldreplacements]
2487 # successors that have already been added to succstocheck once
2488 # successors that have already been added to succstocheck once
2488 seensuccs = set().union(
2489 seensuccs = set().union(
2489 *oldsuccs
2490 *oldsuccs
2490 ) # create a set from an iterable of tuples
2491 ) # create a set from an iterable of tuples
2491 succstocheck = list(seensuccs)
2492 succstocheck = list(seensuccs)
2492 while succstocheck:
2493 while succstocheck:
2493 n = succstocheck.pop()
2494 n = succstocheck.pop()
2494 missing = get_rev(n) is None
2495 missing = get_rev(n) is None
2495 markers = obsstore.successors.get(n, ())
2496 markers = obsstore.successors.get(n, ())
2496 if missing and not markers:
2497 if missing and not markers:
2497 # dead end, mark it as such
2498 # dead end, mark it as such
2498 newreplacements.append((n, ()))
2499 newreplacements.append((n, ()))
2499 for marker in markers:
2500 for marker in markers:
2500 nsuccs = marker[1]
2501 nsuccs = marker[1]
2501 newreplacements.append((n, nsuccs))
2502 newreplacements.append((n, nsuccs))
2502 for nsucc in nsuccs:
2503 for nsucc in nsuccs:
2503 if nsucc not in seensuccs:
2504 if nsucc not in seensuccs:
2504 seensuccs.add(nsucc)
2505 seensuccs.add(nsucc)
2505 succstocheck.append(nsucc)
2506 succstocheck.append(nsucc)
2506
2507
2507 return newreplacements
2508 return newreplacements
2508
2509
2509
2510
2510 def processreplacement(state):
2511 def processreplacement(state):
2511 """process the list of replacements to return
2512 """process the list of replacements to return
2512
2513
2513 1) the final mapping between original and created nodes
2514 1) the final mapping between original and created nodes
2514 2) the list of temporary node created by histedit
2515 2) the list of temporary node created by histedit
2515 3) the list of new commit created by histedit"""
2516 3) the list of new commit created by histedit"""
2516 replacements = adjustreplacementsfrommarkers(state.repo, state.replacements)
2517 replacements = adjustreplacementsfrommarkers(state.repo, state.replacements)
2517 allsuccs = set()
2518 allsuccs = set()
2518 replaced = set()
2519 replaced = set()
2519 fullmapping = {}
2520 fullmapping = {}
2520 # initialize basic set
2521 # initialize basic set
2521 # fullmapping records all operations recorded in replacement
2522 # fullmapping records all operations recorded in replacement
2522 for rep in replacements:
2523 for rep in replacements:
2523 allsuccs.update(rep[1])
2524 allsuccs.update(rep[1])
2524 replaced.add(rep[0])
2525 replaced.add(rep[0])
2525 fullmapping.setdefault(rep[0], set()).update(rep[1])
2526 fullmapping.setdefault(rep[0], set()).update(rep[1])
2526 new = allsuccs - replaced
2527 new = allsuccs - replaced
2527 tmpnodes = allsuccs & replaced
2528 tmpnodes = allsuccs & replaced
2528 # Reduce content fullmapping into direct relation between original nodes
2529 # Reduce content fullmapping into direct relation between original nodes
2529 # and final node created during history edition
2530 # and final node created during history edition
2530 # Dropped changeset are replaced by an empty list
2531 # Dropped changeset are replaced by an empty list
2531 toproceed = set(fullmapping)
2532 toproceed = set(fullmapping)
2532 final = {}
2533 final = {}
2533 while toproceed:
2534 while toproceed:
2534 for x in list(toproceed):
2535 for x in list(toproceed):
2535 succs = fullmapping[x]
2536 succs = fullmapping[x]
2536 for s in list(succs):
2537 for s in list(succs):
2537 if s in toproceed:
2538 if s in toproceed:
2538 # non final node with unknown closure
2539 # non final node with unknown closure
2539 # We can't process this now
2540 # We can't process this now
2540 break
2541 break
2541 elif s in final:
2542 elif s in final:
2542 # non final node, replace with closure
2543 # non final node, replace with closure
2543 succs.remove(s)
2544 succs.remove(s)
2544 succs.update(final[s])
2545 succs.update(final[s])
2545 else:
2546 else:
2546 final[x] = succs
2547 final[x] = succs
2547 toproceed.remove(x)
2548 toproceed.remove(x)
2548 # remove tmpnodes from final mapping
2549 # remove tmpnodes from final mapping
2549 for n in tmpnodes:
2550 for n in tmpnodes:
2550 del final[n]
2551 del final[n]
2551 # we expect all changes involved in final to exist in the repo
2552 # we expect all changes involved in final to exist in the repo
2552 # turn `final` into list (topologically sorted)
2553 # turn `final` into list (topologically sorted)
2553 get_rev = state.repo.changelog.index.get_rev
2554 get_rev = state.repo.changelog.index.get_rev
2554 for prec, succs in final.items():
2555 for prec, succs in final.items():
2555 final[prec] = sorted(succs, key=get_rev)
2556 final[prec] = sorted(succs, key=get_rev)
2556
2557
2557 # computed topmost element (necessary for bookmark)
2558 # computed topmost element (necessary for bookmark)
2558 if new:
2559 if new:
2559 newtopmost = sorted(new, key=state.repo.changelog.rev)[-1]
2560 newtopmost = sorted(new, key=state.repo.changelog.rev)[-1]
2560 elif not final:
2561 elif not final:
2561 # Nothing rewritten at all. we won't need `newtopmost`
2562 # Nothing rewritten at all. we won't need `newtopmost`
2562 # It is the same as `oldtopmost` and `processreplacement` know it
2563 # It is the same as `oldtopmost` and `processreplacement` know it
2563 newtopmost = None
2564 newtopmost = None
2564 else:
2565 else:
2565 # every body died. The newtopmost is the parent of the root.
2566 # every body died. The newtopmost is the parent of the root.
2566 r = state.repo.changelog.rev
2567 r = state.repo.changelog.rev
2567 newtopmost = state.repo[sorted(final, key=r)[0]].p1().node()
2568 newtopmost = state.repo[sorted(final, key=r)[0]].p1().node()
2568
2569
2569 return final, tmpnodes, new, newtopmost
2570 return final, tmpnodes, new, newtopmost
2570
2571
2571
2572
2572 def movetopmostbookmarks(repo, oldtopmost, newtopmost):
2573 def movetopmostbookmarks(repo, oldtopmost, newtopmost):
2573 """Move bookmark from oldtopmost to newly created topmost
2574 """Move bookmark from oldtopmost to newly created topmost
2574
2575
2575 This is arguably a feature and we may only want that for the active
2576 This is arguably a feature and we may only want that for the active
2576 bookmark. But the behavior is kept compatible with the old version for now.
2577 bookmark. But the behavior is kept compatible with the old version for now.
2577 """
2578 """
2578 if not oldtopmost or not newtopmost:
2579 if not oldtopmost or not newtopmost:
2579 return
2580 return
2580 oldbmarks = repo.nodebookmarks(oldtopmost)
2581 oldbmarks = repo.nodebookmarks(oldtopmost)
2581 if oldbmarks:
2582 if oldbmarks:
2582 with repo.lock(), repo.transaction(b'histedit') as tr:
2583 with repo.lock(), repo.transaction(b'histedit') as tr:
2583 marks = repo._bookmarks
2584 marks = repo._bookmarks
2584 changes = []
2585 changes = []
2585 for name in oldbmarks:
2586 for name in oldbmarks:
2586 changes.append((name, newtopmost))
2587 changes.append((name, newtopmost))
2587 marks.applychanges(repo, tr, changes)
2588 marks.applychanges(repo, tr, changes)
2588
2589
2589
2590
2590 def cleanupnode(ui, repo, nodes, nobackup=False):
2591 def cleanupnode(ui, repo, nodes, nobackup=False):
2591 """strip a group of nodes from the repository
2592 """strip a group of nodes from the repository
2592
2593
2593 The set of node to strip may contains unknown nodes."""
2594 The set of node to strip may contains unknown nodes."""
2594 with repo.lock():
2595 with repo.lock():
2595 # do not let filtering get in the way of the cleanse
2596 # do not let filtering get in the way of the cleanse
2596 # we should probably get rid of obsolescence marker created during the
2597 # we should probably get rid of obsolescence marker created during the
2597 # histedit, but we currently do not have such information.
2598 # histedit, but we currently do not have such information.
2598 repo = repo.unfiltered()
2599 repo = repo.unfiltered()
2599 # Find all nodes that need to be stripped
2600 # Find all nodes that need to be stripped
2600 # (we use %lr instead of %ln to silently ignore unknown items)
2601 # (we use %lr instead of %ln to silently ignore unknown items)
2601 has_node = repo.changelog.index.has_node
2602 has_node = repo.changelog.index.has_node
2602 nodes = sorted(n for n in nodes if has_node(n))
2603 nodes = sorted(n for n in nodes if has_node(n))
2603 roots = [c.node() for c in repo.set(b"roots(%ln)", nodes)]
2604 roots = [c.node() for c in repo.set(b"roots(%ln)", nodes)]
2604 if roots:
2605 if roots:
2605 backup = not nobackup
2606 backup = not nobackup
2606 repair.strip(ui, repo, roots, backup=backup)
2607 repair.strip(ui, repo, roots, backup=backup)
2607
2608
2608
2609
2609 def stripwrapper(orig, ui, repo, nodelist, *args, **kwargs):
2610 def stripwrapper(orig, ui, repo, nodelist, *args, **kwargs):
2610 if isinstance(nodelist, bytes):
2611 if isinstance(nodelist, bytes):
2611 nodelist = [nodelist]
2612 nodelist = [nodelist]
2612 state = histeditstate(repo)
2613 state = histeditstate(repo)
2613 if state.inprogress():
2614 if state.inprogress():
2614 state.read()
2615 state.read()
2615 histedit_nodes = {
2616 histedit_nodes = {
2616 action.node for action in state.actions if action.node
2617 action.node for action in state.actions if action.node
2617 }
2618 }
2618 common_nodes = histedit_nodes & set(nodelist)
2619 common_nodes = histedit_nodes & set(nodelist)
2619 if common_nodes:
2620 if common_nodes:
2620 raise error.Abort(
2621 raise error.Abort(
2621 _(b"histedit in progress, can't strip %s")
2622 _(b"histedit in progress, can't strip %s")
2622 % b', '.join(node.short(x) for x in common_nodes)
2623 % b', '.join(node.short(x) for x in common_nodes)
2623 )
2624 )
2624 return orig(ui, repo, nodelist, *args, **kwargs)
2625 return orig(ui, repo, nodelist, *args, **kwargs)
2625
2626
2626
2627
2627 extensions.wrapfunction(repair, b'strip', stripwrapper)
2628 extensions.wrapfunction(repair, b'strip', stripwrapper)
2628
2629
2629
2630
2630 def summaryhook(ui, repo):
2631 def summaryhook(ui, repo):
2631 state = histeditstate(repo)
2632 state = histeditstate(repo)
2632 if not state.inprogress():
2633 if not state.inprogress():
2633 return
2634 return
2634 state.read()
2635 state.read()
2635 if state.actions:
2636 if state.actions:
2636 # i18n: column positioning for "hg summary"
2637 # i18n: column positioning for "hg summary"
2637 ui.write(
2638 ui.write(
2638 _(b'hist: %s (histedit --continue)\n')
2639 _(b'hist: %s (histedit --continue)\n')
2639 % (
2640 % (
2640 ui.label(_(b'%d remaining'), b'histedit.remaining')
2641 ui.label(_(b'%d remaining'), b'histedit.remaining')
2641 % len(state.actions)
2642 % len(state.actions)
2642 )
2643 )
2643 )
2644 )
2644
2645
2645
2646
2646 def extsetup(ui):
2647 def extsetup(ui):
2647 cmdutil.summaryhooks.add(b'histedit', summaryhook)
2648 cmdutil.summaryhooks.add(b'histedit', summaryhook)
2648 statemod.addunfinished(
2649 statemod.addunfinished(
2649 b'histedit',
2650 b'histedit',
2650 fname=b'histedit-state',
2651 fname=b'histedit-state',
2651 allowcommit=True,
2652 allowcommit=True,
2652 continueflag=True,
2653 continueflag=True,
2653 abortfunc=hgaborthistedit,
2654 abortfunc=hgaborthistedit,
2654 )
2655 )
@@ -1,605 +1,605 b''
1 #testcases abortcommand abortflag
1 #testcases abortcommand abortflag
2
2
3 #if abortflag
3 #if abortflag
4 $ cat >> $HGRCPATH <<EOF
4 $ cat >> $HGRCPATH <<EOF
5 > [alias]
5 > [alias]
6 > abort = histedit --abort
6 > abort = histedit --abort
7 > EOF
7 > EOF
8 #endif
8 #endif
9
9
10 Test argument handling and various data parsing
10 Test argument handling and various data parsing
11 ==================================================
11 ==================================================
12
12
13
13
14 Enable extensions used by this test.
14 Enable extensions used by this test.
15 $ cat >>$HGRCPATH <<EOF
15 $ cat >>$HGRCPATH <<EOF
16 > [extensions]
16 > [extensions]
17 > histedit=
17 > histedit=
18 > EOF
18 > EOF
19
19
20 Repo setup.
20 Repo setup.
21 $ hg init foo
21 $ hg init foo
22 $ cd foo
22 $ cd foo
23 $ echo alpha >> alpha
23 $ echo alpha >> alpha
24 $ hg addr
24 $ hg addr
25 adding alpha
25 adding alpha
26 $ hg ci -m one
26 $ hg ci -m one
27 $ echo alpha >> alpha
27 $ echo alpha >> alpha
28 $ hg ci -m two
28 $ hg ci -m two
29 $ echo alpha >> alpha
29 $ echo alpha >> alpha
30 $ hg ci -m three
30 $ hg ci -m three
31 $ echo alpha >> alpha
31 $ echo alpha >> alpha
32 $ hg ci -m four
32 $ hg ci -m four
33 $ echo alpha >> alpha
33 $ echo alpha >> alpha
34 $ hg ci -m five
34 $ hg ci -m five
35
35
36 $ hg log --style compact --graph
36 $ hg log --style compact --graph
37 @ 4[tip] 08d98a8350f3 1970-01-01 00:00 +0000 test
37 @ 4[tip] 08d98a8350f3 1970-01-01 00:00 +0000 test
38 | five
38 | five
39 |
39 |
40 o 3 c8e68270e35a 1970-01-01 00:00 +0000 test
40 o 3 c8e68270e35a 1970-01-01 00:00 +0000 test
41 | four
41 | four
42 |
42 |
43 o 2 eb57da33312f 1970-01-01 00:00 +0000 test
43 o 2 eb57da33312f 1970-01-01 00:00 +0000 test
44 | three
44 | three
45 |
45 |
46 o 1 579e40513370 1970-01-01 00:00 +0000 test
46 o 1 579e40513370 1970-01-01 00:00 +0000 test
47 | two
47 | two
48 |
48 |
49 o 0 6058cbb6cfd7 1970-01-01 00:00 +0000 test
49 o 0 6058cbb6cfd7 1970-01-01 00:00 +0000 test
50 one
50 one
51
51
52
52
53 histedit --continue/--abort with no existing state
53 histedit --continue/--abort with no existing state
54 --------------------------------------------------
54 --------------------------------------------------
55
55
56 $ hg histedit --continue
56 $ hg histedit --continue
57 abort: no histedit in progress
57 abort: no histedit in progress
58 [20]
58 [20]
59 $ hg abort
59 $ hg abort
60 abort: no histedit in progress (abortflag !)
60 abort: no histedit in progress (abortflag !)
61 abort: no operation in progress (abortcommand !)
61 abort: no operation in progress (abortcommand !)
62 [20]
62 [20]
63
63
64 Run a dummy edit to make sure we get tip^^ correctly via revsingle.
64 Run a dummy edit to make sure we get tip^^ correctly via revsingle.
65 --------------------------------------------------------------------
65 --------------------------------------------------------------------
66
66
67 $ HGEDITOR=cat hg histedit "tip^^"
67 $ HGEDITOR=cat hg histedit "tip^^"
68 pick eb57da33312f 2 three
68 pick eb57da33312f 2 three
69 pick c8e68270e35a 3 four
69 pick c8e68270e35a 3 four
70 pick 08d98a8350f3 4 five
70 pick 08d98a8350f3 4 five
71
71
72 # Edit history between eb57da33312f and 08d98a8350f3
72 # Edit history between eb57da33312f and 08d98a8350f3
73 #
73 #
74 # Commits are listed from least to most recent
74 # Commits are listed from least to most recent
75 #
75 #
76 # You can reorder changesets by reordering the lines
76 # You can reorder changesets by reordering the lines
77 #
77 #
78 # Commands:
78 # Commands:
79 #
79 #
80 # e, edit = use commit, but stop for amending
80 # e, edit = use commit, but stop for amending
81 # m, mess = edit commit message without changing commit content
81 # m, mess = edit commit message without changing commit content
82 # p, pick = use commit
82 # p, pick = use commit
83 # b, base = checkout changeset and apply further changesets from there
83 # b, base = checkout changeset and apply further changesets from there
84 # d, drop = remove commit from history
84 # d, drop = remove commit from history
85 # f, fold = use commit, but combine it with the one above
85 # f, fold = use commit, but combine it with the one above
86 # r, roll = like fold, but discard this commit's description and date
86 # r, roll = like fold, but discard this commit's description and date
87 #
87 #
88
88
89 Run on a revision not ancestors of the current working directory.
89 Run on a revision not ancestors of the current working directory.
90 --------------------------------------------------------------------
90 --------------------------------------------------------------------
91
91
92 $ hg up 2
92 $ hg up 2
93 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
93 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
94 $ hg histedit -r 4
94 $ hg histedit -r 4
95 abort: 08d98a8350f3 is not an ancestor of working directory
95 abort: 08d98a8350f3 is not an ancestor of working directory
96 [255]
96 [255]
97 $ hg up --quiet
97 $ hg up --quiet
98
98
99
99
100 Test that we pick the minimum of a revrange
100 Test that we pick the minimum of a revrange
101 ---------------------------------------
101 ---------------------------------------
102
102
103 $ HGEDITOR=cat hg histedit '2::' --commands - << EOF
103 $ HGEDITOR=cat hg histedit '2::' --commands - << EOF
104 > pick eb57da33312f 2 three
104 > pick eb57da33312f 2 three
105 > pick c8e68270e35a 3 four
105 > pick c8e68270e35a 3 four
106 > pick 08d98a8350f3 4 five
106 > pick 08d98a8350f3 4 five
107 > EOF
107 > EOF
108 $ hg up --quiet
108 $ hg up --quiet
109
109
110 $ HGEDITOR=cat hg histedit 'tip:2' --commands - << EOF
110 $ HGEDITOR=cat hg histedit 'tip:2' --commands - << EOF
111 > pick eb57da33312f 2 three
111 > pick eb57da33312f 2 three
112 > pick c8e68270e35a 3 four
112 > pick c8e68270e35a 3 four
113 > pick 08d98a8350f3 4 five
113 > pick 08d98a8350f3 4 five
114 > EOF
114 > EOF
115 $ hg up --quiet
115 $ hg up --quiet
116
116
117 Test config specified default
117 Test config specified default
118 -----------------------------
118 -----------------------------
119
119
120 $ HGEDITOR=cat hg histedit --config "histedit.defaultrev=only(.) - ::eb57da33312f" --commands - << EOF
120 $ HGEDITOR=cat hg histedit --config "histedit.defaultrev=only(.) - ::eb57da33312f" --commands - << EOF
121 > pick c8e68270e35a 3 four
121 > pick c8e68270e35a 3 four
122 > pick 08d98a8350f3 4 five
122 > pick 08d98a8350f3 4 five
123 > EOF
123 > EOF
124
124
125 Test invalid config default
125 Test invalid config default
126 ---------------------------
126 ---------------------------
127
127
128 $ hg histedit --config "histedit.defaultrev="
128 $ hg histedit --config "histedit.defaultrev="
129 config error: config option histedit.defaultrev can't be empty
129 config error: config option histedit.defaultrev can't be empty
130 [30]
130 [30]
131
131
132 Run on a revision not descendants of the initial parent
132 Run on a revision not descendants of the initial parent
133 --------------------------------------------------------------------
133 --------------------------------------------------------------------
134
134
135 Test the message shown for inconsistent histedit state, which may be
135 Test the message shown for inconsistent histedit state, which may be
136 created (and forgotten) by Mercurial earlier than 2.7. This emulates
136 created (and forgotten) by Mercurial earlier than 2.7. This emulates
137 Mercurial earlier than 2.7 by renaming ".hg/histedit-state"
137 Mercurial earlier than 2.7 by renaming ".hg/histedit-state"
138 temporarily.
138 temporarily.
139
139
140 $ hg log -G -T '{rev} {shortest(node)} {desc}\n' -r 2::
140 $ hg log -G -T '{rev} {shortest(node)} {desc}\n' -r 2::
141 @ 4 08d9 five
141 @ 4 08d9 five
142 |
142 |
143 o 3 c8e6 four
143 o 3 c8e6 four
144 |
144 |
145 o 2 eb57 three
145 o 2 eb57 three
146 |
146 |
147 ~
147 ~
148 $ HGEDITOR=cat hg histedit -r 4 --commands - << EOF
148 $ HGEDITOR=cat hg histedit -r 4 --commands - << EOF
149 > edit 08d98a8350f3 4 five
149 > edit 08d98a8350f3 4 five
150 > EOF
150 > EOF
151 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
151 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
152 Editing (08d98a8350f3), you may commit or record as needed now.
152 Editing (08d98a8350f3), commit as needed now to split the change
153 (hg histedit --continue to resume)
153 (to edit 08d98a8350f3, `hg histedit --continue` after making changes)
154 [240]
154 [240]
155
155
156 $ hg graft --continue
156 $ hg graft --continue
157 abort: no graft in progress
157 abort: no graft in progress
158 (continue: hg histedit --continue)
158 (continue: hg histedit --continue)
159 [20]
159 [20]
160
160
161 $ mv .hg/histedit-state .hg/histedit-state.back
161 $ mv .hg/histedit-state .hg/histedit-state.back
162 $ hg update --quiet --clean 2
162 $ hg update --quiet --clean 2
163 $ echo alpha >> alpha
163 $ echo alpha >> alpha
164 $ mv .hg/histedit-state.back .hg/histedit-state
164 $ mv .hg/histedit-state.back .hg/histedit-state
165
165
166 $ hg histedit --continue
166 $ hg histedit --continue
167 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/08d98a8350f3-02594089-histedit.hg
167 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/08d98a8350f3-02594089-histedit.hg
168 $ hg log -G -T '{rev} {shortest(node)} {desc}\n' -r 2::
168 $ hg log -G -T '{rev} {shortest(node)} {desc}\n' -r 2::
169 @ 4 f5ed five
169 @ 4 f5ed five
170 |
170 |
171 | o 3 c8e6 four
171 | o 3 c8e6 four
172 |/
172 |/
173 o 2 eb57 three
173 o 2 eb57 three
174 |
174 |
175 ~
175 ~
176
176
177 $ hg unbundle -q $TESTTMP/foo/.hg/strip-backup/08d98a8350f3-02594089-histedit.hg
177 $ hg unbundle -q $TESTTMP/foo/.hg/strip-backup/08d98a8350f3-02594089-histedit.hg
178 $ hg strip -q -r f5ed --config extensions.strip=
178 $ hg strip -q -r f5ed --config extensions.strip=
179 $ hg up -q 08d98a8350f3
179 $ hg up -q 08d98a8350f3
180
180
181 Test that missing revisions are detected
181 Test that missing revisions are detected
182 ---------------------------------------
182 ---------------------------------------
183
183
184 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
184 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
185 > pick eb57da33312f 2 three
185 > pick eb57da33312f 2 three
186 > pick 08d98a8350f3 4 five
186 > pick 08d98a8350f3 4 five
187 > EOF
187 > EOF
188 hg: parse error: missing rules for changeset c8e68270e35a
188 hg: parse error: missing rules for changeset c8e68270e35a
189 (use "drop c8e68270e35a" to discard, see also: 'hg help -e histedit.config')
189 (use "drop c8e68270e35a" to discard, see also: 'hg help -e histedit.config')
190 [10]
190 [10]
191
191
192 Test that extra revisions are detected
192 Test that extra revisions are detected
193 ---------------------------------------
193 ---------------------------------------
194
194
195 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
195 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
196 > pick 6058cbb6cfd7 0 one
196 > pick 6058cbb6cfd7 0 one
197 > pick c8e68270e35a 3 four
197 > pick c8e68270e35a 3 four
198 > pick 08d98a8350f3 4 five
198 > pick 08d98a8350f3 4 five
199 > EOF
199 > EOF
200 hg: parse error: pick "6058cbb6cfd7" changeset was not a candidate
200 hg: parse error: pick "6058cbb6cfd7" changeset was not a candidate
201 (only use listed changesets)
201 (only use listed changesets)
202 [10]
202 [10]
203
203
204 Test malformed line
204 Test malformed line
205 ---------------------------------------
205 ---------------------------------------
206
206
207 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
207 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
208 > pickeb57da33312f2three
208 > pickeb57da33312f2three
209 > pick c8e68270e35a 3 four
209 > pick c8e68270e35a 3 four
210 > pick 08d98a8350f3 4 five
210 > pick 08d98a8350f3 4 five
211 > EOF
211 > EOF
212 hg: parse error: malformed line "pickeb57da33312f2three"
212 hg: parse error: malformed line "pickeb57da33312f2three"
213 [10]
213 [10]
214
214
215 Test unknown changeset
215 Test unknown changeset
216 ---------------------------------------
216 ---------------------------------------
217
217
218 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
218 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
219 > pick 0123456789ab 2 three
219 > pick 0123456789ab 2 three
220 > pick c8e68270e35a 3 four
220 > pick c8e68270e35a 3 four
221 > pick 08d98a8350f3 4 five
221 > pick 08d98a8350f3 4 five
222 > EOF
222 > EOF
223 hg: parse error: unknown changeset 0123456789ab listed
223 hg: parse error: unknown changeset 0123456789ab listed
224 [10]
224 [10]
225
225
226 Test unknown command
226 Test unknown command
227 ---------------------------------------
227 ---------------------------------------
228
228
229 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
229 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
230 > coin eb57da33312f 2 three
230 > coin eb57da33312f 2 three
231 > pick c8e68270e35a 3 four
231 > pick c8e68270e35a 3 four
232 > pick 08d98a8350f3 4 five
232 > pick 08d98a8350f3 4 five
233 > EOF
233 > EOF
234 hg: parse error: unknown action "coin"
234 hg: parse error: unknown action "coin"
235 [10]
235 [10]
236
236
237 Test duplicated changeset
237 Test duplicated changeset
238 ---------------------------------------
238 ---------------------------------------
239
239
240 So one is missing and one appear twice.
240 So one is missing and one appear twice.
241
241
242 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
242 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
243 > pick eb57da33312f 2 three
243 > pick eb57da33312f 2 three
244 > pick eb57da33312f 2 three
244 > pick eb57da33312f 2 three
245 > pick 08d98a8350f3 4 five
245 > pick 08d98a8350f3 4 five
246 > EOF
246 > EOF
247 hg: parse error: duplicated command for changeset eb57da33312f
247 hg: parse error: duplicated command for changeset eb57da33312f
248 [10]
248 [10]
249
249
250 Test bogus rev
250 Test bogus rev
251 ---------------------------------------
251 ---------------------------------------
252
252
253 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
253 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
254 > pick eb57da33312f 2 three
254 > pick eb57da33312f 2 three
255 > pick 0u98
255 > pick 0u98
256 > pick 08d98a8350f3 4 five
256 > pick 08d98a8350f3 4 five
257 > EOF
257 > EOF
258 hg: parse error: invalid changeset 0u98
258 hg: parse error: invalid changeset 0u98
259 [10]
259 [10]
260
260
261 Test short version of command
261 Test short version of command
262 ---------------------------------------
262 ---------------------------------------
263
263
264 Note: we use varying amounts of white space between command name and changeset
264 Note: we use varying amounts of white space between command name and changeset
265 short hash. This tests issue3893.
265 short hash. This tests issue3893.
266
266
267 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
267 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
268 > pick eb57da33312f 2 three
268 > pick eb57da33312f 2 three
269 > p c8e68270e35a 3 four
269 > p c8e68270e35a 3 four
270 > f 08d98a8350f3 4 five
270 > f 08d98a8350f3 4 five
271 > EOF
271 > EOF
272 four
272 four
273 ***
273 ***
274 five
274 five
275
275
276
276
277
277
278 HG: Enter commit message. Lines beginning with 'HG:' are removed.
278 HG: Enter commit message. Lines beginning with 'HG:' are removed.
279 HG: Leave message empty to abort commit.
279 HG: Leave message empty to abort commit.
280 HG: --
280 HG: --
281 HG: user: test
281 HG: user: test
282 HG: branch 'default'
282 HG: branch 'default'
283 HG: changed alpha
283 HG: changed alpha
284 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/c8e68270e35a-63d8b8d8-histedit.hg
284 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/c8e68270e35a-63d8b8d8-histedit.hg
285
285
286 $ hg update -q 2
286 $ hg update -q 2
287 $ echo x > x
287 $ echo x > x
288 $ hg add x
288 $ hg add x
289 $ hg commit -m'x' x
289 $ hg commit -m'x' x
290 created new head
290 created new head
291 $ hg histedit -r 'heads(all())'
291 $ hg histedit -r 'heads(all())'
292 abort: The specified revisions must have exactly one common root
292 abort: The specified revisions must have exactly one common root
293 [255]
293 [255]
294
294
295 Test that trimming description using multi-byte characters
295 Test that trimming description using multi-byte characters
296 --------------------------------------------------------------------
296 --------------------------------------------------------------------
297
297
298 $ "$PYTHON" <<EOF
298 $ "$PYTHON" <<EOF
299 > fp = open('logfile', 'wb')
299 > fp = open('logfile', 'wb')
300 > fp.write(b'12345678901234567890123456789012345678901234567890' +
300 > fp.write(b'12345678901234567890123456789012345678901234567890' +
301 > b'12345') # there are 5 more columns for 80 columns
301 > b'12345') # there are 5 more columns for 80 columns
302 >
302 >
303 > # 2 x 4 = 8 columns, but 3 x 4 = 12 bytes
303 > # 2 x 4 = 8 columns, but 3 x 4 = 12 bytes
304 > fp.write(u'\u3042\u3044\u3046\u3048'.encode('utf-8'))
304 > fp.write(u'\u3042\u3044\u3046\u3048'.encode('utf-8'))
305 >
305 >
306 > fp.close()
306 > fp.close()
307 > EOF
307 > EOF
308 $ echo xx >> x
308 $ echo xx >> x
309 $ hg --encoding utf-8 commit --logfile logfile
309 $ hg --encoding utf-8 commit --logfile logfile
310
310
311 $ HGEDITOR=cat hg --encoding utf-8 histedit tip
311 $ HGEDITOR=cat hg --encoding utf-8 histedit tip
312 pick 3d3ea1f3a10b 5 1234567890123456789012345678901234567890123456789012345\xe3\x81\x82... (esc)
312 pick 3d3ea1f3a10b 5 1234567890123456789012345678901234567890123456789012345\xe3\x81\x82... (esc)
313
313
314 # Edit history between 3d3ea1f3a10b and 3d3ea1f3a10b
314 # Edit history between 3d3ea1f3a10b and 3d3ea1f3a10b
315 #
315 #
316 # Commits are listed from least to most recent
316 # Commits are listed from least to most recent
317 #
317 #
318 # You can reorder changesets by reordering the lines
318 # You can reorder changesets by reordering the lines
319 #
319 #
320 # Commands:
320 # Commands:
321 #
321 #
322 # e, edit = use commit, but stop for amending
322 # e, edit = use commit, but stop for amending
323 # m, mess = edit commit message without changing commit content
323 # m, mess = edit commit message without changing commit content
324 # p, pick = use commit
324 # p, pick = use commit
325 # b, base = checkout changeset and apply further changesets from there
325 # b, base = checkout changeset and apply further changesets from there
326 # d, drop = remove commit from history
326 # d, drop = remove commit from history
327 # f, fold = use commit, but combine it with the one above
327 # f, fold = use commit, but combine it with the one above
328 # r, roll = like fold, but discard this commit's description and date
328 # r, roll = like fold, but discard this commit's description and date
329 #
329 #
330
330
331 Test --continue with --keep
331 Test --continue with --keep
332
332
333 $ hg strip -q -r . --config extensions.strip=
333 $ hg strip -q -r . --config extensions.strip=
334 $ hg histedit '.^' -q --keep --commands - << EOF
334 $ hg histedit '.^' -q --keep --commands - << EOF
335 > edit eb57da33312f 2 three
335 > edit eb57da33312f 2 three
336 > pick f3cfcca30c44 4 x
336 > pick f3cfcca30c44 4 x
337 > EOF
337 > EOF
338 Editing (eb57da33312f), you may commit or record as needed now.
338 Editing (eb57da33312f), commit as needed now to split the change
339 (hg histedit --continue to resume)
339 (to edit eb57da33312f, `hg histedit --continue` after making changes)
340 [240]
340 [240]
341 $ echo edit >> alpha
341 $ echo edit >> alpha
342 $ hg histedit -q --continue
342 $ hg histedit -q --continue
343 $ hg log -G -T '{rev}:{node|short} {desc}'
343 $ hg log -G -T '{rev}:{node|short} {desc}'
344 @ 6:8fda0c726bf2 x
344 @ 6:8fda0c726bf2 x
345 |
345 |
346 o 5:63379946892c three
346 o 5:63379946892c three
347 |
347 |
348 | o 4:f3cfcca30c44 x
348 | o 4:f3cfcca30c44 x
349 | |
349 | |
350 | | o 3:2a30f3cfee78 four
350 | | o 3:2a30f3cfee78 four
351 | |/ ***
351 | |/ ***
352 | | five
352 | | five
353 | o 2:eb57da33312f three
353 | o 2:eb57da33312f three
354 |/
354 |/
355 o 1:579e40513370 two
355 o 1:579e40513370 two
356 |
356 |
357 o 0:6058cbb6cfd7 one
357 o 0:6058cbb6cfd7 one
358
358
359
359
360 Test that abort fails gracefully on exception
360 Test that abort fails gracefully on exception
361 ----------------------------------------------
361 ----------------------------------------------
362 $ hg histedit . -q --commands - << EOF
362 $ hg histedit . -q --commands - << EOF
363 > edit 8fda0c726bf2 6 x
363 > edit 8fda0c726bf2 6 x
364 > EOF
364 > EOF
365 Editing (8fda0c726bf2), you may commit or record as needed now.
365 Editing (8fda0c726bf2), commit as needed now to split the change
366 (hg histedit --continue to resume)
366 (to edit 8fda0c726bf2, `hg histedit --continue` after making changes)
367 [240]
367 [240]
368 Corrupt histedit state file
368 Corrupt histedit state file
369 $ sed 's/8fda0c726bf2/123456789012/' .hg/histedit-state > ../corrupt-histedit
369 $ sed 's/8fda0c726bf2/123456789012/' .hg/histedit-state > ../corrupt-histedit
370 $ mv ../corrupt-histedit .hg/histedit-state
370 $ mv ../corrupt-histedit .hg/histedit-state
371 $ hg abort
371 $ hg abort
372 warning: encountered an exception during histedit --abort; the repository may not have been completely cleaned up
372 warning: encountered an exception during histedit --abort; the repository may not have been completely cleaned up
373 abort: $TESTTMP/foo/.hg/strip-backup/*-histedit.hg: $ENOENT$ (glob) (windows !)
373 abort: $TESTTMP/foo/.hg/strip-backup/*-histedit.hg: $ENOENT$ (glob) (windows !)
374 abort: $ENOENT$: '$TESTTMP/foo/.hg/strip-backup/*-histedit.hg' (glob) (no-windows !)
374 abort: $ENOENT$: '$TESTTMP/foo/.hg/strip-backup/*-histedit.hg' (glob) (no-windows !)
375 [255]
375 [255]
376 Histedit state has been exited
376 Histedit state has been exited
377 $ hg summary -q
377 $ hg summary -q
378 parent: 5:63379946892c
378 parent: 5:63379946892c
379 commit: 1 added, 1 unknown (new branch head)
379 commit: 1 added, 1 unknown (new branch head)
380 update: 4 new changesets (update)
380 update: 4 new changesets (update)
381
381
382 $ cd ..
382 $ cd ..
383
383
384 Set up default base revision tests
384 Set up default base revision tests
385
385
386 $ hg init defaultbase
386 $ hg init defaultbase
387 $ cd defaultbase
387 $ cd defaultbase
388 $ touch foo
388 $ touch foo
389 $ hg -q commit -A -m root
389 $ hg -q commit -A -m root
390 $ echo 1 > foo
390 $ echo 1 > foo
391 $ hg commit -m 'public 1'
391 $ hg commit -m 'public 1'
392 $ hg phase --force --public -r .
392 $ hg phase --force --public -r .
393 $ echo 2 > foo
393 $ echo 2 > foo
394 $ hg commit -m 'draft after public'
394 $ hg commit -m 'draft after public'
395 $ hg -q up -r 1
395 $ hg -q up -r 1
396 $ echo 3 > foo
396 $ echo 3 > foo
397 $ hg commit -m 'head 1 public'
397 $ hg commit -m 'head 1 public'
398 created new head
398 created new head
399 $ hg phase --force --public -r .
399 $ hg phase --force --public -r .
400 $ echo 4 > foo
400 $ echo 4 > foo
401 $ hg commit -m 'head 1 draft 1'
401 $ hg commit -m 'head 1 draft 1'
402 $ echo 5 > foo
402 $ echo 5 > foo
403 $ hg commit -m 'head 1 draft 2'
403 $ hg commit -m 'head 1 draft 2'
404 $ hg -q up -r 2
404 $ hg -q up -r 2
405 $ echo 6 > foo
405 $ echo 6 > foo
406 $ hg commit -m 'head 2 commit 1'
406 $ hg commit -m 'head 2 commit 1'
407 $ echo 7 > foo
407 $ echo 7 > foo
408 $ hg commit -m 'head 2 commit 2'
408 $ hg commit -m 'head 2 commit 2'
409 $ hg -q up -r 2
409 $ hg -q up -r 2
410 $ echo 8 > foo
410 $ echo 8 > foo
411 $ hg commit -m 'head 3'
411 $ hg commit -m 'head 3'
412 created new head
412 created new head
413 $ hg -q up -r 2
413 $ hg -q up -r 2
414 $ echo 9 > foo
414 $ echo 9 > foo
415 $ hg commit -m 'head 4'
415 $ hg commit -m 'head 4'
416 created new head
416 created new head
417 $ hg merge --tool :local -r 8
417 $ hg merge --tool :local -r 8
418 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
418 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
419 (branch merge, don't forget to commit)
419 (branch merge, don't forget to commit)
420 $ hg commit -m 'merge head 3 into head 4'
420 $ hg commit -m 'merge head 3 into head 4'
421 $ echo 11 > foo
421 $ echo 11 > foo
422 $ hg commit -m 'commit 1 after merge'
422 $ hg commit -m 'commit 1 after merge'
423 $ echo 12 > foo
423 $ echo 12 > foo
424 $ hg commit -m 'commit 2 after merge'
424 $ hg commit -m 'commit 2 after merge'
425
425
426 $ hg log -G -T '{rev}:{node|short} {phase} {desc}\n'
426 $ hg log -G -T '{rev}:{node|short} {phase} {desc}\n'
427 @ 12:8cde254db839 draft commit 2 after merge
427 @ 12:8cde254db839 draft commit 2 after merge
428 |
428 |
429 o 11:6f2f0241f119 draft commit 1 after merge
429 o 11:6f2f0241f119 draft commit 1 after merge
430 |
430 |
431 o 10:90506cc76b00 draft merge head 3 into head 4
431 o 10:90506cc76b00 draft merge head 3 into head 4
432 |\
432 |\
433 | o 9:f8607a373a97 draft head 4
433 | o 9:f8607a373a97 draft head 4
434 | |
434 | |
435 o | 8:0da92be05148 draft head 3
435 o | 8:0da92be05148 draft head 3
436 |/
436 |/
437 | o 7:4c35cdf97d5e draft head 2 commit 2
437 | o 7:4c35cdf97d5e draft head 2 commit 2
438 | |
438 | |
439 | o 6:931820154288 draft head 2 commit 1
439 | o 6:931820154288 draft head 2 commit 1
440 |/
440 |/
441 | o 5:8cdc02b9bc63 draft head 1 draft 2
441 | o 5:8cdc02b9bc63 draft head 1 draft 2
442 | |
442 | |
443 | o 4:463b8c0d2973 draft head 1 draft 1
443 | o 4:463b8c0d2973 draft head 1 draft 1
444 | |
444 | |
445 | o 3:23a0c4eefcbf public head 1 public
445 | o 3:23a0c4eefcbf public head 1 public
446 | |
446 | |
447 o | 2:4117331c3abb draft draft after public
447 o | 2:4117331c3abb draft draft after public
448 |/
448 |/
449 o 1:4426d359ea59 public public 1
449 o 1:4426d359ea59 public public 1
450 |
450 |
451 o 0:54136a8ddf32 public root
451 o 0:54136a8ddf32 public root
452
452
453
453
454 Default base revision should stop at public changesets
454 Default base revision should stop at public changesets
455
455
456 $ hg -q up 8cdc02b9bc63
456 $ hg -q up 8cdc02b9bc63
457 $ hg histedit --commands - <<EOF
457 $ hg histedit --commands - <<EOF
458 > pick 463b8c0d2973
458 > pick 463b8c0d2973
459 > pick 8cdc02b9bc63
459 > pick 8cdc02b9bc63
460 > EOF
460 > EOF
461
461
462 Default base revision should stop at branchpoint
462 Default base revision should stop at branchpoint
463
463
464 $ hg -q up 4c35cdf97d5e
464 $ hg -q up 4c35cdf97d5e
465 $ hg histedit --commands - <<EOF
465 $ hg histedit --commands - <<EOF
466 > pick 931820154288
466 > pick 931820154288
467 > pick 4c35cdf97d5e
467 > pick 4c35cdf97d5e
468 > EOF
468 > EOF
469
469
470 Default base revision should stop at merge commit
470 Default base revision should stop at merge commit
471
471
472 $ hg -q up 8cde254db839
472 $ hg -q up 8cde254db839
473 $ hg histedit --commands - <<EOF
473 $ hg histedit --commands - <<EOF
474 > pick 6f2f0241f119
474 > pick 6f2f0241f119
475 > pick 8cde254db839
475 > pick 8cde254db839
476 > EOF
476 > EOF
477
477
478 commit --amend should abort if histedit is in progress
478 commit --amend should abort if histedit is in progress
479 (issue4800) and markers are not being created.
479 (issue4800) and markers are not being created.
480 Eventually, histedit could perhaps look at `source` extra,
480 Eventually, histedit could perhaps look at `source` extra,
481 in which case this test should be revisited.
481 in which case this test should be revisited.
482
482
483 $ hg -q up 8cde254db839
483 $ hg -q up 8cde254db839
484 $ hg histedit 6f2f0241f119 --commands - <<EOF
484 $ hg histedit 6f2f0241f119 --commands - <<EOF
485 > pick 8cde254db839
485 > pick 8cde254db839
486 > edit 6f2f0241f119
486 > edit 6f2f0241f119
487 > EOF
487 > EOF
488 merging foo
488 merging foo
489 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
489 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
490 Fix up the change (pick 8cde254db839)
490 Fix up the change (pick 8cde254db839)
491 (hg histedit --continue to resume)
491 (hg histedit --continue to resume)
492 [240]
492 [240]
493 $ hg resolve -m --all
493 $ hg resolve -m --all
494 (no more unresolved files)
494 (no more unresolved files)
495 continue: hg histedit --continue
495 continue: hg histedit --continue
496 $ hg histedit --cont
496 $ hg histedit --cont
497 merging foo
497 merging foo
498 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
498 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
499 Editing (6f2f0241f119), you may commit or record as needed now.
499 Editing (6f2f0241f119), commit as needed now to split the change
500 (hg histedit --continue to resume)
500 (to edit 6f2f0241f119, `hg histedit --continue` after making changes)
501 [240]
501 [240]
502 $ hg resolve -m --all
502 $ hg resolve -m --all
503 (no more unresolved files)
503 (no more unresolved files)
504 continue: hg histedit --continue
504 continue: hg histedit --continue
505 $ hg commit --amend -m 'reject this fold'
505 $ hg commit --amend -m 'reject this fold'
506 abort: histedit in progress
506 abort: histedit in progress
507 (use 'hg histedit --continue' or 'hg histedit --abort')
507 (use 'hg histedit --continue' or 'hg histedit --abort')
508 [20]
508 [20]
509
509
510 With markers enabled, histedit does not get confused, and
510 With markers enabled, histedit does not get confused, and
511 amend should not be blocked by the ongoing histedit.
511 amend should not be blocked by the ongoing histedit.
512
512
513 $ cat >>$HGRCPATH <<EOF
513 $ cat >>$HGRCPATH <<EOF
514 > [experimental]
514 > [experimental]
515 > evolution.createmarkers=True
515 > evolution.createmarkers=True
516 > evolution.allowunstable=True
516 > evolution.allowunstable=True
517 > EOF
517 > EOF
518 $ hg commit --amend -m 'allow this fold'
518 $ hg commit --amend -m 'allow this fold'
519 $ hg histedit --continue
519 $ hg histedit --continue
520
520
521 $ cd ..
521 $ cd ..
522
522
523 Test autoverb feature
523 Test autoverb feature
524
524
525 $ hg init autoverb
525 $ hg init autoverb
526 $ cd autoverb
526 $ cd autoverb
527 $ echo alpha >> alpha
527 $ echo alpha >> alpha
528 $ hg ci -qAm one
528 $ hg ci -qAm one
529 $ echo alpha >> alpha
529 $ echo alpha >> alpha
530 $ hg ci -qm two
530 $ hg ci -qm two
531 $ echo beta >> beta
531 $ echo beta >> beta
532 $ hg ci -qAm "roll! one"
532 $ hg ci -qAm "roll! one"
533
533
534 $ hg log --style compact --graph
534 $ hg log --style compact --graph
535 @ 2[tip] 4f34d0f8b5fa 1970-01-01 00:00 +0000 test
535 @ 2[tip] 4f34d0f8b5fa 1970-01-01 00:00 +0000 test
536 | roll! one
536 | roll! one
537 |
537 |
538 o 1 579e40513370 1970-01-01 00:00 +0000 test
538 o 1 579e40513370 1970-01-01 00:00 +0000 test
539 | two
539 | two
540 |
540 |
541 o 0 6058cbb6cfd7 1970-01-01 00:00 +0000 test
541 o 0 6058cbb6cfd7 1970-01-01 00:00 +0000 test
542 one
542 one
543
543
544
544
545 Check that 'roll' is selected by default
545 Check that 'roll' is selected by default
546
546
547 $ HGEDITOR=cat hg histedit 0 --config experimental.histedit.autoverb=True
547 $ HGEDITOR=cat hg histedit 0 --config experimental.histedit.autoverb=True
548 pick 6058cbb6cfd7 0 one
548 pick 6058cbb6cfd7 0 one
549 roll 4f34d0f8b5fa 2 roll! one
549 roll 4f34d0f8b5fa 2 roll! one
550 pick 579e40513370 1 two
550 pick 579e40513370 1 two
551
551
552 # Edit history between 6058cbb6cfd7 and 4f34d0f8b5fa
552 # Edit history between 6058cbb6cfd7 and 4f34d0f8b5fa
553 #
553 #
554 # Commits are listed from least to most recent
554 # Commits are listed from least to most recent
555 #
555 #
556 # You can reorder changesets by reordering the lines
556 # You can reorder changesets by reordering the lines
557 #
557 #
558 # Commands:
558 # Commands:
559 #
559 #
560 # e, edit = use commit, but stop for amending
560 # e, edit = use commit, but stop for amending
561 # m, mess = edit commit message without changing commit content
561 # m, mess = edit commit message without changing commit content
562 # p, pick = use commit
562 # p, pick = use commit
563 # b, base = checkout changeset and apply further changesets from there
563 # b, base = checkout changeset and apply further changesets from there
564 # d, drop = remove commit from history
564 # d, drop = remove commit from history
565 # f, fold = use commit, but combine it with the one above
565 # f, fold = use commit, but combine it with the one above
566 # r, roll = like fold, but discard this commit's description and date
566 # r, roll = like fold, but discard this commit's description and date
567 #
567 #
568
568
569 $ cd ..
569 $ cd ..
570
570
571 Check that histedit's commands accept revsets
571 Check that histedit's commands accept revsets
572 $ hg init bar
572 $ hg init bar
573 $ cd bar
573 $ cd bar
574 $ echo w >> a
574 $ echo w >> a
575 $ hg ci -qAm "adds a"
575 $ hg ci -qAm "adds a"
576 $ echo x >> b
576 $ echo x >> b
577 $ hg ci -qAm "adds b"
577 $ hg ci -qAm "adds b"
578 $ echo y >> c
578 $ echo y >> c
579 $ hg ci -qAm "adds c"
579 $ hg ci -qAm "adds c"
580 $ echo z >> d
580 $ echo z >> d
581 $ hg ci -qAm "adds d"
581 $ hg ci -qAm "adds d"
582 $ hg log -G -T '{rev} {desc}\n'
582 $ hg log -G -T '{rev} {desc}\n'
583 @ 3 adds d
583 @ 3 adds d
584 |
584 |
585 o 2 adds c
585 o 2 adds c
586 |
586 |
587 o 1 adds b
587 o 1 adds b
588 |
588 |
589 o 0 adds a
589 o 0 adds a
590
590
591 $ HGEDITOR=cat hg histedit "2" --commands - << EOF
591 $ HGEDITOR=cat hg histedit "2" --commands - << EOF
592 > base -4 adds c
592 > base -4 adds c
593 > pick 2 adds c
593 > pick 2 adds c
594 > pick tip adds d
594 > pick tip adds d
595 > EOF
595 > EOF
596 $ hg log -G -T '{rev} {desc}\n'
596 $ hg log -G -T '{rev} {desc}\n'
597 @ 5 adds d
597 @ 5 adds d
598 |
598 |
599 o 4 adds c
599 o 4 adds c
600 |
600 |
601 | o 1 adds b
601 | o 1 adds b
602 |/
602 |/
603 o 0 adds a
603 o 0 adds a
604
604
605
605
@@ -1,552 +1,552 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
55
56 show the edit commands offered
56 show the edit commands offered
57 $ HGEDITOR=cat hg histedit 177f92b77385
57 $ HGEDITOR=cat hg histedit 177f92b77385
58 pick 177f92b77385 2 c
58 pick 177f92b77385 2 c
59 pick 055a42cdd887 3 d
59 pick 055a42cdd887 3 d
60 pick e860deea161a 4 e
60 pick e860deea161a 4 e
61 pick 652413bf663e 5 f
61 pick 652413bf663e 5 f
62
62
63 # Edit history between 177f92b77385 and 652413bf663e
63 # Edit history between 177f92b77385 and 652413bf663e
64 #
64 #
65 # Commits are listed from least to most recent
65 # Commits are listed from least to most recent
66 #
66 #
67 # You can reorder changesets by reordering the lines
67 # You can reorder changesets by reordering the lines
68 #
68 #
69 # Commands:
69 # Commands:
70 #
70 #
71 # e, edit = use commit, but stop for amending
71 # e, edit = use commit, but stop for amending
72 # m, mess = edit commit message without changing commit content
72 # m, mess = edit commit message without changing commit content
73 # p, pick = use commit
73 # p, pick = use commit
74 # b, base = checkout changeset and apply further changesets from there
74 # b, base = checkout changeset and apply further changesets from there
75 # d, drop = remove commit from history
75 # d, drop = remove commit from history
76 # f, fold = use commit, but combine it with the one above
76 # f, fold = use commit, but combine it with the one above
77 # r, roll = like fold, but discard this commit's description and date
77 # r, roll = like fold, but discard this commit's description and date
78 #
78 #
79
79
80
80
81 test customization of revision summary
81 test customization of revision summary
82 $ HGEDITOR=cat hg histedit 177f92b77385 \
82 $ HGEDITOR=cat hg histedit 177f92b77385 \
83 > --config histedit.summary-template='I am rev {rev} desc {desc} tags {tags}'
83 > --config histedit.summary-template='I am rev {rev} desc {desc} tags {tags}'
84 pick 177f92b77385 I am rev 2 desc c tags
84 pick 177f92b77385 I am rev 2 desc c tags
85 pick 055a42cdd887 I am rev 3 desc d tags
85 pick 055a42cdd887 I am rev 3 desc d tags
86 pick e860deea161a I am rev 4 desc e tags
86 pick e860deea161a I am rev 4 desc e tags
87 pick 652413bf663e I am rev 5 desc f tags tip
87 pick 652413bf663e I am rev 5 desc f tags tip
88
88
89 # Edit history between 177f92b77385 and 652413bf663e
89 # Edit history between 177f92b77385 and 652413bf663e
90 #
90 #
91 # Commits are listed from least to most recent
91 # Commits are listed from least to most recent
92 #
92 #
93 # You can reorder changesets by reordering the lines
93 # You can reorder changesets by reordering the lines
94 #
94 #
95 # Commands:
95 # Commands:
96 #
96 #
97 # e, edit = use commit, but stop for amending
97 # e, edit = use commit, but stop for amending
98 # m, mess = edit commit message without changing commit content
98 # m, mess = edit commit message without changing commit content
99 # p, pick = use commit
99 # p, pick = use commit
100 # b, base = checkout changeset and apply further changesets from there
100 # b, base = checkout changeset and apply further changesets from there
101 # d, drop = remove commit from history
101 # d, drop = remove commit from history
102 # f, fold = use commit, but combine it with the one above
102 # f, fold = use commit, but combine it with the one above
103 # r, roll = like fold, but discard this commit's description and date
103 # r, roll = like fold, but discard this commit's description and date
104 #
104 #
105
105
106
106
107 colors in the custom template don't show up in the editor
107 colors in the custom template don't show up in the editor
108 $ HGEDITOR=cat hg histedit 177f92b77385 --color=debug \
108 $ HGEDITOR=cat hg histedit 177f92b77385 --color=debug \
109 > --config histedit.summary-template='I am rev {label("rev", rev)}'
109 > --config histedit.summary-template='I am rev {label("rev", rev)}'
110 pick 177f92b77385 I am rev 2
110 pick 177f92b77385 I am rev 2
111 pick 055a42cdd887 I am rev 3
111 pick 055a42cdd887 I am rev 3
112 pick e860deea161a I am rev 4
112 pick e860deea161a I am rev 4
113 pick 652413bf663e I am rev 5
113 pick 652413bf663e I am rev 5
114
114
115 # Edit history between 177f92b77385 and 652413bf663e
115 # Edit history between 177f92b77385 and 652413bf663e
116 #
116 #
117 # Commits are listed from least to most recent
117 # Commits are listed from least to most recent
118 #
118 #
119 # You can reorder changesets by reordering the lines
119 # You can reorder changesets by reordering the lines
120 #
120 #
121 # Commands:
121 # Commands:
122 #
122 #
123 # e, edit = use commit, but stop for amending
123 # e, edit = use commit, but stop for amending
124 # m, mess = edit commit message without changing commit content
124 # m, mess = edit commit message without changing commit content
125 # p, pick = use commit
125 # p, pick = use commit
126 # b, base = checkout changeset and apply further changesets from there
126 # b, base = checkout changeset and apply further changesets from there
127 # d, drop = remove commit from history
127 # d, drop = remove commit from history
128 # f, fold = use commit, but combine it with the one above
128 # f, fold = use commit, but combine it with the one above
129 # r, roll = like fold, but discard this commit's description and date
129 # r, roll = like fold, but discard this commit's description and date
130 #
130 #
131
131
132
132
133 edit the history
133 edit the history
134 (use a hacky editor to check histedit-last-edit.txt backup)
134 (use a hacky editor to check histedit-last-edit.txt backup)
135
135
136 $ EDITED="$TESTTMP/editedhistory"
136 $ EDITED="$TESTTMP/editedhistory"
137 $ cat > $EDITED <<EOF
137 $ cat > $EDITED <<EOF
138 > edit 177f92b77385 c
138 > edit 177f92b77385 c
139 > pick e860deea161a e
139 > pick e860deea161a e
140 > pick 652413bf663e f
140 > pick 652413bf663e f
141 > pick 055a42cdd887 d
141 > pick 055a42cdd887 d
142 > EOF
142 > EOF
143 $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
143 $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
144 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
144 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
145 Editing (177f92b77385), you may commit or record as needed now.
145 Editing (177f92b77385), commit as needed now to split the change
146 (hg histedit --continue to resume)
146 (to edit 177f92b77385, `hg histedit --continue` after making changes)
147
147
148 rules should end up in .hg/histedit-last-edit.txt:
148 rules should end up in .hg/histedit-last-edit.txt:
149 $ cat .hg/histedit-last-edit.txt
149 $ cat .hg/histedit-last-edit.txt
150 edit 177f92b77385 c
150 edit 177f92b77385 c
151 pick e860deea161a e
151 pick e860deea161a e
152 pick 652413bf663e f
152 pick 652413bf663e f
153 pick 055a42cdd887 d
153 pick 055a42cdd887 d
154
154
155 $ hg histedit --abort
155 $ hg histedit --abort
156 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
156 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
157 $ cat > $EDITED <<EOF
157 $ cat > $EDITED <<EOF
158 > pick 177f92b77385 c
158 > pick 177f92b77385 c
159 > pick e860deea161a e
159 > pick e860deea161a e
160 > pick 652413bf663e f
160 > pick 652413bf663e f
161 > pick 055a42cdd887 d
161 > pick 055a42cdd887 d
162 > EOF
162 > EOF
163 $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
163 $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
164
164
165 log after edit
165 log after edit
166 $ hg log --graph
166 $ hg log --graph
167 @ changeset: 5:07114f51870f
167 @ changeset: 5:07114f51870f
168 | tag: tip
168 | tag: tip
169 | user: test
169 | user: test
170 | date: Thu Jan 01 00:00:00 1970 +0000
170 | date: Thu Jan 01 00:00:00 1970 +0000
171 | summary: d
171 | summary: d
172 |
172 |
173 o changeset: 4:8ade9693061e
173 o changeset: 4:8ade9693061e
174 | user: test
174 | user: test
175 | date: Thu Jan 01 00:00:00 1970 +0000
175 | date: Thu Jan 01 00:00:00 1970 +0000
176 | summary: f
176 | summary: f
177 |
177 |
178 o changeset: 3:d8249471110a
178 o changeset: 3:d8249471110a
179 | user: test
179 | user: test
180 | date: Thu Jan 01 00:00:00 1970 +0000
180 | date: Thu Jan 01 00:00:00 1970 +0000
181 | summary: e
181 | summary: e
182 |
182 |
183 o changeset: 2:177f92b77385
183 o changeset: 2:177f92b77385
184 | user: test
184 | user: test
185 | date: Thu Jan 01 00:00:00 1970 +0000
185 | date: Thu Jan 01 00:00:00 1970 +0000
186 | summary: c
186 | summary: c
187 |
187 |
188 o changeset: 1:d2ae7f538514
188 o changeset: 1:d2ae7f538514
189 | user: test
189 | user: test
190 | date: Thu Jan 01 00:00:00 1970 +0000
190 | date: Thu Jan 01 00:00:00 1970 +0000
191 | summary: b
191 | summary: b
192 |
192 |
193 o changeset: 0:cb9a9f314b8b
193 o changeset: 0:cb9a9f314b8b
194 user: test
194 user: test
195 date: Thu Jan 01 00:00:00 1970 +0000
195 date: Thu Jan 01 00:00:00 1970 +0000
196 summary: a
196 summary: a
197
197
198
198
199
199
200 put things back
200 put things back
201
201
202 $ hg histedit 177f92b77385 --commands - 2>&1 << EOF | fixbundle
202 $ hg histedit 177f92b77385 --commands - 2>&1 << EOF | fixbundle
203 > pick 177f92b77385 c
203 > pick 177f92b77385 c
204 > pick 07114f51870f d
204 > pick 07114f51870f d
205 > pick d8249471110a e
205 > pick d8249471110a e
206 > pick 8ade9693061e f
206 > pick 8ade9693061e f
207 > EOF
207 > EOF
208
208
209 $ hg log --graph
209 $ hg log --graph
210 @ changeset: 5:7eca9b5b1148
210 @ changeset: 5:7eca9b5b1148
211 | tag: tip
211 | tag: tip
212 | user: test
212 | user: test
213 | date: Thu Jan 01 00:00:00 1970 +0000
213 | date: Thu Jan 01 00:00:00 1970 +0000
214 | summary: f
214 | summary: f
215 |
215 |
216 o changeset: 4:915da888f2de
216 o changeset: 4:915da888f2de
217 | user: test
217 | user: test
218 | date: Thu Jan 01 00:00:00 1970 +0000
218 | date: Thu Jan 01 00:00:00 1970 +0000
219 | summary: e
219 | summary: e
220 |
220 |
221 o changeset: 3:10517e47bbbb
221 o changeset: 3:10517e47bbbb
222 | user: test
222 | user: test
223 | date: Thu Jan 01 00:00:00 1970 +0000
223 | date: Thu Jan 01 00:00:00 1970 +0000
224 | summary: d
224 | summary: d
225 |
225 |
226 o changeset: 2:177f92b77385
226 o changeset: 2:177f92b77385
227 | user: test
227 | user: test
228 | date: Thu Jan 01 00:00:00 1970 +0000
228 | date: Thu Jan 01 00:00:00 1970 +0000
229 | summary: c
229 | summary: c
230 |
230 |
231 o changeset: 1:d2ae7f538514
231 o changeset: 1:d2ae7f538514
232 | user: test
232 | user: test
233 | date: Thu Jan 01 00:00:00 1970 +0000
233 | date: Thu Jan 01 00:00:00 1970 +0000
234 | summary: b
234 | summary: b
235 |
235 |
236 o changeset: 0:cb9a9f314b8b
236 o changeset: 0:cb9a9f314b8b
237 user: test
237 user: test
238 date: Thu Jan 01 00:00:00 1970 +0000
238 date: Thu Jan 01 00:00:00 1970 +0000
239 summary: a
239 summary: a
240
240
241
241
242
242
243 slightly different this time
243 slightly different this time
244
244
245 $ hg histedit 177f92b77385 --commands - << EOF 2>&1 | fixbundle
245 $ hg histedit 177f92b77385 --commands - << EOF 2>&1 | fixbundle
246 > pick 10517e47bbbb d
246 > pick 10517e47bbbb d
247 > pick 7eca9b5b1148 f
247 > pick 7eca9b5b1148 f
248 > pick 915da888f2de e
248 > pick 915da888f2de e
249 > pick 177f92b77385 c
249 > pick 177f92b77385 c
250 > EOF
250 > EOF
251 $ hg log --graph
251 $ hg log --graph
252 @ changeset: 5:38b92f448761
252 @ changeset: 5:38b92f448761
253 | tag: tip
253 | tag: tip
254 | user: test
254 | user: test
255 | date: Thu Jan 01 00:00:00 1970 +0000
255 | date: Thu Jan 01 00:00:00 1970 +0000
256 | summary: c
256 | summary: c
257 |
257 |
258 o changeset: 4:de71b079d9ce
258 o changeset: 4:de71b079d9ce
259 | user: test
259 | user: test
260 | date: Thu Jan 01 00:00:00 1970 +0000
260 | date: Thu Jan 01 00:00:00 1970 +0000
261 | summary: e
261 | summary: e
262 |
262 |
263 o changeset: 3:be9ae3a309c6
263 o changeset: 3:be9ae3a309c6
264 | user: test
264 | user: test
265 | date: Thu Jan 01 00:00:00 1970 +0000
265 | date: Thu Jan 01 00:00:00 1970 +0000
266 | summary: f
266 | summary: f
267 |
267 |
268 o changeset: 2:799205341b6b
268 o changeset: 2:799205341b6b
269 | user: test
269 | user: test
270 | date: Thu Jan 01 00:00:00 1970 +0000
270 | date: Thu Jan 01 00:00:00 1970 +0000
271 | summary: d
271 | summary: d
272 |
272 |
273 o changeset: 1:d2ae7f538514
273 o changeset: 1:d2ae7f538514
274 | user: test
274 | user: test
275 | date: Thu Jan 01 00:00:00 1970 +0000
275 | date: Thu Jan 01 00:00:00 1970 +0000
276 | summary: b
276 | summary: b
277 |
277 |
278 o changeset: 0:cb9a9f314b8b
278 o changeset: 0:cb9a9f314b8b
279 user: test
279 user: test
280 date: Thu Jan 01 00:00:00 1970 +0000
280 date: Thu Jan 01 00:00:00 1970 +0000
281 summary: a
281 summary: a
282
282
283
283
284
284
285 keep prevents stripping dead revs
285 keep prevents stripping dead revs
286 $ hg histedit 799205341b6b --keep --commands - 2>&1 << EOF | fixbundle
286 $ hg histedit 799205341b6b --keep --commands - 2>&1 << EOF | fixbundle
287 > pick 799205341b6b d
287 > pick 799205341b6b d
288 > pick be9ae3a309c6 f
288 > pick be9ae3a309c6 f
289 > pick 38b92f448761 c
289 > pick 38b92f448761 c
290 > pick de71b079d9ce e
290 > pick de71b079d9ce e
291 > EOF
291 > EOF
292 $ hg log --graph
292 $ hg log --graph
293 @ changeset: 7:803ef1c6fcfd
293 @ changeset: 7:803ef1c6fcfd
294 | tag: tip
294 | tag: tip
295 | user: test
295 | user: test
296 | date: Thu Jan 01 00:00:00 1970 +0000
296 | date: Thu Jan 01 00:00:00 1970 +0000
297 | summary: e
297 | summary: e
298 |
298 |
299 o changeset: 6:ece0b8d93dda
299 o changeset: 6:ece0b8d93dda
300 | parent: 3:be9ae3a309c6
300 | parent: 3:be9ae3a309c6
301 | user: test
301 | user: test
302 | date: Thu Jan 01 00:00:00 1970 +0000
302 | date: Thu Jan 01 00:00:00 1970 +0000
303 | summary: c
303 | summary: c
304 |
304 |
305 | o changeset: 5:38b92f448761
305 | o changeset: 5:38b92f448761
306 | | user: test
306 | | user: test
307 | | date: Thu Jan 01 00:00:00 1970 +0000
307 | | date: Thu Jan 01 00:00:00 1970 +0000
308 | | summary: c
308 | | summary: c
309 | |
309 | |
310 | o changeset: 4:de71b079d9ce
310 | o changeset: 4:de71b079d9ce
311 |/ user: test
311 |/ user: test
312 | date: Thu Jan 01 00:00:00 1970 +0000
312 | date: Thu Jan 01 00:00:00 1970 +0000
313 | summary: e
313 | summary: e
314 |
314 |
315 o changeset: 3:be9ae3a309c6
315 o changeset: 3:be9ae3a309c6
316 | user: test
316 | user: test
317 | date: Thu Jan 01 00:00:00 1970 +0000
317 | date: Thu Jan 01 00:00:00 1970 +0000
318 | summary: f
318 | summary: f
319 |
319 |
320 o changeset: 2:799205341b6b
320 o changeset: 2:799205341b6b
321 | user: test
321 | user: test
322 | date: Thu Jan 01 00:00:00 1970 +0000
322 | date: Thu Jan 01 00:00:00 1970 +0000
323 | summary: d
323 | summary: d
324 |
324 |
325 o changeset: 1:d2ae7f538514
325 o changeset: 1:d2ae7f538514
326 | user: test
326 | user: test
327 | date: Thu Jan 01 00:00:00 1970 +0000
327 | date: Thu Jan 01 00:00:00 1970 +0000
328 | summary: b
328 | summary: b
329 |
329 |
330 o changeset: 0:cb9a9f314b8b
330 o changeset: 0:cb9a9f314b8b
331 user: test
331 user: test
332 date: Thu Jan 01 00:00:00 1970 +0000
332 date: Thu Jan 01 00:00:00 1970 +0000
333 summary: a
333 summary: a
334
334
335
335
336
336
337 try with --rev
337 try with --rev
338 $ hg histedit --commands - --rev -2 2>&1 <<EOF | fixbundle
338 $ hg histedit --commands - --rev -2 2>&1 <<EOF | fixbundle
339 > pick de71b079d9ce e
339 > pick de71b079d9ce e
340 > pick 38b92f448761 c
340 > pick 38b92f448761 c
341 > EOF
341 > EOF
342 hg: parse error: pick "de71b079d9ce" changeset was not a candidate
342 hg: parse error: pick "de71b079d9ce" changeset was not a candidate
343 (only use listed changesets)
343 (only use listed changesets)
344 $ hg log --graph
344 $ hg log --graph
345 @ changeset: 7:803ef1c6fcfd
345 @ changeset: 7:803ef1c6fcfd
346 | tag: tip
346 | tag: tip
347 | user: test
347 | user: test
348 | date: Thu Jan 01 00:00:00 1970 +0000
348 | date: Thu Jan 01 00:00:00 1970 +0000
349 | summary: e
349 | summary: e
350 |
350 |
351 o changeset: 6:ece0b8d93dda
351 o changeset: 6:ece0b8d93dda
352 | parent: 3:be9ae3a309c6
352 | parent: 3:be9ae3a309c6
353 | user: test
353 | user: test
354 | date: Thu Jan 01 00:00:00 1970 +0000
354 | date: Thu Jan 01 00:00:00 1970 +0000
355 | summary: c
355 | summary: c
356 |
356 |
357 | o changeset: 5:38b92f448761
357 | o changeset: 5:38b92f448761
358 | | user: test
358 | | user: test
359 | | date: Thu Jan 01 00:00:00 1970 +0000
359 | | date: Thu Jan 01 00:00:00 1970 +0000
360 | | summary: c
360 | | summary: c
361 | |
361 | |
362 | o changeset: 4:de71b079d9ce
362 | o changeset: 4:de71b079d9ce
363 |/ user: test
363 |/ user: test
364 | date: Thu Jan 01 00:00:00 1970 +0000
364 | date: Thu Jan 01 00:00:00 1970 +0000
365 | summary: e
365 | summary: e
366 |
366 |
367 o changeset: 3:be9ae3a309c6
367 o changeset: 3:be9ae3a309c6
368 | user: test
368 | user: test
369 | date: Thu Jan 01 00:00:00 1970 +0000
369 | date: Thu Jan 01 00:00:00 1970 +0000
370 | summary: f
370 | summary: f
371 |
371 |
372 o changeset: 2:799205341b6b
372 o changeset: 2:799205341b6b
373 | user: test
373 | user: test
374 | date: Thu Jan 01 00:00:00 1970 +0000
374 | date: Thu Jan 01 00:00:00 1970 +0000
375 | summary: d
375 | summary: d
376 |
376 |
377 o changeset: 1:d2ae7f538514
377 o changeset: 1:d2ae7f538514
378 | user: test
378 | user: test
379 | date: Thu Jan 01 00:00:00 1970 +0000
379 | date: Thu Jan 01 00:00:00 1970 +0000
380 | summary: b
380 | summary: b
381 |
381 |
382 o changeset: 0:cb9a9f314b8b
382 o changeset: 0:cb9a9f314b8b
383 user: test
383 user: test
384 date: Thu Jan 01 00:00:00 1970 +0000
384 date: Thu Jan 01 00:00:00 1970 +0000
385 summary: a
385 summary: a
386
386
387
387
388 Verify that revsetalias entries work with histedit:
388 Verify that revsetalias entries work with histedit:
389 $ cat >> $HGRCPATH <<EOF
389 $ cat >> $HGRCPATH <<EOF
390 > [revsetalias]
390 > [revsetalias]
391 > grandparent(ARG) = p1(p1(ARG))
391 > grandparent(ARG) = p1(p1(ARG))
392 > EOF
392 > EOF
393 $ echo extra commit >> c
393 $ echo extra commit >> c
394 $ hg ci -m 'extra commit to c'
394 $ hg ci -m 'extra commit to c'
395 $ HGEDITOR=cat hg histedit 'grandparent(.)'
395 $ HGEDITOR=cat hg histedit 'grandparent(.)'
396 pick ece0b8d93dda 6 c
396 pick ece0b8d93dda 6 c
397 pick 803ef1c6fcfd 7 e
397 pick 803ef1c6fcfd 7 e
398 pick 9c863c565126 8 extra commit to c
398 pick 9c863c565126 8 extra commit to c
399
399
400 # Edit history between ece0b8d93dda and 9c863c565126
400 # Edit history between ece0b8d93dda and 9c863c565126
401 #
401 #
402 # Commits are listed from least to most recent
402 # Commits are listed from least to most recent
403 #
403 #
404 # You can reorder changesets by reordering the lines
404 # You can reorder changesets by reordering the lines
405 #
405 #
406 # Commands:
406 # Commands:
407 #
407 #
408 # e, edit = use commit, but stop for amending
408 # e, edit = use commit, but stop for amending
409 # m, mess = edit commit message without changing commit content
409 # m, mess = edit commit message without changing commit content
410 # p, pick = use commit
410 # p, pick = use commit
411 # b, base = checkout changeset and apply further changesets from there
411 # b, base = checkout changeset and apply further changesets from there
412 # d, drop = remove commit from history
412 # d, drop = remove commit from history
413 # f, fold = use commit, but combine it with the one above
413 # f, fold = use commit, but combine it with the one above
414 # r, roll = like fold, but discard this commit's description and date
414 # r, roll = like fold, but discard this commit's description and date
415 #
415 #
416
416
417
417
418 should also work if a commit message is missing
418 should also work if a commit message is missing
419 $ BUNDLE="$TESTDIR/missing-comment.hg"
419 $ BUNDLE="$TESTDIR/missing-comment.hg"
420 $ hg init missing
420 $ hg init missing
421 $ cd missing
421 $ cd missing
422 $ hg unbundle $BUNDLE
422 $ hg unbundle $BUNDLE
423 adding changesets
423 adding changesets
424 adding manifests
424 adding manifests
425 adding file changes
425 adding file changes
426 added 3 changesets with 3 changes to 1 files
426 added 3 changesets with 3 changes to 1 files
427 new changesets 141947992243:bd22688093b3 (3 drafts)
427 new changesets 141947992243:bd22688093b3 (3 drafts)
428 (run 'hg update' to get a working copy)
428 (run 'hg update' to get a working copy)
429 $ hg co tip
429 $ hg co tip
430 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
430 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
431 $ hg log --graph
431 $ hg log --graph
432 @ changeset: 2:bd22688093b3
432 @ changeset: 2:bd22688093b3
433 | tag: tip
433 | tag: tip
434 | user: Robert Altman <robert.altman@telventDTN.com>
434 | user: Robert Altman <robert.altman@telventDTN.com>
435 | date: Mon Nov 28 16:40:04 2011 +0000
435 | date: Mon Nov 28 16:40:04 2011 +0000
436 | summary: Update file.
436 | summary: Update file.
437 |
437 |
438 o changeset: 1:3b3e956f9171
438 o changeset: 1:3b3e956f9171
439 | user: Robert Altman <robert.altman@telventDTN.com>
439 | user: Robert Altman <robert.altman@telventDTN.com>
440 | date: Mon Nov 28 16:37:57 2011 +0000
440 | date: Mon Nov 28 16:37:57 2011 +0000
441 |
441 |
442 o changeset: 0:141947992243
442 o changeset: 0:141947992243
443 user: Robert Altman <robert.altman@telventDTN.com>
443 user: Robert Altman <robert.altman@telventDTN.com>
444 date: Mon Nov 28 16:35:28 2011 +0000
444 date: Mon Nov 28 16:35:28 2011 +0000
445 summary: Checked in text file
445 summary: Checked in text file
446
446
447
447
448 $ hg histedit 0
448 $ hg histedit 0
449 $ cd ..
449 $ cd ..
450
450
451 $ cd ..
451 $ cd ..
452
452
453
453
454 Test to make sure folding renames doesn't cause bogus conflicts (issue4251):
454 Test to make sure folding renames doesn't cause bogus conflicts (issue4251):
455 $ hg init issue4251
455 $ hg init issue4251
456 $ cd issue4251
456 $ cd issue4251
457
457
458 $ mkdir initial-dir
458 $ mkdir initial-dir
459 $ echo foo > initial-dir/initial-file
459 $ echo foo > initial-dir/initial-file
460 $ hg add initial-dir/initial-file
460 $ hg add initial-dir/initial-file
461 $ hg commit -m "initial commit"
461 $ hg commit -m "initial commit"
462
462
463 Move the file to a new directory, and in the same commit, change its content:
463 Move the file to a new directory, and in the same commit, change its content:
464 $ mkdir another-dir
464 $ mkdir another-dir
465 $ hg mv initial-dir/initial-file another-dir/
465 $ hg mv initial-dir/initial-file another-dir/
466 $ echo changed > another-dir/initial-file
466 $ echo changed > another-dir/initial-file
467 $ hg commit -m "moved and changed"
467 $ hg commit -m "moved and changed"
468
468
469 Rename the file:
469 Rename the file:
470 $ hg mv another-dir/initial-file another-dir/renamed-file
470 $ hg mv another-dir/initial-file another-dir/renamed-file
471 $ hg commit -m "renamed"
471 $ hg commit -m "renamed"
472
472
473 Now, let's try to fold the second commit into the first:
473 Now, let's try to fold the second commit into the first:
474 $ cat > editor.sh <<EOF
474 $ cat > editor.sh <<EOF
475 > #!/bin/sh
475 > #!/bin/sh
476 > cat > \$1 <<ENDOF
476 > cat > \$1 <<ENDOF
477 > pick b0f4233702ca 0 initial commit
477 > pick b0f4233702ca 0 initial commit
478 > fold 5e8704a8f2d2 1 moved and changed
478 > fold 5e8704a8f2d2 1 moved and changed
479 > pick 40e7299e8fa7 2 renamed
479 > pick 40e7299e8fa7 2 renamed
480 > ENDOF
480 > ENDOF
481 > EOF
481 > EOF
482
482
483 $ HGEDITOR="sh ./editor.sh" hg histedit 0
483 $ HGEDITOR="sh ./editor.sh" hg histedit 0
484 saved backup bundle to $TESTTMP/issue4251/.hg/strip-backup/b0f4233702ca-4cf5af69-histedit.hg
484 saved backup bundle to $TESTTMP/issue4251/.hg/strip-backup/b0f4233702ca-4cf5af69-histedit.hg
485
485
486 $ hg --config diff.git=yes export 0
486 $ hg --config diff.git=yes export 0
487 # HG changeset patch
487 # HG changeset patch
488 # User test
488 # User test
489 # Date 0 0
489 # Date 0 0
490 # Thu Jan 01 00:00:00 1970 +0000
490 # Thu Jan 01 00:00:00 1970 +0000
491 # Node ID fffadc26f8f85623ce60b028a3f1ccc3730f8530
491 # Node ID fffadc26f8f85623ce60b028a3f1ccc3730f8530
492 # Parent 0000000000000000000000000000000000000000
492 # Parent 0000000000000000000000000000000000000000
493 pick b0f4233702ca 0 initial commit
493 pick b0f4233702ca 0 initial commit
494 fold 5e8704a8f2d2 1 moved and changed
494 fold 5e8704a8f2d2 1 moved and changed
495 pick 40e7299e8fa7 2 renamed
495 pick 40e7299e8fa7 2 renamed
496
496
497 diff --git a/another-dir/initial-file b/another-dir/initial-file
497 diff --git a/another-dir/initial-file b/another-dir/initial-file
498 new file mode 100644
498 new file mode 100644
499 --- /dev/null
499 --- /dev/null
500 +++ b/another-dir/initial-file
500 +++ b/another-dir/initial-file
501 @@ -0,0 +1,1 @@
501 @@ -0,0 +1,1 @@
502 +changed
502 +changed
503
503
504
504
505 $ hg --config diff.git=yes export 1
505 $ hg --config diff.git=yes export 1
506 # HG changeset patch
506 # HG changeset patch
507 # User test
507 # User test
508 # Date 0 0
508 # Date 0 0
509 # Thu Jan 01 00:00:00 1970 +0000
509 # Thu Jan 01 00:00:00 1970 +0000
510 # Node ID 9b730d82b00af8a2766facebfa47cc124405a118
510 # Node ID 9b730d82b00af8a2766facebfa47cc124405a118
511 # Parent fffadc26f8f85623ce60b028a3f1ccc3730f8530
511 # Parent fffadc26f8f85623ce60b028a3f1ccc3730f8530
512 renamed
512 renamed
513
513
514 diff --git a/another-dir/initial-file b/another-dir/renamed-file
514 diff --git a/another-dir/initial-file b/another-dir/renamed-file
515 rename from another-dir/initial-file
515 rename from another-dir/initial-file
516 rename to another-dir/renamed-file
516 rename to another-dir/renamed-file
517
517
518
518
519 $ cd ..
519 $ cd ..
520
520
521 Test that branches are preserved and stays active
521 Test that branches are preserved and stays active
522 -------------------------------------------------
522 -------------------------------------------------
523
523
524 $ hg init repo-with-branch
524 $ hg init repo-with-branch
525 $ cd repo-with-branch
525 $ cd repo-with-branch
526 $ echo a > a
526 $ echo a > a
527 $ hg add a
527 $ hg add a
528 $ hg commit -m A
528 $ hg commit -m A
529 $ hg branch foo
529 $ hg branch foo
530 marked working directory as branch foo
530 marked working directory as branch foo
531 (branches are permanent and global, did you want a bookmark?)
531 (branches are permanent and global, did you want a bookmark?)
532 $ echo a > b
532 $ echo a > b
533 $ hg add b
533 $ hg add b
534 $ hg commit -m foo-B
534 $ hg commit -m foo-B
535 $ echo a > c
535 $ echo a > c
536 $ hg add c
536 $ hg add c
537 $ hg commit -m foo-C
537 $ hg commit -m foo-C
538
538
539 $ hg branch
539 $ hg branch
540 foo
540 foo
541 $ echo "pick efefa76d6dc3 2 foo-C" >> cmd
541 $ echo "pick efefa76d6dc3 2 foo-C" >> cmd
542 $ echo "pick 7336e7550422 1 foo-B" >> cmd
542 $ echo "pick 7336e7550422 1 foo-B" >> cmd
543
543
544 $ HGEDITOR=cat hg histedit -r ".^" --commands cmd --quiet
544 $ HGEDITOR=cat hg histedit -r ".^" --commands cmd --quiet
545 $ hg log --template '{rev} {branch}\n'
545 $ hg log --template '{rev} {branch}\n'
546 2 foo
546 2 foo
547 1 foo
547 1 foo
548 0 default
548 0 default
549 $ hg branch
549 $ hg branch
550 foo
550 foo
551
551
552 $ cd ..
552 $ cd ..
@@ -1,556 +1,556 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 > strip=
6 > strip=
7 > mockmakedate = $TESTDIR/mockmakedate.py
7 > mockmakedate = $TESTDIR/mockmakedate.py
8 > EOF
8 > EOF
9
9
10 $ initrepo ()
10 $ initrepo ()
11 > {
11 > {
12 > hg init r
12 > hg init r
13 > cd r
13 > cd r
14 > for x in a b c d e f g; do
14 > for x in a b c d e f g; do
15 > echo $x > $x
15 > echo $x > $x
16 > hg add $x
16 > hg add $x
17 > hg ci -m $x
17 > hg ci -m $x
18 > done
18 > done
19 > }
19 > }
20
20
21 $ initrepo
21 $ initrepo
22
22
23 log before edit
23 log before edit
24 $ hg log --graph
24 $ hg log --graph
25 @ changeset: 6:3c6a8ed2ebe8
25 @ changeset: 6:3c6a8ed2ebe8
26 | tag: tip
26 | tag: tip
27 | user: test
27 | user: test
28 | date: Thu Jan 01 00:00:00 1970 +0000
28 | date: Thu Jan 01 00:00:00 1970 +0000
29 | summary: g
29 | summary: g
30 |
30 |
31 o changeset: 5:652413bf663e
31 o changeset: 5:652413bf663e
32 | user: test
32 | user: test
33 | date: Thu Jan 01 00:00:00 1970 +0000
33 | date: Thu Jan 01 00:00:00 1970 +0000
34 | summary: f
34 | summary: f
35 |
35 |
36 o changeset: 4:e860deea161a
36 o changeset: 4:e860deea161a
37 | user: test
37 | user: test
38 | date: Thu Jan 01 00:00:00 1970 +0000
38 | date: Thu Jan 01 00:00:00 1970 +0000
39 | summary: e
39 | summary: e
40 |
40 |
41 o changeset: 3:055a42cdd887
41 o changeset: 3:055a42cdd887
42 | user: test
42 | user: test
43 | date: Thu Jan 01 00:00:00 1970 +0000
43 | date: Thu Jan 01 00:00:00 1970 +0000
44 | summary: d
44 | summary: d
45 |
45 |
46 o changeset: 2:177f92b77385
46 o changeset: 2:177f92b77385
47 | user: test
47 | user: test
48 | date: Thu Jan 01 00:00:00 1970 +0000
48 | date: Thu Jan 01 00:00:00 1970 +0000
49 | summary: c
49 | summary: c
50 |
50 |
51 o changeset: 1:d2ae7f538514
51 o changeset: 1:d2ae7f538514
52 | user: test
52 | user: test
53 | date: Thu Jan 01 00:00:00 1970 +0000
53 | date: Thu Jan 01 00:00:00 1970 +0000
54 | summary: b
54 | summary: b
55 |
55 |
56 o changeset: 0:cb9a9f314b8b
56 o changeset: 0:cb9a9f314b8b
57 user: test
57 user: test
58 date: Thu Jan 01 00:00:00 1970 +0000
58 date: Thu Jan 01 00:00:00 1970 +0000
59 summary: a
59 summary: a
60
60
61 dirty a file
61 dirty a file
62 $ echo a > g
62 $ echo a > g
63 $ hg histedit 177f92b77385 --commands - 2>&1 << EOF
63 $ hg histedit 177f92b77385 --commands - 2>&1 << EOF
64 > EOF
64 > EOF
65 abort: uncommitted changes
65 abort: uncommitted changes
66 [20]
66 [20]
67 $ echo g > g
67 $ echo g > g
68
68
69 edit the history
69 edit the history
70 $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle
70 $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle
71 > pick 177f92b77385 c
71 > pick 177f92b77385 c
72 > pick 055a42cdd887 d
72 > pick 055a42cdd887 d
73 > edit e860deea161a e
73 > edit e860deea161a e
74 > pick 652413bf663e f
74 > pick 652413bf663e f
75 > pick 3c6a8ed2ebe8 g
75 > pick 3c6a8ed2ebe8 g
76 > EOF
76 > EOF
77 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
77 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
78 Editing (e860deea161a), you may commit or record as needed now.
78 Editing (e860deea161a), commit as needed now to split the change
79 (hg histedit --continue to resume)
79 (to edit e860deea161a, `hg histedit --continue` after making changes)
80
80
81 try to update and get an error
81 try to update and get an error
82 $ hg update tip
82 $ hg update tip
83 abort: histedit in progress
83 abort: histedit in progress
84 (use 'hg histedit --continue' or 'hg histedit --abort')
84 (use 'hg histedit --continue' or 'hg histedit --abort')
85 [20]
85 [20]
86
86
87 edit the plan via the editor
87 edit the plan via the editor
88 $ cat >> $TESTTMP/editplan.sh <<EOF
88 $ cat >> $TESTTMP/editplan.sh <<EOF
89 > cat > \$1 <<EOF2
89 > cat > \$1 <<EOF2
90 > drop e860deea161a e
90 > drop e860deea161a e
91 > drop 652413bf663e f
91 > drop 652413bf663e f
92 > drop 3c6a8ed2ebe8 g
92 > drop 3c6a8ed2ebe8 g
93 > EOF2
93 > EOF2
94 > EOF
94 > EOF
95 $ HGEDITOR="sh $TESTTMP/editplan.sh" hg histedit --edit-plan
95 $ HGEDITOR="sh $TESTTMP/editplan.sh" hg histedit --edit-plan
96 $ cat .hg/histedit-state
96 $ cat .hg/histedit-state
97 v1
97 v1
98 055a42cdd88768532f9cf79daa407fc8d138de9b
98 055a42cdd88768532f9cf79daa407fc8d138de9b
99 3c6a8ed2ebe862cc949d2caa30775dd6f16fb799
99 3c6a8ed2ebe862cc949d2caa30775dd6f16fb799
100 False
100 False
101 3
101 3
102 drop
102 drop
103 e860deea161a2f77de56603b340ebbb4536308ae
103 e860deea161a2f77de56603b340ebbb4536308ae
104 drop
104 drop
105 652413bf663ef2a641cab26574e46d5f5a64a55a
105 652413bf663ef2a641cab26574e46d5f5a64a55a
106 drop
106 drop
107 3c6a8ed2ebe862cc949d2caa30775dd6f16fb799
107 3c6a8ed2ebe862cc949d2caa30775dd6f16fb799
108 0
108 0
109 strip-backup/177f92b77385-0ebe6a8f-histedit.hg
109 strip-backup/177f92b77385-0ebe6a8f-histedit.hg
110
110
111 edit the plan via --commands
111 edit the plan via --commands
112 $ hg histedit --edit-plan --commands - 2>&1 << EOF
112 $ hg histedit --edit-plan --commands - 2>&1 << EOF
113 > edit e860deea161a e
113 > edit e860deea161a e
114 > pick 652413bf663e f
114 > pick 652413bf663e f
115 > drop 3c6a8ed2ebe8 g
115 > drop 3c6a8ed2ebe8 g
116 > EOF
116 > EOF
117 $ cat .hg/histedit-state
117 $ cat .hg/histedit-state
118 v1
118 v1
119 055a42cdd88768532f9cf79daa407fc8d138de9b
119 055a42cdd88768532f9cf79daa407fc8d138de9b
120 3c6a8ed2ebe862cc949d2caa30775dd6f16fb799
120 3c6a8ed2ebe862cc949d2caa30775dd6f16fb799
121 False
121 False
122 3
122 3
123 edit
123 edit
124 e860deea161a2f77de56603b340ebbb4536308ae
124 e860deea161a2f77de56603b340ebbb4536308ae
125 pick
125 pick
126 652413bf663ef2a641cab26574e46d5f5a64a55a
126 652413bf663ef2a641cab26574e46d5f5a64a55a
127 drop
127 drop
128 3c6a8ed2ebe862cc949d2caa30775dd6f16fb799
128 3c6a8ed2ebe862cc949d2caa30775dd6f16fb799
129 0
129 0
130 strip-backup/177f92b77385-0ebe6a8f-histedit.hg
130 strip-backup/177f92b77385-0ebe6a8f-histedit.hg
131
131
132 Go at a random point and try to continue
132 Go at a random point and try to continue
133
133
134 $ hg id -n
134 $ hg id -n
135 3+
135 3+
136 $ hg up 0
136 $ hg up 0
137 abort: histedit in progress
137 abort: histedit in progress
138 (use 'hg histedit --continue' or 'hg histedit --abort')
138 (use 'hg histedit --continue' or 'hg histedit --abort')
139 [20]
139 [20]
140
140
141 Try to delete necessary commit
141 Try to delete necessary commit
142 $ hg strip -r 652413b
142 $ hg strip -r 652413b
143 abort: histedit in progress, can't strip 652413bf663e
143 abort: histedit in progress, can't strip 652413bf663e
144 [255]
144 [255]
145
145
146 commit, then edit the revision
146 commit, then edit the revision
147 $ hg ci -m 'wat'
147 $ hg ci -m 'wat'
148 created new head
148 created new head
149 $ echo a > e
149 $ echo a > e
150
150
151 qnew should fail while we're in the middle of the edit step
151 qnew should fail while we're in the middle of the edit step
152
152
153 $ hg --config extensions.mq= qnew please-fail
153 $ hg --config extensions.mq= qnew please-fail
154 abort: histedit in progress
154 abort: histedit in progress
155 (use 'hg histedit --continue' or 'hg histedit --abort')
155 (use 'hg histedit --continue' or 'hg histedit --abort')
156 [20]
156 [20]
157 $ HGEDITOR='echo foobaz > ' hg histedit --continue 2>&1 | fixbundle
157 $ HGEDITOR='echo foobaz > ' hg histedit --continue 2>&1 | fixbundle
158
158
159 $ hg log --graph
159 $ hg log --graph
160 @ changeset: 6:b5f70786f9b0
160 @ changeset: 6:b5f70786f9b0
161 | tag: tip
161 | tag: tip
162 | user: test
162 | user: test
163 | date: Thu Jan 01 00:00:00 1970 +0000
163 | date: Thu Jan 01 00:00:00 1970 +0000
164 | summary: f
164 | summary: f
165 |
165 |
166 o changeset: 5:a5e1ba2f7afb
166 o changeset: 5:a5e1ba2f7afb
167 | user: test
167 | user: test
168 | date: Thu Jan 01 00:00:00 1970 +0000
168 | date: Thu Jan 01 00:00:00 1970 +0000
169 | summary: foobaz
169 | summary: foobaz
170 |
170 |
171 o changeset: 4:1a60820cd1f6
171 o changeset: 4:1a60820cd1f6
172 | user: test
172 | user: test
173 | date: Thu Jan 01 00:00:00 1970 +0000
173 | date: Thu Jan 01 00:00:00 1970 +0000
174 | summary: wat
174 | summary: wat
175 |
175 |
176 o changeset: 3:055a42cdd887
176 o changeset: 3:055a42cdd887
177 | user: test
177 | user: test
178 | date: Thu Jan 01 00:00:00 1970 +0000
178 | date: Thu Jan 01 00:00:00 1970 +0000
179 | summary: d
179 | summary: d
180 |
180 |
181 o changeset: 2:177f92b77385
181 o changeset: 2:177f92b77385
182 | user: test
182 | user: test
183 | date: Thu Jan 01 00:00:00 1970 +0000
183 | date: Thu Jan 01 00:00:00 1970 +0000
184 | summary: c
184 | summary: c
185 |
185 |
186 o changeset: 1:d2ae7f538514
186 o changeset: 1:d2ae7f538514
187 | user: test
187 | user: test
188 | date: Thu Jan 01 00:00:00 1970 +0000
188 | date: Thu Jan 01 00:00:00 1970 +0000
189 | summary: b
189 | summary: b
190 |
190 |
191 o changeset: 0:cb9a9f314b8b
191 o changeset: 0:cb9a9f314b8b
192 user: test
192 user: test
193 date: Thu Jan 01 00:00:00 1970 +0000
193 date: Thu Jan 01 00:00:00 1970 +0000
194 summary: a
194 summary: a
195
195
196
196
197 $ hg cat e
197 $ hg cat e
198 a
198 a
199
199
200 Stripping necessary commits should not break --abort
200 Stripping necessary commits should not break --abort
201
201
202 $ hg histedit 1a60820cd1f6 --commands - 2>&1 << EOF| fixbundle
202 $ hg histedit 1a60820cd1f6 --commands - 2>&1 << EOF| fixbundle
203 > edit 1a60820cd1f6 wat
203 > edit 1a60820cd1f6 wat
204 > pick a5e1ba2f7afb foobaz
204 > pick a5e1ba2f7afb foobaz
205 > pick b5f70786f9b0 g
205 > pick b5f70786f9b0 g
206 > EOF
206 > EOF
207 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
207 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
208 Editing (1a60820cd1f6), you may commit or record as needed now.
208 Editing (1a60820cd1f6), commit as needed now to split the change
209 (hg histedit --continue to resume)
209 (to edit 1a60820cd1f6, `hg histedit --continue` after making changes)
210
210
211 $ mv .hg/histedit-state .hg/histedit-state.bak
211 $ mv .hg/histedit-state .hg/histedit-state.bak
212 $ hg strip -q -r b5f70786f9b0
212 $ hg strip -q -r b5f70786f9b0
213 $ mv .hg/histedit-state.bak .hg/histedit-state
213 $ mv .hg/histedit-state.bak .hg/histedit-state
214 $ hg histedit --abort
214 $ hg histedit --abort
215 adding changesets
215 adding changesets
216 adding manifests
216 adding manifests
217 adding file changes
217 adding file changes
218 added 1 changesets with 1 changes to 3 files
218 added 1 changesets with 1 changes to 3 files
219 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
219 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
220 $ hg log -r .
220 $ hg log -r .
221 changeset: 6:b5f70786f9b0
221 changeset: 6:b5f70786f9b0
222 tag: tip
222 tag: tip
223 user: test
223 user: test
224 date: Thu Jan 01 00:00:00 1970 +0000
224 date: Thu Jan 01 00:00:00 1970 +0000
225 summary: f
225 summary: f
226
226
227
227
228 check histedit_source
228 check histedit_source
229
229
230 $ hg log --debug --rev 5
230 $ hg log --debug --rev 5
231 changeset: 5:a5e1ba2f7afb899ef1581cea528fd885d2fca70d
231 changeset: 5:a5e1ba2f7afb899ef1581cea528fd885d2fca70d
232 phase: draft
232 phase: draft
233 parent: 4:1a60820cd1f6004a362aa622ebc47d59bc48eb34
233 parent: 4:1a60820cd1f6004a362aa622ebc47d59bc48eb34
234 parent: -1:0000000000000000000000000000000000000000
234 parent: -1:0000000000000000000000000000000000000000
235 manifest: 5:5ad3be8791f39117565557781f5464363b918a45
235 manifest: 5:5ad3be8791f39117565557781f5464363b918a45
236 user: test
236 user: test
237 date: Thu Jan 01 00:00:00 1970 +0000
237 date: Thu Jan 01 00:00:00 1970 +0000
238 files: e
238 files: e
239 extra: branch=default
239 extra: branch=default
240 extra: histedit_source=e860deea161a2f77de56603b340ebbb4536308ae
240 extra: histedit_source=e860deea161a2f77de56603b340ebbb4536308ae
241 description:
241 description:
242 foobaz
242 foobaz
243
243
244
244
245
245
246 $ hg histedit tip --commands - 2>&1 <<EOF| fixbundle
246 $ hg histedit tip --commands - 2>&1 <<EOF| fixbundle
247 > edit b5f70786f9b0 f
247 > edit b5f70786f9b0 f
248 > EOF
248 > EOF
249 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
249 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
250 Editing (b5f70786f9b0), you may commit or record as needed now.
250 Editing (b5f70786f9b0), commit as needed now to split the change
251 (hg histedit --continue to resume)
251 (to edit b5f70786f9b0, `hg histedit --continue` after making changes)
252 $ hg status
252 $ hg status
253 A f
253 A f
254
254
255 $ hg summary
255 $ hg summary
256 parent: 5:a5e1ba2f7afb
256 parent: 5:a5e1ba2f7afb
257 foobaz
257 foobaz
258 branch: default
258 branch: default
259 commit: 1 added (new branch head)
259 commit: 1 added (new branch head)
260 update: 1 new changesets (update)
260 update: 1 new changesets (update)
261 phases: 7 draft
261 phases: 7 draft
262 hist: 1 remaining (histedit --continue)
262 hist: 1 remaining (histedit --continue)
263
263
264 (test also that editor is invoked if histedit is continued for
264 (test also that editor is invoked if histedit is continued for
265 "edit" action)
265 "edit" action)
266
266
267 $ HGEDITOR='cat' hg histedit --continue
267 $ HGEDITOR='cat' hg histedit --continue
268 f
268 f
269
269
270
270
271 HG: Enter commit message. Lines beginning with 'HG:' are removed.
271 HG: Enter commit message. Lines beginning with 'HG:' are removed.
272 HG: Leave message empty to abort commit.
272 HG: Leave message empty to abort commit.
273 HG: --
273 HG: --
274 HG: user: test
274 HG: user: test
275 HG: branch 'default'
275 HG: branch 'default'
276 HG: added f
276 HG: added f
277 saved backup bundle to $TESTTMP/r/.hg/strip-backup/b5f70786f9b0-c28d9c86-histedit.hg
277 saved backup bundle to $TESTTMP/r/.hg/strip-backup/b5f70786f9b0-c28d9c86-histedit.hg
278
278
279 $ hg status
279 $ hg status
280
280
281 log after edit
281 log after edit
282 $ hg log --limit 1
282 $ hg log --limit 1
283 changeset: 6:a107ee126658
283 changeset: 6:a107ee126658
284 tag: tip
284 tag: tip
285 user: test
285 user: test
286 date: Thu Jan 01 00:00:00 1970 +0000
286 date: Thu Jan 01 00:00:00 1970 +0000
287 summary: f
287 summary: f
288
288
289
289
290 say we'll change the message, but don't.
290 say we'll change the message, but don't.
291 $ cat > ../edit.sh <<EOF
291 $ cat > ../edit.sh <<EOF
292 > cat "\$1" | sed s/pick/mess/ > tmp
292 > cat "\$1" | sed s/pick/mess/ > tmp
293 > mv tmp "\$1"
293 > mv tmp "\$1"
294 > EOF
294 > EOF
295 $ HGEDITOR="sh ../edit.sh" hg histedit tip 2>&1 | fixbundle
295 $ HGEDITOR="sh ../edit.sh" hg histedit tip 2>&1 | fixbundle
296 $ hg status
296 $ hg status
297 $ hg log --limit 1
297 $ hg log --limit 1
298 changeset: 6:1fd3b2fe7754
298 changeset: 6:1fd3b2fe7754
299 tag: tip
299 tag: tip
300 user: test
300 user: test
301 date: Thu Jan 01 00:00:00 1970 +0000
301 date: Thu Jan 01 00:00:00 1970 +0000
302 summary: f
302 summary: f
303
303
304
304
305 modify the message
305 modify the message
306
306
307 check saving last-message.txt, at first
307 check saving last-message.txt, at first
308
308
309 $ cat > $TESTTMP/commitfailure.py <<EOF
309 $ cat > $TESTTMP/commitfailure.py <<EOF
310 > from mercurial import error
310 > from mercurial import error
311 > def reposetup(ui, repo):
311 > def reposetup(ui, repo):
312 > class commitfailure(repo.__class__):
312 > class commitfailure(repo.__class__):
313 > def commit(self, *args, **kwargs):
313 > def commit(self, *args, **kwargs):
314 > raise error.Abort(b'emulating unexpected abort')
314 > raise error.Abort(b'emulating unexpected abort')
315 > repo.__class__ = commitfailure
315 > repo.__class__ = commitfailure
316 > EOF
316 > EOF
317 $ cat >> .hg/hgrc <<EOF
317 $ cat >> .hg/hgrc <<EOF
318 > [extensions]
318 > [extensions]
319 > # this failure occurs before editor invocation
319 > # this failure occurs before editor invocation
320 > commitfailure = $TESTTMP/commitfailure.py
320 > commitfailure = $TESTTMP/commitfailure.py
321 > EOF
321 > EOF
322
322
323 $ cat > $TESTTMP/editor.sh <<EOF
323 $ cat > $TESTTMP/editor.sh <<EOF
324 > echo "==== before editing"
324 > echo "==== before editing"
325 > cat \$1
325 > cat \$1
326 > echo "===="
326 > echo "===="
327 > echo "check saving last-message.txt" >> \$1
327 > echo "check saving last-message.txt" >> \$1
328 > EOF
328 > EOF
329
329
330 (test that editor is not invoked before transaction starting)
330 (test that editor is not invoked before transaction starting)
331
331
332 $ rm -f .hg/last-message.txt
332 $ rm -f .hg/last-message.txt
333 $ HGEDITOR="sh $TESTTMP/editor.sh" hg histedit tip --commands - 2>&1 << EOF | fixbundle
333 $ HGEDITOR="sh $TESTTMP/editor.sh" hg histedit tip --commands - 2>&1 << EOF | fixbundle
334 > mess 1fd3b2fe7754 f
334 > mess 1fd3b2fe7754 f
335 > EOF
335 > EOF
336 abort: emulating unexpected abort
336 abort: emulating unexpected abort
337 $ test -f .hg/last-message.txt
337 $ test -f .hg/last-message.txt
338 [1]
338 [1]
339
339
340 $ cat >> .hg/hgrc <<EOF
340 $ cat >> .hg/hgrc <<EOF
341 > [extensions]
341 > [extensions]
342 > commitfailure = !
342 > commitfailure = !
343 > EOF
343 > EOF
344 $ hg histedit --abort -q
344 $ hg histedit --abort -q
345
345
346 (test that editor is invoked and commit message is saved into
346 (test that editor is invoked and commit message is saved into
347 "last-message.txt")
347 "last-message.txt")
348
348
349 $ cat >> .hg/hgrc <<EOF
349 $ cat >> .hg/hgrc <<EOF
350 > [hooks]
350 > [hooks]
351 > # this failure occurs after editor invocation
351 > # this failure occurs after editor invocation
352 > pretxncommit.unexpectedabort = false
352 > pretxncommit.unexpectedabort = false
353 > EOF
353 > EOF
354
354
355 $ hg status --rev '1fd3b2fe7754^1' --rev 1fd3b2fe7754
355 $ hg status --rev '1fd3b2fe7754^1' --rev 1fd3b2fe7754
356 A f
356 A f
357
357
358 $ rm -f .hg/last-message.txt
358 $ rm -f .hg/last-message.txt
359 $ HGEDITOR="sh $TESTTMP/editor.sh" hg histedit tip --commands - 2>&1 << EOF
359 $ HGEDITOR="sh $TESTTMP/editor.sh" hg histedit tip --commands - 2>&1 << EOF
360 > mess 1fd3b2fe7754 f
360 > mess 1fd3b2fe7754 f
361 > EOF
361 > EOF
362 ==== before editing
362 ==== before editing
363 f
363 f
364
364
365
365
366 HG: Enter commit message. Lines beginning with 'HG:' are removed.
366 HG: Enter commit message. Lines beginning with 'HG:' are removed.
367 HG: Leave message empty to abort commit.
367 HG: Leave message empty to abort commit.
368 HG: --
368 HG: --
369 HG: user: test
369 HG: user: test
370 HG: branch 'default'
370 HG: branch 'default'
371 HG: added f
371 HG: added f
372 ====
372 ====
373 transaction abort!
373 transaction abort!
374 rollback completed
374 rollback completed
375 note: commit message saved in .hg/last-message.txt
375 note: commit message saved in .hg/last-message.txt
376 note: use 'hg commit --logfile .hg/last-message.txt --edit' to reuse it
376 note: use 'hg commit --logfile .hg/last-message.txt --edit' to reuse it
377 abort: pretxncommit.unexpectedabort hook exited with status 1
377 abort: pretxncommit.unexpectedabort hook exited with status 1
378 [255]
378 [255]
379 $ cat .hg/last-message.txt
379 $ cat .hg/last-message.txt
380 f
380 f
381
381
382
382
383 check saving last-message.txt
383 check saving last-message.txt
384
384
385 (test also that editor is invoked if histedit is continued for "message"
385 (test also that editor is invoked if histedit is continued for "message"
386 action)
386 action)
387
387
388 $ HGEDITOR=cat hg histedit --continue
388 $ HGEDITOR=cat hg histedit --continue
389 f
389 f
390
390
391
391
392 HG: Enter commit message. Lines beginning with 'HG:' are removed.
392 HG: Enter commit message. Lines beginning with 'HG:' are removed.
393 HG: Leave message empty to abort commit.
393 HG: Leave message empty to abort commit.
394 HG: --
394 HG: --
395 HG: user: test
395 HG: user: test
396 HG: branch 'default'
396 HG: branch 'default'
397 HG: added f
397 HG: added f
398 transaction abort!
398 transaction abort!
399 rollback completed
399 rollback completed
400 note: commit message saved in .hg/last-message.txt
400 note: commit message saved in .hg/last-message.txt
401 note: use 'hg commit --logfile .hg/last-message.txt --edit' to reuse it
401 note: use 'hg commit --logfile .hg/last-message.txt --edit' to reuse it
402 abort: pretxncommit.unexpectedabort hook exited with status 1
402 abort: pretxncommit.unexpectedabort hook exited with status 1
403 [255]
403 [255]
404
404
405 $ cat >> .hg/hgrc <<EOF
405 $ cat >> .hg/hgrc <<EOF
406 > [hooks]
406 > [hooks]
407 > pretxncommit.unexpectedabort =
407 > pretxncommit.unexpectedabort =
408 > EOF
408 > EOF
409 $ hg histedit --abort -q
409 $ hg histedit --abort -q
410
410
411 then, check "modify the message" itself
411 then, check "modify the message" itself
412
412
413 $ hg histedit tip --commands - 2>&1 << EOF | fixbundle
413 $ hg histedit tip --commands - 2>&1 << EOF | fixbundle
414 > mess 1fd3b2fe7754 f
414 > mess 1fd3b2fe7754 f
415 > EOF
415 > EOF
416 $ hg status
416 $ hg status
417 $ hg log --limit 1
417 $ hg log --limit 1
418 changeset: 6:62feedb1200e
418 changeset: 6:62feedb1200e
419 tag: tip
419 tag: tip
420 user: test
420 user: test
421 date: Thu Jan 01 00:00:00 1970 +0000
421 date: Thu Jan 01 00:00:00 1970 +0000
422 summary: f
422 summary: f
423
423
424
424
425 rollback should not work after a histedit
425 rollback should not work after a histedit
426 $ hg rollback
426 $ hg rollback
427 no rollback information available
427 no rollback information available
428 [1]
428 [1]
429
429
430 $ cd ..
430 $ cd ..
431 $ hg clone -qr0 r r0
431 $ hg clone -qr0 r r0
432 $ cd r0
432 $ cd r0
433 $ hg phase -fdr0
433 $ hg phase -fdr0
434 $ hg histedit --commands - 0 2>&1 << EOF
434 $ hg histedit --commands - 0 2>&1 << EOF
435 > edit cb9a9f314b8b a > $EDITED
435 > edit cb9a9f314b8b a > $EDITED
436 > EOF
436 > EOF
437 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
437 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
438 Editing (cb9a9f314b8b), you may commit or record as needed now.
438 Editing (cb9a9f314b8b), commit as needed now to split the change
439 (hg histedit --continue to resume)
439 (to edit cb9a9f314b8b, `hg histedit --continue` after making changes)
440 [240]
440 [240]
441 $ HGEDITOR=true hg histedit --continue
441 $ HGEDITOR=true hg histedit --continue
442 saved backup bundle to $TESTTMP/r0/.hg/strip-backup/cb9a9f314b8b-cc5ccb0b-histedit.hg
442 saved backup bundle to $TESTTMP/r0/.hg/strip-backup/cb9a9f314b8b-cc5ccb0b-histedit.hg
443
443
444 $ hg log -G
444 $ hg log -G
445 @ changeset: 0:0efcea34f18a
445 @ changeset: 0:0efcea34f18a
446 tag: tip
446 tag: tip
447 user: test
447 user: test
448 date: Thu Jan 01 00:00:00 1970 +0000
448 date: Thu Jan 01 00:00:00 1970 +0000
449 summary: a
449 summary: a
450
450
451 $ echo foo >> b
451 $ echo foo >> b
452 $ hg addr
452 $ hg addr
453 adding b
453 adding b
454 $ hg ci -m 'add b'
454 $ hg ci -m 'add b'
455 $ echo foo >> a
455 $ echo foo >> a
456 $ hg ci -m 'extend a'
456 $ hg ci -m 'extend a'
457 $ hg phase --public 1
457 $ hg phase --public 1
458 Attempting to fold a change into a public change should not work:
458 Attempting to fold a change into a public change should not work:
459 $ cat > ../edit.sh <<EOF
459 $ cat > ../edit.sh <<EOF
460 > cat "\$1" | sed s/pick/fold/ > tmp
460 > cat "\$1" | sed s/pick/fold/ > tmp
461 > mv tmp "\$1"
461 > mv tmp "\$1"
462 > EOF
462 > EOF
463 $ HGEDITOR="sh ../edit.sh" hg histedit 2
463 $ HGEDITOR="sh ../edit.sh" hg histedit 2
464 warning: histedit rules saved to: .hg/histedit-last-edit.txt
464 warning: histedit rules saved to: .hg/histedit-last-edit.txt
465 hg: parse error: first changeset cannot use verb "fold"
465 hg: parse error: first changeset cannot use verb "fold"
466 [10]
466 [10]
467 $ cat .hg/histedit-last-edit.txt
467 $ cat .hg/histedit-last-edit.txt
468 fold 0012be4a27ea 2 extend a
468 fold 0012be4a27ea 2 extend a
469
469
470 # Edit history between 0012be4a27ea and 0012be4a27ea
470 # Edit history between 0012be4a27ea and 0012be4a27ea
471 #
471 #
472 # Commits are listed from least to most recent
472 # Commits are listed from least to most recent
473 #
473 #
474 # You can reorder changesets by reordering the lines
474 # You can reorder changesets by reordering the lines
475 #
475 #
476 # Commands:
476 # Commands:
477 #
477 #
478 # e, edit = use commit, but stop for amending
478 # e, edit = use commit, but stop for amending
479 # m, mess = edit commit message without changing commit content
479 # m, mess = edit commit message without changing commit content
480 # p, fold = use commit
480 # p, fold = use commit
481 # b, base = checkout changeset and apply further changesets from there
481 # b, base = checkout changeset and apply further changesets from there
482 # d, drop = remove commit from history
482 # d, drop = remove commit from history
483 # f, fold = use commit, but combine it with the one above
483 # f, fold = use commit, but combine it with the one above
484 # r, roll = like fold, but discard this commit's description and date
484 # r, roll = like fold, but discard this commit's description and date
485 #
485 #
486
486
487 $ cd ..
487 $ cd ..
488
488
489 ============================================
489 ============================================
490 Test update-timestamp config option in mess|
490 Test update-timestamp config option in mess|
491 ============================================
491 ============================================
492
492
493 $ addwithdate ()
493 $ addwithdate ()
494 > {
494 > {
495 > echo $1 > $1
495 > echo $1 > $1
496 > hg add $1
496 > hg add $1
497 > hg ci -m $1 -d "$2 0"
497 > hg ci -m $1 -d "$2 0"
498 > }
498 > }
499
499
500 $ initrepo ()
500 $ initrepo ()
501 > {
501 > {
502 > hg init r2
502 > hg init r2
503 > cd r2
503 > cd r2
504 > addwithdate a 1
504 > addwithdate a 1
505 > addwithdate b 2
505 > addwithdate b 2
506 > addwithdate c 3
506 > addwithdate c 3
507 > addwithdate d 4
507 > addwithdate d 4
508 > addwithdate e 5
508 > addwithdate e 5
509 > addwithdate f 6
509 > addwithdate f 6
510 > }
510 > }
511
511
512 $ initrepo
512 $ initrepo
513
513
514 log before edit
514 log before edit
515
515
516 $ hg log --limit 1
516 $ hg log --limit 1
517 changeset: 5:178e35e0ce73
517 changeset: 5:178e35e0ce73
518 tag: tip
518 tag: tip
519 user: test
519 user: test
520 date: Thu Jan 01 00:00:06 1970 +0000
520 date: Thu Jan 01 00:00:06 1970 +0000
521 summary: f
521 summary: f
522
522
523 $ hg histedit tip --commands - 2>&1 --config rewrite.update-timestamp=True << EOF | fixbundle
523 $ hg histedit tip --commands - 2>&1 --config rewrite.update-timestamp=True << EOF | fixbundle
524 > mess 178e35e0ce73 f
524 > mess 178e35e0ce73 f
525 > EOF
525 > EOF
526
526
527 log after edit
527 log after edit
528
528
529 $ hg log --limit 1
529 $ hg log --limit 1
530 changeset: 5:98bf456d476b
530 changeset: 5:98bf456d476b
531 tag: tip
531 tag: tip
532 user: test
532 user: test
533 date: Thu Jan 01 00:00:00 1970 +0000
533 date: Thu Jan 01 00:00:00 1970 +0000
534 summary: f
534 summary: f
535
535
536
536
537 $ cd ..
537 $ cd ..
538
538
539 warn the user on editing tagged commits
539 warn the user on editing tagged commits
540
540
541 $ hg init issue4017
541 $ hg init issue4017
542 $ cd issue4017
542 $ cd issue4017
543 $ echo > a
543 $ echo > a
544 $ hg ci -Am 'add a'
544 $ hg ci -Am 'add a'
545 adding a
545 adding a
546 $ hg tag a
546 $ hg tag a
547 $ hg tags
547 $ hg tags
548 tip 1:bd7ee4f3939b
548 tip 1:bd7ee4f3939b
549 a 0:a8a82d372bb3
549 a 0:a8a82d372bb3
550 $ hg histedit
550 $ hg histedit
551 warning: tags associated with the given changeset will be lost after histedit.
551 warning: tags associated with the given changeset will be lost after histedit.
552 do you want to continue (yN)? n
552 do you want to continue (yN)? n
553 abort: histedit cancelled
553 abort: histedit cancelled
554
554
555 [255]
555 [255]
556 $ cd ..
556 $ cd ..
@@ -1,80 +1,80 b''
1 #testcases abortcommand abortflag
1 #testcases abortcommand abortflag
2
2
3 #if abortflag
3 #if abortflag
4 $ cat >> $HGRCPATH <<EOF
4 $ cat >> $HGRCPATH <<EOF
5 > [alias]
5 > [alias]
6 > abort = histedit --abort
6 > abort = histedit --abort
7 > EOF
7 > EOF
8 #endif
8 #endif
9
9
10 $ . "$TESTDIR/histedit-helpers.sh"
10 $ . "$TESTDIR/histedit-helpers.sh"
11
11
12 Enable extension used by this test
12 Enable extension used by this test
13 $ cat >>$HGRCPATH <<EOF
13 $ cat >>$HGRCPATH <<EOF
14 > [extensions]
14 > [extensions]
15 > histedit=
15 > histedit=
16 > EOF
16 > EOF
17
17
18 =================================
18 =================================
19 Test backup-bundle config option|
19 Test backup-bundle config option|
20 =================================
20 =================================
21 Repo setup:
21 Repo setup:
22 $ hg init foo
22 $ hg init foo
23 $ cd foo
23 $ cd foo
24 $ echo first>file
24 $ echo first>file
25 $ hg ci -qAm one
25 $ hg ci -qAm one
26 $ echo second>>file
26 $ echo second>>file
27 $ hg ci -m two
27 $ hg ci -m two
28 $ echo third>>file
28 $ echo third>>file
29 $ hg ci -m three
29 $ hg ci -m three
30 $ echo forth>>file
30 $ echo forth>>file
31 $ hg ci -m four
31 $ hg ci -m four
32 $ hg log -G --style compact
32 $ hg log -G --style compact
33 @ 3[tip] 7d5187087c79 1970-01-01 00:00 +0000 test
33 @ 3[tip] 7d5187087c79 1970-01-01 00:00 +0000 test
34 | four
34 | four
35 |
35 |
36 o 2 80d23dfa866d 1970-01-01 00:00 +0000 test
36 o 2 80d23dfa866d 1970-01-01 00:00 +0000 test
37 | three
37 | three
38 |
38 |
39 o 1 6153eb23e623 1970-01-01 00:00 +0000 test
39 o 1 6153eb23e623 1970-01-01 00:00 +0000 test
40 | two
40 | two
41 |
41 |
42 o 0 36b4bdd91f5b 1970-01-01 00:00 +0000 test
42 o 0 36b4bdd91f5b 1970-01-01 00:00 +0000 test
43 one
43 one
44
44
45 Test when `backup-bundle` config option is enabled:
45 Test when `backup-bundle` config option is enabled:
46 $ hg histedit -r '36b4bdd91f5b' --commands - << EOF
46 $ hg histedit -r '36b4bdd91f5b' --commands - << EOF
47 > pick 36b4bdd91f5b 0 one
47 > pick 36b4bdd91f5b 0 one
48 > pick 6153eb23e623 1 two
48 > pick 6153eb23e623 1 two
49 > roll 80d23dfa866d 2 three
49 > roll 80d23dfa866d 2 three
50 > edit 7d5187087c79 3 four
50 > edit 7d5187087c79 3 four
51 > EOF
51 > EOF
52 merging file
52 merging file
53 Editing (7d5187087c79), you may commit or record as needed now.
53 Editing (7d5187087c79), commit as needed now to split the change
54 (hg histedit --continue to resume)
54 (to edit 7d5187087c79, `hg histedit --continue` after making changes)
55 [240]
55 [240]
56 $ hg abort
56 $ hg abort
57 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
57 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
58 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/1d8f701c7b35-cf7be322-backup.hg
58 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/1d8f701c7b35-cf7be322-backup.hg
59 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/5c0056670bce-b54b65d0-backup.hg
59 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/5c0056670bce-b54b65d0-backup.hg
60
60
61 Test when `backup-bundle` config option is not enabled
61 Test when `backup-bundle` config option is not enabled
62 Enable config option:
62 Enable config option:
63 $ cat >>$HGRCPATH <<EOF
63 $ cat >>$HGRCPATH <<EOF
64 > [rewrite]
64 > [rewrite]
65 > backup-bundle = False
65 > backup-bundle = False
66 > EOF
66 > EOF
67
67
68 $ hg histedit -r '36b4bdd91f5b' --commands - << EOF
68 $ hg histedit -r '36b4bdd91f5b' --commands - << EOF
69 > pick 36b4bdd91f5b 0 one
69 > pick 36b4bdd91f5b 0 one
70 > pick 6153eb23e623 1 two
70 > pick 6153eb23e623 1 two
71 > roll 80d23dfa866d 2 three
71 > roll 80d23dfa866d 2 three
72 > edit 7d5187087c79 3 four
72 > edit 7d5187087c79 3 four
73 > EOF
73 > EOF
74 merging file
74 merging file
75 Editing (7d5187087c79), you may commit or record as needed now.
75 Editing (7d5187087c79), commit as needed now to split the change
76 (hg histedit --continue to resume)
76 (to edit 7d5187087c79, `hg histedit --continue` after making changes)
77 [240]
77 [240]
78
78
79 $ hg abort
79 $ hg abort
80 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
80 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -1,218 +1,218 b''
1 test for old histedit issue #6:
1 test for old histedit issue #6:
2 editing a changeset without any actual change would corrupt the repository
2 editing a changeset without any actual change would corrupt the repository
3
3
4 $ . "$TESTDIR/histedit-helpers.sh"
4 $ . "$TESTDIR/histedit-helpers.sh"
5
5
6 $ cat >> $HGRCPATH <<EOF
6 $ cat >> $HGRCPATH <<EOF
7 > [extensions]
7 > [extensions]
8 > histedit=
8 > histedit=
9 > EOF
9 > EOF
10
10
11 $ initrepo ()
11 $ initrepo ()
12 > {
12 > {
13 > dir="$1"
13 > dir="$1"
14 > comment="$2"
14 > comment="$2"
15 > if [ -n "${comment}" ]; then
15 > if [ -n "${comment}" ]; then
16 > echo % ${comment}
16 > echo % ${comment}
17 > echo % ${comment} | sed 's:.:-:g'
17 > echo % ${comment} | sed 's:.:-:g'
18 > fi
18 > fi
19 > hg init ${dir}
19 > hg init ${dir}
20 > cd ${dir}
20 > cd ${dir}
21 > for x in a b c d e f ; do
21 > for x in a b c d e f ; do
22 > echo $x > $x
22 > echo $x > $x
23 > hg add $x
23 > hg add $x
24 > hg ci -m $x
24 > hg ci -m $x
25 > done
25 > done
26 > cd ..
26 > cd ..
27 > }
27 > }
28
28
29 $ geneditor ()
29 $ geneditor ()
30 > {
30 > {
31 > # generate an editor script for selecting changesets to be edited
31 > # generate an editor script for selecting changesets to be edited
32 > choice=$1 # changesets that should be edited (using sed line ranges)
32 > choice=$1 # changesets that should be edited (using sed line ranges)
33 > cat <<EOF | sed 's:^....::'
33 > cat <<EOF | sed 's:^....::'
34 > # editing the rules, replacing 'pick' with 'edit' for the chosen lines
34 > # editing the rules, replacing 'pick' with 'edit' for the chosen lines
35 > sed '${choice}s:^pick:edit:' "\$1" > "\${1}.tmp"
35 > sed '${choice}s:^pick:edit:' "\$1" > "\${1}.tmp"
36 > mv "\${1}.tmp" "\$1"
36 > mv "\${1}.tmp" "\$1"
37 > # displaying the resulting rules, minus comments and empty lines
37 > # displaying the resulting rules, minus comments and empty lines
38 > sed '/^#/d;/^$/d;s:^:| :' "\$1" >&2
38 > sed '/^#/d;/^$/d;s:^:| :' "\$1" >&2
39 > EOF
39 > EOF
40 > }
40 > }
41
41
42 $ startediting ()
42 $ startediting ()
43 > {
43 > {
44 > # begin an editing session
44 > # begin an editing session
45 > choice="$1" # changesets that should be edited
45 > choice="$1" # changesets that should be edited
46 > number="$2" # number of changesets considered (from tip)
46 > number="$2" # number of changesets considered (from tip)
47 > comment="$3"
47 > comment="$3"
48 > geneditor "${choice}" > edit.sh
48 > geneditor "${choice}" > edit.sh
49 > echo % start editing the history ${comment}
49 > echo % start editing the history ${comment}
50 > HGEDITOR="sh ./edit.sh" hg histedit -- -${number} 2>&1 | fixbundle
50 > HGEDITOR="sh ./edit.sh" hg histedit -- -${number} 2>&1 | fixbundle
51 > }
51 > }
52
52
53 $ continueediting ()
53 $ continueediting ()
54 > {
54 > {
55 > # continue an edit already in progress
55 > # continue an edit already in progress
56 > editor="$1" # message editor when finalizing editing
56 > editor="$1" # message editor when finalizing editing
57 > comment="$2"
57 > comment="$2"
58 > echo % finalize changeset editing ${comment}
58 > echo % finalize changeset editing ${comment}
59 > HGEDITOR=${editor} hg histedit --continue 2>&1 | fixbundle
59 > HGEDITOR=${editor} hg histedit --continue 2>&1 | fixbundle
60 > }
60 > }
61
61
62 $ graphlog ()
62 $ graphlog ()
63 > {
63 > {
64 > comment="${1:-log}"
64 > comment="${1:-log}"
65 > echo % "${comment}"
65 > echo % "${comment}"
66 > hg log -G --template '{rev} {node} \"{desc|firstline}\"\n'
66 > hg log -G --template '{rev} {node} \"{desc|firstline}\"\n'
67 > }
67 > }
68
68
69
69
70 $ initrepo r1 "test editing with no change"
70 $ initrepo r1 "test editing with no change"
71 % test editing with no change
71 % test editing with no change
72 -----------------------------
72 -----------------------------
73 $ cd r1
73 $ cd r1
74 $ graphlog "log before editing"
74 $ graphlog "log before editing"
75 % log before editing
75 % log before editing
76 @ 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
76 @ 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
77 |
77 |
78 o 4 e860deea161a2f77de56603b340ebbb4536308ae "e"
78 o 4 e860deea161a2f77de56603b340ebbb4536308ae "e"
79 |
79 |
80 o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
80 o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
81 |
81 |
82 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
82 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
83 |
83 |
84 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
84 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
85 |
85 |
86 o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
86 o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
87
87
88 $ startediting 2 3 "(not changing anything)" # edit the 2nd of 3 changesets
88 $ startediting 2 3 "(not changing anything)" # edit the 2nd of 3 changesets
89 % start editing the history (not changing anything)
89 % start editing the history (not changing anything)
90 | pick 055a42cdd887 3 d
90 | pick 055a42cdd887 3 d
91 | edit e860deea161a 4 e
91 | edit e860deea161a 4 e
92 | pick 652413bf663e 5 f
92 | pick 652413bf663e 5 f
93 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
93 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
94 Editing (e860deea161a), you may commit or record as needed now.
94 Editing (e860deea161a), commit as needed now to split the change
95 (hg histedit --continue to resume)
95 (to edit e860deea161a, `hg histedit --continue` after making changes)
96 $ continueediting true "(leaving commit message unaltered)"
96 $ continueediting true "(leaving commit message unaltered)"
97 % finalize changeset editing (leaving commit message unaltered)
97 % finalize changeset editing (leaving commit message unaltered)
98
98
99
99
100 check state of working copy
100 check state of working copy
101 $ hg id
101 $ hg id
102 794fe033d0a0 tip
102 794fe033d0a0 tip
103
103
104 $ graphlog "log after history editing"
104 $ graphlog "log after history editing"
105 % log after history editing
105 % log after history editing
106 @ 5 794fe033d0a030f8df77c5de945fca35c9181c30 "f"
106 @ 5 794fe033d0a030f8df77c5de945fca35c9181c30 "f"
107 |
107 |
108 o 4 04d2fab980779f332dec458cc944f28de8b43435 "e"
108 o 4 04d2fab980779f332dec458cc944f28de8b43435 "e"
109 |
109 |
110 o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
110 o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
111 |
111 |
112 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
112 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
113 |
113 |
114 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
114 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
115 |
115 |
116 o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
116 o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
117
117
118
118
119 $ cd ..
119 $ cd ..
120
120
121 $ initrepo r2 "test editing with no change, then abort"
121 $ initrepo r2 "test editing with no change, then abort"
122 % test editing with no change, then abort
122 % test editing with no change, then abort
123 -----------------------------------------
123 -----------------------------------------
124 $ cd r2
124 $ cd r2
125 $ graphlog "log before editing"
125 $ graphlog "log before editing"
126 % log before editing
126 % log before editing
127 @ 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
127 @ 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
128 |
128 |
129 o 4 e860deea161a2f77de56603b340ebbb4536308ae "e"
129 o 4 e860deea161a2f77de56603b340ebbb4536308ae "e"
130 |
130 |
131 o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
131 o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
132 |
132 |
133 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
133 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
134 |
134 |
135 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
135 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
136 |
136 |
137 o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
137 o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
138
138
139 $ startediting 1,2 3 "(not changing anything)" # edit the 1st two of 3 changesets
139 $ startediting 1,2 3 "(not changing anything)" # edit the 1st two of 3 changesets
140 % start editing the history (not changing anything)
140 % start editing the history (not changing anything)
141 | edit 055a42cdd887 3 d
141 | edit 055a42cdd887 3 d
142 | edit e860deea161a 4 e
142 | edit e860deea161a 4 e
143 | pick 652413bf663e 5 f
143 | pick 652413bf663e 5 f
144 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
144 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
145 Editing (055a42cdd887), you may commit or record as needed now.
145 Editing (055a42cdd887), commit as needed now to split the change
146 (hg histedit --continue to resume)
146 (to edit 055a42cdd887, `hg histedit --continue` after making changes)
147 $ continueediting true "(leaving commit message unaltered)"
147 $ continueediting true "(leaving commit message unaltered)"
148 % finalize changeset editing (leaving commit message unaltered)
148 % finalize changeset editing (leaving commit message unaltered)
149 Editing (e860deea161a), you may commit or record as needed now.
149 Editing (e860deea161a), commit as needed now to split the change
150 (hg histedit --continue to resume)
150 (to edit e860deea161a, `hg histedit --continue` after making changes)
151 $ graphlog "log after first edit"
151 $ graphlog "log after first edit"
152 % log after first edit
152 % log after first edit
153 @ 6 e5ae3ca2f1ffdbd89ec41ebc273a231f7c3022f2 "d"
153 @ 6 e5ae3ca2f1ffdbd89ec41ebc273a231f7c3022f2 "d"
154 |
154 |
155 | o 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
155 | o 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
156 | |
156 | |
157 | o 4 e860deea161a2f77de56603b340ebbb4536308ae "e"
157 | o 4 e860deea161a2f77de56603b340ebbb4536308ae "e"
158 | |
158 | |
159 | o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
159 | o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
160 |/
160 |/
161 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
161 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
162 |
162 |
163 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
163 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
164 |
164 |
165 o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
165 o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
166
166
167
167
168 abort editing session, after first forcibly updating away
168 abort editing session, after first forcibly updating away
169 $ hg up 0
169 $ hg up 0
170 abort: histedit in progress
170 abort: histedit in progress
171 (use 'hg histedit --continue' or 'hg histedit --abort')
171 (use 'hg histedit --continue' or 'hg histedit --abort')
172 [20]
172 [20]
173 $ mv .hg/histedit-state .hg/histedit-state-ignore
173 $ mv .hg/histedit-state .hg/histedit-state-ignore
174 $ hg up 0
174 $ hg up 0
175 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
175 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
176 $ mv .hg/histedit-state-ignore .hg/histedit-state
176 $ mv .hg/histedit-state-ignore .hg/histedit-state
177 $ hg sum
177 $ hg sum
178 parent: 0:cb9a9f314b8b
178 parent: 0:cb9a9f314b8b
179 a
179 a
180 branch: default
180 branch: default
181 commit: 1 added, 1 unknown (new branch head)
181 commit: 1 added, 1 unknown (new branch head)
182 update: 6 new changesets (update)
182 update: 6 new changesets (update)
183 phases: 7 draft
183 phases: 7 draft
184 hist: 2 remaining (histedit --continue)
184 hist: 2 remaining (histedit --continue)
185
185
186 $ hg histedit --abort 2>&1 | fixbundle
186 $ hg histedit --abort 2>&1 | fixbundle
187
187
188 modified files should survive the abort when we've moved away already
188 modified files should survive the abort when we've moved away already
189 $ hg st
189 $ hg st
190 A e
190 A e
191 ? edit.sh
191 ? edit.sh
192
192
193 $ graphlog "log after abort"
193 $ graphlog "log after abort"
194 % log after abort
194 % log after abort
195 o 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
195 o 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
196 |
196 |
197 o 4 e860deea161a2f77de56603b340ebbb4536308ae "e"
197 o 4 e860deea161a2f77de56603b340ebbb4536308ae "e"
198 |
198 |
199 o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
199 o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
200 |
200 |
201 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
201 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
202 |
202 |
203 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
203 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
204 |
204 |
205 @ 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
205 @ 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
206
206
207 aborting and not changing files can skip mentioning updating (no) files
207 aborting and not changing files can skip mentioning updating (no) files
208 $ hg up
208 $ hg up
209 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
209 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
210 $ hg commit --close-branch -m 'closebranch'
210 $ hg commit --close-branch -m 'closebranch'
211 $ startediting 1 1 "(not changing anything)" # edit the 3rd of 3 changesets
211 $ startediting 1 1 "(not changing anything)" # edit the 3rd of 3 changesets
212 % start editing the history (not changing anything)
212 % start editing the history (not changing anything)
213 | edit 292aec348d9e 6 closebranch
213 | edit 292aec348d9e 6 closebranch
214 Editing (292aec348d9e), you may commit or record as needed now.
214 Editing (292aec348d9e), commit as needed now to split the change
215 (hg histedit --continue to resume)
215 (to edit 292aec348d9e, `hg histedit --continue` after making changes)
216 $ hg histedit --abort
216 $ hg histedit --abort
217
217
218 $ cd ..
218 $ cd ..
@@ -1,591 +1,591 b''
1 #testcases abortcommand abortflag
1 #testcases abortcommand abortflag
2
2
3 #if abortflag
3 #if abortflag
4 $ cat >> $HGRCPATH <<EOF
4 $ cat >> $HGRCPATH <<EOF
5 > [alias]
5 > [alias]
6 > abort = histedit --abort
6 > abort = histedit --abort
7 > EOF
7 > EOF
8 #endif
8 #endif
9
9
10 $ . "$TESTDIR/histedit-helpers.sh"
10 $ . "$TESTDIR/histedit-helpers.sh"
11
11
12 Enable obsolete
12 Enable obsolete
13
13
14 $ cat >> $HGRCPATH << EOF
14 $ cat >> $HGRCPATH << EOF
15 > [ui]
15 > [ui]
16 > logtemplate= {rev}:{node|short} {desc|firstline}
16 > logtemplate= {rev}:{node|short} {desc|firstline}
17 > [phases]
17 > [phases]
18 > publish=False
18 > publish=False
19 > [experimental]
19 > [experimental]
20 > evolution.createmarkers=True
20 > evolution.createmarkers=True
21 > evolution.allowunstable=True
21 > evolution.allowunstable=True
22 > [extensions]
22 > [extensions]
23 > histedit=
23 > histedit=
24 > rebase=
24 > rebase=
25 > EOF
25 > EOF
26
26
27 Test that histedit learns about obsolescence not stored in histedit state
27 Test that histedit learns about obsolescence not stored in histedit state
28 $ hg init boo
28 $ hg init boo
29 $ cd boo
29 $ cd boo
30 $ echo a > a
30 $ echo a > a
31 $ hg ci -Am a
31 $ hg ci -Am a
32 adding a
32 adding a
33 $ echo a > b
33 $ echo a > b
34 $ echo a > c
34 $ echo a > c
35 $ echo a > c
35 $ echo a > c
36 $ hg ci -Am b
36 $ hg ci -Am b
37 adding b
37 adding b
38 adding c
38 adding c
39 $ echo a > d
39 $ echo a > d
40 $ hg ci -Am c
40 $ hg ci -Am c
41 adding d
41 adding d
42 $ echo "pick `hg log -r 0 -T '{node|short}'`" > plan
42 $ echo "pick `hg log -r 0 -T '{node|short}'`" > plan
43 $ echo "pick `hg log -r 2 -T '{node|short}'`" >> plan
43 $ echo "pick `hg log -r 2 -T '{node|short}'`" >> plan
44 $ echo "edit `hg log -r 1 -T '{node|short}'`" >> plan
44 $ echo "edit `hg log -r 1 -T '{node|short}'`" >> plan
45 $ hg histedit -r 'all()' --commands plan
45 $ hg histedit -r 'all()' --commands plan
46 Editing (1b2d564fad96), you may commit or record as needed now.
46 Editing (1b2d564fad96), commit as needed now to split the change
47 (hg histedit --continue to resume)
47 (to edit 1b2d564fad96, `hg histedit --continue` after making changes)
48 [240]
48 [240]
49 $ hg st
49 $ hg st
50 A b
50 A b
51 A c
51 A c
52 ? plan
52 ? plan
53 $ hg commit --amend b
53 $ hg commit --amend b
54 $ hg histedit --continue
54 $ hg histedit --continue
55 $ hg log -G
55 $ hg log -G
56 @ 5:46abc7c4d873 b
56 @ 5:46abc7c4d873 b
57 |
57 |
58 o 4:49d44ab2be1b c
58 o 4:49d44ab2be1b c
59 |
59 |
60 o 0:cb9a9f314b8b a
60 o 0:cb9a9f314b8b a
61
61
62 $ hg debugobsolete
62 $ hg debugobsolete
63 e72d22b19f8ecf4150ab4f91d0973fd9955d3ddf 49d44ab2be1b67a79127568a67c9c99430633b48 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
63 e72d22b19f8ecf4150ab4f91d0973fd9955d3ddf 49d44ab2be1b67a79127568a67c9c99430633b48 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
64 1b2d564fad96311b45362f17c2aa855150efb35f 46abc7c4d8738e8563e577f7889e1b6db3da4199 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '12', 'operation': 'histedit', 'user': 'test'}
64 1b2d564fad96311b45362f17c2aa855150efb35f 46abc7c4d8738e8563e577f7889e1b6db3da4199 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '12', 'operation': 'histedit', 'user': 'test'}
65 114f4176969ef342759a8a57e6bccefc4234829b 49d44ab2be1b67a79127568a67c9c99430633b48 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '12', 'operation': 'histedit', 'user': 'test'}
65 114f4176969ef342759a8a57e6bccefc4234829b 49d44ab2be1b67a79127568a67c9c99430633b48 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '12', 'operation': 'histedit', 'user': 'test'}
66
66
67 With some node gone missing during the edit.
67 With some node gone missing during the edit.
68
68
69 $ echo "pick `hg log -r 0 -T '{node|short}'`" > plan
69 $ echo "pick `hg log -r 0 -T '{node|short}'`" > plan
70 $ echo "pick `hg log -r 5 -T '{node|short}'`" >> plan
70 $ echo "pick `hg log -r 5 -T '{node|short}'`" >> plan
71 $ echo "edit `hg log -r 4 -T '{node|short}'`" >> plan
71 $ echo "edit `hg log -r 4 -T '{node|short}'`" >> plan
72 $ hg histedit -r 'all()' --commands plan
72 $ hg histedit -r 'all()' --commands plan
73 Editing (49d44ab2be1b), you may commit or record as needed now.
73 Editing (49d44ab2be1b), commit as needed now to split the change
74 (hg histedit --continue to resume)
74 (to edit 49d44ab2be1b, `hg histedit --continue` after making changes)
75 [240]
75 [240]
76 $ hg st
76 $ hg st
77 A b
77 A b
78 A d
78 A d
79 ? plan
79 ? plan
80 $ hg commit --amend -X . -m XXXXXX
80 $ hg commit --amend -X . -m XXXXXX
81 $ hg commit --amend -X . -m b2
81 $ hg commit --amend -X . -m b2
82 $ hg --hidden --config extensions.strip= strip 'desc(XXXXXX)' --no-backup
82 $ hg --hidden --config extensions.strip= strip 'desc(XXXXXX)' --no-backup
83 $ hg histedit --continue
83 $ hg histedit --continue
84 $ hg log -G
84 $ hg log -G
85 @ 8:273c1f3b8626 c
85 @ 8:273c1f3b8626 c
86 |
86 |
87 o 7:aba7da937030 b2
87 o 7:aba7da937030 b2
88 |
88 |
89 o 0:cb9a9f314b8b a
89 o 0:cb9a9f314b8b a
90
90
91 $ hg debugobsolete
91 $ hg debugobsolete
92 e72d22b19f8ecf4150ab4f91d0973fd9955d3ddf 49d44ab2be1b67a79127568a67c9c99430633b48 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
92 e72d22b19f8ecf4150ab4f91d0973fd9955d3ddf 49d44ab2be1b67a79127568a67c9c99430633b48 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
93 1b2d564fad96311b45362f17c2aa855150efb35f 46abc7c4d8738e8563e577f7889e1b6db3da4199 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '12', 'operation': 'histedit', 'user': 'test'}
93 1b2d564fad96311b45362f17c2aa855150efb35f 46abc7c4d8738e8563e577f7889e1b6db3da4199 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '12', 'operation': 'histedit', 'user': 'test'}
94 114f4176969ef342759a8a57e6bccefc4234829b 49d44ab2be1b67a79127568a67c9c99430633b48 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '12', 'operation': 'histedit', 'user': 'test'}
94 114f4176969ef342759a8a57e6bccefc4234829b 49d44ab2be1b67a79127568a67c9c99430633b48 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '12', 'operation': 'histedit', 'user': 'test'}
95 76f72745eac0643d16530e56e2f86e36e40631f1 2ca853e48edbd6453a0674dc0fe28a0974c51b9c 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'test'}
95 76f72745eac0643d16530e56e2f86e36e40631f1 2ca853e48edbd6453a0674dc0fe28a0974c51b9c 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'test'}
96 2ca853e48edbd6453a0674dc0fe28a0974c51b9c aba7da93703075eec9fb1dbaf143ff2bc1c49d46 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'test'}
96 2ca853e48edbd6453a0674dc0fe28a0974c51b9c aba7da93703075eec9fb1dbaf143ff2bc1c49d46 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'test'}
97 49d44ab2be1b67a79127568a67c9c99430633b48 273c1f3b86267ed3ec684bb13af1fa4d6ba56e02 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'histedit', 'user': 'test'}
97 49d44ab2be1b67a79127568a67c9c99430633b48 273c1f3b86267ed3ec684bb13af1fa4d6ba56e02 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'histedit', 'user': 'test'}
98 46abc7c4d8738e8563e577f7889e1b6db3da4199 aba7da93703075eec9fb1dbaf143ff2bc1c49d46 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '5', 'operation': 'histedit', 'user': 'test'}
98 46abc7c4d8738e8563e577f7889e1b6db3da4199 aba7da93703075eec9fb1dbaf143ff2bc1c49d46 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '5', 'operation': 'histedit', 'user': 'test'}
99 $ cd ..
99 $ cd ..
100
100
101 Base setup for the rest of the testing
101 Base setup for the rest of the testing
102 ======================================
102 ======================================
103
103
104 $ hg init base
104 $ hg init base
105 $ cd base
105 $ cd base
106
106
107 $ for x in a b c d e f ; do
107 $ for x in a b c d e f ; do
108 > echo $x > $x
108 > echo $x > $x
109 > hg add $x
109 > hg add $x
110 > hg ci -m $x
110 > hg ci -m $x
111 > done
111 > done
112
112
113 $ hg log --graph
113 $ hg log --graph
114 @ 5:652413bf663e f
114 @ 5:652413bf663e f
115 |
115 |
116 o 4:e860deea161a e
116 o 4:e860deea161a e
117 |
117 |
118 o 3:055a42cdd887 d
118 o 3:055a42cdd887 d
119 |
119 |
120 o 2:177f92b77385 c
120 o 2:177f92b77385 c
121 |
121 |
122 o 1:d2ae7f538514 b
122 o 1:d2ae7f538514 b
123 |
123 |
124 o 0:cb9a9f314b8b a
124 o 0:cb9a9f314b8b a
125
125
126
126
127 $ HGEDITOR=cat hg histedit 1
127 $ HGEDITOR=cat hg histedit 1
128 pick d2ae7f538514 1 b
128 pick d2ae7f538514 1 b
129 pick 177f92b77385 2 c
129 pick 177f92b77385 2 c
130 pick 055a42cdd887 3 d
130 pick 055a42cdd887 3 d
131 pick e860deea161a 4 e
131 pick e860deea161a 4 e
132 pick 652413bf663e 5 f
132 pick 652413bf663e 5 f
133
133
134 # Edit history between d2ae7f538514 and 652413bf663e
134 # Edit history between d2ae7f538514 and 652413bf663e
135 #
135 #
136 # Commits are listed from least to most recent
136 # Commits are listed from least to most recent
137 #
137 #
138 # You can reorder changesets by reordering the lines
138 # You can reorder changesets by reordering the lines
139 #
139 #
140 # Commands:
140 # Commands:
141 #
141 #
142 # e, edit = use commit, but stop for amending
142 # e, edit = use commit, but stop for amending
143 # m, mess = edit commit message without changing commit content
143 # m, mess = edit commit message without changing commit content
144 # p, pick = use commit
144 # p, pick = use commit
145 # b, base = checkout changeset and apply further changesets from there
145 # b, base = checkout changeset and apply further changesets from there
146 # d, drop = remove commit from history
146 # d, drop = remove commit from history
147 # f, fold = use commit, but combine it with the one above
147 # f, fold = use commit, but combine it with the one above
148 # r, roll = like fold, but discard this commit's description and date
148 # r, roll = like fold, but discard this commit's description and date
149 #
149 #
150 $ hg histedit 1 --commands - --verbose <<EOF | grep histedit
150 $ hg histedit 1 --commands - --verbose <<EOF | grep histedit
151 > pick 177f92b77385 2 c
151 > pick 177f92b77385 2 c
152 > drop d2ae7f538514 1 b
152 > drop d2ae7f538514 1 b
153 > pick 055a42cdd887 3 d
153 > pick 055a42cdd887 3 d
154 > fold e860deea161a 4 e
154 > fold e860deea161a 4 e
155 > pick 652413bf663e 5 f
155 > pick 652413bf663e 5 f
156 > EOF
156 > EOF
157 [1]
157 [1]
158 $ hg log --graph --hidden
158 $ hg log --graph --hidden
159 @ 10:cacdfd884a93 f
159 @ 10:cacdfd884a93 f
160 |
160 |
161 o 9:59d9f330561f d
161 o 9:59d9f330561f d
162 |
162 |
163 | x 8:b558abc46d09 fold-temp-revision e860deea161a
163 | x 8:b558abc46d09 fold-temp-revision e860deea161a
164 | |
164 | |
165 | x 7:96e494a2d553 d
165 | x 7:96e494a2d553 d
166 |/
166 |/
167 o 6:b346ab9a313d c
167 o 6:b346ab9a313d c
168 |
168 |
169 | x 5:652413bf663e f
169 | x 5:652413bf663e f
170 | |
170 | |
171 | x 4:e860deea161a e
171 | x 4:e860deea161a e
172 | |
172 | |
173 | x 3:055a42cdd887 d
173 | x 3:055a42cdd887 d
174 | |
174 | |
175 | x 2:177f92b77385 c
175 | x 2:177f92b77385 c
176 | |
176 | |
177 | x 1:d2ae7f538514 b
177 | x 1:d2ae7f538514 b
178 |/
178 |/
179 o 0:cb9a9f314b8b a
179 o 0:cb9a9f314b8b a
180
180
181 $ hg debugobsolete
181 $ hg debugobsolete
182 d2ae7f538514cd87c17547b0de4cea71fe1af9fb 0 {cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'histedit', 'user': 'test'}
182 d2ae7f538514cd87c17547b0de4cea71fe1af9fb 0 {cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'histedit', 'user': 'test'}
183 177f92b773850b59254aa5e923436f921b55483b b346ab9a313db8537ecf96fca3ca3ca984ef3bd7 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'histedit', 'user': 'test'}
183 177f92b773850b59254aa5e923436f921b55483b b346ab9a313db8537ecf96fca3ca3ca984ef3bd7 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'histedit', 'user': 'test'}
184 055a42cdd88768532f9cf79daa407fc8d138de9b 59d9f330561fd6c88b1a6b32f0e45034d88db784 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'operation': 'histedit', 'user': 'test'}
184 055a42cdd88768532f9cf79daa407fc8d138de9b 59d9f330561fd6c88b1a6b32f0e45034d88db784 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'operation': 'histedit', 'user': 'test'}
185 e860deea161a2f77de56603b340ebbb4536308ae 59d9f330561fd6c88b1a6b32f0e45034d88db784 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'operation': 'histedit', 'user': 'test'}
185 e860deea161a2f77de56603b340ebbb4536308ae 59d9f330561fd6c88b1a6b32f0e45034d88db784 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'operation': 'histedit', 'user': 'test'}
186 652413bf663ef2a641cab26574e46d5f5a64a55a cacdfd884a9321ec4e1de275ef3949fa953a1f83 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'histedit', 'user': 'test'}
186 652413bf663ef2a641cab26574e46d5f5a64a55a cacdfd884a9321ec4e1de275ef3949fa953a1f83 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'histedit', 'user': 'test'}
187 96e494a2d553dd05902ba1cee1d94d4cb7b8faed 0 {b346ab9a313db8537ecf96fca3ca3ca984ef3bd7} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'histedit', 'user': 'test'}
187 96e494a2d553dd05902ba1cee1d94d4cb7b8faed 0 {b346ab9a313db8537ecf96fca3ca3ca984ef3bd7} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'histedit', 'user': 'test'}
188 b558abc46d09c30f57ac31e85a8a3d64d2e906e4 0 {96e494a2d553dd05902ba1cee1d94d4cb7b8faed} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'histedit', 'user': 'test'}
188 b558abc46d09c30f57ac31e85a8a3d64d2e906e4 0 {96e494a2d553dd05902ba1cee1d94d4cb7b8faed} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'histedit', 'user': 'test'}
189
189
190
190
191 Ensure hidden revision does not prevent histedit
191 Ensure hidden revision does not prevent histedit
192 -------------------------------------------------
192 -------------------------------------------------
193
193
194 create an hidden revision
194 create an hidden revision
195
195
196 $ hg histedit 6 --commands - << EOF
196 $ hg histedit 6 --commands - << EOF
197 > pick b346ab9a313d 6 c
197 > pick b346ab9a313d 6 c
198 > drop 59d9f330561f 7 d
198 > drop 59d9f330561f 7 d
199 > pick cacdfd884a93 8 f
199 > pick cacdfd884a93 8 f
200 > EOF
200 > EOF
201 $ hg log --graph
201 $ hg log --graph
202 @ 11:c13eb81022ca f
202 @ 11:c13eb81022ca f
203 |
203 |
204 o 6:b346ab9a313d c
204 o 6:b346ab9a313d c
205 |
205 |
206 o 0:cb9a9f314b8b a
206 o 0:cb9a9f314b8b a
207
207
208 check hidden revision are ignored (6 have hidden children 7 and 8)
208 check hidden revision are ignored (6 have hidden children 7 and 8)
209
209
210 $ hg histedit 6 --commands - << EOF
210 $ hg histedit 6 --commands - << EOF
211 > pick b346ab9a313d 6 c
211 > pick b346ab9a313d 6 c
212 > pick c13eb81022ca 8 f
212 > pick c13eb81022ca 8 f
213 > EOF
213 > EOF
214
214
215
215
216
216
217 Test that rewriting leaving instability behind is allowed
217 Test that rewriting leaving instability behind is allowed
218 ---------------------------------------------------------------------
218 ---------------------------------------------------------------------
219
219
220 $ hg up '.^'
220 $ hg up '.^'
221 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
221 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
222 $ hg log -r 'children(.)'
222 $ hg log -r 'children(.)'
223 11:c13eb81022ca f (no-eol)
223 11:c13eb81022ca f (no-eol)
224 $ hg histedit -r '.' --commands - <<EOF
224 $ hg histedit -r '.' --commands - <<EOF
225 > edit b346ab9a313d 6 c
225 > edit b346ab9a313d 6 c
226 > EOF
226 > EOF
227 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
227 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
228 Editing (b346ab9a313d), you may commit or record as needed now.
228 Editing (b346ab9a313d), commit as needed now to split the change
229 (hg histedit --continue to resume)
229 (to edit b346ab9a313d, `hg histedit --continue` after making changes)
230 [240]
230 [240]
231 $ echo c >> c
231 $ echo c >> c
232 $ hg histedit --continue
232 $ hg histedit --continue
233 1 new orphan changesets
233 1 new orphan changesets
234
234
235 $ hg log -r 'orphan()'
235 $ hg log -r 'orphan()'
236 11:c13eb81022ca f (no-eol)
236 11:c13eb81022ca f (no-eol)
237
237
238 stabilise
238 stabilise
239
239
240 $ hg rebase -r 'orphan()' -d .
240 $ hg rebase -r 'orphan()' -d .
241 rebasing 11:c13eb81022ca "f"
241 rebasing 11:c13eb81022ca "f"
242 $ hg up tip -q
242 $ hg up tip -q
243
243
244 Test dropping of changeset on the top of the stack
244 Test dropping of changeset on the top of the stack
245 -------------------------------------------------------
245 -------------------------------------------------------
246
246
247 Nothing is rewritten below, the working directory parent must be change for the
247 Nothing is rewritten below, the working directory parent must be change for the
248 dropped changeset to be hidden.
248 dropped changeset to be hidden.
249
249
250 $ cd ..
250 $ cd ..
251 $ hg clone base droplast
251 $ hg clone base droplast
252 updating to branch default
252 updating to branch default
253 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
253 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
254 $ cd droplast
254 $ cd droplast
255 $ hg histedit -r '40db8afa467b' --commands - << EOF
255 $ hg histedit -r '40db8afa467b' --commands - << EOF
256 > pick 40db8afa467b 10 c
256 > pick 40db8afa467b 10 c
257 > drop b449568bf7fc 11 f
257 > drop b449568bf7fc 11 f
258 > EOF
258 > EOF
259 $ hg log -G
259 $ hg log -G
260 @ 12:40db8afa467b c
260 @ 12:40db8afa467b c
261 |
261 |
262 o 0:cb9a9f314b8b a
262 o 0:cb9a9f314b8b a
263
263
264
264
265 With rewritten ancestors
265 With rewritten ancestors
266
266
267 $ echo e > e
267 $ echo e > e
268 $ hg add e
268 $ hg add e
269 $ hg commit -m g
269 $ hg commit -m g
270 $ echo f > f
270 $ echo f > f
271 $ hg add f
271 $ hg add f
272 $ hg commit -m h
272 $ hg commit -m h
273 $ hg histedit -r '40db8afa467b' --commands - << EOF
273 $ hg histedit -r '40db8afa467b' --commands - << EOF
274 > pick 47a8561c0449 12 g
274 > pick 47a8561c0449 12 g
275 > pick 40db8afa467b 10 c
275 > pick 40db8afa467b 10 c
276 > drop 1b3b05f35ff0 13 h
276 > drop 1b3b05f35ff0 13 h
277 > EOF
277 > EOF
278 $ hg log -G
278 $ hg log -G
279 @ 17:ee6544123ab8 c
279 @ 17:ee6544123ab8 c
280 |
280 |
281 o 16:269e713e9eae g
281 o 16:269e713e9eae g
282 |
282 |
283 o 0:cb9a9f314b8b a
283 o 0:cb9a9f314b8b a
284
284
285 $ cd ../base
285 $ cd ../base
286
286
287
287
288
288
289 Test phases support
289 Test phases support
290 ===========================================
290 ===========================================
291
291
292 Check that histedit respect immutability
292 Check that histedit respect immutability
293 -------------------------------------------
293 -------------------------------------------
294
294
295 $ cat >> $HGRCPATH << EOF
295 $ cat >> $HGRCPATH << EOF
296 > [command-templates]
296 > [command-templates]
297 > log = {rev}:{node|short} ({phase}) {desc|firstline}\n
297 > log = {rev}:{node|short} ({phase}) {desc|firstline}\n
298 > EOF
298 > EOF
299
299
300 $ hg ph -pv '.^'
300 $ hg ph -pv '.^'
301 phase changed for 2 changesets
301 phase changed for 2 changesets
302 $ hg log -G
302 $ hg log -G
303 @ 13:b449568bf7fc (draft) f
303 @ 13:b449568bf7fc (draft) f
304 |
304 |
305 o 12:40db8afa467b (public) c
305 o 12:40db8afa467b (public) c
306 |
306 |
307 o 0:cb9a9f314b8b (public) a
307 o 0:cb9a9f314b8b (public) a
308
308
309 $ hg histedit -r '.~2'
309 $ hg histedit -r '.~2'
310 abort: cannot edit public changesets
310 abort: cannot edit public changesets
311 (see 'hg help phases' for details)
311 (see 'hg help phases' for details)
312 [10]
312 [10]
313
313
314
314
315 Prepare further testing
315 Prepare further testing
316 -------------------------------------------
316 -------------------------------------------
317
317
318 $ for x in g h i j k ; do
318 $ for x in g h i j k ; do
319 > echo $x > $x
319 > echo $x > $x
320 > hg add $x
320 > hg add $x
321 > hg ci -m $x
321 > hg ci -m $x
322 > done
322 > done
323 $ hg phase --force --secret .~2
323 $ hg phase --force --secret .~2
324 $ hg log -G
324 $ hg log -G
325 @ 18:ee118ab9fa44 (secret) k
325 @ 18:ee118ab9fa44 (secret) k
326 |
326 |
327 o 17:3a6c53ee7f3d (secret) j
327 o 17:3a6c53ee7f3d (secret) j
328 |
328 |
329 o 16:b605fb7503f2 (secret) i
329 o 16:b605fb7503f2 (secret) i
330 |
330 |
331 o 15:7395e1ff83bd (draft) h
331 o 15:7395e1ff83bd (draft) h
332 |
332 |
333 o 14:6b70183d2492 (draft) g
333 o 14:6b70183d2492 (draft) g
334 |
334 |
335 o 13:b449568bf7fc (draft) f
335 o 13:b449568bf7fc (draft) f
336 |
336 |
337 o 12:40db8afa467b (public) c
337 o 12:40db8afa467b (public) c
338 |
338 |
339 o 0:cb9a9f314b8b (public) a
339 o 0:cb9a9f314b8b (public) a
340
340
341 $ cd ..
341 $ cd ..
342
342
343 simple phase conservation
343 simple phase conservation
344 -------------------------------------------
344 -------------------------------------------
345
345
346 Resulting changeset should conserve the phase of the original one whatever the
346 Resulting changeset should conserve the phase of the original one whatever the
347 phases.new-commit option is.
347 phases.new-commit option is.
348
348
349 New-commit as draft (default)
349 New-commit as draft (default)
350
350
351 $ cp -R base simple-draft
351 $ cp -R base simple-draft
352 $ cd simple-draft
352 $ cd simple-draft
353 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
353 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
354 > edit b449568bf7fc 11 f
354 > edit b449568bf7fc 11 f
355 > pick 6b70183d2492 12 g
355 > pick 6b70183d2492 12 g
356 > pick 7395e1ff83bd 13 h
356 > pick 7395e1ff83bd 13 h
357 > pick b605fb7503f2 14 i
357 > pick b605fb7503f2 14 i
358 > pick 3a6c53ee7f3d 15 j
358 > pick 3a6c53ee7f3d 15 j
359 > pick ee118ab9fa44 16 k
359 > pick ee118ab9fa44 16 k
360 > EOF
360 > EOF
361 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
361 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
362 Editing (b449568bf7fc), you may commit or record as needed now.
362 Editing (b449568bf7fc), commit as needed now to split the change
363 (hg histedit --continue to resume)
363 (to edit b449568bf7fc, `hg histedit --continue` after making changes)
364 [240]
364 [240]
365 $ echo f >> f
365 $ echo f >> f
366 $ hg histedit --continue
366 $ hg histedit --continue
367 $ hg log -G
367 $ hg log -G
368 @ 24:12e89af74238 (secret) k
368 @ 24:12e89af74238 (secret) k
369 |
369 |
370 o 23:636a8687b22e (secret) j
370 o 23:636a8687b22e (secret) j
371 |
371 |
372 o 22:ccaf0a38653f (secret) i
372 o 22:ccaf0a38653f (secret) i
373 |
373 |
374 o 21:11a89d1c2613 (draft) h
374 o 21:11a89d1c2613 (draft) h
375 |
375 |
376 o 20:c1dec7ca82ea (draft) g
376 o 20:c1dec7ca82ea (draft) g
377 |
377 |
378 o 19:087281e68428 (draft) f
378 o 19:087281e68428 (draft) f
379 |
379 |
380 o 12:40db8afa467b (public) c
380 o 12:40db8afa467b (public) c
381 |
381 |
382 o 0:cb9a9f314b8b (public) a
382 o 0:cb9a9f314b8b (public) a
383
383
384 $ cd ..
384 $ cd ..
385
385
386
386
387 New-commit as secret (config)
387 New-commit as secret (config)
388
388
389 $ cp -R base simple-secret
389 $ cp -R base simple-secret
390 $ cd simple-secret
390 $ cd simple-secret
391 $ cat >> .hg/hgrc << EOF
391 $ cat >> .hg/hgrc << EOF
392 > [phases]
392 > [phases]
393 > new-commit=secret
393 > new-commit=secret
394 > EOF
394 > EOF
395 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
395 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
396 > edit b449568bf7fc 11 f
396 > edit b449568bf7fc 11 f
397 > pick 6b70183d2492 12 g
397 > pick 6b70183d2492 12 g
398 > pick 7395e1ff83bd 13 h
398 > pick 7395e1ff83bd 13 h
399 > pick b605fb7503f2 14 i
399 > pick b605fb7503f2 14 i
400 > pick 3a6c53ee7f3d 15 j
400 > pick 3a6c53ee7f3d 15 j
401 > pick ee118ab9fa44 16 k
401 > pick ee118ab9fa44 16 k
402 > EOF
402 > EOF
403 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
403 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
404 Editing (b449568bf7fc), you may commit or record as needed now.
404 Editing (b449568bf7fc), commit as needed now to split the change
405 (hg histedit --continue to resume)
405 (to edit b449568bf7fc, `hg histedit --continue` after making changes)
406 [240]
406 [240]
407 $ echo f >> f
407 $ echo f >> f
408 $ hg histedit --continue
408 $ hg histedit --continue
409 $ hg log -G
409 $ hg log -G
410 @ 24:12e89af74238 (secret) k
410 @ 24:12e89af74238 (secret) k
411 |
411 |
412 o 23:636a8687b22e (secret) j
412 o 23:636a8687b22e (secret) j
413 |
413 |
414 o 22:ccaf0a38653f (secret) i
414 o 22:ccaf0a38653f (secret) i
415 |
415 |
416 o 21:11a89d1c2613 (draft) h
416 o 21:11a89d1c2613 (draft) h
417 |
417 |
418 o 20:c1dec7ca82ea (draft) g
418 o 20:c1dec7ca82ea (draft) g
419 |
419 |
420 o 19:087281e68428 (draft) f
420 o 19:087281e68428 (draft) f
421 |
421 |
422 o 12:40db8afa467b (public) c
422 o 12:40db8afa467b (public) c
423 |
423 |
424 o 0:cb9a9f314b8b (public) a
424 o 0:cb9a9f314b8b (public) a
425
425
426 $ cd ..
426 $ cd ..
427
427
428
428
429 Changeset reordering
429 Changeset reordering
430 -------------------------------------------
430 -------------------------------------------
431
431
432 If a secret changeset is put before a draft one, all descendant should be secret.
432 If a secret changeset is put before a draft one, all descendant should be secret.
433 It seems more important to present the secret phase.
433 It seems more important to present the secret phase.
434
434
435 $ cp -R base reorder
435 $ cp -R base reorder
436 $ cd reorder
436 $ cd reorder
437 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
437 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
438 > pick b449568bf7fc 11 f
438 > pick b449568bf7fc 11 f
439 > pick 3a6c53ee7f3d 15 j
439 > pick 3a6c53ee7f3d 15 j
440 > pick 6b70183d2492 12 g
440 > pick 6b70183d2492 12 g
441 > pick b605fb7503f2 14 i
441 > pick b605fb7503f2 14 i
442 > pick 7395e1ff83bd 13 h
442 > pick 7395e1ff83bd 13 h
443 > pick ee118ab9fa44 16 k
443 > pick ee118ab9fa44 16 k
444 > EOF
444 > EOF
445 $ hg log -G
445 $ hg log -G
446 @ 23:558246857888 (secret) k
446 @ 23:558246857888 (secret) k
447 |
447 |
448 o 22:28bd44768535 (secret) h
448 o 22:28bd44768535 (secret) h
449 |
449 |
450 o 21:d5395202aeb9 (secret) i
450 o 21:d5395202aeb9 (secret) i
451 |
451 |
452 o 20:21edda8e341b (secret) g
452 o 20:21edda8e341b (secret) g
453 |
453 |
454 o 19:5ab64f3a4832 (secret) j
454 o 19:5ab64f3a4832 (secret) j
455 |
455 |
456 o 13:b449568bf7fc (draft) f
456 o 13:b449568bf7fc (draft) f
457 |
457 |
458 o 12:40db8afa467b (public) c
458 o 12:40db8afa467b (public) c
459 |
459 |
460 o 0:cb9a9f314b8b (public) a
460 o 0:cb9a9f314b8b (public) a
461
461
462 $ cd ..
462 $ cd ..
463
463
464 Changeset folding
464 Changeset folding
465 -------------------------------------------
465 -------------------------------------------
466
466
467 Folding a secret changeset with a draft one turn the result secret (again,
467 Folding a secret changeset with a draft one turn the result secret (again,
468 better safe than sorry). Folding between same phase changeset still works
468 better safe than sorry). Folding between same phase changeset still works
469
469
470 Note that there is a few reordering in this series for more extensive test
470 Note that there is a few reordering in this series for more extensive test
471
471
472 $ cp -R base folding
472 $ cp -R base folding
473 $ cd folding
473 $ cd folding
474 $ cat >> .hg/hgrc << EOF
474 $ cat >> .hg/hgrc << EOF
475 > [phases]
475 > [phases]
476 > new-commit=secret
476 > new-commit=secret
477 > EOF
477 > EOF
478 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
478 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
479 > pick 7395e1ff83bd 13 h
479 > pick 7395e1ff83bd 13 h
480 > fold b449568bf7fc 11 f
480 > fold b449568bf7fc 11 f
481 > pick 6b70183d2492 12 g
481 > pick 6b70183d2492 12 g
482 > fold 3a6c53ee7f3d 15 j
482 > fold 3a6c53ee7f3d 15 j
483 > pick b605fb7503f2 14 i
483 > pick b605fb7503f2 14 i
484 > fold ee118ab9fa44 16 k
484 > fold ee118ab9fa44 16 k
485 > EOF
485 > EOF
486 $ hg log -G
486 $ hg log -G
487 @ 27:f9daec13fb98 (secret) i
487 @ 27:f9daec13fb98 (secret) i
488 |
488 |
489 o 24:49807617f46a (secret) g
489 o 24:49807617f46a (secret) g
490 |
490 |
491 o 21:050280826e04 (draft) h
491 o 21:050280826e04 (draft) h
492 |
492 |
493 o 12:40db8afa467b (public) c
493 o 12:40db8afa467b (public) c
494 |
494 |
495 o 0:cb9a9f314b8b (public) a
495 o 0:cb9a9f314b8b (public) a
496
496
497 $ hg co 49807617f46a
497 $ hg co 49807617f46a
498 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
498 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
499 $ echo wat >> wat
499 $ echo wat >> wat
500 $ hg add wat
500 $ hg add wat
501 $ hg ci -m 'add wat'
501 $ hg ci -m 'add wat'
502 created new head
502 created new head
503 $ hg merge f9daec13fb98
503 $ hg merge f9daec13fb98
504 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
504 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
505 (branch merge, don't forget to commit)
505 (branch merge, don't forget to commit)
506 $ hg ci -m 'merge'
506 $ hg ci -m 'merge'
507 $ echo not wat > wat
507 $ echo not wat > wat
508 $ hg ci -m 'modify wat'
508 $ hg ci -m 'modify wat'
509 $ hg histedit 050280826e04
509 $ hg histedit 050280826e04
510 abort: cannot edit history that contains merges
510 abort: cannot edit history that contains merges
511 [255]
511 [255]
512 $ cd ..
512 $ cd ..
513
513
514 Check abort behavior
514 Check abort behavior
515 -------------------------------------------
515 -------------------------------------------
516
516
517 We checks that abort properly clean the repository so the same histedit can be
517 We checks that abort properly clean the repository so the same histedit can be
518 attempted later.
518 attempted later.
519
519
520 $ cp -R base abort
520 $ cp -R base abort
521 $ cd abort
521 $ cd abort
522 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
522 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
523 > pick b449568bf7fc 13 f
523 > pick b449568bf7fc 13 f
524 > pick 7395e1ff83bd 15 h
524 > pick 7395e1ff83bd 15 h
525 > pick 6b70183d2492 14 g
525 > pick 6b70183d2492 14 g
526 > pick b605fb7503f2 16 i
526 > pick b605fb7503f2 16 i
527 > roll 3a6c53ee7f3d 17 j
527 > roll 3a6c53ee7f3d 17 j
528 > edit ee118ab9fa44 18 k
528 > edit ee118ab9fa44 18 k
529 > EOF
529 > EOF
530 Editing (ee118ab9fa44), you may commit or record as needed now.
530 Editing (ee118ab9fa44), commit as needed now to split the change
531 (hg histedit --continue to resume)
531 (to edit ee118ab9fa44, `hg histedit --continue` after making changes)
532 [240]
532 [240]
533
533
534 #if abortcommand
534 #if abortcommand
535 when in dry-run mode
535 when in dry-run mode
536 $ hg abort --dry-run
536 $ hg abort --dry-run
537 histedit in progress, will be aborted
537 histedit in progress, will be aborted
538 #endif
538 #endif
539
539
540 $ hg abort
540 $ hg abort
541 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
541 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
542 saved backup bundle to $TESTTMP/abort/.hg/strip-backup/4dc06258baa6-dff4ef05-backup.hg
542 saved backup bundle to $TESTTMP/abort/.hg/strip-backup/4dc06258baa6-dff4ef05-backup.hg
543
543
544 $ hg log -G
544 $ hg log -G
545 @ 18:ee118ab9fa44 (secret) k
545 @ 18:ee118ab9fa44 (secret) k
546 |
546 |
547 o 17:3a6c53ee7f3d (secret) j
547 o 17:3a6c53ee7f3d (secret) j
548 |
548 |
549 o 16:b605fb7503f2 (secret) i
549 o 16:b605fb7503f2 (secret) i
550 |
550 |
551 o 15:7395e1ff83bd (draft) h
551 o 15:7395e1ff83bd (draft) h
552 |
552 |
553 o 14:6b70183d2492 (draft) g
553 o 14:6b70183d2492 (draft) g
554 |
554 |
555 o 13:b449568bf7fc (draft) f
555 o 13:b449568bf7fc (draft) f
556 |
556 |
557 o 12:40db8afa467b (public) c
557 o 12:40db8afa467b (public) c
558 |
558 |
559 o 0:cb9a9f314b8b (public) a
559 o 0:cb9a9f314b8b (public) a
560
560
561 $ hg histedit -r 'b449568bf7fc' --commands - << EOF --config experimental.evolution.track-operation=1
561 $ hg histedit -r 'b449568bf7fc' --commands - << EOF --config experimental.evolution.track-operation=1
562 > pick b449568bf7fc 13 f
562 > pick b449568bf7fc 13 f
563 > pick 7395e1ff83bd 15 h
563 > pick 7395e1ff83bd 15 h
564 > pick 6b70183d2492 14 g
564 > pick 6b70183d2492 14 g
565 > pick b605fb7503f2 16 i
565 > pick b605fb7503f2 16 i
566 > pick 3a6c53ee7f3d 17 j
566 > pick 3a6c53ee7f3d 17 j
567 > edit ee118ab9fa44 18 k
567 > edit ee118ab9fa44 18 k
568 > EOF
568 > EOF
569 Editing (ee118ab9fa44), you may commit or record as needed now.
569 Editing (ee118ab9fa44), commit as needed now to split the change
570 (hg histedit --continue to resume)
570 (to edit ee118ab9fa44, `hg histedit --continue` after making changes)
571 [240]
571 [240]
572 $ hg histedit --continue --config experimental.evolution.track-operation=1
572 $ hg histedit --continue --config experimental.evolution.track-operation=1
573 $ hg log -G
573 $ hg log -G
574 @ 23:175d6b286a22 (secret) k
574 @ 23:175d6b286a22 (secret) k
575 |
575 |
576 o 22:44ca09d59ae4 (secret) j
576 o 22:44ca09d59ae4 (secret) j
577 |
577 |
578 o 21:31747692a644 (secret) i
578 o 21:31747692a644 (secret) i
579 |
579 |
580 o 20:9985cd4f21fa (draft) g
580 o 20:9985cd4f21fa (draft) g
581 |
581 |
582 o 19:4dc06258baa6 (draft) h
582 o 19:4dc06258baa6 (draft) h
583 |
583 |
584 o 13:b449568bf7fc (draft) f
584 o 13:b449568bf7fc (draft) f
585 |
585 |
586 o 12:40db8afa467b (public) c
586 o 12:40db8afa467b (public) c
587 |
587 |
588 o 0:cb9a9f314b8b (public) a
588 o 0:cb9a9f314b8b (public) a
589
589
590 $ hg debugobsolete --rev .
590 $ hg debugobsolete --rev .
591 ee118ab9fa44ebb86be85996548b5517a39e5093 175d6b286a224c23f192e79a581ce83131a53fa2 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'histedit', 'user': 'test'}
591 ee118ab9fa44ebb86be85996548b5517a39e5093 175d6b286a224c23f192e79a581ce83131a53fa2 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'histedit', 'user': 'test'}
@@ -1,473 +1,473 b''
1 Create configuration
1 Create configuration
2
2
3 $ echo "[ui]" >> $HGRCPATH
3 $ echo "[ui]" >> $HGRCPATH
4 $ echo "interactive=true" >> $HGRCPATH
4 $ echo "interactive=true" >> $HGRCPATH
5
5
6 help record (no record)
6 help record (no record)
7
7
8 $ hg help record
8 $ hg help record
9 record extension - commands to interactively select changes for
9 record extension - commands to interactively select changes for
10 commit/qrefresh (DEPRECATED)
10 commit/qrefresh (DEPRECATED)
11
11
12 The feature provided by this extension has been moved into core Mercurial as
12 The feature provided by this extension has been moved into core Mercurial as
13 'hg commit --interactive'.
13 'hg commit --interactive'.
14
14
15 (use 'hg help extensions' for information on enabling extensions)
15 (use 'hg help extensions' for information on enabling extensions)
16
16
17 help qrecord (no record)
17 help qrecord (no record)
18
18
19 $ hg help qrecord
19 $ hg help qrecord
20 'qrecord' is provided by the following extension:
20 'qrecord' is provided by the following extension:
21
21
22 record commands to interactively select changes for commit/qrefresh
22 record commands to interactively select changes for commit/qrefresh
23 (DEPRECATED)
23 (DEPRECATED)
24
24
25 (use 'hg help extensions' for information on enabling extensions)
25 (use 'hg help extensions' for information on enabling extensions)
26
26
27 $ echo "[extensions]" >> $HGRCPATH
27 $ echo "[extensions]" >> $HGRCPATH
28 $ echo "record=" >> $HGRCPATH
28 $ echo "record=" >> $HGRCPATH
29
29
30 help record (record)
30 help record (record)
31
31
32 $ hg help record
32 $ hg help record
33 hg record [OPTION]... [FILE]...
33 hg record [OPTION]... [FILE]...
34
34
35 interactively select changes to commit
35 interactively select changes to commit
36
36
37 If a list of files is omitted, all changes reported by 'hg status' will be
37 If a list of files is omitted, all changes reported by 'hg status' will be
38 candidates for recording.
38 candidates for recording.
39
39
40 See 'hg help dates' for a list of formats valid for -d/--date.
40 See 'hg help dates' for a list of formats valid for -d/--date.
41
41
42 If using the text interface (see 'hg help config'), you will be prompted
42 If using the text interface (see 'hg help config'), you will be prompted
43 for whether to record changes to each modified file, and for files with
43 for whether to record changes to each modified file, and for files with
44 multiple changes, for each change to use. For each query, the following
44 multiple changes, for each change to use. For each query, the following
45 responses are possible:
45 responses are possible:
46
46
47 y - record this change
47 y - record this change
48 n - skip this change
48 n - skip this change
49 e - edit this change manually
49 e - edit this change manually
50
50
51 s - skip remaining changes to this file
51 s - skip remaining changes to this file
52 f - record remaining changes to this file
52 f - record remaining changes to this file
53
53
54 d - done, skip remaining changes and files
54 d - done, skip remaining changes and files
55 a - record all changes to all remaining files
55 a - record all changes to all remaining files
56 q - quit, recording no changes
56 q - quit, recording no changes
57
57
58 ? - display help
58 ? - display help
59
59
60 This command is not available when committing a merge.
60 This command is not available when committing a merge.
61
61
62 (use 'hg help -e record' to show help for the record extension)
62 (use 'hg help -e record' to show help for the record extension)
63
63
64 options ([+] can be repeated):
64 options ([+] can be repeated):
65
65
66 -A --addremove mark new/missing files as added/removed before
66 -A --addremove mark new/missing files as added/removed before
67 committing
67 committing
68 --close-branch mark a branch head as closed
68 --close-branch mark a branch head as closed
69 --amend amend the parent of the working directory
69 --amend amend the parent of the working directory
70 -s --secret use the secret phase for committing
70 -s --secret use the secret phase for committing
71 -e --edit invoke editor on commit messages
71 -e --edit invoke editor on commit messages
72 -I --include PATTERN [+] include names matching the given patterns
72 -I --include PATTERN [+] include names matching the given patterns
73 -X --exclude PATTERN [+] exclude names matching the given patterns
73 -X --exclude PATTERN [+] exclude names matching the given patterns
74 -m --message TEXT use text as commit message
74 -m --message TEXT use text as commit message
75 -l --logfile FILE read commit message from file
75 -l --logfile FILE read commit message from file
76 -d --date DATE record the specified date as commit date
76 -d --date DATE record the specified date as commit date
77 -u --user USER record the specified user as committer
77 -u --user USER record the specified user as committer
78 -S --subrepos recurse into subrepositories
78 -S --subrepos recurse into subrepositories
79 -w --ignore-all-space ignore white space when comparing lines
79 -w --ignore-all-space ignore white space when comparing lines
80 -b --ignore-space-change ignore changes in the amount of white space
80 -b --ignore-space-change ignore changes in the amount of white space
81 -B --ignore-blank-lines ignore changes whose lines are all blank
81 -B --ignore-blank-lines ignore changes whose lines are all blank
82 -Z --ignore-space-at-eol ignore changes in whitespace at EOL
82 -Z --ignore-space-at-eol ignore changes in whitespace at EOL
83
83
84 (some details hidden, use --verbose to show complete help)
84 (some details hidden, use --verbose to show complete help)
85
85
86 help (no mq, so no qrecord)
86 help (no mq, so no qrecord)
87
87
88 $ hg help qrecord
88 $ hg help qrecord
89 hg qrecord [OPTION]... PATCH [FILE]...
89 hg qrecord [OPTION]... PATCH [FILE]...
90
90
91 interactively record a new patch
91 interactively record a new patch
92
92
93 See 'hg help qnew' & 'hg help record' for more information and usage.
93 See 'hg help qnew' & 'hg help record' for more information and usage.
94
94
95 (some details hidden, use --verbose to show complete help)
95 (some details hidden, use --verbose to show complete help)
96
96
97 $ hg init a
97 $ hg init a
98
98
99 qrecord (mq not present)
99 qrecord (mq not present)
100
100
101 $ hg -R a qrecord
101 $ hg -R a qrecord
102 hg qrecord: invalid arguments
102 hg qrecord: invalid arguments
103 hg qrecord [OPTION]... PATCH [FILE]...
103 hg qrecord [OPTION]... PATCH [FILE]...
104
104
105 interactively record a new patch
105 interactively record a new patch
106
106
107 (use 'hg qrecord -h' to show more help)
107 (use 'hg qrecord -h' to show more help)
108 [255]
108 [255]
109
109
110 qrecord patch (mq not present)
110 qrecord patch (mq not present)
111
111
112 $ hg -R a qrecord patch
112 $ hg -R a qrecord patch
113 abort: 'mq' extension not loaded
113 abort: 'mq' extension not loaded
114 [255]
114 [255]
115
115
116 help (bad mq)
116 help (bad mq)
117
117
118 $ echo "mq=nonexistent" >> $HGRCPATH
118 $ echo "mq=nonexistent" >> $HGRCPATH
119 $ hg help qrecord
119 $ hg help qrecord
120 *** failed to import extension mq from nonexistent: [Errno *] * (glob)
120 *** failed to import extension mq from nonexistent: [Errno *] * (glob)
121 hg qrecord [OPTION]... PATCH [FILE]...
121 hg qrecord [OPTION]... PATCH [FILE]...
122
122
123 interactively record a new patch
123 interactively record a new patch
124
124
125 See 'hg help qnew' & 'hg help record' for more information and usage.
125 See 'hg help qnew' & 'hg help record' for more information and usage.
126
126
127 (some details hidden, use --verbose to show complete help)
127 (some details hidden, use --verbose to show complete help)
128
128
129 help (mq present)
129 help (mq present)
130
130
131 $ sed 's/mq=nonexistent/mq=/' $HGRCPATH > hgrc.tmp
131 $ sed 's/mq=nonexistent/mq=/' $HGRCPATH > hgrc.tmp
132 $ mv hgrc.tmp $HGRCPATH
132 $ mv hgrc.tmp $HGRCPATH
133
133
134 $ hg help qrecord
134 $ hg help qrecord
135 hg qrecord [OPTION]... PATCH [FILE]...
135 hg qrecord [OPTION]... PATCH [FILE]...
136
136
137 interactively record a new patch
137 interactively record a new patch
138
138
139 See 'hg help qnew' & 'hg help record' for more information and usage.
139 See 'hg help qnew' & 'hg help record' for more information and usage.
140
140
141 options ([+] can be repeated):
141 options ([+] can be repeated):
142
142
143 -e --edit invoke editor on commit messages
143 -e --edit invoke editor on commit messages
144 -g --git use git extended diff format
144 -g --git use git extended diff format
145 -U --currentuser add "From: <current user>" to patch
145 -U --currentuser add "From: <current user>" to patch
146 -u --user USER add "From: <USER>" to patch
146 -u --user USER add "From: <USER>" to patch
147 -D --currentdate add "Date: <current date>" to patch
147 -D --currentdate add "Date: <current date>" to patch
148 -d --date DATE add "Date: <DATE>" to patch
148 -d --date DATE add "Date: <DATE>" to patch
149 -I --include PATTERN [+] include names matching the given patterns
149 -I --include PATTERN [+] include names matching the given patterns
150 -X --exclude PATTERN [+] exclude names matching the given patterns
150 -X --exclude PATTERN [+] exclude names matching the given patterns
151 -m --message TEXT use text as commit message
151 -m --message TEXT use text as commit message
152 -l --logfile FILE read commit message from file
152 -l --logfile FILE read commit message from file
153 -w --ignore-all-space ignore white space when comparing lines
153 -w --ignore-all-space ignore white space when comparing lines
154 -b --ignore-space-change ignore changes in the amount of white space
154 -b --ignore-space-change ignore changes in the amount of white space
155 -B --ignore-blank-lines ignore changes whose lines are all blank
155 -B --ignore-blank-lines ignore changes whose lines are all blank
156 -Z --ignore-space-at-eol ignore changes in whitespace at EOL
156 -Z --ignore-space-at-eol ignore changes in whitespace at EOL
157 --mq operate on patch repository
157 --mq operate on patch repository
158
158
159 (some details hidden, use --verbose to show complete help)
159 (some details hidden, use --verbose to show complete help)
160
160
161 $ cd a
161 $ cd a
162
162
163 Base commit
163 Base commit
164
164
165 $ cat > 1.txt <<EOF
165 $ cat > 1.txt <<EOF
166 > 1
166 > 1
167 > 2
167 > 2
168 > 3
168 > 3
169 > 4
169 > 4
170 > 5
170 > 5
171 > EOF
171 > EOF
172 $ cat > 2.txt <<EOF
172 $ cat > 2.txt <<EOF
173 > a
173 > a
174 > b
174 > b
175 > c
175 > c
176 > d
176 > d
177 > e
177 > e
178 > f
178 > f
179 > EOF
179 > EOF
180
180
181 $ mkdir dir
181 $ mkdir dir
182 $ cat > dir/a.txt <<EOF
182 $ cat > dir/a.txt <<EOF
183 > hello world
183 > hello world
184 >
184 >
185 > someone
185 > someone
186 > up
186 > up
187 > there
187 > there
188 > loves
188 > loves
189 > me
189 > me
190 > EOF
190 > EOF
191
191
192 $ hg add 1.txt 2.txt dir/a.txt
192 $ hg add 1.txt 2.txt dir/a.txt
193 $ hg commit -m 'initial checkin'
193 $ hg commit -m 'initial checkin'
194
194
195 Changing files
195 Changing files
196
196
197 $ sed -e 's/2/2 2/;s/4/4 4/' 1.txt > 1.txt.new
197 $ sed -e 's/2/2 2/;s/4/4 4/' 1.txt > 1.txt.new
198 $ sed -e 's/b/b b/' 2.txt > 2.txt.new
198 $ sed -e 's/b/b b/' 2.txt > 2.txt.new
199 $ sed -e 's/hello world/hello world!/' dir/a.txt > dir/a.txt.new
199 $ sed -e 's/hello world/hello world!/' dir/a.txt > dir/a.txt.new
200
200
201 $ mv -f 1.txt.new 1.txt
201 $ mv -f 1.txt.new 1.txt
202 $ mv -f 2.txt.new 2.txt
202 $ mv -f 2.txt.new 2.txt
203 $ mv -f dir/a.txt.new dir/a.txt
203 $ mv -f dir/a.txt.new dir/a.txt
204
204
205 Whole diff
205 Whole diff
206
206
207 $ hg diff --nodates
207 $ hg diff --nodates
208 diff -r 1057167b20ef 1.txt
208 diff -r 1057167b20ef 1.txt
209 --- a/1.txt
209 --- a/1.txt
210 +++ b/1.txt
210 +++ b/1.txt
211 @@ -1,5 +1,5 @@
211 @@ -1,5 +1,5 @@
212 1
212 1
213 -2
213 -2
214 +2 2
214 +2 2
215 3
215 3
216 -4
216 -4
217 +4 4
217 +4 4
218 5
218 5
219 diff -r 1057167b20ef 2.txt
219 diff -r 1057167b20ef 2.txt
220 --- a/2.txt
220 --- a/2.txt
221 +++ b/2.txt
221 +++ b/2.txt
222 @@ -1,5 +1,5 @@
222 @@ -1,5 +1,5 @@
223 a
223 a
224 -b
224 -b
225 +b b
225 +b b
226 c
226 c
227 d
227 d
228 e
228 e
229 diff -r 1057167b20ef dir/a.txt
229 diff -r 1057167b20ef dir/a.txt
230 --- a/dir/a.txt
230 --- a/dir/a.txt
231 +++ b/dir/a.txt
231 +++ b/dir/a.txt
232 @@ -1,4 +1,4 @@
232 @@ -1,4 +1,4 @@
233 -hello world
233 -hello world
234 +hello world!
234 +hello world!
235
235
236 someone
236 someone
237 up
237 up
238
238
239 qrecord with bad patch name, should abort before prompting
239 qrecord with bad patch name, should abort before prompting
240
240
241 $ hg qrecord .hg
241 $ hg qrecord .hg
242 abort: patch name cannot begin with ".hg"
242 abort: patch name cannot begin with ".hg"
243 [255]
243 [255]
244 $ hg qrecord ' foo'
244 $ hg qrecord ' foo'
245 abort: patch name cannot begin or end with whitespace
245 abort: patch name cannot begin or end with whitespace
246 [255]
246 [255]
247 $ hg qrecord 'foo '
247 $ hg qrecord 'foo '
248 abort: patch name cannot begin or end with whitespace
248 abort: patch name cannot begin or end with whitespace
249 [255]
249 [255]
250
250
251 qrecord a.patch
251 qrecord a.patch
252
252
253 $ hg qrecord -d '0 0' -m aaa a.patch <<EOF
253 $ hg qrecord -d '0 0' -m aaa a.patch <<EOF
254 > y
254 > y
255 > y
255 > y
256 > n
256 > n
257 > y
257 > y
258 > y
258 > y
259 > n
259 > n
260 > EOF
260 > EOF
261 diff --git a/1.txt b/1.txt
261 diff --git a/1.txt b/1.txt
262 2 hunks, 2 lines changed
262 2 hunks, 2 lines changed
263 examine changes to '1.txt'?
263 examine changes to '1.txt'?
264 (enter ? for help) [Ynesfdaq?] y
264 (enter ? for help) [Ynesfdaq?] y
265
265
266 @@ -1,3 +1,3 @@
266 @@ -1,3 +1,3 @@
267 1
267 1
268 -2
268 -2
269 +2 2
269 +2 2
270 3
270 3
271 record change 1/4 to '1.txt'?
271 record change 1/4 to '1.txt'?
272 (enter ? for help) [Ynesfdaq?] y
272 (enter ? for help) [Ynesfdaq?] y
273
273
274 @@ -3,3 +3,3 @@
274 @@ -3,3 +3,3 @@
275 3
275 3
276 -4
276 -4
277 +4 4
277 +4 4
278 5
278 5
279 record change 2/4 to '1.txt'?
279 record change 2/4 to '1.txt'?
280 (enter ? for help) [Ynesfdaq?] n
280 (enter ? for help) [Ynesfdaq?] n
281
281
282 diff --git a/2.txt b/2.txt
282 diff --git a/2.txt b/2.txt
283 1 hunks, 1 lines changed
283 1 hunks, 1 lines changed
284 examine changes to '2.txt'?
284 examine changes to '2.txt'?
285 (enter ? for help) [Ynesfdaq?] y
285 (enter ? for help) [Ynesfdaq?] y
286
286
287 @@ -1,5 +1,5 @@
287 @@ -1,5 +1,5 @@
288 a
288 a
289 -b
289 -b
290 +b b
290 +b b
291 c
291 c
292 d
292 d
293 e
293 e
294 record change 3/4 to '2.txt'?
294 record change 3/4 to '2.txt'?
295 (enter ? for help) [Ynesfdaq?] y
295 (enter ? for help) [Ynesfdaq?] y
296
296
297 diff --git a/dir/a.txt b/dir/a.txt
297 diff --git a/dir/a.txt b/dir/a.txt
298 1 hunks, 1 lines changed
298 1 hunks, 1 lines changed
299 examine changes to 'dir/a.txt'?
299 examine changes to 'dir/a.txt'?
300 (enter ? for help) [Ynesfdaq?] n
300 (enter ? for help) [Ynesfdaq?] n
301
301
302
302
303 After qrecord a.patch 'tip'"
303 After qrecord a.patch 'tip'"
304
304
305 $ hg tip -p
305 $ hg tip -p
306 changeset: 1:5d1ca63427ee
306 changeset: 1:5d1ca63427ee
307 tag: a.patch
307 tag: a.patch
308 tag: qbase
308 tag: qbase
309 tag: qtip
309 tag: qtip
310 tag: tip
310 tag: tip
311 user: test
311 user: test
312 date: Thu Jan 01 00:00:00 1970 +0000
312 date: Thu Jan 01 00:00:00 1970 +0000
313 summary: aaa
313 summary: aaa
314
314
315 diff -r 1057167b20ef -r 5d1ca63427ee 1.txt
315 diff -r 1057167b20ef -r 5d1ca63427ee 1.txt
316 --- a/1.txt Thu Jan 01 00:00:00 1970 +0000
316 --- a/1.txt Thu Jan 01 00:00:00 1970 +0000
317 +++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
317 +++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
318 @@ -1,5 +1,5 @@
318 @@ -1,5 +1,5 @@
319 1
319 1
320 -2
320 -2
321 +2 2
321 +2 2
322 3
322 3
323 4
323 4
324 5
324 5
325 diff -r 1057167b20ef -r 5d1ca63427ee 2.txt
325 diff -r 1057167b20ef -r 5d1ca63427ee 2.txt
326 --- a/2.txt Thu Jan 01 00:00:00 1970 +0000
326 --- a/2.txt Thu Jan 01 00:00:00 1970 +0000
327 +++ b/2.txt Thu Jan 01 00:00:00 1970 +0000
327 +++ b/2.txt Thu Jan 01 00:00:00 1970 +0000
328 @@ -1,5 +1,5 @@
328 @@ -1,5 +1,5 @@
329 a
329 a
330 -b
330 -b
331 +b b
331 +b b
332 c
332 c
333 d
333 d
334 e
334 e
335
335
336
336
337 After qrecord a.patch 'diff'"
337 After qrecord a.patch 'diff'"
338
338
339 $ hg diff --nodates
339 $ hg diff --nodates
340 diff -r 5d1ca63427ee 1.txt
340 diff -r 5d1ca63427ee 1.txt
341 --- a/1.txt
341 --- a/1.txt
342 +++ b/1.txt
342 +++ b/1.txt
343 @@ -1,5 +1,5 @@
343 @@ -1,5 +1,5 @@
344 1
344 1
345 2 2
345 2 2
346 3
346 3
347 -4
347 -4
348 +4 4
348 +4 4
349 5
349 5
350 diff -r 5d1ca63427ee dir/a.txt
350 diff -r 5d1ca63427ee dir/a.txt
351 --- a/dir/a.txt
351 --- a/dir/a.txt
352 +++ b/dir/a.txt
352 +++ b/dir/a.txt
353 @@ -1,4 +1,4 @@
353 @@ -1,4 +1,4 @@
354 -hello world
354 -hello world
355 +hello world!
355 +hello world!
356
356
357 someone
357 someone
358 up
358 up
359
359
360 qrecord b.patch
360 qrecord b.patch
361
361
362 $ hg qrecord -d '0 0' -m bbb b.patch <<EOF
362 $ hg qrecord -d '0 0' -m bbb b.patch <<EOF
363 > y
363 > y
364 > y
364 > y
365 > y
365 > y
366 > y
366 > y
367 > EOF
367 > EOF
368 diff --git a/1.txt b/1.txt
368 diff --git a/1.txt b/1.txt
369 1 hunks, 1 lines changed
369 1 hunks, 1 lines changed
370 examine changes to '1.txt'?
370 examine changes to '1.txt'?
371 (enter ? for help) [Ynesfdaq?] y
371 (enter ? for help) [Ynesfdaq?] y
372
372
373 @@ -1,5 +1,5 @@
373 @@ -1,5 +1,5 @@
374 1
374 1
375 2 2
375 2 2
376 3
376 3
377 -4
377 -4
378 +4 4
378 +4 4
379 5
379 5
380 record change 1/2 to '1.txt'?
380 record change 1/2 to '1.txt'?
381 (enter ? for help) [Ynesfdaq?] y
381 (enter ? for help) [Ynesfdaq?] y
382
382
383 diff --git a/dir/a.txt b/dir/a.txt
383 diff --git a/dir/a.txt b/dir/a.txt
384 1 hunks, 1 lines changed
384 1 hunks, 1 lines changed
385 examine changes to 'dir/a.txt'?
385 examine changes to 'dir/a.txt'?
386 (enter ? for help) [Ynesfdaq?] y
386 (enter ? for help) [Ynesfdaq?] y
387
387
388 @@ -1,4 +1,4 @@
388 @@ -1,4 +1,4 @@
389 -hello world
389 -hello world
390 +hello world!
390 +hello world!
391
391
392 someone
392 someone
393 up
393 up
394 record change 2/2 to 'dir/a.txt'?
394 record change 2/2 to 'dir/a.txt'?
395 (enter ? for help) [Ynesfdaq?] y
395 (enter ? for help) [Ynesfdaq?] y
396
396
397
397
398 After qrecord b.patch 'tip'
398 After qrecord b.patch 'tip'
399
399
400 $ hg tip -p
400 $ hg tip -p
401 changeset: 2:b056198bf878
401 changeset: 2:b056198bf878
402 tag: b.patch
402 tag: b.patch
403 tag: qtip
403 tag: qtip
404 tag: tip
404 tag: tip
405 user: test
405 user: test
406 date: Thu Jan 01 00:00:00 1970 +0000
406 date: Thu Jan 01 00:00:00 1970 +0000
407 summary: bbb
407 summary: bbb
408
408
409 diff -r 5d1ca63427ee -r b056198bf878 1.txt
409 diff -r 5d1ca63427ee -r b056198bf878 1.txt
410 --- a/1.txt Thu Jan 01 00:00:00 1970 +0000
410 --- a/1.txt Thu Jan 01 00:00:00 1970 +0000
411 +++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
411 +++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
412 @@ -1,5 +1,5 @@
412 @@ -1,5 +1,5 @@
413 1
413 1
414 2 2
414 2 2
415 3
415 3
416 -4
416 -4
417 +4 4
417 +4 4
418 5
418 5
419 diff -r 5d1ca63427ee -r b056198bf878 dir/a.txt
419 diff -r 5d1ca63427ee -r b056198bf878 dir/a.txt
420 --- a/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
420 --- a/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
421 +++ b/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
421 +++ b/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
422 @@ -1,4 +1,4 @@
422 @@ -1,4 +1,4 @@
423 -hello world
423 -hello world
424 +hello world!
424 +hello world!
425
425
426 someone
426 someone
427 up
427 up
428
428
429
429
430 After qrecord b.patch 'diff'
430 After qrecord b.patch 'diff'
431
431
432 $ hg diff --nodates
432 $ hg diff --nodates
433
433
434 $ cd ..
434 $ cd ..
435
435
436 qrecord should throw an error when histedit in process
436 qrecord should throw an error when histedit in process
437
437
438 $ hg init issue5981
438 $ hg init issue5981
439 $ cd issue5981
439 $ cd issue5981
440 $ cat >> $HGRCPATH <<EOF
440 $ cat >> $HGRCPATH <<EOF
441 > [extensions]
441 > [extensions]
442 > histedit=
442 > histedit=
443 > mq=
443 > mq=
444 > EOF
444 > EOF
445 $ echo > a
445 $ echo > a
446 $ hg ci -Am 'foo bar'
446 $ hg ci -Am 'foo bar'
447 adding a
447 adding a
448 $ hg log
448 $ hg log
449 changeset: 0:ea55e2ae468f
449 changeset: 0:ea55e2ae468f
450 tag: tip
450 tag: tip
451 user: test
451 user: test
452 date: Thu Jan 01 00:00:00 1970 +0000
452 date: Thu Jan 01 00:00:00 1970 +0000
453 summary: foo bar
453 summary: foo bar
454
454
455 $ hg histedit tip --commands - 2>&1 <<EOF
455 $ hg histedit tip --commands - 2>&1 <<EOF
456 > edit ea55e2ae468f foo bar
456 > edit ea55e2ae468f foo bar
457 > EOF
457 > EOF
458 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
458 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
459 Editing (ea55e2ae468f), you may commit or record as needed now.
459 Editing (ea55e2ae468f), commit as needed now to split the change
460 (hg histedit --continue to resume)
460 (to edit ea55e2ae468f, `hg histedit --continue` after making changes)
461 [240]
461 [240]
462 $ echo 'foo bar' > a
462 $ echo 'foo bar' > a
463 $ hg qrecord -d '0 0' -m aaa a.patch <<EOF
463 $ hg qrecord -d '0 0' -m aaa a.patch <<EOF
464 > y
464 > y
465 > y
465 > y
466 > n
466 > n
467 > y
467 > y
468 > y
468 > y
469 > n
469 > n
470 > EOF
470 > EOF
471 abort: histedit in progress
471 abort: histedit in progress
472 (use 'hg histedit --continue' or 'hg histedit --abort')
472 (use 'hg histedit --continue' or 'hg histedit --abort')
473 [20]
473 [20]
@@ -1,460 +1,460 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > rebase=
3 > rebase=
4 > histedit=
4 > histedit=
5 >
5 >
6 > [alias]
6 > [alias]
7 > tglog = log -G --template "{rev}: {node|short} '{desc}' {branches}\n"
7 > tglog = log -G --template "{rev}: {node|short} '{desc}' {branches}\n"
8 > EOF
8 > EOF
9
9
10
10
11 $ hg init a
11 $ hg init a
12 $ cd a
12 $ cd a
13
13
14 $ echo C1 > C1
14 $ echo C1 > C1
15 $ hg ci -Am C1
15 $ hg ci -Am C1
16 adding C1
16 adding C1
17
17
18 $ echo C2 > C2
18 $ echo C2 > C2
19 $ hg ci -Am C2
19 $ hg ci -Am C2
20 adding C2
20 adding C2
21
21
22 $ cd ..
22 $ cd ..
23
23
24 $ hg clone a b
24 $ hg clone a b
25 updating to branch default
25 updating to branch default
26 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
26 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
27
27
28 $ hg clone a c
28 $ hg clone a c
29 updating to branch default
29 updating to branch default
30 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
30 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
31
31
32 $ cd b
32 $ cd b
33
33
34 $ echo L1 > L1
34 $ echo L1 > L1
35 $ hg ci -Am L1
35 $ hg ci -Am L1
36 adding L1
36 adding L1
37
37
38
38
39 $ cd ../a
39 $ cd ../a
40
40
41 $ echo R1 > R1
41 $ echo R1 > R1
42 $ hg ci -Am R1
42 $ hg ci -Am R1
43 adding R1
43 adding R1
44
44
45
45
46 $ cd ../b
46 $ cd ../b
47
47
48 Now b has one revision to be pulled from a:
48 Now b has one revision to be pulled from a:
49
49
50 $ hg pull --rebase
50 $ hg pull --rebase
51 pulling from $TESTTMP/a
51 pulling from $TESTTMP/a
52 searching for changes
52 searching for changes
53 adding changesets
53 adding changesets
54 adding manifests
54 adding manifests
55 adding file changes
55 adding file changes
56 added 1 changesets with 1 changes to 1 files (+1 heads)
56 added 1 changesets with 1 changes to 1 files (+1 heads)
57 new changesets 77ae9631bcca
57 new changesets 77ae9631bcca
58 rebasing 2:ff8d69a621f9 "L1"
58 rebasing 2:ff8d69a621f9 "L1"
59 saved backup bundle to $TESTTMP/b/.hg/strip-backup/ff8d69a621f9-160fa373-rebase.hg
59 saved backup bundle to $TESTTMP/b/.hg/strip-backup/ff8d69a621f9-160fa373-rebase.hg
60
60
61 $ hg tglog
61 $ hg tglog
62 @ 3: d80cc2da061e 'L1'
62 @ 3: d80cc2da061e 'L1'
63 |
63 |
64 o 2: 77ae9631bcca 'R1'
64 o 2: 77ae9631bcca 'R1'
65 |
65 |
66 o 1: 783333faa078 'C2'
66 o 1: 783333faa078 'C2'
67 |
67 |
68 o 0: 05d58a0c15dd 'C1'
68 o 0: 05d58a0c15dd 'C1'
69
69
70 Re-run:
70 Re-run:
71
71
72 $ hg pull --rebase
72 $ hg pull --rebase
73 pulling from $TESTTMP/a
73 pulling from $TESTTMP/a
74 searching for changes
74 searching for changes
75 no changes found
75 no changes found
76
76
77 Abort pull early if working dir is not clean:
77 Abort pull early if working dir is not clean:
78
78
79 $ echo L1-mod > L1
79 $ echo L1-mod > L1
80 $ hg pull --rebase
80 $ hg pull --rebase
81 abort: uncommitted changes
81 abort: uncommitted changes
82 (cannot pull with rebase: please commit or shelve your changes first)
82 (cannot pull with rebase: please commit or shelve your changes first)
83 [20]
83 [20]
84 $ hg update --clean --quiet
84 $ hg update --clean --quiet
85
85
86 Abort pull early if another operation (histedit) is in progress:
86 Abort pull early if another operation (histedit) is in progress:
87
87
88 $ hg histedit . -q --commands - << EOF
88 $ hg histedit . -q --commands - << EOF
89 > edit d80cc2da061e histedit: generate unfinished state
89 > edit d80cc2da061e histedit: generate unfinished state
90 > EOF
90 > EOF
91 Editing (d80cc2da061e), you may commit or record as needed now.
91 Editing (d80cc2da061e), commit as needed now to split the change
92 (hg histedit --continue to resume)
92 (to edit d80cc2da061e, `hg histedit --continue` after making changes)
93 [240]
93 [240]
94 $ hg pull --rebase
94 $ hg pull --rebase
95 abort: histedit in progress
95 abort: histedit in progress
96 (use 'hg histedit --continue' or 'hg histedit --abort')
96 (use 'hg histedit --continue' or 'hg histedit --abort')
97 [20]
97 [20]
98 $ hg histedit --abort --quiet
98 $ hg histedit --abort --quiet
99
99
100 Abort pull early with pending uncommitted merge:
100 Abort pull early with pending uncommitted merge:
101
101
102 $ cd ..
102 $ cd ..
103 $ hg clone --noupdate c d
103 $ hg clone --noupdate c d
104 $ cd d
104 $ cd d
105 $ hg tglog
105 $ hg tglog
106 o 1: 783333faa078 'C2'
106 o 1: 783333faa078 'C2'
107 |
107 |
108 o 0: 05d58a0c15dd 'C1'
108 o 0: 05d58a0c15dd 'C1'
109
109
110 $ hg update --quiet 0
110 $ hg update --quiet 0
111 $ echo M1 > M1
111 $ echo M1 > M1
112 $ hg commit --quiet -Am M1
112 $ hg commit --quiet -Am M1
113 $ hg update --quiet 1
113 $ hg update --quiet 1
114 $ hg merge 2
114 $ hg merge 2
115 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
115 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
116 (branch merge, don't forget to commit)
116 (branch merge, don't forget to commit)
117 $ hg pull --rebase
117 $ hg pull --rebase
118 abort: outstanding uncommitted merge
118 abort: outstanding uncommitted merge
119 (cannot pull with rebase: please commit or shelve your changes first)
119 (cannot pull with rebase: please commit or shelve your changes first)
120 [20]
120 [20]
121 $ hg update --clean --quiet
121 $ hg update --clean --quiet
122
122
123 Abort pull early with unclean subrepo:
123 Abort pull early with unclean subrepo:
124 $ echo s = s > .hgsub
124 $ echo s = s > .hgsub
125 $ hg add .hgsub
125 $ hg add .hgsub
126 $ hg init s
126 $ hg init s
127 $ hg commit -m "generated a subrepo"
127 $ hg commit -m "generated a subrepo"
128 $ echo a > s/a
128 $ echo a > s/a
129 $ hg -R s add s/a
129 $ hg -R s add s/a
130 $ hg pull --rebase
130 $ hg pull --rebase
131 abort: uncommitted changes in subrepository "s"
131 abort: uncommitted changes in subrepository "s"
132 (cannot pull with rebase: please commit or shelve your changes first)
132 (cannot pull with rebase: please commit or shelve your changes first)
133 [255]
133 [255]
134
134
135 Invoke pull --rebase and nothing to rebase:
135 Invoke pull --rebase and nothing to rebase:
136
136
137 $ cd ../c
137 $ cd ../c
138
138
139 $ hg book norebase
139 $ hg book norebase
140 $ hg pull --rebase
140 $ hg pull --rebase
141 pulling from $TESTTMP/a
141 pulling from $TESTTMP/a
142 searching for changes
142 searching for changes
143 adding changesets
143 adding changesets
144 adding manifests
144 adding manifests
145 adding file changes
145 adding file changes
146 added 1 changesets with 1 changes to 1 files
146 added 1 changesets with 1 changes to 1 files
147 new changesets 77ae9631bcca
147 new changesets 77ae9631bcca
148 nothing to rebase - updating instead
148 nothing to rebase - updating instead
149 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
149 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 updating bookmark norebase
150 updating bookmark norebase
151
151
152 $ hg tglog -l 1
152 $ hg tglog -l 1
153 @ 2: 77ae9631bcca 'R1'
153 @ 2: 77ae9631bcca 'R1'
154 |
154 |
155 ~
155 ~
156
156
157 pull --rebase --update should ignore --update:
157 pull --rebase --update should ignore --update:
158
158
159 $ hg pull --rebase --update
159 $ hg pull --rebase --update
160 pulling from $TESTTMP/a
160 pulling from $TESTTMP/a
161 searching for changes
161 searching for changes
162 no changes found
162 no changes found
163
163
164 pull --rebase doesn't update if nothing has been pulled:
164 pull --rebase doesn't update if nothing has been pulled:
165
165
166 $ hg up -q 1
166 $ hg up -q 1
167
167
168 $ hg pull --rebase
168 $ hg pull --rebase
169 pulling from $TESTTMP/a
169 pulling from $TESTTMP/a
170 searching for changes
170 searching for changes
171 no changes found
171 no changes found
172
172
173 $ hg tglog -l 1
173 $ hg tglog -l 1
174 o 2: 77ae9631bcca 'R1'
174 o 2: 77ae9631bcca 'R1'
175 |
175 |
176 ~
176 ~
177
177
178 $ cd ..
178 $ cd ..
179
179
180 pull --rebase works when a specific revision is pulled (issue3619)
180 pull --rebase works when a specific revision is pulled (issue3619)
181
181
182 $ cd a
182 $ cd a
183 $ hg tglog
183 $ hg tglog
184 @ 2: 77ae9631bcca 'R1'
184 @ 2: 77ae9631bcca 'R1'
185 |
185 |
186 o 1: 783333faa078 'C2'
186 o 1: 783333faa078 'C2'
187 |
187 |
188 o 0: 05d58a0c15dd 'C1'
188 o 0: 05d58a0c15dd 'C1'
189
189
190 $ echo R2 > R2
190 $ echo R2 > R2
191 $ hg ci -Am R2
191 $ hg ci -Am R2
192 adding R2
192 adding R2
193 $ echo R3 > R3
193 $ echo R3 > R3
194 $ hg ci -Am R3
194 $ hg ci -Am R3
195 adding R3
195 adding R3
196 $ cd ../c
196 $ cd ../c
197 $ hg tglog
197 $ hg tglog
198 o 2: 77ae9631bcca 'R1'
198 o 2: 77ae9631bcca 'R1'
199 |
199 |
200 @ 1: 783333faa078 'C2'
200 @ 1: 783333faa078 'C2'
201 |
201 |
202 o 0: 05d58a0c15dd 'C1'
202 o 0: 05d58a0c15dd 'C1'
203
203
204 $ echo L1 > L1
204 $ echo L1 > L1
205 $ hg ci -Am L1
205 $ hg ci -Am L1
206 adding L1
206 adding L1
207 created new head
207 created new head
208 $ hg pull --rev tip --rebase
208 $ hg pull --rev tip --rebase
209 pulling from $TESTTMP/a
209 pulling from $TESTTMP/a
210 searching for changes
210 searching for changes
211 adding changesets
211 adding changesets
212 adding manifests
212 adding manifests
213 adding file changes
213 adding file changes
214 added 2 changesets with 2 changes to 2 files
214 added 2 changesets with 2 changes to 2 files
215 new changesets 31cd3a05214e:770a61882ace
215 new changesets 31cd3a05214e:770a61882ace
216 rebasing 3:ff8d69a621f9 "L1"
216 rebasing 3:ff8d69a621f9 "L1"
217 saved backup bundle to $TESTTMP/c/.hg/strip-backup/ff8d69a621f9-160fa373-rebase.hg
217 saved backup bundle to $TESTTMP/c/.hg/strip-backup/ff8d69a621f9-160fa373-rebase.hg
218 $ hg tglog
218 $ hg tglog
219 @ 5: 518d153c0ba3 'L1'
219 @ 5: 518d153c0ba3 'L1'
220 |
220 |
221 o 4: 770a61882ace 'R3'
221 o 4: 770a61882ace 'R3'
222 |
222 |
223 o 3: 31cd3a05214e 'R2'
223 o 3: 31cd3a05214e 'R2'
224 |
224 |
225 o 2: 77ae9631bcca 'R1'
225 o 2: 77ae9631bcca 'R1'
226 |
226 |
227 o 1: 783333faa078 'C2'
227 o 1: 783333faa078 'C2'
228 |
228 |
229 o 0: 05d58a0c15dd 'C1'
229 o 0: 05d58a0c15dd 'C1'
230
230
231 pull --rebase works with bundle2 turned on
231 pull --rebase works with bundle2 turned on
232
232
233 $ cd ../a
233 $ cd ../a
234 $ echo R4 > R4
234 $ echo R4 > R4
235 $ hg ci -Am R4
235 $ hg ci -Am R4
236 adding R4
236 adding R4
237 $ hg tglog
237 $ hg tglog
238 @ 5: 00e3b7781125 'R4'
238 @ 5: 00e3b7781125 'R4'
239 |
239 |
240 o 4: 770a61882ace 'R3'
240 o 4: 770a61882ace 'R3'
241 |
241 |
242 o 3: 31cd3a05214e 'R2'
242 o 3: 31cd3a05214e 'R2'
243 |
243 |
244 o 2: 77ae9631bcca 'R1'
244 o 2: 77ae9631bcca 'R1'
245 |
245 |
246 o 1: 783333faa078 'C2'
246 o 1: 783333faa078 'C2'
247 |
247 |
248 o 0: 05d58a0c15dd 'C1'
248 o 0: 05d58a0c15dd 'C1'
249
249
250 $ cd ../c
250 $ cd ../c
251 $ hg pull --rebase
251 $ hg pull --rebase
252 pulling from $TESTTMP/a
252 pulling from $TESTTMP/a
253 searching for changes
253 searching for changes
254 adding changesets
254 adding changesets
255 adding manifests
255 adding manifests
256 adding file changes
256 adding file changes
257 added 1 changesets with 1 changes to 1 files (+1 heads)
257 added 1 changesets with 1 changes to 1 files (+1 heads)
258 new changesets 00e3b7781125
258 new changesets 00e3b7781125
259 rebasing 5:518d153c0ba3 "L1"
259 rebasing 5:518d153c0ba3 "L1"
260 saved backup bundle to $TESTTMP/c/.hg/strip-backup/518d153c0ba3-73407f14-rebase.hg
260 saved backup bundle to $TESTTMP/c/.hg/strip-backup/518d153c0ba3-73407f14-rebase.hg
261 $ hg tglog
261 $ hg tglog
262 @ 6: 0d0727eb7ce0 'L1'
262 @ 6: 0d0727eb7ce0 'L1'
263 |
263 |
264 o 5: 00e3b7781125 'R4'
264 o 5: 00e3b7781125 'R4'
265 |
265 |
266 o 4: 770a61882ace 'R3'
266 o 4: 770a61882ace 'R3'
267 |
267 |
268 o 3: 31cd3a05214e 'R2'
268 o 3: 31cd3a05214e 'R2'
269 |
269 |
270 o 2: 77ae9631bcca 'R1'
270 o 2: 77ae9631bcca 'R1'
271 |
271 |
272 o 1: 783333faa078 'C2'
272 o 1: 783333faa078 'C2'
273 |
273 |
274 o 0: 05d58a0c15dd 'C1'
274 o 0: 05d58a0c15dd 'C1'
275
275
276
276
277 pull --rebase only update if there is nothing to rebase
277 pull --rebase only update if there is nothing to rebase
278
278
279 $ cd ../a
279 $ cd ../a
280 $ echo R5 > R5
280 $ echo R5 > R5
281 $ hg ci -Am R5
281 $ hg ci -Am R5
282 adding R5
282 adding R5
283 $ hg tglog
283 $ hg tglog
284 @ 6: 88dd24261747 'R5'
284 @ 6: 88dd24261747 'R5'
285 |
285 |
286 o 5: 00e3b7781125 'R4'
286 o 5: 00e3b7781125 'R4'
287 |
287 |
288 o 4: 770a61882ace 'R3'
288 o 4: 770a61882ace 'R3'
289 |
289 |
290 o 3: 31cd3a05214e 'R2'
290 o 3: 31cd3a05214e 'R2'
291 |
291 |
292 o 2: 77ae9631bcca 'R1'
292 o 2: 77ae9631bcca 'R1'
293 |
293 |
294 o 1: 783333faa078 'C2'
294 o 1: 783333faa078 'C2'
295 |
295 |
296 o 0: 05d58a0c15dd 'C1'
296 o 0: 05d58a0c15dd 'C1'
297
297
298 $ cd ../c
298 $ cd ../c
299 $ echo L2 > L2
299 $ echo L2 > L2
300 $ hg ci -Am L2
300 $ hg ci -Am L2
301 adding L2
301 adding L2
302 $ hg up 'desc(L1)'
302 $ hg up 'desc(L1)'
303 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
303 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
304 $ hg pull --rebase
304 $ hg pull --rebase
305 pulling from $TESTTMP/a
305 pulling from $TESTTMP/a
306 searching for changes
306 searching for changes
307 adding changesets
307 adding changesets
308 adding manifests
308 adding manifests
309 adding file changes
309 adding file changes
310 added 1 changesets with 1 changes to 1 files (+1 heads)
310 added 1 changesets with 1 changes to 1 files (+1 heads)
311 new changesets 88dd24261747
311 new changesets 88dd24261747
312 rebasing 6:0d0727eb7ce0 "L1"
312 rebasing 6:0d0727eb7ce0 "L1"
313 rebasing 7:c1f58876e3bf "L2"
313 rebasing 7:c1f58876e3bf "L2"
314 saved backup bundle to $TESTTMP/c/.hg/strip-backup/0d0727eb7ce0-ef61ccb2-rebase.hg
314 saved backup bundle to $TESTTMP/c/.hg/strip-backup/0d0727eb7ce0-ef61ccb2-rebase.hg
315 $ hg tglog
315 $ hg tglog
316 o 8: 6dc0ea5dcf55 'L2'
316 o 8: 6dc0ea5dcf55 'L2'
317 |
317 |
318 @ 7: 864e0a2d2614 'L1'
318 @ 7: 864e0a2d2614 'L1'
319 |
319 |
320 o 6: 88dd24261747 'R5'
320 o 6: 88dd24261747 'R5'
321 |
321 |
322 o 5: 00e3b7781125 'R4'
322 o 5: 00e3b7781125 'R4'
323 |
323 |
324 o 4: 770a61882ace 'R3'
324 o 4: 770a61882ace 'R3'
325 |
325 |
326 o 3: 31cd3a05214e 'R2'
326 o 3: 31cd3a05214e 'R2'
327 |
327 |
328 o 2: 77ae9631bcca 'R1'
328 o 2: 77ae9631bcca 'R1'
329 |
329 |
330 o 1: 783333faa078 'C2'
330 o 1: 783333faa078 'C2'
331 |
331 |
332 o 0: 05d58a0c15dd 'C1'
332 o 0: 05d58a0c15dd 'C1'
333
333
334
334
335 pull --rebase update (no rebase) use proper update:
335 pull --rebase update (no rebase) use proper update:
336
336
337 - warn about other head.
337 - warn about other head.
338
338
339 $ cd ../a
339 $ cd ../a
340 $ echo R6 > R6
340 $ echo R6 > R6
341 $ hg ci -Am R6
341 $ hg ci -Am R6
342 adding R6
342 adding R6
343 $ cd ../c
343 $ cd ../c
344 $ hg up 'desc(R5)'
344 $ hg up 'desc(R5)'
345 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
345 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
346 $ hg pull --rebase
346 $ hg pull --rebase
347 pulling from $TESTTMP/a
347 pulling from $TESTTMP/a
348 searching for changes
348 searching for changes
349 adding changesets
349 adding changesets
350 adding manifests
350 adding manifests
351 adding file changes
351 adding file changes
352 added 1 changesets with 1 changes to 1 files (+1 heads)
352 added 1 changesets with 1 changes to 1 files (+1 heads)
353 new changesets 65bc164c1d9b
353 new changesets 65bc164c1d9b
354 nothing to rebase - updating instead
354 nothing to rebase - updating instead
355 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
355 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
356 updated to "65bc164c1d9b: R6"
356 updated to "65bc164c1d9b: R6"
357 1 other heads for branch "default"
357 1 other heads for branch "default"
358 $ hg tglog
358 $ hg tglog
359 @ 9: 65bc164c1d9b 'R6'
359 @ 9: 65bc164c1d9b 'R6'
360 |
360 |
361 | o 8: 6dc0ea5dcf55 'L2'
361 | o 8: 6dc0ea5dcf55 'L2'
362 | |
362 | |
363 | o 7: 864e0a2d2614 'L1'
363 | o 7: 864e0a2d2614 'L1'
364 |/
364 |/
365 o 6: 88dd24261747 'R5'
365 o 6: 88dd24261747 'R5'
366 |
366 |
367 o 5: 00e3b7781125 'R4'
367 o 5: 00e3b7781125 'R4'
368 |
368 |
369 o 4: 770a61882ace 'R3'
369 o 4: 770a61882ace 'R3'
370 |
370 |
371 o 3: 31cd3a05214e 'R2'
371 o 3: 31cd3a05214e 'R2'
372 |
372 |
373 o 2: 77ae9631bcca 'R1'
373 o 2: 77ae9631bcca 'R1'
374 |
374 |
375 o 1: 783333faa078 'C2'
375 o 1: 783333faa078 'C2'
376 |
376 |
377 o 0: 05d58a0c15dd 'C1'
377 o 0: 05d58a0c15dd 'C1'
378
378
379
379
380 Multiple pre-existing heads on the branch
380 Multiple pre-existing heads on the branch
381 -----------------------------------------
381 -----------------------------------------
382
382
383 Pull bring content, but nothing on the current branch, we should not consider
383 Pull bring content, but nothing on the current branch, we should not consider
384 pre-existing heads.
384 pre-existing heads.
385
385
386 $ cd ../a
386 $ cd ../a
387 $ hg branch unrelatedbranch
387 $ hg branch unrelatedbranch
388 marked working directory as branch unrelatedbranch
388 marked working directory as branch unrelatedbranch
389 (branches are permanent and global, did you want a bookmark?)
389 (branches are permanent and global, did you want a bookmark?)
390 $ echo B1 > B1
390 $ echo B1 > B1
391 $ hg commit -Am B1
391 $ hg commit -Am B1
392 adding B1
392 adding B1
393 $ cd ../c
393 $ cd ../c
394 $ hg up 'desc(L2)'
394 $ hg up 'desc(L2)'
395 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
395 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
396 $ hg pull --rebase
396 $ hg pull --rebase
397 pulling from $TESTTMP/a
397 pulling from $TESTTMP/a
398 searching for changes
398 searching for changes
399 adding changesets
399 adding changesets
400 adding manifests
400 adding manifests
401 adding file changes
401 adding file changes
402 added 1 changesets with 1 changes to 1 files
402 added 1 changesets with 1 changes to 1 files
403 new changesets 39c381359968
403 new changesets 39c381359968
404 nothing to rebase
404 nothing to rebase
405
405
406 There is two local heads and we pull a third one.
406 There is two local heads and we pull a third one.
407 The second local head should not confuse the `hg pull rebase`.
407 The second local head should not confuse the `hg pull rebase`.
408
408
409 $ hg up 'desc(R6)'
409 $ hg up 'desc(R6)'
410 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
410 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
411 $ echo M1 > M1
411 $ echo M1 > M1
412 $ hg commit -Am M1
412 $ hg commit -Am M1
413 adding M1
413 adding M1
414 $ cd ../a
414 $ cd ../a
415 $ hg up 'desc(R6)'
415 $ hg up 'desc(R6)'
416 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
416 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
417 $ echo R7 > R7
417 $ echo R7 > R7
418 $ hg commit -Am R7
418 $ hg commit -Am R7
419 adding R7
419 adding R7
420 $ cd ../c
420 $ cd ../c
421 $ hg up 'desc(L2)'
421 $ hg up 'desc(L2)'
422 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
422 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
423 $ hg pull --rebase
423 $ hg pull --rebase
424 pulling from $TESTTMP/a
424 pulling from $TESTTMP/a
425 searching for changes
425 searching for changes
426 adding changesets
426 adding changesets
427 adding manifests
427 adding manifests
428 adding file changes
428 adding file changes
429 added 1 changesets with 1 changes to 1 files (+1 heads)
429 added 1 changesets with 1 changes to 1 files (+1 heads)
430 new changesets f7d3e42052f9
430 new changesets f7d3e42052f9
431 rebasing 7:864e0a2d2614 "L1"
431 rebasing 7:864e0a2d2614 "L1"
432 rebasing 8:6dc0ea5dcf55 "L2"
432 rebasing 8:6dc0ea5dcf55 "L2"
433 saved backup bundle to $TESTTMP/c/.hg/strip-backup/864e0a2d2614-2f72c89c-rebase.hg
433 saved backup bundle to $TESTTMP/c/.hg/strip-backup/864e0a2d2614-2f72c89c-rebase.hg
434 $ hg tglog
434 $ hg tglog
435 @ 12: 3603a865eea0 'L2'
435 @ 12: 3603a865eea0 'L2'
436 |
436 |
437 o 11: bcc8a9cd04bf 'L1'
437 o 11: bcc8a9cd04bf 'L1'
438 |
438 |
439 o 10: f7d3e42052f9 'R7'
439 o 10: f7d3e42052f9 'R7'
440 |
440 |
441 | o 9: 41fab4eef82f 'M1'
441 | o 9: 41fab4eef82f 'M1'
442 |/
442 |/
443 | o 8: 39c381359968 'B1' unrelatedbranch
443 | o 8: 39c381359968 'B1' unrelatedbranch
444 |/
444 |/
445 o 7: 65bc164c1d9b 'R6'
445 o 7: 65bc164c1d9b 'R6'
446 |
446 |
447 o 6: 88dd24261747 'R5'
447 o 6: 88dd24261747 'R5'
448 |
448 |
449 o 5: 00e3b7781125 'R4'
449 o 5: 00e3b7781125 'R4'
450 |
450 |
451 o 4: 770a61882ace 'R3'
451 o 4: 770a61882ace 'R3'
452 |
452 |
453 o 3: 31cd3a05214e 'R2'
453 o 3: 31cd3a05214e 'R2'
454 |
454 |
455 o 2: 77ae9631bcca 'R1'
455 o 2: 77ae9631bcca 'R1'
456 |
456 |
457 o 1: 783333faa078 'C2'
457 o 1: 783333faa078 'C2'
458 |
458 |
459 o 0: 05d58a0c15dd 'C1'
459 o 0: 05d58a0c15dd 'C1'
460
460
General Comments 0
You need to be logged in to leave comments. Login now