##// END OF EJS Templates
split: use field names instead of field numbers on scmutil.status...
Augie Fackler -
r44040:705738de default
parent child Browse files
Show More
@@ -1,204 +1,206 b''
1 1 # split.py - split a changeset into smaller ones
2 2 #
3 3 # Copyright 2015 Laurent Charignon <lcharignon@fb.com>
4 4 # Copyright 2017 Facebook, Inc.
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8 """command to split a changeset into smaller ones (EXPERIMENTAL)"""
9 9
10 10 from __future__ import absolute_import
11 11
12 12 from mercurial.i18n import _
13 13
14 14 from mercurial.node import (
15 15 nullid,
16 16 short,
17 17 )
18 18
19 19 from mercurial import (
20 20 bookmarks,
21 21 cmdutil,
22 22 commands,
23 23 error,
24 24 hg,
25 25 obsolete,
26 26 phases,
27 27 pycompat,
28 28 registrar,
29 29 revsetlang,
30 30 scmutil,
31 31 )
32 32
33 33 # allow people to use split without explicitly enabling rebase extension
34 34 from . import rebase
35 35
36 36 cmdtable = {}
37 37 command = registrar.command(cmdtable)
38 38
39 39 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
40 40 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
41 41 # be specifying the version(s) of Mercurial they are tested with, or
42 42 # leave the attribute unspecified.
43 43 testedwith = b'ships-with-hg-core'
44 44
45 45
46 46 @command(
47 47 b'split',
48 48 [
49 49 (b'r', b'rev', b'', _(b"revision to split"), _(b'REV')),
50 50 (b'', b'rebase', True, _(b'rebase descendants after split')),
51 51 ]
52 52 + cmdutil.commitopts2,
53 53 _(b'hg split [--no-rebase] [[-r] REV]'),
54 54 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
55 55 helpbasic=True,
56 56 )
57 57 def split(ui, repo, *revs, **opts):
58 58 """split a changeset into smaller ones
59 59
60 60 Repeatedly prompt changes and commit message for new changesets until there
61 61 is nothing left in the original changeset.
62 62
63 63 If --rev was not given, split the working directory parent.
64 64
65 65 By default, rebase connected non-obsoleted descendants onto the new
66 66 changeset. Use --no-rebase to avoid the rebase.
67 67 """
68 68 opts = pycompat.byteskwargs(opts)
69 69 revlist = []
70 70 if opts.get(b'rev'):
71 71 revlist.append(opts.get(b'rev'))
72 72 revlist.extend(revs)
73 73 with repo.wlock(), repo.lock(), repo.transaction(b'split') as tr:
74 74 revs = scmutil.revrange(repo, revlist or [b'.'])
75 75 if len(revs) > 1:
76 76 raise error.Abort(_(b'cannot split multiple revisions'))
77 77
78 78 rev = revs.first()
79 79 ctx = repo[rev]
80 80 if rev is None or ctx.node() == nullid:
81 81 ui.status(_(b'nothing to split\n'))
82 82 return 1
83 83 if ctx.node() is None:
84 84 raise error.Abort(_(b'cannot split working directory'))
85 85
86 86 # rewriteutil.precheck is not very useful here because:
87 87 # 1. null check is done above and it's more friendly to return 1
88 88 # instead of abort
89 89 # 2. mergestate check is done below by cmdutil.bailifchanged
90 90 # 3. unstable check is more complex here because of --rebase
91 91 #
92 92 # So only "public" check is useful and it's checked directly here.
93 93 if ctx.phase() == phases.public:
94 94 raise error.Abort(
95 95 _(b'cannot split public changeset'),
96 96 hint=_(b"see 'hg help phases' for details"),
97 97 )
98 98
99 99 descendants = list(repo.revs(b'(%d::) - (%d)', rev, rev))
100 100 alloworphaned = obsolete.isenabled(repo, obsolete.allowunstableopt)
101 101 if opts.get(b'rebase'):
102 102 # Skip obsoleted descendants and their descendants so the rebase
103 103 # won't cause conflicts for sure.
104 104 torebase = list(
105 105 repo.revs(
106 106 b'%ld - (%ld & obsolete())::', descendants, descendants
107 107 )
108 108 )
109 109 if not alloworphaned and len(torebase) != len(descendants):
110 110 raise error.Abort(
111 111 _(b'split would leave orphaned changesets behind')
112 112 )
113 113 else:
114 114 if not alloworphaned and descendants:
115 115 raise error.Abort(
116 116 _(b'cannot split changeset with children without rebase')
117 117 )
118 118 torebase = ()
119 119
120 120 if len(ctx.parents()) > 1:
121 121 raise error.Abort(_(b'cannot split a merge changeset'))
122 122
123 123 cmdutil.bailifchanged(repo)
124 124
125 125 # Deactivate bookmark temporarily so it won't get moved unintentionally
126 126 bname = repo._activebookmark
127 127 if bname and repo._bookmarks[bname] != ctx.node():
128 128 bookmarks.deactivate(repo)
129 129
130 130 wnode = repo[b'.'].node()
131 131 top = None
132 132 try:
133 133 top = dosplit(ui, repo, tr, ctx, opts)
134 134 finally:
135 135 # top is None: split failed, need update --clean recovery.
136 136 # wnode == ctx.node(): wnode split, no need to update.
137 137 if top is None or wnode != ctx.node():
138 138 hg.clean(repo, wnode, show_stats=False)
139 139 if bname:
140 140 bookmarks.activate(repo, bname)
141 141 if torebase and top:
142 142 dorebase(ui, repo, torebase, top)
143 143
144 144
145 145 def dosplit(ui, repo, tr, ctx, opts):
146 146 committed = [] # [ctx]
147 147
148 148 # Set working parent to ctx.p1(), and keep working copy as ctx's content
149 149 if ctx.node() != repo.dirstate.p1():
150 150 hg.clean(repo, ctx.node(), show_stats=False)
151 151 with repo.dirstate.parentchange():
152 152 scmutil.movedirstate(repo, ctx.p1())
153 153
154 154 # Any modified, added, removed, deleted result means split is incomplete
155 incomplete = lambda repo: any(repo.status()[:4])
155 def incomplete(repo):
156 st = repo.status()
157 return any((st.modified, st.added, st.removed, st.deleted))
156 158
157 159 # Main split loop
158 160 while incomplete(repo):
159 161 if committed:
160 162 header = _(
161 163 b'HG: Splitting %s. So far it has been split into:\n'
162 164 ) % short(ctx.node())
163 165 for c in committed:
164 166 firstline = c.description().split(b'\n', 1)[0]
165 167 header += _(b'HG: - %s: %s\n') % (short(c.node()), firstline)
166 168 header += _(
167 169 b'HG: Write commit message for the next split changeset.\n'
168 170 )
169 171 else:
170 172 header = _(
171 173 b'HG: Splitting %s. Write commit message for the '
172 174 b'first split changeset.\n'
173 175 ) % short(ctx.node())
174 176 opts.update(
175 177 {
176 178 b'edit': True,
177 179 b'interactive': True,
178 180 b'message': header + ctx.description(),
179 181 }
180 182 )
181 183 commands.commit(ui, repo, **pycompat.strkwargs(opts))
182 184 newctx = repo[b'.']
183 185 committed.append(newctx)
184 186
185 187 if not committed:
186 188 raise error.Abort(_(b'cannot split an empty revision'))
187 189
188 190 scmutil.cleanupnodes(
189 191 repo,
190 192 {ctx.node(): [c.node() for c in committed]},
191 193 operation=b'split',
192 194 fixphase=True,
193 195 )
194 196
195 197 return committed[-1]
196 198
197 199
198 200 def dorebase(ui, repo, src, destctx):
199 201 rebase.rebase(
200 202 ui,
201 203 repo,
202 204 rev=[revsetlang.formatspec(b'%ld', src)],
203 205 dest=revsetlang.formatspec(b'%d', destctx.rev()),
204 206 )
General Comments 0
You need to be logged in to leave comments. Login now