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