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