##// END OF EJS Templates
histedit: replaces patching logic by merges...
Pierre-Yves David -
r17647:d34ba499 default
parent child Browse files
Show More
@@ -1,749 +1,745
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 633536316234 and 7c2fd3b9020c
31 # Edit history between 633536316234 and 7c2fd3b9020c
32 #
32 #
33 # Commands:
33 # Commands:
34 # p, pick = use commit
34 # p, pick = use commit
35 # e, edit = use commit, but stop for amending
35 # e, edit = use commit, but stop for amending
36 # f, fold = use commit, but fold into previous commit (combines N and N-1)
36 # f, fold = use commit, but fold into previous commit (combines N and N-1)
37 # d, drop = remove commit from history
37 # d, drop = remove commit from history
38 # m, mess = edit message without changing commit content
38 # m, mess = edit message without changing commit content
39 #
39 #
40
40
41 In this file, lines beginning with ``#`` are ignored. You must specify a rule
41 In this file, lines beginning with ``#`` are ignored. You must specify a rule
42 for each revision in your history. For example, if you had meant to add gamma
42 for each revision in your history. For example, if you had meant to add gamma
43 before beta, and then wanted to add delta in the same revision as beta, you
43 before beta, and then wanted to add delta in the same revision as beta, you
44 would reorganize the file to look like this::
44 would reorganize the file to look like this::
45
45
46 pick 030b686bedc4 Add gamma
46 pick 030b686bedc4 Add gamma
47 pick c561b4e977df Add beta
47 pick c561b4e977df Add beta
48 fold 7c2fd3b9020c Add delta
48 fold 7c2fd3b9020c Add delta
49
49
50 # Edit history between 633536316234 and 7c2fd3b9020c
50 # Edit history between 633536316234 and 7c2fd3b9020c
51 #
51 #
52 # Commands:
52 # Commands:
53 # p, pick = use commit
53 # p, pick = use commit
54 # e, edit = use commit, but stop for amending
54 # e, edit = use commit, but stop for amending
55 # f, fold = use commit, but fold into previous commit (combines N and N-1)
55 # f, fold = use commit, but fold into previous commit (combines N and N-1)
56 # d, drop = remove commit from history
56 # d, drop = remove commit from history
57 # m, mess = edit message without changing commit content
57 # m, mess = edit message without changing commit content
58 #
58 #
59
59
60 At which point you close the editor and ``histedit`` starts working. When you
60 At which point you close the editor and ``histedit`` starts working. When you
61 specify a ``fold`` operation, ``histedit`` will open an editor when it folds
61 specify a ``fold`` operation, ``histedit`` will open an editor when it folds
62 those revisions together, offering you a chance to clean up the commit message::
62 those revisions together, offering you a chance to clean up the commit message::
63
63
64 Add beta
64 Add beta
65 ***
65 ***
66 Add delta
66 Add delta
67
67
68 Edit the commit message to your liking, then close the editor. For
68 Edit the commit message to your liking, then close the editor. For
69 this example, let's assume that the commit message was changed to
69 this example, let's assume that the commit message was changed to
70 ``Add beta and delta.`` After histedit has run and had a chance to
70 ``Add beta and delta.`` After histedit has run and had a chance to
71 remove any old or temporary revisions it needed, the history looks
71 remove any old or temporary revisions it needed, the history looks
72 like this::
72 like this::
73
73
74 @ 2[tip] 989b4d060121 2009-04-27 18:04 -0500 durin42
74 @ 2[tip] 989b4d060121 2009-04-27 18:04 -0500 durin42
75 | Add beta and delta.
75 | Add beta and delta.
76 |
76 |
77 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
77 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
78 | Add gamma
78 | Add gamma
79 |
79 |
80 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
80 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
81 Add alpha
81 Add alpha
82
82
83 Note that ``histedit`` does *not* remove any revisions (even its own temporary
83 Note that ``histedit`` does *not* remove any revisions (even its own temporary
84 ones) until after it has completed all the editing operations, so it will
84 ones) until after it has completed all the editing operations, so it will
85 probably perform several strip operations when it's done. For the above example,
85 probably perform several strip operations when it's done. For the above example,
86 it had to run strip twice. Strip can be slow depending on a variety of factors,
86 it had to run strip twice. Strip can be slow depending on a variety of factors,
87 so you might need to be a little patient. You can choose to keep the original
87 so you might need to be a little patient. You can choose to keep the original
88 revisions by passing the ``--keep`` flag.
88 revisions by passing the ``--keep`` flag.
89
89
90 The ``edit`` operation will drop you back to a command prompt,
90 The ``edit`` operation will drop you back to a command prompt,
91 allowing you to edit files freely, or even use ``hg record`` to commit
91 allowing you to edit files freely, or even use ``hg record`` to commit
92 some changes as a separate commit. When you're done, any remaining
92 some changes as a separate commit. When you're done, any remaining
93 uncommitted changes will be committed as well. When done, run ``hg
93 uncommitted changes will be committed as well. When done, run ``hg
94 histedit --continue`` to finish this step. You'll be prompted for a
94 histedit --continue`` to finish this step. You'll be prompted for a
95 new commit message, but the default commit message will be the
95 new commit message, but the default commit message will be the
96 original message for the ``edit`` ed revision.
96 original message for the ``edit`` ed revision.
97
97
98 The ``message`` operation will give you a chance to revise a commit
98 The ``message`` operation will give you a chance to revise a commit
99 message without changing the contents. It's a shortcut for doing
99 message without changing the contents. It's a shortcut for doing
100 ``edit`` immediately followed by `hg histedit --continue``.
100 ``edit`` immediately followed by `hg histedit --continue``.
101
101
102 If ``histedit`` encounters a conflict when moving a revision (while
102 If ``histedit`` encounters a conflict when moving a revision (while
103 handling ``pick`` or ``fold``), it'll stop in a similar manner to
103 handling ``pick`` or ``fold``), it'll stop in a similar manner to
104 ``edit`` with the difference that it won't prompt you for a commit
104 ``edit`` with the difference that it won't prompt you for a commit
105 message when done. If you decide at this point that you don't like how
105 message when done. If you decide at this point that you don't like how
106 much work it will be to rearrange history, or that you made a mistake,
106 much work it will be to rearrange history, or that you made a mistake,
107 you can use ``hg histedit --abort`` to abandon the new changes you
107 you can use ``hg histedit --abort`` to abandon the new changes you
108 have made and return to the state before you attempted to edit your
108 have made and return to the state before you attempted to edit your
109 history.
109 history.
110
110
111 If we clone the example repository above and add three more changes, such that
111 If we clone the example repository above and add three more changes, such that
112 we have the following history::
112 we have the following history::
113
113
114 @ 6[tip] 038383181893 2009-04-27 18:04 -0500 stefan
114 @ 6[tip] 038383181893 2009-04-27 18:04 -0500 stefan
115 | Add theta
115 | Add theta
116 |
116 |
117 o 5 140988835471 2009-04-27 18:04 -0500 stefan
117 o 5 140988835471 2009-04-27 18:04 -0500 stefan
118 | Add eta
118 | Add eta
119 |
119 |
120 o 4 122930637314 2009-04-27 18:04 -0500 stefan
120 o 4 122930637314 2009-04-27 18:04 -0500 stefan
121 | Add zeta
121 | Add zeta
122 |
122 |
123 o 3 836302820282 2009-04-27 18:04 -0500 stefan
123 o 3 836302820282 2009-04-27 18:04 -0500 stefan
124 | Add epsilon
124 | Add epsilon
125 |
125 |
126 o 2 989b4d060121 2009-04-27 18:04 -0500 durin42
126 o 2 989b4d060121 2009-04-27 18:04 -0500 durin42
127 | Add beta and delta.
127 | Add beta and delta.
128 |
128 |
129 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
129 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
130 | Add gamma
130 | Add gamma
131 |
131 |
132 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
132 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
133 Add alpha
133 Add alpha
134
134
135 If you run ``hg histedit --outgoing`` on the clone then it is the same
135 If you run ``hg histedit --outgoing`` on the clone then it is the same
136 as running ``hg histedit 836302820282``. If you need plan to push to a
136 as running ``hg histedit 836302820282``. If you need plan to push to a
137 repository that Mercurial does not detect to be related to the source
137 repository that Mercurial does not detect to be related to the source
138 repo, you can add a ``--force`` option.
138 repo, you can add a ``--force`` option.
139 """
139 """
140
140
141 try:
141 try:
142 import cPickle as pickle
142 import cPickle as pickle
143 except ImportError:
143 except ImportError:
144 import pickle
144 import pickle
145 import tempfile
146 import os
145 import os
147
146
148 from mercurial import bookmarks
147 from mercurial import bookmarks
149 from mercurial import cmdutil
148 from mercurial import cmdutil
150 from mercurial import discovery
149 from mercurial import discovery
151 from mercurial import error
150 from mercurial import error
152 from mercurial import copies
151 from mercurial import copies
153 from mercurial import context
152 from mercurial import context
154 from mercurial import hg
153 from mercurial import hg
155 from mercurial import lock as lockmod
154 from mercurial import lock as lockmod
156 from mercurial import node
155 from mercurial import node
157 from mercurial import patch
158 from mercurial import repair
156 from mercurial import repair
159 from mercurial import scmutil
157 from mercurial import scmutil
160 from mercurial import util
158 from mercurial import util
159 from mercurial import merge as mergemod
161 from mercurial.i18n import _
160 from mercurial.i18n import _
162
161
163 cmdtable = {}
162 cmdtable = {}
164 command = cmdutil.command(cmdtable)
163 command = cmdutil.command(cmdtable)
165
164
166 testedwith = 'internal'
165 testedwith = 'internal'
167
166
168 # i18n: command names and abbreviations must remain untranslated
167 # i18n: command names and abbreviations must remain untranslated
169 editcomment = _("""# Edit history between %s and %s
168 editcomment = _("""# Edit history between %s and %s
170 #
169 #
171 # Commands:
170 # Commands:
172 # p, pick = use commit
171 # p, pick = use commit
173 # e, edit = use commit, but stop for amending
172 # e, edit = use commit, but stop for amending
174 # f, fold = use commit, but fold into previous commit (combines N and N-1)
173 # f, fold = use commit, but fold into previous commit (combines N and N-1)
175 # d, drop = remove commit from history
174 # d, drop = remove commit from history
176 # m, mess = edit message without changing commit content
175 # m, mess = edit message without changing commit content
177 #
176 #
178 """)
177 """)
179
178
180 def foldchanges(ui, repo, node1, node2, opts):
179 def applychanges(ui, repo, ctx, opts):
181 """Produce a new changeset that represents the diff from node1 to node2."""
180 """Merge changeset from ctx (only) in the current working directory"""
182 try:
181 wcpar = repo.dirstate.parents()[0]
183 fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-')
182 if ctx.p1().node() == wcpar:
184 fp = os.fdopen(fd, 'w')
183 # edition ar "in place" we do not need to make any merge,
185 diffopts = patch.diffopts(ui, opts)
184 # just applies changes on parent for edition
186 diffopts.git = True
185 cmdutil.revert(ui, repo, ctx, (wcpar, node.nullid), all=True)
187 diffopts.ignorews = False
186 stats = None
188 diffopts.ignorewsamount = False
187 else:
189 diffopts.ignoreblanklines = False
188 try:
190 gen = patch.diff(repo, node1, node2, opts=diffopts)
189 # ui.forcemerge is an internal variable, do not document
191 for chunk in gen:
190 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
192 fp.write(chunk)
191 stats = mergemod.update(repo, ctx.node(), True, True, False,
193 fp.close()
192 ctx.p1().node())
194 files = set()
193 finally:
195 patch.patch(ui, repo, patchfile, files=files, eolmode=None)
194 repo.ui.setconfig('ui', 'forcemerge', '')
196 finally:
195 repo.setparents(wcpar, node.nullid)
197 os.unlink(patchfile)
196 repo.dirstate.write()
198 return files
197 # fix up dirstate for copies and renames
198 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
199 return stats
199
200
200 def collapse(repo, first, last, commitopts):
201 def collapse(repo, first, last, commitopts):
201 """collapse the set of revisions from first to last as new one.
202 """collapse the set of revisions from first to last as new one.
202
203
203 Expected commit options are:
204 Expected commit options are:
204 - message
205 - message
205 - date
206 - date
206 - username
207 - username
207 Edition of commit message is trigered in all case.
208 Edition of commit message is trigered in all case.
208
209
209 This function works in memory."""
210 This function works in memory."""
210 ctxs = list(repo.set('%d::%d', first, last))
211 ctxs = list(repo.set('%d::%d', first, last))
211 if not ctxs:
212 if not ctxs:
212 return None
213 return None
213 base = first.parents()[0]
214 base = first.parents()[0]
214
215
215 # commit a new version of the old changeset, including the update
216 # commit a new version of the old changeset, including the update
216 # collect all files which might be affected
217 # collect all files which might be affected
217 files = set()
218 files = set()
218 for ctx in ctxs:
219 for ctx in ctxs:
219 files.update(ctx.files())
220 files.update(ctx.files())
220
221
221 # Recompute copies (avoid recording a -> b -> a)
222 # Recompute copies (avoid recording a -> b -> a)
222 copied = copies.pathcopies(first, last)
223 copied = copies.pathcopies(first, last)
223
224
224 # prune files which were reverted by the updates
225 # prune files which were reverted by the updates
225 def samefile(f):
226 def samefile(f):
226 if f in last.manifest():
227 if f in last.manifest():
227 a = last.filectx(f)
228 a = last.filectx(f)
228 if f in base.manifest():
229 if f in base.manifest():
229 b = base.filectx(f)
230 b = base.filectx(f)
230 return (a.data() == b.data()
231 return (a.data() == b.data()
231 and a.flags() == b.flags())
232 and a.flags() == b.flags())
232 else:
233 else:
233 return False
234 return False
234 else:
235 else:
235 return f not in base.manifest()
236 return f not in base.manifest()
236 files = [f for f in files if not samefile(f)]
237 files = [f for f in files if not samefile(f)]
237 # commit version of these files as defined by head
238 # commit version of these files as defined by head
238 headmf = last.manifest()
239 headmf = last.manifest()
239 def filectxfn(repo, ctx, path):
240 def filectxfn(repo, ctx, path):
240 if path in headmf:
241 if path in headmf:
241 fctx = last[path]
242 fctx = last[path]
242 flags = fctx.flags()
243 flags = fctx.flags()
243 mctx = context.memfilectx(fctx.path(), fctx.data(),
244 mctx = context.memfilectx(fctx.path(), fctx.data(),
244 islink='l' in flags,
245 islink='l' in flags,
245 isexec='x' in flags,
246 isexec='x' in flags,
246 copied=copied.get(path))
247 copied=copied.get(path))
247 return mctx
248 return mctx
248 raise IOError()
249 raise IOError()
249
250
250 if commitopts.get('message'):
251 if commitopts.get('message'):
251 message = commitopts['message']
252 message = commitopts['message']
252 else:
253 else:
253 message = first.description()
254 message = first.description()
254 user = commitopts.get('user')
255 user = commitopts.get('user')
255 date = commitopts.get('date')
256 date = commitopts.get('date')
256 extra = first.extra()
257 extra = first.extra()
257
258
258 parents = (first.p1().node(), first.p2().node())
259 parents = (first.p1().node(), first.p2().node())
259 new = context.memctx(repo,
260 new = context.memctx(repo,
260 parents=parents,
261 parents=parents,
261 text=message,
262 text=message,
262 files=files,
263 files=files,
263 filectxfn=filectxfn,
264 filectxfn=filectxfn,
264 user=user,
265 user=user,
265 date=date,
266 date=date,
266 extra=extra)
267 extra=extra)
267 new._text = cmdutil.commitforceeditor(repo, new, [])
268 new._text = cmdutil.commitforceeditor(repo, new, [])
268 return repo.commitctx(new)
269 return repo.commitctx(new)
269
270
270 def pick(ui, repo, ctx, ha, opts):
271 def pick(ui, repo, ctx, ha, opts):
271 oldctx = repo[ha]
272 oldctx = repo[ha]
272 if oldctx.parents()[0] == ctx:
273 if oldctx.parents()[0] == ctx:
273 ui.debug('node %s unchanged\n' % ha)
274 ui.debug('node %s unchanged\n' % ha)
274 return oldctx, [], [], []
275 return oldctx, [], [], []
275 hg.update(repo, ctx.node())
276 hg.update(repo, ctx.node())
276 try:
277 stats = applychanges(ui, repo, oldctx, opts)
277 files = foldchanges(ui, repo, oldctx.p1().node() , ha, opts)
278 if stats and stats[3] > 0:
278 if not files:
279 ui.warn(_('%s: empty changeset')
280 % node.hex(ha))
281 return ctx, [], [], []
282 except Exception:
283 raise util.Abort(_('Fix up the change and run '
279 raise util.Abort(_('Fix up the change and run '
284 'hg histedit --continue'))
280 'hg histedit --continue'))
281 # drop the second merge parent
285 n = repo.commit(text=oldctx.description(), user=oldctx.user(),
282 n = repo.commit(text=oldctx.description(), user=oldctx.user(),
286 date=oldctx.date(), extra=oldctx.extra())
283 date=oldctx.date(), extra=oldctx.extra())
284 if n is None:
285 ui.warn(_('%s: empty changeset\n')
286 % node.hex(ha))
287 return ctx, [], [], []
287 return repo[n], [n], [oldctx.node()], []
288 return repo[n], [n], [oldctx.node()], []
288
289
289
290
290 def edit(ui, repo, ctx, ha, opts):
291 def edit(ui, repo, ctx, ha, opts):
291 oldctx = repo[ha]
292 oldctx = repo[ha]
292 hg.update(repo, ctx.node())
293 hg.update(repo, ctx.node())
293 try:
294 applychanges(ui, repo, oldctx, opts)
294 foldchanges(ui, repo, oldctx.p1().node() , ha, opts)
295 except Exception:
296 pass
297 raise util.Abort(_('Make changes as needed, you may commit or record as '
295 raise util.Abort(_('Make changes as needed, you may commit or record as '
298 'needed now.\nWhen you are finished, run hg'
296 'needed now.\nWhen you are finished, run hg'
299 ' histedit --continue to resume.'))
297 ' histedit --continue to resume.'))
300
298
301 def fold(ui, repo, ctx, ha, opts):
299 def fold(ui, repo, ctx, ha, opts):
302 oldctx = repo[ha]
300 oldctx = repo[ha]
303 hg.update(repo, ctx.node())
301 hg.update(repo, ctx.node())
304 try:
302 stats = applychanges(ui, repo, oldctx, opts)
305 files = foldchanges(ui, repo, oldctx.p1().node() , ha, opts)
303 if stats and stats[3] > 0:
306 if not files:
307 ui.warn(_('%s: empty changeset')
308 % node.hex(ha))
309 return ctx, [], [], []
310 except Exception:
311 raise util.Abort(_('Fix up the change and run '
304 raise util.Abort(_('Fix up the change and run '
312 'hg histedit --continue'))
305 'hg histedit --continue'))
313 n = repo.commit(text='fold-temp-revision %s' % ha, user=oldctx.user(),
306 n = repo.commit(text='fold-temp-revision %s' % ha, user=oldctx.user(),
314 date=oldctx.date(), extra=oldctx.extra())
307 date=oldctx.date(), extra=oldctx.extra())
308 if n is None:
309 ui.warn(_('%s: empty changeset')
310 % node.hex(ha))
311 return ctx, [], [], []
315 return finishfold(ui, repo, ctx, oldctx, n, opts, [])
312 return finishfold(ui, repo, ctx, oldctx, n, opts, [])
316
313
317 def finishfold(ui, repo, ctx, oldctx, newnode, opts, internalchanges):
314 def finishfold(ui, repo, ctx, oldctx, newnode, opts, internalchanges):
318 parent = ctx.parents()[0].node()
315 parent = ctx.parents()[0].node()
319 hg.update(repo, parent)
316 hg.update(repo, parent)
320 ### prepare new commit data
317 ### prepare new commit data
321 commitopts = opts.copy()
318 commitopts = opts.copy()
322 # username
319 # username
323 if ctx.user() == oldctx.user():
320 if ctx.user() == oldctx.user():
324 username = ctx.user()
321 username = ctx.user()
325 else:
322 else:
326 username = ui.username()
323 username = ui.username()
327 commitopts['user'] = username
324 commitopts['user'] = username
328 # commit message
325 # commit message
329 newmessage = '\n***\n'.join(
326 newmessage = '\n***\n'.join(
330 [ctx.description()] +
327 [ctx.description()] +
331 [repo[r].description() for r in internalchanges] +
328 [repo[r].description() for r in internalchanges] +
332 [oldctx.description()]) + '\n'
329 [oldctx.description()]) + '\n'
333 commitopts['message'] = newmessage
330 commitopts['message'] = newmessage
334 # date
331 # date
335 commitopts['date'] = max(ctx.date(), oldctx.date())
332 commitopts['date'] = max(ctx.date(), oldctx.date())
336 n = collapse(repo, ctx, repo[newnode], commitopts)
333 n = collapse(repo, ctx, repo[newnode], commitopts)
337 if n is None:
334 if n is None:
338 return ctx, [], [], []
335 return ctx, [], [], []
339 hg.update(repo, n)
336 hg.update(repo, n)
340 return repo[n], [n], [oldctx.node(), ctx.node()], [newnode]
337 return repo[n], [n], [oldctx.node(), ctx.node()], [newnode]
341
338
342 def drop(ui, repo, ctx, ha, opts):
339 def drop(ui, repo, ctx, ha, opts):
343 return ctx, [], [repo[ha].node()], []
340 return ctx, [], [repo[ha].node()], []
344
341
345
342
346 def message(ui, repo, ctx, ha, opts):
343 def message(ui, repo, ctx, ha, opts):
347 oldctx = repo[ha]
344 oldctx = repo[ha]
348 hg.update(repo, ctx.node())
345 hg.update(repo, ctx.node())
349 try:
346 stats = applychanges(ui, repo, oldctx, opts)
350 foldchanges(ui, repo, oldctx.p1().node() , ha, opts)
347 if stats and stats[3] > 0:
351 except Exception:
352 raise util.Abort(_('Fix up the change and run '
348 raise util.Abort(_('Fix up the change and run '
353 'hg histedit --continue'))
349 'hg histedit --continue'))
354 message = oldctx.description() + '\n'
350 message = oldctx.description() + '\n'
355 message = ui.edit(message, ui.username())
351 message = ui.edit(message, ui.username())
356 new = repo.commit(text=message, user=oldctx.user(), date=oldctx.date(),
352 new = repo.commit(text=message, user=oldctx.user(), date=oldctx.date(),
357 extra=oldctx.extra())
353 extra=oldctx.extra())
358 newctx = repo[new]
354 newctx = repo[new]
359 if oldctx.node() != newctx.node():
355 if oldctx.node() != newctx.node():
360 return newctx, [new], [oldctx.node()], []
356 return newctx, [new], [oldctx.node()], []
361 # We didn't make an edit, so just indicate no replaced nodes
357 # We didn't make an edit, so just indicate no replaced nodes
362 return newctx, [new], [], []
358 return newctx, [new], [], []
363
359
364 actiontable = {'p': pick,
360 actiontable = {'p': pick,
365 'pick': pick,
361 'pick': pick,
366 'e': edit,
362 'e': edit,
367 'edit': edit,
363 'edit': edit,
368 'f': fold,
364 'f': fold,
369 'fold': fold,
365 'fold': fold,
370 'd': drop,
366 'd': drop,
371 'drop': drop,
367 'drop': drop,
372 'm': message,
368 'm': message,
373 'mess': message,
369 'mess': message,
374 }
370 }
375
371
376 @command('histedit',
372 @command('histedit',
377 [('', 'commands', '',
373 [('', 'commands', '',
378 _('Read history edits from the specified file.')),
374 _('Read history edits from the specified file.')),
379 ('c', 'continue', False, _('continue an edit already in progress')),
375 ('c', 'continue', False, _('continue an edit already in progress')),
380 ('k', 'keep', False,
376 ('k', 'keep', False,
381 _("don't strip old nodes after edit is complete")),
377 _("don't strip old nodes after edit is complete")),
382 ('', 'abort', False, _('abort an edit in progress')),
378 ('', 'abort', False, _('abort an edit in progress')),
383 ('o', 'outgoing', False, _('changesets not found in destination')),
379 ('o', 'outgoing', False, _('changesets not found in destination')),
384 ('f', 'force', False,
380 ('f', 'force', False,
385 _('force outgoing even for unrelated repositories')),
381 _('force outgoing even for unrelated repositories')),
386 ('r', 'rev', [], _('first revision to be edited'))],
382 ('r', 'rev', [], _('first revision to be edited'))],
387 _("[PARENT]"))
383 _("[PARENT]"))
388 def histedit(ui, repo, *parent, **opts):
384 def histedit(ui, repo, *parent, **opts):
389 """interactively edit changeset history
385 """interactively edit changeset history
390 """
386 """
391 # TODO only abort if we try and histedit mq patches, not just
387 # TODO only abort if we try and histedit mq patches, not just
392 # blanket if mq patches are applied somewhere
388 # blanket if mq patches are applied somewhere
393 mq = getattr(repo, 'mq', None)
389 mq = getattr(repo, 'mq', None)
394 if mq and mq.applied:
390 if mq and mq.applied:
395 raise util.Abort(_('source has mq patches applied'))
391 raise util.Abort(_('source has mq patches applied'))
396
392
397 parent = list(parent) + opts.get('rev', [])
393 parent = list(parent) + opts.get('rev', [])
398 if opts.get('outgoing'):
394 if opts.get('outgoing'):
399 if len(parent) > 1:
395 if len(parent) > 1:
400 raise util.Abort(
396 raise util.Abort(
401 _('only one repo argument allowed with --outgoing'))
397 _('only one repo argument allowed with --outgoing'))
402 elif parent:
398 elif parent:
403 parent = parent[0]
399 parent = parent[0]
404
400
405 dest = ui.expandpath(parent or 'default-push', parent or 'default')
401 dest = ui.expandpath(parent or 'default-push', parent or 'default')
406 dest, revs = hg.parseurl(dest, None)[:2]
402 dest, revs = hg.parseurl(dest, None)[:2]
407 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
403 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
408
404
409 revs, checkout = hg.addbranchrevs(repo, repo, revs, None)
405 revs, checkout = hg.addbranchrevs(repo, repo, revs, None)
410 other = hg.peer(repo, opts, dest)
406 other = hg.peer(repo, opts, dest)
411
407
412 if revs:
408 if revs:
413 revs = [repo.lookup(rev) for rev in revs]
409 revs = [repo.lookup(rev) for rev in revs]
414
410
415 parent = discovery.findcommonoutgoing(
411 parent = discovery.findcommonoutgoing(
416 repo, other, [], force=opts.get('force')).missing[0:1]
412 repo, other, [], force=opts.get('force')).missing[0:1]
417 else:
413 else:
418 if opts.get('force'):
414 if opts.get('force'):
419 raise util.Abort(_('--force only allowed with --outgoing'))
415 raise util.Abort(_('--force only allowed with --outgoing'))
420
416
421 if opts.get('continue', False):
417 if opts.get('continue', False):
422 if len(parent) != 0:
418 if len(parent) != 0:
423 raise util.Abort(_('no arguments allowed with --continue'))
419 raise util.Abort(_('no arguments allowed with --continue'))
424 (parentctxnode, created, replaced,
420 (parentctxnode, created, replaced,
425 tmpnodes, existing, rules, keep, tip, replacemap) = readstate(repo)
421 tmpnodes, existing, rules, keep, tip, replacemap) = readstate(repo)
426 currentparent, wantnull = repo.dirstate.parents()
422 currentparent, wantnull = repo.dirstate.parents()
427 parentctx = repo[parentctxnode]
423 parentctx = repo[parentctxnode]
428 # existing is the list of revisions initially considered by
424 # existing is the list of revisions initially considered by
429 # histedit. Here we use it to list new changesets, descendants
425 # histedit. Here we use it to list new changesets, descendants
430 # of parentctx without an 'existing' changeset in-between. We
426 # of parentctx without an 'existing' changeset in-between. We
431 # also have to exclude 'existing' changesets which were
427 # also have to exclude 'existing' changesets which were
432 # previously dropped.
428 # previously dropped.
433 descendants = set(c.node() for c in
429 descendants = set(c.node() for c in
434 repo.set('(%n::) - %n', parentctxnode, parentctxnode))
430 repo.set('(%n::) - %n', parentctxnode, parentctxnode))
435 existing = set(existing)
431 existing = set(existing)
436 notdropped = set(n for n in existing if n in descendants and
432 notdropped = set(n for n in existing if n in descendants and
437 (n not in replacemap or replacemap[n] in descendants))
433 (n not in replacemap or replacemap[n] in descendants))
438 # Discover any nodes the user has added in the interim. We can
434 # Discover any nodes the user has added in the interim. We can
439 # miss changesets which were dropped and recreated the same.
435 # miss changesets which were dropped and recreated the same.
440 newchildren = list(c.node() for c in repo.set(
436 newchildren = list(c.node() for c in repo.set(
441 'sort(%ln - (%ln or %ln::))', descendants, existing, notdropped))
437 'sort(%ln - (%ln or %ln::))', descendants, existing, notdropped))
442 action, currentnode = rules.pop(0)
438 action, currentnode = rules.pop(0)
443 if action in ('f', 'fold'):
439 if action in ('f', 'fold'):
444 tmpnodes.extend(newchildren)
440 tmpnodes.extend(newchildren)
445 else:
441 else:
446 created.extend(newchildren)
442 created.extend(newchildren)
447
443
448 m, a, r, d = repo.status()[:4]
444 m, a, r, d = repo.status()[:4]
449 oldctx = repo[currentnode]
445 oldctx = repo[currentnode]
450 message = oldctx.description() + '\n'
446 message = oldctx.description() + '\n'
451 if action in ('e', 'edit', 'm', 'mess'):
447 if action in ('e', 'edit', 'm', 'mess'):
452 message = ui.edit(message, ui.username())
448 message = ui.edit(message, ui.username())
453 elif action in ('f', 'fold'):
449 elif action in ('f', 'fold'):
454 message = 'fold-temp-revision %s' % currentnode
450 message = 'fold-temp-revision %s' % currentnode
455 new = None
451 new = None
456 if m or a or r or d:
452 if m or a or r or d:
457 new = repo.commit(text=message, user=oldctx.user(),
453 new = repo.commit(text=message, user=oldctx.user(),
458 date=oldctx.date(), extra=oldctx.extra())
454 date=oldctx.date(), extra=oldctx.extra())
459
455
460 # If we're resuming a fold and we have new changes, mark the
456 # If we're resuming a fold and we have new changes, mark the
461 # replacements and finish the fold. If not, it's more like a
457 # replacements and finish the fold. If not, it's more like a
462 # drop of the changesets that disappeared, and we can skip
458 # drop of the changesets that disappeared, and we can skip
463 # this step.
459 # this step.
464 if action in ('f', 'fold') and (new or newchildren):
460 if action in ('f', 'fold') and (new or newchildren):
465 if new:
461 if new:
466 tmpnodes.append(new)
462 tmpnodes.append(new)
467 else:
463 else:
468 new = newchildren[-1]
464 new = newchildren[-1]
469 (parentctx, created_, replaced_, tmpnodes_) = finishfold(
465 (parentctx, created_, replaced_, tmpnodes_) = finishfold(
470 ui, repo, parentctx, oldctx, new, opts, newchildren)
466 ui, repo, parentctx, oldctx, new, opts, newchildren)
471 replaced.extend(replaced_)
467 replaced.extend(replaced_)
472 created.extend(created_)
468 created.extend(created_)
473 tmpnodes.extend(tmpnodes_)
469 tmpnodes.extend(tmpnodes_)
474 elif action not in ('d', 'drop'):
470 elif action not in ('d', 'drop'):
475 if new != oldctx.node():
471 if new != oldctx.node():
476 replaced.append(oldctx.node())
472 replaced.append(oldctx.node())
477 if new:
473 if new:
478 if new != oldctx.node():
474 if new != oldctx.node():
479 created.append(new)
475 created.append(new)
480 parentctx = repo[new]
476 parentctx = repo[new]
481
477
482 elif opts.get('abort', False):
478 elif opts.get('abort', False):
483 if len(parent) != 0:
479 if len(parent) != 0:
484 raise util.Abort(_('no arguments allowed with --abort'))
480 raise util.Abort(_('no arguments allowed with --abort'))
485 (parentctxnode, created, replaced, tmpnodes,
481 (parentctxnode, created, replaced, tmpnodes,
486 existing, rules, keep, tip, replacemap) = readstate(repo)
482 existing, rules, keep, tip, replacemap) = readstate(repo)
487 ui.debug('restore wc to old tip %s\n' % node.hex(tip))
483 ui.debug('restore wc to old tip %s\n' % node.hex(tip))
488 hg.clean(repo, tip)
484 hg.clean(repo, tip)
489 ui.debug('should strip created nodes %s\n' %
485 ui.debug('should strip created nodes %s\n' %
490 ', '.join([node.hex(n)[:12] for n in created]))
486 ', '.join([node.hex(n)[:12] for n in created]))
491 ui.debug('should strip temp nodes %s\n' %
487 ui.debug('should strip temp nodes %s\n' %
492 ', '.join([node.hex(n)[:12] for n in tmpnodes]))
488 ', '.join([node.hex(n)[:12] for n in tmpnodes]))
493 for nodes in (created, tmpnodes):
489 for nodes in (created, tmpnodes):
494 lock = None
490 lock = None
495 try:
491 try:
496 lock = repo.lock()
492 lock = repo.lock()
497 for n in reversed(nodes):
493 for n in reversed(nodes):
498 try:
494 try:
499 repair.strip(ui, repo, n)
495 repair.strip(ui, repo, n)
500 except error.LookupError:
496 except error.LookupError:
501 pass
497 pass
502 finally:
498 finally:
503 lockmod.release(lock)
499 lockmod.release(lock)
504 os.unlink(os.path.join(repo.path, 'histedit-state'))
500 os.unlink(os.path.join(repo.path, 'histedit-state'))
505 return
501 return
506 else:
502 else:
507 cmdutil.bailifchanged(repo)
503 cmdutil.bailifchanged(repo)
508 if os.path.exists(os.path.join(repo.path, 'histedit-state')):
504 if os.path.exists(os.path.join(repo.path, 'histedit-state')):
509 raise util.Abort(_('history edit already in progress, try '
505 raise util.Abort(_('history edit already in progress, try '
510 '--continue or --abort'))
506 '--continue or --abort'))
511
507
512 tip, empty = repo.dirstate.parents()
508 tip, empty = repo.dirstate.parents()
513
509
514
510
515 if len(parent) != 1:
511 if len(parent) != 1:
516 raise util.Abort(_('histedit requires exactly one parent revision'))
512 raise util.Abort(_('histedit requires exactly one parent revision'))
517 parent = scmutil.revsingle(repo, parent[0]).node()
513 parent = scmutil.revsingle(repo, parent[0]).node()
518
514
519 keep = opts.get('keep', False)
515 keep = opts.get('keep', False)
520 revs = between(repo, parent, tip, keep)
516 revs = between(repo, parent, tip, keep)
521
517
522 ctxs = [repo[r] for r in revs]
518 ctxs = [repo[r] for r in revs]
523 existing = [r.node() for r in ctxs]
519 existing = [r.node() for r in ctxs]
524 rules = opts.get('commands', '')
520 rules = opts.get('commands', '')
525 if not rules:
521 if not rules:
526 rules = '\n'.join([makedesc(c) for c in ctxs])
522 rules = '\n'.join([makedesc(c) for c in ctxs])
527 rules += '\n\n'
523 rules += '\n\n'
528 rules += editcomment % (node.hex(parent)[:12], node.hex(tip)[:12])
524 rules += editcomment % (node.hex(parent)[:12], node.hex(tip)[:12])
529 rules = ui.edit(rules, ui.username())
525 rules = ui.edit(rules, ui.username())
530 # Save edit rules in .hg/histedit-last-edit.txt in case
526 # Save edit rules in .hg/histedit-last-edit.txt in case
531 # the user needs to ask for help after something
527 # the user needs to ask for help after something
532 # surprising happens.
528 # surprising happens.
533 f = open(repo.join('histedit-last-edit.txt'), 'w')
529 f = open(repo.join('histedit-last-edit.txt'), 'w')
534 f.write(rules)
530 f.write(rules)
535 f.close()
531 f.close()
536 else:
532 else:
537 f = open(rules)
533 f = open(rules)
538 rules = f.read()
534 rules = f.read()
539 f.close()
535 f.close()
540 rules = [l for l in (r.strip() for r in rules.splitlines())
536 rules = [l for l in (r.strip() for r in rules.splitlines())
541 if l and not l[0] == '#']
537 if l and not l[0] == '#']
542 rules = verifyrules(rules, repo, ctxs)
538 rules = verifyrules(rules, repo, ctxs)
543
539
544 parentctx = repo[parent].parents()[0]
540 parentctx = repo[parent].parents()[0]
545 keep = opts.get('keep', False)
541 keep = opts.get('keep', False)
546 replaced = []
542 replaced = []
547 replacemap = {}
543 replacemap = {}
548 tmpnodes = []
544 tmpnodes = []
549 created = []
545 created = []
550
546
551
547
552 while rules:
548 while rules:
553 writestate(repo, parentctx.node(), created, replaced,
549 writestate(repo, parentctx.node(), created, replaced,
554 tmpnodes, existing, rules, keep, tip, replacemap)
550 tmpnodes, existing, rules, keep, tip, replacemap)
555 action, ha = rules.pop(0)
551 action, ha = rules.pop(0)
556 ui.debug('histedit: processing %s %s\n' % (action, ha))
552 ui.debug('histedit: processing %s %s\n' % (action, ha))
557 (parentctx, created_, replaced_, tmpnodes_) = actiontable[action](
553 (parentctx, created_, replaced_, tmpnodes_) = actiontable[action](
558 ui, repo, parentctx, ha, opts)
554 ui, repo, parentctx, ha, opts)
559
555
560 if replaced_:
556 if replaced_:
561 clen, rlen = len(created_), len(replaced_)
557 clen, rlen = len(created_), len(replaced_)
562 if clen == rlen == 1:
558 if clen == rlen == 1:
563 ui.debug('histedit: exact replacement of %s with %s\n' % (
559 ui.debug('histedit: exact replacement of %s with %s\n' % (
564 node.short(replaced_[0]), node.short(created_[0])))
560 node.short(replaced_[0]), node.short(created_[0])))
565
561
566 replacemap[replaced_[0]] = created_[0]
562 replacemap[replaced_[0]] = created_[0]
567 elif clen > rlen:
563 elif clen > rlen:
568 assert rlen == 1, ('unexpected replacement of '
564 assert rlen == 1, ('unexpected replacement of '
569 '%d changes with %d changes' % (rlen, clen))
565 '%d changes with %d changes' % (rlen, clen))
570 # made more changesets than we're replacing
566 # made more changesets than we're replacing
571 # TODO synthesize patch names for created patches
567 # TODO synthesize patch names for created patches
572 replacemap[replaced_[0]] = created_[-1]
568 replacemap[replaced_[0]] = created_[-1]
573 ui.debug('histedit: created many, assuming %s replaced by %s' %
569 ui.debug('histedit: created many, assuming %s replaced by %s' %
574 (node.short(replaced_[0]), node.short(created_[-1])))
570 (node.short(replaced_[0]), node.short(created_[-1])))
575 elif rlen > clen:
571 elif rlen > clen:
576 if not created_:
572 if not created_:
577 # This must be a drop. Try and put our metadata on
573 # This must be a drop. Try and put our metadata on
578 # the parent change.
574 # the parent change.
579 assert rlen == 1
575 assert rlen == 1
580 r = replaced_[0]
576 r = replaced_[0]
581 ui.debug('histedit: %s seems replaced with nothing, '
577 ui.debug('histedit: %s seems replaced with nothing, '
582 'finding a parent\n' % (node.short(r)))
578 'finding a parent\n' % (node.short(r)))
583 pctx = repo[r].parents()[0]
579 pctx = repo[r].parents()[0]
584 if pctx.node() in replacemap:
580 if pctx.node() in replacemap:
585 ui.debug('histedit: parent is already replaced\n')
581 ui.debug('histedit: parent is already replaced\n')
586 replacemap[r] = replacemap[pctx.node()]
582 replacemap[r] = replacemap[pctx.node()]
587 else:
583 else:
588 replacemap[r] = pctx.node()
584 replacemap[r] = pctx.node()
589 ui.debug('histedit: %s best replaced by %s\n' % (
585 ui.debug('histedit: %s best replaced by %s\n' % (
590 node.short(r), node.short(replacemap[r])))
586 node.short(r), node.short(replacemap[r])))
591 else:
587 else:
592 assert len(created_) == 1
588 assert len(created_) == 1
593 for r in replaced_:
589 for r in replaced_:
594 ui.debug('histedit: %s replaced by %s\n' % (
590 ui.debug('histedit: %s replaced by %s\n' % (
595 node.short(r), node.short(created_[0])))
591 node.short(r), node.short(created_[0])))
596 replacemap[r] = created_[0]
592 replacemap[r] = created_[0]
597 else:
593 else:
598 assert False, (
594 assert False, (
599 'Unhandled case in replacement mapping! '
595 'Unhandled case in replacement mapping! '
600 'replacing %d changes with %d changes' % (rlen, clen))
596 'replacing %d changes with %d changes' % (rlen, clen))
601 created.extend(created_)
597 created.extend(created_)
602 replaced.extend(replaced_)
598 replaced.extend(replaced_)
603 tmpnodes.extend(tmpnodes_)
599 tmpnodes.extend(tmpnodes_)
604
600
605 hg.update(repo, parentctx.node())
601 hg.update(repo, parentctx.node())
606
602
607 if not keep:
603 if not keep:
608 if replacemap:
604 if replacemap:
609 ui.note(_('histedit: Should update metadata for the following '
605 ui.note(_('histedit: Should update metadata for the following '
610 'changes:\n'))
606 'changes:\n'))
611
607
612 def copybms(old, new):
608 def copybms(old, new):
613 if old in tmpnodes or old in created:
609 if old in tmpnodes or old in created:
614 # can't have any metadata we'd want to update
610 # can't have any metadata we'd want to update
615 return
611 return
616 while new in replacemap:
612 while new in replacemap:
617 new = replacemap[new]
613 new = replacemap[new]
618 ui.note(_('histedit: %s to %s\n') % (node.short(old),
614 ui.note(_('histedit: %s to %s\n') % (node.short(old),
619 node.short(new)))
615 node.short(new)))
620 octx = repo[old]
616 octx = repo[old]
621 marks = octx.bookmarks()
617 marks = octx.bookmarks()
622 if marks:
618 if marks:
623 ui.note(_('histedit: moving bookmarks %s\n') %
619 ui.note(_('histedit: moving bookmarks %s\n') %
624 ', '.join(marks))
620 ', '.join(marks))
625 for mark in marks:
621 for mark in marks:
626 repo._bookmarks[mark] = new
622 repo._bookmarks[mark] = new
627 bookmarks.write(repo)
623 bookmarks.write(repo)
628
624
629 # We assume that bookmarks on the tip should remain
625 # We assume that bookmarks on the tip should remain
630 # tipmost, but bookmarks on non-tip changesets should go
626 # tipmost, but bookmarks on non-tip changesets should go
631 # to their most reasonable successor. As a result, find
627 # to their most reasonable successor. As a result, find
632 # the old tip and new tip and copy those bookmarks first,
628 # the old tip and new tip and copy those bookmarks first,
633 # then do the rest of the bookmark copies.
629 # then do the rest of the bookmark copies.
634 oldtip = sorted(replacemap.keys(), key=repo.changelog.rev)[-1]
630 oldtip = sorted(replacemap.keys(), key=repo.changelog.rev)[-1]
635 newtip = sorted(replacemap.values(), key=repo.changelog.rev)[-1]
631 newtip = sorted(replacemap.values(), key=repo.changelog.rev)[-1]
636 copybms(oldtip, newtip)
632 copybms(oldtip, newtip)
637
633
638 for old, new in sorted(replacemap.iteritems()):
634 for old, new in sorted(replacemap.iteritems()):
639 copybms(old, new)
635 copybms(old, new)
640 # TODO update mq state
636 # TODO update mq state
641
637
642 ui.debug('should strip replaced nodes %s\n' %
638 ui.debug('should strip replaced nodes %s\n' %
643 ', '.join([node.hex(n)[:12] for n in replaced]))
639 ', '.join([node.hex(n)[:12] for n in replaced]))
644 lock = None
640 lock = None
645 try:
641 try:
646 lock = repo.lock()
642 lock = repo.lock()
647 for n in sorted(replaced, key=lambda x: repo[x].rev()):
643 for n in sorted(replaced, key=lambda x: repo[x].rev()):
648 try:
644 try:
649 repair.strip(ui, repo, n)
645 repair.strip(ui, repo, n)
650 except error.LookupError:
646 except error.LookupError:
651 pass
647 pass
652 finally:
648 finally:
653 lockmod.release(lock)
649 lockmod.release(lock)
654
650
655 ui.debug('should strip temp nodes %s\n' %
651 ui.debug('should strip temp nodes %s\n' %
656 ', '.join([node.hex(n)[:12] for n in tmpnodes]))
652 ', '.join([node.hex(n)[:12] for n in tmpnodes]))
657 lock = None
653 lock = None
658 try:
654 try:
659 lock = repo.lock()
655 lock = repo.lock()
660 for n in reversed(tmpnodes):
656 for n in reversed(tmpnodes):
661 try:
657 try:
662 repair.strip(ui, repo, n)
658 repair.strip(ui, repo, n)
663 except error.LookupError:
659 except error.LookupError:
664 pass
660 pass
665 finally:
661 finally:
666 lockmod.release(lock)
662 lockmod.release(lock)
667 os.unlink(os.path.join(repo.path, 'histedit-state'))
663 os.unlink(os.path.join(repo.path, 'histedit-state'))
668 if os.path.exists(repo.sjoin('undo')):
664 if os.path.exists(repo.sjoin('undo')):
669 os.unlink(repo.sjoin('undo'))
665 os.unlink(repo.sjoin('undo'))
670
666
671
667
672 def between(repo, old, new, keep):
668 def between(repo, old, new, keep):
673 """select and validate the set of revision to edit
669 """select and validate the set of revision to edit
674
670
675 When keep is false, the specified set can't have children."""
671 When keep is false, the specified set can't have children."""
676 revs = [old]
672 revs = [old]
677 current = old
673 current = old
678 while current != new:
674 while current != new:
679 ctx = repo[current]
675 ctx = repo[current]
680 if not keep and len(ctx.children()) > 1:
676 if not keep and len(ctx.children()) > 1:
681 raise util.Abort(_('cannot edit history that would orphan nodes'))
677 raise util.Abort(_('cannot edit history that would orphan nodes'))
682 if len(ctx.parents()) != 1 and ctx.parents()[1] != node.nullid:
678 if len(ctx.parents()) != 1 and ctx.parents()[1] != node.nullid:
683 raise util.Abort(_("can't edit history with merges"))
679 raise util.Abort(_("can't edit history with merges"))
684 if not ctx.children():
680 if not ctx.children():
685 current = new
681 current = new
686 else:
682 else:
687 current = ctx.children()[0].node()
683 current = ctx.children()[0].node()
688 revs.append(current)
684 revs.append(current)
689 if len(repo[current].children()) and not keep:
685 if len(repo[current].children()) and not keep:
690 raise util.Abort(_('cannot edit history that would orphan nodes'))
686 raise util.Abort(_('cannot edit history that would orphan nodes'))
691 return revs
687 return revs
692
688
693
689
694 def writestate(repo, parentctxnode, created, replaced,
690 def writestate(repo, parentctxnode, created, replaced,
695 tmpnodes, existing, rules, keep, oldtip, replacemap):
691 tmpnodes, existing, rules, keep, oldtip, replacemap):
696 fp = open(os.path.join(repo.path, 'histedit-state'), 'w')
692 fp = open(os.path.join(repo.path, 'histedit-state'), 'w')
697 pickle.dump((parentctxnode, created, replaced,
693 pickle.dump((parentctxnode, created, replaced,
698 tmpnodes, existing, rules, keep, oldtip, replacemap),
694 tmpnodes, existing, rules, keep, oldtip, replacemap),
699 fp)
695 fp)
700 fp.close()
696 fp.close()
701
697
702 def readstate(repo):
698 def readstate(repo):
703 """Returns a tuple of (parentnode, created, replaced, tmp, existing, rules,
699 """Returns a tuple of (parentnode, created, replaced, tmp, existing, rules,
704 keep, oldtip, replacemap ).
700 keep, oldtip, replacemap ).
705 """
701 """
706 fp = open(os.path.join(repo.path, 'histedit-state'))
702 fp = open(os.path.join(repo.path, 'histedit-state'))
707 return pickle.load(fp)
703 return pickle.load(fp)
708
704
709
705
710 def makedesc(c):
706 def makedesc(c):
711 """build a initial action line for a ctx `c`
707 """build a initial action line for a ctx `c`
712
708
713 line are in the form:
709 line are in the form:
714
710
715 pick <hash> <rev> <summary>
711 pick <hash> <rev> <summary>
716 """
712 """
717 summary = ''
713 summary = ''
718 if c.description():
714 if c.description():
719 summary = c.description().splitlines()[0]
715 summary = c.description().splitlines()[0]
720 line = 'pick %s %d %s' % (c.hex()[:12], c.rev(), summary)
716 line = 'pick %s %d %s' % (c.hex()[:12], c.rev(), summary)
721 return line[:80] # trim to 80 chars so it's not stupidly wide in my editor
717 return line[:80] # trim to 80 chars so it's not stupidly wide in my editor
722
718
723 def verifyrules(rules, repo, ctxs):
719 def verifyrules(rules, repo, ctxs):
724 """Verify that there exists exactly one edit rule per given changeset.
720 """Verify that there exists exactly one edit rule per given changeset.
725
721
726 Will abort if there are to many or too few rules, a malformed rule,
722 Will abort if there are to many or too few rules, a malformed rule,
727 or a rule on a changeset outside of the user-given range.
723 or a rule on a changeset outside of the user-given range.
728 """
724 """
729 parsed = []
725 parsed = []
730 if len(rules) != len(ctxs):
726 if len(rules) != len(ctxs):
731 raise util.Abort(_('must specify a rule for each changeset once'))
727 raise util.Abort(_('must specify a rule for each changeset once'))
732 for r in rules:
728 for r in rules:
733 if ' ' not in r:
729 if ' ' not in r:
734 raise util.Abort(_('malformed line "%s"') % r)
730 raise util.Abort(_('malformed line "%s"') % r)
735 action, rest = r.split(' ', 1)
731 action, rest = r.split(' ', 1)
736 if ' ' in rest.strip():
732 if ' ' in rest.strip():
737 ha, rest = rest.split(' ', 1)
733 ha, rest = rest.split(' ', 1)
738 else:
734 else:
739 ha = r.strip()
735 ha = r.strip()
740 try:
736 try:
741 if repo[ha] not in ctxs:
737 if repo[ha] not in ctxs:
742 raise util.Abort(
738 raise util.Abort(
743 _('may not use changesets other than the ones listed'))
739 _('may not use changesets other than the ones listed'))
744 except error.RepoError:
740 except error.RepoError:
745 raise util.Abort(_('unknown changeset %s listed') % ha)
741 raise util.Abort(_('unknown changeset %s listed') % ha)
746 if action not in actiontable:
742 if action not in actiontable:
747 raise util.Abort(_('unknown action "%s"') % action)
743 raise util.Abort(_('unknown action "%s"') % action)
748 parsed.append([action, ha])
744 parsed.append([action, ha])
749 return parsed
745 return parsed
@@ -1,181 +1,184
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 > graphlog=
5 > graphlog=
6 > histedit=
6 > histedit=
7 > EOF
7 > EOF
8
8
9 $ initrepo ()
9 $ initrepo ()
10 > {
10 > {
11 > hg init $1
11 > hg init $1
12 > cd $1
12 > cd $1
13 > for x in a b c d e f ; do
13 > for x in a b c d e f ; do
14 > echo $x$x$x$x$x > $x
14 > echo $x$x$x$x$x > $x
15 > hg add $x
15 > hg add $x
16 > done
16 > done
17 > hg ci -m 'Initial commit'
17 > hg ci -m 'Initial commit'
18 > for x in a b c d e f ; do
18 > for x in a b c d e f ; do
19 > echo $x > $x
19 > echo $x > $x
20 > hg ci -m $x
20 > hg ci -m $x
21 > done
21 > done
22 > echo 'I can haz no commute' > e
22 > echo 'I can haz no commute' > e
23 > hg ci -m 'does not commute with e'
23 > hg ci -m 'does not commute with e'
24 > cd ..
24 > cd ..
25 > }
25 > }
26
26
27 $ initrepo r
27 $ initrepo r
28 $ cd r
28 $ cd r
29 Initial generation of the command files
29 Initial generation of the command files
30
30
31 $ EDITED="$TESTTMP/editedhistory"
31 $ EDITED="$TESTTMP/editedhistory"
32 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 3 >> $EDITED
32 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 3 >> $EDITED
33 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 4 >> $EDITED
33 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 4 >> $EDITED
34 $ hg log --template 'fold {node|short} {rev} {desc}\n' -r 7 >> $EDITED
34 $ hg log --template 'fold {node|short} {rev} {desc}\n' -r 7 >> $EDITED
35 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 5 >> $EDITED
35 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 5 >> $EDITED
36 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 6 >> $EDITED
36 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 6 >> $EDITED
37 $ cat $EDITED
37 $ cat $EDITED
38 pick 65a9a84f33fd 3 c
38 pick 65a9a84f33fd 3 c
39 pick 00f1c5383965 4 d
39 pick 00f1c5383965 4 d
40 fold 39522b764e3d 7 does not commute with e
40 fold 39522b764e3d 7 does not commute with e
41 pick 7b4e2f4b7bcd 5 e
41 pick 7b4e2f4b7bcd 5 e
42 pick 500cac37a696 6 f
42 pick 500cac37a696 6 f
43
43
44 log before edit
44 log before edit
45 $ hg log --graph
45 $ hg log --graph
46 @ changeset: 7:39522b764e3d
46 @ changeset: 7:39522b764e3d
47 | tag: tip
47 | tag: tip
48 | user: test
48 | user: test
49 | date: Thu Jan 01 00:00:00 1970 +0000
49 | date: Thu Jan 01 00:00:00 1970 +0000
50 | summary: does not commute with e
50 | summary: does not commute with e
51 |
51 |
52 o changeset: 6:500cac37a696
52 o changeset: 6:500cac37a696
53 | user: test
53 | user: test
54 | date: Thu Jan 01 00:00:00 1970 +0000
54 | date: Thu Jan 01 00:00:00 1970 +0000
55 | summary: f
55 | summary: f
56 |
56 |
57 o changeset: 5:7b4e2f4b7bcd
57 o changeset: 5:7b4e2f4b7bcd
58 | user: test
58 | user: test
59 | date: Thu Jan 01 00:00:00 1970 +0000
59 | date: Thu Jan 01 00:00:00 1970 +0000
60 | summary: e
60 | summary: e
61 |
61 |
62 o changeset: 4:00f1c5383965
62 o changeset: 4:00f1c5383965
63 | user: test
63 | user: test
64 | date: Thu Jan 01 00:00:00 1970 +0000
64 | date: Thu Jan 01 00:00:00 1970 +0000
65 | summary: d
65 | summary: d
66 |
66 |
67 o changeset: 3:65a9a84f33fd
67 o changeset: 3:65a9a84f33fd
68 | user: test
68 | user: test
69 | date: Thu Jan 01 00:00:00 1970 +0000
69 | date: Thu Jan 01 00:00:00 1970 +0000
70 | summary: c
70 | summary: c
71 |
71 |
72 o changeset: 2:da6535b52e45
72 o changeset: 2:da6535b52e45
73 | user: test
73 | user: test
74 | date: Thu Jan 01 00:00:00 1970 +0000
74 | date: Thu Jan 01 00:00:00 1970 +0000
75 | summary: b
75 | summary: b
76 |
76 |
77 o changeset: 1:c1f09da44841
77 o changeset: 1:c1f09da44841
78 | user: test
78 | user: test
79 | date: Thu Jan 01 00:00:00 1970 +0000
79 | date: Thu Jan 01 00:00:00 1970 +0000
80 | summary: a
80 | summary: a
81 |
81 |
82 o changeset: 0:1715188a53c7
82 o changeset: 0:1715188a53c7
83 user: test
83 user: test
84 date: Thu Jan 01 00:00:00 1970 +0000
84 date: Thu Jan 01 00:00:00 1970 +0000
85 summary: Initial commit
85 summary: Initial commit
86
86
87
87
88 edit the history
88 edit the history
89 $ HGEDITOR="cat \"$EDITED\" > " hg histedit 3 2>&1 | fixbundle
89 $ HGEDITOR="cat \"$EDITED\" > " hg histedit 3 2>&1 | fixbundle
90 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
91 patching file e
91 merging e
92 Hunk #1 FAILED at 0
92 warning: conflicts during merge.
93 1 out of 1 hunks FAILED -- saving rejects to file e.rej
93 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
94 abort: Fix up the change and run hg histedit --continue
94 abort: Fix up the change and run hg histedit --continue
95
95
96 fix up
96 fix up
97 $ echo a > e
97 $ echo 'I can haz no commute' > e
98 $ hg resolve --mark e
98 $ cat > cat.py <<EOF
99 $ cat > cat.py <<EOF
99 > import sys
100 > import sys
100 > print open(sys.argv[1]).read()
101 > print open(sys.argv[1]).read()
101 > print
102 > print
102 > print
103 > print
103 > EOF
104 > EOF
104 $ HGEDITOR="python cat.py" hg histedit --continue 2>&1 | fixbundle | grep -v '2 files removed'
105 $ HGEDITOR="python cat.py" hg histedit --continue 2>&1 | fixbundle | grep -v '2 files removed'
105 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
106 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
106 d
107 d
107 ***
108 ***
108 does not commute with e
109 does not commute with e
109
110
110
111
111
112
112 HG: Enter commit message. Lines beginning with 'HG:' are removed.
113 HG: Enter commit message. Lines beginning with 'HG:' are removed.
113 HG: Leave message empty to abort commit.
114 HG: Leave message empty to abort commit.
114 HG: --
115 HG: --
115 HG: user: test
116 HG: user: test
116 HG: branch 'default'
117 HG: branch 'default'
117 HG: changed d
118 HG: changed d
118 HG: changed e
119 HG: changed e
119
120
120
121
121
122
122 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
123 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
123 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
124 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
124 patching file e
125 merging e
125 Hunk #1 FAILED at 0
126 warning: conflicts during merge.
126 1 out of 1 hunks FAILED -- saving rejects to file e.rej
127 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
127 abort: Fix up the change and run hg histedit --continue
128 abort: Fix up the change and run hg histedit --continue
128
129
129 just continue this time
130 just continue this time
131 $ hg revert -r 'p1()' e
132 $ hg resolve --mark e
130 $ hg histedit --continue 2>&1 | fixbundle
133 $ hg histedit --continue 2>&1 | fixbundle
131 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
134 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
132 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
135 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
133
136
134 log after edit
137 log after edit
135 $ hg log --graph
138 $ hg log --graph
136 @ changeset: 5:45bd04206744
139 @ changeset: 5:2696a654c663
137 | tag: tip
140 | tag: tip
138 | user: test
141 | user: test
139 | date: Thu Jan 01 00:00:00 1970 +0000
142 | date: Thu Jan 01 00:00:00 1970 +0000
140 | summary: f
143 | summary: f
141 |
144 |
142 o changeset: 4:abff6367c13a
145 o changeset: 4:ec2c1cf833a8
143 | user: test
146 | user: test
144 | date: Thu Jan 01 00:00:00 1970 +0000
147 | date: Thu Jan 01 00:00:00 1970 +0000
145 | summary: d
148 | summary: d
146 |
149 |
147 o changeset: 3:65a9a84f33fd
150 o changeset: 3:65a9a84f33fd
148 | user: test
151 | user: test
149 | date: Thu Jan 01 00:00:00 1970 +0000
152 | date: Thu Jan 01 00:00:00 1970 +0000
150 | summary: c
153 | summary: c
151 |
154 |
152 o changeset: 2:da6535b52e45
155 o changeset: 2:da6535b52e45
153 | user: test
156 | user: test
154 | date: Thu Jan 01 00:00:00 1970 +0000
157 | date: Thu Jan 01 00:00:00 1970 +0000
155 | summary: b
158 | summary: b
156 |
159 |
157 o changeset: 1:c1f09da44841
160 o changeset: 1:c1f09da44841
158 | user: test
161 | user: test
159 | date: Thu Jan 01 00:00:00 1970 +0000
162 | date: Thu Jan 01 00:00:00 1970 +0000
160 | summary: a
163 | summary: a
161 |
164 |
162 o changeset: 0:1715188a53c7
165 o changeset: 0:1715188a53c7
163 user: test
166 user: test
164 date: Thu Jan 01 00:00:00 1970 +0000
167 date: Thu Jan 01 00:00:00 1970 +0000
165 summary: Initial commit
168 summary: Initial commit
166
169
167
170
168 contents of e
171 contents of e
169 $ hg cat e
172 $ hg cat e
170 a
173 I can haz no commute
171
174
172 manifest
175 manifest
173 $ hg manifest
176 $ hg manifest
174 a
177 a
175 b
178 b
176 c
179 c
177 d
180 d
178 e
181 e
179 f
182 f
180
183
181 $ cd ..
184 $ cd ..
@@ -1,249 +1,261
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 > graphlog=
5 > graphlog=
6 > histedit=
6 > histedit=
7 > EOF
7 > EOF
8
8
9 $ EDITED="$TESTTMP/editedhistory"
9 $ EDITED="$TESTTMP/editedhistory"
10 $ cat > $EDITED <<EOF
10 $ cat > $EDITED <<EOF
11 > pick e860deea161a e
11 > pick e860deea161a e
12 > pick 652413bf663e f
12 > pick 652413bf663e f
13 > fold 177f92b77385 c
13 > fold 177f92b77385 c
14 > pick 055a42cdd887 d
14 > pick 055a42cdd887 d
15 > EOF
15 > EOF
16 $ initrepo ()
16 $ initrepo ()
17 > {
17 > {
18 > hg init r
18 > hg init r
19 > cd r
19 > cd r
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
26
27 $ initrepo
27 $ initrepo
28
28
29 log before edit
29 log before edit
30 $ hg log --graph
30 $ hg log --graph
31 @ changeset: 5:652413bf663e
31 @ changeset: 5:652413bf663e
32 | tag: tip
32 | tag: tip
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: f
35 | summary: f
36 |
36 |
37 o changeset: 4:e860deea161a
37 o changeset: 4:e860deea161a
38 | user: test
38 | user: test
39 | date: Thu Jan 01 00:00:00 1970 +0000
39 | date: Thu Jan 01 00:00:00 1970 +0000
40 | summary: e
40 | summary: e
41 |
41 |
42 o changeset: 3:055a42cdd887
42 o changeset: 3:055a42cdd887
43 | user: test
43 | user: test
44 | date: Thu Jan 01 00:00:00 1970 +0000
44 | date: Thu Jan 01 00:00:00 1970 +0000
45 | summary: d
45 | summary: d
46 |
46 |
47 o changeset: 2:177f92b77385
47 o changeset: 2:177f92b77385
48 | user: test
48 | user: test
49 | date: Thu Jan 01 00:00:00 1970 +0000
49 | date: Thu Jan 01 00:00:00 1970 +0000
50 | summary: c
50 | summary: c
51 |
51 |
52 o changeset: 1:d2ae7f538514
52 o changeset: 1:d2ae7f538514
53 | user: test
53 | user: test
54 | date: Thu Jan 01 00:00:00 1970 +0000
54 | date: Thu Jan 01 00:00:00 1970 +0000
55 | summary: b
55 | summary: b
56 |
56 |
57 o changeset: 0:cb9a9f314b8b
57 o changeset: 0:cb9a9f314b8b
58 user: test
58 user: test
59 date: Thu Jan 01 00:00:00 1970 +0000
59 date: Thu Jan 01 00:00:00 1970 +0000
60 summary: a
60 summary: a
61
61
62
62
63 edit the history
63 edit the history
64 $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
64 $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
65 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
65 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
66 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
66 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
67 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
67 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
68 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
68 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
69 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
69 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
70 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
70 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
71 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
71 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
72
72
73 log after edit
73 log after edit
74 $ hg log --graph
74 $ hg log --graph
75 @ changeset: 4:82b0c1ff1777
75 @ changeset: 4:82b0c1ff1777
76 | tag: tip
76 | tag: tip
77 | user: test
77 | user: test
78 | date: Thu Jan 01 00:00:00 1970 +0000
78 | date: Thu Jan 01 00:00:00 1970 +0000
79 | summary: d
79 | summary: d
80 |
80 |
81 o changeset: 3:150aafb44a91
81 o changeset: 3:150aafb44a91
82 | user: test
82 | user: test
83 | date: Thu Jan 01 00:00:00 1970 +0000
83 | date: Thu Jan 01 00:00:00 1970 +0000
84 | summary: pick e860deea161a e
84 | summary: pick e860deea161a e
85 |
85 |
86 o changeset: 2:493dc0964412
86 o changeset: 2:493dc0964412
87 | user: test
87 | user: test
88 | date: Thu Jan 01 00:00:00 1970 +0000
88 | date: Thu Jan 01 00:00:00 1970 +0000
89 | summary: e
89 | summary: e
90 |
90 |
91 o changeset: 1:d2ae7f538514
91 o changeset: 1:d2ae7f538514
92 | user: test
92 | user: test
93 | date: Thu Jan 01 00:00:00 1970 +0000
93 | date: Thu Jan 01 00:00:00 1970 +0000
94 | summary: b
94 | summary: b
95 |
95 |
96 o changeset: 0:cb9a9f314b8b
96 o changeset: 0:cb9a9f314b8b
97 user: test
97 user: test
98 date: Thu Jan 01 00:00:00 1970 +0000
98 date: Thu Jan 01 00:00:00 1970 +0000
99 summary: a
99 summary: a
100
100
101
101
102 post-fold manifest
102 post-fold manifest
103 $ hg manifest
103 $ hg manifest
104 a
104 a
105 b
105 b
106 c
106 c
107 d
107 d
108 e
108 e
109 f
109 f
110
110
111 $ cd ..
111 $ cd ..
112
112
113 folding and creating no new change doesn't break:
113 folding and creating no new change doesn't break:
114 $ mkdir fold-to-empty-test
114 $ mkdir fold-to-empty-test
115 $ cd fold-to-empty-test
115 $ cd fold-to-empty-test
116 $ hg init
116 $ hg init
117 $ printf "1\n2\n3\n" > file
117 $ printf "1\n2\n3\n" > file
118 $ hg add file
118 $ hg add file
119 $ hg commit -m '1+2+3'
119 $ hg commit -m '1+2+3'
120 $ echo 4 >> file
120 $ echo 4 >> file
121 $ hg commit -m '+4'
121 $ hg commit -m '+4'
122 $ echo 5 >> file
122 $ echo 5 >> file
123 $ hg commit -m '+5'
123 $ hg commit -m '+5'
124 $ echo 6 >> file
124 $ echo 6 >> file
125 $ hg commit -m '+6'
125 $ hg commit -m '+6'
126 $ hg log --graph
126 $ hg log --graph
127 @ changeset: 3:251d831eeec5
127 @ changeset: 3:251d831eeec5
128 | tag: tip
128 | tag: tip
129 | user: test
129 | user: test
130 | date: Thu Jan 01 00:00:00 1970 +0000
130 | date: Thu Jan 01 00:00:00 1970 +0000
131 | summary: +6
131 | summary: +6
132 |
132 |
133 o changeset: 2:888f9082bf99
133 o changeset: 2:888f9082bf99
134 | user: test
134 | user: test
135 | date: Thu Jan 01 00:00:00 1970 +0000
135 | date: Thu Jan 01 00:00:00 1970 +0000
136 | summary: +5
136 | summary: +5
137 |
137 |
138 o changeset: 1:617f94f13c0f
138 o changeset: 1:617f94f13c0f
139 | user: test
139 | user: test
140 | date: Thu Jan 01 00:00:00 1970 +0000
140 | date: Thu Jan 01 00:00:00 1970 +0000
141 | summary: +4
141 | summary: +4
142 |
142 |
143 o changeset: 0:0189ba417d34
143 o changeset: 0:0189ba417d34
144 user: test
144 user: test
145 date: Thu Jan 01 00:00:00 1970 +0000
145 date: Thu Jan 01 00:00:00 1970 +0000
146 summary: 1+2+3
146 summary: 1+2+3
147
147
148
148
149 $ cat > editor.py <<EOF
149 $ cat > editor.py <<EOF
150 > import re, sys
150 > import re, sys
151 > rules = sys.argv[1]
151 > rules = sys.argv[1]
152 > data = open(rules).read()
152 > data = open(rules).read()
153 > data = re.sub(r'pick ([0-9a-f]{12} 2 \+5)', r'drop \1', data)
153 > data = re.sub(r'pick ([0-9a-f]{12} 2 \+5)', r'drop \1', data)
154 > data = re.sub(r'pick ([0-9a-f]{12} 2 \+6)', r'fold \1', data)
154 > data = re.sub(r'pick ([0-9a-f]{12} 2 \+6)', r'fold \1', data)
155 > open(rules, 'w').write(data)
155 > open(rules, 'w').write(data)
156 > EOF
156 > EOF
157
157
158 $ HGEDITOR='python editor.py' hg histedit 1
158 $ HGEDITOR='python editor.py' hg histedit 1
159 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
159 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
160 patching file file
160 merging file
161 Hunk #1 FAILED at 2
161 warning: conflicts during merge.
162 1 out of 1 hunks FAILED -- saving rejects to file file.rej
162 merging file incomplete! (edit conflicts, then use 'hg resolve --mark')
163 abort: Fix up the change and run hg histedit --continue
163 abort: Fix up the change and run hg histedit --continue
164 [255]
164 [255]
165 There were conflicts, but we'll continue without resolving. This
165 There were conflicts, we keep P1 content. This
166 should effectively drop the changes from +6.
166 should effectively drop the changes from +6.
167 $ hg status
167 $ hg status
168 M file
168 ? editor.py
169 ? editor.py
169 ? file.rej
170 ? file.orig
171 $ hg resolve -l
172 U file
173 $ hg revert -r 'p1()' file
174 $ hg resolve --mark file
170 $ hg histedit --continue
175 $ hg histedit --continue
171 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
176 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
172 saved backup bundle to $TESTTMP/*-backup.hg (glob)
177 saved backup bundle to $TESTTMP/*-backup.hg (glob)
173 $ hg log --graph
178 $ hg log --graph
174 @ changeset: 1:617f94f13c0f
179 @ changeset: 1:617f94f13c0f
175 | tag: tip
180 | tag: tip
176 | user: test
181 | user: test
177 | date: Thu Jan 01 00:00:00 1970 +0000
182 | date: Thu Jan 01 00:00:00 1970 +0000
178 | summary: +4
183 | summary: +4
179 |
184 |
180 o changeset: 0:0189ba417d34
185 o changeset: 0:0189ba417d34
181 user: test
186 user: test
182 date: Thu Jan 01 00:00:00 1970 +0000
187 date: Thu Jan 01 00:00:00 1970 +0000
183 summary: 1+2+3
188 summary: 1+2+3
184
189
185
190
186 $ cd ..
191 $ cd ..
187
192
188 Test corner case where folded revision is separated from its parent by a
193 Test corner case where folded revision is separated from its parent by a
189 dropped revision.
194 dropped revision.
190
195
191
196
192 $ hg init fold-with-dropped
197 $ hg init fold-with-dropped
193 $ cd fold-with-dropped
198 $ cd fold-with-dropped
194 $ printf "1\n2\n3\n" > file
199 $ printf "1\n2\n3\n" > file
195 $ hg commit -Am '1+2+3'
200 $ hg commit -Am '1+2+3'
196 adding file
201 adding file
197 $ echo 4 >> file
202 $ echo 4 >> file
198 $ hg commit -m '+4'
203 $ hg commit -m '+4'
199 $ echo 5 >> file
204 $ echo 5 >> file
200 $ hg commit -m '+5'
205 $ hg commit -m '+5'
201 $ echo 6 >> file
206 $ echo 6 >> file
202 $ hg commit -m '+6'
207 $ hg commit -m '+6'
203 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n'
208 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n'
204 @ 3:251d831eeec5 +6
209 @ 3:251d831eeec5 +6
205 |
210 |
206 o 2:888f9082bf99 +5
211 o 2:888f9082bf99 +5
207 |
212 |
208 o 1:617f94f13c0f +4
213 o 1:617f94f13c0f +4
209 |
214 |
210 o 0:0189ba417d34 1+2+3
215 o 0:0189ba417d34 1+2+3
211
216
212 $ EDITED="$TESTTMP/editcommands"
217 $ EDITED="$TESTTMP/editcommands"
213 $ cat > $EDITED <<EOF
218 $ cat > $EDITED <<EOF
214 > pick 617f94f13c0f 1 +4
219 > pick 617f94f13c0f 1 +4
215 > drop 888f9082bf99 2 +5
220 > drop 888f9082bf99 2 +5
216 > fold 251d831eeec5 3 +6
221 > fold 251d831eeec5 3 +6
217 > EOF
222 > EOF
218 $ HGEDITOR="cat $EDITED >" hg histedit 1
223 $ HGEDITOR="cat $EDITED >" hg histedit 1
219 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
224 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
220 patching file file
225 merging file
221 Hunk #1 FAILED at 2
226 warning: conflicts during merge.
222 1 out of 1 hunks FAILED -- saving rejects to file file.rej
227 merging file incomplete! (edit conflicts, then use 'hg resolve --mark')
223 abort: Fix up the change and run hg histedit --continue
228 abort: Fix up the change and run hg histedit --continue
224 [255]
229 [255]
225 $ echo 5 >> file
230 $ cat > file << EOF
231 > 1
232 > 2
233 > 3
234 > 4
235 > 5
236 > EOF
237 $ hg resolve --mark file
226 $ hg commit -m '+5.2'
238 $ hg commit -m '+5.2'
227 created new head
239 created new head
228 $ echo 6 >> file
240 $ echo 6 >> file
229 $ HGEDITOR=cat hg histedit --continue
241 $ HGEDITOR=cat hg histedit --continue
230 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
242 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
231 +4
243 +4
232 ***
244 ***
233 +5.2
245 +5.2
234 ***
246 ***
235 +6
247 +6
236
248
237
249
238
250
239 HG: Enter commit message. Lines beginning with 'HG:' are removed.
251 HG: Enter commit message. Lines beginning with 'HG:' are removed.
240 HG: Leave message empty to abort commit.
252 HG: Leave message empty to abort commit.
241 HG: --
253 HG: --
242 HG: user: test
254 HG: user: test
243 HG: branch 'default'
255 HG: branch 'default'
244 HG: changed file
256 HG: changed file
245 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
257 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
246 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
258 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
247 saved backup bundle to $TESTTMP/fold-with-dropped/.hg/strip-backup/617f94f13c0f-backup.hg (glob)
259 saved backup bundle to $TESTTMP/fold-with-dropped/.hg/strip-backup/617f94f13c0f-backup.hg (glob)
248 $ cd ..
260 $ cd ..
249
261
@@ -1,131 +1,129
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 > graphlog=
5 > graphlog=
6 > histedit=
6 > histedit=
7 > EOF
7 > EOF
8
8
9 $ EDITED="$TESTTMP/editedhistory"
9 $ EDITED="$TESTTMP/editedhistory"
10 $ cat > $EDITED <<EOF
10 $ cat > $EDITED <<EOF
11 > pick 177f92b77385 c
11 > pick 177f92b77385 c
12 > pick 055a42cdd887 d
12 > pick 055a42cdd887 d
13 > pick bfa474341cc9 does not commute with e
13 > pick bfa474341cc9 does not commute with e
14 > pick e860deea161a e
14 > pick e860deea161a e
15 > pick 652413bf663e f
15 > pick 652413bf663e f
16 > EOF
16 > EOF
17 $ initrepo ()
17 $ initrepo ()
18 > {
18 > {
19 > hg init r
19 > hg init r
20 > cd r
20 > cd r
21 > for x in a b c d e f ; do
21 > for x in a b c d e f ; do
22 > echo $x > $x
22 > echo $x > $x
23 > hg add $x
23 > hg add $x
24 > hg ci -m $x
24 > hg ci -m $x
25 > done
25 > done
26 > echo a >> e
26 > echo a >> e
27 > hg ci -m 'does not commute with e'
27 > hg ci -m 'does not commute with e'
28 > cd ..
28 > cd ..
29 > }
29 > }
30
30
31 $ initrepo
31 $ initrepo
32 $ cd r
32 $ cd r
33
33
34 log before edit
34 log before edit
35 $ hg log --graph
35 $ hg log --graph
36 @ changeset: 6:bfa474341cc9
36 @ changeset: 6:bfa474341cc9
37 | tag: tip
37 | tag: tip
38 | user: test
38 | user: test
39 | date: Thu Jan 01 00:00:00 1970 +0000
39 | date: Thu Jan 01 00:00:00 1970 +0000
40 | summary: does not commute with e
40 | summary: does not commute with e
41 |
41 |
42 o changeset: 5:652413bf663e
42 o changeset: 5:652413bf663e
43 | user: test
43 | user: test
44 | date: Thu Jan 01 00:00:00 1970 +0000
44 | date: Thu Jan 01 00:00:00 1970 +0000
45 | summary: f
45 | summary: f
46 |
46 |
47 o changeset: 4:e860deea161a
47 o changeset: 4:e860deea161a
48 | user: test
48 | user: test
49 | date: Thu Jan 01 00:00:00 1970 +0000
49 | date: Thu Jan 01 00:00:00 1970 +0000
50 | summary: e
50 | summary: e
51 |
51 |
52 o changeset: 3:055a42cdd887
52 o changeset: 3:055a42cdd887
53 | user: test
53 | user: test
54 | date: Thu Jan 01 00:00:00 1970 +0000
54 | date: Thu Jan 01 00:00:00 1970 +0000
55 | summary: d
55 | summary: d
56 |
56 |
57 o changeset: 2:177f92b77385
57 o changeset: 2:177f92b77385
58 | user: test
58 | user: test
59 | date: Thu Jan 01 00:00:00 1970 +0000
59 | date: Thu Jan 01 00:00:00 1970 +0000
60 | summary: c
60 | summary: c
61 |
61 |
62 o changeset: 1:d2ae7f538514
62 o changeset: 1:d2ae7f538514
63 | user: test
63 | user: test
64 | date: Thu Jan 01 00:00:00 1970 +0000
64 | date: Thu Jan 01 00:00:00 1970 +0000
65 | summary: b
65 | summary: b
66 |
66 |
67 o changeset: 0:cb9a9f314b8b
67 o changeset: 0:cb9a9f314b8b
68 user: test
68 user: test
69 date: Thu Jan 01 00:00:00 1970 +0000
69 date: Thu Jan 01 00:00:00 1970 +0000
70 summary: a
70 summary: a
71
71
72
72
73 edit the history
73 edit the history
74 $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
74 $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
75 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
75 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
76 1 out of 1 hunks FAILED -- saving rejects to file e.rej
76 remote changed e which local deleted
77 use (c)hanged version or leave (d)eleted? c
78 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 merging e
80 warning: conflicts during merge.
81 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
77 abort: Fix up the change and run hg histedit --continue
82 abort: Fix up the change and run hg histedit --continue
78
83
79 fix up (pre abort)
80 $ echo a > e
81 $ hg add e
82 $ hg histedit --continue 2>&1 | fixbundle
83 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 file e already exists
85 1 out of 1 hunks FAILED -- saving rejects to file e.rej
86 abort: Fix up the change and run hg histedit --continue
87
84
88 abort the edit
85 abort the edit
89 $ hg histedit --abort 2>&1 | fixbundle
86 $ hg histedit --abort 2>&1 | fixbundle
90 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
87 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
91
88
92 log after abort
89 log after abort
90 $ hg resolve -l
93 $ hg log --graph
91 $ hg log --graph
94 @ changeset: 6:bfa474341cc9
92 @ changeset: 6:bfa474341cc9
95 | tag: tip
93 | tag: tip
96 | user: test
94 | user: test
97 | date: Thu Jan 01 00:00:00 1970 +0000
95 | date: Thu Jan 01 00:00:00 1970 +0000
98 | summary: does not commute with e
96 | summary: does not commute with e
99 |
97 |
100 o changeset: 5:652413bf663e
98 o changeset: 5:652413bf663e
101 | user: test
99 | user: test
102 | date: Thu Jan 01 00:00:00 1970 +0000
100 | date: Thu Jan 01 00:00:00 1970 +0000
103 | summary: f
101 | summary: f
104 |
102 |
105 o changeset: 4:e860deea161a
103 o changeset: 4:e860deea161a
106 | user: test
104 | user: test
107 | date: Thu Jan 01 00:00:00 1970 +0000
105 | date: Thu Jan 01 00:00:00 1970 +0000
108 | summary: e
106 | summary: e
109 |
107 |
110 o changeset: 3:055a42cdd887
108 o changeset: 3:055a42cdd887
111 | user: test
109 | user: test
112 | date: Thu Jan 01 00:00:00 1970 +0000
110 | date: Thu Jan 01 00:00:00 1970 +0000
113 | summary: d
111 | summary: d
114 |
112 |
115 o changeset: 2:177f92b77385
113 o changeset: 2:177f92b77385
116 | user: test
114 | user: test
117 | date: Thu Jan 01 00:00:00 1970 +0000
115 | date: Thu Jan 01 00:00:00 1970 +0000
118 | summary: c
116 | summary: c
119 |
117 |
120 o changeset: 1:d2ae7f538514
118 o changeset: 1:d2ae7f538514
121 | user: test
119 | user: test
122 | date: Thu Jan 01 00:00:00 1970 +0000
120 | date: Thu Jan 01 00:00:00 1970 +0000
123 | summary: b
121 | summary: b
124 |
122 |
125 o changeset: 0:cb9a9f314b8b
123 o changeset: 0:cb9a9f314b8b
126 user: test
124 user: test
127 date: Thu Jan 01 00:00:00 1970 +0000
125 date: Thu Jan 01 00:00:00 1970 +0000
128 summary: a
126 summary: a
129
127
130
128
131 $ cd ..
129 $ cd ..
@@ -1,286 +1,292
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 > graphlog=
5 > graphlog=
6 > histedit=
6 > histedit=
7 > EOF
7 > EOF
8
8
9 $ initrepo ()
9 $ initrepo ()
10 > {
10 > {
11 > hg init $1
11 > hg init $1
12 > cd $1
12 > cd $1
13 > for x in a b c d e f ; do
13 > for x in a b c d e f ; do
14 > echo $x$x$x$x$x > $x
14 > echo $x$x$x$x$x > $x
15 > hg add $x
15 > hg add $x
16 > done
16 > done
17 > hg ci -m 'Initial commit'
17 > hg ci -m 'Initial commit'
18 > for x in a b c d e f ; do
18 > for x in a b c d e f ; do
19 > echo $x > $x
19 > echo $x > $x
20 > hg ci -m $x
20 > hg ci -m $x
21 > done
21 > done
22 > echo 'I can haz no commute' > e
22 > echo 'I can haz no commute' > e
23 > hg ci -m 'does not commute with e'
23 > hg ci -m 'does not commute with e'
24 > cd ..
24 > cd ..
25 > }
25 > }
26
26
27 $ initrepo r1
27 $ initrepo r1
28 $ cd r1
28 $ cd r1
29
29
30 Initial generation of the command files
30 Initial generation of the command files
31
31
32 $ EDITED="$TESTTMP/editedhistory"
32 $ EDITED="$TESTTMP/editedhistory"
33 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 3 >> $EDITED
33 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 3 >> $EDITED
34 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 4 >> $EDITED
34 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 4 >> $EDITED
35 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 7 >> $EDITED
35 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 7 >> $EDITED
36 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 5 >> $EDITED
36 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 5 >> $EDITED
37 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 6 >> $EDITED
37 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 6 >> $EDITED
38 $ cat $EDITED
38 $ cat $EDITED
39 pick 65a9a84f33fd 3 c
39 pick 65a9a84f33fd 3 c
40 pick 00f1c5383965 4 d
40 pick 00f1c5383965 4 d
41 pick 39522b764e3d 7 does not commute with e
41 pick 39522b764e3d 7 does not commute with e
42 pick 7b4e2f4b7bcd 5 e
42 pick 7b4e2f4b7bcd 5 e
43 pick 500cac37a696 6 f
43 pick 500cac37a696 6 f
44
44
45 log before edit
45 log before edit
46 $ hg log --graph
46 $ hg log --graph
47 @ changeset: 7:39522b764e3d
47 @ changeset: 7:39522b764e3d
48 | tag: tip
48 | tag: tip
49 | user: test
49 | user: test
50 | date: Thu Jan 01 00:00:00 1970 +0000
50 | date: Thu Jan 01 00:00:00 1970 +0000
51 | summary: does not commute with e
51 | summary: does not commute with e
52 |
52 |
53 o changeset: 6:500cac37a696
53 o changeset: 6:500cac37a696
54 | user: test
54 | user: test
55 | date: Thu Jan 01 00:00:00 1970 +0000
55 | date: Thu Jan 01 00:00:00 1970 +0000
56 | summary: f
56 | summary: f
57 |
57 |
58 o changeset: 5:7b4e2f4b7bcd
58 o changeset: 5:7b4e2f4b7bcd
59 | user: test
59 | user: test
60 | date: Thu Jan 01 00:00:00 1970 +0000
60 | date: Thu Jan 01 00:00:00 1970 +0000
61 | summary: e
61 | summary: e
62 |
62 |
63 o changeset: 4:00f1c5383965
63 o changeset: 4:00f1c5383965
64 | user: test
64 | user: test
65 | date: Thu Jan 01 00:00:00 1970 +0000
65 | date: Thu Jan 01 00:00:00 1970 +0000
66 | summary: d
66 | summary: d
67 |
67 |
68 o changeset: 3:65a9a84f33fd
68 o changeset: 3:65a9a84f33fd
69 | user: test
69 | user: test
70 | date: Thu Jan 01 00:00:00 1970 +0000
70 | date: Thu Jan 01 00:00:00 1970 +0000
71 | summary: c
71 | summary: c
72 |
72 |
73 o changeset: 2:da6535b52e45
73 o changeset: 2:da6535b52e45
74 | user: test
74 | user: test
75 | date: Thu Jan 01 00:00:00 1970 +0000
75 | date: Thu Jan 01 00:00:00 1970 +0000
76 | summary: b
76 | summary: b
77 |
77 |
78 o changeset: 1:c1f09da44841
78 o changeset: 1:c1f09da44841
79 | user: test
79 | user: test
80 | date: Thu Jan 01 00:00:00 1970 +0000
80 | date: Thu Jan 01 00:00:00 1970 +0000
81 | summary: a
81 | summary: a
82 |
82 |
83 o changeset: 0:1715188a53c7
83 o changeset: 0:1715188a53c7
84 user: test
84 user: test
85 date: Thu Jan 01 00:00:00 1970 +0000
85 date: Thu Jan 01 00:00:00 1970 +0000
86 summary: Initial commit
86 summary: Initial commit
87
87
88
88
89 edit the history
89 edit the history
90 $ HGEDITOR="cat \"$EDITED\" > " hg histedit 3 2>&1 | fixbundle
90 $ HGEDITOR="cat \"$EDITED\" > " hg histedit 3 2>&1 | fixbundle
91 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
91 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
92 patching file e
92 merging e
93 Hunk #1 FAILED at 0
93 warning: conflicts during merge.
94 1 out of 1 hunks FAILED -- saving rejects to file e.rej
94 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
95 abort: Fix up the change and run hg histedit --continue
95 abort: Fix up the change and run hg histedit --continue
96
96
97 abort the edit
97 abort the edit
98 $ hg histedit --abort 2>&1 | fixbundle
98 $ hg histedit --abort 2>&1 | fixbundle
99 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
99 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
100
100
101
101
102 second edit set
102 second edit set
103
103
104 $ hg log --graph
104 $ hg log --graph
105 @ changeset: 7:39522b764e3d
105 @ changeset: 7:39522b764e3d
106 | tag: tip
106 | tag: tip
107 | user: test
107 | user: test
108 | date: Thu Jan 01 00:00:00 1970 +0000
108 | date: Thu Jan 01 00:00:00 1970 +0000
109 | summary: does not commute with e
109 | summary: does not commute with e
110 |
110 |
111 o changeset: 6:500cac37a696
111 o changeset: 6:500cac37a696
112 | user: test
112 | user: test
113 | date: Thu Jan 01 00:00:00 1970 +0000
113 | date: Thu Jan 01 00:00:00 1970 +0000
114 | summary: f
114 | summary: f
115 |
115 |
116 o changeset: 5:7b4e2f4b7bcd
116 o changeset: 5:7b4e2f4b7bcd
117 | user: test
117 | user: test
118 | date: Thu Jan 01 00:00:00 1970 +0000
118 | date: Thu Jan 01 00:00:00 1970 +0000
119 | summary: e
119 | summary: e
120 |
120 |
121 o changeset: 4:00f1c5383965
121 o changeset: 4:00f1c5383965
122 | user: test
122 | user: test
123 | date: Thu Jan 01 00:00:00 1970 +0000
123 | date: Thu Jan 01 00:00:00 1970 +0000
124 | summary: d
124 | summary: d
125 |
125 |
126 o changeset: 3:65a9a84f33fd
126 o changeset: 3:65a9a84f33fd
127 | user: test
127 | user: test
128 | date: Thu Jan 01 00:00:00 1970 +0000
128 | date: Thu Jan 01 00:00:00 1970 +0000
129 | summary: c
129 | summary: c
130 |
130 |
131 o changeset: 2:da6535b52e45
131 o changeset: 2:da6535b52e45
132 | user: test
132 | user: test
133 | date: Thu Jan 01 00:00:00 1970 +0000
133 | date: Thu Jan 01 00:00:00 1970 +0000
134 | summary: b
134 | summary: b
135 |
135 |
136 o changeset: 1:c1f09da44841
136 o changeset: 1:c1f09da44841
137 | user: test
137 | user: test
138 | date: Thu Jan 01 00:00:00 1970 +0000
138 | date: Thu Jan 01 00:00:00 1970 +0000
139 | summary: a
139 | summary: a
140 |
140 |
141 o changeset: 0:1715188a53c7
141 o changeset: 0:1715188a53c7
142 user: test
142 user: test
143 date: Thu Jan 01 00:00:00 1970 +0000
143 date: Thu Jan 01 00:00:00 1970 +0000
144 summary: Initial commit
144 summary: Initial commit
145
145
146
146
147 edit the history
147 edit the history
148 $ HGEDITOR="cat \"$EDITED\" > " hg histedit 3 2>&1 | fixbundle
148 $ HGEDITOR="cat \"$EDITED\" > " hg histedit 3 2>&1 | fixbundle
149 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
149 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 patching file e
150 merging e
151 Hunk #1 FAILED at 0
151 warning: conflicts during merge.
152 1 out of 1 hunks FAILED -- saving rejects to file e.rej
152 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
153 abort: Fix up the change and run hg histedit --continue
153 abort: Fix up the change and run hg histedit --continue
154
154
155 fix up
155 fix up
156 $ echo 'I can haz no commute' > e
156 $ echo 'I can haz no commute' > e
157 $ hg resolve --mark e
157 $ hg histedit --continue 2>&1 | fixbundle
158 $ hg histedit --continue 2>&1 | fixbundle
158 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
159 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
159 patching file e
160 merging e
160 Hunk #1 FAILED at 0
161 warning: conflicts during merge.
161 1 out of 1 hunks FAILED -- saving rejects to file e.rej
162 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
162 abort: Fix up the change and run hg histedit --continue
163 abort: Fix up the change and run hg histedit --continue
163
164
164 just continue this time
165 just continue this time
166 $ hg revert -r 'p1()' e
167 $ hg resolve --mark e
165 $ hg histedit --continue 2>&1 | fixbundle
168 $ hg histedit --continue 2>&1 | fixbundle
166 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
169 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
167 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
170 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
168
171
169 log after edit
172 log after edit
170 $ hg log --graph
173 $ hg log --graph
171 @ changeset: 6:8e082d1a72ea
174 @ changeset: 6:8e082d1a72ea
172 | tag: tip
175 | tag: tip
173 | user: test
176 | user: test
174 | date: Thu Jan 01 00:00:00 1970 +0000
177 | date: Thu Jan 01 00:00:00 1970 +0000
175 | summary: f
178 | summary: f
176 |
179 |
177 o changeset: 5:13b04d775b81
180 o changeset: 5:13b04d775b81
178 | user: test
181 | user: test
179 | date: Thu Jan 01 00:00:00 1970 +0000
182 | date: Thu Jan 01 00:00:00 1970 +0000
180 | summary: does not commute with e
183 | summary: does not commute with e
181 |
184 |
182 o changeset: 4:00f1c5383965
185 o changeset: 4:00f1c5383965
183 | user: test
186 | user: test
184 | date: Thu Jan 01 00:00:00 1970 +0000
187 | date: Thu Jan 01 00:00:00 1970 +0000
185 | summary: d
188 | summary: d
186 |
189 |
187 o changeset: 3:65a9a84f33fd
190 o changeset: 3:65a9a84f33fd
188 | user: test
191 | user: test
189 | date: Thu Jan 01 00:00:00 1970 +0000
192 | date: Thu Jan 01 00:00:00 1970 +0000
190 | summary: c
193 | summary: c
191 |
194 |
192 o changeset: 2:da6535b52e45
195 o changeset: 2:da6535b52e45
193 | user: test
196 | user: test
194 | date: Thu Jan 01 00:00:00 1970 +0000
197 | date: Thu Jan 01 00:00:00 1970 +0000
195 | summary: b
198 | summary: b
196 |
199 |
197 o changeset: 1:c1f09da44841
200 o changeset: 1:c1f09da44841
198 | user: test
201 | user: test
199 | date: Thu Jan 01 00:00:00 1970 +0000
202 | date: Thu Jan 01 00:00:00 1970 +0000
200 | summary: a
203 | summary: a
201 |
204 |
202 o changeset: 0:1715188a53c7
205 o changeset: 0:1715188a53c7
203 user: test
206 user: test
204 date: Thu Jan 01 00:00:00 1970 +0000
207 date: Thu Jan 01 00:00:00 1970 +0000
205 summary: Initial commit
208 summary: Initial commit
206
209
207
210
208 start over
211 start over
209
212
210 $ cd ..
213 $ cd ..
211
214
212 $ initrepo r2
215 $ initrepo r2
213 $ cd r2
216 $ cd r2
214 $ rm $EDITED
217 $ rm $EDITED
215 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 3 >> $EDITED
218 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 3 >> $EDITED
216 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 4 >> $EDITED
219 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 4 >> $EDITED
217 $ hg log --template 'mess {node|short} {rev} {desc}\n' -r 7 >> $EDITED
220 $ hg log --template 'mess {node|short} {rev} {desc}\n' -r 7 >> $EDITED
218 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 5 >> $EDITED
221 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 5 >> $EDITED
219 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 6 >> $EDITED
222 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 6 >> $EDITED
220 $ cat $EDITED
223 $ cat $EDITED
221 pick 65a9a84f33fd 3 c
224 pick 65a9a84f33fd 3 c
222 pick 00f1c5383965 4 d
225 pick 00f1c5383965 4 d
223 mess 39522b764e3d 7 does not commute with e
226 mess 39522b764e3d 7 does not commute with e
224 pick 7b4e2f4b7bcd 5 e
227 pick 7b4e2f4b7bcd 5 e
225 pick 500cac37a696 6 f
228 pick 500cac37a696 6 f
226
229
227 edit the history, this time with a fold action
230 edit the history, this time with a fold action
228 $ HGEDITOR="cat \"$EDITED\" > " hg histedit 3 2>&1 | fixbundle
231 $ HGEDITOR="cat \"$EDITED\" > " hg histedit 3 2>&1 | fixbundle
229 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
232 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
230 patching file e
233 merging e
231 Hunk #1 FAILED at 0
234 warning: conflicts during merge.
232 1 out of 1 hunks FAILED -- saving rejects to file e.rej
235 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
233 abort: Fix up the change and run hg histedit --continue
236 abort: Fix up the change and run hg histedit --continue
234
237
235 $ echo 'I can haz no commute' > e
238 $ echo 'I can haz no commute' > e
239 $ hg resolve --mark e
236 $ HGEDITOR="cat \"$EDITED\" > " hg histedit --continue 2>&1 | fixbundle
240 $ HGEDITOR="cat \"$EDITED\" > " hg histedit --continue 2>&1 | fixbundle
237 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
241 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
238 patching file e
242 merging e
239 Hunk #1 FAILED at 0
243 warning: conflicts during merge.
240 1 out of 1 hunks FAILED -- saving rejects to file e.rej
244 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
241 abort: Fix up the change and run hg histedit --continue
245 abort: Fix up the change and run hg histedit --continue
242 second edit also fails, but just continue
246 second edit also fails, but just continue
247 $ hg revert -r 'p1()' e
248 $ hg resolve --mark e
243 $ hg histedit --continue 2>&1 | fixbundle
249 $ hg histedit --continue 2>&1 | fixbundle
244 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
250 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
245 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
251 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
246
252
247 post message fix
253 post message fix
248 $ hg log --graph
254 $ hg log --graph
249 @ changeset: 6:f14da722aa4b
255 @ changeset: 6:f14da722aa4b
250 | tag: tip
256 | tag: tip
251 | user: test
257 | user: test
252 | date: Thu Jan 01 00:00:00 1970 +0000
258 | date: Thu Jan 01 00:00:00 1970 +0000
253 | summary: f
259 | summary: f
254 |
260 |
255 o changeset: 5:382ff1adf0ed
261 o changeset: 5:382ff1adf0ed
256 | user: test
262 | user: test
257 | date: Thu Jan 01 00:00:00 1970 +0000
263 | date: Thu Jan 01 00:00:00 1970 +0000
258 | summary: pick 65a9a84f33fd 3 c
264 | summary: pick 65a9a84f33fd 3 c
259 |
265 |
260 o changeset: 4:00f1c5383965
266 o changeset: 4:00f1c5383965
261 | user: test
267 | user: test
262 | date: Thu Jan 01 00:00:00 1970 +0000
268 | date: Thu Jan 01 00:00:00 1970 +0000
263 | summary: d
269 | summary: d
264 |
270 |
265 o changeset: 3:65a9a84f33fd
271 o changeset: 3:65a9a84f33fd
266 | user: test
272 | user: test
267 | date: Thu Jan 01 00:00:00 1970 +0000
273 | date: Thu Jan 01 00:00:00 1970 +0000
268 | summary: c
274 | summary: c
269 |
275 |
270 o changeset: 2:da6535b52e45
276 o changeset: 2:da6535b52e45
271 | user: test
277 | user: test
272 | date: Thu Jan 01 00:00:00 1970 +0000
278 | date: Thu Jan 01 00:00:00 1970 +0000
273 | summary: b
279 | summary: b
274 |
280 |
275 o changeset: 1:c1f09da44841
281 o changeset: 1:c1f09da44841
276 | user: test
282 | user: test
277 | date: Thu Jan 01 00:00:00 1970 +0000
283 | date: Thu Jan 01 00:00:00 1970 +0000
278 | summary: a
284 | summary: a
279 |
285 |
280 o changeset: 0:1715188a53c7
286 o changeset: 0:1715188a53c7
281 user: test
287 user: test
282 date: Thu Jan 01 00:00:00 1970 +0000
288 date: Thu Jan 01 00:00:00 1970 +0000
283 summary: Initial commit
289 summary: Initial commit
284
290
285
291
286 $ cd ..
292 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now