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