##// END OF EJS Templates
split: use ctx.rev() instead of %d % ctx...
Gregory Szorc -
r36426:83bade62 default
parent child Browse files
Show More
@@ -1,178 +1,178 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 (
35 35 rebase,
36 36 )
37 37
38 38 cmdtable = {}
39 39 command = registrar.command(cmdtable)
40 40
41 41 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
42 42 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
43 43 # be specifying the version(s) of Mercurial they are tested with, or
44 44 # leave the attribute unspecified.
45 45 testedwith = 'ships-with-hg-core'
46 46
47 47 @command('^split',
48 48 [('r', 'rev', '', _("revision to split"), _('REV')),
49 49 ('', 'rebase', True, _('rebase descendants after split')),
50 50 ] + cmdutil.commitopts2,
51 51 _('hg split [--no-rebase] [[-r] REV]'))
52 52 def split(ui, repo, *revs, **opts):
53 53 """split a changeset into smaller ones
54 54
55 55 Repeatedly prompt changes and commit message for new changesets until there
56 56 is nothing left in the original changeset.
57 57
58 58 If --rev was not given, split the working directory parent.
59 59
60 60 By default, rebase connected non-obsoleted descendants onto the new
61 61 changeset. Use --no-rebase to avoid the rebase.
62 62 """
63 63 revlist = []
64 64 if opts.get('rev'):
65 65 revlist.append(opts.get('rev'))
66 66 revlist.extend(revs)
67 67 with repo.wlock(), repo.lock(), repo.transaction('split') as tr:
68 68 revs = scmutil.revrange(repo, revlist or ['.'])
69 69 if len(revs) > 1:
70 70 raise error.Abort(_('cannot split multiple revisions'))
71 71
72 72 rev = revs.first()
73 73 ctx = repo[rev]
74 74 if rev is None or ctx.node() == nullid:
75 75 ui.status(_('nothing to split\n'))
76 76 return 1
77 77 if ctx.node() is None:
78 78 raise error.Abort(_('cannot split working directory'))
79 79
80 80 # rewriteutil.precheck is not very useful here because:
81 81 # 1. null check is done above and it's more friendly to return 1
82 82 # instead of abort
83 83 # 2. mergestate check is done below by cmdutil.bailifchanged
84 84 # 3. unstable check is more complex here because of --rebase
85 85 #
86 86 # So only "public" check is useful and it's checked directly here.
87 87 if ctx.phase() == phases.public:
88 88 raise error.Abort(_('cannot split public changeset'),
89 89 hint=_("see 'hg help phases' for details"))
90 90
91 91 descendants = list(repo.revs('(%d::) - (%d)', rev, rev))
92 92 alloworphaned = obsolete.isenabled(repo, obsolete.allowunstableopt)
93 93 if opts.get('rebase'):
94 94 # Skip obsoleted descendants and their descendants so the rebase
95 95 # won't cause conflicts for sure.
96 96 torebase = list(repo.revs('%ld - (%ld & obsolete())::',
97 97 descendants, descendants))
98 98 if not alloworphaned and len(torebase) != len(descendants):
99 99 raise error.Abort(_('split would leave orphaned changesets '
100 100 'behind'))
101 101 else:
102 102 if not alloworphaned and descendants:
103 103 raise error.Abort(
104 104 _('cannot split changeset with children without rebase'))
105 105 torebase = ()
106 106
107 107 if len(ctx.parents()) > 1:
108 108 raise error.Abort(_('cannot split a merge changeset'))
109 109
110 110 cmdutil.bailifchanged(repo)
111 111
112 112 # Deactivate bookmark temporarily so it won't get moved unintentionally
113 113 bname = repo._activebookmark
114 114 if bname and repo._bookmarks[bname] != ctx.node():
115 115 bookmarks.deactivate(repo)
116 116
117 117 wnode = repo['.'].node()
118 118 top = None
119 119 try:
120 120 top = dosplit(ui, repo, tr, ctx, opts)
121 121 finally:
122 122 # top is None: split failed, need update --clean recovery.
123 123 # wnode == ctx.node(): wnode split, no need to update.
124 124 if top is None or wnode != ctx.node():
125 125 hg.clean(repo, wnode, show_stats=False)
126 126 if bname:
127 127 bookmarks.activate(repo, bname)
128 128 if torebase and top:
129 129 dorebase(ui, repo, torebase, top)
130 130
131 131 def dosplit(ui, repo, tr, ctx, opts):
132 132 committed = [] # [ctx]
133 133
134 134 # Set working parent to ctx.p1(), and keep working copy as ctx's content
135 135 # NOTE: if we can have "update without touching working copy" API, the
136 136 # revert step could be cheaper.
137 137 hg.clean(repo, ctx.p1().node(), show_stats=False)
138 138 parents = repo.changelog.parents(ctx.node())
139 139 ui.pushbuffer()
140 140 cmdutil.revert(ui, repo, ctx, parents)
141 141 ui.popbuffer() # discard "reverting ..." messages
142 142
143 143 # Any modified, added, removed, deleted result means split is incomplete
144 144 incomplete = lambda repo: any(repo.status()[:4])
145 145
146 146 # Main split loop
147 147 while incomplete(repo):
148 148 if committed:
149 149 header = (_('HG: Splitting %s. So far it has been split into:\n')
150 150 % short(ctx.node()))
151 151 for c in committed:
152 152 firstline = c.description().split('\n', 1)[0]
153 153 header += _('HG: - %s: %s\n') % (short(c.node()), firstline)
154 154 header += _('HG: Write commit message for the next split '
155 155 'changeset.\n')
156 156 else:
157 157 header = _('HG: Splitting %s. Write commit message for the '
158 158 'first split changeset.\n') % short(ctx.node())
159 159 opts.update({
160 160 'edit': True,
161 161 'interactive': True,
162 162 'message': header + ctx.description(),
163 163 })
164 164 commands.commit(ui, repo, **pycompat.strkwargs(opts))
165 165 newctx = repo['.']
166 166 committed.append(newctx)
167 167
168 168 if not committed:
169 169 raise error.Abort(_('cannot split an empty revision'))
170 170
171 171 scmutil.cleanupnodes(repo, {ctx.node(): [c.node() for c in committed]},
172 172 operation='split')
173 173
174 174 return committed[-1]
175 175
176 def dorebase(ui, repo, src, dest):
176 def dorebase(ui, repo, src, destctx):
177 177 rebase.rebase(ui, repo, rev=[revsetlang.formatspec('%ld', src)],
178 dest=revsetlang.formatspec('%d', dest))
178 dest=revsetlang.formatspec('%d', destctx.rev()))
General Comments 0
You need to be logged in to leave comments. Login now