##// END OF EJS Templates
histedit: narrow the scope of discarded ui output...
Rodrigo Damazio Bovendorp -
r42219:15d2afa3 default
parent child Browse files
Show More
@@ -0,0 +1,57 b''
1 Test histedit extension: Merge tools
2 ====================================
3
4 Initialization
5 ---------------
6
7 $ . "$TESTDIR/histedit-helpers.sh"
8
9 $ cat >> $HGRCPATH <<EOF
10 > [alias]
11 > logt = log --template '{rev}:{node|short} {desc|firstline}\n'
12 > [extensions]
13 > histedit=
14 > mockmakedate = $TESTDIR/mockmakedate.py
15 > [ui]
16 > pre-merge-tool-output-template='pre-merge message for {node}\n'
17 > EOF
18
19 Merge conflict
20 --------------
21
22 $ hg init r
23 $ cd r
24 $ echo foo > file
25 $ hg add file
26 $ hg ci -m "First" -d "1 0"
27 $ echo bar > file
28 $ hg ci -m "Second" -d "2 0"
29
30 $ hg logt --graph
31 @ 1:2aa920f62fb9 Second
32 |
33 o 0:7181f42b8fca First
34
35
36 Invert the order of the commits, but fail the merge.
37 $ hg histedit --config ui.merge=false --commands - 2>&1 <<EOF | fixbundle
38 > pick 2aa920f62fb9 Second
39 > pick 7181f42b8fca First
40 > EOF
41 merging file
42 pre-merge message for b90fa2e91a6d11013945a5f684be45b84a8ca6ec
43 merging file failed!
44 Fix up the change (pick 7181f42b8fca)
45 (hg histedit --continue to resume)
46
47 $ hg histedit --abort | fixbundle
48 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
49
50 Invert the order of the commits, and pretend the merge succeeded.
51 $ hg histedit --config ui.merge=true --commands - 2>&1 <<EOF | fixbundle
52 > pick 2aa920f62fb9 Second
53 > pick 7181f42b8fca First
54 > EOF
55 merging file
56 pre-merge message for b90fa2e91a6d11013945a5f684be45b84a8ca6ec
57 7181f42b8fca: skipping changeset (no changes)
@@ -1,2272 +1,2271 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 import (
208 from mercurial import (
209 bundle2,
209 bundle2,
210 cmdutil,
210 cmdutil,
211 context,
211 context,
212 copies,
212 copies,
213 destutil,
213 destutil,
214 discovery,
214 discovery,
215 error,
215 error,
216 exchange,
216 exchange,
217 extensions,
217 extensions,
218 hg,
218 hg,
219 logcmdutil,
219 logcmdutil,
220 merge as mergemod,
220 merge as mergemod,
221 mergeutil,
221 mergeutil,
222 node,
222 node,
223 obsolete,
223 obsolete,
224 pycompat,
224 pycompat,
225 registrar,
225 registrar,
226 repair,
226 repair,
227 scmutil,
227 scmutil,
228 state as statemod,
228 state as statemod,
229 util,
229 util,
230 )
230 )
231 from mercurial.utils import (
231 from mercurial.utils import (
232 dateutil,
232 dateutil,
233 stringutil,
233 stringutil,
234 )
234 )
235
235
236 pickle = util.pickle
236 pickle = util.pickle
237 cmdtable = {}
237 cmdtable = {}
238 command = registrar.command(cmdtable)
238 command = registrar.command(cmdtable)
239
239
240 configtable = {}
240 configtable = {}
241 configitem = registrar.configitem(configtable)
241 configitem = registrar.configitem(configtable)
242 configitem('experimental', 'histedit.autoverb',
242 configitem('experimental', 'histedit.autoverb',
243 default=False,
243 default=False,
244 )
244 )
245 configitem('histedit', 'defaultrev',
245 configitem('histedit', 'defaultrev',
246 default=None,
246 default=None,
247 )
247 )
248 configitem('histedit', 'dropmissing',
248 configitem('histedit', 'dropmissing',
249 default=False,
249 default=False,
250 )
250 )
251 configitem('histedit', 'linelen',
251 configitem('histedit', 'linelen',
252 default=80,
252 default=80,
253 )
253 )
254 configitem('histedit', 'singletransaction',
254 configitem('histedit', 'singletransaction',
255 default=False,
255 default=False,
256 )
256 )
257 configitem('ui', 'interface.histedit',
257 configitem('ui', 'interface.histedit',
258 default=None,
258 default=None,
259 )
259 )
260 configitem('histedit', 'summary-template',
260 configitem('histedit', 'summary-template',
261 default='{rev} {desc|firstline}')
261 default='{rev} {desc|firstline}')
262
262
263 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
263 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
264 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
264 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
265 # be specifying the version(s) of Mercurial they are tested with, or
265 # be specifying the version(s) of Mercurial they are tested with, or
266 # leave the attribute unspecified.
266 # leave the attribute unspecified.
267 testedwith = 'ships-with-hg-core'
267 testedwith = 'ships-with-hg-core'
268
268
269 actiontable = {}
269 actiontable = {}
270 primaryactions = set()
270 primaryactions = set()
271 secondaryactions = set()
271 secondaryactions = set()
272 tertiaryactions = set()
272 tertiaryactions = set()
273 internalactions = set()
273 internalactions = set()
274
274
275 def geteditcomment(ui, first, last):
275 def geteditcomment(ui, first, last):
276 """ construct the editor comment
276 """ construct the editor comment
277 The comment includes::
277 The comment includes::
278 - an intro
278 - an intro
279 - sorted primary commands
279 - sorted primary commands
280 - sorted short commands
280 - sorted short commands
281 - sorted long commands
281 - sorted long commands
282 - additional hints
282 - additional hints
283
283
284 Commands are only included once.
284 Commands are only included once.
285 """
285 """
286 intro = _("""Edit history between %s and %s
286 intro = _("""Edit history between %s and %s
287
287
288 Commits are listed from least to most recent
288 Commits are listed from least to most recent
289
289
290 You can reorder changesets by reordering the lines
290 You can reorder changesets by reordering the lines
291
291
292 Commands:
292 Commands:
293 """)
293 """)
294 actions = []
294 actions = []
295 def addverb(v):
295 def addverb(v):
296 a = actiontable[v]
296 a = actiontable[v]
297 lines = a.message.split("\n")
297 lines = a.message.split("\n")
298 if len(a.verbs):
298 if len(a.verbs):
299 v = ', '.join(sorted(a.verbs, key=lambda v: len(v)))
299 v = ', '.join(sorted(a.verbs, key=lambda v: len(v)))
300 actions.append(" %s = %s" % (v, lines[0]))
300 actions.append(" %s = %s" % (v, lines[0]))
301 actions.extend([' %s' for l in lines[1:]])
301 actions.extend([' %s' for l in lines[1:]])
302
302
303 for v in (
303 for v in (
304 sorted(primaryactions) +
304 sorted(primaryactions) +
305 sorted(secondaryactions) +
305 sorted(secondaryactions) +
306 sorted(tertiaryactions)
306 sorted(tertiaryactions)
307 ):
307 ):
308 addverb(v)
308 addverb(v)
309 actions.append('')
309 actions.append('')
310
310
311 hints = []
311 hints = []
312 if ui.configbool('histedit', 'dropmissing'):
312 if ui.configbool('histedit', 'dropmissing'):
313 hints.append("Deleting a changeset from the list "
313 hints.append("Deleting a changeset from the list "
314 "will DISCARD it from the edited history!")
314 "will DISCARD it from the edited history!")
315
315
316 lines = (intro % (first, last)).split('\n') + actions + hints
316 lines = (intro % (first, last)).split('\n') + actions + hints
317
317
318 return ''.join(['# %s\n' % l if l else '#\n' for l in lines])
318 return ''.join(['# %s\n' % l if l else '#\n' for l in lines])
319
319
320 class histeditstate(object):
320 class histeditstate(object):
321 def __init__(self, repo):
321 def __init__(self, repo):
322 self.repo = repo
322 self.repo = repo
323 self.actions = None
323 self.actions = None
324 self.keep = None
324 self.keep = None
325 self.topmost = None
325 self.topmost = None
326 self.parentctxnode = None
326 self.parentctxnode = None
327 self.lock = None
327 self.lock = None
328 self.wlock = None
328 self.wlock = None
329 self.backupfile = None
329 self.backupfile = None
330 self.stateobj = statemod.cmdstate(repo, 'histedit-state')
330 self.stateobj = statemod.cmdstate(repo, 'histedit-state')
331 self.replacements = []
331 self.replacements = []
332
332
333 def read(self):
333 def read(self):
334 """Load histedit state from disk and set fields appropriately."""
334 """Load histedit state from disk and set fields appropriately."""
335 if not self.stateobj.exists():
335 if not self.stateobj.exists():
336 cmdutil.wrongtooltocontinue(self.repo, _('histedit'))
336 cmdutil.wrongtooltocontinue(self.repo, _('histedit'))
337
337
338 data = self._read()
338 data = self._read()
339
339
340 self.parentctxnode = data['parentctxnode']
340 self.parentctxnode = data['parentctxnode']
341 actions = parserules(data['rules'], self)
341 actions = parserules(data['rules'], self)
342 self.actions = actions
342 self.actions = actions
343 self.keep = data['keep']
343 self.keep = data['keep']
344 self.topmost = data['topmost']
344 self.topmost = data['topmost']
345 self.replacements = data['replacements']
345 self.replacements = data['replacements']
346 self.backupfile = data['backupfile']
346 self.backupfile = data['backupfile']
347
347
348 def _read(self):
348 def _read(self):
349 fp = self.repo.vfs.read('histedit-state')
349 fp = self.repo.vfs.read('histedit-state')
350 if fp.startswith('v1\n'):
350 if fp.startswith('v1\n'):
351 data = self._load()
351 data = self._load()
352 parentctxnode, rules, keep, topmost, replacements, backupfile = data
352 parentctxnode, rules, keep, topmost, replacements, backupfile = data
353 else:
353 else:
354 data = pickle.loads(fp)
354 data = pickle.loads(fp)
355 parentctxnode, rules, keep, topmost, replacements = data
355 parentctxnode, rules, keep, topmost, replacements = data
356 backupfile = None
356 backupfile = None
357 rules = "\n".join(["%s %s" % (verb, rest) for [verb, rest] in rules])
357 rules = "\n".join(["%s %s" % (verb, rest) for [verb, rest] in rules])
358
358
359 return {'parentctxnode': parentctxnode, "rules": rules, "keep": keep,
359 return {'parentctxnode': parentctxnode, "rules": rules, "keep": keep,
360 "topmost": topmost, "replacements": replacements,
360 "topmost": topmost, "replacements": replacements,
361 "backupfile": backupfile}
361 "backupfile": backupfile}
362
362
363 def write(self, tr=None):
363 def write(self, tr=None):
364 if tr:
364 if tr:
365 tr.addfilegenerator('histedit-state', ('histedit-state',),
365 tr.addfilegenerator('histedit-state', ('histedit-state',),
366 self._write, location='plain')
366 self._write, location='plain')
367 else:
367 else:
368 with self.repo.vfs("histedit-state", "w") as f:
368 with self.repo.vfs("histedit-state", "w") as f:
369 self._write(f)
369 self._write(f)
370
370
371 def _write(self, fp):
371 def _write(self, fp):
372 fp.write('v1\n')
372 fp.write('v1\n')
373 fp.write('%s\n' % node.hex(self.parentctxnode))
373 fp.write('%s\n' % node.hex(self.parentctxnode))
374 fp.write('%s\n' % node.hex(self.topmost))
374 fp.write('%s\n' % node.hex(self.topmost))
375 fp.write('%s\n' % ('True' if self.keep else 'False'))
375 fp.write('%s\n' % ('True' if self.keep else 'False'))
376 fp.write('%d\n' % len(self.actions))
376 fp.write('%d\n' % len(self.actions))
377 for action in self.actions:
377 for action in self.actions:
378 fp.write('%s\n' % action.tostate())
378 fp.write('%s\n' % action.tostate())
379 fp.write('%d\n' % len(self.replacements))
379 fp.write('%d\n' % len(self.replacements))
380 for replacement in self.replacements:
380 for replacement in self.replacements:
381 fp.write('%s%s\n' % (node.hex(replacement[0]), ''.join(node.hex(r)
381 fp.write('%s%s\n' % (node.hex(replacement[0]), ''.join(node.hex(r)
382 for r in replacement[1])))
382 for r in replacement[1])))
383 backupfile = self.backupfile
383 backupfile = self.backupfile
384 if not backupfile:
384 if not backupfile:
385 backupfile = ''
385 backupfile = ''
386 fp.write('%s\n' % backupfile)
386 fp.write('%s\n' % backupfile)
387
387
388 def _load(self):
388 def _load(self):
389 fp = self.repo.vfs('histedit-state', 'r')
389 fp = self.repo.vfs('histedit-state', 'r')
390 lines = [l[:-1] for l in fp.readlines()]
390 lines = [l[:-1] for l in fp.readlines()]
391
391
392 index = 0
392 index = 0
393 lines[index] # version number
393 lines[index] # version number
394 index += 1
394 index += 1
395
395
396 parentctxnode = node.bin(lines[index])
396 parentctxnode = node.bin(lines[index])
397 index += 1
397 index += 1
398
398
399 topmost = node.bin(lines[index])
399 topmost = node.bin(lines[index])
400 index += 1
400 index += 1
401
401
402 keep = lines[index] == 'True'
402 keep = lines[index] == 'True'
403 index += 1
403 index += 1
404
404
405 # Rules
405 # Rules
406 rules = []
406 rules = []
407 rulelen = int(lines[index])
407 rulelen = int(lines[index])
408 index += 1
408 index += 1
409 for i in pycompat.xrange(rulelen):
409 for i in pycompat.xrange(rulelen):
410 ruleaction = lines[index]
410 ruleaction = lines[index]
411 index += 1
411 index += 1
412 rule = lines[index]
412 rule = lines[index]
413 index += 1
413 index += 1
414 rules.append((ruleaction, rule))
414 rules.append((ruleaction, rule))
415
415
416 # Replacements
416 # Replacements
417 replacements = []
417 replacements = []
418 replacementlen = int(lines[index])
418 replacementlen = int(lines[index])
419 index += 1
419 index += 1
420 for i in pycompat.xrange(replacementlen):
420 for i in pycompat.xrange(replacementlen):
421 replacement = lines[index]
421 replacement = lines[index]
422 original = node.bin(replacement[:40])
422 original = node.bin(replacement[:40])
423 succ = [node.bin(replacement[i:i + 40]) for i in
423 succ = [node.bin(replacement[i:i + 40]) for i in
424 range(40, len(replacement), 40)]
424 range(40, len(replacement), 40)]
425 replacements.append((original, succ))
425 replacements.append((original, succ))
426 index += 1
426 index += 1
427
427
428 backupfile = lines[index]
428 backupfile = lines[index]
429 index += 1
429 index += 1
430
430
431 fp.close()
431 fp.close()
432
432
433 return parentctxnode, rules, keep, topmost, replacements, backupfile
433 return parentctxnode, rules, keep, topmost, replacements, backupfile
434
434
435 def clear(self):
435 def clear(self):
436 if self.inprogress():
436 if self.inprogress():
437 self.repo.vfs.unlink('histedit-state')
437 self.repo.vfs.unlink('histedit-state')
438
438
439 def inprogress(self):
439 def inprogress(self):
440 return self.repo.vfs.exists('histedit-state')
440 return self.repo.vfs.exists('histedit-state')
441
441
442
442
443 class histeditaction(object):
443 class histeditaction(object):
444 def __init__(self, state, node):
444 def __init__(self, state, node):
445 self.state = state
445 self.state = state
446 self.repo = state.repo
446 self.repo = state.repo
447 self.node = node
447 self.node = node
448
448
449 @classmethod
449 @classmethod
450 def fromrule(cls, state, rule):
450 def fromrule(cls, state, rule):
451 """Parses the given rule, returning an instance of the histeditaction.
451 """Parses the given rule, returning an instance of the histeditaction.
452 """
452 """
453 ruleid = rule.strip().split(' ', 1)[0]
453 ruleid = rule.strip().split(' ', 1)[0]
454 # ruleid can be anything from rev numbers, hashes, "bookmarks" etc
454 # ruleid can be anything from rev numbers, hashes, "bookmarks" etc
455 # Check for validation of rule ids and get the rulehash
455 # Check for validation of rule ids and get the rulehash
456 try:
456 try:
457 rev = node.bin(ruleid)
457 rev = node.bin(ruleid)
458 except TypeError:
458 except TypeError:
459 try:
459 try:
460 _ctx = scmutil.revsingle(state.repo, ruleid)
460 _ctx = scmutil.revsingle(state.repo, ruleid)
461 rulehash = _ctx.hex()
461 rulehash = _ctx.hex()
462 rev = node.bin(rulehash)
462 rev = node.bin(rulehash)
463 except error.RepoLookupError:
463 except error.RepoLookupError:
464 raise error.ParseError(_("invalid changeset %s") % ruleid)
464 raise error.ParseError(_("invalid changeset %s") % ruleid)
465 return cls(state, rev)
465 return cls(state, rev)
466
466
467 def verify(self, prev, expected, seen):
467 def verify(self, prev, expected, seen):
468 """ Verifies semantic correctness of the rule"""
468 """ Verifies semantic correctness of the rule"""
469 repo = self.repo
469 repo = self.repo
470 ha = node.hex(self.node)
470 ha = node.hex(self.node)
471 self.node = scmutil.resolvehexnodeidprefix(repo, ha)
471 self.node = scmutil.resolvehexnodeidprefix(repo, ha)
472 if self.node is None:
472 if self.node is None:
473 raise error.ParseError(_('unknown changeset %s listed') % ha[:12])
473 raise error.ParseError(_('unknown changeset %s listed') % ha[:12])
474 self._verifynodeconstraints(prev, expected, seen)
474 self._verifynodeconstraints(prev, expected, seen)
475
475
476 def _verifynodeconstraints(self, prev, expected, seen):
476 def _verifynodeconstraints(self, prev, expected, seen):
477 # by default command need a node in the edited list
477 # by default command need a node in the edited list
478 if self.node not in expected:
478 if self.node not in expected:
479 raise error.ParseError(_('%s "%s" changeset was not a candidate')
479 raise error.ParseError(_('%s "%s" changeset was not a candidate')
480 % (self.verb, node.short(self.node)),
480 % (self.verb, node.short(self.node)),
481 hint=_('only use listed changesets'))
481 hint=_('only use listed changesets'))
482 # and only one command per node
482 # and only one command per node
483 if self.node in seen:
483 if self.node in seen:
484 raise error.ParseError(_('duplicated command for changeset %s') %
484 raise error.ParseError(_('duplicated command for changeset %s') %
485 node.short(self.node))
485 node.short(self.node))
486
486
487 def torule(self):
487 def torule(self):
488 """build a histedit rule line for an action
488 """build a histedit rule line for an action
489
489
490 by default lines are in the form:
490 by default lines are in the form:
491 <hash> <rev> <summary>
491 <hash> <rev> <summary>
492 """
492 """
493 ctx = self.repo[self.node]
493 ctx = self.repo[self.node]
494 ui = self.repo.ui
494 ui = self.repo.ui
495 summary = cmdutil.rendertemplate(
495 summary = cmdutil.rendertemplate(
496 ctx, ui.config('histedit', 'summary-template')) or ''
496 ctx, ui.config('histedit', 'summary-template')) or ''
497 summary = summary.splitlines()[0]
497 summary = summary.splitlines()[0]
498 line = '%s %s %s' % (self.verb, ctx, summary)
498 line = '%s %s %s' % (self.verb, ctx, summary)
499 # trim to 75 columns by default so it's not stupidly wide in my editor
499 # trim to 75 columns by default so it's not stupidly wide in my editor
500 # (the 5 more are left for verb)
500 # (the 5 more are left for verb)
501 maxlen = self.repo.ui.configint('histedit', 'linelen')
501 maxlen = self.repo.ui.configint('histedit', 'linelen')
502 maxlen = max(maxlen, 22) # avoid truncating hash
502 maxlen = max(maxlen, 22) # avoid truncating hash
503 return stringutil.ellipsis(line, maxlen)
503 return stringutil.ellipsis(line, maxlen)
504
504
505 def tostate(self):
505 def tostate(self):
506 """Print an action in format used by histedit state files
506 """Print an action in format used by histedit state files
507 (the first line is a verb, the remainder is the second)
507 (the first line is a verb, the remainder is the second)
508 """
508 """
509 return "%s\n%s" % (self.verb, node.hex(self.node))
509 return "%s\n%s" % (self.verb, node.hex(self.node))
510
510
511 def run(self):
511 def run(self):
512 """Runs the action. The default behavior is simply apply the action's
512 """Runs the action. The default behavior is simply apply the action's
513 rulectx onto the current parentctx."""
513 rulectx onto the current parentctx."""
514 self.applychange()
514 self.applychange()
515 self.continuedirty()
515 self.continuedirty()
516 return self.continueclean()
516 return self.continueclean()
517
517
518 def applychange(self):
518 def applychange(self):
519 """Applies the changes from this action's rulectx onto the current
519 """Applies the changes from this action's rulectx onto the current
520 parentctx, but does not commit them."""
520 parentctx, but does not commit them."""
521 repo = self.repo
521 repo = self.repo
522 rulectx = repo[self.node]
522 rulectx = repo[self.node]
523 repo.ui.pushbuffer(error=True, labeled=True)
523 repo.ui.pushbuffer(error=True, labeled=True)
524 hg.update(repo, self.state.parentctxnode, quietempty=True)
524 hg.update(repo, self.state.parentctxnode, quietempty=True)
525 repo.ui.popbuffer()
525 stats = applychanges(repo.ui, repo, rulectx, {})
526 stats = applychanges(repo.ui, repo, rulectx, {})
526 repo.dirstate.setbranch(rulectx.branch())
527 repo.dirstate.setbranch(rulectx.branch())
527 if stats.unresolvedcount:
528 if stats.unresolvedcount:
528 buf = repo.ui.popbuffer()
529 repo.ui.write(buf)
530 raise error.InterventionRequired(
529 raise error.InterventionRequired(
531 _('Fix up the change (%s %s)') %
530 _('Fix up the change (%s %s)') %
532 (self.verb, node.short(self.node)),
531 (self.verb, node.short(self.node)),
533 hint=_('hg histedit --continue to resume'))
532 hint=_('hg histedit --continue to resume'))
534 else:
535 repo.ui.popbuffer()
536
533
537 def continuedirty(self):
534 def continuedirty(self):
538 """Continues the action when changes have been applied to the working
535 """Continues the action when changes have been applied to the working
539 copy. The default behavior is to commit the dirty changes."""
536 copy. The default behavior is to commit the dirty changes."""
540 repo = self.repo
537 repo = self.repo
541 rulectx = repo[self.node]
538 rulectx = repo[self.node]
542
539
543 editor = self.commiteditor()
540 editor = self.commiteditor()
544 commit = commitfuncfor(repo, rulectx)
541 commit = commitfuncfor(repo, rulectx)
545 if repo.ui.configbool('rewrite', 'update-timestamp'):
542 if repo.ui.configbool('rewrite', 'update-timestamp'):
546 date = dateutil.makedate()
543 date = dateutil.makedate()
547 else:
544 else:
548 date = rulectx.date()
545 date = rulectx.date()
549 commit(text=rulectx.description(), user=rulectx.user(),
546 commit(text=rulectx.description(), user=rulectx.user(),
550 date=date, extra=rulectx.extra(), editor=editor)
547 date=date, extra=rulectx.extra(), editor=editor)
551
548
552 def commiteditor(self):
549 def commiteditor(self):
553 """The editor to be used to edit the commit message."""
550 """The editor to be used to edit the commit message."""
554 return False
551 return False
555
552
556 def continueclean(self):
553 def continueclean(self):
557 """Continues the action when the working copy is clean. The default
554 """Continues the action when the working copy is clean. The default
558 behavior is to accept the current commit as the new version of the
555 behavior is to accept the current commit as the new version of the
559 rulectx."""
556 rulectx."""
560 ctx = self.repo['.']
557 ctx = self.repo['.']
561 if ctx.node() == self.state.parentctxnode:
558 if ctx.node() == self.state.parentctxnode:
562 self.repo.ui.warn(_('%s: skipping changeset (no changes)\n') %
559 self.repo.ui.warn(_('%s: skipping changeset (no changes)\n') %
563 node.short(self.node))
560 node.short(self.node))
564 return ctx, [(self.node, tuple())]
561 return ctx, [(self.node, tuple())]
565 if ctx.node() == self.node:
562 if ctx.node() == self.node:
566 # Nothing changed
563 # Nothing changed
567 return ctx, []
564 return ctx, []
568 return ctx, [(self.node, (ctx.node(),))]
565 return ctx, [(self.node, (ctx.node(),))]
569
566
570 def commitfuncfor(repo, src):
567 def commitfuncfor(repo, src):
571 """Build a commit function for the replacement of <src>
568 """Build a commit function for the replacement of <src>
572
569
573 This function ensure we apply the same treatment to all changesets.
570 This function ensure we apply the same treatment to all changesets.
574
571
575 - Add a 'histedit_source' entry in extra.
572 - Add a 'histedit_source' entry in extra.
576
573
577 Note that fold has its own separated logic because its handling is a bit
574 Note that fold has its own separated logic because its handling is a bit
578 different and not easily factored out of the fold method.
575 different and not easily factored out of the fold method.
579 """
576 """
580 phasemin = src.phase()
577 phasemin = src.phase()
581 def commitfunc(**kwargs):
578 def commitfunc(**kwargs):
582 overrides = {('phases', 'new-commit'): phasemin}
579 overrides = {('phases', 'new-commit'): phasemin}
583 with repo.ui.configoverride(overrides, 'histedit'):
580 with repo.ui.configoverride(overrides, 'histedit'):
584 extra = kwargs.get(r'extra', {}).copy()
581 extra = kwargs.get(r'extra', {}).copy()
585 extra['histedit_source'] = src.hex()
582 extra['histedit_source'] = src.hex()
586 kwargs[r'extra'] = extra
583 kwargs[r'extra'] = extra
587 return repo.commit(**kwargs)
584 return repo.commit(**kwargs)
588 return commitfunc
585 return commitfunc
589
586
590 def applychanges(ui, repo, ctx, opts):
587 def applychanges(ui, repo, ctx, opts):
591 """Merge changeset from ctx (only) in the current working directory"""
588 """Merge changeset from ctx (only) in the current working directory"""
592 wcpar = repo.dirstate.p1()
589 wcpar = repo.dirstate.p1()
593 if ctx.p1().node() == wcpar:
590 if ctx.p1().node() == wcpar:
594 # edits are "in place" we do not need to make any merge,
591 # edits are "in place" we do not need to make any merge,
595 # just applies changes on parent for editing
592 # just applies changes on parent for editing
593 ui.pushbuffer()
596 cmdutil.revert(ui, repo, ctx, (wcpar, node.nullid), all=True)
594 cmdutil.revert(ui, repo, ctx, (wcpar, node.nullid), all=True)
597 stats = mergemod.updateresult(0, 0, 0, 0)
595 stats = mergemod.updateresult(0, 0, 0, 0)
596 ui.popbuffer()
598 else:
597 else:
599 try:
598 try:
600 # ui.forcemerge is an internal variable, do not document
599 # ui.forcemerge is an internal variable, do not document
601 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
600 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
602 'histedit')
601 'histedit')
603 stats = mergemod.graft(repo, ctx, ctx.p1(), ['local', 'histedit'])
602 stats = mergemod.graft(repo, ctx, ctx.p1(), ['local', 'histedit'])
604 finally:
603 finally:
605 repo.ui.setconfig('ui', 'forcemerge', '', 'histedit')
604 repo.ui.setconfig('ui', 'forcemerge', '', 'histedit')
606 return stats
605 return stats
607
606
608 def collapse(repo, firstctx, lastctx, commitopts, skipprompt=False):
607 def collapse(repo, firstctx, lastctx, commitopts, skipprompt=False):
609 """collapse the set of revisions from first to last as new one.
608 """collapse the set of revisions from first to last as new one.
610
609
611 Expected commit options are:
610 Expected commit options are:
612 - message
611 - message
613 - date
612 - date
614 - username
613 - username
615 Commit message is edited in all cases.
614 Commit message is edited in all cases.
616
615
617 This function works in memory."""
616 This function works in memory."""
618 ctxs = list(repo.set('%d::%d', firstctx.rev(), lastctx.rev()))
617 ctxs = list(repo.set('%d::%d', firstctx.rev(), lastctx.rev()))
619 if not ctxs:
618 if not ctxs:
620 return None
619 return None
621 for c in ctxs:
620 for c in ctxs:
622 if not c.mutable():
621 if not c.mutable():
623 raise error.ParseError(
622 raise error.ParseError(
624 _("cannot fold into public change %s") % node.short(c.node()))
623 _("cannot fold into public change %s") % node.short(c.node()))
625 base = firstctx.p1()
624 base = firstctx.p1()
626
625
627 # commit a new version of the old changeset, including the update
626 # commit a new version of the old changeset, including the update
628 # collect all files which might be affected
627 # collect all files which might be affected
629 files = set()
628 files = set()
630 for ctx in ctxs:
629 for ctx in ctxs:
631 files.update(ctx.files())
630 files.update(ctx.files())
632
631
633 # Recompute copies (avoid recording a -> b -> a)
632 # Recompute copies (avoid recording a -> b -> a)
634 copied = copies.pathcopies(base, lastctx)
633 copied = copies.pathcopies(base, lastctx)
635
634
636 # prune files which were reverted by the updates
635 # prune files which were reverted by the updates
637 files = [f for f in files if not cmdutil.samefile(f, lastctx, base)]
636 files = [f for f in files if not cmdutil.samefile(f, lastctx, base)]
638 # commit version of these files as defined by head
637 # commit version of these files as defined by head
639 headmf = lastctx.manifest()
638 headmf = lastctx.manifest()
640 def filectxfn(repo, ctx, path):
639 def filectxfn(repo, ctx, path):
641 if path in headmf:
640 if path in headmf:
642 fctx = lastctx[path]
641 fctx = lastctx[path]
643 flags = fctx.flags()
642 flags = fctx.flags()
644 mctx = context.memfilectx(repo, ctx,
643 mctx = context.memfilectx(repo, ctx,
645 fctx.path(), fctx.data(),
644 fctx.path(), fctx.data(),
646 islink='l' in flags,
645 islink='l' in flags,
647 isexec='x' in flags,
646 isexec='x' in flags,
648 copysource=copied.get(path))
647 copysource=copied.get(path))
649 return mctx
648 return mctx
650 return None
649 return None
651
650
652 if commitopts.get('message'):
651 if commitopts.get('message'):
653 message = commitopts['message']
652 message = commitopts['message']
654 else:
653 else:
655 message = firstctx.description()
654 message = firstctx.description()
656 user = commitopts.get('user')
655 user = commitopts.get('user')
657 date = commitopts.get('date')
656 date = commitopts.get('date')
658 extra = commitopts.get('extra')
657 extra = commitopts.get('extra')
659
658
660 parents = (firstctx.p1().node(), firstctx.p2().node())
659 parents = (firstctx.p1().node(), firstctx.p2().node())
661 editor = None
660 editor = None
662 if not skipprompt:
661 if not skipprompt:
663 editor = cmdutil.getcommiteditor(edit=True, editform='histedit.fold')
662 editor = cmdutil.getcommiteditor(edit=True, editform='histedit.fold')
664 new = context.memctx(repo,
663 new = context.memctx(repo,
665 parents=parents,
664 parents=parents,
666 text=message,
665 text=message,
667 files=files,
666 files=files,
668 filectxfn=filectxfn,
667 filectxfn=filectxfn,
669 user=user,
668 user=user,
670 date=date,
669 date=date,
671 extra=extra,
670 extra=extra,
672 editor=editor)
671 editor=editor)
673 return repo.commitctx(new)
672 return repo.commitctx(new)
674
673
675 def _isdirtywc(repo):
674 def _isdirtywc(repo):
676 return repo[None].dirty(missing=True)
675 return repo[None].dirty(missing=True)
677
676
678 def abortdirty():
677 def abortdirty():
679 raise error.Abort(_('working copy has pending changes'),
678 raise error.Abort(_('working copy has pending changes'),
680 hint=_('amend, commit, or revert them and run histedit '
679 hint=_('amend, commit, or revert them and run histedit '
681 '--continue, or abort with histedit --abort'))
680 '--continue, or abort with histedit --abort'))
682
681
683 def action(verbs, message, priority=False, internal=False):
682 def action(verbs, message, priority=False, internal=False):
684 def wrap(cls):
683 def wrap(cls):
685 assert not priority or not internal
684 assert not priority or not internal
686 verb = verbs[0]
685 verb = verbs[0]
687 if priority:
686 if priority:
688 primaryactions.add(verb)
687 primaryactions.add(verb)
689 elif internal:
688 elif internal:
690 internalactions.add(verb)
689 internalactions.add(verb)
691 elif len(verbs) > 1:
690 elif len(verbs) > 1:
692 secondaryactions.add(verb)
691 secondaryactions.add(verb)
693 else:
692 else:
694 tertiaryactions.add(verb)
693 tertiaryactions.add(verb)
695
694
696 cls.verb = verb
695 cls.verb = verb
697 cls.verbs = verbs
696 cls.verbs = verbs
698 cls.message = message
697 cls.message = message
699 for verb in verbs:
698 for verb in verbs:
700 actiontable[verb] = cls
699 actiontable[verb] = cls
701 return cls
700 return cls
702 return wrap
701 return wrap
703
702
704 @action(['pick', 'p'],
703 @action(['pick', 'p'],
705 _('use commit'),
704 _('use commit'),
706 priority=True)
705 priority=True)
707 class pick(histeditaction):
706 class pick(histeditaction):
708 def run(self):
707 def run(self):
709 rulectx = self.repo[self.node]
708 rulectx = self.repo[self.node]
710 if rulectx.p1().node() == self.state.parentctxnode:
709 if rulectx.p1().node() == self.state.parentctxnode:
711 self.repo.ui.debug('node %s unchanged\n' % node.short(self.node))
710 self.repo.ui.debug('node %s unchanged\n' % node.short(self.node))
712 return rulectx, []
711 return rulectx, []
713
712
714 return super(pick, self).run()
713 return super(pick, self).run()
715
714
716 @action(['edit', 'e'],
715 @action(['edit', 'e'],
717 _('use commit, but stop for amending'),
716 _('use commit, but stop for amending'),
718 priority=True)
717 priority=True)
719 class edit(histeditaction):
718 class edit(histeditaction):
720 def run(self):
719 def run(self):
721 repo = self.repo
720 repo = self.repo
722 rulectx = repo[self.node]
721 rulectx = repo[self.node]
723 hg.update(repo, self.state.parentctxnode, quietempty=True)
722 hg.update(repo, self.state.parentctxnode, quietempty=True)
724 applychanges(repo.ui, repo, rulectx, {})
723 applychanges(repo.ui, repo, rulectx, {})
725 raise error.InterventionRequired(
724 raise error.InterventionRequired(
726 _('Editing (%s), you may commit or record as needed now.')
725 _('Editing (%s), you may commit or record as needed now.')
727 % node.short(self.node),
726 % node.short(self.node),
728 hint=_('hg histedit --continue to resume'))
727 hint=_('hg histedit --continue to resume'))
729
728
730 def commiteditor(self):
729 def commiteditor(self):
731 return cmdutil.getcommiteditor(edit=True, editform='histedit.edit')
730 return cmdutil.getcommiteditor(edit=True, editform='histedit.edit')
732
731
733 @action(['fold', 'f'],
732 @action(['fold', 'f'],
734 _('use commit, but combine it with the one above'))
733 _('use commit, but combine it with the one above'))
735 class fold(histeditaction):
734 class fold(histeditaction):
736 def verify(self, prev, expected, seen):
735 def verify(self, prev, expected, seen):
737 """ Verifies semantic correctness of the fold rule"""
736 """ Verifies semantic correctness of the fold rule"""
738 super(fold, self).verify(prev, expected, seen)
737 super(fold, self).verify(prev, expected, seen)
739 repo = self.repo
738 repo = self.repo
740 if not prev:
739 if not prev:
741 c = repo[self.node].p1()
740 c = repo[self.node].p1()
742 elif not prev.verb in ('pick', 'base'):
741 elif not prev.verb in ('pick', 'base'):
743 return
742 return
744 else:
743 else:
745 c = repo[prev.node]
744 c = repo[prev.node]
746 if not c.mutable():
745 if not c.mutable():
747 raise error.ParseError(
746 raise error.ParseError(
748 _("cannot fold into public change %s") % node.short(c.node()))
747 _("cannot fold into public change %s") % node.short(c.node()))
749
748
750
749
751 def continuedirty(self):
750 def continuedirty(self):
752 repo = self.repo
751 repo = self.repo
753 rulectx = repo[self.node]
752 rulectx = repo[self.node]
754
753
755 commit = commitfuncfor(repo, rulectx)
754 commit = commitfuncfor(repo, rulectx)
756 commit(text='fold-temp-revision %s' % node.short(self.node),
755 commit(text='fold-temp-revision %s' % node.short(self.node),
757 user=rulectx.user(), date=rulectx.date(),
756 user=rulectx.user(), date=rulectx.date(),
758 extra=rulectx.extra())
757 extra=rulectx.extra())
759
758
760 def continueclean(self):
759 def continueclean(self):
761 repo = self.repo
760 repo = self.repo
762 ctx = repo['.']
761 ctx = repo['.']
763 rulectx = repo[self.node]
762 rulectx = repo[self.node]
764 parentctxnode = self.state.parentctxnode
763 parentctxnode = self.state.parentctxnode
765 if ctx.node() == parentctxnode:
764 if ctx.node() == parentctxnode:
766 repo.ui.warn(_('%s: empty changeset\n') %
765 repo.ui.warn(_('%s: empty changeset\n') %
767 node.short(self.node))
766 node.short(self.node))
768 return ctx, [(self.node, (parentctxnode,))]
767 return ctx, [(self.node, (parentctxnode,))]
769
768
770 parentctx = repo[parentctxnode]
769 parentctx = repo[parentctxnode]
771 newcommits = set(c.node() for c in repo.set('(%d::. - %d)',
770 newcommits = set(c.node() for c in repo.set('(%d::. - %d)',
772 parentctx.rev(),
771 parentctx.rev(),
773 parentctx.rev()))
772 parentctx.rev()))
774 if not newcommits:
773 if not newcommits:
775 repo.ui.warn(_('%s: cannot fold - working copy is not a '
774 repo.ui.warn(_('%s: cannot fold - working copy is not a '
776 'descendant of previous commit %s\n') %
775 'descendant of previous commit %s\n') %
777 (node.short(self.node), node.short(parentctxnode)))
776 (node.short(self.node), node.short(parentctxnode)))
778 return ctx, [(self.node, (ctx.node(),))]
777 return ctx, [(self.node, (ctx.node(),))]
779
778
780 middlecommits = newcommits.copy()
779 middlecommits = newcommits.copy()
781 middlecommits.discard(ctx.node())
780 middlecommits.discard(ctx.node())
782
781
783 return self.finishfold(repo.ui, repo, parentctx, rulectx, ctx.node(),
782 return self.finishfold(repo.ui, repo, parentctx, rulectx, ctx.node(),
784 middlecommits)
783 middlecommits)
785
784
786 def skipprompt(self):
785 def skipprompt(self):
787 """Returns true if the rule should skip the message editor.
786 """Returns true if the rule should skip the message editor.
788
787
789 For example, 'fold' wants to show an editor, but 'rollup'
788 For example, 'fold' wants to show an editor, but 'rollup'
790 doesn't want to.
789 doesn't want to.
791 """
790 """
792 return False
791 return False
793
792
794 def mergedescs(self):
793 def mergedescs(self):
795 """Returns true if the rule should merge messages of multiple changes.
794 """Returns true if the rule should merge messages of multiple changes.
796
795
797 This exists mainly so that 'rollup' rules can be a subclass of
796 This exists mainly so that 'rollup' rules can be a subclass of
798 'fold'.
797 'fold'.
799 """
798 """
800 return True
799 return True
801
800
802 def firstdate(self):
801 def firstdate(self):
803 """Returns true if the rule should preserve the date of the first
802 """Returns true if the rule should preserve the date of the first
804 change.
803 change.
805
804
806 This exists mainly so that 'rollup' rules can be a subclass of
805 This exists mainly so that 'rollup' rules can be a subclass of
807 'fold'.
806 'fold'.
808 """
807 """
809 return False
808 return False
810
809
811 def finishfold(self, ui, repo, ctx, oldctx, newnode, internalchanges):
810 def finishfold(self, ui, repo, ctx, oldctx, newnode, internalchanges):
812 parent = ctx.p1().node()
811 parent = ctx.p1().node()
813 hg.updaterepo(repo, parent, overwrite=False)
812 hg.updaterepo(repo, parent, overwrite=False)
814 ### prepare new commit data
813 ### prepare new commit data
815 commitopts = {}
814 commitopts = {}
816 commitopts['user'] = ctx.user()
815 commitopts['user'] = ctx.user()
817 # commit message
816 # commit message
818 if not self.mergedescs():
817 if not self.mergedescs():
819 newmessage = ctx.description()
818 newmessage = ctx.description()
820 else:
819 else:
821 newmessage = '\n***\n'.join(
820 newmessage = '\n***\n'.join(
822 [ctx.description()] +
821 [ctx.description()] +
823 [repo[r].description() for r in internalchanges] +
822 [repo[r].description() for r in internalchanges] +
824 [oldctx.description()]) + '\n'
823 [oldctx.description()]) + '\n'
825 commitopts['message'] = newmessage
824 commitopts['message'] = newmessage
826 # date
825 # date
827 if self.firstdate():
826 if self.firstdate():
828 commitopts['date'] = ctx.date()
827 commitopts['date'] = ctx.date()
829 else:
828 else:
830 commitopts['date'] = max(ctx.date(), oldctx.date())
829 commitopts['date'] = max(ctx.date(), oldctx.date())
831 # if date is to be updated to current
830 # if date is to be updated to current
832 if ui.configbool('rewrite', 'update-timestamp'):
831 if ui.configbool('rewrite', 'update-timestamp'):
833 commitopts['date'] = dateutil.makedate()
832 commitopts['date'] = dateutil.makedate()
834
833
835 extra = ctx.extra().copy()
834 extra = ctx.extra().copy()
836 # histedit_source
835 # histedit_source
837 # note: ctx is likely a temporary commit but that the best we can do
836 # note: ctx is likely a temporary commit but that the best we can do
838 # here. This is sufficient to solve issue3681 anyway.
837 # here. This is sufficient to solve issue3681 anyway.
839 extra['histedit_source'] = '%s,%s' % (ctx.hex(), oldctx.hex())
838 extra['histedit_source'] = '%s,%s' % (ctx.hex(), oldctx.hex())
840 commitopts['extra'] = extra
839 commitopts['extra'] = extra
841 phasemin = max(ctx.phase(), oldctx.phase())
840 phasemin = max(ctx.phase(), oldctx.phase())
842 overrides = {('phases', 'new-commit'): phasemin}
841 overrides = {('phases', 'new-commit'): phasemin}
843 with repo.ui.configoverride(overrides, 'histedit'):
842 with repo.ui.configoverride(overrides, 'histedit'):
844 n = collapse(repo, ctx, repo[newnode], commitopts,
843 n = collapse(repo, ctx, repo[newnode], commitopts,
845 skipprompt=self.skipprompt())
844 skipprompt=self.skipprompt())
846 if n is None:
845 if n is None:
847 return ctx, []
846 return ctx, []
848 hg.updaterepo(repo, n, overwrite=False)
847 hg.updaterepo(repo, n, overwrite=False)
849 replacements = [(oldctx.node(), (newnode,)),
848 replacements = [(oldctx.node(), (newnode,)),
850 (ctx.node(), (n,)),
849 (ctx.node(), (n,)),
851 (newnode, (n,)),
850 (newnode, (n,)),
852 ]
851 ]
853 for ich in internalchanges:
852 for ich in internalchanges:
854 replacements.append((ich, (n,)))
853 replacements.append((ich, (n,)))
855 return repo[n], replacements
854 return repo[n], replacements
856
855
857 @action(['base', 'b'],
856 @action(['base', 'b'],
858 _('checkout changeset and apply further changesets from there'))
857 _('checkout changeset and apply further changesets from there'))
859 class base(histeditaction):
858 class base(histeditaction):
860
859
861 def run(self):
860 def run(self):
862 if self.repo['.'].node() != self.node:
861 if self.repo['.'].node() != self.node:
863 mergemod.update(self.repo, self.node, branchmerge=False, force=True)
862 mergemod.update(self.repo, self.node, branchmerge=False, force=True)
864 return self.continueclean()
863 return self.continueclean()
865
864
866 def continuedirty(self):
865 def continuedirty(self):
867 abortdirty()
866 abortdirty()
868
867
869 def continueclean(self):
868 def continueclean(self):
870 basectx = self.repo['.']
869 basectx = self.repo['.']
871 return basectx, []
870 return basectx, []
872
871
873 def _verifynodeconstraints(self, prev, expected, seen):
872 def _verifynodeconstraints(self, prev, expected, seen):
874 # base can only be use with a node not in the edited set
873 # base can only be use with a node not in the edited set
875 if self.node in expected:
874 if self.node in expected:
876 msg = _('%s "%s" changeset was an edited list candidate')
875 msg = _('%s "%s" changeset was an edited list candidate')
877 raise error.ParseError(
876 raise error.ParseError(
878 msg % (self.verb, node.short(self.node)),
877 msg % (self.verb, node.short(self.node)),
879 hint=_('base must only use unlisted changesets'))
878 hint=_('base must only use unlisted changesets'))
880
879
881 @action(['_multifold'],
880 @action(['_multifold'],
882 _(
881 _(
883 """fold subclass used for when multiple folds happen in a row
882 """fold subclass used for when multiple folds happen in a row
884
883
885 We only want to fire the editor for the folded message once when
884 We only want to fire the editor for the folded message once when
886 (say) four changes are folded down into a single change. This is
885 (say) four changes are folded down into a single change. This is
887 similar to rollup, but we should preserve both messages so that
886 similar to rollup, but we should preserve both messages so that
888 when the last fold operation runs we can show the user all the
887 when the last fold operation runs we can show the user all the
889 commit messages in their editor.
888 commit messages in their editor.
890 """),
889 """),
891 internal=True)
890 internal=True)
892 class _multifold(fold):
891 class _multifold(fold):
893 def skipprompt(self):
892 def skipprompt(self):
894 return True
893 return True
895
894
896 @action(["roll", "r"],
895 @action(["roll", "r"],
897 _("like fold, but discard this commit's description and date"))
896 _("like fold, but discard this commit's description and date"))
898 class rollup(fold):
897 class rollup(fold):
899 def mergedescs(self):
898 def mergedescs(self):
900 return False
899 return False
901
900
902 def skipprompt(self):
901 def skipprompt(self):
903 return True
902 return True
904
903
905 def firstdate(self):
904 def firstdate(self):
906 return True
905 return True
907
906
908 @action(["drop", "d"],
907 @action(["drop", "d"],
909 _('remove commit from history'))
908 _('remove commit from history'))
910 class drop(histeditaction):
909 class drop(histeditaction):
911 def run(self):
910 def run(self):
912 parentctx = self.repo[self.state.parentctxnode]
911 parentctx = self.repo[self.state.parentctxnode]
913 return parentctx, [(self.node, tuple())]
912 return parentctx, [(self.node, tuple())]
914
913
915 @action(["mess", "m"],
914 @action(["mess", "m"],
916 _('edit commit message without changing commit content'),
915 _('edit commit message without changing commit content'),
917 priority=True)
916 priority=True)
918 class message(histeditaction):
917 class message(histeditaction):
919 def commiteditor(self):
918 def commiteditor(self):
920 return cmdutil.getcommiteditor(edit=True, editform='histedit.mess')
919 return cmdutil.getcommiteditor(edit=True, editform='histedit.mess')
921
920
922 def findoutgoing(ui, repo, remote=None, force=False, opts=None):
921 def findoutgoing(ui, repo, remote=None, force=False, opts=None):
923 """utility function to find the first outgoing changeset
922 """utility function to find the first outgoing changeset
924
923
925 Used by initialization code"""
924 Used by initialization code"""
926 if opts is None:
925 if opts is None:
927 opts = {}
926 opts = {}
928 dest = ui.expandpath(remote or 'default-push', remote or 'default')
927 dest = ui.expandpath(remote or 'default-push', remote or 'default')
929 dest, branches = hg.parseurl(dest, None)[:2]
928 dest, branches = hg.parseurl(dest, None)[:2]
930 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
929 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
931
930
932 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
931 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
933 other = hg.peer(repo, opts, dest)
932 other = hg.peer(repo, opts, dest)
934
933
935 if revs:
934 if revs:
936 revs = [repo.lookup(rev) for rev in revs]
935 revs = [repo.lookup(rev) for rev in revs]
937
936
938 outgoing = discovery.findcommonoutgoing(repo, other, revs, force=force)
937 outgoing = discovery.findcommonoutgoing(repo, other, revs, force=force)
939 if not outgoing.missing:
938 if not outgoing.missing:
940 raise error.Abort(_('no outgoing ancestors'))
939 raise error.Abort(_('no outgoing ancestors'))
941 roots = list(repo.revs("roots(%ln)", outgoing.missing))
940 roots = list(repo.revs("roots(%ln)", outgoing.missing))
942 if len(roots) > 1:
941 if len(roots) > 1:
943 msg = _('there are ambiguous outgoing revisions')
942 msg = _('there are ambiguous outgoing revisions')
944 hint = _("see 'hg help histedit' for more detail")
943 hint = _("see 'hg help histedit' for more detail")
945 raise error.Abort(msg, hint=hint)
944 raise error.Abort(msg, hint=hint)
946 return repo[roots[0]].node()
945 return repo[roots[0]].node()
947
946
948 # Curses Support
947 # Curses Support
949 try:
948 try:
950 import curses
949 import curses
951
950
952 # Curses requires setting the locale or it will default to the C
951 # Curses requires setting the locale or it will default to the C
953 # locale. This sets the locale to the user's default system
952 # locale. This sets the locale to the user's default system
954 # locale.
953 # locale.
955 import locale
954 import locale
956 locale.setlocale(locale.LC_ALL, r'')
955 locale.setlocale(locale.LC_ALL, r'')
957 except ImportError:
956 except ImportError:
958 curses = None
957 curses = None
959
958
960 KEY_LIST = ['pick', 'edit', 'fold', 'drop', 'mess', 'roll']
959 KEY_LIST = ['pick', 'edit', 'fold', 'drop', 'mess', 'roll']
961 ACTION_LABELS = {
960 ACTION_LABELS = {
962 'fold': '^fold',
961 'fold': '^fold',
963 'roll': '^roll',
962 'roll': '^roll',
964 }
963 }
965
964
966 COLOR_HELP, COLOR_SELECTED, COLOR_OK, COLOR_WARN, COLOR_CURRENT = 1, 2, 3, 4, 5
965 COLOR_HELP, COLOR_SELECTED, COLOR_OK, COLOR_WARN, COLOR_CURRENT = 1, 2, 3, 4, 5
967
966
968 E_QUIT, E_HISTEDIT = 1, 2
967 E_QUIT, E_HISTEDIT = 1, 2
969 E_PAGEDOWN, E_PAGEUP, E_LINEUP, E_LINEDOWN, E_RESIZE = 3, 4, 5, 6, 7
968 E_PAGEDOWN, E_PAGEUP, E_LINEUP, E_LINEDOWN, E_RESIZE = 3, 4, 5, 6, 7
970 MODE_INIT, MODE_PATCH, MODE_RULES, MODE_HELP = 0, 1, 2, 3
969 MODE_INIT, MODE_PATCH, MODE_RULES, MODE_HELP = 0, 1, 2, 3
971
970
972 KEYTABLE = {
971 KEYTABLE = {
973 'global': {
972 'global': {
974 'h': 'next-action',
973 'h': 'next-action',
975 'KEY_RIGHT': 'next-action',
974 'KEY_RIGHT': 'next-action',
976 'l': 'prev-action',
975 'l': 'prev-action',
977 'KEY_LEFT': 'prev-action',
976 'KEY_LEFT': 'prev-action',
978 'q': 'quit',
977 'q': 'quit',
979 'c': 'histedit',
978 'c': 'histedit',
980 'C': 'histedit',
979 'C': 'histedit',
981 'v': 'showpatch',
980 'v': 'showpatch',
982 '?': 'help',
981 '?': 'help',
983 },
982 },
984 MODE_RULES: {
983 MODE_RULES: {
985 'd': 'action-drop',
984 'd': 'action-drop',
986 'e': 'action-edit',
985 'e': 'action-edit',
987 'f': 'action-fold',
986 'f': 'action-fold',
988 'm': 'action-mess',
987 'm': 'action-mess',
989 'p': 'action-pick',
988 'p': 'action-pick',
990 'r': 'action-roll',
989 'r': 'action-roll',
991 ' ': 'select',
990 ' ': 'select',
992 'j': 'down',
991 'j': 'down',
993 'k': 'up',
992 'k': 'up',
994 'KEY_DOWN': 'down',
993 'KEY_DOWN': 'down',
995 'KEY_UP': 'up',
994 'KEY_UP': 'up',
996 'J': 'move-down',
995 'J': 'move-down',
997 'K': 'move-up',
996 'K': 'move-up',
998 'KEY_NPAGE': 'move-down',
997 'KEY_NPAGE': 'move-down',
999 'KEY_PPAGE': 'move-up',
998 'KEY_PPAGE': 'move-up',
1000 '0': 'goto', # Used for 0..9
999 '0': 'goto', # Used for 0..9
1001 },
1000 },
1002 MODE_PATCH: {
1001 MODE_PATCH: {
1003 ' ': 'page-down',
1002 ' ': 'page-down',
1004 'KEY_NPAGE': 'page-down',
1003 'KEY_NPAGE': 'page-down',
1005 'KEY_PPAGE': 'page-up',
1004 'KEY_PPAGE': 'page-up',
1006 'j': 'line-down',
1005 'j': 'line-down',
1007 'k': 'line-up',
1006 'k': 'line-up',
1008 'KEY_DOWN': 'line-down',
1007 'KEY_DOWN': 'line-down',
1009 'KEY_UP': 'line-up',
1008 'KEY_UP': 'line-up',
1010 'J': 'down',
1009 'J': 'down',
1011 'K': 'up',
1010 'K': 'up',
1012 },
1011 },
1013 MODE_HELP: {
1012 MODE_HELP: {
1014 },
1013 },
1015 }
1014 }
1016
1015
1017 def screen_size():
1016 def screen_size():
1018 return struct.unpack('hh', fcntl.ioctl(1, termios.TIOCGWINSZ, ' '))
1017 return struct.unpack('hh', fcntl.ioctl(1, termios.TIOCGWINSZ, ' '))
1019
1018
1020 class histeditrule(object):
1019 class histeditrule(object):
1021 def __init__(self, ctx, pos, action='pick'):
1020 def __init__(self, ctx, pos, action='pick'):
1022 self.ctx = ctx
1021 self.ctx = ctx
1023 self.action = action
1022 self.action = action
1024 self.origpos = pos
1023 self.origpos = pos
1025 self.pos = pos
1024 self.pos = pos
1026 self.conflicts = []
1025 self.conflicts = []
1027
1026
1028 def __str__(self):
1027 def __str__(self):
1029 # Some actions ('fold' and 'roll') combine a patch with a previous one.
1028 # Some actions ('fold' and 'roll') combine a patch with a previous one.
1030 # Add a marker showing which patch they apply to, and also omit the
1029 # Add a marker showing which patch they apply to, and also omit the
1031 # description for 'roll' (since it will get discarded). Example display:
1030 # description for 'roll' (since it will get discarded). Example display:
1032 #
1031 #
1033 # #10 pick 316392:06a16c25c053 add option to skip tests
1032 # #10 pick 316392:06a16c25c053 add option to skip tests
1034 # #11 ^roll 316393:71313c964cc5
1033 # #11 ^roll 316393:71313c964cc5
1035 # #12 pick 316394:ab31f3973b0d include mfbt for mozilla-config.h
1034 # #12 pick 316394:ab31f3973b0d include mfbt for mozilla-config.h
1036 # #13 ^fold 316395:14ce5803f4c3 fix warnings
1035 # #13 ^fold 316395:14ce5803f4c3 fix warnings
1037 #
1036 #
1038 # The carets point to the changeset being folded into ("roll this
1037 # The carets point to the changeset being folded into ("roll this
1039 # changeset into the changeset above").
1038 # changeset into the changeset above").
1040 action = ACTION_LABELS.get(self.action, self.action)
1039 action = ACTION_LABELS.get(self.action, self.action)
1041 h = self.ctx.hex()[0:12]
1040 h = self.ctx.hex()[0:12]
1042 r = self.ctx.rev()
1041 r = self.ctx.rev()
1043 desc = self.ctx.description().splitlines()[0].strip()
1042 desc = self.ctx.description().splitlines()[0].strip()
1044 if self.action == 'roll':
1043 if self.action == 'roll':
1045 desc = ''
1044 desc = ''
1046 return "#{0:<2} {1:<6} {2}:{3} {4}".format(
1045 return "#{0:<2} {1:<6} {2}:{3} {4}".format(
1047 self.origpos, action, r, h, desc)
1046 self.origpos, action, r, h, desc)
1048
1047
1049 def checkconflicts(self, other):
1048 def checkconflicts(self, other):
1050 if other.pos > self.pos and other.origpos <= self.origpos:
1049 if other.pos > self.pos and other.origpos <= self.origpos:
1051 if set(other.ctx.files()) & set(self.ctx.files()) != set():
1050 if set(other.ctx.files()) & set(self.ctx.files()) != set():
1052 self.conflicts.append(other)
1051 self.conflicts.append(other)
1053 return self.conflicts
1052 return self.conflicts
1054
1053
1055 if other in self.conflicts:
1054 if other in self.conflicts:
1056 self.conflicts.remove(other)
1055 self.conflicts.remove(other)
1057 return self.conflicts
1056 return self.conflicts
1058
1057
1059 # ============ EVENTS ===============
1058 # ============ EVENTS ===============
1060 def movecursor(state, oldpos, newpos):
1059 def movecursor(state, oldpos, newpos):
1061 '''Change the rule/changeset that the cursor is pointing to, regardless of
1060 '''Change the rule/changeset that the cursor is pointing to, regardless of
1062 current mode (you can switch between patches from the view patch window).'''
1061 current mode (you can switch between patches from the view patch window).'''
1063 state['pos'] = newpos
1062 state['pos'] = newpos
1064
1063
1065 mode, _ = state['mode']
1064 mode, _ = state['mode']
1066 if mode == MODE_RULES:
1065 if mode == MODE_RULES:
1067 # Scroll through the list by updating the view for MODE_RULES, so that
1066 # Scroll through the list by updating the view for MODE_RULES, so that
1068 # even if we are not currently viewing the rules, switching back will
1067 # even if we are not currently viewing the rules, switching back will
1069 # result in the cursor's rule being visible.
1068 # result in the cursor's rule being visible.
1070 modestate = state['modes'][MODE_RULES]
1069 modestate = state['modes'][MODE_RULES]
1071 if newpos < modestate['line_offset']:
1070 if newpos < modestate['line_offset']:
1072 modestate['line_offset'] = newpos
1071 modestate['line_offset'] = newpos
1073 elif newpos > modestate['line_offset'] + state['page_height'] - 1:
1072 elif newpos > modestate['line_offset'] + state['page_height'] - 1:
1074 modestate['line_offset'] = newpos - state['page_height'] + 1
1073 modestate['line_offset'] = newpos - state['page_height'] + 1
1075
1074
1076 # Reset the patch view region to the top of the new patch.
1075 # Reset the patch view region to the top of the new patch.
1077 state['modes'][MODE_PATCH]['line_offset'] = 0
1076 state['modes'][MODE_PATCH]['line_offset'] = 0
1078
1077
1079 def changemode(state, mode):
1078 def changemode(state, mode):
1080 curmode, _ = state['mode']
1079 curmode, _ = state['mode']
1081 state['mode'] = (mode, curmode)
1080 state['mode'] = (mode, curmode)
1082
1081
1083 def makeselection(state, pos):
1082 def makeselection(state, pos):
1084 state['selected'] = pos
1083 state['selected'] = pos
1085
1084
1086 def swap(state, oldpos, newpos):
1085 def swap(state, oldpos, newpos):
1087 """Swap two positions and calculate necessary conflicts in
1086 """Swap two positions and calculate necessary conflicts in
1088 O(|newpos-oldpos|) time"""
1087 O(|newpos-oldpos|) time"""
1089
1088
1090 rules = state['rules']
1089 rules = state['rules']
1091 assert 0 <= oldpos < len(rules) and 0 <= newpos < len(rules)
1090 assert 0 <= oldpos < len(rules) and 0 <= newpos < len(rules)
1092
1091
1093 rules[oldpos], rules[newpos] = rules[newpos], rules[oldpos]
1092 rules[oldpos], rules[newpos] = rules[newpos], rules[oldpos]
1094
1093
1095 # TODO: swap should not know about histeditrule's internals
1094 # TODO: swap should not know about histeditrule's internals
1096 rules[newpos].pos = newpos
1095 rules[newpos].pos = newpos
1097 rules[oldpos].pos = oldpos
1096 rules[oldpos].pos = oldpos
1098
1097
1099 start = min(oldpos, newpos)
1098 start = min(oldpos, newpos)
1100 end = max(oldpos, newpos)
1099 end = max(oldpos, newpos)
1101 for r in pycompat.xrange(start, end + 1):
1100 for r in pycompat.xrange(start, end + 1):
1102 rules[newpos].checkconflicts(rules[r])
1101 rules[newpos].checkconflicts(rules[r])
1103 rules[oldpos].checkconflicts(rules[r])
1102 rules[oldpos].checkconflicts(rules[r])
1104
1103
1105 if state['selected']:
1104 if state['selected']:
1106 makeselection(state, newpos)
1105 makeselection(state, newpos)
1107
1106
1108 def changeaction(state, pos, action):
1107 def changeaction(state, pos, action):
1109 """Change the action state on the given position to the new action"""
1108 """Change the action state on the given position to the new action"""
1110 rules = state['rules']
1109 rules = state['rules']
1111 assert 0 <= pos < len(rules)
1110 assert 0 <= pos < len(rules)
1112 rules[pos].action = action
1111 rules[pos].action = action
1113
1112
1114 def cycleaction(state, pos, next=False):
1113 def cycleaction(state, pos, next=False):
1115 """Changes the action state the next or the previous action from
1114 """Changes the action state the next or the previous action from
1116 the action list"""
1115 the action list"""
1117 rules = state['rules']
1116 rules = state['rules']
1118 assert 0 <= pos < len(rules)
1117 assert 0 <= pos < len(rules)
1119 current = rules[pos].action
1118 current = rules[pos].action
1120
1119
1121 assert current in KEY_LIST
1120 assert current in KEY_LIST
1122
1121
1123 index = KEY_LIST.index(current)
1122 index = KEY_LIST.index(current)
1124 if next:
1123 if next:
1125 index += 1
1124 index += 1
1126 else:
1125 else:
1127 index -= 1
1126 index -= 1
1128 changeaction(state, pos, KEY_LIST[index % len(KEY_LIST)])
1127 changeaction(state, pos, KEY_LIST[index % len(KEY_LIST)])
1129
1128
1130 def changeview(state, delta, unit):
1129 def changeview(state, delta, unit):
1131 '''Change the region of whatever is being viewed (a patch or the list of
1130 '''Change the region of whatever is being viewed (a patch or the list of
1132 changesets). 'delta' is an amount (+/- 1) and 'unit' is 'page' or 'line'.'''
1131 changesets). 'delta' is an amount (+/- 1) and 'unit' is 'page' or 'line'.'''
1133 mode, _ = state['mode']
1132 mode, _ = state['mode']
1134 if mode != MODE_PATCH:
1133 if mode != MODE_PATCH:
1135 return
1134 return
1136 mode_state = state['modes'][mode]
1135 mode_state = state['modes'][mode]
1137 num_lines = len(patchcontents(state))
1136 num_lines = len(patchcontents(state))
1138 page_height = state['page_height']
1137 page_height = state['page_height']
1139 unit = page_height if unit == 'page' else 1
1138 unit = page_height if unit == 'page' else 1
1140 num_pages = 1 + (num_lines - 1) / page_height
1139 num_pages = 1 + (num_lines - 1) / page_height
1141 max_offset = (num_pages - 1) * page_height
1140 max_offset = (num_pages - 1) * page_height
1142 newline = mode_state['line_offset'] + delta * unit
1141 newline = mode_state['line_offset'] + delta * unit
1143 mode_state['line_offset'] = max(0, min(max_offset, newline))
1142 mode_state['line_offset'] = max(0, min(max_offset, newline))
1144
1143
1145 def event(state, ch):
1144 def event(state, ch):
1146 """Change state based on the current character input
1145 """Change state based on the current character input
1147
1146
1148 This takes the current state and based on the current character input from
1147 This takes the current state and based on the current character input from
1149 the user we change the state.
1148 the user we change the state.
1150 """
1149 """
1151 selected = state['selected']
1150 selected = state['selected']
1152 oldpos = state['pos']
1151 oldpos = state['pos']
1153 rules = state['rules']
1152 rules = state['rules']
1154
1153
1155 if ch in (curses.KEY_RESIZE, "KEY_RESIZE"):
1154 if ch in (curses.KEY_RESIZE, "KEY_RESIZE"):
1156 return E_RESIZE
1155 return E_RESIZE
1157
1156
1158 lookup_ch = ch
1157 lookup_ch = ch
1159 if '0' <= ch <= '9':
1158 if '0' <= ch <= '9':
1160 lookup_ch = '0'
1159 lookup_ch = '0'
1161
1160
1162 curmode, prevmode = state['mode']
1161 curmode, prevmode = state['mode']
1163 action = KEYTABLE[curmode].get(lookup_ch, KEYTABLE['global'].get(lookup_ch))
1162 action = KEYTABLE[curmode].get(lookup_ch, KEYTABLE['global'].get(lookup_ch))
1164 if action is None:
1163 if action is None:
1165 return
1164 return
1166 if action in ('down', 'move-down'):
1165 if action in ('down', 'move-down'):
1167 newpos = min(oldpos + 1, len(rules) - 1)
1166 newpos = min(oldpos + 1, len(rules) - 1)
1168 movecursor(state, oldpos, newpos)
1167 movecursor(state, oldpos, newpos)
1169 if selected is not None or action == 'move-down':
1168 if selected is not None or action == 'move-down':
1170 swap(state, oldpos, newpos)
1169 swap(state, oldpos, newpos)
1171 elif action in ('up', 'move-up'):
1170 elif action in ('up', 'move-up'):
1172 newpos = max(0, oldpos - 1)
1171 newpos = max(0, oldpos - 1)
1173 movecursor(state, oldpos, newpos)
1172 movecursor(state, oldpos, newpos)
1174 if selected is not None or action == 'move-up':
1173 if selected is not None or action == 'move-up':
1175 swap(state, oldpos, newpos)
1174 swap(state, oldpos, newpos)
1176 elif action == 'next-action':
1175 elif action == 'next-action':
1177 cycleaction(state, oldpos, next=True)
1176 cycleaction(state, oldpos, next=True)
1178 elif action == 'prev-action':
1177 elif action == 'prev-action':
1179 cycleaction(state, oldpos, next=False)
1178 cycleaction(state, oldpos, next=False)
1180 elif action == 'select':
1179 elif action == 'select':
1181 selected = oldpos if selected is None else None
1180 selected = oldpos if selected is None else None
1182 makeselection(state, selected)
1181 makeselection(state, selected)
1183 elif action == 'goto' and int(ch) < len(rules) and len(rules) <= 10:
1182 elif action == 'goto' and int(ch) < len(rules) and len(rules) <= 10:
1184 newrule = next((r for r in rules if r.origpos == int(ch)))
1183 newrule = next((r for r in rules if r.origpos == int(ch)))
1185 movecursor(state, oldpos, newrule.pos)
1184 movecursor(state, oldpos, newrule.pos)
1186 if selected is not None:
1185 if selected is not None:
1187 swap(state, oldpos, newrule.pos)
1186 swap(state, oldpos, newrule.pos)
1188 elif action.startswith('action-'):
1187 elif action.startswith('action-'):
1189 changeaction(state, oldpos, action[7:])
1188 changeaction(state, oldpos, action[7:])
1190 elif action == 'showpatch':
1189 elif action == 'showpatch':
1191 changemode(state, MODE_PATCH if curmode != MODE_PATCH else prevmode)
1190 changemode(state, MODE_PATCH if curmode != MODE_PATCH else prevmode)
1192 elif action == 'help':
1191 elif action == 'help':
1193 changemode(state, MODE_HELP if curmode != MODE_HELP else prevmode)
1192 changemode(state, MODE_HELP if curmode != MODE_HELP else prevmode)
1194 elif action == 'quit':
1193 elif action == 'quit':
1195 return E_QUIT
1194 return E_QUIT
1196 elif action == 'histedit':
1195 elif action == 'histedit':
1197 return E_HISTEDIT
1196 return E_HISTEDIT
1198 elif action == 'page-down':
1197 elif action == 'page-down':
1199 return E_PAGEDOWN
1198 return E_PAGEDOWN
1200 elif action == 'page-up':
1199 elif action == 'page-up':
1201 return E_PAGEUP
1200 return E_PAGEUP
1202 elif action == 'line-down':
1201 elif action == 'line-down':
1203 return E_LINEDOWN
1202 return E_LINEDOWN
1204 elif action == 'line-up':
1203 elif action == 'line-up':
1205 return E_LINEUP
1204 return E_LINEUP
1206
1205
1207 def makecommands(rules):
1206 def makecommands(rules):
1208 """Returns a list of commands consumable by histedit --commands based on
1207 """Returns a list of commands consumable by histedit --commands based on
1209 our list of rules"""
1208 our list of rules"""
1210 commands = []
1209 commands = []
1211 for rules in rules:
1210 for rules in rules:
1212 commands.append("{0} {1}\n".format(rules.action, rules.ctx))
1211 commands.append("{0} {1}\n".format(rules.action, rules.ctx))
1213 return commands
1212 return commands
1214
1213
1215 def addln(win, y, x, line, color=None):
1214 def addln(win, y, x, line, color=None):
1216 """Add a line to the given window left padding but 100% filled with
1215 """Add a line to the given window left padding but 100% filled with
1217 whitespace characters, so that the color appears on the whole line"""
1216 whitespace characters, so that the color appears on the whole line"""
1218 maxy, maxx = win.getmaxyx()
1217 maxy, maxx = win.getmaxyx()
1219 length = maxx - 1 - x
1218 length = maxx - 1 - x
1220 line = ("{0:<%d}" % length).format(str(line).strip())[:length]
1219 line = ("{0:<%d}" % length).format(str(line).strip())[:length]
1221 if y < 0:
1220 if y < 0:
1222 y = maxy + y
1221 y = maxy + y
1223 if x < 0:
1222 if x < 0:
1224 x = maxx + x
1223 x = maxx + x
1225 if color:
1224 if color:
1226 win.addstr(y, x, line, color)
1225 win.addstr(y, x, line, color)
1227 else:
1226 else:
1228 win.addstr(y, x, line)
1227 win.addstr(y, x, line)
1229
1228
1230 def patchcontents(state):
1229 def patchcontents(state):
1231 repo = state['repo']
1230 repo = state['repo']
1232 rule = state['rules'][state['pos']]
1231 rule = state['rules'][state['pos']]
1233 displayer = logcmdutil.changesetdisplayer(repo.ui, repo, {
1232 displayer = logcmdutil.changesetdisplayer(repo.ui, repo, {
1234 'patch': True, 'verbose': True
1233 'patch': True, 'verbose': True
1235 }, buffered=True)
1234 }, buffered=True)
1236 displayer.show(rule.ctx)
1235 displayer.show(rule.ctx)
1237 displayer.close()
1236 displayer.close()
1238 return displayer.hunk[rule.ctx.rev()].splitlines()
1237 return displayer.hunk[rule.ctx.rev()].splitlines()
1239
1238
1240 def _chisteditmain(repo, rules, stdscr):
1239 def _chisteditmain(repo, rules, stdscr):
1241 # initialize color pattern
1240 # initialize color pattern
1242 curses.init_pair(COLOR_HELP, curses.COLOR_WHITE, curses.COLOR_BLUE)
1241 curses.init_pair(COLOR_HELP, curses.COLOR_WHITE, curses.COLOR_BLUE)
1243 curses.init_pair(COLOR_SELECTED, curses.COLOR_BLACK, curses.COLOR_WHITE)
1242 curses.init_pair(COLOR_SELECTED, curses.COLOR_BLACK, curses.COLOR_WHITE)
1244 curses.init_pair(COLOR_WARN, curses.COLOR_BLACK, curses.COLOR_YELLOW)
1243 curses.init_pair(COLOR_WARN, curses.COLOR_BLACK, curses.COLOR_YELLOW)
1245 curses.init_pair(COLOR_OK, curses.COLOR_BLACK, curses.COLOR_GREEN)
1244 curses.init_pair(COLOR_OK, curses.COLOR_BLACK, curses.COLOR_GREEN)
1246 curses.init_pair(COLOR_CURRENT, curses.COLOR_WHITE, curses.COLOR_MAGENTA)
1245 curses.init_pair(COLOR_CURRENT, curses.COLOR_WHITE, curses.COLOR_MAGENTA)
1247
1246
1248 # don't display the cursor
1247 # don't display the cursor
1249 try:
1248 try:
1250 curses.curs_set(0)
1249 curses.curs_set(0)
1251 except curses.error:
1250 except curses.error:
1252 pass
1251 pass
1253
1252
1254 def rendercommit(win, state):
1253 def rendercommit(win, state):
1255 """Renders the commit window that shows the log of the current selected
1254 """Renders the commit window that shows the log of the current selected
1256 commit"""
1255 commit"""
1257 pos = state['pos']
1256 pos = state['pos']
1258 rules = state['rules']
1257 rules = state['rules']
1259 rule = rules[pos]
1258 rule = rules[pos]
1260
1259
1261 ctx = rule.ctx
1260 ctx = rule.ctx
1262 win.box()
1261 win.box()
1263
1262
1264 maxy, maxx = win.getmaxyx()
1263 maxy, maxx = win.getmaxyx()
1265 length = maxx - 3
1264 length = maxx - 3
1266
1265
1267 line = "changeset: {0}:{1:<12}".format(ctx.rev(), ctx)
1266 line = "changeset: {0}:{1:<12}".format(ctx.rev(), ctx)
1268 win.addstr(1, 1, line[:length])
1267 win.addstr(1, 1, line[:length])
1269
1268
1270 line = "user: {0}".format(ctx.user())
1269 line = "user: {0}".format(ctx.user())
1271 win.addstr(2, 1, line[:length])
1270 win.addstr(2, 1, line[:length])
1272
1271
1273 bms = repo.nodebookmarks(ctx.node())
1272 bms = repo.nodebookmarks(ctx.node())
1274 line = "bookmark: {0}".format(' '.join(bms))
1273 line = "bookmark: {0}".format(' '.join(bms))
1275 win.addstr(3, 1, line[:length])
1274 win.addstr(3, 1, line[:length])
1276
1275
1277 line = "files: {0}".format(','.join(ctx.files()))
1276 line = "files: {0}".format(','.join(ctx.files()))
1278 win.addstr(4, 1, line[:length])
1277 win.addstr(4, 1, line[:length])
1279
1278
1280 line = "summary: {0}".format(ctx.description().splitlines()[0])
1279 line = "summary: {0}".format(ctx.description().splitlines()[0])
1281 win.addstr(5, 1, line[:length])
1280 win.addstr(5, 1, line[:length])
1282
1281
1283 conflicts = rule.conflicts
1282 conflicts = rule.conflicts
1284 if len(conflicts) > 0:
1283 if len(conflicts) > 0:
1285 conflictstr = ','.join(map(lambda r: str(r.ctx), conflicts))
1284 conflictstr = ','.join(map(lambda r: str(r.ctx), conflicts))
1286 conflictstr = "changed files overlap with {0}".format(conflictstr)
1285 conflictstr = "changed files overlap with {0}".format(conflictstr)
1287 else:
1286 else:
1288 conflictstr = 'no overlap'
1287 conflictstr = 'no overlap'
1289
1288
1290 win.addstr(6, 1, conflictstr[:length])
1289 win.addstr(6, 1, conflictstr[:length])
1291 win.noutrefresh()
1290 win.noutrefresh()
1292
1291
1293 def helplines(mode):
1292 def helplines(mode):
1294 if mode == MODE_PATCH:
1293 if mode == MODE_PATCH:
1295 help = """\
1294 help = """\
1296 ?: help, k/up: line up, j/down: line down, v: stop viewing patch
1295 ?: help, k/up: line up, j/down: line down, v: stop viewing patch
1297 pgup: prev page, space/pgdn: next page, c: commit, q: abort
1296 pgup: prev page, space/pgdn: next page, c: commit, q: abort
1298 """
1297 """
1299 else:
1298 else:
1300 help = """\
1299 help = """\
1301 ?: help, k/up: move up, j/down: move down, space: select, v: view patch
1300 ?: help, k/up: move up, j/down: move down, space: select, v: view patch
1302 d: drop, e: edit, f: fold, m: mess, p: pick, r: roll
1301 d: drop, e: edit, f: fold, m: mess, p: pick, r: roll
1303 pgup/K: move patch up, pgdn/J: move patch down, c: commit, q: abort
1302 pgup/K: move patch up, pgdn/J: move patch down, c: commit, q: abort
1304 """
1303 """
1305 return help.splitlines()
1304 return help.splitlines()
1306
1305
1307 def renderhelp(win, state):
1306 def renderhelp(win, state):
1308 maxy, maxx = win.getmaxyx()
1307 maxy, maxx = win.getmaxyx()
1309 mode, _ = state['mode']
1308 mode, _ = state['mode']
1310 for y, line in enumerate(helplines(mode)):
1309 for y, line in enumerate(helplines(mode)):
1311 if y >= maxy:
1310 if y >= maxy:
1312 break
1311 break
1313 addln(win, y, 0, line, curses.color_pair(COLOR_HELP))
1312 addln(win, y, 0, line, curses.color_pair(COLOR_HELP))
1314 win.noutrefresh()
1313 win.noutrefresh()
1315
1314
1316 def renderrules(rulesscr, state):
1315 def renderrules(rulesscr, state):
1317 rules = state['rules']
1316 rules = state['rules']
1318 pos = state['pos']
1317 pos = state['pos']
1319 selected = state['selected']
1318 selected = state['selected']
1320 start = state['modes'][MODE_RULES]['line_offset']
1319 start = state['modes'][MODE_RULES]['line_offset']
1321
1320
1322 conflicts = [r.ctx for r in rules if r.conflicts]
1321 conflicts = [r.ctx for r in rules if r.conflicts]
1323 if len(conflicts) > 0:
1322 if len(conflicts) > 0:
1324 line = "potential conflict in %s" % ','.join(map(str, conflicts))
1323 line = "potential conflict in %s" % ','.join(map(str, conflicts))
1325 addln(rulesscr, -1, 0, line, curses.color_pair(COLOR_WARN))
1324 addln(rulesscr, -1, 0, line, curses.color_pair(COLOR_WARN))
1326
1325
1327 for y, rule in enumerate(rules[start:]):
1326 for y, rule in enumerate(rules[start:]):
1328 if y >= state['page_height']:
1327 if y >= state['page_height']:
1329 break
1328 break
1330 if len(rule.conflicts) > 0:
1329 if len(rule.conflicts) > 0:
1331 rulesscr.addstr(y, 0, " ", curses.color_pair(COLOR_WARN))
1330 rulesscr.addstr(y, 0, " ", curses.color_pair(COLOR_WARN))
1332 else:
1331 else:
1333 rulesscr.addstr(y, 0, " ", curses.COLOR_BLACK)
1332 rulesscr.addstr(y, 0, " ", curses.COLOR_BLACK)
1334 if y + start == selected:
1333 if y + start == selected:
1335 addln(rulesscr, y, 2, rule, curses.color_pair(COLOR_SELECTED))
1334 addln(rulesscr, y, 2, rule, curses.color_pair(COLOR_SELECTED))
1336 elif y + start == pos:
1335 elif y + start == pos:
1337 addln(rulesscr, y, 2, rule,
1336 addln(rulesscr, y, 2, rule,
1338 curses.color_pair(COLOR_CURRENT) | curses.A_BOLD)
1337 curses.color_pair(COLOR_CURRENT) | curses.A_BOLD)
1339 else:
1338 else:
1340 addln(rulesscr, y, 2, rule)
1339 addln(rulesscr, y, 2, rule)
1341 rulesscr.noutrefresh()
1340 rulesscr.noutrefresh()
1342
1341
1343 def renderstring(win, state, output):
1342 def renderstring(win, state, output):
1344 maxy, maxx = win.getmaxyx()
1343 maxy, maxx = win.getmaxyx()
1345 length = min(maxy - 1, len(output))
1344 length = min(maxy - 1, len(output))
1346 for y in range(0, length):
1345 for y in range(0, length):
1347 win.addstr(y, 0, output[y])
1346 win.addstr(y, 0, output[y])
1348 win.noutrefresh()
1347 win.noutrefresh()
1349
1348
1350 def renderpatch(win, state):
1349 def renderpatch(win, state):
1351 start = state['modes'][MODE_PATCH]['line_offset']
1350 start = state['modes'][MODE_PATCH]['line_offset']
1352 renderstring(win, state, patchcontents(state)[start:])
1351 renderstring(win, state, patchcontents(state)[start:])
1353
1352
1354 def layout(mode):
1353 def layout(mode):
1355 maxy, maxx = stdscr.getmaxyx()
1354 maxy, maxx = stdscr.getmaxyx()
1356 helplen = len(helplines(mode))
1355 helplen = len(helplines(mode))
1357 return {
1356 return {
1358 'commit': (8, maxx),
1357 'commit': (8, maxx),
1359 'help': (helplen, maxx),
1358 'help': (helplen, maxx),
1360 'main': (maxy - helplen - 8, maxx),
1359 'main': (maxy - helplen - 8, maxx),
1361 }
1360 }
1362
1361
1363 def drawvertwin(size, y, x):
1362 def drawvertwin(size, y, x):
1364 win = curses.newwin(size[0], size[1], y, x)
1363 win = curses.newwin(size[0], size[1], y, x)
1365 y += size[0]
1364 y += size[0]
1366 return win, y, x
1365 return win, y, x
1367
1366
1368 state = {
1367 state = {
1369 'pos': 0,
1368 'pos': 0,
1370 'rules': rules,
1369 'rules': rules,
1371 'selected': None,
1370 'selected': None,
1372 'mode': (MODE_INIT, MODE_INIT),
1371 'mode': (MODE_INIT, MODE_INIT),
1373 'page_height': None,
1372 'page_height': None,
1374 'modes': {
1373 'modes': {
1375 MODE_RULES: {
1374 MODE_RULES: {
1376 'line_offset': 0,
1375 'line_offset': 0,
1377 },
1376 },
1378 MODE_PATCH: {
1377 MODE_PATCH: {
1379 'line_offset': 0,
1378 'line_offset': 0,
1380 }
1379 }
1381 },
1380 },
1382 'repo': repo,
1381 'repo': repo,
1383 }
1382 }
1384
1383
1385 # eventloop
1384 # eventloop
1386 ch = None
1385 ch = None
1387 stdscr.clear()
1386 stdscr.clear()
1388 stdscr.refresh()
1387 stdscr.refresh()
1389 while True:
1388 while True:
1390 try:
1389 try:
1391 oldmode, _ = state['mode']
1390 oldmode, _ = state['mode']
1392 if oldmode == MODE_INIT:
1391 if oldmode == MODE_INIT:
1393 changemode(state, MODE_RULES)
1392 changemode(state, MODE_RULES)
1394 e = event(state, ch)
1393 e = event(state, ch)
1395
1394
1396 if e == E_QUIT:
1395 if e == E_QUIT:
1397 return False
1396 return False
1398 if e == E_HISTEDIT:
1397 if e == E_HISTEDIT:
1399 return state['rules']
1398 return state['rules']
1400 else:
1399 else:
1401 if e == E_RESIZE:
1400 if e == E_RESIZE:
1402 size = screen_size()
1401 size = screen_size()
1403 if size != stdscr.getmaxyx():
1402 if size != stdscr.getmaxyx():
1404 curses.resizeterm(*size)
1403 curses.resizeterm(*size)
1405
1404
1406 curmode, _ = state['mode']
1405 curmode, _ = state['mode']
1407 sizes = layout(curmode)
1406 sizes = layout(curmode)
1408 if curmode != oldmode:
1407 if curmode != oldmode:
1409 state['page_height'] = sizes['main'][0]
1408 state['page_height'] = sizes['main'][0]
1410 # Adjust the view to fit the current screen size.
1409 # Adjust the view to fit the current screen size.
1411 movecursor(state, state['pos'], state['pos'])
1410 movecursor(state, state['pos'], state['pos'])
1412
1411
1413 # Pack the windows against the top, each pane spread across the
1412 # Pack the windows against the top, each pane spread across the
1414 # full width of the screen.
1413 # full width of the screen.
1415 y, x = (0, 0)
1414 y, x = (0, 0)
1416 helpwin, y, x = drawvertwin(sizes['help'], y, x)
1415 helpwin, y, x = drawvertwin(sizes['help'], y, x)
1417 mainwin, y, x = drawvertwin(sizes['main'], y, x)
1416 mainwin, y, x = drawvertwin(sizes['main'], y, x)
1418 commitwin, y, x = drawvertwin(sizes['commit'], y, x)
1417 commitwin, y, x = drawvertwin(sizes['commit'], y, x)
1419
1418
1420 if e in (E_PAGEDOWN, E_PAGEUP, E_LINEDOWN, E_LINEUP):
1419 if e in (E_PAGEDOWN, E_PAGEUP, E_LINEDOWN, E_LINEUP):
1421 if e == E_PAGEDOWN:
1420 if e == E_PAGEDOWN:
1422 changeview(state, +1, 'page')
1421 changeview(state, +1, 'page')
1423 elif e == E_PAGEUP:
1422 elif e == E_PAGEUP:
1424 changeview(state, -1, 'page')
1423 changeview(state, -1, 'page')
1425 elif e == E_LINEDOWN:
1424 elif e == E_LINEDOWN:
1426 changeview(state, +1, 'line')
1425 changeview(state, +1, 'line')
1427 elif e == E_LINEUP:
1426 elif e == E_LINEUP:
1428 changeview(state, -1, 'line')
1427 changeview(state, -1, 'line')
1429
1428
1430 # start rendering
1429 # start rendering
1431 commitwin.erase()
1430 commitwin.erase()
1432 helpwin.erase()
1431 helpwin.erase()
1433 mainwin.erase()
1432 mainwin.erase()
1434 if curmode == MODE_PATCH:
1433 if curmode == MODE_PATCH:
1435 renderpatch(mainwin, state)
1434 renderpatch(mainwin, state)
1436 elif curmode == MODE_HELP:
1435 elif curmode == MODE_HELP:
1437 renderstring(mainwin, state, __doc__.strip().splitlines())
1436 renderstring(mainwin, state, __doc__.strip().splitlines())
1438 else:
1437 else:
1439 renderrules(mainwin, state)
1438 renderrules(mainwin, state)
1440 rendercommit(commitwin, state)
1439 rendercommit(commitwin, state)
1441 renderhelp(helpwin, state)
1440 renderhelp(helpwin, state)
1442 curses.doupdate()
1441 curses.doupdate()
1443 # done rendering
1442 # done rendering
1444 ch = stdscr.getkey()
1443 ch = stdscr.getkey()
1445 except curses.error:
1444 except curses.error:
1446 pass
1445 pass
1447
1446
1448 def _chistedit(ui, repo, *freeargs, **opts):
1447 def _chistedit(ui, repo, *freeargs, **opts):
1449 """interactively edit changeset history via a curses interface
1448 """interactively edit changeset history via a curses interface
1450
1449
1451 Provides a ncurses interface to histedit. Press ? in chistedit mode
1450 Provides a ncurses interface to histedit. Press ? in chistedit mode
1452 to see an extensive help. Requires python-curses to be installed."""
1451 to see an extensive help. Requires python-curses to be installed."""
1453
1452
1454 if curses is None:
1453 if curses is None:
1455 raise error.Abort(_("Python curses library required"))
1454 raise error.Abort(_("Python curses library required"))
1456
1455
1457 # disable color
1456 # disable color
1458 ui._colormode = None
1457 ui._colormode = None
1459
1458
1460 try:
1459 try:
1461 keep = opts.get('keep')
1460 keep = opts.get('keep')
1462 revs = opts.get('rev', [])[:]
1461 revs = opts.get('rev', [])[:]
1463 cmdutil.checkunfinished(repo)
1462 cmdutil.checkunfinished(repo)
1464 cmdutil.bailifchanged(repo)
1463 cmdutil.bailifchanged(repo)
1465
1464
1466 if os.path.exists(os.path.join(repo.path, 'histedit-state')):
1465 if os.path.exists(os.path.join(repo.path, 'histedit-state')):
1467 raise error.Abort(_('history edit already in progress, try '
1466 raise error.Abort(_('history edit already in progress, try '
1468 '--continue or --abort'))
1467 '--continue or --abort'))
1469 revs.extend(freeargs)
1468 revs.extend(freeargs)
1470 if not revs:
1469 if not revs:
1471 defaultrev = destutil.desthistedit(ui, repo)
1470 defaultrev = destutil.desthistedit(ui, repo)
1472 if defaultrev is not None:
1471 if defaultrev is not None:
1473 revs.append(defaultrev)
1472 revs.append(defaultrev)
1474 if len(revs) != 1:
1473 if len(revs) != 1:
1475 raise error.Abort(
1474 raise error.Abort(
1476 _('histedit requires exactly one ancestor revision'))
1475 _('histedit requires exactly one ancestor revision'))
1477
1476
1478 rr = list(repo.set('roots(%ld)', scmutil.revrange(repo, revs)))
1477 rr = list(repo.set('roots(%ld)', scmutil.revrange(repo, revs)))
1479 if len(rr) != 1:
1478 if len(rr) != 1:
1480 raise error.Abort(_('The specified revisions must have '
1479 raise error.Abort(_('The specified revisions must have '
1481 'exactly one common root'))
1480 'exactly one common root'))
1482 root = rr[0].node()
1481 root = rr[0].node()
1483
1482
1484 topmost = repo.dirstate.p1()
1483 topmost = repo.dirstate.p1()
1485 revs = between(repo, root, topmost, keep)
1484 revs = between(repo, root, topmost, keep)
1486 if not revs:
1485 if not revs:
1487 raise error.Abort(_('%s is not an ancestor of working directory') %
1486 raise error.Abort(_('%s is not an ancestor of working directory') %
1488 node.short(root))
1487 node.short(root))
1489
1488
1490 ctxs = []
1489 ctxs = []
1491 for i, r in enumerate(revs):
1490 for i, r in enumerate(revs):
1492 ctxs.append(histeditrule(repo[r], i))
1491 ctxs.append(histeditrule(repo[r], i))
1493 rc = curses.wrapper(functools.partial(_chisteditmain, repo, ctxs))
1492 rc = curses.wrapper(functools.partial(_chisteditmain, repo, ctxs))
1494 curses.echo()
1493 curses.echo()
1495 curses.endwin()
1494 curses.endwin()
1496 if rc is False:
1495 if rc is False:
1497 ui.write(_("histedit aborted\n"))
1496 ui.write(_("histedit aborted\n"))
1498 return 0
1497 return 0
1499 if type(rc) is list:
1498 if type(rc) is list:
1500 ui.status(_("performing changes\n"))
1499 ui.status(_("performing changes\n"))
1501 rules = makecommands(rc)
1500 rules = makecommands(rc)
1502 filename = repo.vfs.join('chistedit')
1501 filename = repo.vfs.join('chistedit')
1503 with open(filename, 'w+') as fp:
1502 with open(filename, 'w+') as fp:
1504 for r in rules:
1503 for r in rules:
1505 fp.write(r)
1504 fp.write(r)
1506 opts['commands'] = filename
1505 opts['commands'] = filename
1507 return _texthistedit(ui, repo, *freeargs, **opts)
1506 return _texthistedit(ui, repo, *freeargs, **opts)
1508 except KeyboardInterrupt:
1507 except KeyboardInterrupt:
1509 pass
1508 pass
1510 return -1
1509 return -1
1511
1510
1512 @command('histedit',
1511 @command('histedit',
1513 [('', 'commands', '',
1512 [('', 'commands', '',
1514 _('read history edits from the specified file'), _('FILE')),
1513 _('read history edits from the specified file'), _('FILE')),
1515 ('c', 'continue', False, _('continue an edit already in progress')),
1514 ('c', 'continue', False, _('continue an edit already in progress')),
1516 ('', 'edit-plan', False, _('edit remaining actions list')),
1515 ('', 'edit-plan', False, _('edit remaining actions list')),
1517 ('k', 'keep', False,
1516 ('k', 'keep', False,
1518 _("don't strip old nodes after edit is complete")),
1517 _("don't strip old nodes after edit is complete")),
1519 ('', 'abort', False, _('abort an edit in progress')),
1518 ('', 'abort', False, _('abort an edit in progress')),
1520 ('o', 'outgoing', False, _('changesets not found in destination')),
1519 ('o', 'outgoing', False, _('changesets not found in destination')),
1521 ('f', 'force', False,
1520 ('f', 'force', False,
1522 _('force outgoing even for unrelated repositories')),
1521 _('force outgoing even for unrelated repositories')),
1523 ('r', 'rev', [], _('first revision to be edited'), _('REV'))] +
1522 ('r', 'rev', [], _('first revision to be edited'), _('REV'))] +
1524 cmdutil.formatteropts,
1523 cmdutil.formatteropts,
1525 _("[OPTIONS] ([ANCESTOR] | --outgoing [URL])"),
1524 _("[OPTIONS] ([ANCESTOR] | --outgoing [URL])"),
1526 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
1525 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
1527 def histedit(ui, repo, *freeargs, **opts):
1526 def histedit(ui, repo, *freeargs, **opts):
1528 """interactively edit changeset history
1527 """interactively edit changeset history
1529
1528
1530 This command lets you edit a linear series of changesets (up to
1529 This command lets you edit a linear series of changesets (up to
1531 and including the working directory, which should be clean).
1530 and including the working directory, which should be clean).
1532 You can:
1531 You can:
1533
1532
1534 - `pick` to [re]order a changeset
1533 - `pick` to [re]order a changeset
1535
1534
1536 - `drop` to omit changeset
1535 - `drop` to omit changeset
1537
1536
1538 - `mess` to reword the changeset commit message
1537 - `mess` to reword the changeset commit message
1539
1538
1540 - `fold` to combine it with the preceding changeset (using the later date)
1539 - `fold` to combine it with the preceding changeset (using the later date)
1541
1540
1542 - `roll` like fold, but discarding this commit's description and date
1541 - `roll` like fold, but discarding this commit's description and date
1543
1542
1544 - `edit` to edit this changeset (preserving date)
1543 - `edit` to edit this changeset (preserving date)
1545
1544
1546 - `base` to checkout changeset and apply further changesets from there
1545 - `base` to checkout changeset and apply further changesets from there
1547
1546
1548 There are a number of ways to select the root changeset:
1547 There are a number of ways to select the root changeset:
1549
1548
1550 - Specify ANCESTOR directly
1549 - Specify ANCESTOR directly
1551
1550
1552 - Use --outgoing -- it will be the first linear changeset not
1551 - Use --outgoing -- it will be the first linear changeset not
1553 included in destination. (See :hg:`help config.paths.default-push`)
1552 included in destination. (See :hg:`help config.paths.default-push`)
1554
1553
1555 - Otherwise, the value from the "histedit.defaultrev" config option
1554 - Otherwise, the value from the "histedit.defaultrev" config option
1556 is used as a revset to select the base revision when ANCESTOR is not
1555 is used as a revset to select the base revision when ANCESTOR is not
1557 specified. The first revision returned by the revset is used. By
1556 specified. The first revision returned by the revset is used. By
1558 default, this selects the editable history that is unique to the
1557 default, this selects the editable history that is unique to the
1559 ancestry of the working directory.
1558 ancestry of the working directory.
1560
1559
1561 .. container:: verbose
1560 .. container:: verbose
1562
1561
1563 If you use --outgoing, this command will abort if there are ambiguous
1562 If you use --outgoing, this command will abort if there are ambiguous
1564 outgoing revisions. For example, if there are multiple branches
1563 outgoing revisions. For example, if there are multiple branches
1565 containing outgoing revisions.
1564 containing outgoing revisions.
1566
1565
1567 Use "min(outgoing() and ::.)" or similar revset specification
1566 Use "min(outgoing() and ::.)" or similar revset specification
1568 instead of --outgoing to specify edit target revision exactly in
1567 instead of --outgoing to specify edit target revision exactly in
1569 such ambiguous situation. See :hg:`help revsets` for detail about
1568 such ambiguous situation. See :hg:`help revsets` for detail about
1570 selecting revisions.
1569 selecting revisions.
1571
1570
1572 .. container:: verbose
1571 .. container:: verbose
1573
1572
1574 Examples:
1573 Examples:
1575
1574
1576 - A number of changes have been made.
1575 - A number of changes have been made.
1577 Revision 3 is no longer needed.
1576 Revision 3 is no longer needed.
1578
1577
1579 Start history editing from revision 3::
1578 Start history editing from revision 3::
1580
1579
1581 hg histedit -r 3
1580 hg histedit -r 3
1582
1581
1583 An editor opens, containing the list of revisions,
1582 An editor opens, containing the list of revisions,
1584 with specific actions specified::
1583 with specific actions specified::
1585
1584
1586 pick 5339bf82f0ca 3 Zworgle the foobar
1585 pick 5339bf82f0ca 3 Zworgle the foobar
1587 pick 8ef592ce7cc4 4 Bedazzle the zerlog
1586 pick 8ef592ce7cc4 4 Bedazzle the zerlog
1588 pick 0a9639fcda9d 5 Morgify the cromulancy
1587 pick 0a9639fcda9d 5 Morgify the cromulancy
1589
1588
1590 Additional information about the possible actions
1589 Additional information about the possible actions
1591 to take appears below the list of revisions.
1590 to take appears below the list of revisions.
1592
1591
1593 To remove revision 3 from the history,
1592 To remove revision 3 from the history,
1594 its action (at the beginning of the relevant line)
1593 its action (at the beginning of the relevant line)
1595 is changed to 'drop'::
1594 is changed to 'drop'::
1596
1595
1597 drop 5339bf82f0ca 3 Zworgle the foobar
1596 drop 5339bf82f0ca 3 Zworgle the foobar
1598 pick 8ef592ce7cc4 4 Bedazzle the zerlog
1597 pick 8ef592ce7cc4 4 Bedazzle the zerlog
1599 pick 0a9639fcda9d 5 Morgify the cromulancy
1598 pick 0a9639fcda9d 5 Morgify the cromulancy
1600
1599
1601 - A number of changes have been made.
1600 - A number of changes have been made.
1602 Revision 2 and 4 need to be swapped.
1601 Revision 2 and 4 need to be swapped.
1603
1602
1604 Start history editing from revision 2::
1603 Start history editing from revision 2::
1605
1604
1606 hg histedit -r 2
1605 hg histedit -r 2
1607
1606
1608 An editor opens, containing the list of revisions,
1607 An editor opens, containing the list of revisions,
1609 with specific actions specified::
1608 with specific actions specified::
1610
1609
1611 pick 252a1af424ad 2 Blorb a morgwazzle
1610 pick 252a1af424ad 2 Blorb a morgwazzle
1612 pick 5339bf82f0ca 3 Zworgle the foobar
1611 pick 5339bf82f0ca 3 Zworgle the foobar
1613 pick 8ef592ce7cc4 4 Bedazzle the zerlog
1612 pick 8ef592ce7cc4 4 Bedazzle the zerlog
1614
1613
1615 To swap revision 2 and 4, its lines are swapped
1614 To swap revision 2 and 4, its lines are swapped
1616 in the editor::
1615 in the editor::
1617
1616
1618 pick 8ef592ce7cc4 4 Bedazzle the zerlog
1617 pick 8ef592ce7cc4 4 Bedazzle the zerlog
1619 pick 5339bf82f0ca 3 Zworgle the foobar
1618 pick 5339bf82f0ca 3 Zworgle the foobar
1620 pick 252a1af424ad 2 Blorb a morgwazzle
1619 pick 252a1af424ad 2 Blorb a morgwazzle
1621
1620
1622 Returns 0 on success, 1 if user intervention is required (not only
1621 Returns 0 on success, 1 if user intervention is required (not only
1623 for intentional "edit" command, but also for resolving unexpected
1622 for intentional "edit" command, but also for resolving unexpected
1624 conflicts).
1623 conflicts).
1625 """
1624 """
1626 # kludge: _chistedit only works for starting an edit, not aborting
1625 # kludge: _chistedit only works for starting an edit, not aborting
1627 # or continuing, so fall back to regular _texthistedit for those
1626 # or continuing, so fall back to regular _texthistedit for those
1628 # operations.
1627 # operations.
1629 if ui.interface('histedit') == 'curses' and _getgoal(
1628 if ui.interface('histedit') == 'curses' and _getgoal(
1630 pycompat.byteskwargs(opts)) == goalnew:
1629 pycompat.byteskwargs(opts)) == goalnew:
1631 return _chistedit(ui, repo, *freeargs, **opts)
1630 return _chistedit(ui, repo, *freeargs, **opts)
1632 return _texthistedit(ui, repo, *freeargs, **opts)
1631 return _texthistedit(ui, repo, *freeargs, **opts)
1633
1632
1634 def _texthistedit(ui, repo, *freeargs, **opts):
1633 def _texthistedit(ui, repo, *freeargs, **opts):
1635 state = histeditstate(repo)
1634 state = histeditstate(repo)
1636 with repo.wlock() as wlock, repo.lock() as lock:
1635 with repo.wlock() as wlock, repo.lock() as lock:
1637 state.wlock = wlock
1636 state.wlock = wlock
1638 state.lock = lock
1637 state.lock = lock
1639 _histedit(ui, repo, state, *freeargs, **opts)
1638 _histedit(ui, repo, state, *freeargs, **opts)
1640
1639
1641 goalcontinue = 'continue'
1640 goalcontinue = 'continue'
1642 goalabort = 'abort'
1641 goalabort = 'abort'
1643 goaleditplan = 'edit-plan'
1642 goaleditplan = 'edit-plan'
1644 goalnew = 'new'
1643 goalnew = 'new'
1645
1644
1646 def _getgoal(opts):
1645 def _getgoal(opts):
1647 if opts.get(b'continue'):
1646 if opts.get(b'continue'):
1648 return goalcontinue
1647 return goalcontinue
1649 if opts.get(b'abort'):
1648 if opts.get(b'abort'):
1650 return goalabort
1649 return goalabort
1651 if opts.get(b'edit_plan'):
1650 if opts.get(b'edit_plan'):
1652 return goaleditplan
1651 return goaleditplan
1653 return goalnew
1652 return goalnew
1654
1653
1655 def _readfile(ui, path):
1654 def _readfile(ui, path):
1656 if path == '-':
1655 if path == '-':
1657 with ui.timeblockedsection('histedit'):
1656 with ui.timeblockedsection('histedit'):
1658 return ui.fin.read()
1657 return ui.fin.read()
1659 else:
1658 else:
1660 with open(path, 'rb') as f:
1659 with open(path, 'rb') as f:
1661 return f.read()
1660 return f.read()
1662
1661
1663 def _validateargs(ui, repo, state, freeargs, opts, goal, rules, revs):
1662 def _validateargs(ui, repo, state, freeargs, opts, goal, rules, revs):
1664 # TODO only abort if we try to histedit mq patches, not just
1663 # TODO only abort if we try to histedit mq patches, not just
1665 # blanket if mq patches are applied somewhere
1664 # blanket if mq patches are applied somewhere
1666 mq = getattr(repo, 'mq', None)
1665 mq = getattr(repo, 'mq', None)
1667 if mq and mq.applied:
1666 if mq and mq.applied:
1668 raise error.Abort(_('source has mq patches applied'))
1667 raise error.Abort(_('source has mq patches applied'))
1669
1668
1670 # basic argument incompatibility processing
1669 # basic argument incompatibility processing
1671 outg = opts.get('outgoing')
1670 outg = opts.get('outgoing')
1672 editplan = opts.get('edit_plan')
1671 editplan = opts.get('edit_plan')
1673 abort = opts.get('abort')
1672 abort = opts.get('abort')
1674 force = opts.get('force')
1673 force = opts.get('force')
1675 if force and not outg:
1674 if force and not outg:
1676 raise error.Abort(_('--force only allowed with --outgoing'))
1675 raise error.Abort(_('--force only allowed with --outgoing'))
1677 if goal == 'continue':
1676 if goal == 'continue':
1678 if any((outg, abort, revs, freeargs, rules, editplan)):
1677 if any((outg, abort, revs, freeargs, rules, editplan)):
1679 raise error.Abort(_('no arguments allowed with --continue'))
1678 raise error.Abort(_('no arguments allowed with --continue'))
1680 elif goal == 'abort':
1679 elif goal == 'abort':
1681 if any((outg, revs, freeargs, rules, editplan)):
1680 if any((outg, revs, freeargs, rules, editplan)):
1682 raise error.Abort(_('no arguments allowed with --abort'))
1681 raise error.Abort(_('no arguments allowed with --abort'))
1683 elif goal == 'edit-plan':
1682 elif goal == 'edit-plan':
1684 if any((outg, revs, freeargs)):
1683 if any((outg, revs, freeargs)):
1685 raise error.Abort(_('only --commands argument allowed with '
1684 raise error.Abort(_('only --commands argument allowed with '
1686 '--edit-plan'))
1685 '--edit-plan'))
1687 else:
1686 else:
1688 if state.inprogress():
1687 if state.inprogress():
1689 raise error.Abort(_('history edit already in progress, try '
1688 raise error.Abort(_('history edit already in progress, try '
1690 '--continue or --abort'))
1689 '--continue or --abort'))
1691 if outg:
1690 if outg:
1692 if revs:
1691 if revs:
1693 raise error.Abort(_('no revisions allowed with --outgoing'))
1692 raise error.Abort(_('no revisions allowed with --outgoing'))
1694 if len(freeargs) > 1:
1693 if len(freeargs) > 1:
1695 raise error.Abort(
1694 raise error.Abort(
1696 _('only one repo argument allowed with --outgoing'))
1695 _('only one repo argument allowed with --outgoing'))
1697 else:
1696 else:
1698 revs.extend(freeargs)
1697 revs.extend(freeargs)
1699 if len(revs) == 0:
1698 if len(revs) == 0:
1700 defaultrev = destutil.desthistedit(ui, repo)
1699 defaultrev = destutil.desthistedit(ui, repo)
1701 if defaultrev is not None:
1700 if defaultrev is not None:
1702 revs.append(defaultrev)
1701 revs.append(defaultrev)
1703
1702
1704 if len(revs) != 1:
1703 if len(revs) != 1:
1705 raise error.Abort(
1704 raise error.Abort(
1706 _('histedit requires exactly one ancestor revision'))
1705 _('histedit requires exactly one ancestor revision'))
1707
1706
1708 def _histedit(ui, repo, state, *freeargs, **opts):
1707 def _histedit(ui, repo, state, *freeargs, **opts):
1709 opts = pycompat.byteskwargs(opts)
1708 opts = pycompat.byteskwargs(opts)
1710 fm = ui.formatter('histedit', opts)
1709 fm = ui.formatter('histedit', opts)
1711 fm.startitem()
1710 fm.startitem()
1712 goal = _getgoal(opts)
1711 goal = _getgoal(opts)
1713 revs = opts.get('rev', [])
1712 revs = opts.get('rev', [])
1714 nobackup = not ui.configbool('rewrite', 'backup-bundle')
1713 nobackup = not ui.configbool('rewrite', 'backup-bundle')
1715 rules = opts.get('commands', '')
1714 rules = opts.get('commands', '')
1716 state.keep = opts.get('keep', False)
1715 state.keep = opts.get('keep', False)
1717
1716
1718 _validateargs(ui, repo, state, freeargs, opts, goal, rules, revs)
1717 _validateargs(ui, repo, state, freeargs, opts, goal, rules, revs)
1719
1718
1720 hastags = False
1719 hastags = False
1721 if revs:
1720 if revs:
1722 revs = scmutil.revrange(repo, revs)
1721 revs = scmutil.revrange(repo, revs)
1723 ctxs = [repo[rev] for rev in revs]
1722 ctxs = [repo[rev] for rev in revs]
1724 for ctx in ctxs:
1723 for ctx in ctxs:
1725 tags = [tag for tag in ctx.tags() if tag != 'tip']
1724 tags = [tag for tag in ctx.tags() if tag != 'tip']
1726 if not hastags:
1725 if not hastags:
1727 hastags = len(tags)
1726 hastags = len(tags)
1728 if hastags:
1727 if hastags:
1729 if ui.promptchoice(_('warning: tags associated with the given'
1728 if ui.promptchoice(_('warning: tags associated with the given'
1730 ' changeset will be lost after histedit.\n'
1729 ' changeset will be lost after histedit.\n'
1731 'do you want to continue (yN)? $$ &Yes $$ &No'),
1730 'do you want to continue (yN)? $$ &Yes $$ &No'),
1732 default=1):
1731 default=1):
1733 raise error.Abort(_('histedit cancelled\n'))
1732 raise error.Abort(_('histedit cancelled\n'))
1734 # rebuild state
1733 # rebuild state
1735 if goal == goalcontinue:
1734 if goal == goalcontinue:
1736 state.read()
1735 state.read()
1737 state = bootstrapcontinue(ui, state, opts)
1736 state = bootstrapcontinue(ui, state, opts)
1738 elif goal == goaleditplan:
1737 elif goal == goaleditplan:
1739 _edithisteditplan(ui, repo, state, rules)
1738 _edithisteditplan(ui, repo, state, rules)
1740 return
1739 return
1741 elif goal == goalabort:
1740 elif goal == goalabort:
1742 _aborthistedit(ui, repo, state, nobackup=nobackup)
1741 _aborthistedit(ui, repo, state, nobackup=nobackup)
1743 return
1742 return
1744 else:
1743 else:
1745 # goal == goalnew
1744 # goal == goalnew
1746 _newhistedit(ui, repo, state, revs, freeargs, opts)
1745 _newhistedit(ui, repo, state, revs, freeargs, opts)
1747
1746
1748 _continuehistedit(ui, repo, state)
1747 _continuehistedit(ui, repo, state)
1749 _finishhistedit(ui, repo, state, fm)
1748 _finishhistedit(ui, repo, state, fm)
1750 fm.end()
1749 fm.end()
1751
1750
1752 def _continuehistedit(ui, repo, state):
1751 def _continuehistedit(ui, repo, state):
1753 """This function runs after either:
1752 """This function runs after either:
1754 - bootstrapcontinue (if the goal is 'continue')
1753 - bootstrapcontinue (if the goal is 'continue')
1755 - _newhistedit (if the goal is 'new')
1754 - _newhistedit (if the goal is 'new')
1756 """
1755 """
1757 # preprocess rules so that we can hide inner folds from the user
1756 # preprocess rules so that we can hide inner folds from the user
1758 # and only show one editor
1757 # and only show one editor
1759 actions = state.actions[:]
1758 actions = state.actions[:]
1760 for idx, (action, nextact) in enumerate(
1759 for idx, (action, nextact) in enumerate(
1761 zip(actions, actions[1:] + [None])):
1760 zip(actions, actions[1:] + [None])):
1762 if action.verb == 'fold' and nextact and nextact.verb == 'fold':
1761 if action.verb == 'fold' and nextact and nextact.verb == 'fold':
1763 state.actions[idx].__class__ = _multifold
1762 state.actions[idx].__class__ = _multifold
1764
1763
1765 # Force an initial state file write, so the user can run --abort/continue
1764 # Force an initial state file write, so the user can run --abort/continue
1766 # even if there's an exception before the first transaction serialize.
1765 # even if there's an exception before the first transaction serialize.
1767 state.write()
1766 state.write()
1768
1767
1769 tr = None
1768 tr = None
1770 # Don't use singletransaction by default since it rolls the entire
1769 # Don't use singletransaction by default since it rolls the entire
1771 # transaction back if an unexpected exception happens (like a
1770 # transaction back if an unexpected exception happens (like a
1772 # pretxncommit hook throws, or the user aborts the commit msg editor).
1771 # pretxncommit hook throws, or the user aborts the commit msg editor).
1773 if ui.configbool("histedit", "singletransaction"):
1772 if ui.configbool("histedit", "singletransaction"):
1774 # Don't use a 'with' for the transaction, since actions may close
1773 # Don't use a 'with' for the transaction, since actions may close
1775 # and reopen a transaction. For example, if the action executes an
1774 # and reopen a transaction. For example, if the action executes an
1776 # external process it may choose to commit the transaction first.
1775 # external process it may choose to commit the transaction first.
1777 tr = repo.transaction('histedit')
1776 tr = repo.transaction('histedit')
1778 progress = ui.makeprogress(_("editing"), unit=_('changes'),
1777 progress = ui.makeprogress(_("editing"), unit=_('changes'),
1779 total=len(state.actions))
1778 total=len(state.actions))
1780 with progress, util.acceptintervention(tr):
1779 with progress, util.acceptintervention(tr):
1781 while state.actions:
1780 while state.actions:
1782 state.write(tr=tr)
1781 state.write(tr=tr)
1783 actobj = state.actions[0]
1782 actobj = state.actions[0]
1784 progress.increment(item=actobj.torule())
1783 progress.increment(item=actobj.torule())
1785 ui.debug('histedit: processing %s %s\n' % (actobj.verb,
1784 ui.debug('histedit: processing %s %s\n' % (actobj.verb,
1786 actobj.torule()))
1785 actobj.torule()))
1787 parentctx, replacement_ = actobj.run()
1786 parentctx, replacement_ = actobj.run()
1788 state.parentctxnode = parentctx.node()
1787 state.parentctxnode = parentctx.node()
1789 state.replacements.extend(replacement_)
1788 state.replacements.extend(replacement_)
1790 state.actions.pop(0)
1789 state.actions.pop(0)
1791
1790
1792 state.write()
1791 state.write()
1793
1792
1794 def _finishhistedit(ui, repo, state, fm):
1793 def _finishhistedit(ui, repo, state, fm):
1795 """This action runs when histedit is finishing its session"""
1794 """This action runs when histedit is finishing its session"""
1796 hg.updaterepo(repo, state.parentctxnode, overwrite=False)
1795 hg.updaterepo(repo, state.parentctxnode, overwrite=False)
1797
1796
1798 mapping, tmpnodes, created, ntm = processreplacement(state)
1797 mapping, tmpnodes, created, ntm = processreplacement(state)
1799 if mapping:
1798 if mapping:
1800 for prec, succs in mapping.iteritems():
1799 for prec, succs in mapping.iteritems():
1801 if not succs:
1800 if not succs:
1802 ui.debug('histedit: %s is dropped\n' % node.short(prec))
1801 ui.debug('histedit: %s is dropped\n' % node.short(prec))
1803 else:
1802 else:
1804 ui.debug('histedit: %s is replaced by %s\n' % (
1803 ui.debug('histedit: %s is replaced by %s\n' % (
1805 node.short(prec), node.short(succs[0])))
1804 node.short(prec), node.short(succs[0])))
1806 if len(succs) > 1:
1805 if len(succs) > 1:
1807 m = 'histedit: %s'
1806 m = 'histedit: %s'
1808 for n in succs[1:]:
1807 for n in succs[1:]:
1809 ui.debug(m % node.short(n))
1808 ui.debug(m % node.short(n))
1810
1809
1811 if not state.keep:
1810 if not state.keep:
1812 if mapping:
1811 if mapping:
1813 movetopmostbookmarks(repo, state.topmost, ntm)
1812 movetopmostbookmarks(repo, state.topmost, ntm)
1814 # TODO update mq state
1813 # TODO update mq state
1815 else:
1814 else:
1816 mapping = {}
1815 mapping = {}
1817
1816
1818 for n in tmpnodes:
1817 for n in tmpnodes:
1819 if n in repo:
1818 if n in repo:
1820 mapping[n] = ()
1819 mapping[n] = ()
1821
1820
1822 # remove entries about unknown nodes
1821 # remove entries about unknown nodes
1823 nodemap = repo.unfiltered().changelog.nodemap
1822 nodemap = repo.unfiltered().changelog.nodemap
1824 mapping = {k: v for k, v in mapping.items()
1823 mapping = {k: v for k, v in mapping.items()
1825 if k in nodemap and all(n in nodemap for n in v)}
1824 if k in nodemap and all(n in nodemap for n in v)}
1826 scmutil.cleanupnodes(repo, mapping, 'histedit')
1825 scmutil.cleanupnodes(repo, mapping, 'histedit')
1827 hf = fm.hexfunc
1826 hf = fm.hexfunc
1828 fl = fm.formatlist
1827 fl = fm.formatlist
1829 fd = fm.formatdict
1828 fd = fm.formatdict
1830 nodechanges = fd({hf(oldn): fl([hf(n) for n in newn], name='node')
1829 nodechanges = fd({hf(oldn): fl([hf(n) for n in newn], name='node')
1831 for oldn, newn in mapping.iteritems()},
1830 for oldn, newn in mapping.iteritems()},
1832 key="oldnode", value="newnodes")
1831 key="oldnode", value="newnodes")
1833 fm.data(nodechanges=nodechanges)
1832 fm.data(nodechanges=nodechanges)
1834
1833
1835 state.clear()
1834 state.clear()
1836 if os.path.exists(repo.sjoin('undo')):
1835 if os.path.exists(repo.sjoin('undo')):
1837 os.unlink(repo.sjoin('undo'))
1836 os.unlink(repo.sjoin('undo'))
1838 if repo.vfs.exists('histedit-last-edit.txt'):
1837 if repo.vfs.exists('histedit-last-edit.txt'):
1839 repo.vfs.unlink('histedit-last-edit.txt')
1838 repo.vfs.unlink('histedit-last-edit.txt')
1840
1839
1841 def _aborthistedit(ui, repo, state, nobackup=False):
1840 def _aborthistedit(ui, repo, state, nobackup=False):
1842 try:
1841 try:
1843 state.read()
1842 state.read()
1844 __, leafs, tmpnodes, __ = processreplacement(state)
1843 __, leafs, tmpnodes, __ = processreplacement(state)
1845 ui.debug('restore wc to old parent %s\n'
1844 ui.debug('restore wc to old parent %s\n'
1846 % node.short(state.topmost))
1845 % node.short(state.topmost))
1847
1846
1848 # Recover our old commits if necessary
1847 # Recover our old commits if necessary
1849 if not state.topmost in repo and state.backupfile:
1848 if not state.topmost in repo and state.backupfile:
1850 backupfile = repo.vfs.join(state.backupfile)
1849 backupfile = repo.vfs.join(state.backupfile)
1851 f = hg.openpath(ui, backupfile)
1850 f = hg.openpath(ui, backupfile)
1852 gen = exchange.readbundle(ui, f, backupfile)
1851 gen = exchange.readbundle(ui, f, backupfile)
1853 with repo.transaction('histedit.abort') as tr:
1852 with repo.transaction('histedit.abort') as tr:
1854 bundle2.applybundle(repo, gen, tr, source='histedit',
1853 bundle2.applybundle(repo, gen, tr, source='histedit',
1855 url='bundle:' + backupfile)
1854 url='bundle:' + backupfile)
1856
1855
1857 os.remove(backupfile)
1856 os.remove(backupfile)
1858
1857
1859 # check whether we should update away
1858 # check whether we should update away
1860 if repo.unfiltered().revs('parents() and (%n or %ln::)',
1859 if repo.unfiltered().revs('parents() and (%n or %ln::)',
1861 state.parentctxnode, leafs | tmpnodes):
1860 state.parentctxnode, leafs | tmpnodes):
1862 hg.clean(repo, state.topmost, show_stats=True, quietempty=True)
1861 hg.clean(repo, state.topmost, show_stats=True, quietempty=True)
1863 cleanupnode(ui, repo, tmpnodes, nobackup=nobackup)
1862 cleanupnode(ui, repo, tmpnodes, nobackup=nobackup)
1864 cleanupnode(ui, repo, leafs, nobackup=nobackup)
1863 cleanupnode(ui, repo, leafs, nobackup=nobackup)
1865 except Exception:
1864 except Exception:
1866 if state.inprogress():
1865 if state.inprogress():
1867 ui.warn(_('warning: encountered an exception during histedit '
1866 ui.warn(_('warning: encountered an exception during histedit '
1868 '--abort; the repository may not have been completely '
1867 '--abort; the repository may not have been completely '
1869 'cleaned up\n'))
1868 'cleaned up\n'))
1870 raise
1869 raise
1871 finally:
1870 finally:
1872 state.clear()
1871 state.clear()
1873
1872
1874 def _edithisteditplan(ui, repo, state, rules):
1873 def _edithisteditplan(ui, repo, state, rules):
1875 state.read()
1874 state.read()
1876 if not rules:
1875 if not rules:
1877 comment = geteditcomment(ui,
1876 comment = geteditcomment(ui,
1878 node.short(state.parentctxnode),
1877 node.short(state.parentctxnode),
1879 node.short(state.topmost))
1878 node.short(state.topmost))
1880 rules = ruleeditor(repo, ui, state.actions, comment)
1879 rules = ruleeditor(repo, ui, state.actions, comment)
1881 else:
1880 else:
1882 rules = _readfile(ui, rules)
1881 rules = _readfile(ui, rules)
1883 actions = parserules(rules, state)
1882 actions = parserules(rules, state)
1884 ctxs = [repo[act.node]
1883 ctxs = [repo[act.node]
1885 for act in state.actions if act.node]
1884 for act in state.actions if act.node]
1886 warnverifyactions(ui, repo, actions, state, ctxs)
1885 warnverifyactions(ui, repo, actions, state, ctxs)
1887 state.actions = actions
1886 state.actions = actions
1888 state.write()
1887 state.write()
1889
1888
1890 def _newhistedit(ui, repo, state, revs, freeargs, opts):
1889 def _newhistedit(ui, repo, state, revs, freeargs, opts):
1891 outg = opts.get('outgoing')
1890 outg = opts.get('outgoing')
1892 rules = opts.get('commands', '')
1891 rules = opts.get('commands', '')
1893 force = opts.get('force')
1892 force = opts.get('force')
1894
1893
1895 cmdutil.checkunfinished(repo)
1894 cmdutil.checkunfinished(repo)
1896 cmdutil.bailifchanged(repo)
1895 cmdutil.bailifchanged(repo)
1897
1896
1898 topmost = repo.dirstate.p1()
1897 topmost = repo.dirstate.p1()
1899 if outg:
1898 if outg:
1900 if freeargs:
1899 if freeargs:
1901 remote = freeargs[0]
1900 remote = freeargs[0]
1902 else:
1901 else:
1903 remote = None
1902 remote = None
1904 root = findoutgoing(ui, repo, remote, force, opts)
1903 root = findoutgoing(ui, repo, remote, force, opts)
1905 else:
1904 else:
1906 rr = list(repo.set('roots(%ld)', scmutil.revrange(repo, revs)))
1905 rr = list(repo.set('roots(%ld)', scmutil.revrange(repo, revs)))
1907 if len(rr) != 1:
1906 if len(rr) != 1:
1908 raise error.Abort(_('The specified revisions must have '
1907 raise error.Abort(_('The specified revisions must have '
1909 'exactly one common root'))
1908 'exactly one common root'))
1910 root = rr[0].node()
1909 root = rr[0].node()
1911
1910
1912 revs = between(repo, root, topmost, state.keep)
1911 revs = between(repo, root, topmost, state.keep)
1913 if not revs:
1912 if not revs:
1914 raise error.Abort(_('%s is not an ancestor of working directory') %
1913 raise error.Abort(_('%s is not an ancestor of working directory') %
1915 node.short(root))
1914 node.short(root))
1916
1915
1917 ctxs = [repo[r] for r in revs]
1916 ctxs = [repo[r] for r in revs]
1918 if not rules:
1917 if not rules:
1919 comment = geteditcomment(ui, node.short(root), node.short(topmost))
1918 comment = geteditcomment(ui, node.short(root), node.short(topmost))
1920 actions = [pick(state, r) for r in revs]
1919 actions = [pick(state, r) for r in revs]
1921 rules = ruleeditor(repo, ui, actions, comment)
1920 rules = ruleeditor(repo, ui, actions, comment)
1922 else:
1921 else:
1923 rules = _readfile(ui, rules)
1922 rules = _readfile(ui, rules)
1924 actions = parserules(rules, state)
1923 actions = parserules(rules, state)
1925 warnverifyactions(ui, repo, actions, state, ctxs)
1924 warnverifyactions(ui, repo, actions, state, ctxs)
1926
1925
1927 parentctxnode = repo[root].p1().node()
1926 parentctxnode = repo[root].p1().node()
1928
1927
1929 state.parentctxnode = parentctxnode
1928 state.parentctxnode = parentctxnode
1930 state.actions = actions
1929 state.actions = actions
1931 state.topmost = topmost
1930 state.topmost = topmost
1932 state.replacements = []
1931 state.replacements = []
1933
1932
1934 ui.log("histedit", "%d actions to histedit\n", len(actions),
1933 ui.log("histedit", "%d actions to histedit\n", len(actions),
1935 histedit_num_actions=len(actions))
1934 histedit_num_actions=len(actions))
1936
1935
1937 # Create a backup so we can always abort completely.
1936 # Create a backup so we can always abort completely.
1938 backupfile = None
1937 backupfile = None
1939 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1938 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1940 backupfile = repair.backupbundle(repo, [parentctxnode],
1939 backupfile = repair.backupbundle(repo, [parentctxnode],
1941 [topmost], root, 'histedit')
1940 [topmost], root, 'histedit')
1942 state.backupfile = backupfile
1941 state.backupfile = backupfile
1943
1942
1944 def _getsummary(ctx):
1943 def _getsummary(ctx):
1945 # a common pattern is to extract the summary but default to the empty
1944 # a common pattern is to extract the summary but default to the empty
1946 # string
1945 # string
1947 summary = ctx.description() or ''
1946 summary = ctx.description() or ''
1948 if summary:
1947 if summary:
1949 summary = summary.splitlines()[0]
1948 summary = summary.splitlines()[0]
1950 return summary
1949 return summary
1951
1950
1952 def bootstrapcontinue(ui, state, opts):
1951 def bootstrapcontinue(ui, state, opts):
1953 repo = state.repo
1952 repo = state.repo
1954
1953
1955 ms = mergemod.mergestate.read(repo)
1954 ms = mergemod.mergestate.read(repo)
1956 mergeutil.checkunresolved(ms)
1955 mergeutil.checkunresolved(ms)
1957
1956
1958 if state.actions:
1957 if state.actions:
1959 actobj = state.actions.pop(0)
1958 actobj = state.actions.pop(0)
1960
1959
1961 if _isdirtywc(repo):
1960 if _isdirtywc(repo):
1962 actobj.continuedirty()
1961 actobj.continuedirty()
1963 if _isdirtywc(repo):
1962 if _isdirtywc(repo):
1964 abortdirty()
1963 abortdirty()
1965
1964
1966 parentctx, replacements = actobj.continueclean()
1965 parentctx, replacements = actobj.continueclean()
1967
1966
1968 state.parentctxnode = parentctx.node()
1967 state.parentctxnode = parentctx.node()
1969 state.replacements.extend(replacements)
1968 state.replacements.extend(replacements)
1970
1969
1971 return state
1970 return state
1972
1971
1973 def between(repo, old, new, keep):
1972 def between(repo, old, new, keep):
1974 """select and validate the set of revision to edit
1973 """select and validate the set of revision to edit
1975
1974
1976 When keep is false, the specified set can't have children."""
1975 When keep is false, the specified set can't have children."""
1977 revs = repo.revs('%n::%n', old, new)
1976 revs = repo.revs('%n::%n', old, new)
1978 if revs and not keep:
1977 if revs and not keep:
1979 if (not obsolete.isenabled(repo, obsolete.allowunstableopt) and
1978 if (not obsolete.isenabled(repo, obsolete.allowunstableopt) and
1980 repo.revs('(%ld::) - (%ld)', revs, revs)):
1979 repo.revs('(%ld::) - (%ld)', revs, revs)):
1981 raise error.Abort(_('can only histedit a changeset together '
1980 raise error.Abort(_('can only histedit a changeset together '
1982 'with all its descendants'))
1981 'with all its descendants'))
1983 if repo.revs('(%ld) and merge()', revs):
1982 if repo.revs('(%ld) and merge()', revs):
1984 raise error.Abort(_('cannot edit history that contains merges'))
1983 raise error.Abort(_('cannot edit history that contains merges'))
1985 root = repo[revs.first()] # list is already sorted by repo.revs()
1984 root = repo[revs.first()] # list is already sorted by repo.revs()
1986 if not root.mutable():
1985 if not root.mutable():
1987 raise error.Abort(_('cannot edit public changeset: %s') % root,
1986 raise error.Abort(_('cannot edit public changeset: %s') % root,
1988 hint=_("see 'hg help phases' for details"))
1987 hint=_("see 'hg help phases' for details"))
1989 return pycompat.maplist(repo.changelog.node, revs)
1988 return pycompat.maplist(repo.changelog.node, revs)
1990
1989
1991 def ruleeditor(repo, ui, actions, editcomment=""):
1990 def ruleeditor(repo, ui, actions, editcomment=""):
1992 """open an editor to edit rules
1991 """open an editor to edit rules
1993
1992
1994 rules are in the format [ [act, ctx], ...] like in state.rules
1993 rules are in the format [ [act, ctx], ...] like in state.rules
1995 """
1994 """
1996 if repo.ui.configbool("experimental", "histedit.autoverb"):
1995 if repo.ui.configbool("experimental", "histedit.autoverb"):
1997 newact = util.sortdict()
1996 newact = util.sortdict()
1998 for act in actions:
1997 for act in actions:
1999 ctx = repo[act.node]
1998 ctx = repo[act.node]
2000 summary = _getsummary(ctx)
1999 summary = _getsummary(ctx)
2001 fword = summary.split(' ', 1)[0].lower()
2000 fword = summary.split(' ', 1)[0].lower()
2002 added = False
2001 added = False
2003
2002
2004 # if it doesn't end with the special character '!' just skip this
2003 # if it doesn't end with the special character '!' just skip this
2005 if fword.endswith('!'):
2004 if fword.endswith('!'):
2006 fword = fword[:-1]
2005 fword = fword[:-1]
2007 if fword in primaryactions | secondaryactions | tertiaryactions:
2006 if fword in primaryactions | secondaryactions | tertiaryactions:
2008 act.verb = fword
2007 act.verb = fword
2009 # get the target summary
2008 # get the target summary
2010 tsum = summary[len(fword) + 1:].lstrip()
2009 tsum = summary[len(fword) + 1:].lstrip()
2011 # safe but slow: reverse iterate over the actions so we
2010 # safe but slow: reverse iterate over the actions so we
2012 # don't clash on two commits having the same summary
2011 # don't clash on two commits having the same summary
2013 for na, l in reversed(list(newact.iteritems())):
2012 for na, l in reversed(list(newact.iteritems())):
2014 actx = repo[na.node]
2013 actx = repo[na.node]
2015 asum = _getsummary(actx)
2014 asum = _getsummary(actx)
2016 if asum == tsum:
2015 if asum == tsum:
2017 added = True
2016 added = True
2018 l.append(act)
2017 l.append(act)
2019 break
2018 break
2020
2019
2021 if not added:
2020 if not added:
2022 newact[act] = []
2021 newact[act] = []
2023
2022
2024 # copy over and flatten the new list
2023 # copy over and flatten the new list
2025 actions = []
2024 actions = []
2026 for na, l in newact.iteritems():
2025 for na, l in newact.iteritems():
2027 actions.append(na)
2026 actions.append(na)
2028 actions += l
2027 actions += l
2029
2028
2030 rules = '\n'.join([act.torule() for act in actions])
2029 rules = '\n'.join([act.torule() for act in actions])
2031 rules += '\n\n'
2030 rules += '\n\n'
2032 rules += editcomment
2031 rules += editcomment
2033 rules = ui.edit(rules, ui.username(), {'prefix': 'histedit'},
2032 rules = ui.edit(rules, ui.username(), {'prefix': 'histedit'},
2034 repopath=repo.path, action='histedit')
2033 repopath=repo.path, action='histedit')
2035
2034
2036 # Save edit rules in .hg/histedit-last-edit.txt in case
2035 # Save edit rules in .hg/histedit-last-edit.txt in case
2037 # the user needs to ask for help after something
2036 # the user needs to ask for help after something
2038 # surprising happens.
2037 # surprising happens.
2039 with repo.vfs('histedit-last-edit.txt', 'wb') as f:
2038 with repo.vfs('histedit-last-edit.txt', 'wb') as f:
2040 f.write(rules)
2039 f.write(rules)
2041
2040
2042 return rules
2041 return rules
2043
2042
2044 def parserules(rules, state):
2043 def parserules(rules, state):
2045 """Read the histedit rules string and return list of action objects """
2044 """Read the histedit rules string and return list of action objects """
2046 rules = [l for l in (r.strip() for r in rules.splitlines())
2045 rules = [l for l in (r.strip() for r in rules.splitlines())
2047 if l and not l.startswith('#')]
2046 if l and not l.startswith('#')]
2048 actions = []
2047 actions = []
2049 for r in rules:
2048 for r in rules:
2050 if ' ' not in r:
2049 if ' ' not in r:
2051 raise error.ParseError(_('malformed line "%s"') % r)
2050 raise error.ParseError(_('malformed line "%s"') % r)
2052 verb, rest = r.split(' ', 1)
2051 verb, rest = r.split(' ', 1)
2053
2052
2054 if verb not in actiontable:
2053 if verb not in actiontable:
2055 raise error.ParseError(_('unknown action "%s"') % verb)
2054 raise error.ParseError(_('unknown action "%s"') % verb)
2056
2055
2057 action = actiontable[verb].fromrule(state, rest)
2056 action = actiontable[verb].fromrule(state, rest)
2058 actions.append(action)
2057 actions.append(action)
2059 return actions
2058 return actions
2060
2059
2061 def warnverifyactions(ui, repo, actions, state, ctxs):
2060 def warnverifyactions(ui, repo, actions, state, ctxs):
2062 try:
2061 try:
2063 verifyactions(actions, state, ctxs)
2062 verifyactions(actions, state, ctxs)
2064 except error.ParseError:
2063 except error.ParseError:
2065 if repo.vfs.exists('histedit-last-edit.txt'):
2064 if repo.vfs.exists('histedit-last-edit.txt'):
2066 ui.warn(_('warning: histedit rules saved '
2065 ui.warn(_('warning: histedit rules saved '
2067 'to: .hg/histedit-last-edit.txt\n'))
2066 'to: .hg/histedit-last-edit.txt\n'))
2068 raise
2067 raise
2069
2068
2070 def verifyactions(actions, state, ctxs):
2069 def verifyactions(actions, state, ctxs):
2071 """Verify that there exists exactly one action per given changeset and
2070 """Verify that there exists exactly one action per given changeset and
2072 other constraints.
2071 other constraints.
2073
2072
2074 Will abort if there are to many or too few rules, a malformed rule,
2073 Will abort if there are to many or too few rules, a malformed rule,
2075 or a rule on a changeset outside of the user-given range.
2074 or a rule on a changeset outside of the user-given range.
2076 """
2075 """
2077 expected = set(c.node() for c in ctxs)
2076 expected = set(c.node() for c in ctxs)
2078 seen = set()
2077 seen = set()
2079 prev = None
2078 prev = None
2080
2079
2081 if actions and actions[0].verb in ['roll', 'fold']:
2080 if actions and actions[0].verb in ['roll', 'fold']:
2082 raise error.ParseError(_('first changeset cannot use verb "%s"') %
2081 raise error.ParseError(_('first changeset cannot use verb "%s"') %
2083 actions[0].verb)
2082 actions[0].verb)
2084
2083
2085 for action in actions:
2084 for action in actions:
2086 action.verify(prev, expected, seen)
2085 action.verify(prev, expected, seen)
2087 prev = action
2086 prev = action
2088 if action.node is not None:
2087 if action.node is not None:
2089 seen.add(action.node)
2088 seen.add(action.node)
2090 missing = sorted(expected - seen) # sort to stabilize output
2089 missing = sorted(expected - seen) # sort to stabilize output
2091
2090
2092 if state.repo.ui.configbool('histedit', 'dropmissing'):
2091 if state.repo.ui.configbool('histedit', 'dropmissing'):
2093 if len(actions) == 0:
2092 if len(actions) == 0:
2094 raise error.ParseError(_('no rules provided'),
2093 raise error.ParseError(_('no rules provided'),
2095 hint=_('use strip extension to remove commits'))
2094 hint=_('use strip extension to remove commits'))
2096
2095
2097 drops = [drop(state, n) for n in missing]
2096 drops = [drop(state, n) for n in missing]
2098 # put the in the beginning so they execute immediately and
2097 # put the in the beginning so they execute immediately and
2099 # don't show in the edit-plan in the future
2098 # don't show in the edit-plan in the future
2100 actions[:0] = drops
2099 actions[:0] = drops
2101 elif missing:
2100 elif missing:
2102 raise error.ParseError(_('missing rules for changeset %s') %
2101 raise error.ParseError(_('missing rules for changeset %s') %
2103 node.short(missing[0]),
2102 node.short(missing[0]),
2104 hint=_('use "drop %s" to discard, see also: '
2103 hint=_('use "drop %s" to discard, see also: '
2105 "'hg help -e histedit.config'")
2104 "'hg help -e histedit.config'")
2106 % node.short(missing[0]))
2105 % node.short(missing[0]))
2107
2106
2108 def adjustreplacementsfrommarkers(repo, oldreplacements):
2107 def adjustreplacementsfrommarkers(repo, oldreplacements):
2109 """Adjust replacements from obsolescence markers
2108 """Adjust replacements from obsolescence markers
2110
2109
2111 Replacements structure is originally generated based on
2110 Replacements structure is originally generated based on
2112 histedit's state and does not account for changes that are
2111 histedit's state and does not account for changes that are
2113 not recorded there. This function fixes that by adding
2112 not recorded there. This function fixes that by adding
2114 data read from obsolescence markers"""
2113 data read from obsolescence markers"""
2115 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2114 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2116 return oldreplacements
2115 return oldreplacements
2117
2116
2118 unfi = repo.unfiltered()
2117 unfi = repo.unfiltered()
2119 nm = unfi.changelog.nodemap
2118 nm = unfi.changelog.nodemap
2120 obsstore = repo.obsstore
2119 obsstore = repo.obsstore
2121 newreplacements = list(oldreplacements)
2120 newreplacements = list(oldreplacements)
2122 oldsuccs = [r[1] for r in oldreplacements]
2121 oldsuccs = [r[1] for r in oldreplacements]
2123 # successors that have already been added to succstocheck once
2122 # successors that have already been added to succstocheck once
2124 seensuccs = set().union(*oldsuccs) # create a set from an iterable of tuples
2123 seensuccs = set().union(*oldsuccs) # create a set from an iterable of tuples
2125 succstocheck = list(seensuccs)
2124 succstocheck = list(seensuccs)
2126 while succstocheck:
2125 while succstocheck:
2127 n = succstocheck.pop()
2126 n = succstocheck.pop()
2128 missing = nm.get(n) is None
2127 missing = nm.get(n) is None
2129 markers = obsstore.successors.get(n, ())
2128 markers = obsstore.successors.get(n, ())
2130 if missing and not markers:
2129 if missing and not markers:
2131 # dead end, mark it as such
2130 # dead end, mark it as such
2132 newreplacements.append((n, ()))
2131 newreplacements.append((n, ()))
2133 for marker in markers:
2132 for marker in markers:
2134 nsuccs = marker[1]
2133 nsuccs = marker[1]
2135 newreplacements.append((n, nsuccs))
2134 newreplacements.append((n, nsuccs))
2136 for nsucc in nsuccs:
2135 for nsucc in nsuccs:
2137 if nsucc not in seensuccs:
2136 if nsucc not in seensuccs:
2138 seensuccs.add(nsucc)
2137 seensuccs.add(nsucc)
2139 succstocheck.append(nsucc)
2138 succstocheck.append(nsucc)
2140
2139
2141 return newreplacements
2140 return newreplacements
2142
2141
2143 def processreplacement(state):
2142 def processreplacement(state):
2144 """process the list of replacements to return
2143 """process the list of replacements to return
2145
2144
2146 1) the final mapping between original and created nodes
2145 1) the final mapping between original and created nodes
2147 2) the list of temporary node created by histedit
2146 2) the list of temporary node created by histedit
2148 3) the list of new commit created by histedit"""
2147 3) the list of new commit created by histedit"""
2149 replacements = adjustreplacementsfrommarkers(state.repo, state.replacements)
2148 replacements = adjustreplacementsfrommarkers(state.repo, state.replacements)
2150 allsuccs = set()
2149 allsuccs = set()
2151 replaced = set()
2150 replaced = set()
2152 fullmapping = {}
2151 fullmapping = {}
2153 # initialize basic set
2152 # initialize basic set
2154 # fullmapping records all operations recorded in replacement
2153 # fullmapping records all operations recorded in replacement
2155 for rep in replacements:
2154 for rep in replacements:
2156 allsuccs.update(rep[1])
2155 allsuccs.update(rep[1])
2157 replaced.add(rep[0])
2156 replaced.add(rep[0])
2158 fullmapping.setdefault(rep[0], set()).update(rep[1])
2157 fullmapping.setdefault(rep[0], set()).update(rep[1])
2159 new = allsuccs - replaced
2158 new = allsuccs - replaced
2160 tmpnodes = allsuccs & replaced
2159 tmpnodes = allsuccs & replaced
2161 # Reduce content fullmapping into direct relation between original nodes
2160 # Reduce content fullmapping into direct relation between original nodes
2162 # and final node created during history edition
2161 # and final node created during history edition
2163 # Dropped changeset are replaced by an empty list
2162 # Dropped changeset are replaced by an empty list
2164 toproceed = set(fullmapping)
2163 toproceed = set(fullmapping)
2165 final = {}
2164 final = {}
2166 while toproceed:
2165 while toproceed:
2167 for x in list(toproceed):
2166 for x in list(toproceed):
2168 succs = fullmapping[x]
2167 succs = fullmapping[x]
2169 for s in list(succs):
2168 for s in list(succs):
2170 if s in toproceed:
2169 if s in toproceed:
2171 # non final node with unknown closure
2170 # non final node with unknown closure
2172 # We can't process this now
2171 # We can't process this now
2173 break
2172 break
2174 elif s in final:
2173 elif s in final:
2175 # non final node, replace with closure
2174 # non final node, replace with closure
2176 succs.remove(s)
2175 succs.remove(s)
2177 succs.update(final[s])
2176 succs.update(final[s])
2178 else:
2177 else:
2179 final[x] = succs
2178 final[x] = succs
2180 toproceed.remove(x)
2179 toproceed.remove(x)
2181 # remove tmpnodes from final mapping
2180 # remove tmpnodes from final mapping
2182 for n in tmpnodes:
2181 for n in tmpnodes:
2183 del final[n]
2182 del final[n]
2184 # we expect all changes involved in final to exist in the repo
2183 # we expect all changes involved in final to exist in the repo
2185 # turn `final` into list (topologically sorted)
2184 # turn `final` into list (topologically sorted)
2186 nm = state.repo.changelog.nodemap
2185 nm = state.repo.changelog.nodemap
2187 for prec, succs in final.items():
2186 for prec, succs in final.items():
2188 final[prec] = sorted(succs, key=nm.get)
2187 final[prec] = sorted(succs, key=nm.get)
2189
2188
2190 # computed topmost element (necessary for bookmark)
2189 # computed topmost element (necessary for bookmark)
2191 if new:
2190 if new:
2192 newtopmost = sorted(new, key=state.repo.changelog.rev)[-1]
2191 newtopmost = sorted(new, key=state.repo.changelog.rev)[-1]
2193 elif not final:
2192 elif not final:
2194 # Nothing rewritten at all. we won't need `newtopmost`
2193 # Nothing rewritten at all. we won't need `newtopmost`
2195 # It is the same as `oldtopmost` and `processreplacement` know it
2194 # It is the same as `oldtopmost` and `processreplacement` know it
2196 newtopmost = None
2195 newtopmost = None
2197 else:
2196 else:
2198 # every body died. The newtopmost is the parent of the root.
2197 # every body died. The newtopmost is the parent of the root.
2199 r = state.repo.changelog.rev
2198 r = state.repo.changelog.rev
2200 newtopmost = state.repo[sorted(final, key=r)[0]].p1().node()
2199 newtopmost = state.repo[sorted(final, key=r)[0]].p1().node()
2201
2200
2202 return final, tmpnodes, new, newtopmost
2201 return final, tmpnodes, new, newtopmost
2203
2202
2204 def movetopmostbookmarks(repo, oldtopmost, newtopmost):
2203 def movetopmostbookmarks(repo, oldtopmost, newtopmost):
2205 """Move bookmark from oldtopmost to newly created topmost
2204 """Move bookmark from oldtopmost to newly created topmost
2206
2205
2207 This is arguably a feature and we may only want that for the active
2206 This is arguably a feature and we may only want that for the active
2208 bookmark. But the behavior is kept compatible with the old version for now.
2207 bookmark. But the behavior is kept compatible with the old version for now.
2209 """
2208 """
2210 if not oldtopmost or not newtopmost:
2209 if not oldtopmost or not newtopmost:
2211 return
2210 return
2212 oldbmarks = repo.nodebookmarks(oldtopmost)
2211 oldbmarks = repo.nodebookmarks(oldtopmost)
2213 if oldbmarks:
2212 if oldbmarks:
2214 with repo.lock(), repo.transaction('histedit') as tr:
2213 with repo.lock(), repo.transaction('histedit') as tr:
2215 marks = repo._bookmarks
2214 marks = repo._bookmarks
2216 changes = []
2215 changes = []
2217 for name in oldbmarks:
2216 for name in oldbmarks:
2218 changes.append((name, newtopmost))
2217 changes.append((name, newtopmost))
2219 marks.applychanges(repo, tr, changes)
2218 marks.applychanges(repo, tr, changes)
2220
2219
2221 def cleanupnode(ui, repo, nodes, nobackup=False):
2220 def cleanupnode(ui, repo, nodes, nobackup=False):
2222 """strip a group of nodes from the repository
2221 """strip a group of nodes from the repository
2223
2222
2224 The set of node to strip may contains unknown nodes."""
2223 The set of node to strip may contains unknown nodes."""
2225 with repo.lock():
2224 with repo.lock():
2226 # do not let filtering get in the way of the cleanse
2225 # do not let filtering get in the way of the cleanse
2227 # we should probably get rid of obsolescence marker created during the
2226 # we should probably get rid of obsolescence marker created during the
2228 # histedit, but we currently do not have such information.
2227 # histedit, but we currently do not have such information.
2229 repo = repo.unfiltered()
2228 repo = repo.unfiltered()
2230 # Find all nodes that need to be stripped
2229 # Find all nodes that need to be stripped
2231 # (we use %lr instead of %ln to silently ignore unknown items)
2230 # (we use %lr instead of %ln to silently ignore unknown items)
2232 nm = repo.changelog.nodemap
2231 nm = repo.changelog.nodemap
2233 nodes = sorted(n for n in nodes if n in nm)
2232 nodes = sorted(n for n in nodes if n in nm)
2234 roots = [c.node() for c in repo.set("roots(%ln)", nodes)]
2233 roots = [c.node() for c in repo.set("roots(%ln)", nodes)]
2235 if roots:
2234 if roots:
2236 backup = not nobackup
2235 backup = not nobackup
2237 repair.strip(ui, repo, roots, backup=backup)
2236 repair.strip(ui, repo, roots, backup=backup)
2238
2237
2239 def stripwrapper(orig, ui, repo, nodelist, *args, **kwargs):
2238 def stripwrapper(orig, ui, repo, nodelist, *args, **kwargs):
2240 if isinstance(nodelist, str):
2239 if isinstance(nodelist, str):
2241 nodelist = [nodelist]
2240 nodelist = [nodelist]
2242 state = histeditstate(repo)
2241 state = histeditstate(repo)
2243 if state.inprogress():
2242 if state.inprogress():
2244 state.read()
2243 state.read()
2245 histedit_nodes = {action.node for action
2244 histedit_nodes = {action.node for action
2246 in state.actions if action.node}
2245 in state.actions if action.node}
2247 common_nodes = histedit_nodes & set(nodelist)
2246 common_nodes = histedit_nodes & set(nodelist)
2248 if common_nodes:
2247 if common_nodes:
2249 raise error.Abort(_("histedit in progress, can't strip %s")
2248 raise error.Abort(_("histedit in progress, can't strip %s")
2250 % ', '.join(node.short(x) for x in common_nodes))
2249 % ', '.join(node.short(x) for x in common_nodes))
2251 return orig(ui, repo, nodelist, *args, **kwargs)
2250 return orig(ui, repo, nodelist, *args, **kwargs)
2252
2251
2253 extensions.wrapfunction(repair, 'strip', stripwrapper)
2252 extensions.wrapfunction(repair, 'strip', stripwrapper)
2254
2253
2255 def summaryhook(ui, repo):
2254 def summaryhook(ui, repo):
2256 state = histeditstate(repo)
2255 state = histeditstate(repo)
2257 if not state.inprogress():
2256 if not state.inprogress():
2258 return
2257 return
2259 state.read()
2258 state.read()
2260 if state.actions:
2259 if state.actions:
2261 # i18n: column positioning for "hg summary"
2260 # i18n: column positioning for "hg summary"
2262 ui.write(_('hist: %s (histedit --continue)\n') %
2261 ui.write(_('hist: %s (histedit --continue)\n') %
2263 (ui.label(_('%d remaining'), 'histedit.remaining') %
2262 (ui.label(_('%d remaining'), 'histedit.remaining') %
2264 len(state.actions)))
2263 len(state.actions)))
2265
2264
2266 def extsetup(ui):
2265 def extsetup(ui):
2267 cmdutil.summaryhooks.add('histedit', summaryhook)
2266 cmdutil.summaryhooks.add('histedit', summaryhook)
2268 cmdutil.unfinishedstates.append(
2267 cmdutil.unfinishedstates.append(
2269 ['histedit-state', False, True, _('histedit in progress'),
2268 ['histedit-state', False, True, _('histedit in progress'),
2270 _("use 'hg histedit --continue' or 'hg histedit --abort'")])
2269 _("use 'hg histedit --continue' or 'hg histedit --abort'")])
2271 cmdutil.afterresolvedstates.append(
2270 cmdutil.afterresolvedstates.append(
2272 ['histedit-state', _('hg histedit --continue')])
2271 ['histedit-state', _('hg histedit --continue')])
@@ -1,597 +1,595 b''
1 Test argument handling and various data parsing
1 Test argument handling and various data parsing
2 ==================================================
2 ==================================================
3
3
4
4
5 Enable extensions used by this test.
5 Enable extensions used by this test.
6 $ cat >>$HGRCPATH <<EOF
6 $ cat >>$HGRCPATH <<EOF
7 > [extensions]
7 > [extensions]
8 > histedit=
8 > histedit=
9 > EOF
9 > EOF
10
10
11 Repo setup.
11 Repo setup.
12 $ hg init foo
12 $ hg init foo
13 $ cd foo
13 $ cd foo
14 $ echo alpha >> alpha
14 $ echo alpha >> alpha
15 $ hg addr
15 $ hg addr
16 adding alpha
16 adding alpha
17 $ hg ci -m one
17 $ hg ci -m one
18 $ echo alpha >> alpha
18 $ echo alpha >> alpha
19 $ hg ci -m two
19 $ hg ci -m two
20 $ echo alpha >> alpha
20 $ echo alpha >> alpha
21 $ hg ci -m three
21 $ hg ci -m three
22 $ echo alpha >> alpha
22 $ echo alpha >> alpha
23 $ hg ci -m four
23 $ hg ci -m four
24 $ echo alpha >> alpha
24 $ echo alpha >> alpha
25 $ hg ci -m five
25 $ hg ci -m five
26
26
27 $ hg log --style compact --graph
27 $ hg log --style compact --graph
28 @ 4[tip] 08d98a8350f3 1970-01-01 00:00 +0000 test
28 @ 4[tip] 08d98a8350f3 1970-01-01 00:00 +0000 test
29 | five
29 | five
30 |
30 |
31 o 3 c8e68270e35a 1970-01-01 00:00 +0000 test
31 o 3 c8e68270e35a 1970-01-01 00:00 +0000 test
32 | four
32 | four
33 |
33 |
34 o 2 eb57da33312f 1970-01-01 00:00 +0000 test
34 o 2 eb57da33312f 1970-01-01 00:00 +0000 test
35 | three
35 | three
36 |
36 |
37 o 1 579e40513370 1970-01-01 00:00 +0000 test
37 o 1 579e40513370 1970-01-01 00:00 +0000 test
38 | two
38 | two
39 |
39 |
40 o 0 6058cbb6cfd7 1970-01-01 00:00 +0000 test
40 o 0 6058cbb6cfd7 1970-01-01 00:00 +0000 test
41 one
41 one
42
42
43
43
44 histedit --continue/--abort with no existing state
44 histedit --continue/--abort with no existing state
45 --------------------------------------------------
45 --------------------------------------------------
46
46
47 $ hg histedit --continue
47 $ hg histedit --continue
48 abort: no histedit in progress
48 abort: no histedit in progress
49 [255]
49 [255]
50 $ hg histedit --abort
50 $ hg histedit --abort
51 abort: no histedit in progress
51 abort: no histedit in progress
52 [255]
52 [255]
53
53
54 Run a dummy edit to make sure we get tip^^ correctly via revsingle.
54 Run a dummy edit to make sure we get tip^^ correctly via revsingle.
55 --------------------------------------------------------------------
55 --------------------------------------------------------------------
56
56
57 $ HGEDITOR=cat hg histedit "tip^^"
57 $ HGEDITOR=cat hg histedit "tip^^"
58 pick eb57da33312f 2 three
58 pick eb57da33312f 2 three
59 pick c8e68270e35a 3 four
59 pick c8e68270e35a 3 four
60 pick 08d98a8350f3 4 five
60 pick 08d98a8350f3 4 five
61
61
62 # Edit history between eb57da33312f and 08d98a8350f3
62 # Edit history between eb57da33312f and 08d98a8350f3
63 #
63 #
64 # Commits are listed from least to most recent
64 # Commits are listed from least to most recent
65 #
65 #
66 # You can reorder changesets by reordering the lines
66 # You can reorder changesets by reordering the lines
67 #
67 #
68 # Commands:
68 # Commands:
69 #
69 #
70 # e, edit = use commit, but stop for amending
70 # e, edit = use commit, but stop for amending
71 # m, mess = edit commit message without changing commit content
71 # m, mess = edit commit message without changing commit content
72 # p, pick = use commit
72 # p, pick = use commit
73 # b, base = checkout changeset and apply further changesets from there
73 # b, base = checkout changeset and apply further changesets from there
74 # d, drop = remove commit from history
74 # d, drop = remove commit from history
75 # f, fold = use commit, but combine it with the one above
75 # f, fold = use commit, but combine it with the one above
76 # r, roll = like fold, but discard this commit's description and date
76 # r, roll = like fold, but discard this commit's description and date
77 #
77 #
78
78
79 Run on a revision not ancestors of the current working directory.
79 Run on a revision not ancestors of the current working directory.
80 --------------------------------------------------------------------
80 --------------------------------------------------------------------
81
81
82 $ hg up 2
82 $ hg up 2
83 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
83 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 $ hg histedit -r 4
84 $ hg histedit -r 4
85 abort: 08d98a8350f3 is not an ancestor of working directory
85 abort: 08d98a8350f3 is not an ancestor of working directory
86 [255]
86 [255]
87 $ hg up --quiet
87 $ hg up --quiet
88
88
89
89
90 Test that we pick the minimum of a revrange
90 Test that we pick the minimum of a revrange
91 ---------------------------------------
91 ---------------------------------------
92
92
93 $ HGEDITOR=cat hg histedit '2::' --commands - << EOF
93 $ HGEDITOR=cat hg histedit '2::' --commands - << EOF
94 > pick eb57da33312f 2 three
94 > pick eb57da33312f 2 three
95 > pick c8e68270e35a 3 four
95 > pick c8e68270e35a 3 four
96 > pick 08d98a8350f3 4 five
96 > pick 08d98a8350f3 4 five
97 > EOF
97 > EOF
98 $ hg up --quiet
98 $ hg up --quiet
99
99
100 $ HGEDITOR=cat hg histedit 'tip:2' --commands - << EOF
100 $ HGEDITOR=cat hg histedit 'tip:2' --commands - << EOF
101 > pick eb57da33312f 2 three
101 > pick eb57da33312f 2 three
102 > pick c8e68270e35a 3 four
102 > pick c8e68270e35a 3 four
103 > pick 08d98a8350f3 4 five
103 > pick 08d98a8350f3 4 five
104 > EOF
104 > EOF
105 $ hg up --quiet
105 $ hg up --quiet
106
106
107 Test config specified default
107 Test config specified default
108 -----------------------------
108 -----------------------------
109
109
110 $ HGEDITOR=cat hg histedit --config "histedit.defaultrev=only(.) - ::eb57da33312f" --commands - << EOF
110 $ HGEDITOR=cat hg histedit --config "histedit.defaultrev=only(.) - ::eb57da33312f" --commands - << EOF
111 > pick c8e68270e35a 3 four
111 > pick c8e68270e35a 3 four
112 > pick 08d98a8350f3 4 five
112 > pick 08d98a8350f3 4 five
113 > EOF
113 > EOF
114
114
115 Test invalid config default
115 Test invalid config default
116 ---------------------------
116 ---------------------------
117
117
118 $ hg histedit --config "histedit.defaultrev="
118 $ hg histedit --config "histedit.defaultrev="
119 abort: config option histedit.defaultrev can't be empty
119 abort: config option histedit.defaultrev can't be empty
120 [255]
120 [255]
121
121
122 Run on a revision not descendants of the initial parent
122 Run on a revision not descendants of the initial parent
123 --------------------------------------------------------------------
123 --------------------------------------------------------------------
124
124
125 Test the message shown for inconsistent histedit state, which may be
125 Test the message shown for inconsistent histedit state, which may be
126 created (and forgotten) by Mercurial earlier than 2.7. This emulates
126 created (and forgotten) by Mercurial earlier than 2.7. This emulates
127 Mercurial earlier than 2.7 by renaming ".hg/histedit-state"
127 Mercurial earlier than 2.7 by renaming ".hg/histedit-state"
128 temporarily.
128 temporarily.
129
129
130 $ hg log -G -T '{rev} {shortest(node)} {desc}\n' -r 2::
130 $ hg log -G -T '{rev} {shortest(node)} {desc}\n' -r 2::
131 @ 4 08d9 five
131 @ 4 08d9 five
132 |
132 |
133 o 3 c8e6 four
133 o 3 c8e6 four
134 |
134 |
135 o 2 eb57 three
135 o 2 eb57 three
136 |
136 |
137 ~
137 ~
138 $ HGEDITOR=cat hg histedit -r 4 --commands - << EOF
138 $ HGEDITOR=cat hg histedit -r 4 --commands - << EOF
139 > edit 08d98a8350f3 4 five
139 > edit 08d98a8350f3 4 five
140 > EOF
140 > EOF
141 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
142 reverting alpha
143 Editing (08d98a8350f3), you may commit or record as needed now.
142 Editing (08d98a8350f3), you may commit or record as needed now.
144 (hg histedit --continue to resume)
143 (hg histedit --continue to resume)
145 [1]
144 [1]
146
145
147 $ hg graft --continue
146 $ hg graft --continue
148 abort: no graft in progress
147 abort: no graft in progress
149 (continue: hg histedit --continue)
148 (continue: hg histedit --continue)
150 [255]
149 [255]
151
150
152 $ mv .hg/histedit-state .hg/histedit-state.back
151 $ mv .hg/histedit-state .hg/histedit-state.back
153 $ hg update --quiet --clean 2
152 $ hg update --quiet --clean 2
154 $ echo alpha >> alpha
153 $ echo alpha >> alpha
155 $ mv .hg/histedit-state.back .hg/histedit-state
154 $ mv .hg/histedit-state.back .hg/histedit-state
156
155
157 $ hg histedit --continue
156 $ hg histedit --continue
158 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/08d98a8350f3-02594089-histedit.hg
157 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/08d98a8350f3-02594089-histedit.hg
159 $ hg log -G -T '{rev} {shortest(node)} {desc}\n' -r 2::
158 $ hg log -G -T '{rev} {shortest(node)} {desc}\n' -r 2::
160 @ 4 f5ed five
159 @ 4 f5ed five
161 |
160 |
162 | o 3 c8e6 four
161 | o 3 c8e6 four
163 |/
162 |/
164 o 2 eb57 three
163 o 2 eb57 three
165 |
164 |
166 ~
165 ~
167
166
168 $ hg unbundle -q $TESTTMP/foo/.hg/strip-backup/08d98a8350f3-02594089-histedit.hg
167 $ hg unbundle -q $TESTTMP/foo/.hg/strip-backup/08d98a8350f3-02594089-histedit.hg
169 $ hg strip -q -r f5ed --config extensions.strip=
168 $ hg strip -q -r f5ed --config extensions.strip=
170 $ hg up -q 08d98a8350f3
169 $ hg up -q 08d98a8350f3
171
170
172 Test that missing revisions are detected
171 Test that missing revisions are detected
173 ---------------------------------------
172 ---------------------------------------
174
173
175 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
174 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
176 > pick eb57da33312f 2 three
175 > pick eb57da33312f 2 three
177 > pick 08d98a8350f3 4 five
176 > pick 08d98a8350f3 4 five
178 > EOF
177 > EOF
179 hg: parse error: missing rules for changeset c8e68270e35a
178 hg: parse error: missing rules for changeset c8e68270e35a
180 (use "drop c8e68270e35a" to discard, see also: 'hg help -e histedit.config')
179 (use "drop c8e68270e35a" to discard, see also: 'hg help -e histedit.config')
181 [255]
180 [255]
182
181
183 Test that extra revisions are detected
182 Test that extra revisions are detected
184 ---------------------------------------
183 ---------------------------------------
185
184
186 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
185 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
187 > pick 6058cbb6cfd7 0 one
186 > pick 6058cbb6cfd7 0 one
188 > pick c8e68270e35a 3 four
187 > pick c8e68270e35a 3 four
189 > pick 08d98a8350f3 4 five
188 > pick 08d98a8350f3 4 five
190 > EOF
189 > EOF
191 hg: parse error: pick "6058cbb6cfd7" changeset was not a candidate
190 hg: parse error: pick "6058cbb6cfd7" changeset was not a candidate
192 (only use listed changesets)
191 (only use listed changesets)
193 [255]
192 [255]
194
193
195 Test malformed line
194 Test malformed line
196 ---------------------------------------
195 ---------------------------------------
197
196
198 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
197 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
199 > pickeb57da33312f2three
198 > pickeb57da33312f2three
200 > pick c8e68270e35a 3 four
199 > pick c8e68270e35a 3 four
201 > pick 08d98a8350f3 4 five
200 > pick 08d98a8350f3 4 five
202 > EOF
201 > EOF
203 hg: parse error: malformed line "pickeb57da33312f2three"
202 hg: parse error: malformed line "pickeb57da33312f2three"
204 [255]
203 [255]
205
204
206 Test unknown changeset
205 Test unknown changeset
207 ---------------------------------------
206 ---------------------------------------
208
207
209 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
208 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
210 > pick 0123456789ab 2 three
209 > pick 0123456789ab 2 three
211 > pick c8e68270e35a 3 four
210 > pick c8e68270e35a 3 four
212 > pick 08d98a8350f3 4 five
211 > pick 08d98a8350f3 4 five
213 > EOF
212 > EOF
214 hg: parse error: unknown changeset 0123456789ab listed
213 hg: parse error: unknown changeset 0123456789ab listed
215 [255]
214 [255]
216
215
217 Test unknown command
216 Test unknown command
218 ---------------------------------------
217 ---------------------------------------
219
218
220 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
219 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
221 > coin eb57da33312f 2 three
220 > coin eb57da33312f 2 three
222 > pick c8e68270e35a 3 four
221 > pick c8e68270e35a 3 four
223 > pick 08d98a8350f3 4 five
222 > pick 08d98a8350f3 4 five
224 > EOF
223 > EOF
225 hg: parse error: unknown action "coin"
224 hg: parse error: unknown action "coin"
226 [255]
225 [255]
227
226
228 Test duplicated changeset
227 Test duplicated changeset
229 ---------------------------------------
228 ---------------------------------------
230
229
231 So one is missing and one appear twice.
230 So one is missing and one appear twice.
232
231
233 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
232 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
234 > pick eb57da33312f 2 three
233 > pick eb57da33312f 2 three
235 > pick eb57da33312f 2 three
234 > pick eb57da33312f 2 three
236 > pick 08d98a8350f3 4 five
235 > pick 08d98a8350f3 4 five
237 > EOF
236 > EOF
238 hg: parse error: duplicated command for changeset eb57da33312f
237 hg: parse error: duplicated command for changeset eb57da33312f
239 [255]
238 [255]
240
239
241 Test bogus rev
240 Test bogus rev
242 ---------------------------------------
241 ---------------------------------------
243
242
244 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
243 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
245 > pick eb57da33312f 2 three
244 > pick eb57da33312f 2 three
246 > pick 0u98
245 > pick 0u98
247 > pick 08d98a8350f3 4 five
246 > pick 08d98a8350f3 4 five
248 > EOF
247 > EOF
249 hg: parse error: invalid changeset 0u98
248 hg: parse error: invalid changeset 0u98
250 [255]
249 [255]
251
250
252 Test short version of command
251 Test short version of command
253 ---------------------------------------
252 ---------------------------------------
254
253
255 Note: we use varying amounts of white space between command name and changeset
254 Note: we use varying amounts of white space between command name and changeset
256 short hash. This tests issue3893.
255 short hash. This tests issue3893.
257
256
258 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
257 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
259 > pick eb57da33312f 2 three
258 > pick eb57da33312f 2 three
260 > p c8e68270e35a 3 four
259 > p c8e68270e35a 3 four
261 > f 08d98a8350f3 4 five
260 > f 08d98a8350f3 4 five
262 > EOF
261 > EOF
263 four
262 four
264 ***
263 ***
265 five
264 five
266
265
267
266
268
267
269 HG: Enter commit message. Lines beginning with 'HG:' are removed.
268 HG: Enter commit message. Lines beginning with 'HG:' are removed.
270 HG: Leave message empty to abort commit.
269 HG: Leave message empty to abort commit.
271 HG: --
270 HG: --
272 HG: user: test
271 HG: user: test
273 HG: branch 'default'
272 HG: branch 'default'
274 HG: changed alpha
273 HG: changed alpha
275 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/c8e68270e35a-63d8b8d8-histedit.hg
274 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/c8e68270e35a-63d8b8d8-histedit.hg
276
275
277 $ hg update -q 2
276 $ hg update -q 2
278 $ echo x > x
277 $ echo x > x
279 $ hg add x
278 $ hg add x
280 $ hg commit -m'x' x
279 $ hg commit -m'x' x
281 created new head
280 created new head
282 $ hg histedit -r 'heads(all())'
281 $ hg histedit -r 'heads(all())'
283 abort: The specified revisions must have exactly one common root
282 abort: The specified revisions must have exactly one common root
284 [255]
283 [255]
285
284
286 Test that trimming description using multi-byte characters
285 Test that trimming description using multi-byte characters
287 --------------------------------------------------------------------
286 --------------------------------------------------------------------
288
287
289 $ "$PYTHON" <<EOF
288 $ "$PYTHON" <<EOF
290 > fp = open('logfile', 'wb')
289 > fp = open('logfile', 'wb')
291 > fp.write(b'12345678901234567890123456789012345678901234567890' +
290 > fp.write(b'12345678901234567890123456789012345678901234567890' +
292 > b'12345') # there are 5 more columns for 80 columns
291 > b'12345') # there are 5 more columns for 80 columns
293 >
292 >
294 > # 2 x 4 = 8 columns, but 3 x 4 = 12 bytes
293 > # 2 x 4 = 8 columns, but 3 x 4 = 12 bytes
295 > fp.write(u'\u3042\u3044\u3046\u3048'.encode('utf-8'))
294 > fp.write(u'\u3042\u3044\u3046\u3048'.encode('utf-8'))
296 >
295 >
297 > fp.close()
296 > fp.close()
298 > EOF
297 > EOF
299 $ echo xx >> x
298 $ echo xx >> x
300 $ hg --encoding utf-8 commit --logfile logfile
299 $ hg --encoding utf-8 commit --logfile logfile
301
300
302 $ HGEDITOR=cat hg --encoding utf-8 histedit tip
301 $ HGEDITOR=cat hg --encoding utf-8 histedit tip
303 pick 3d3ea1f3a10b 5 1234567890123456789012345678901234567890123456789012345\xe3\x81\x82... (esc)
302 pick 3d3ea1f3a10b 5 1234567890123456789012345678901234567890123456789012345\xe3\x81\x82... (esc)
304
303
305 # Edit history between 3d3ea1f3a10b and 3d3ea1f3a10b
304 # Edit history between 3d3ea1f3a10b and 3d3ea1f3a10b
306 #
305 #
307 # Commits are listed from least to most recent
306 # Commits are listed from least to most recent
308 #
307 #
309 # You can reorder changesets by reordering the lines
308 # You can reorder changesets by reordering the lines
310 #
309 #
311 # Commands:
310 # Commands:
312 #
311 #
313 # e, edit = use commit, but stop for amending
312 # e, edit = use commit, but stop for amending
314 # m, mess = edit commit message without changing commit content
313 # m, mess = edit commit message without changing commit content
315 # p, pick = use commit
314 # p, pick = use commit
316 # b, base = checkout changeset and apply further changesets from there
315 # b, base = checkout changeset and apply further changesets from there
317 # d, drop = remove commit from history
316 # d, drop = remove commit from history
318 # f, fold = use commit, but combine it with the one above
317 # f, fold = use commit, but combine it with the one above
319 # r, roll = like fold, but discard this commit's description and date
318 # r, roll = like fold, but discard this commit's description and date
320 #
319 #
321
320
322 Test --continue with --keep
321 Test --continue with --keep
323
322
324 $ hg strip -q -r . --config extensions.strip=
323 $ hg strip -q -r . --config extensions.strip=
325 $ hg histedit '.^' -q --keep --commands - << EOF
324 $ hg histedit '.^' -q --keep --commands - << EOF
326 > edit eb57da33312f 2 three
325 > edit eb57da33312f 2 three
327 > pick f3cfcca30c44 4 x
326 > pick f3cfcca30c44 4 x
328 > EOF
327 > EOF
329 Editing (eb57da33312f), you may commit or record as needed now.
328 Editing (eb57da33312f), you may commit or record as needed now.
330 (hg histedit --continue to resume)
329 (hg histedit --continue to resume)
331 [1]
330 [1]
332 $ echo edit >> alpha
331 $ echo edit >> alpha
333 $ hg histedit -q --continue
332 $ hg histedit -q --continue
334 $ hg log -G -T '{rev}:{node|short} {desc}'
333 $ hg log -G -T '{rev}:{node|short} {desc}'
335 @ 6:8fda0c726bf2 x
334 @ 6:8fda0c726bf2 x
336 |
335 |
337 o 5:63379946892c three
336 o 5:63379946892c three
338 |
337 |
339 | o 4:f3cfcca30c44 x
338 | o 4:f3cfcca30c44 x
340 | |
339 | |
341 | | o 3:2a30f3cfee78 four
340 | | o 3:2a30f3cfee78 four
342 | |/ ***
341 | |/ ***
343 | | five
342 | | five
344 | o 2:eb57da33312f three
343 | o 2:eb57da33312f three
345 |/
344 |/
346 o 1:579e40513370 two
345 o 1:579e40513370 two
347 |
346 |
348 o 0:6058cbb6cfd7 one
347 o 0:6058cbb6cfd7 one
349
348
350
349
351 Test that abort fails gracefully on exception
350 Test that abort fails gracefully on exception
352 ----------------------------------------------
351 ----------------------------------------------
353 $ hg histedit . -q --commands - << EOF
352 $ hg histedit . -q --commands - << EOF
354 > edit 8fda0c726bf2 6 x
353 > edit 8fda0c726bf2 6 x
355 > EOF
354 > EOF
356 Editing (8fda0c726bf2), you may commit or record as needed now.
355 Editing (8fda0c726bf2), you may commit or record as needed now.
357 (hg histedit --continue to resume)
356 (hg histedit --continue to resume)
358 [1]
357 [1]
359 Corrupt histedit state file
358 Corrupt histedit state file
360 $ sed 's/8fda0c726bf2/123456789012/' .hg/histedit-state > ../corrupt-histedit
359 $ sed 's/8fda0c726bf2/123456789012/' .hg/histedit-state > ../corrupt-histedit
361 $ mv ../corrupt-histedit .hg/histedit-state
360 $ mv ../corrupt-histedit .hg/histedit-state
362 $ hg histedit --abort
361 $ hg histedit --abort
363 warning: encountered an exception during histedit --abort; the repository may not have been completely cleaned up
362 warning: encountered an exception during histedit --abort; the repository may not have been completely cleaned up
364 abort: $TESTTMP/foo/.hg/strip-backup/*-histedit.hg: $ENOENT$ (glob) (windows !)
363 abort: $TESTTMP/foo/.hg/strip-backup/*-histedit.hg: $ENOENT$ (glob) (windows !)
365 abort: $ENOENT$: '$TESTTMP/foo/.hg/strip-backup/*-histedit.hg' (glob) (no-windows !)
364 abort: $ENOENT$: '$TESTTMP/foo/.hg/strip-backup/*-histedit.hg' (glob) (no-windows !)
366 [255]
365 [255]
367 Histedit state has been exited
366 Histedit state has been exited
368 $ hg summary -q
367 $ hg summary -q
369 parent: 5:63379946892c
368 parent: 5:63379946892c
370 commit: 1 added, 1 unknown (new branch head)
369 commit: 1 added, 1 unknown (new branch head)
371 update: 4 new changesets (update)
370 update: 4 new changesets (update)
372
371
373 $ cd ..
372 $ cd ..
374
373
375 Set up default base revision tests
374 Set up default base revision tests
376
375
377 $ hg init defaultbase
376 $ hg init defaultbase
378 $ cd defaultbase
377 $ cd defaultbase
379 $ touch foo
378 $ touch foo
380 $ hg -q commit -A -m root
379 $ hg -q commit -A -m root
381 $ echo 1 > foo
380 $ echo 1 > foo
382 $ hg commit -m 'public 1'
381 $ hg commit -m 'public 1'
383 $ hg phase --force --public -r .
382 $ hg phase --force --public -r .
384 $ echo 2 > foo
383 $ echo 2 > foo
385 $ hg commit -m 'draft after public'
384 $ hg commit -m 'draft after public'
386 $ hg -q up -r 1
385 $ hg -q up -r 1
387 $ echo 3 > foo
386 $ echo 3 > foo
388 $ hg commit -m 'head 1 public'
387 $ hg commit -m 'head 1 public'
389 created new head
388 created new head
390 $ hg phase --force --public -r .
389 $ hg phase --force --public -r .
391 $ echo 4 > foo
390 $ echo 4 > foo
392 $ hg commit -m 'head 1 draft 1'
391 $ hg commit -m 'head 1 draft 1'
393 $ echo 5 > foo
392 $ echo 5 > foo
394 $ hg commit -m 'head 1 draft 2'
393 $ hg commit -m 'head 1 draft 2'
395 $ hg -q up -r 2
394 $ hg -q up -r 2
396 $ echo 6 > foo
395 $ echo 6 > foo
397 $ hg commit -m 'head 2 commit 1'
396 $ hg commit -m 'head 2 commit 1'
398 $ echo 7 > foo
397 $ echo 7 > foo
399 $ hg commit -m 'head 2 commit 2'
398 $ hg commit -m 'head 2 commit 2'
400 $ hg -q up -r 2
399 $ hg -q up -r 2
401 $ echo 8 > foo
400 $ echo 8 > foo
402 $ hg commit -m 'head 3'
401 $ hg commit -m 'head 3'
403 created new head
402 created new head
404 $ hg -q up -r 2
403 $ hg -q up -r 2
405 $ echo 9 > foo
404 $ echo 9 > foo
406 $ hg commit -m 'head 4'
405 $ hg commit -m 'head 4'
407 created new head
406 created new head
408 $ hg merge --tool :local -r 8
407 $ hg merge --tool :local -r 8
409 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
408 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
410 (branch merge, don't forget to commit)
409 (branch merge, don't forget to commit)
411 $ hg commit -m 'merge head 3 into head 4'
410 $ hg commit -m 'merge head 3 into head 4'
412 $ echo 11 > foo
411 $ echo 11 > foo
413 $ hg commit -m 'commit 1 after merge'
412 $ hg commit -m 'commit 1 after merge'
414 $ echo 12 > foo
413 $ echo 12 > foo
415 $ hg commit -m 'commit 2 after merge'
414 $ hg commit -m 'commit 2 after merge'
416
415
417 $ hg log -G -T '{rev}:{node|short} {phase} {desc}\n'
416 $ hg log -G -T '{rev}:{node|short} {phase} {desc}\n'
418 @ 12:8cde254db839 draft commit 2 after merge
417 @ 12:8cde254db839 draft commit 2 after merge
419 |
418 |
420 o 11:6f2f0241f119 draft commit 1 after merge
419 o 11:6f2f0241f119 draft commit 1 after merge
421 |
420 |
422 o 10:90506cc76b00 draft merge head 3 into head 4
421 o 10:90506cc76b00 draft merge head 3 into head 4
423 |\
422 |\
424 | o 9:f8607a373a97 draft head 4
423 | o 9:f8607a373a97 draft head 4
425 | |
424 | |
426 o | 8:0da92be05148 draft head 3
425 o | 8:0da92be05148 draft head 3
427 |/
426 |/
428 | o 7:4c35cdf97d5e draft head 2 commit 2
427 | o 7:4c35cdf97d5e draft head 2 commit 2
429 | |
428 | |
430 | o 6:931820154288 draft head 2 commit 1
429 | o 6:931820154288 draft head 2 commit 1
431 |/
430 |/
432 | o 5:8cdc02b9bc63 draft head 1 draft 2
431 | o 5:8cdc02b9bc63 draft head 1 draft 2
433 | |
432 | |
434 | o 4:463b8c0d2973 draft head 1 draft 1
433 | o 4:463b8c0d2973 draft head 1 draft 1
435 | |
434 | |
436 | o 3:23a0c4eefcbf public head 1 public
435 | o 3:23a0c4eefcbf public head 1 public
437 | |
436 | |
438 o | 2:4117331c3abb draft draft after public
437 o | 2:4117331c3abb draft draft after public
439 |/
438 |/
440 o 1:4426d359ea59 public public 1
439 o 1:4426d359ea59 public public 1
441 |
440 |
442 o 0:54136a8ddf32 public root
441 o 0:54136a8ddf32 public root
443
442
444
443
445 Default base revision should stop at public changesets
444 Default base revision should stop at public changesets
446
445
447 $ hg -q up 8cdc02b9bc63
446 $ hg -q up 8cdc02b9bc63
448 $ hg histedit --commands - <<EOF
447 $ hg histedit --commands - <<EOF
449 > pick 463b8c0d2973
448 > pick 463b8c0d2973
450 > pick 8cdc02b9bc63
449 > pick 8cdc02b9bc63
451 > EOF
450 > EOF
452
451
453 Default base revision should stop at branchpoint
452 Default base revision should stop at branchpoint
454
453
455 $ hg -q up 4c35cdf97d5e
454 $ hg -q up 4c35cdf97d5e
456 $ hg histedit --commands - <<EOF
455 $ hg histedit --commands - <<EOF
457 > pick 931820154288
456 > pick 931820154288
458 > pick 4c35cdf97d5e
457 > pick 4c35cdf97d5e
459 > EOF
458 > EOF
460
459
461 Default base revision should stop at merge commit
460 Default base revision should stop at merge commit
462
461
463 $ hg -q up 8cde254db839
462 $ hg -q up 8cde254db839
464 $ hg histedit --commands - <<EOF
463 $ hg histedit --commands - <<EOF
465 > pick 6f2f0241f119
464 > pick 6f2f0241f119
466 > pick 8cde254db839
465 > pick 8cde254db839
467 > EOF
466 > EOF
468
467
469 commit --amend should abort if histedit is in progress
468 commit --amend should abort if histedit is in progress
470 (issue4800) and markers are not being created.
469 (issue4800) and markers are not being created.
471 Eventually, histedit could perhaps look at `source` extra,
470 Eventually, histedit could perhaps look at `source` extra,
472 in which case this test should be revisited.
471 in which case this test should be revisited.
473
472
474 $ hg -q up 8cde254db839
473 $ hg -q up 8cde254db839
475 $ hg histedit 6f2f0241f119 --commands - <<EOF
474 $ hg histedit 6f2f0241f119 --commands - <<EOF
476 > pick 8cde254db839
475 > pick 8cde254db839
477 > edit 6f2f0241f119
476 > edit 6f2f0241f119
478 > EOF
477 > EOF
479 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
480 merging foo
478 merging foo
481 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
479 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
482 Fix up the change (pick 8cde254db839)
480 Fix up the change (pick 8cde254db839)
483 (hg histedit --continue to resume)
481 (hg histedit --continue to resume)
484 [1]
482 [1]
485 $ hg resolve -m --all
483 $ hg resolve -m --all
486 (no more unresolved files)
484 (no more unresolved files)
487 continue: hg histedit --continue
485 continue: hg histedit --continue
488 $ hg histedit --cont
486 $ hg histedit --cont
489 merging foo
487 merging foo
490 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
488 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
491 Editing (6f2f0241f119), you may commit or record as needed now.
489 Editing (6f2f0241f119), you may commit or record as needed now.
492 (hg histedit --continue to resume)
490 (hg histedit --continue to resume)
493 [1]
491 [1]
494 $ hg resolve -m --all
492 $ hg resolve -m --all
495 (no more unresolved files)
493 (no more unresolved files)
496 continue: hg histedit --continue
494 continue: hg histedit --continue
497 $ hg commit --amend -m 'reject this fold'
495 $ hg commit --amend -m 'reject this fold'
498 abort: histedit in progress
496 abort: histedit in progress
499 (use 'hg histedit --continue' or 'hg histedit --abort')
497 (use 'hg histedit --continue' or 'hg histedit --abort')
500 [255]
498 [255]
501
499
502 With markers enabled, histedit does not get confused, and
500 With markers enabled, histedit does not get confused, and
503 amend should not be blocked by the ongoing histedit.
501 amend should not be blocked by the ongoing histedit.
504
502
505 $ cat >>$HGRCPATH <<EOF
503 $ cat >>$HGRCPATH <<EOF
506 > [experimental]
504 > [experimental]
507 > evolution.createmarkers=True
505 > evolution.createmarkers=True
508 > evolution.allowunstable=True
506 > evolution.allowunstable=True
509 > EOF
507 > EOF
510 $ hg commit --amend -m 'allow this fold'
508 $ hg commit --amend -m 'allow this fold'
511 $ hg histedit --continue
509 $ hg histedit --continue
512
510
513 $ cd ..
511 $ cd ..
514
512
515 Test autoverb feature
513 Test autoverb feature
516
514
517 $ hg init autoverb
515 $ hg init autoverb
518 $ cd autoverb
516 $ cd autoverb
519 $ echo alpha >> alpha
517 $ echo alpha >> alpha
520 $ hg ci -qAm one
518 $ hg ci -qAm one
521 $ echo alpha >> alpha
519 $ echo alpha >> alpha
522 $ hg ci -qm two
520 $ hg ci -qm two
523 $ echo beta >> beta
521 $ echo beta >> beta
524 $ hg ci -qAm "roll! one"
522 $ hg ci -qAm "roll! one"
525
523
526 $ hg log --style compact --graph
524 $ hg log --style compact --graph
527 @ 2[tip] 4f34d0f8b5fa 1970-01-01 00:00 +0000 test
525 @ 2[tip] 4f34d0f8b5fa 1970-01-01 00:00 +0000 test
528 | roll! one
526 | roll! one
529 |
527 |
530 o 1 579e40513370 1970-01-01 00:00 +0000 test
528 o 1 579e40513370 1970-01-01 00:00 +0000 test
531 | two
529 | two
532 |
530 |
533 o 0 6058cbb6cfd7 1970-01-01 00:00 +0000 test
531 o 0 6058cbb6cfd7 1970-01-01 00:00 +0000 test
534 one
532 one
535
533
536
534
537 Check that 'roll' is selected by default
535 Check that 'roll' is selected by default
538
536
539 $ HGEDITOR=cat hg histedit 0 --config experimental.histedit.autoverb=True
537 $ HGEDITOR=cat hg histedit 0 --config experimental.histedit.autoverb=True
540 pick 6058cbb6cfd7 0 one
538 pick 6058cbb6cfd7 0 one
541 roll 4f34d0f8b5fa 2 roll! one
539 roll 4f34d0f8b5fa 2 roll! one
542 pick 579e40513370 1 two
540 pick 579e40513370 1 two
543
541
544 # Edit history between 6058cbb6cfd7 and 4f34d0f8b5fa
542 # Edit history between 6058cbb6cfd7 and 4f34d0f8b5fa
545 #
543 #
546 # Commits are listed from least to most recent
544 # Commits are listed from least to most recent
547 #
545 #
548 # You can reorder changesets by reordering the lines
546 # You can reorder changesets by reordering the lines
549 #
547 #
550 # Commands:
548 # Commands:
551 #
549 #
552 # e, edit = use commit, but stop for amending
550 # e, edit = use commit, but stop for amending
553 # m, mess = edit commit message without changing commit content
551 # m, mess = edit commit message without changing commit content
554 # p, pick = use commit
552 # p, pick = use commit
555 # b, base = checkout changeset and apply further changesets from there
553 # b, base = checkout changeset and apply further changesets from there
556 # d, drop = remove commit from history
554 # d, drop = remove commit from history
557 # f, fold = use commit, but combine it with the one above
555 # f, fold = use commit, but combine it with the one above
558 # r, roll = like fold, but discard this commit's description and date
556 # r, roll = like fold, but discard this commit's description and date
559 #
557 #
560
558
561 $ cd ..
559 $ cd ..
562
560
563 Check that histedit's commands accept revsets
561 Check that histedit's commands accept revsets
564 $ hg init bar
562 $ hg init bar
565 $ cd bar
563 $ cd bar
566 $ echo w >> a
564 $ echo w >> a
567 $ hg ci -qAm "adds a"
565 $ hg ci -qAm "adds a"
568 $ echo x >> b
566 $ echo x >> b
569 $ hg ci -qAm "adds b"
567 $ hg ci -qAm "adds b"
570 $ echo y >> c
568 $ echo y >> c
571 $ hg ci -qAm "adds c"
569 $ hg ci -qAm "adds c"
572 $ echo z >> d
570 $ echo z >> d
573 $ hg ci -qAm "adds d"
571 $ hg ci -qAm "adds d"
574 $ hg log -G -T '{rev} {desc}\n'
572 $ hg log -G -T '{rev} {desc}\n'
575 @ 3 adds d
573 @ 3 adds d
576 |
574 |
577 o 2 adds c
575 o 2 adds c
578 |
576 |
579 o 1 adds b
577 o 1 adds b
580 |
578 |
581 o 0 adds a
579 o 0 adds a
582
580
583 $ HGEDITOR=cat hg histedit "2" --commands - << EOF
581 $ HGEDITOR=cat hg histedit "2" --commands - << EOF
584 > base -4 adds c
582 > base -4 adds c
585 > pick 2 adds c
583 > pick 2 adds c
586 > pick tip adds d
584 > pick tip adds d
587 > EOF
585 > EOF
588 $ hg log -G -T '{rev} {desc}\n'
586 $ hg log -G -T '{rev} {desc}\n'
589 @ 5 adds d
587 @ 5 adds d
590 |
588 |
591 o 4 adds c
589 o 4 adds c
592 |
590 |
593 | o 1 adds b
591 | o 1 adds b
594 |/
592 |/
595 o 0 adds a
593 o 0 adds a
596
594
597
595
@@ -1,555 +1,554 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 [255]
66 [255]
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), you may commit or record as needed now.
79 (hg histedit --continue to resume)
79 (hg histedit --continue to resume)
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 [255]
85 [255]
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 [255]
139 [255]
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 [255]
156 [255]
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), you may commit or record as needed now.
209 (hg histedit --continue to resume)
209 (hg histedit --continue to resume)
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), you may commit or record as needed now.
251 (hg histedit --continue to resume)
251 (hg histedit --continue to resume)
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('emulating unexpected abort')
314 > raise error.Abort('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 abort: pretxncommit.unexpectedabort hook exited with status 1
376 abort: pretxncommit.unexpectedabort hook exited with status 1
377 [255]
377 [255]
378 $ cat .hg/last-message.txt
378 $ cat .hg/last-message.txt
379 f
379 f
380
380
381
381
382 check saving last-message.txt
382 check saving last-message.txt
383
383
384 (test also that editor is invoked if histedit is continued for "message"
384 (test also that editor is invoked if histedit is continued for "message"
385 action)
385 action)
386
386
387 $ HGEDITOR=cat hg histedit --continue
387 $ HGEDITOR=cat hg histedit --continue
388 f
388 f
389
389
390
390
391 HG: Enter commit message. Lines beginning with 'HG:' are removed.
391 HG: Enter commit message. Lines beginning with 'HG:' are removed.
392 HG: Leave message empty to abort commit.
392 HG: Leave message empty to abort commit.
393 HG: --
393 HG: --
394 HG: user: test
394 HG: user: test
395 HG: branch 'default'
395 HG: branch 'default'
396 HG: added f
396 HG: added f
397 transaction abort!
397 transaction abort!
398 rollback completed
398 rollback completed
399 note: commit message saved in .hg/last-message.txt
399 note: commit message saved in .hg/last-message.txt
400 abort: pretxncommit.unexpectedabort hook exited with status 1
400 abort: pretxncommit.unexpectedabort hook exited with status 1
401 [255]
401 [255]
402
402
403 $ cat >> .hg/hgrc <<EOF
403 $ cat >> .hg/hgrc <<EOF
404 > [hooks]
404 > [hooks]
405 > pretxncommit.unexpectedabort =
405 > pretxncommit.unexpectedabort =
406 > EOF
406 > EOF
407 $ hg histedit --abort -q
407 $ hg histedit --abort -q
408
408
409 then, check "modify the message" itself
409 then, check "modify the message" itself
410
410
411 $ hg histedit tip --commands - 2>&1 << EOF | fixbundle
411 $ hg histedit tip --commands - 2>&1 << EOF | fixbundle
412 > mess 1fd3b2fe7754 f
412 > mess 1fd3b2fe7754 f
413 > EOF
413 > EOF
414 $ hg status
414 $ hg status
415 $ hg log --limit 1
415 $ hg log --limit 1
416 changeset: 6:62feedb1200e
416 changeset: 6:62feedb1200e
417 tag: tip
417 tag: tip
418 user: test
418 user: test
419 date: Thu Jan 01 00:00:00 1970 +0000
419 date: Thu Jan 01 00:00:00 1970 +0000
420 summary: f
420 summary: f
421
421
422
422
423 rollback should not work after a histedit
423 rollback should not work after a histedit
424 $ hg rollback
424 $ hg rollback
425 no rollback information available
425 no rollback information available
426 [1]
426 [1]
427
427
428 $ cd ..
428 $ cd ..
429 $ hg clone -qr0 r r0
429 $ hg clone -qr0 r r0
430 $ cd r0
430 $ cd r0
431 $ hg phase -fdr0
431 $ hg phase -fdr0
432 $ hg histedit --commands - 0 2>&1 << EOF
432 $ hg histedit --commands - 0 2>&1 << EOF
433 > edit cb9a9f314b8b a > $EDITED
433 > edit cb9a9f314b8b a > $EDITED
434 > EOF
434 > EOF
435 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
435 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
436 adding a
437 Editing (cb9a9f314b8b), you may commit or record as needed now.
436 Editing (cb9a9f314b8b), you may commit or record as needed now.
438 (hg histedit --continue to resume)
437 (hg histedit --continue to resume)
439 [1]
438 [1]
440 $ HGEDITOR=true hg histedit --continue
439 $ HGEDITOR=true hg histedit --continue
441 saved backup bundle to $TESTTMP/r0/.hg/strip-backup/cb9a9f314b8b-cc5ccb0b-histedit.hg
440 saved backup bundle to $TESTTMP/r0/.hg/strip-backup/cb9a9f314b8b-cc5ccb0b-histedit.hg
442
441
443 $ hg log -G
442 $ hg log -G
444 @ changeset: 0:0efcea34f18a
443 @ changeset: 0:0efcea34f18a
445 tag: tip
444 tag: tip
446 user: test
445 user: test
447 date: Thu Jan 01 00:00:00 1970 +0000
446 date: Thu Jan 01 00:00:00 1970 +0000
448 summary: a
447 summary: a
449
448
450 $ echo foo >> b
449 $ echo foo >> b
451 $ hg addr
450 $ hg addr
452 adding b
451 adding b
453 $ hg ci -m 'add b'
452 $ hg ci -m 'add b'
454 $ echo foo >> a
453 $ echo foo >> a
455 $ hg ci -m 'extend a'
454 $ hg ci -m 'extend a'
456 $ hg phase --public 1
455 $ hg phase --public 1
457 Attempting to fold a change into a public change should not work:
456 Attempting to fold a change into a public change should not work:
458 $ cat > ../edit.sh <<EOF
457 $ cat > ../edit.sh <<EOF
459 > cat "\$1" | sed s/pick/fold/ > tmp
458 > cat "\$1" | sed s/pick/fold/ > tmp
460 > mv tmp "\$1"
459 > mv tmp "\$1"
461 > EOF
460 > EOF
462 $ HGEDITOR="sh ../edit.sh" hg histedit 2
461 $ HGEDITOR="sh ../edit.sh" hg histedit 2
463 warning: histedit rules saved to: .hg/histedit-last-edit.txt
462 warning: histedit rules saved to: .hg/histedit-last-edit.txt
464 hg: parse error: first changeset cannot use verb "fold"
463 hg: parse error: first changeset cannot use verb "fold"
465 [255]
464 [255]
466 $ cat .hg/histedit-last-edit.txt
465 $ cat .hg/histedit-last-edit.txt
467 fold 0012be4a27ea 2 extend a
466 fold 0012be4a27ea 2 extend a
468
467
469 # Edit history between 0012be4a27ea and 0012be4a27ea
468 # Edit history between 0012be4a27ea and 0012be4a27ea
470 #
469 #
471 # Commits are listed from least to most recent
470 # Commits are listed from least to most recent
472 #
471 #
473 # You can reorder changesets by reordering the lines
472 # You can reorder changesets by reordering the lines
474 #
473 #
475 # Commands:
474 # Commands:
476 #
475 #
477 # e, edit = use commit, but stop for amending
476 # e, edit = use commit, but stop for amending
478 # m, mess = edit commit message without changing commit content
477 # m, mess = edit commit message without changing commit content
479 # p, fold = use commit
478 # p, fold = use commit
480 # b, base = checkout changeset and apply further changesets from there
479 # b, base = checkout changeset and apply further changesets from there
481 # d, drop = remove commit from history
480 # d, drop = remove commit from history
482 # f, fold = use commit, but combine it with the one above
481 # f, fold = use commit, but combine it with the one above
483 # r, roll = like fold, but discard this commit's description and date
482 # r, roll = like fold, but discard this commit's description and date
484 #
483 #
485
484
486 $ cd ..
485 $ cd ..
487
486
488 ============================================
487 ============================================
489 Test update-timestamp config option in mess|
488 Test update-timestamp config option in mess|
490 ============================================
489 ============================================
491
490
492 $ addwithdate ()
491 $ addwithdate ()
493 > {
492 > {
494 > echo $1 > $1
493 > echo $1 > $1
495 > hg add $1
494 > hg add $1
496 > hg ci -m $1 -d "$2 0"
495 > hg ci -m $1 -d "$2 0"
497 > }
496 > }
498
497
499 $ initrepo ()
498 $ initrepo ()
500 > {
499 > {
501 > hg init r2
500 > hg init r2
502 > cd r2
501 > cd r2
503 > addwithdate a 1
502 > addwithdate a 1
504 > addwithdate b 2
503 > addwithdate b 2
505 > addwithdate c 3
504 > addwithdate c 3
506 > addwithdate d 4
505 > addwithdate d 4
507 > addwithdate e 5
506 > addwithdate e 5
508 > addwithdate f 6
507 > addwithdate f 6
509 > }
508 > }
510
509
511 $ initrepo
510 $ initrepo
512
511
513 log before edit
512 log before edit
514
513
515 $ hg log --limit 1
514 $ hg log --limit 1
516 changeset: 5:178e35e0ce73
515 changeset: 5:178e35e0ce73
517 tag: tip
516 tag: tip
518 user: test
517 user: test
519 date: Thu Jan 01 00:00:06 1970 +0000
518 date: Thu Jan 01 00:00:06 1970 +0000
520 summary: f
519 summary: f
521
520
522 $ hg histedit tip --commands - 2>&1 --config rewrite.update-timestamp=True << EOF | fixbundle
521 $ hg histedit tip --commands - 2>&1 --config rewrite.update-timestamp=True << EOF | fixbundle
523 > mess 178e35e0ce73 f
522 > mess 178e35e0ce73 f
524 > EOF
523 > EOF
525
524
526 log after edit
525 log after edit
527
526
528 $ hg log --limit 1
527 $ hg log --limit 1
529 changeset: 5:98bf456d476b
528 changeset: 5:98bf456d476b
530 tag: tip
529 tag: tip
531 user: test
530 user: test
532 date: Thu Jan 01 00:00:00 1970 +0000
531 date: Thu Jan 01 00:00:00 1970 +0000
533 summary: f
532 summary: f
534
533
535
534
536 $ cd ..
535 $ cd ..
537
536
538 warn the user on editing tagged commits
537 warn the user on editing tagged commits
539
538
540 $ hg init issue4017
539 $ hg init issue4017
541 $ cd issue4017
540 $ cd issue4017
542 $ echo > a
541 $ echo > a
543 $ hg ci -Am 'add a'
542 $ hg ci -Am 'add a'
544 adding a
543 adding a
545 $ hg tag a
544 $ hg tag a
546 $ hg tags
545 $ hg tags
547 tip 1:bd7ee4f3939b
546 tip 1:bd7ee4f3939b
548 a 0:a8a82d372bb3
547 a 0:a8a82d372bb3
549 $ hg histedit
548 $ hg histedit
550 warning: tags associated with the given changeset will be lost after histedit.
549 warning: tags associated with the given changeset will be lost after histedit.
551 do you want to continue (yN)? n
550 do you want to continue (yN)? n
552 abort: histedit cancelled
551 abort: histedit cancelled
553
552
554 [255]
553 [255]
555 $ cd ..
554 $ cd ..
@@ -1,344 +1,342 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 $ modwithdate ()
8 $ modwithdate ()
9 > {
9 > {
10 > echo $1 > $1
10 > echo $1 > $1
11 > hg ci -m $1 -d "$2 0"
11 > hg ci -m $1 -d "$2 0"
12 > }
12 > }
13
13
14 $ initrepo ()
14 $ initrepo ()
15 > {
15 > {
16 > hg init $1
16 > hg init $1
17 > cd $1
17 > cd $1
18 > for x in a b c d e f ; do
18 > for x in a b c d e f ; do
19 > echo $x$x$x$x$x > $x
19 > echo $x$x$x$x$x > $x
20 > hg add $x
20 > hg add $x
21 > done
21 > done
22 > hg ci -m 'Initial commit'
22 > hg ci -m 'Initial commit'
23 > modwithdate a 1
23 > modwithdate a 1
24 > modwithdate b 2
24 > modwithdate b 2
25 > modwithdate c 3
25 > modwithdate c 3
26 > modwithdate d 4
26 > modwithdate d 4
27 > modwithdate e 5
27 > modwithdate e 5
28 > modwithdate f 6
28 > modwithdate f 6
29 > echo 'I can haz no commute' > e
29 > echo 'I can haz no commute' > e
30 > hg ci -m 'does not commute with e' -d '7 0'
30 > hg ci -m 'does not commute with e' -d '7 0'
31 > cd ..
31 > cd ..
32 > }
32 > }
33
33
34 $ initrepo r
34 $ initrepo r
35 $ cd r
35 $ cd r
36 Initial generation of the command files
36 Initial generation of the command files
37
37
38 $ EDITED="$TESTTMP/editedhistory"
38 $ EDITED="$TESTTMP/editedhistory"
39 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 3 >> $EDITED
39 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 3 >> $EDITED
40 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 4 >> $EDITED
40 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 4 >> $EDITED
41 $ hg log --template 'fold {node|short} {rev} {desc}\n' -r 7 >> $EDITED
41 $ hg log --template 'fold {node|short} {rev} {desc}\n' -r 7 >> $EDITED
42 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 5 >> $EDITED
42 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 5 >> $EDITED
43 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 6 >> $EDITED
43 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 6 >> $EDITED
44 $ cat $EDITED
44 $ cat $EDITED
45 pick 092e4ce14829 3 c
45 pick 092e4ce14829 3 c
46 pick ae78f4c9d74f 4 d
46 pick ae78f4c9d74f 4 d
47 fold 42abbb61bede 7 does not commute with e
47 fold 42abbb61bede 7 does not commute with e
48 pick 7f3755409b00 5 e
48 pick 7f3755409b00 5 e
49 pick dd184f2faeb0 6 f
49 pick dd184f2faeb0 6 f
50
50
51 log before edit
51 log before edit
52 $ hg log --graph
52 $ hg log --graph
53 @ changeset: 7:42abbb61bede
53 @ changeset: 7:42abbb61bede
54 | tag: tip
54 | tag: tip
55 | user: test
55 | user: test
56 | date: Thu Jan 01 00:00:07 1970 +0000
56 | date: Thu Jan 01 00:00:07 1970 +0000
57 | summary: does not commute with e
57 | summary: does not commute with e
58 |
58 |
59 o changeset: 6:dd184f2faeb0
59 o changeset: 6:dd184f2faeb0
60 | user: test
60 | user: test
61 | date: Thu Jan 01 00:00:06 1970 +0000
61 | date: Thu Jan 01 00:00:06 1970 +0000
62 | summary: f
62 | summary: f
63 |
63 |
64 o changeset: 5:7f3755409b00
64 o changeset: 5:7f3755409b00
65 | user: test
65 | user: test
66 | date: Thu Jan 01 00:00:05 1970 +0000
66 | date: Thu Jan 01 00:00:05 1970 +0000
67 | summary: e
67 | summary: e
68 |
68 |
69 o changeset: 4:ae78f4c9d74f
69 o changeset: 4:ae78f4c9d74f
70 | user: test
70 | user: test
71 | date: Thu Jan 01 00:00:04 1970 +0000
71 | date: Thu Jan 01 00:00:04 1970 +0000
72 | summary: d
72 | summary: d
73 |
73 |
74 o changeset: 3:092e4ce14829
74 o changeset: 3:092e4ce14829
75 | user: test
75 | user: test
76 | date: Thu Jan 01 00:00:03 1970 +0000
76 | date: Thu Jan 01 00:00:03 1970 +0000
77 | summary: c
77 | summary: c
78 |
78 |
79 o changeset: 2:40ccdd8beb95
79 o changeset: 2:40ccdd8beb95
80 | user: test
80 | user: test
81 | date: Thu Jan 01 00:00:02 1970 +0000
81 | date: Thu Jan 01 00:00:02 1970 +0000
82 | summary: b
82 | summary: b
83 |
83 |
84 o changeset: 1:cd997a145b29
84 o changeset: 1:cd997a145b29
85 | user: test
85 | user: test
86 | date: Thu Jan 01 00:00:01 1970 +0000
86 | date: Thu Jan 01 00:00:01 1970 +0000
87 | summary: a
87 | summary: a
88 |
88 |
89 o changeset: 0:1715188a53c7
89 o changeset: 0:1715188a53c7
90 user: test
90 user: test
91 date: Thu Jan 01 00:00:00 1970 +0000
91 date: Thu Jan 01 00:00:00 1970 +0000
92 summary: Initial commit
92 summary: Initial commit
93
93
94
94
95 edit the history
95 edit the history
96 $ hg histedit 3 --commands $EDITED 2>&1 | fixbundle
96 $ hg histedit 3 --commands $EDITED 2>&1 | fixbundle
97 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 merging e
97 merging e
99 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
98 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
100 Fix up the change (fold 42abbb61bede)
99 Fix up the change (fold 42abbb61bede)
101 (hg histedit --continue to resume)
100 (hg histedit --continue to resume)
102
101
103 fix up
102 fix up
104 $ echo 'I can haz no commute' > e
103 $ echo 'I can haz no commute' > e
105 $ hg resolve --mark e
104 $ hg resolve --mark e
106 (no more unresolved files)
105 (no more unresolved files)
107 continue: hg histedit --continue
106 continue: hg histedit --continue
108 $ HGEDITOR=cat hg histedit --continue 2>&1 | fixbundle | grep -v '2 files removed'
107 $ HGEDITOR=cat hg histedit --continue 2>&1 | fixbundle | grep -v '2 files removed'
109 d
108 d
110 ***
109 ***
111 does not commute with e
110 does not commute with e
112
111
113
112
114
113
115 HG: Enter commit message. Lines beginning with 'HG:' are removed.
114 HG: Enter commit message. Lines beginning with 'HG:' are removed.
116 HG: Leave message empty to abort commit.
115 HG: Leave message empty to abort commit.
117 HG: --
116 HG: --
118 HG: user: test
117 HG: user: test
119 HG: branch 'default'
118 HG: branch 'default'
120 HG: changed d
119 HG: changed d
121 HG: changed e
120 HG: changed e
122 merging e
121 merging e
123 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
122 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
124 Fix up the change (pick 7f3755409b00)
123 Fix up the change (pick 7f3755409b00)
125 (hg histedit --continue to resume)
124 (hg histedit --continue to resume)
126
125
127 just continue this time
126 just continue this time
128 keep the non-commuting change, and thus the pending change will be dropped
127 keep the non-commuting change, and thus the pending change will be dropped
129 $ hg revert -r 'p1()' e
128 $ hg revert -r 'p1()' e
130 $ hg resolve --mark e
129 $ hg resolve --mark e
131 (no more unresolved files)
130 (no more unresolved files)
132 continue: hg histedit --continue
131 continue: hg histedit --continue
133 $ hg diff
132 $ hg diff
134 $ hg histedit --continue 2>&1 | fixbundle
133 $ hg histedit --continue 2>&1 | fixbundle
135 7f3755409b00: skipping changeset (no changes)
134 7f3755409b00: skipping changeset (no changes)
136
135
137 log after edit
136 log after edit
138 $ hg log --graph
137 $ hg log --graph
139 @ changeset: 5:1300355b1a54
138 @ changeset: 5:1300355b1a54
140 | tag: tip
139 | tag: tip
141 | user: test
140 | user: test
142 | date: Thu Jan 01 00:00:06 1970 +0000
141 | date: Thu Jan 01 00:00:06 1970 +0000
143 | summary: f
142 | summary: f
144 |
143 |
145 o changeset: 4:e2ac33269083
144 o changeset: 4:e2ac33269083
146 | user: test
145 | user: test
147 | date: Thu Jan 01 00:00:07 1970 +0000
146 | date: Thu Jan 01 00:00:07 1970 +0000
148 | summary: d
147 | summary: d
149 |
148 |
150 o changeset: 3:092e4ce14829
149 o changeset: 3:092e4ce14829
151 | user: test
150 | user: test
152 | date: Thu Jan 01 00:00:03 1970 +0000
151 | date: Thu Jan 01 00:00:03 1970 +0000
153 | summary: c
152 | summary: c
154 |
153 |
155 o changeset: 2:40ccdd8beb95
154 o changeset: 2:40ccdd8beb95
156 | user: test
155 | user: test
157 | date: Thu Jan 01 00:00:02 1970 +0000
156 | date: Thu Jan 01 00:00:02 1970 +0000
158 | summary: b
157 | summary: b
159 |
158 |
160 o changeset: 1:cd997a145b29
159 o changeset: 1:cd997a145b29
161 | user: test
160 | user: test
162 | date: Thu Jan 01 00:00:01 1970 +0000
161 | date: Thu Jan 01 00:00:01 1970 +0000
163 | summary: a
162 | summary: a
164 |
163 |
165 o changeset: 0:1715188a53c7
164 o changeset: 0:1715188a53c7
166 user: test
165 user: test
167 date: Thu Jan 01 00:00:00 1970 +0000
166 date: Thu Jan 01 00:00:00 1970 +0000
168 summary: Initial commit
167 summary: Initial commit
169
168
170
169
171 contents of e
170 contents of e
172 $ hg cat e
171 $ hg cat e
173 I can haz no commute
172 I can haz no commute
174
173
175 manifest
174 manifest
176 $ hg manifest
175 $ hg manifest
177 a
176 a
178 b
177 b
179 c
178 c
180 d
179 d
181 e
180 e
182 f
181 f
183
182
184 $ cd ..
183 $ cd ..
185
184
186 Repeat test using "roll", not "fold". "roll" folds in changes but drops message and date
185 Repeat test using "roll", not "fold". "roll" folds in changes but drops message and date
187
186
188 $ initrepo r2
187 $ initrepo r2
189 $ cd r2
188 $ cd r2
190
189
191 Initial generation of the command files
190 Initial generation of the command files
192
191
193 $ EDITED="$TESTTMP/editedhistory.2"
192 $ EDITED="$TESTTMP/editedhistory.2"
194 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 3 >> $EDITED
193 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 3 >> $EDITED
195 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 4 >> $EDITED
194 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 4 >> $EDITED
196 $ hg log --template 'roll {node|short} {rev} {desc}\n' -r 7 >> $EDITED
195 $ hg log --template 'roll {node|short} {rev} {desc}\n' -r 7 >> $EDITED
197 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 5 >> $EDITED
196 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 5 >> $EDITED
198 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 6 >> $EDITED
197 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 6 >> $EDITED
199 $ cat $EDITED
198 $ cat $EDITED
200 pick 092e4ce14829 3 c
199 pick 092e4ce14829 3 c
201 pick ae78f4c9d74f 4 d
200 pick ae78f4c9d74f 4 d
202 roll 42abbb61bede 7 does not commute with e
201 roll 42abbb61bede 7 does not commute with e
203 pick 7f3755409b00 5 e
202 pick 7f3755409b00 5 e
204 pick dd184f2faeb0 6 f
203 pick dd184f2faeb0 6 f
205
204
206 log before edit
205 log before edit
207 $ hg log --graph
206 $ hg log --graph
208 @ changeset: 7:42abbb61bede
207 @ changeset: 7:42abbb61bede
209 | tag: tip
208 | tag: tip
210 | user: test
209 | user: test
211 | date: Thu Jan 01 00:00:07 1970 +0000
210 | date: Thu Jan 01 00:00:07 1970 +0000
212 | summary: does not commute with e
211 | summary: does not commute with e
213 |
212 |
214 o changeset: 6:dd184f2faeb0
213 o changeset: 6:dd184f2faeb0
215 | user: test
214 | user: test
216 | date: Thu Jan 01 00:00:06 1970 +0000
215 | date: Thu Jan 01 00:00:06 1970 +0000
217 | summary: f
216 | summary: f
218 |
217 |
219 o changeset: 5:7f3755409b00
218 o changeset: 5:7f3755409b00
220 | user: test
219 | user: test
221 | date: Thu Jan 01 00:00:05 1970 +0000
220 | date: Thu Jan 01 00:00:05 1970 +0000
222 | summary: e
221 | summary: e
223 |
222 |
224 o changeset: 4:ae78f4c9d74f
223 o changeset: 4:ae78f4c9d74f
225 | user: test
224 | user: test
226 | date: Thu Jan 01 00:00:04 1970 +0000
225 | date: Thu Jan 01 00:00:04 1970 +0000
227 | summary: d
226 | summary: d
228 |
227 |
229 o changeset: 3:092e4ce14829
228 o changeset: 3:092e4ce14829
230 | user: test
229 | user: test
231 | date: Thu Jan 01 00:00:03 1970 +0000
230 | date: Thu Jan 01 00:00:03 1970 +0000
232 | summary: c
231 | summary: c
233 |
232 |
234 o changeset: 2:40ccdd8beb95
233 o changeset: 2:40ccdd8beb95
235 | user: test
234 | user: test
236 | date: Thu Jan 01 00:00:02 1970 +0000
235 | date: Thu Jan 01 00:00:02 1970 +0000
237 | summary: b
236 | summary: b
238 |
237 |
239 o changeset: 1:cd997a145b29
238 o changeset: 1:cd997a145b29
240 | user: test
239 | user: test
241 | date: Thu Jan 01 00:00:01 1970 +0000
240 | date: Thu Jan 01 00:00:01 1970 +0000
242 | summary: a
241 | summary: a
243 |
242 |
244 o changeset: 0:1715188a53c7
243 o changeset: 0:1715188a53c7
245 user: test
244 user: test
246 date: Thu Jan 01 00:00:00 1970 +0000
245 date: Thu Jan 01 00:00:00 1970 +0000
247 summary: Initial commit
246 summary: Initial commit
248
247
249
248
250 edit the history
249 edit the history
251 $ hg histedit 3 --commands $EDITED 2>&1 | fixbundle
250 $ hg histedit 3 --commands $EDITED 2>&1 | fixbundle
252 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
253 merging e
251 merging e
254 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
252 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
255 Fix up the change (roll 42abbb61bede)
253 Fix up the change (roll 42abbb61bede)
256 (hg histedit --continue to resume)
254 (hg histedit --continue to resume)
257
255
258 fix up
256 fix up
259 $ echo 'I can haz no commute' > e
257 $ echo 'I can haz no commute' > e
260 $ hg resolve --mark e
258 $ hg resolve --mark e
261 (no more unresolved files)
259 (no more unresolved files)
262 continue: hg histedit --continue
260 continue: hg histedit --continue
263 $ hg histedit --continue 2>&1 | fixbundle | grep -v '2 files removed'
261 $ hg histedit --continue 2>&1 | fixbundle | grep -v '2 files removed'
264 merging e
262 merging e
265 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
263 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
266 Fix up the change (pick 7f3755409b00)
264 Fix up the change (pick 7f3755409b00)
267 (hg histedit --continue to resume)
265 (hg histedit --continue to resume)
268
266
269 just continue this time
267 just continue this time
270 $ hg revert -r 'p1()' e
268 $ hg revert -r 'p1()' e
271 $ hg resolve --mark e
269 $ hg resolve --mark e
272 (no more unresolved files)
270 (no more unresolved files)
273 continue: hg histedit --continue
271 continue: hg histedit --continue
274 $ hg histedit --continue 2>&1 | fixbundle
272 $ hg histedit --continue 2>&1 | fixbundle
275 7f3755409b00: skipping changeset (no changes)
273 7f3755409b00: skipping changeset (no changes)
276
274
277 log after edit
275 log after edit
278 $ hg log --graph
276 $ hg log --graph
279 @ changeset: 5:b538bcb461be
277 @ changeset: 5:b538bcb461be
280 | tag: tip
278 | tag: tip
281 | user: test
279 | user: test
282 | date: Thu Jan 01 00:00:06 1970 +0000
280 | date: Thu Jan 01 00:00:06 1970 +0000
283 | summary: f
281 | summary: f
284 |
282 |
285 o changeset: 4:317e37cb6d66
283 o changeset: 4:317e37cb6d66
286 | user: test
284 | user: test
287 | date: Thu Jan 01 00:00:04 1970 +0000
285 | date: Thu Jan 01 00:00:04 1970 +0000
288 | summary: d
286 | summary: d
289 |
287 |
290 o changeset: 3:092e4ce14829
288 o changeset: 3:092e4ce14829
291 | user: test
289 | user: test
292 | date: Thu Jan 01 00:00:03 1970 +0000
290 | date: Thu Jan 01 00:00:03 1970 +0000
293 | summary: c
291 | summary: c
294 |
292 |
295 o changeset: 2:40ccdd8beb95
293 o changeset: 2:40ccdd8beb95
296 | user: test
294 | user: test
297 | date: Thu Jan 01 00:00:02 1970 +0000
295 | date: Thu Jan 01 00:00:02 1970 +0000
298 | summary: b
296 | summary: b
299 |
297 |
300 o changeset: 1:cd997a145b29
298 o changeset: 1:cd997a145b29
301 | user: test
299 | user: test
302 | date: Thu Jan 01 00:00:01 1970 +0000
300 | date: Thu Jan 01 00:00:01 1970 +0000
303 | summary: a
301 | summary: a
304 |
302 |
305 o changeset: 0:1715188a53c7
303 o changeset: 0:1715188a53c7
306 user: test
304 user: test
307 date: Thu Jan 01 00:00:00 1970 +0000
305 date: Thu Jan 01 00:00:00 1970 +0000
308 summary: Initial commit
306 summary: Initial commit
309
307
310
308
311 contents of e
309 contents of e
312 $ hg cat e
310 $ hg cat e
313 I can haz no commute
311 I can haz no commute
314
312
315 manifest
313 manifest
316 $ hg manifest
314 $ hg manifest
317 a
315 a
318 b
316 b
319 c
317 c
320 d
318 d
321 e
319 e
322 f
320 f
323
321
324 description is taken from rollup target commit
322 description is taken from rollup target commit
325
323
326 $ hg log --debug --rev 4
324 $ hg log --debug --rev 4
327 changeset: 4:317e37cb6d66c1c84628c00e5bf4c8c292831951
325 changeset: 4:317e37cb6d66c1c84628c00e5bf4c8c292831951
328 phase: draft
326 phase: draft
329 parent: 3:092e4ce14829f4974399ce4316d59f64ef0b6725
327 parent: 3:092e4ce14829f4974399ce4316d59f64ef0b6725
330 parent: -1:0000000000000000000000000000000000000000
328 parent: -1:0000000000000000000000000000000000000000
331 manifest: 4:b068a323d969f22af1296ec6a5ea9384cef437ac
329 manifest: 4:b068a323d969f22af1296ec6a5ea9384cef437ac
332 user: test
330 user: test
333 date: Thu Jan 01 00:00:04 1970 +0000
331 date: Thu Jan 01 00:00:04 1970 +0000
334 files: d e
332 files: d e
335 extra: branch=default
333 extra: branch=default
336 extra: histedit_source=ae78f4c9d74ffa4b6cb5045001c303fe9204e890,42abbb61bede6f4366fa1e74a664343e5d558a70
334 extra: histedit_source=ae78f4c9d74ffa4b6cb5045001c303fe9204e890,42abbb61bede6f4366fa1e74a664343e5d558a70
337 description:
335 description:
338 d
336 d
339
337
340
338
341
339
342 done with repo r2
340 done with repo r2
343
341
344 $ cd ..
342 $ cd ..
@@ -1,707 +1,706 b''
1 Test histedit extension: Fold commands
1 Test histedit extension: Fold commands
2 ======================================
2 ======================================
3
3
4 This test file is dedicated to testing the fold command in non conflicting
4 This test file is dedicated to testing the fold command in non conflicting
5 case.
5 case.
6
6
7 Initialization
7 Initialization
8 ---------------
8 ---------------
9
9
10
10
11 $ . "$TESTDIR/histedit-helpers.sh"
11 $ . "$TESTDIR/histedit-helpers.sh"
12
12
13 $ cat >> $HGRCPATH <<EOF
13 $ cat >> $HGRCPATH <<EOF
14 > [alias]
14 > [alias]
15 > logt = log --template '{rev}:{node|short} {desc|firstline}\n'
15 > logt = log --template '{rev}:{node|short} {desc|firstline}\n'
16 > [extensions]
16 > [extensions]
17 > histedit=
17 > histedit=
18 > mockmakedate = $TESTDIR/mockmakedate.py
18 > mockmakedate = $TESTDIR/mockmakedate.py
19 > EOF
19 > EOF
20
20
21
21
22 Simple folding
22 Simple folding
23 --------------------
23 --------------------
24 $ addwithdate ()
24 $ addwithdate ()
25 > {
25 > {
26 > echo $1 > $1
26 > echo $1 > $1
27 > hg add $1
27 > hg add $1
28 > hg ci -m $1 -d "$2 0"
28 > hg ci -m $1 -d "$2 0"
29 > }
29 > }
30
30
31 $ initrepo ()
31 $ initrepo ()
32 > {
32 > {
33 > hg init r
33 > hg init r
34 > cd r
34 > cd r
35 > addwithdate a 1
35 > addwithdate a 1
36 > addwithdate b 2
36 > addwithdate b 2
37 > addwithdate c 3
37 > addwithdate c 3
38 > addwithdate d 4
38 > addwithdate d 4
39 > addwithdate e 5
39 > addwithdate e 5
40 > addwithdate f 6
40 > addwithdate f 6
41 > }
41 > }
42
42
43 $ initrepo
43 $ initrepo
44
44
45 log before edit
45 log before edit
46 $ hg logt --graph
46 $ hg logt --graph
47 @ 5:178e35e0ce73 f
47 @ 5:178e35e0ce73 f
48 |
48 |
49 o 4:1ddb6c90f2ee e
49 o 4:1ddb6c90f2ee e
50 |
50 |
51 o 3:532247a8969b d
51 o 3:532247a8969b d
52 |
52 |
53 o 2:ff2c9fa2018b c
53 o 2:ff2c9fa2018b c
54 |
54 |
55 o 1:97d72e5f12c7 b
55 o 1:97d72e5f12c7 b
56 |
56 |
57 o 0:8580ff50825a a
57 o 0:8580ff50825a a
58
58
59
59
60 $ hg histedit ff2c9fa2018b --commands - 2>&1 <<EOF | fixbundle
60 $ hg histedit ff2c9fa2018b --commands - 2>&1 <<EOF | fixbundle
61 > pick 1ddb6c90f2ee e
61 > pick 1ddb6c90f2ee e
62 > pick 178e35e0ce73 f
62 > pick 178e35e0ce73 f
63 > fold ff2c9fa2018b c
63 > fold ff2c9fa2018b c
64 > pick 532247a8969b d
64 > pick 532247a8969b d
65 > EOF
65 > EOF
66
66
67 log after edit
67 log after edit
68 $ hg logt --graph
68 $ hg logt --graph
69 @ 4:c4d7f3def76d d
69 @ 4:c4d7f3def76d d
70 |
70 |
71 o 3:575228819b7e f
71 o 3:575228819b7e f
72 |
72 |
73 o 2:505a591af19e e
73 o 2:505a591af19e e
74 |
74 |
75 o 1:97d72e5f12c7 b
75 o 1:97d72e5f12c7 b
76 |
76 |
77 o 0:8580ff50825a a
77 o 0:8580ff50825a a
78
78
79
79
80 post-fold manifest
80 post-fold manifest
81 $ hg manifest
81 $ hg manifest
82 a
82 a
83 b
83 b
84 c
84 c
85 d
85 d
86 e
86 e
87 f
87 f
88
88
89
89
90 check histedit_source, including that it uses the later date, from the first changeset
90 check histedit_source, including that it uses the later date, from the first changeset
91
91
92 $ hg log --debug --rev 3
92 $ hg log --debug --rev 3
93 changeset: 3:575228819b7e6ed69e8c0a6a383ee59a80db7358
93 changeset: 3:575228819b7e6ed69e8c0a6a383ee59a80db7358
94 phase: draft
94 phase: draft
95 parent: 2:505a591af19eed18f560af827b9e03d2076773dc
95 parent: 2:505a591af19eed18f560af827b9e03d2076773dc
96 parent: -1:0000000000000000000000000000000000000000
96 parent: -1:0000000000000000000000000000000000000000
97 manifest: 3:81eede616954057198ead0b2c73b41d1f392829a
97 manifest: 3:81eede616954057198ead0b2c73b41d1f392829a
98 user: test
98 user: test
99 date: Thu Jan 01 00:00:06 1970 +0000
99 date: Thu Jan 01 00:00:06 1970 +0000
100 files+: c f
100 files+: c f
101 extra: branch=default
101 extra: branch=default
102 extra: histedit_source=7cad1d7030207872dfd1c3a7cb430f24f2884086,ff2c9fa2018b15fa74b33363bda9527323e2a99f
102 extra: histedit_source=7cad1d7030207872dfd1c3a7cb430f24f2884086,ff2c9fa2018b15fa74b33363bda9527323e2a99f
103 description:
103 description:
104 f
104 f
105 ***
105 ***
106 c
106 c
107
107
108
108
109
109
110 rollup will fold without preserving the folded commit's message or date
110 rollup will fold without preserving the folded commit's message or date
111
111
112 $ OLDHGEDITOR=$HGEDITOR
112 $ OLDHGEDITOR=$HGEDITOR
113 $ HGEDITOR=false
113 $ HGEDITOR=false
114 $ hg histedit 97d72e5f12c7 --commands - 2>&1 <<EOF | fixbundle
114 $ hg histedit 97d72e5f12c7 --commands - 2>&1 <<EOF | fixbundle
115 > pick 97d72e5f12c7 b
115 > pick 97d72e5f12c7 b
116 > roll 505a591af19e e
116 > roll 505a591af19e e
117 > pick 575228819b7e f
117 > pick 575228819b7e f
118 > pick c4d7f3def76d d
118 > pick c4d7f3def76d d
119 > EOF
119 > EOF
120
120
121 $ HGEDITOR=$OLDHGEDITOR
121 $ HGEDITOR=$OLDHGEDITOR
122
122
123 log after edit
123 log after edit
124 $ hg logt --graph
124 $ hg logt --graph
125 @ 3:bab801520cec d
125 @ 3:bab801520cec d
126 |
126 |
127 o 2:58c8f2bfc151 f
127 o 2:58c8f2bfc151 f
128 |
128 |
129 o 1:5d939c56c72e b
129 o 1:5d939c56c72e b
130 |
130 |
131 o 0:8580ff50825a a
131 o 0:8580ff50825a a
132
132
133
133
134 description is taken from rollup target commit
134 description is taken from rollup target commit
135
135
136 $ hg log --debug --rev 1
136 $ hg log --debug --rev 1
137 changeset: 1:5d939c56c72e77e29f5167696218e2131a40f5cf
137 changeset: 1:5d939c56c72e77e29f5167696218e2131a40f5cf
138 phase: draft
138 phase: draft
139 parent: 0:8580ff50825a50c8f716709acdf8de0deddcd6ab
139 parent: 0:8580ff50825a50c8f716709acdf8de0deddcd6ab
140 parent: -1:0000000000000000000000000000000000000000
140 parent: -1:0000000000000000000000000000000000000000
141 manifest: 1:b5e112a3a8354e269b1524729f0918662d847c38
141 manifest: 1:b5e112a3a8354e269b1524729f0918662d847c38
142 user: test
142 user: test
143 date: Thu Jan 01 00:00:02 1970 +0000
143 date: Thu Jan 01 00:00:02 1970 +0000
144 files+: b e
144 files+: b e
145 extra: branch=default
145 extra: branch=default
146 extra: histedit_source=97d72e5f12c7e84f85064aa72e5a297142c36ed9,505a591af19eed18f560af827b9e03d2076773dc
146 extra: histedit_source=97d72e5f12c7e84f85064aa72e5a297142c36ed9,505a591af19eed18f560af827b9e03d2076773dc
147 description:
147 description:
148 b
148 b
149
149
150
150
151
151
152 check saving last-message.txt
152 check saving last-message.txt
153
153
154 $ cat > $TESTTMP/abortfolding.py <<EOF
154 $ cat > $TESTTMP/abortfolding.py <<EOF
155 > from mercurial import util
155 > from mercurial import util
156 > def abortfolding(ui, repo, hooktype, **kwargs):
156 > def abortfolding(ui, repo, hooktype, **kwargs):
157 > ctx = repo[kwargs.get('node')]
157 > ctx = repo[kwargs.get('node')]
158 > if set(ctx.files()) == {b'c', b'd', b'f'}:
158 > if set(ctx.files()) == {b'c', b'd', b'f'}:
159 > return True # abort folding commit only
159 > return True # abort folding commit only
160 > ui.warn(b'allow non-folding commit\\n')
160 > ui.warn(b'allow non-folding commit\\n')
161 > EOF
161 > EOF
162 $ cat > .hg/hgrc <<EOF
162 $ cat > .hg/hgrc <<EOF
163 > [hooks]
163 > [hooks]
164 > pretxncommit.abortfolding = python:$TESTTMP/abortfolding.py:abortfolding
164 > pretxncommit.abortfolding = python:$TESTTMP/abortfolding.py:abortfolding
165 > EOF
165 > EOF
166
166
167 $ cat > $TESTTMP/editor.sh << EOF
167 $ cat > $TESTTMP/editor.sh << EOF
168 > echo "==== before editing"
168 > echo "==== before editing"
169 > cat \$1
169 > cat \$1
170 > echo "===="
170 > echo "===="
171 > echo "check saving last-message.txt" >> \$1
171 > echo "check saving last-message.txt" >> \$1
172 > EOF
172 > EOF
173
173
174 $ rm -f .hg/last-message.txt
174 $ rm -f .hg/last-message.txt
175 $ hg status --rev '58c8f2bfc151^1::bab801520cec'
175 $ hg status --rev '58c8f2bfc151^1::bab801520cec'
176 A c
176 A c
177 A d
177 A d
178 A f
178 A f
179 $ HGEDITOR="sh $TESTTMP/editor.sh" hg histedit 58c8f2bfc151 --commands - 2>&1 <<EOF
179 $ HGEDITOR="sh $TESTTMP/editor.sh" hg histedit 58c8f2bfc151 --commands - 2>&1 <<EOF
180 > pick 58c8f2bfc151 f
180 > pick 58c8f2bfc151 f
181 > fold bab801520cec d
181 > fold bab801520cec d
182 > EOF
182 > EOF
183 allow non-folding commit
183 allow non-folding commit
184 ==== before editing
184 ==== before editing
185 f
185 f
186 ***
186 ***
187 c
187 c
188 ***
188 ***
189 d
189 d
190
190
191
191
192
192
193 HG: Enter commit message. Lines beginning with 'HG:' are removed.
193 HG: Enter commit message. Lines beginning with 'HG:' are removed.
194 HG: Leave message empty to abort commit.
194 HG: Leave message empty to abort commit.
195 HG: --
195 HG: --
196 HG: user: test
196 HG: user: test
197 HG: branch 'default'
197 HG: branch 'default'
198 HG: added c
198 HG: added c
199 HG: added d
199 HG: added d
200 HG: added f
200 HG: added f
201 ====
201 ====
202 transaction abort!
202 transaction abort!
203 rollback completed
203 rollback completed
204 abort: pretxncommit.abortfolding hook failed
204 abort: pretxncommit.abortfolding hook failed
205 [255]
205 [255]
206
206
207 $ cat .hg/last-message.txt
207 $ cat .hg/last-message.txt
208 f
208 f
209 ***
209 ***
210 c
210 c
211 ***
211 ***
212 d
212 d
213
213
214
214
215
215
216 check saving last-message.txt
216 check saving last-message.txt
217
217
218 $ cd ..
218 $ cd ..
219 $ rm -r r
219 $ rm -r r
220
220
221 folding preserves initial author but uses later date
221 folding preserves initial author but uses later date
222 ----------------------------------------------------
222 ----------------------------------------------------
223
223
224 $ initrepo
224 $ initrepo
225
225
226 $ hg ci -d '7 0' --user "someone else" --amend --quiet
226 $ hg ci -d '7 0' --user "someone else" --amend --quiet
227
227
228 tip before edit
228 tip before edit
229 $ hg log --rev .
229 $ hg log --rev .
230 changeset: 5:10c36dd37515
230 changeset: 5:10c36dd37515
231 tag: tip
231 tag: tip
232 user: someone else
232 user: someone else
233 date: Thu Jan 01 00:00:07 1970 +0000
233 date: Thu Jan 01 00:00:07 1970 +0000
234 summary: f
234 summary: f
235
235
236
236
237 $ hg --config progress.debug=1 --debug \
237 $ hg --config progress.debug=1 --debug \
238 > histedit 1ddb6c90f2ee --commands - 2>&1 <<EOF | \
238 > histedit 1ddb6c90f2ee --commands - 2>&1 <<EOF | \
239 > egrep 'editing|unresolved'
239 > egrep 'editing|unresolved'
240 > pick 1ddb6c90f2ee e
240 > pick 1ddb6c90f2ee e
241 > fold 10c36dd37515 f
241 > fold 10c36dd37515 f
242 > EOF
242 > EOF
243 editing: pick 1ddb6c90f2ee 4 e 1/2 changes (50.00%)
243 editing: pick 1ddb6c90f2ee 4 e 1/2 changes (50.00%)
244 editing: fold 10c36dd37515 5 f 2/2 changes (100.00%)
244 editing: fold 10c36dd37515 5 f 2/2 changes (100.00%)
245
245
246 tip after edit, which should use the later date, from the second changeset
246 tip after edit, which should use the later date, from the second changeset
247 $ hg log --rev .
247 $ hg log --rev .
248 changeset: 4:e4f3ec5d0b40
248 changeset: 4:e4f3ec5d0b40
249 tag: tip
249 tag: tip
250 user: test
250 user: test
251 date: Thu Jan 01 00:00:07 1970 +0000
251 date: Thu Jan 01 00:00:07 1970 +0000
252 summary: e
252 summary: e
253
253
254
254
255 $ cd ..
255 $ cd ..
256 $ rm -r r
256 $ rm -r r
257
257
258 folding and creating no new change doesn't break:
258 folding and creating no new change doesn't break:
259 -------------------------------------------------
259 -------------------------------------------------
260
260
261 folded content is dropped during a merge. The folded commit should properly disappear.
261 folded content is dropped during a merge. The folded commit should properly disappear.
262
262
263 $ mkdir fold-to-empty-test
263 $ mkdir fold-to-empty-test
264 $ cd fold-to-empty-test
264 $ cd fold-to-empty-test
265 $ hg init
265 $ hg init
266 $ printf "1\n2\n3\n" > file
266 $ printf "1\n2\n3\n" > file
267 $ hg add file
267 $ hg add file
268 $ hg commit -m '1+2+3'
268 $ hg commit -m '1+2+3'
269 $ echo 4 >> file
269 $ echo 4 >> file
270 $ hg commit -m '+4'
270 $ hg commit -m '+4'
271 $ echo 5 >> file
271 $ echo 5 >> file
272 $ hg commit -m '+5'
272 $ hg commit -m '+5'
273 $ echo 6 >> file
273 $ echo 6 >> file
274 $ hg commit -m '+6'
274 $ hg commit -m '+6'
275 $ hg logt --graph
275 $ hg logt --graph
276 @ 3:251d831eeec5 +6
276 @ 3:251d831eeec5 +6
277 |
277 |
278 o 2:888f9082bf99 +5
278 o 2:888f9082bf99 +5
279 |
279 |
280 o 1:617f94f13c0f +4
280 o 1:617f94f13c0f +4
281 |
281 |
282 o 0:0189ba417d34 1+2+3
282 o 0:0189ba417d34 1+2+3
283
283
284
284
285 $ hg histedit 1 --commands - << EOF
285 $ hg histedit 1 --commands - << EOF
286 > pick 617f94f13c0f 1 +4
286 > pick 617f94f13c0f 1 +4
287 > drop 888f9082bf99 2 +5
287 > drop 888f9082bf99 2 +5
288 > fold 251d831eeec5 3 +6
288 > fold 251d831eeec5 3 +6
289 > EOF
289 > EOF
290 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
291 merging file
290 merging file
292 warning: conflicts while merging file! (edit, then use 'hg resolve --mark')
291 warning: conflicts while merging file! (edit, then use 'hg resolve --mark')
293 Fix up the change (fold 251d831eeec5)
292 Fix up the change (fold 251d831eeec5)
294 (hg histedit --continue to resume)
293 (hg histedit --continue to resume)
295 [1]
294 [1]
296 There were conflicts, we keep P1 content. This
295 There were conflicts, we keep P1 content. This
297 should effectively drop the changes from +6.
296 should effectively drop the changes from +6.
298
297
299 $ hg status -v
298 $ hg status -v
300 M file
299 M file
301 ? file.orig
300 ? file.orig
302 # The repository is in an unfinished *histedit* state.
301 # The repository is in an unfinished *histedit* state.
303
302
304 # Unresolved merge conflicts:
303 # Unresolved merge conflicts:
305 #
304 #
306 # file
305 # file
307 #
306 #
308 # To mark files as resolved: hg resolve --mark FILE
307 # To mark files as resolved: hg resolve --mark FILE
309
308
310 # To continue: hg histedit --continue
309 # To continue: hg histedit --continue
311 # To abort: hg histedit --abort
310 # To abort: hg histedit --abort
312
311
313 $ hg resolve -l
312 $ hg resolve -l
314 U file
313 U file
315 $ hg revert -r 'p1()' file
314 $ hg revert -r 'p1()' file
316 $ hg resolve --mark file
315 $ hg resolve --mark file
317 (no more unresolved files)
316 (no more unresolved files)
318 continue: hg histedit --continue
317 continue: hg histedit --continue
319 $ hg histedit --continue
318 $ hg histedit --continue
320 251d831eeec5: empty changeset
319 251d831eeec5: empty changeset
321 saved backup bundle to $TESTTMP/fold-to-empty-test/.hg/strip-backup/888f9082bf99-daa0b8b3-histedit.hg
320 saved backup bundle to $TESTTMP/fold-to-empty-test/.hg/strip-backup/888f9082bf99-daa0b8b3-histedit.hg
322 $ hg logt --graph
321 $ hg logt --graph
323 @ 1:617f94f13c0f +4
322 @ 1:617f94f13c0f +4
324 |
323 |
325 o 0:0189ba417d34 1+2+3
324 o 0:0189ba417d34 1+2+3
326
325
327
326
328 $ cd ..
327 $ cd ..
329
328
330
329
331 Test fold through dropped
330 Test fold through dropped
332 -------------------------
331 -------------------------
333
332
334
333
335 Test corner case where folded revision is separated from its parent by a
334 Test corner case where folded revision is separated from its parent by a
336 dropped revision.
335 dropped revision.
337
336
338
337
339 $ hg init fold-with-dropped
338 $ hg init fold-with-dropped
340 $ cd fold-with-dropped
339 $ cd fold-with-dropped
341 $ printf "1\n2\n3\n" > file
340 $ printf "1\n2\n3\n" > file
342 $ hg commit -Am '1+2+3'
341 $ hg commit -Am '1+2+3'
343 adding file
342 adding file
344 $ echo 4 >> file
343 $ echo 4 >> file
345 $ hg commit -m '+4'
344 $ hg commit -m '+4'
346 $ echo 5 >> file
345 $ echo 5 >> file
347 $ hg commit -m '+5'
346 $ hg commit -m '+5'
348 $ echo 6 >> file
347 $ echo 6 >> file
349 $ hg commit -m '+6'
348 $ hg commit -m '+6'
350 $ hg logt -G
349 $ hg logt -G
351 @ 3:251d831eeec5 +6
350 @ 3:251d831eeec5 +6
352 |
351 |
353 o 2:888f9082bf99 +5
352 o 2:888f9082bf99 +5
354 |
353 |
355 o 1:617f94f13c0f +4
354 o 1:617f94f13c0f +4
356 |
355 |
357 o 0:0189ba417d34 1+2+3
356 o 0:0189ba417d34 1+2+3
358
357
359 $ hg histedit 1 --commands - << EOF
358 $ hg histedit 1 --commands - << EOF
360 > pick 617f94f13c0f 1 +4
359 > pick 617f94f13c0f 1 +4
361 > drop 888f9082bf99 2 +5
360 > drop 888f9082bf99 2 +5
362 > fold 251d831eeec5 3 +6
361 > fold 251d831eeec5 3 +6
363 > EOF
362 > EOF
364 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
365 merging file
363 merging file
366 warning: conflicts while merging file! (edit, then use 'hg resolve --mark')
364 warning: conflicts while merging file! (edit, then use 'hg resolve --mark')
367 Fix up the change (fold 251d831eeec5)
365 Fix up the change (fold 251d831eeec5)
368 (hg histedit --continue to resume)
366 (hg histedit --continue to resume)
369 [1]
367 [1]
370 $ cat > file << EOF
368 $ cat > file << EOF
371 > 1
369 > 1
372 > 2
370 > 2
373 > 3
371 > 3
374 > 4
372 > 4
375 > 5
373 > 5
376 > EOF
374 > EOF
377 $ hg resolve --mark file
375 $ hg resolve --mark file
378 (no more unresolved files)
376 (no more unresolved files)
379 continue: hg histedit --continue
377 continue: hg histedit --continue
380 $ hg commit -m '+5.2'
378 $ hg commit -m '+5.2'
381 created new head
379 created new head
382 $ echo 6 >> file
380 $ echo 6 >> file
383 $ HGEDITOR=cat hg histedit --continue
381 $ HGEDITOR=cat hg histedit --continue
384 +4
382 +4
385 ***
383 ***
386 +5.2
384 +5.2
387 ***
385 ***
388 +6
386 +6
389
387
390
388
391
389
392 HG: Enter commit message. Lines beginning with 'HG:' are removed.
390 HG: Enter commit message. Lines beginning with 'HG:' are removed.
393 HG: Leave message empty to abort commit.
391 HG: Leave message empty to abort commit.
394 HG: --
392 HG: --
395 HG: user: test
393 HG: user: test
396 HG: branch 'default'
394 HG: branch 'default'
397 HG: changed file
395 HG: changed file
398 saved backup bundle to $TESTTMP/fold-with-dropped/.hg/strip-backup/617f94f13c0f-3d69522c-histedit.hg
396 saved backup bundle to $TESTTMP/fold-with-dropped/.hg/strip-backup/617f94f13c0f-3d69522c-histedit.hg
399 $ hg logt -G
397 $ hg logt -G
400 @ 1:10c647b2cdd5 +4
398 @ 1:10c647b2cdd5 +4
401 |
399 |
402 o 0:0189ba417d34 1+2+3
400 o 0:0189ba417d34 1+2+3
403
401
404 $ hg export tip
402 $ hg export tip
405 # HG changeset patch
403 # HG changeset patch
406 # User test
404 # User test
407 # Date 0 0
405 # Date 0 0
408 # Thu Jan 01 00:00:00 1970 +0000
406 # Thu Jan 01 00:00:00 1970 +0000
409 # Node ID 10c647b2cdd54db0603ecb99b2ff5ce66d5a5323
407 # Node ID 10c647b2cdd54db0603ecb99b2ff5ce66d5a5323
410 # Parent 0189ba417d34df9dda55f88b637dcae9917b5964
408 # Parent 0189ba417d34df9dda55f88b637dcae9917b5964
411 +4
409 +4
412 ***
410 ***
413 +5.2
411 +5.2
414 ***
412 ***
415 +6
413 +6
416
414
417 diff -r 0189ba417d34 -r 10c647b2cdd5 file
415 diff -r 0189ba417d34 -r 10c647b2cdd5 file
418 --- a/file Thu Jan 01 00:00:00 1970 +0000
416 --- a/file Thu Jan 01 00:00:00 1970 +0000
419 +++ b/file Thu Jan 01 00:00:00 1970 +0000
417 +++ b/file Thu Jan 01 00:00:00 1970 +0000
420 @@ -1,3 +1,6 @@
418 @@ -1,3 +1,6 @@
421 1
419 1
422 2
420 2
423 3
421 3
424 +4
422 +4
425 +5
423 +5
426 +6
424 +6
427 $ cd ..
425 $ cd ..
428
426
429
427
430 Folding with initial rename (issue3729)
428 Folding with initial rename (issue3729)
431 ---------------------------------------
429 ---------------------------------------
432
430
433 $ hg init fold-rename
431 $ hg init fold-rename
434 $ cd fold-rename
432 $ cd fold-rename
435 $ echo a > a.txt
433 $ echo a > a.txt
436 $ hg add a.txt
434 $ hg add a.txt
437 $ hg commit -m a
435 $ hg commit -m a
438 $ hg rename a.txt b.txt
436 $ hg rename a.txt b.txt
439 $ hg commit -m rename
437 $ hg commit -m rename
440 $ echo b >> b.txt
438 $ echo b >> b.txt
441 $ hg commit -m b
439 $ hg commit -m b
442
440
443 $ hg logt --follow b.txt
441 $ hg logt --follow b.txt
444 2:e0371e0426bc b
442 2:e0371e0426bc b
445 1:1c4f440a8085 rename
443 1:1c4f440a8085 rename
446 0:6c795aa153cb a
444 0:6c795aa153cb a
447
445
448 $ hg histedit 1c4f440a8085 --commands - 2>&1 << EOF | fixbundle
446 $ hg histedit 1c4f440a8085 --commands - 2>&1 << EOF | fixbundle
449 > pick 1c4f440a8085 rename
447 > pick 1c4f440a8085 rename
450 > fold e0371e0426bc b
448 > fold e0371e0426bc b
451 > EOF
449 > EOF
452
450
453 $ hg logt --follow b.txt
451 $ hg logt --follow b.txt
454 1:cf858d235c76 rename
452 1:cf858d235c76 rename
455 0:6c795aa153cb a
453 0:6c795aa153cb a
456
454
457 $ cd ..
455 $ cd ..
458
456
459 Folding with swapping
457 Folding with swapping
460 ---------------------
458 ---------------------
461
459
462 This is an excuse to test hook with histedit temporary commit (issue4422)
460 This is an excuse to test hook with histedit temporary commit (issue4422)
463
461
464
462
465 $ hg init issue4422
463 $ hg init issue4422
466 $ cd issue4422
464 $ cd issue4422
467 $ echo a > a.txt
465 $ echo a > a.txt
468 $ hg add a.txt
466 $ hg add a.txt
469 $ hg commit -m a
467 $ hg commit -m a
470 $ echo b > b.txt
468 $ echo b > b.txt
471 $ hg add b.txt
469 $ hg add b.txt
472 $ hg commit -m b
470 $ hg commit -m b
473 $ echo c > c.txt
471 $ echo c > c.txt
474 $ hg add c.txt
472 $ hg add c.txt
475 $ hg commit -m c
473 $ hg commit -m c
476
474
477 $ hg logt
475 $ hg logt
478 2:a1a953ffb4b0 c
476 2:a1a953ffb4b0 c
479 1:199b6bb90248 b
477 1:199b6bb90248 b
480 0:6c795aa153cb a
478 0:6c795aa153cb a
481
479
482 $ hg histedit 6c795aa153cb --config hooks.commit='echo commit $HG_NODE' --config hooks.tonative.commit=True \
480 $ hg histedit 6c795aa153cb --config hooks.commit='echo commit $HG_NODE' --config hooks.tonative.commit=True \
483 > --commands - 2>&1 << EOF | fixbundle
481 > --commands - 2>&1 << EOF | fixbundle
484 > pick 199b6bb90248 b
482 > pick 199b6bb90248 b
485 > fold a1a953ffb4b0 c
483 > fold a1a953ffb4b0 c
486 > pick 6c795aa153cb a
484 > pick 6c795aa153cb a
487 > EOF
485 > EOF
488 commit 9599899f62c05f4377548c32bf1c9f1a39634b0c
486 commit 9599899f62c05f4377548c32bf1c9f1a39634b0c
489
487
490 $ hg logt
488 $ hg logt
491 1:9599899f62c0 a
489 1:9599899f62c0 a
492 0:79b99e9c8e49 b
490 0:79b99e9c8e49 b
493
491
494 Test unix -> windows style variable substitution in external hooks.
492 Test unix -> windows style variable substitution in external hooks.
495
493
496 $ cat > $TESTTMP/tmp.hgrc <<'EOF'
494 $ cat > $TESTTMP/tmp.hgrc <<'EOF'
497 > [hooks]
495 > [hooks]
498 > pre-add = echo no variables
496 > pre-add = echo no variables
499 > post-add = echo ran $HG_ARGS, literal \$non-var, 'also $non-var', $HG_RESULT
497 > post-add = echo ran $HG_ARGS, literal \$non-var, 'also $non-var', $HG_RESULT
500 > tonative.post-add = True
498 > tonative.post-add = True
501 > EOF
499 > EOF
502
500
503 $ echo "foo" > amended.txt
501 $ echo "foo" > amended.txt
504 $ HGRCPATH=$TESTTMP/tmp.hgrc hg add -v amended.txt
502 $ HGRCPATH=$TESTTMP/tmp.hgrc hg add -v amended.txt
505 running hook pre-add: echo no variables
503 running hook pre-add: echo no variables
506 no variables
504 no variables
507 adding amended.txt
505 adding amended.txt
508 converting hook "post-add" to native (windows !)
506 converting hook "post-add" to native (windows !)
509 running hook post-add: echo ran %HG_ARGS%, literal $non-var, "also $non-var", %HG_RESULT% (windows !)
507 running hook post-add: echo ran %HG_ARGS%, literal $non-var, "also $non-var", %HG_RESULT% (windows !)
510 running hook post-add: echo ran $HG_ARGS, literal \$non-var, 'also $non-var', $HG_RESULT (no-windows !)
508 running hook post-add: echo ran $HG_ARGS, literal \$non-var, 'also $non-var', $HG_RESULT (no-windows !)
511 ran add -v amended.txt, literal $non-var, "also $non-var", 0 (windows !)
509 ran add -v amended.txt, literal $non-var, "also $non-var", 0 (windows !)
512 ran add -v amended.txt, literal $non-var, also $non-var, 0 (no-windows !)
510 ran add -v amended.txt, literal $non-var, also $non-var, 0 (no-windows !)
513 $ hg ci -q --config extensions.largefiles= --amend -I amended.txt
511 $ hg ci -q --config extensions.largefiles= --amend -I amended.txt
514 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
512 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
515
513
516 Test that folding multiple changes in a row doesn't show multiple
514 Test that folding multiple changes in a row doesn't show multiple
517 editors.
515 editors.
518
516
519 $ echo foo >> foo
517 $ echo foo >> foo
520 $ hg add foo
518 $ hg add foo
521 $ hg ci -m foo1
519 $ hg ci -m foo1
522 $ echo foo >> foo
520 $ echo foo >> foo
523 $ hg ci -m foo2
521 $ hg ci -m foo2
524 $ echo foo >> foo
522 $ echo foo >> foo
525 $ hg ci -m foo3
523 $ hg ci -m foo3
526 $ hg logt
524 $ hg logt
527 4:21679ff7675c foo3
525 4:21679ff7675c foo3
528 3:b7389cc4d66e foo2
526 3:b7389cc4d66e foo2
529 2:0e01aeef5fa8 foo1
527 2:0e01aeef5fa8 foo1
530 1:578c7455730c a
528 1:578c7455730c a
531 0:79b99e9c8e49 b
529 0:79b99e9c8e49 b
532 $ cat > "$TESTTMP/editor.sh" <<EOF
530 $ cat > "$TESTTMP/editor.sh" <<EOF
533 > echo ran editor >> "$TESTTMP/editorlog.txt"
531 > echo ran editor >> "$TESTTMP/editorlog.txt"
534 > cat \$1 >> "$TESTTMP/editorlog.txt"
532 > cat \$1 >> "$TESTTMP/editorlog.txt"
535 > echo END >> "$TESTTMP/editorlog.txt"
533 > echo END >> "$TESTTMP/editorlog.txt"
536 > echo merged foos > \$1
534 > echo merged foos > \$1
537 > EOF
535 > EOF
538 $ HGEDITOR="sh \"$TESTTMP/editor.sh\"" hg histedit 1 --commands - 2>&1 <<EOF | fixbundle
536 $ HGEDITOR="sh \"$TESTTMP/editor.sh\"" hg histedit 1 --commands - 2>&1 <<EOF | fixbundle
539 > pick 578c7455730c 1 a
537 > pick 578c7455730c 1 a
540 > pick 0e01aeef5fa8 2 foo1
538 > pick 0e01aeef5fa8 2 foo1
541 > fold b7389cc4d66e 3 foo2
539 > fold b7389cc4d66e 3 foo2
542 > fold 21679ff7675c 4 foo3
540 > fold 21679ff7675c 4 foo3
543 > EOF
541 > EOF
542 merging foo
544 $ hg logt
543 $ hg logt
545 2:e8bedbda72c1 merged foos
544 2:e8bedbda72c1 merged foos
546 1:578c7455730c a
545 1:578c7455730c a
547 0:79b99e9c8e49 b
546 0:79b99e9c8e49 b
548 Editor should have run only once
547 Editor should have run only once
549 $ cat $TESTTMP/editorlog.txt
548 $ cat $TESTTMP/editorlog.txt
550 ran editor
549 ran editor
551 foo1
550 foo1
552 ***
551 ***
553 foo2
552 foo2
554 ***
553 ***
555 foo3
554 foo3
556
555
557
556
558
557
559 HG: Enter commit message. Lines beginning with 'HG:' are removed.
558 HG: Enter commit message. Lines beginning with 'HG:' are removed.
560 HG: Leave message empty to abort commit.
559 HG: Leave message empty to abort commit.
561 HG: --
560 HG: --
562 HG: user: test
561 HG: user: test
563 HG: branch 'default'
562 HG: branch 'default'
564 HG: added foo
563 HG: added foo
565 END
564 END
566
565
567 $ cd ..
566 $ cd ..
568
567
569 Test rolling into a commit with multiple children (issue5498)
568 Test rolling into a commit with multiple children (issue5498)
570
569
571 $ hg init roll
570 $ hg init roll
572 $ cd roll
571 $ cd roll
573 $ echo a > a
572 $ echo a > a
574 $ hg commit -qAm aa
573 $ hg commit -qAm aa
575 $ echo b > b
574 $ echo b > b
576 $ hg commit -qAm bb
575 $ hg commit -qAm bb
577 $ hg up -q ".^"
576 $ hg up -q ".^"
578 $ echo c > c
577 $ echo c > c
579 $ hg commit -qAm cc
578 $ hg commit -qAm cc
580 $ hg log -G -T '{node|short} {desc}'
579 $ hg log -G -T '{node|short} {desc}'
581 @ 5db65b93a12b cc
580 @ 5db65b93a12b cc
582 |
581 |
583 | o 301d76bdc3ae bb
582 | o 301d76bdc3ae bb
584 |/
583 |/
585 o 8f0162e483d0 aa
584 o 8f0162e483d0 aa
586
585
587
586
588 $ hg histedit . --commands - << EOF
587 $ hg histedit . --commands - << EOF
589 > r 5db65b93a12b
588 > r 5db65b93a12b
590 > EOF
589 > EOF
591 hg: parse error: first changeset cannot use verb "roll"
590 hg: parse error: first changeset cannot use verb "roll"
592 [255]
591 [255]
593 $ hg log -G -T '{node|short} {desc}'
592 $ hg log -G -T '{node|short} {desc}'
594 @ 5db65b93a12b cc
593 @ 5db65b93a12b cc
595 |
594 |
596 | o 301d76bdc3ae bb
595 | o 301d76bdc3ae bb
597 |/
596 |/
598 o 8f0162e483d0 aa
597 o 8f0162e483d0 aa
599
598
600
599
601 $ cd ..
600 $ cd ..
602
601
603 ====================================
602 ====================================
604 Test update-timestamp config option|
603 Test update-timestamp config option|
605 ====================================
604 ====================================
606
605
607 $ addwithdate ()
606 $ addwithdate ()
608 > {
607 > {
609 > echo $1 > $1
608 > echo $1 > $1
610 > hg add $1
609 > hg add $1
611 > hg ci -m $1 -d "$2 0"
610 > hg ci -m $1 -d "$2 0"
612 > }
611 > }
613
612
614 $ initrepo ()
613 $ initrepo ()
615 > {
614 > {
616 > hg init r
615 > hg init r
617 > cd r
616 > cd r
618 > addwithdate a 1
617 > addwithdate a 1
619 > addwithdate b 2
618 > addwithdate b 2
620 > addwithdate c 3
619 > addwithdate c 3
621 > addwithdate d 4
620 > addwithdate d 4
622 > addwithdate e 5
621 > addwithdate e 5
623 > addwithdate f 6
622 > addwithdate f 6
624 > }
623 > }
625
624
626 $ initrepo
625 $ initrepo
627
626
628 log before edit
627 log before edit
629
628
630 $ hg log
629 $ hg log
631 changeset: 5:178e35e0ce73
630 changeset: 5:178e35e0ce73
632 tag: tip
631 tag: tip
633 user: test
632 user: test
634 date: Thu Jan 01 00:00:06 1970 +0000
633 date: Thu Jan 01 00:00:06 1970 +0000
635 summary: f
634 summary: f
636
635
637 changeset: 4:1ddb6c90f2ee
636 changeset: 4:1ddb6c90f2ee
638 user: test
637 user: test
639 date: Thu Jan 01 00:00:05 1970 +0000
638 date: Thu Jan 01 00:00:05 1970 +0000
640 summary: e
639 summary: e
641
640
642 changeset: 3:532247a8969b
641 changeset: 3:532247a8969b
643 user: test
642 user: test
644 date: Thu Jan 01 00:00:04 1970 +0000
643 date: Thu Jan 01 00:00:04 1970 +0000
645 summary: d
644 summary: d
646
645
647 changeset: 2:ff2c9fa2018b
646 changeset: 2:ff2c9fa2018b
648 user: test
647 user: test
649 date: Thu Jan 01 00:00:03 1970 +0000
648 date: Thu Jan 01 00:00:03 1970 +0000
650 summary: c
649 summary: c
651
650
652 changeset: 1:97d72e5f12c7
651 changeset: 1:97d72e5f12c7
653 user: test
652 user: test
654 date: Thu Jan 01 00:00:02 1970 +0000
653 date: Thu Jan 01 00:00:02 1970 +0000
655 summary: b
654 summary: b
656
655
657 changeset: 0:8580ff50825a
656 changeset: 0:8580ff50825a
658 user: test
657 user: test
659 date: Thu Jan 01 00:00:01 1970 +0000
658 date: Thu Jan 01 00:00:01 1970 +0000
660 summary: a
659 summary: a
661
660
662
661
663 $ hg histedit 1ddb6c90f2ee --commands - 2>&1 --config rewrite.update-timestamp=True <<EOF | fixbundle
662 $ hg histedit 1ddb6c90f2ee --commands - 2>&1 --config rewrite.update-timestamp=True <<EOF | fixbundle
664 > pick 178e35e0ce73 f
663 > pick 178e35e0ce73 f
665 > fold 1ddb6c90f2ee e
664 > fold 1ddb6c90f2ee e
666 > EOF
665 > EOF
667
666
668 log after edit
667 log after edit
669 observe time from f is updated
668 observe time from f is updated
670
669
671 $ hg log
670 $ hg log
672 changeset: 4:f7909b1863a2
671 changeset: 4:f7909b1863a2
673 tag: tip
672 tag: tip
674 user: test
673 user: test
675 date: Thu Jan 01 00:00:01 1970 +0000
674 date: Thu Jan 01 00:00:01 1970 +0000
676 summary: f
675 summary: f
677
676
678 changeset: 3:532247a8969b
677 changeset: 3:532247a8969b
679 user: test
678 user: test
680 date: Thu Jan 01 00:00:04 1970 +0000
679 date: Thu Jan 01 00:00:04 1970 +0000
681 summary: d
680 summary: d
682
681
683 changeset: 2:ff2c9fa2018b
682 changeset: 2:ff2c9fa2018b
684 user: test
683 user: test
685 date: Thu Jan 01 00:00:03 1970 +0000
684 date: Thu Jan 01 00:00:03 1970 +0000
686 summary: c
685 summary: c
687
686
688 changeset: 1:97d72e5f12c7
687 changeset: 1:97d72e5f12c7
689 user: test
688 user: test
690 date: Thu Jan 01 00:00:02 1970 +0000
689 date: Thu Jan 01 00:00:02 1970 +0000
691 summary: b
690 summary: b
692
691
693 changeset: 0:8580ff50825a
692 changeset: 0:8580ff50825a
694 user: test
693 user: test
695 date: Thu Jan 01 00:00:01 1970 +0000
694 date: Thu Jan 01 00:00:01 1970 +0000
696 summary: a
695 summary: a
697
696
698 post-fold manifest
697 post-fold manifest
699 $ hg manifest
698 $ hg manifest
700 a
699 a
701 b
700 b
702 c
701 c
703 d
702 d
704 e
703 e
705 f
704 f
706
705
707 $ cd ..
706 $ cd ..
@@ -1,306 +1,303 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 $1
10 > hg init $1
11 > cd $1
11 > cd $1
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$x$x$x > $x
13 > echo $x$x$x$x$x > $x
14 > hg add $x
14 > hg add $x
15 > done
15 > done
16 > hg ci -m 'Initial commit'
16 > hg ci -m 'Initial commit'
17 > for x in a b c d e f ; do
17 > for x in a b c d e f ; do
18 > echo $x > $x
18 > echo $x > $x
19 > hg ci -m $x
19 > hg ci -m $x
20 > done
20 > done
21 > echo 'I can haz no commute' > e
21 > echo 'I can haz no commute' > e
22 > hg ci -m 'does not commute with e'
22 > hg ci -m 'does not commute with e'
23 > cd ..
23 > cd ..
24 > }
24 > }
25
25
26 $ initrepo r1
26 $ initrepo r1
27 $ cd r1
27 $ cd r1
28
28
29 Initial generation of the command files
29 Initial generation of the command files
30
30
31 $ EDITED="$TESTTMP/editedhistory"
31 $ EDITED="$TESTTMP/editedhistory"
32 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 3 >> $EDITED
32 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 3 >> $EDITED
33 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 4 >> $EDITED
33 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 4 >> $EDITED
34 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 7 >> $EDITED
34 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 7 >> $EDITED
35 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 5 >> $EDITED
35 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 5 >> $EDITED
36 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 6 >> $EDITED
36 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 6 >> $EDITED
37 $ cat $EDITED
37 $ cat $EDITED
38 pick 65a9a84f33fd 3 c
38 pick 65a9a84f33fd 3 c
39 pick 00f1c5383965 4 d
39 pick 00f1c5383965 4 d
40 pick 39522b764e3d 7 does not commute with e
40 pick 39522b764e3d 7 does not commute with e
41 pick 7b4e2f4b7bcd 5 e
41 pick 7b4e2f4b7bcd 5 e
42 pick 500cac37a696 6 f
42 pick 500cac37a696 6 f
43
43
44 log before edit
44 log before edit
45 $ hg log --graph
45 $ hg log --graph
46 @ changeset: 7:39522b764e3d
46 @ changeset: 7:39522b764e3d
47 | tag: tip
47 | tag: tip
48 | user: test
48 | user: test
49 | date: Thu Jan 01 00:00:00 1970 +0000
49 | date: Thu Jan 01 00:00:00 1970 +0000
50 | summary: does not commute with e
50 | summary: does not commute with e
51 |
51 |
52 o changeset: 6:500cac37a696
52 o changeset: 6:500cac37a696
53 | user: test
53 | user: test
54 | date: Thu Jan 01 00:00:00 1970 +0000
54 | date: Thu Jan 01 00:00:00 1970 +0000
55 | summary: f
55 | summary: f
56 |
56 |
57 o changeset: 5:7b4e2f4b7bcd
57 o changeset: 5:7b4e2f4b7bcd
58 | user: test
58 | user: test
59 | date: Thu Jan 01 00:00:00 1970 +0000
59 | date: Thu Jan 01 00:00:00 1970 +0000
60 | summary: e
60 | summary: e
61 |
61 |
62 o changeset: 4:00f1c5383965
62 o changeset: 4:00f1c5383965
63 | user: test
63 | user: test
64 | date: Thu Jan 01 00:00:00 1970 +0000
64 | date: Thu Jan 01 00:00:00 1970 +0000
65 | summary: d
65 | summary: d
66 |
66 |
67 o changeset: 3:65a9a84f33fd
67 o changeset: 3:65a9a84f33fd
68 | user: test
68 | user: test
69 | date: Thu Jan 01 00:00:00 1970 +0000
69 | date: Thu Jan 01 00:00:00 1970 +0000
70 | summary: c
70 | summary: c
71 |
71 |
72 o changeset: 2:da6535b52e45
72 o changeset: 2:da6535b52e45
73 | user: test
73 | user: test
74 | date: Thu Jan 01 00:00:00 1970 +0000
74 | date: Thu Jan 01 00:00:00 1970 +0000
75 | summary: b
75 | summary: b
76 |
76 |
77 o changeset: 1:c1f09da44841
77 o changeset: 1:c1f09da44841
78 | user: test
78 | user: test
79 | date: Thu Jan 01 00:00:00 1970 +0000
79 | date: Thu Jan 01 00:00:00 1970 +0000
80 | summary: a
80 | summary: a
81 |
81 |
82 o changeset: 0:1715188a53c7
82 o changeset: 0:1715188a53c7
83 user: test
83 user: test
84 date: Thu Jan 01 00:00:00 1970 +0000
84 date: Thu Jan 01 00:00:00 1970 +0000
85 summary: Initial commit
85 summary: Initial commit
86
86
87
87
88 edit the history
88 edit the history
89 $ hg histedit 3 --commands $EDITED 2>&1 | fixbundle
89 $ hg histedit 3 --commands $EDITED 2>&1 | fixbundle
90 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
91 merging e
90 merging e
92 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
91 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
93 Fix up the change (pick 39522b764e3d)
92 Fix up the change (pick 39522b764e3d)
94 (hg histedit --continue to resume)
93 (hg histedit --continue to resume)
95
94
96 abort the edit
95 abort the edit
97 $ hg histedit --abort 2>&1 | fixbundle
96 $ hg histedit --abort 2>&1 | fixbundle
98 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
97 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
99
98
100
99
101 second edit set
100 second edit set
102
101
103 $ hg log --graph
102 $ hg log --graph
104 @ changeset: 7:39522b764e3d
103 @ changeset: 7:39522b764e3d
105 | tag: tip
104 | tag: tip
106 | user: test
105 | user: test
107 | date: Thu Jan 01 00:00:00 1970 +0000
106 | date: Thu Jan 01 00:00:00 1970 +0000
108 | summary: does not commute with e
107 | summary: does not commute with e
109 |
108 |
110 o changeset: 6:500cac37a696
109 o changeset: 6:500cac37a696
111 | user: test
110 | user: test
112 | date: Thu Jan 01 00:00:00 1970 +0000
111 | date: Thu Jan 01 00:00:00 1970 +0000
113 | summary: f
112 | summary: f
114 |
113 |
115 o changeset: 5:7b4e2f4b7bcd
114 o changeset: 5:7b4e2f4b7bcd
116 | user: test
115 | user: test
117 | date: Thu Jan 01 00:00:00 1970 +0000
116 | date: Thu Jan 01 00:00:00 1970 +0000
118 | summary: e
117 | summary: e
119 |
118 |
120 o changeset: 4:00f1c5383965
119 o changeset: 4:00f1c5383965
121 | user: test
120 | user: test
122 | date: Thu Jan 01 00:00:00 1970 +0000
121 | date: Thu Jan 01 00:00:00 1970 +0000
123 | summary: d
122 | summary: d
124 |
123 |
125 o changeset: 3:65a9a84f33fd
124 o changeset: 3:65a9a84f33fd
126 | user: test
125 | user: test
127 | date: Thu Jan 01 00:00:00 1970 +0000
126 | date: Thu Jan 01 00:00:00 1970 +0000
128 | summary: c
127 | summary: c
129 |
128 |
130 o changeset: 2:da6535b52e45
129 o changeset: 2:da6535b52e45
131 | user: test
130 | user: test
132 | date: Thu Jan 01 00:00:00 1970 +0000
131 | date: Thu Jan 01 00:00:00 1970 +0000
133 | summary: b
132 | summary: b
134 |
133 |
135 o changeset: 1:c1f09da44841
134 o changeset: 1:c1f09da44841
136 | user: test
135 | user: test
137 | date: Thu Jan 01 00:00:00 1970 +0000
136 | date: Thu Jan 01 00:00:00 1970 +0000
138 | summary: a
137 | summary: a
139 |
138 |
140 o changeset: 0:1715188a53c7
139 o changeset: 0:1715188a53c7
141 user: test
140 user: test
142 date: Thu Jan 01 00:00:00 1970 +0000
141 date: Thu Jan 01 00:00:00 1970 +0000
143 summary: Initial commit
142 summary: Initial commit
144
143
145
144
146 edit the history
145 edit the history
147 $ hg histedit 3 --commands $EDITED 2>&1 | fixbundle
146 $ hg histedit 3 --commands $EDITED 2>&1 | fixbundle
148 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
149 merging e
147 merging e
150 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
148 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
151 Fix up the change (pick 39522b764e3d)
149 Fix up the change (pick 39522b764e3d)
152 (hg histedit --continue to resume)
150 (hg histedit --continue to resume)
153
151
154 fix up
152 fix up
155 $ echo 'I can haz no commute' > e
153 $ echo 'I can haz no commute' > e
156 $ hg resolve --mark e
154 $ hg resolve --mark e
157 (no more unresolved files)
155 (no more unresolved files)
158 continue: hg histedit --continue
156 continue: hg histedit --continue
159 $ hg histedit --continue 2>&1 | fixbundle
157 $ hg histedit --continue 2>&1 | fixbundle
160 merging e
158 merging e
161 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
159 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
162 Fix up the change (pick 7b4e2f4b7bcd)
160 Fix up the change (pick 7b4e2f4b7bcd)
163 (hg histedit --continue to resume)
161 (hg histedit --continue to resume)
164 We forcibly enable curses here so we can verify that continuing works
162 We forcibly enable curses here so we can verify that continuing works
165 with curses enabled.
163 with curses enabled.
166 $ hg histedit --continue --config ui.interactive=true \
164 $ hg histedit --continue --config ui.interactive=true \
167 > --config ui.interface=curses 2>&1 | fixbundle
165 > --config ui.interface=curses 2>&1 | fixbundle
168 abort: unresolved merge conflicts (see 'hg help resolve')
166 abort: unresolved merge conflicts (see 'hg help resolve')
169
167
170 This failure is caused by 7b4e2f4b7bcd "e" not rebasing the non commutative
168 This failure is caused by 7b4e2f4b7bcd "e" not rebasing the non commutative
171 former children.
169 former children.
172
170
173 just continue this time
171 just continue this time
174 $ hg revert -r 'p1()' e
172 $ hg revert -r 'p1()' e
175 make sure the to-be-empty commit doesn't screw up the state (issue5545)
173 make sure the to-be-empty commit doesn't screw up the state (issue5545)
176 $ hg histedit --continue 2>&1 | fixbundle
174 $ hg histedit --continue 2>&1 | fixbundle
177 abort: unresolved merge conflicts (see 'hg help resolve')
175 abort: unresolved merge conflicts (see 'hg help resolve')
178 $ hg resolve --mark e
176 $ hg resolve --mark e
179 (no more unresolved files)
177 (no more unresolved files)
180 continue: hg histedit --continue
178 continue: hg histedit --continue
181 $ hg histedit --continue 2>&1 | fixbundle
179 $ hg histedit --continue 2>&1 | fixbundle
182 7b4e2f4b7bcd: skipping changeset (no changes)
180 7b4e2f4b7bcd: skipping changeset (no changes)
183
181
184 log after edit
182 log after edit
185 $ hg log --graph
183 $ hg log --graph
186 @ changeset: 6:7efe1373e4bc
184 @ changeset: 6:7efe1373e4bc
187 | tag: tip
185 | tag: tip
188 | user: test
186 | user: test
189 | date: Thu Jan 01 00:00:00 1970 +0000
187 | date: Thu Jan 01 00:00:00 1970 +0000
190 | summary: f
188 | summary: f
191 |
189 |
192 o changeset: 5:e334d87a1e55
190 o changeset: 5:e334d87a1e55
193 | user: test
191 | user: test
194 | date: Thu Jan 01 00:00:00 1970 +0000
192 | date: Thu Jan 01 00:00:00 1970 +0000
195 | summary: does not commute with e
193 | summary: does not commute with e
196 |
194 |
197 o changeset: 4:00f1c5383965
195 o changeset: 4:00f1c5383965
198 | user: test
196 | user: test
199 | date: Thu Jan 01 00:00:00 1970 +0000
197 | date: Thu Jan 01 00:00:00 1970 +0000
200 | summary: d
198 | summary: d
201 |
199 |
202 o changeset: 3:65a9a84f33fd
200 o changeset: 3:65a9a84f33fd
203 | user: test
201 | user: test
204 | date: Thu Jan 01 00:00:00 1970 +0000
202 | date: Thu Jan 01 00:00:00 1970 +0000
205 | summary: c
203 | summary: c
206 |
204 |
207 o changeset: 2:da6535b52e45
205 o changeset: 2:da6535b52e45
208 | user: test
206 | user: test
209 | date: Thu Jan 01 00:00:00 1970 +0000
207 | date: Thu Jan 01 00:00:00 1970 +0000
210 | summary: b
208 | summary: b
211 |
209 |
212 o changeset: 1:c1f09da44841
210 o changeset: 1:c1f09da44841
213 | user: test
211 | user: test
214 | date: Thu Jan 01 00:00:00 1970 +0000
212 | date: Thu Jan 01 00:00:00 1970 +0000
215 | summary: a
213 | summary: a
216 |
214 |
217 o changeset: 0:1715188a53c7
215 o changeset: 0:1715188a53c7
218 user: test
216 user: test
219 date: Thu Jan 01 00:00:00 1970 +0000
217 date: Thu Jan 01 00:00:00 1970 +0000
220 summary: Initial commit
218 summary: Initial commit
221
219
222
220
223 start over
221 start over
224
222
225 $ cd ..
223 $ cd ..
226
224
227 $ initrepo r2
225 $ initrepo r2
228 $ cd r2
226 $ cd r2
229 $ rm $EDITED
227 $ rm $EDITED
230 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 3 >> $EDITED
228 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 3 >> $EDITED
231 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 4 >> $EDITED
229 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 4 >> $EDITED
232 $ hg log --template 'mess {node|short} {rev} {desc}\n' -r 7 >> $EDITED
230 $ hg log --template 'mess {node|short} {rev} {desc}\n' -r 7 >> $EDITED
233 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 5 >> $EDITED
231 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 5 >> $EDITED
234 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 6 >> $EDITED
232 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 6 >> $EDITED
235 $ cat $EDITED
233 $ cat $EDITED
236 pick 65a9a84f33fd 3 c
234 pick 65a9a84f33fd 3 c
237 pick 00f1c5383965 4 d
235 pick 00f1c5383965 4 d
238 mess 39522b764e3d 7 does not commute with e
236 mess 39522b764e3d 7 does not commute with e
239 pick 7b4e2f4b7bcd 5 e
237 pick 7b4e2f4b7bcd 5 e
240 pick 500cac37a696 6 f
238 pick 500cac37a696 6 f
241
239
242 edit the history, this time with a fold action
240 edit the history, this time with a fold action
243 $ hg histedit 3 --commands $EDITED 2>&1 | fixbundle
241 $ hg histedit 3 --commands $EDITED 2>&1 | fixbundle
244 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
245 merging e
242 merging e
246 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
243 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
247 Fix up the change (mess 39522b764e3d)
244 Fix up the change (mess 39522b764e3d)
248 (hg histedit --continue to resume)
245 (hg histedit --continue to resume)
249
246
250 $ echo 'I can haz no commute' > e
247 $ echo 'I can haz no commute' > e
251 $ hg resolve --mark e
248 $ hg resolve --mark e
252 (no more unresolved files)
249 (no more unresolved files)
253 continue: hg histedit --continue
250 continue: hg histedit --continue
254 $ hg histedit --continue 2>&1 | fixbundle
251 $ hg histedit --continue 2>&1 | fixbundle
255 merging e
252 merging e
256 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
253 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
257 Fix up the change (pick 7b4e2f4b7bcd)
254 Fix up the change (pick 7b4e2f4b7bcd)
258 (hg histedit --continue to resume)
255 (hg histedit --continue to resume)
259 second edit also fails, but just continue
256 second edit also fails, but just continue
260 $ hg revert -r 'p1()' e
257 $ hg revert -r 'p1()' e
261 $ hg resolve --mark e
258 $ hg resolve --mark e
262 (no more unresolved files)
259 (no more unresolved files)
263 continue: hg histedit --continue
260 continue: hg histedit --continue
264 $ hg histedit --continue 2>&1 | fixbundle
261 $ hg histedit --continue 2>&1 | fixbundle
265 7b4e2f4b7bcd: skipping changeset (no changes)
262 7b4e2f4b7bcd: skipping changeset (no changes)
266
263
267 post message fix
264 post message fix
268 $ hg log --graph
265 $ hg log --graph
269 @ changeset: 6:7efe1373e4bc
266 @ changeset: 6:7efe1373e4bc
270 | tag: tip
267 | tag: tip
271 | user: test
268 | user: test
272 | date: Thu Jan 01 00:00:00 1970 +0000
269 | date: Thu Jan 01 00:00:00 1970 +0000
273 | summary: f
270 | summary: f
274 |
271 |
275 o changeset: 5:e334d87a1e55
272 o changeset: 5:e334d87a1e55
276 | user: test
273 | user: test
277 | date: Thu Jan 01 00:00:00 1970 +0000
274 | date: Thu Jan 01 00:00:00 1970 +0000
278 | summary: does not commute with e
275 | summary: does not commute with e
279 |
276 |
280 o changeset: 4:00f1c5383965
277 o changeset: 4:00f1c5383965
281 | user: test
278 | user: test
282 | date: Thu Jan 01 00:00:00 1970 +0000
279 | date: Thu Jan 01 00:00:00 1970 +0000
283 | summary: d
280 | summary: d
284 |
281 |
285 o changeset: 3:65a9a84f33fd
282 o changeset: 3:65a9a84f33fd
286 | user: test
283 | user: test
287 | date: Thu Jan 01 00:00:00 1970 +0000
284 | date: Thu Jan 01 00:00:00 1970 +0000
288 | summary: c
285 | summary: c
289 |
286 |
290 o changeset: 2:da6535b52e45
287 o changeset: 2:da6535b52e45
291 | user: test
288 | user: test
292 | date: Thu Jan 01 00:00:00 1970 +0000
289 | date: Thu Jan 01 00:00:00 1970 +0000
293 | summary: b
290 | summary: b
294 |
291 |
295 o changeset: 1:c1f09da44841
292 o changeset: 1:c1f09da44841
296 | user: test
293 | user: test
297 | date: Thu Jan 01 00:00:00 1970 +0000
294 | date: Thu Jan 01 00:00:00 1970 +0000
298 | summary: a
295 | summary: a
299 |
296 |
300 o changeset: 0:1715188a53c7
297 o changeset: 0:1715188a53c7
301 user: test
298 user: test
302 date: Thu Jan 01 00:00:00 1970 +0000
299 date: Thu Jan 01 00:00:00 1970 +0000
303 summary: Initial commit
300 summary: Initial commit
304
301
305
302
306 $ cd ..
303 $ cd ..
@@ -1,579 +1,576 b''
1 $ . "$TESTDIR/histedit-helpers.sh"
1 $ . "$TESTDIR/histedit-helpers.sh"
2
2
3 Enable obsolete
3 Enable obsolete
4
4
5 $ cat >> $HGRCPATH << EOF
5 $ cat >> $HGRCPATH << EOF
6 > [ui]
6 > [ui]
7 > logtemplate= {rev}:{node|short} {desc|firstline}
7 > logtemplate= {rev}:{node|short} {desc|firstline}
8 > [phases]
8 > [phases]
9 > publish=False
9 > publish=False
10 > [experimental]
10 > [experimental]
11 > evolution.createmarkers=True
11 > evolution.createmarkers=True
12 > evolution.allowunstable=True
12 > evolution.allowunstable=True
13 > [extensions]
13 > [extensions]
14 > histedit=
14 > histedit=
15 > rebase=
15 > rebase=
16 > EOF
16 > EOF
17
17
18 Test that histedit learns about obsolescence not stored in histedit state
18 Test that histedit learns about obsolescence not stored in histedit state
19 $ hg init boo
19 $ hg init boo
20 $ cd boo
20 $ cd boo
21 $ echo a > a
21 $ echo a > a
22 $ hg ci -Am a
22 $ hg ci -Am a
23 adding a
23 adding a
24 $ echo a > b
24 $ echo a > b
25 $ echo a > c
25 $ echo a > c
26 $ echo a > c
26 $ echo a > c
27 $ hg ci -Am b
27 $ hg ci -Am b
28 adding b
28 adding b
29 adding c
29 adding c
30 $ echo a > d
30 $ echo a > d
31 $ hg ci -Am c
31 $ hg ci -Am c
32 adding d
32 adding d
33 $ echo "pick `hg log -r 0 -T '{node|short}'`" > plan
33 $ echo "pick `hg log -r 0 -T '{node|short}'`" > plan
34 $ echo "pick `hg log -r 2 -T '{node|short}'`" >> plan
34 $ echo "pick `hg log -r 2 -T '{node|short}'`" >> plan
35 $ echo "edit `hg log -r 1 -T '{node|short}'`" >> plan
35 $ echo "edit `hg log -r 1 -T '{node|short}'`" >> plan
36 $ hg histedit -r 'all()' --commands plan
36 $ hg histedit -r 'all()' --commands plan
37 Editing (1b2d564fad96), you may commit or record as needed now.
37 Editing (1b2d564fad96), you may commit or record as needed now.
38 (hg histedit --continue to resume)
38 (hg histedit --continue to resume)
39 [1]
39 [1]
40 $ hg st
40 $ hg st
41 A b
41 A b
42 A c
42 A c
43 ? plan
43 ? plan
44 $ hg commit --amend b
44 $ hg commit --amend b
45 $ hg histedit --continue
45 $ hg histedit --continue
46 $ hg log -G
46 $ hg log -G
47 @ 5:46abc7c4d873 b
47 @ 5:46abc7c4d873 b
48 |
48 |
49 o 4:49d44ab2be1b c
49 o 4:49d44ab2be1b c
50 |
50 |
51 o 0:cb9a9f314b8b a
51 o 0:cb9a9f314b8b a
52
52
53 $ hg debugobsolete
53 $ hg debugobsolete
54 e72d22b19f8ecf4150ab4f91d0973fd9955d3ddf 49d44ab2be1b67a79127568a67c9c99430633b48 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
54 e72d22b19f8ecf4150ab4f91d0973fd9955d3ddf 49d44ab2be1b67a79127568a67c9c99430633b48 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
55 1b2d564fad96311b45362f17c2aa855150efb35f 46abc7c4d8738e8563e577f7889e1b6db3da4199 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '12', 'operation': 'histedit', 'user': 'test'}
55 1b2d564fad96311b45362f17c2aa855150efb35f 46abc7c4d8738e8563e577f7889e1b6db3da4199 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '12', 'operation': 'histedit', 'user': 'test'}
56 114f4176969ef342759a8a57e6bccefc4234829b 49d44ab2be1b67a79127568a67c9c99430633b48 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '12', 'operation': 'histedit', 'user': 'test'}
56 114f4176969ef342759a8a57e6bccefc4234829b 49d44ab2be1b67a79127568a67c9c99430633b48 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '12', 'operation': 'histedit', 'user': 'test'}
57
57
58 With some node gone missing during the edit.
58 With some node gone missing during the edit.
59
59
60 $ echo "pick `hg log -r 0 -T '{node|short}'`" > plan
60 $ echo "pick `hg log -r 0 -T '{node|short}'`" > plan
61 $ echo "pick `hg log -r 5 -T '{node|short}'`" >> plan
61 $ echo "pick `hg log -r 5 -T '{node|short}'`" >> plan
62 $ echo "edit `hg log -r 4 -T '{node|short}'`" >> plan
62 $ echo "edit `hg log -r 4 -T '{node|short}'`" >> plan
63 $ hg histedit -r 'all()' --commands plan
63 $ hg histedit -r 'all()' --commands plan
64 Editing (49d44ab2be1b), you may commit or record as needed now.
64 Editing (49d44ab2be1b), you may commit or record as needed now.
65 (hg histedit --continue to resume)
65 (hg histedit --continue to resume)
66 [1]
66 [1]
67 $ hg st
67 $ hg st
68 A b
68 A b
69 A d
69 A d
70 ? plan
70 ? plan
71 $ hg commit --amend -X . -m XXXXXX
71 $ hg commit --amend -X . -m XXXXXX
72 $ hg commit --amend -X . -m b2
72 $ hg commit --amend -X . -m b2
73 $ hg --hidden --config extensions.strip= strip 'desc(XXXXXX)' --no-backup
73 $ hg --hidden --config extensions.strip= strip 'desc(XXXXXX)' --no-backup
74 $ hg histedit --continue
74 $ hg histedit --continue
75 $ hg log -G
75 $ hg log -G
76 @ 8:273c1f3b8626 c
76 @ 8:273c1f3b8626 c
77 |
77 |
78 o 7:aba7da937030 b2
78 o 7:aba7da937030 b2
79 |
79 |
80 o 0:cb9a9f314b8b a
80 o 0:cb9a9f314b8b a
81
81
82 $ hg debugobsolete
82 $ hg debugobsolete
83 e72d22b19f8ecf4150ab4f91d0973fd9955d3ddf 49d44ab2be1b67a79127568a67c9c99430633b48 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
83 e72d22b19f8ecf4150ab4f91d0973fd9955d3ddf 49d44ab2be1b67a79127568a67c9c99430633b48 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
84 1b2d564fad96311b45362f17c2aa855150efb35f 46abc7c4d8738e8563e577f7889e1b6db3da4199 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '12', 'operation': 'histedit', 'user': 'test'}
84 1b2d564fad96311b45362f17c2aa855150efb35f 46abc7c4d8738e8563e577f7889e1b6db3da4199 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '12', 'operation': 'histedit', 'user': 'test'}
85 114f4176969ef342759a8a57e6bccefc4234829b 49d44ab2be1b67a79127568a67c9c99430633b48 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '12', 'operation': 'histedit', 'user': 'test'}
85 114f4176969ef342759a8a57e6bccefc4234829b 49d44ab2be1b67a79127568a67c9c99430633b48 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '12', 'operation': 'histedit', 'user': 'test'}
86 76f72745eac0643d16530e56e2f86e36e40631f1 2ca853e48edbd6453a0674dc0fe28a0974c51b9c 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'test'}
86 76f72745eac0643d16530e56e2f86e36e40631f1 2ca853e48edbd6453a0674dc0fe28a0974c51b9c 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'test'}
87 2ca853e48edbd6453a0674dc0fe28a0974c51b9c aba7da93703075eec9fb1dbaf143ff2bc1c49d46 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'test'}
87 2ca853e48edbd6453a0674dc0fe28a0974c51b9c aba7da93703075eec9fb1dbaf143ff2bc1c49d46 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'test'}
88 49d44ab2be1b67a79127568a67c9c99430633b48 273c1f3b86267ed3ec684bb13af1fa4d6ba56e02 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'histedit', 'user': 'test'}
88 49d44ab2be1b67a79127568a67c9c99430633b48 273c1f3b86267ed3ec684bb13af1fa4d6ba56e02 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'histedit', 'user': 'test'}
89 46abc7c4d8738e8563e577f7889e1b6db3da4199 aba7da93703075eec9fb1dbaf143ff2bc1c49d46 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '5', 'operation': 'histedit', 'user': 'test'}
89 46abc7c4d8738e8563e577f7889e1b6db3da4199 aba7da93703075eec9fb1dbaf143ff2bc1c49d46 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '5', 'operation': 'histedit', 'user': 'test'}
90 $ cd ..
90 $ cd ..
91
91
92 Base setup for the rest of the testing
92 Base setup for the rest of the testing
93 ======================================
93 ======================================
94
94
95 $ hg init base
95 $ hg init base
96 $ cd base
96 $ cd base
97
97
98 $ for x in a b c d e f ; do
98 $ for x in a b c d e f ; do
99 > echo $x > $x
99 > echo $x > $x
100 > hg add $x
100 > hg add $x
101 > hg ci -m $x
101 > hg ci -m $x
102 > done
102 > done
103
103
104 $ hg log --graph
104 $ hg log --graph
105 @ 5:652413bf663e f
105 @ 5:652413bf663e f
106 |
106 |
107 o 4:e860deea161a e
107 o 4:e860deea161a e
108 |
108 |
109 o 3:055a42cdd887 d
109 o 3:055a42cdd887 d
110 |
110 |
111 o 2:177f92b77385 c
111 o 2:177f92b77385 c
112 |
112 |
113 o 1:d2ae7f538514 b
113 o 1:d2ae7f538514 b
114 |
114 |
115 o 0:cb9a9f314b8b a
115 o 0:cb9a9f314b8b a
116
116
117
117
118 $ HGEDITOR=cat hg histedit 1
118 $ HGEDITOR=cat hg histedit 1
119 pick d2ae7f538514 1 b
119 pick d2ae7f538514 1 b
120 pick 177f92b77385 2 c
120 pick 177f92b77385 2 c
121 pick 055a42cdd887 3 d
121 pick 055a42cdd887 3 d
122 pick e860deea161a 4 e
122 pick e860deea161a 4 e
123 pick 652413bf663e 5 f
123 pick 652413bf663e 5 f
124
124
125 # Edit history between d2ae7f538514 and 652413bf663e
125 # Edit history between d2ae7f538514 and 652413bf663e
126 #
126 #
127 # Commits are listed from least to most recent
127 # Commits are listed from least to most recent
128 #
128 #
129 # You can reorder changesets by reordering the lines
129 # You can reorder changesets by reordering the lines
130 #
130 #
131 # Commands:
131 # Commands:
132 #
132 #
133 # e, edit = use commit, but stop for amending
133 # e, edit = use commit, but stop for amending
134 # m, mess = edit commit message without changing commit content
134 # m, mess = edit commit message without changing commit content
135 # p, pick = use commit
135 # p, pick = use commit
136 # b, base = checkout changeset and apply further changesets from there
136 # b, base = checkout changeset and apply further changesets from there
137 # d, drop = remove commit from history
137 # d, drop = remove commit from history
138 # f, fold = use commit, but combine it with the one above
138 # f, fold = use commit, but combine it with the one above
139 # r, roll = like fold, but discard this commit's description and date
139 # r, roll = like fold, but discard this commit's description and date
140 #
140 #
141 $ hg histedit 1 --commands - --verbose <<EOF | grep histedit
141 $ hg histedit 1 --commands - --verbose <<EOF | grep histedit
142 > pick 177f92b77385 2 c
142 > pick 177f92b77385 2 c
143 > drop d2ae7f538514 1 b
143 > drop d2ae7f538514 1 b
144 > pick 055a42cdd887 3 d
144 > pick 055a42cdd887 3 d
145 > fold e860deea161a 4 e
145 > fold e860deea161a 4 e
146 > pick 652413bf663e 5 f
146 > pick 652413bf663e 5 f
147 > EOF
147 > EOF
148 [1]
148 [1]
149 $ hg log --graph --hidden
149 $ hg log --graph --hidden
150 @ 10:cacdfd884a93 f
150 @ 10:cacdfd884a93 f
151 |
151 |
152 o 9:59d9f330561f d
152 o 9:59d9f330561f d
153 |
153 |
154 | x 8:b558abc46d09 fold-temp-revision e860deea161a
154 | x 8:b558abc46d09 fold-temp-revision e860deea161a
155 | |
155 | |
156 | x 7:96e494a2d553 d
156 | x 7:96e494a2d553 d
157 |/
157 |/
158 o 6:b346ab9a313d c
158 o 6:b346ab9a313d c
159 |
159 |
160 | x 5:652413bf663e f
160 | x 5:652413bf663e f
161 | |
161 | |
162 | x 4:e860deea161a e
162 | x 4:e860deea161a e
163 | |
163 | |
164 | x 3:055a42cdd887 d
164 | x 3:055a42cdd887 d
165 | |
165 | |
166 | x 2:177f92b77385 c
166 | x 2:177f92b77385 c
167 | |
167 | |
168 | x 1:d2ae7f538514 b
168 | x 1:d2ae7f538514 b
169 |/
169 |/
170 o 0:cb9a9f314b8b a
170 o 0:cb9a9f314b8b a
171
171
172 $ hg debugobsolete
172 $ hg debugobsolete
173 d2ae7f538514cd87c17547b0de4cea71fe1af9fb 0 {cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'histedit', 'user': 'test'}
173 d2ae7f538514cd87c17547b0de4cea71fe1af9fb 0 {cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'histedit', 'user': 'test'}
174 177f92b773850b59254aa5e923436f921b55483b b346ab9a313db8537ecf96fca3ca3ca984ef3bd7 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'histedit', 'user': 'test'}
174 177f92b773850b59254aa5e923436f921b55483b b346ab9a313db8537ecf96fca3ca3ca984ef3bd7 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'histedit', 'user': 'test'}
175 055a42cdd88768532f9cf79daa407fc8d138de9b 59d9f330561fd6c88b1a6b32f0e45034d88db784 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'operation': 'histedit', 'user': 'test'}
175 055a42cdd88768532f9cf79daa407fc8d138de9b 59d9f330561fd6c88b1a6b32f0e45034d88db784 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'operation': 'histedit', 'user': 'test'}
176 e860deea161a2f77de56603b340ebbb4536308ae 59d9f330561fd6c88b1a6b32f0e45034d88db784 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'operation': 'histedit', 'user': 'test'}
176 e860deea161a2f77de56603b340ebbb4536308ae 59d9f330561fd6c88b1a6b32f0e45034d88db784 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'operation': 'histedit', 'user': 'test'}
177 652413bf663ef2a641cab26574e46d5f5a64a55a cacdfd884a9321ec4e1de275ef3949fa953a1f83 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'histedit', 'user': 'test'}
177 652413bf663ef2a641cab26574e46d5f5a64a55a cacdfd884a9321ec4e1de275ef3949fa953a1f83 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'histedit', 'user': 'test'}
178 96e494a2d553dd05902ba1cee1d94d4cb7b8faed 0 {b346ab9a313db8537ecf96fca3ca3ca984ef3bd7} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'histedit', 'user': 'test'}
178 96e494a2d553dd05902ba1cee1d94d4cb7b8faed 0 {b346ab9a313db8537ecf96fca3ca3ca984ef3bd7} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'histedit', 'user': 'test'}
179 b558abc46d09c30f57ac31e85a8a3d64d2e906e4 0 {96e494a2d553dd05902ba1cee1d94d4cb7b8faed} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'histedit', 'user': 'test'}
179 b558abc46d09c30f57ac31e85a8a3d64d2e906e4 0 {96e494a2d553dd05902ba1cee1d94d4cb7b8faed} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'histedit', 'user': 'test'}
180
180
181
181
182 Ensure hidden revision does not prevent histedit
182 Ensure hidden revision does not prevent histedit
183 -------------------------------------------------
183 -------------------------------------------------
184
184
185 create an hidden revision
185 create an hidden revision
186
186
187 $ hg histedit 6 --commands - << EOF
187 $ hg histedit 6 --commands - << EOF
188 > pick b346ab9a313d 6 c
188 > pick b346ab9a313d 6 c
189 > drop 59d9f330561f 7 d
189 > drop 59d9f330561f 7 d
190 > pick cacdfd884a93 8 f
190 > pick cacdfd884a93 8 f
191 > EOF
191 > EOF
192 $ hg log --graph
192 $ hg log --graph
193 @ 11:c13eb81022ca f
193 @ 11:c13eb81022ca f
194 |
194 |
195 o 6:b346ab9a313d c
195 o 6:b346ab9a313d c
196 |
196 |
197 o 0:cb9a9f314b8b a
197 o 0:cb9a9f314b8b a
198
198
199 check hidden revision are ignored (6 have hidden children 7 and 8)
199 check hidden revision are ignored (6 have hidden children 7 and 8)
200
200
201 $ hg histedit 6 --commands - << EOF
201 $ hg histedit 6 --commands - << EOF
202 > pick b346ab9a313d 6 c
202 > pick b346ab9a313d 6 c
203 > pick c13eb81022ca 8 f
203 > pick c13eb81022ca 8 f
204 > EOF
204 > EOF
205
205
206
206
207
207
208 Test that rewriting leaving instability behind is allowed
208 Test that rewriting leaving instability behind is allowed
209 ---------------------------------------------------------------------
209 ---------------------------------------------------------------------
210
210
211 $ hg up '.^'
211 $ hg up '.^'
212 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
212 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
213 $ hg log -r 'children(.)'
213 $ hg log -r 'children(.)'
214 11:c13eb81022ca f (no-eol)
214 11:c13eb81022ca f (no-eol)
215 $ hg histedit -r '.' --commands - <<EOF
215 $ hg histedit -r '.' --commands - <<EOF
216 > edit b346ab9a313d 6 c
216 > edit b346ab9a313d 6 c
217 > EOF
217 > EOF
218 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
218 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
219 adding c
220 Editing (b346ab9a313d), you may commit or record as needed now.
219 Editing (b346ab9a313d), you may commit or record as needed now.
221 (hg histedit --continue to resume)
220 (hg histedit --continue to resume)
222 [1]
221 [1]
223 $ echo c >> c
222 $ echo c >> c
224 $ hg histedit --continue
223 $ hg histedit --continue
225 1 new orphan changesets
224 1 new orphan changesets
226
225
227 $ hg log -r 'orphan()'
226 $ hg log -r 'orphan()'
228 11:c13eb81022ca f (no-eol)
227 11:c13eb81022ca f (no-eol)
229
228
230 stabilise
229 stabilise
231
230
232 $ hg rebase -r 'orphan()' -d .
231 $ hg rebase -r 'orphan()' -d .
233 rebasing 11:c13eb81022ca "f"
232 rebasing 11:c13eb81022ca "f"
234 $ hg up tip -q
233 $ hg up tip -q
235
234
236 Test dropping of changeset on the top of the stack
235 Test dropping of changeset on the top of the stack
237 -------------------------------------------------------
236 -------------------------------------------------------
238
237
239 Nothing is rewritten below, the working directory parent must be change for the
238 Nothing is rewritten below, the working directory parent must be change for the
240 dropped changeset to be hidden.
239 dropped changeset to be hidden.
241
240
242 $ cd ..
241 $ cd ..
243 $ hg clone base droplast
242 $ hg clone base droplast
244 updating to branch default
243 updating to branch default
245 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
244 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
246 $ cd droplast
245 $ cd droplast
247 $ hg histedit -r '40db8afa467b' --commands - << EOF
246 $ hg histedit -r '40db8afa467b' --commands - << EOF
248 > pick 40db8afa467b 10 c
247 > pick 40db8afa467b 10 c
249 > drop b449568bf7fc 11 f
248 > drop b449568bf7fc 11 f
250 > EOF
249 > EOF
251 $ hg log -G
250 $ hg log -G
252 @ 12:40db8afa467b c
251 @ 12:40db8afa467b c
253 |
252 |
254 o 0:cb9a9f314b8b a
253 o 0:cb9a9f314b8b a
255
254
256
255
257 With rewritten ancestors
256 With rewritten ancestors
258
257
259 $ echo e > e
258 $ echo e > e
260 $ hg add e
259 $ hg add e
261 $ hg commit -m g
260 $ hg commit -m g
262 $ echo f > f
261 $ echo f > f
263 $ hg add f
262 $ hg add f
264 $ hg commit -m h
263 $ hg commit -m h
265 $ hg histedit -r '40db8afa467b' --commands - << EOF
264 $ hg histedit -r '40db8afa467b' --commands - << EOF
266 > pick 47a8561c0449 12 g
265 > pick 47a8561c0449 12 g
267 > pick 40db8afa467b 10 c
266 > pick 40db8afa467b 10 c
268 > drop 1b3b05f35ff0 13 h
267 > drop 1b3b05f35ff0 13 h
269 > EOF
268 > EOF
270 $ hg log -G
269 $ hg log -G
271 @ 17:ee6544123ab8 c
270 @ 17:ee6544123ab8 c
272 |
271 |
273 o 16:269e713e9eae g
272 o 16:269e713e9eae g
274 |
273 |
275 o 0:cb9a9f314b8b a
274 o 0:cb9a9f314b8b a
276
275
277 $ cd ../base
276 $ cd ../base
278
277
279
278
280
279
281 Test phases support
280 Test phases support
282 ===========================================
281 ===========================================
283
282
284 Check that histedit respect immutability
283 Check that histedit respect immutability
285 -------------------------------------------
284 -------------------------------------------
286
285
287 $ cat >> $HGRCPATH << EOF
286 $ cat >> $HGRCPATH << EOF
288 > [ui]
287 > [ui]
289 > logtemplate= {rev}:{node|short} ({phase}) {desc|firstline}\n
288 > logtemplate= {rev}:{node|short} ({phase}) {desc|firstline}\n
290 > EOF
289 > EOF
291
290
292 $ hg ph -pv '.^'
291 $ hg ph -pv '.^'
293 phase changed for 2 changesets
292 phase changed for 2 changesets
294 $ hg log -G
293 $ hg log -G
295 @ 13:b449568bf7fc (draft) f
294 @ 13:b449568bf7fc (draft) f
296 |
295 |
297 o 12:40db8afa467b (public) c
296 o 12:40db8afa467b (public) c
298 |
297 |
299 o 0:cb9a9f314b8b (public) a
298 o 0:cb9a9f314b8b (public) a
300
299
301 $ hg histedit -r '.~2'
300 $ hg histedit -r '.~2'
302 abort: cannot edit public changeset: cb9a9f314b8b
301 abort: cannot edit public changeset: cb9a9f314b8b
303 (see 'hg help phases' for details)
302 (see 'hg help phases' for details)
304 [255]
303 [255]
305
304
306
305
307 Prepare further testing
306 Prepare further testing
308 -------------------------------------------
307 -------------------------------------------
309
308
310 $ for x in g h i j k ; do
309 $ for x in g h i j k ; do
311 > echo $x > $x
310 > echo $x > $x
312 > hg add $x
311 > hg add $x
313 > hg ci -m $x
312 > hg ci -m $x
314 > done
313 > done
315 $ hg phase --force --secret .~2
314 $ hg phase --force --secret .~2
316 $ hg log -G
315 $ hg log -G
317 @ 18:ee118ab9fa44 (secret) k
316 @ 18:ee118ab9fa44 (secret) k
318 |
317 |
319 o 17:3a6c53ee7f3d (secret) j
318 o 17:3a6c53ee7f3d (secret) j
320 |
319 |
321 o 16:b605fb7503f2 (secret) i
320 o 16:b605fb7503f2 (secret) i
322 |
321 |
323 o 15:7395e1ff83bd (draft) h
322 o 15:7395e1ff83bd (draft) h
324 |
323 |
325 o 14:6b70183d2492 (draft) g
324 o 14:6b70183d2492 (draft) g
326 |
325 |
327 o 13:b449568bf7fc (draft) f
326 o 13:b449568bf7fc (draft) f
328 |
327 |
329 o 12:40db8afa467b (public) c
328 o 12:40db8afa467b (public) c
330 |
329 |
331 o 0:cb9a9f314b8b (public) a
330 o 0:cb9a9f314b8b (public) a
332
331
333 $ cd ..
332 $ cd ..
334
333
335 simple phase conservation
334 simple phase conservation
336 -------------------------------------------
335 -------------------------------------------
337
336
338 Resulting changeset should conserve the phase of the original one whatever the
337 Resulting changeset should conserve the phase of the original one whatever the
339 phases.new-commit option is.
338 phases.new-commit option is.
340
339
341 New-commit as draft (default)
340 New-commit as draft (default)
342
341
343 $ cp -R base simple-draft
342 $ cp -R base simple-draft
344 $ cd simple-draft
343 $ cd simple-draft
345 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
344 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
346 > edit b449568bf7fc 11 f
345 > edit b449568bf7fc 11 f
347 > pick 6b70183d2492 12 g
346 > pick 6b70183d2492 12 g
348 > pick 7395e1ff83bd 13 h
347 > pick 7395e1ff83bd 13 h
349 > pick b605fb7503f2 14 i
348 > pick b605fb7503f2 14 i
350 > pick 3a6c53ee7f3d 15 j
349 > pick 3a6c53ee7f3d 15 j
351 > pick ee118ab9fa44 16 k
350 > pick ee118ab9fa44 16 k
352 > EOF
351 > EOF
353 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
352 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
354 adding f
355 Editing (b449568bf7fc), you may commit or record as needed now.
353 Editing (b449568bf7fc), you may commit or record as needed now.
356 (hg histedit --continue to resume)
354 (hg histedit --continue to resume)
357 [1]
355 [1]
358 $ echo f >> f
356 $ echo f >> f
359 $ hg histedit --continue
357 $ hg histedit --continue
360 $ hg log -G
358 $ hg log -G
361 @ 24:12e89af74238 (secret) k
359 @ 24:12e89af74238 (secret) k
362 |
360 |
363 o 23:636a8687b22e (secret) j
361 o 23:636a8687b22e (secret) j
364 |
362 |
365 o 22:ccaf0a38653f (secret) i
363 o 22:ccaf0a38653f (secret) i
366 |
364 |
367 o 21:11a89d1c2613 (draft) h
365 o 21:11a89d1c2613 (draft) h
368 |
366 |
369 o 20:c1dec7ca82ea (draft) g
367 o 20:c1dec7ca82ea (draft) g
370 |
368 |
371 o 19:087281e68428 (draft) f
369 o 19:087281e68428 (draft) f
372 |
370 |
373 o 12:40db8afa467b (public) c
371 o 12:40db8afa467b (public) c
374 |
372 |
375 o 0:cb9a9f314b8b (public) a
373 o 0:cb9a9f314b8b (public) a
376
374
377 $ cd ..
375 $ cd ..
378
376
379
377
380 New-commit as secret (config)
378 New-commit as secret (config)
381
379
382 $ cp -R base simple-secret
380 $ cp -R base simple-secret
383 $ cd simple-secret
381 $ cd simple-secret
384 $ cat >> .hg/hgrc << EOF
382 $ cat >> .hg/hgrc << EOF
385 > [phases]
383 > [phases]
386 > new-commit=secret
384 > new-commit=secret
387 > EOF
385 > EOF
388 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
386 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
389 > edit b449568bf7fc 11 f
387 > edit b449568bf7fc 11 f
390 > pick 6b70183d2492 12 g
388 > pick 6b70183d2492 12 g
391 > pick 7395e1ff83bd 13 h
389 > pick 7395e1ff83bd 13 h
392 > pick b605fb7503f2 14 i
390 > pick b605fb7503f2 14 i
393 > pick 3a6c53ee7f3d 15 j
391 > pick 3a6c53ee7f3d 15 j
394 > pick ee118ab9fa44 16 k
392 > pick ee118ab9fa44 16 k
395 > EOF
393 > EOF
396 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
394 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
397 adding f
398 Editing (b449568bf7fc), you may commit or record as needed now.
395 Editing (b449568bf7fc), you may commit or record as needed now.
399 (hg histedit --continue to resume)
396 (hg histedit --continue to resume)
400 [1]
397 [1]
401 $ echo f >> f
398 $ echo f >> f
402 $ hg histedit --continue
399 $ hg histedit --continue
403 $ hg log -G
400 $ hg log -G
404 @ 24:12e89af74238 (secret) k
401 @ 24:12e89af74238 (secret) k
405 |
402 |
406 o 23:636a8687b22e (secret) j
403 o 23:636a8687b22e (secret) j
407 |
404 |
408 o 22:ccaf0a38653f (secret) i
405 o 22:ccaf0a38653f (secret) i
409 |
406 |
410 o 21:11a89d1c2613 (draft) h
407 o 21:11a89d1c2613 (draft) h
411 |
408 |
412 o 20:c1dec7ca82ea (draft) g
409 o 20:c1dec7ca82ea (draft) g
413 |
410 |
414 o 19:087281e68428 (draft) f
411 o 19:087281e68428 (draft) f
415 |
412 |
416 o 12:40db8afa467b (public) c
413 o 12:40db8afa467b (public) c
417 |
414 |
418 o 0:cb9a9f314b8b (public) a
415 o 0:cb9a9f314b8b (public) a
419
416
420 $ cd ..
417 $ cd ..
421
418
422
419
423 Changeset reordering
420 Changeset reordering
424 -------------------------------------------
421 -------------------------------------------
425
422
426 If a secret changeset is put before a draft one, all descendant should be secret.
423 If a secret changeset is put before a draft one, all descendant should be secret.
427 It seems more important to present the secret phase.
424 It seems more important to present the secret phase.
428
425
429 $ cp -R base reorder
426 $ cp -R base reorder
430 $ cd reorder
427 $ cd reorder
431 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
428 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
432 > pick b449568bf7fc 11 f
429 > pick b449568bf7fc 11 f
433 > pick 3a6c53ee7f3d 15 j
430 > pick 3a6c53ee7f3d 15 j
434 > pick 6b70183d2492 12 g
431 > pick 6b70183d2492 12 g
435 > pick b605fb7503f2 14 i
432 > pick b605fb7503f2 14 i
436 > pick 7395e1ff83bd 13 h
433 > pick 7395e1ff83bd 13 h
437 > pick ee118ab9fa44 16 k
434 > pick ee118ab9fa44 16 k
438 > EOF
435 > EOF
439 $ hg log -G
436 $ hg log -G
440 @ 23:558246857888 (secret) k
437 @ 23:558246857888 (secret) k
441 |
438 |
442 o 22:28bd44768535 (secret) h
439 o 22:28bd44768535 (secret) h
443 |
440 |
444 o 21:d5395202aeb9 (secret) i
441 o 21:d5395202aeb9 (secret) i
445 |
442 |
446 o 20:21edda8e341b (secret) g
443 o 20:21edda8e341b (secret) g
447 |
444 |
448 o 19:5ab64f3a4832 (secret) j
445 o 19:5ab64f3a4832 (secret) j
449 |
446 |
450 o 13:b449568bf7fc (draft) f
447 o 13:b449568bf7fc (draft) f
451 |
448 |
452 o 12:40db8afa467b (public) c
449 o 12:40db8afa467b (public) c
453 |
450 |
454 o 0:cb9a9f314b8b (public) a
451 o 0:cb9a9f314b8b (public) a
455
452
456 $ cd ..
453 $ cd ..
457
454
458 Changeset folding
455 Changeset folding
459 -------------------------------------------
456 -------------------------------------------
460
457
461 Folding a secret changeset with a draft one turn the result secret (again,
458 Folding a secret changeset with a draft one turn the result secret (again,
462 better safe than sorry). Folding between same phase changeset still works
459 better safe than sorry). Folding between same phase changeset still works
463
460
464 Note that there is a few reordering in this series for more extensive test
461 Note that there is a few reordering in this series for more extensive test
465
462
466 $ cp -R base folding
463 $ cp -R base folding
467 $ cd folding
464 $ cd folding
468 $ cat >> .hg/hgrc << EOF
465 $ cat >> .hg/hgrc << EOF
469 > [phases]
466 > [phases]
470 > new-commit=secret
467 > new-commit=secret
471 > EOF
468 > EOF
472 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
469 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
473 > pick 7395e1ff83bd 13 h
470 > pick 7395e1ff83bd 13 h
474 > fold b449568bf7fc 11 f
471 > fold b449568bf7fc 11 f
475 > pick 6b70183d2492 12 g
472 > pick 6b70183d2492 12 g
476 > fold 3a6c53ee7f3d 15 j
473 > fold 3a6c53ee7f3d 15 j
477 > pick b605fb7503f2 14 i
474 > pick b605fb7503f2 14 i
478 > fold ee118ab9fa44 16 k
475 > fold ee118ab9fa44 16 k
479 > EOF
476 > EOF
480 $ hg log -G
477 $ hg log -G
481 @ 27:f9daec13fb98 (secret) i
478 @ 27:f9daec13fb98 (secret) i
482 |
479 |
483 o 24:49807617f46a (secret) g
480 o 24:49807617f46a (secret) g
484 |
481 |
485 o 21:050280826e04 (draft) h
482 o 21:050280826e04 (draft) h
486 |
483 |
487 o 12:40db8afa467b (public) c
484 o 12:40db8afa467b (public) c
488 |
485 |
489 o 0:cb9a9f314b8b (public) a
486 o 0:cb9a9f314b8b (public) a
490
487
491 $ hg co 49807617f46a
488 $ hg co 49807617f46a
492 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
489 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
493 $ echo wat >> wat
490 $ echo wat >> wat
494 $ hg add wat
491 $ hg add wat
495 $ hg ci -m 'add wat'
492 $ hg ci -m 'add wat'
496 created new head
493 created new head
497 $ hg merge f9daec13fb98
494 $ hg merge f9daec13fb98
498 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
495 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
499 (branch merge, don't forget to commit)
496 (branch merge, don't forget to commit)
500 $ hg ci -m 'merge'
497 $ hg ci -m 'merge'
501 $ echo not wat > wat
498 $ echo not wat > wat
502 $ hg ci -m 'modify wat'
499 $ hg ci -m 'modify wat'
503 $ hg histedit 050280826e04
500 $ hg histedit 050280826e04
504 abort: cannot edit history that contains merges
501 abort: cannot edit history that contains merges
505 [255]
502 [255]
506 $ cd ..
503 $ cd ..
507
504
508 Check abort behavior
505 Check abort behavior
509 -------------------------------------------
506 -------------------------------------------
510
507
511 We checks that abort properly clean the repository so the same histedit can be
508 We checks that abort properly clean the repository so the same histedit can be
512 attempted later.
509 attempted later.
513
510
514 $ cp -R base abort
511 $ cp -R base abort
515 $ cd abort
512 $ cd abort
516 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
513 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
517 > pick b449568bf7fc 13 f
514 > pick b449568bf7fc 13 f
518 > pick 7395e1ff83bd 15 h
515 > pick 7395e1ff83bd 15 h
519 > pick 6b70183d2492 14 g
516 > pick 6b70183d2492 14 g
520 > pick b605fb7503f2 16 i
517 > pick b605fb7503f2 16 i
521 > roll 3a6c53ee7f3d 17 j
518 > roll 3a6c53ee7f3d 17 j
522 > edit ee118ab9fa44 18 k
519 > edit ee118ab9fa44 18 k
523 > EOF
520 > EOF
524 Editing (ee118ab9fa44), you may commit or record as needed now.
521 Editing (ee118ab9fa44), you may commit or record as needed now.
525 (hg histedit --continue to resume)
522 (hg histedit --continue to resume)
526 [1]
523 [1]
527
524
528 $ hg histedit --abort
525 $ hg histedit --abort
529 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
526 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
530 saved backup bundle to $TESTTMP/abort/.hg/strip-backup/4dc06258baa6-dff4ef05-backup.hg
527 saved backup bundle to $TESTTMP/abort/.hg/strip-backup/4dc06258baa6-dff4ef05-backup.hg
531
528
532 $ hg log -G
529 $ hg log -G
533 @ 18:ee118ab9fa44 (secret) k
530 @ 18:ee118ab9fa44 (secret) k
534 |
531 |
535 o 17:3a6c53ee7f3d (secret) j
532 o 17:3a6c53ee7f3d (secret) j
536 |
533 |
537 o 16:b605fb7503f2 (secret) i
534 o 16:b605fb7503f2 (secret) i
538 |
535 |
539 o 15:7395e1ff83bd (draft) h
536 o 15:7395e1ff83bd (draft) h
540 |
537 |
541 o 14:6b70183d2492 (draft) g
538 o 14:6b70183d2492 (draft) g
542 |
539 |
543 o 13:b449568bf7fc (draft) f
540 o 13:b449568bf7fc (draft) f
544 |
541 |
545 o 12:40db8afa467b (public) c
542 o 12:40db8afa467b (public) c
546 |
543 |
547 o 0:cb9a9f314b8b (public) a
544 o 0:cb9a9f314b8b (public) a
548
545
549 $ hg histedit -r 'b449568bf7fc' --commands - << EOF --config experimental.evolution.track-operation=1
546 $ hg histedit -r 'b449568bf7fc' --commands - << EOF --config experimental.evolution.track-operation=1
550 > pick b449568bf7fc 13 f
547 > pick b449568bf7fc 13 f
551 > pick 7395e1ff83bd 15 h
548 > pick 7395e1ff83bd 15 h
552 > pick 6b70183d2492 14 g
549 > pick 6b70183d2492 14 g
553 > pick b605fb7503f2 16 i
550 > pick b605fb7503f2 16 i
554 > pick 3a6c53ee7f3d 17 j
551 > pick 3a6c53ee7f3d 17 j
555 > edit ee118ab9fa44 18 k
552 > edit ee118ab9fa44 18 k
556 > EOF
553 > EOF
557 Editing (ee118ab9fa44), you may commit or record as needed now.
554 Editing (ee118ab9fa44), you may commit or record as needed now.
558 (hg histedit --continue to resume)
555 (hg histedit --continue to resume)
559 [1]
556 [1]
560 $ hg histedit --continue --config experimental.evolution.track-operation=1
557 $ hg histedit --continue --config experimental.evolution.track-operation=1
561 $ hg log -G
558 $ hg log -G
562 @ 23:175d6b286a22 (secret) k
559 @ 23:175d6b286a22 (secret) k
563 |
560 |
564 o 22:44ca09d59ae4 (secret) j
561 o 22:44ca09d59ae4 (secret) j
565 |
562 |
566 o 21:31747692a644 (secret) i
563 o 21:31747692a644 (secret) i
567 |
564 |
568 o 20:9985cd4f21fa (draft) g
565 o 20:9985cd4f21fa (draft) g
569 |
566 |
570 o 19:4dc06258baa6 (draft) h
567 o 19:4dc06258baa6 (draft) h
571 |
568 |
572 o 13:b449568bf7fc (draft) f
569 o 13:b449568bf7fc (draft) f
573 |
570 |
574 o 12:40db8afa467b (public) c
571 o 12:40db8afa467b (public) c
575 |
572 |
576 o 0:cb9a9f314b8b (public) a
573 o 0:cb9a9f314b8b (public) a
577
574
578 $ hg debugobsolete --rev .
575 $ hg debugobsolete --rev .
579 ee118ab9fa44ebb86be85996548b5517a39e5093 175d6b286a224c23f192e79a581ce83131a53fa2 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'histedit', 'user': 'test'}
576 ee118ab9fa44ebb86be85996548b5517a39e5093 175d6b286a224c23f192e79a581ce83131a53fa2 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'histedit', 'user': 'test'}
@@ -1,464 +1,463 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'? [Ynesfdaq?] y
263 examine changes to '1.txt'? [Ynesfdaq?] y
264
264
265 @@ -1,3 +1,3 @@
265 @@ -1,3 +1,3 @@
266 1
266 1
267 -2
267 -2
268 +2 2
268 +2 2
269 3
269 3
270 record change 1/4 to '1.txt'? [Ynesfdaq?] y
270 record change 1/4 to '1.txt'? [Ynesfdaq?] y
271
271
272 @@ -3,3 +3,3 @@
272 @@ -3,3 +3,3 @@
273 3
273 3
274 -4
274 -4
275 +4 4
275 +4 4
276 5
276 5
277 record change 2/4 to '1.txt'? [Ynesfdaq?] n
277 record change 2/4 to '1.txt'? [Ynesfdaq?] n
278
278
279 diff --git a/2.txt b/2.txt
279 diff --git a/2.txt b/2.txt
280 1 hunks, 1 lines changed
280 1 hunks, 1 lines changed
281 examine changes to '2.txt'? [Ynesfdaq?] y
281 examine changes to '2.txt'? [Ynesfdaq?] y
282
282
283 @@ -1,5 +1,5 @@
283 @@ -1,5 +1,5 @@
284 a
284 a
285 -b
285 -b
286 +b b
286 +b b
287 c
287 c
288 d
288 d
289 e
289 e
290 record change 3/4 to '2.txt'? [Ynesfdaq?] y
290 record change 3/4 to '2.txt'? [Ynesfdaq?] y
291
291
292 diff --git a/dir/a.txt b/dir/a.txt
292 diff --git a/dir/a.txt b/dir/a.txt
293 1 hunks, 1 lines changed
293 1 hunks, 1 lines changed
294 examine changes to 'dir/a.txt'? [Ynesfdaq?] n
294 examine changes to 'dir/a.txt'? [Ynesfdaq?] n
295
295
296
296
297 After qrecord a.patch 'tip'"
297 After qrecord a.patch 'tip'"
298
298
299 $ hg tip -p
299 $ hg tip -p
300 changeset: 1:5d1ca63427ee
300 changeset: 1:5d1ca63427ee
301 tag: a.patch
301 tag: a.patch
302 tag: qbase
302 tag: qbase
303 tag: qtip
303 tag: qtip
304 tag: tip
304 tag: tip
305 user: test
305 user: test
306 date: Thu Jan 01 00:00:00 1970 +0000
306 date: Thu Jan 01 00:00:00 1970 +0000
307 summary: aaa
307 summary: aaa
308
308
309 diff -r 1057167b20ef -r 5d1ca63427ee 1.txt
309 diff -r 1057167b20ef -r 5d1ca63427ee 1.txt
310 --- a/1.txt Thu Jan 01 00:00:00 1970 +0000
310 --- a/1.txt Thu Jan 01 00:00:00 1970 +0000
311 +++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
311 +++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
312 @@ -1,5 +1,5 @@
312 @@ -1,5 +1,5 @@
313 1
313 1
314 -2
314 -2
315 +2 2
315 +2 2
316 3
316 3
317 4
317 4
318 5
318 5
319 diff -r 1057167b20ef -r 5d1ca63427ee 2.txt
319 diff -r 1057167b20ef -r 5d1ca63427ee 2.txt
320 --- a/2.txt Thu Jan 01 00:00:00 1970 +0000
320 --- a/2.txt Thu Jan 01 00:00:00 1970 +0000
321 +++ b/2.txt Thu Jan 01 00:00:00 1970 +0000
321 +++ b/2.txt Thu Jan 01 00:00:00 1970 +0000
322 @@ -1,5 +1,5 @@
322 @@ -1,5 +1,5 @@
323 a
323 a
324 -b
324 -b
325 +b b
325 +b b
326 c
326 c
327 d
327 d
328 e
328 e
329
329
330
330
331 After qrecord a.patch 'diff'"
331 After qrecord a.patch 'diff'"
332
332
333 $ hg diff --nodates
333 $ hg diff --nodates
334 diff -r 5d1ca63427ee 1.txt
334 diff -r 5d1ca63427ee 1.txt
335 --- a/1.txt
335 --- a/1.txt
336 +++ b/1.txt
336 +++ b/1.txt
337 @@ -1,5 +1,5 @@
337 @@ -1,5 +1,5 @@
338 1
338 1
339 2 2
339 2 2
340 3
340 3
341 -4
341 -4
342 +4 4
342 +4 4
343 5
343 5
344 diff -r 5d1ca63427ee dir/a.txt
344 diff -r 5d1ca63427ee dir/a.txt
345 --- a/dir/a.txt
345 --- a/dir/a.txt
346 +++ b/dir/a.txt
346 +++ b/dir/a.txt
347 @@ -1,4 +1,4 @@
347 @@ -1,4 +1,4 @@
348 -hello world
348 -hello world
349 +hello world!
349 +hello world!
350
350
351 someone
351 someone
352 up
352 up
353
353
354 qrecord b.patch
354 qrecord b.patch
355
355
356 $ hg qrecord -d '0 0' -m bbb b.patch <<EOF
356 $ hg qrecord -d '0 0' -m bbb b.patch <<EOF
357 > y
357 > y
358 > y
358 > y
359 > y
359 > y
360 > y
360 > y
361 > EOF
361 > EOF
362 diff --git a/1.txt b/1.txt
362 diff --git a/1.txt b/1.txt
363 1 hunks, 1 lines changed
363 1 hunks, 1 lines changed
364 examine changes to '1.txt'? [Ynesfdaq?] y
364 examine changes to '1.txt'? [Ynesfdaq?] y
365
365
366 @@ -1,5 +1,5 @@
366 @@ -1,5 +1,5 @@
367 1
367 1
368 2 2
368 2 2
369 3
369 3
370 -4
370 -4
371 +4 4
371 +4 4
372 5
372 5
373 record change 1/2 to '1.txt'? [Ynesfdaq?] y
373 record change 1/2 to '1.txt'? [Ynesfdaq?] y
374
374
375 diff --git a/dir/a.txt b/dir/a.txt
375 diff --git a/dir/a.txt b/dir/a.txt
376 1 hunks, 1 lines changed
376 1 hunks, 1 lines changed
377 examine changes to 'dir/a.txt'? [Ynesfdaq?] y
377 examine changes to 'dir/a.txt'? [Ynesfdaq?] y
378
378
379 @@ -1,4 +1,4 @@
379 @@ -1,4 +1,4 @@
380 -hello world
380 -hello world
381 +hello world!
381 +hello world!
382
382
383 someone
383 someone
384 up
384 up
385 record change 2/2 to 'dir/a.txt'? [Ynesfdaq?] y
385 record change 2/2 to 'dir/a.txt'? [Ynesfdaq?] y
386
386
387
387
388 After qrecord b.patch 'tip'
388 After qrecord b.patch 'tip'
389
389
390 $ hg tip -p
390 $ hg tip -p
391 changeset: 2:b056198bf878
391 changeset: 2:b056198bf878
392 tag: b.patch
392 tag: b.patch
393 tag: qtip
393 tag: qtip
394 tag: tip
394 tag: tip
395 user: test
395 user: test
396 date: Thu Jan 01 00:00:00 1970 +0000
396 date: Thu Jan 01 00:00:00 1970 +0000
397 summary: bbb
397 summary: bbb
398
398
399 diff -r 5d1ca63427ee -r b056198bf878 1.txt
399 diff -r 5d1ca63427ee -r b056198bf878 1.txt
400 --- a/1.txt Thu Jan 01 00:00:00 1970 +0000
400 --- a/1.txt Thu Jan 01 00:00:00 1970 +0000
401 +++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
401 +++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
402 @@ -1,5 +1,5 @@
402 @@ -1,5 +1,5 @@
403 1
403 1
404 2 2
404 2 2
405 3
405 3
406 -4
406 -4
407 +4 4
407 +4 4
408 5
408 5
409 diff -r 5d1ca63427ee -r b056198bf878 dir/a.txt
409 diff -r 5d1ca63427ee -r b056198bf878 dir/a.txt
410 --- a/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
410 --- a/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
411 +++ b/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
411 +++ b/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
412 @@ -1,4 +1,4 @@
412 @@ -1,4 +1,4 @@
413 -hello world
413 -hello world
414 +hello world!
414 +hello world!
415
415
416 someone
416 someone
417 up
417 up
418
418
419
419
420 After qrecord b.patch 'diff'
420 After qrecord b.patch 'diff'
421
421
422 $ hg diff --nodates
422 $ hg diff --nodates
423
423
424 $ cd ..
424 $ cd ..
425
425
426 qrecord should throw an error when histedit in process
426 qrecord should throw an error when histedit in process
427
427
428 $ hg init issue5981
428 $ hg init issue5981
429 $ cd issue5981
429 $ cd issue5981
430 $ cat >> $HGRCPATH <<EOF
430 $ cat >> $HGRCPATH <<EOF
431 > [extensions]
431 > [extensions]
432 > histedit=
432 > histedit=
433 > mq=
433 > mq=
434 > EOF
434 > EOF
435 $ echo > a
435 $ echo > a
436 $ hg ci -Am 'foo bar'
436 $ hg ci -Am 'foo bar'
437 adding a
437 adding a
438 $ hg log
438 $ hg log
439 changeset: 0:ea55e2ae468f
439 changeset: 0:ea55e2ae468f
440 tag: tip
440 tag: tip
441 user: test
441 user: test
442 date: Thu Jan 01 00:00:00 1970 +0000
442 date: Thu Jan 01 00:00:00 1970 +0000
443 summary: foo bar
443 summary: foo bar
444
444
445 $ hg histedit tip --commands - 2>&1 <<EOF
445 $ hg histedit tip --commands - 2>&1 <<EOF
446 > edit ea55e2ae468f foo bar
446 > edit ea55e2ae468f foo bar
447 > EOF
447 > EOF
448 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
448 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
449 adding a
450 Editing (ea55e2ae468f), you may commit or record as needed now.
449 Editing (ea55e2ae468f), you may commit or record as needed now.
451 (hg histedit --continue to resume)
450 (hg histedit --continue to resume)
452 [1]
451 [1]
453 $ echo 'foo bar' > a
452 $ echo 'foo bar' > a
454 $ hg qrecord -d '0 0' -m aaa a.patch <<EOF
453 $ hg qrecord -d '0 0' -m aaa a.patch <<EOF
455 > y
454 > y
456 > y
455 > y
457 > n
456 > n
458 > y
457 > y
459 > y
458 > y
460 > n
459 > n
461 > EOF
460 > EOF
462 abort: histedit in progress
461 abort: histedit in progress
463 (use 'hg histedit --continue' or 'hg histedit --abort')
462 (use 'hg histedit --continue' or 'hg histedit --abort')
464 [255]
463 [255]
General Comments 0
You need to be logged in to leave comments. Login now