Mercurial > hg
annotate hgext/split.py @ 44719:c482e2fe444c
phabricator: prevent posting obsolete commits
I don't see why this would be useful in the first place. But I had a coworker
submit a single commit that was not a branch head, and the result was to orphan
its child and keep the original commit visible. He then did up arrow + Enter,
and it happily created a new review (since the URL isn't amended into the
original commit specified on the command line) and a new successor, resulting in
a local divergence. I'd like to fix the issue with creating orphans, but this
is simple enough to prevent on its own.
Differential Revision: https://phab.mercurial-scm.org/D8437
author | Matt Harbison <matt_harbison@yahoo.com> |
---|---|
date | Sun, 12 Apr 2020 13:11:42 -0400 |
parents | 2349a60f33db |
children | 84ce9ffc95ad |
rev | line source |
---|---|
35455 | 1 # split.py - split a changeset into smaller ones |
2 # | |
3 # Copyright 2015 Laurent Charignon <lcharignon@fb.com> | |
4 # Copyright 2017 Facebook, Inc. | |
5 # | |
6 # This software may be used and distributed according to the terms of the | |
7 # GNU General Public License version 2 or any later version. | |
8 """command to split a changeset into smaller ones (EXPERIMENTAL)""" | |
9 | |
10 from __future__ import absolute_import | |
11 | |
12 from mercurial.i18n import _ | |
13 | |
14 from mercurial.node import ( | |
15 nullid, | |
16 short, | |
17 ) | |
18 | |
19 from mercurial import ( | |
20 bookmarks, | |
21 cmdutil, | |
22 commands, | |
23 error, | |
24 hg, | |
36400
7b86aa31b004
py3: fix handling of keyword arguments at more places
Pulkit Goyal <7895pulkit@gmail.com>
parents:
35455
diff
changeset
|
25 pycompat, |
35455 | 26 registrar, |
27 revsetlang, | |
43935
2349a60f33db
split: use rewriteutil.precheck() instead of reimplementing it
Martin von Zweigbergk <martinvonz@google.com>
parents:
43641
diff
changeset
|
28 rewriteutil, |
35455 | 29 scmutil, |
30 ) | |
31 | |
32 # allow people to use split without explicitly enabling rebase extension | |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
33 from . import rebase |
35455 | 34 |
35 cmdtable = {} | |
36 command = registrar.command(cmdtable) | |
37 | |
38 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for | |
39 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should | |
40 # be specifying the version(s) of Mercurial they are tested with, or | |
41 # leave the attribute unspecified. | |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
42 testedwith = b'ships-with-hg-core' |
35455 | 43 |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
44 |
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
45 @command( |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
46 b'split', |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
47 [ |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
48 (b'r', b'rev', b'', _(b"revision to split"), _(b'REV')), |
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
49 (b'', b'rebase', True, _(b'rebase descendants after split')), |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
50 ] |
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
51 + cmdutil.commitopts2, |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
52 _(b'hg split [--no-rebase] [[-r] REV]'), |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
53 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, |
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
54 helpbasic=True, |
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
55 ) |
35455 | 56 def split(ui, repo, *revs, **opts): |
57 """split a changeset into smaller ones | |
58 | |
59 Repeatedly prompt changes and commit message for new changesets until there | |
60 is nothing left in the original changeset. | |
61 | |
62 If --rev was not given, split the working directory parent. | |
63 | |
64 By default, rebase connected non-obsoleted descendants onto the new | |
65 changeset. Use --no-rebase to avoid the rebase. | |
66 """ | |
38069
5ba0cf22e4d0
py3: fix kwargs handling in hgext/split.py
Pulkit Goyal <7895pulkit@gmail.com>
parents:
36408
diff
changeset
|
67 opts = pycompat.byteskwargs(opts) |
35455 | 68 revlist = [] |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
69 if opts.get(b'rev'): |
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
70 revlist.append(opts.get(b'rev')) |
35455 | 71 revlist.extend(revs) |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
72 with repo.wlock(), repo.lock(), repo.transaction(b'split') as tr: |
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
73 revs = scmutil.revrange(repo, revlist or [b'.']) |
35455 | 74 if len(revs) > 1: |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
75 raise error.Abort(_(b'cannot split multiple revisions')) |
35455 | 76 |
77 rev = revs.first() | |
78 ctx = repo[rev] | |
43935
2349a60f33db
split: use rewriteutil.precheck() instead of reimplementing it
Martin von Zweigbergk <martinvonz@google.com>
parents:
43641
diff
changeset
|
79 # Handle nullid specially here (instead of leaving for precheck() |
2349a60f33db
split: use rewriteutil.precheck() instead of reimplementing it
Martin von Zweigbergk <martinvonz@google.com>
parents:
43641
diff
changeset
|
80 # below) so we get a nicer message and error code. |
35455 | 81 if rev is None or ctx.node() == nullid: |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
82 ui.status(_(b'nothing to split\n')) |
35455 | 83 return 1 |
84 if ctx.node() is None: | |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
85 raise error.Abort(_(b'cannot split working directory')) |
35455 | 86 |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
87 if opts.get(b'rebase'): |
35455 | 88 # Skip obsoleted descendants and their descendants so the rebase |
89 # won't cause conflicts for sure. | |
43935
2349a60f33db
split: use rewriteutil.precheck() instead of reimplementing it
Martin von Zweigbergk <martinvonz@google.com>
parents:
43641
diff
changeset
|
90 descendants = list(repo.revs(b'(%d::) - (%d)', rev, rev)) |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
91 torebase = list( |
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
92 repo.revs( |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
93 b'%ld - (%ld & obsolete())::', descendants, descendants |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
94 ) |
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
95 ) |
35455 | 96 else: |
43935
2349a60f33db
split: use rewriteutil.precheck() instead of reimplementing it
Martin von Zweigbergk <martinvonz@google.com>
parents:
43641
diff
changeset
|
97 torebase = [] |
2349a60f33db
split: use rewriteutil.precheck() instead of reimplementing it
Martin von Zweigbergk <martinvonz@google.com>
parents:
43641
diff
changeset
|
98 rewriteutil.precheck(repo, [rev] + torebase, b'split') |
35455 | 99 |
100 if len(ctx.parents()) > 1: | |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
101 raise error.Abort(_(b'cannot split a merge changeset')) |
35455 | 102 |
103 cmdutil.bailifchanged(repo) | |
104 | |
105 # Deactivate bookmark temporarily so it won't get moved unintentionally | |
106 bname = repo._activebookmark | |
107 if bname and repo._bookmarks[bname] != ctx.node(): | |
108 bookmarks.deactivate(repo) | |
109 | |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
110 wnode = repo[b'.'].node() |
35455 | 111 top = None |
112 try: | |
113 top = dosplit(ui, repo, tr, ctx, opts) | |
114 finally: | |
115 # top is None: split failed, need update --clean recovery. | |
116 # wnode == ctx.node(): wnode split, no need to update. | |
117 if top is None or wnode != ctx.node(): | |
118 hg.clean(repo, wnode, show_stats=False) | |
119 if bname: | |
120 bookmarks.activate(repo, bname) | |
121 if torebase and top: | |
122 dorebase(ui, repo, torebase, top) | |
123 | |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
124 |
35455 | 125 def dosplit(ui, repo, tr, ctx, opts): |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
126 committed = [] # [ctx] |
35455 | 127 |
128 # Set working parent to ctx.p1(), and keep working copy as ctx's content | |
41966
42e2c7c52e1b
split: use the new movedirstate() we now have in scmutil
Martin von Zweigbergk <martinvonz@google.com>
parents:
40295
diff
changeset
|
129 if ctx.node() != repo.dirstate.p1(): |
42e2c7c52e1b
split: use the new movedirstate() we now have in scmutil
Martin von Zweigbergk <martinvonz@google.com>
parents:
40295
diff
changeset
|
130 hg.clean(repo, ctx.node(), show_stats=False) |
42e2c7c52e1b
split: use the new movedirstate() we now have in scmutil
Martin von Zweigbergk <martinvonz@google.com>
parents:
40295
diff
changeset
|
131 with repo.dirstate.parentchange(): |
42e2c7c52e1b
split: use the new movedirstate() we now have in scmutil
Martin von Zweigbergk <martinvonz@google.com>
parents:
40295
diff
changeset
|
132 scmutil.movedirstate(repo, ctx.p1()) |
35455 | 133 |
134 # Any modified, added, removed, deleted result means split is incomplete | |
43641
705738def50c
split: use field names instead of field numbers on scmutil.status
Augie Fackler <augie@google.com>
parents:
43117
diff
changeset
|
135 def incomplete(repo): |
705738def50c
split: use field names instead of field numbers on scmutil.status
Augie Fackler <augie@google.com>
parents:
43117
diff
changeset
|
136 st = repo.status() |
705738def50c
split: use field names instead of field numbers on scmutil.status
Augie Fackler <augie@google.com>
parents:
43117
diff
changeset
|
137 return any((st.modified, st.added, st.removed, st.deleted)) |
35455 | 138 |
139 # Main split loop | |
140 while incomplete(repo): | |
141 if committed: | |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
142 header = _( |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
143 b'HG: Splitting %s. So far it has been split into:\n' |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
144 ) % short(ctx.node()) |
35455 | 145 for c in committed: |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
146 firstline = c.description().split(b'\n', 1)[0] |
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
147 header += _(b'HG: - %s: %s\n') % (short(c.node()), firstline) |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
148 header += _( |
43117
8ff1ecfadcd1
cleanup: join string literals that are already on one line
Martin von Zweigbergk <martinvonz@google.com>
parents:
43077
diff
changeset
|
149 b'HG: Write commit message for the next split changeset.\n' |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
150 ) |
35455 | 151 else: |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
152 header = _( |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
153 b'HG: Splitting %s. Write commit message for the ' |
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
154 b'first split changeset.\n' |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
155 ) % short(ctx.node()) |
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
156 opts.update( |
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
157 { |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
158 b'edit': True, |
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
159 b'interactive': True, |
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
160 b'message': header + ctx.description(), |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
161 } |
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
162 ) |
36400
7b86aa31b004
py3: fix handling of keyword arguments at more places
Pulkit Goyal <7895pulkit@gmail.com>
parents:
35455
diff
changeset
|
163 commands.commit(ui, repo, **pycompat.strkwargs(opts)) |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
164 newctx = repo[b'.'] |
35455 | 165 committed.append(newctx) |
166 | |
167 if not committed: | |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
168 raise error.Abort(_(b'cannot split an empty revision')) |
35455 | 169 |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
170 scmutil.cleanupnodes( |
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
171 repo, |
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
172 {ctx.node(): [c.node() for c in committed]}, |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
173 operation=b'split', |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
174 fixphase=True, |
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
175 ) |
35455 | 176 |
177 return committed[-1] | |
178 | |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
179 |
36408
83bade6206d4
split: use ctx.rev() instead of %d % ctx
Gregory Szorc <gregory.szorc@gmail.com>
parents:
36400
diff
changeset
|
180 def dorebase(ui, repo, src, destctx): |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
181 rebase.rebase( |
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
182 ui, |
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
183 repo, |
43077
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
184 rev=[revsetlang.formatspec(b'%ld', src)], |
687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Augie Fackler <augie@google.com>
parents:
43076
diff
changeset
|
185 dest=revsetlang.formatspec(b'%d', destctx.rev()), |
43076
2372284d9457
formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents:
41966
diff
changeset
|
186 ) |