Mercurial > hg
annotate hgext/split.py @ 41098:cfd95219102d
strip: move attributes shortcut assigned earlier
This series will add support for "soft" stripping using the archived phase in
addition to the usual heavy weight revlog stripping. First, we need to
refactor the strip function to extract logic common to the "soft-strip" case.
We are about to extract bits of the strip function into separate functions.
Those blocks of code contain variables for frequently accessed attributes. We
move those variables outside of the soon to be moved blocks.
author | Boris Feld <boris.feld@octobus.net> |
---|---|
date | Wed, 02 Jan 2019 05:02:25 +0100 |
parents | fa88170c10bb |
children | 42e2c7c52e1b |
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, | |
25 obsolete, | |
26 phases, | |
36400
7b86aa31b004
py3: fix handling of keyword arguments at more places
Pulkit Goyal <7895pulkit@gmail.com>
parents:
35455
diff
changeset
|
27 pycompat, |
35455 | 28 registrar, |
29 revsetlang, | |
30 scmutil, | |
31 ) | |
32 | |
33 # allow people to use split without explicitly enabling rebase extension | |
34 from . import ( | |
35 rebase, | |
36 ) | |
37 | |
38 cmdtable = {} | |
39 command = registrar.command(cmdtable) | |
40 | |
41 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for | |
42 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should | |
43 # be specifying the version(s) of Mercurial they are tested with, or | |
44 # leave the attribute unspecified. | |
45 testedwith = 'ships-with-hg-core' | |
46 | |
40295
fa88170c10bb
help: adding a proper declaration for shortlist/basic commands (API)
Rodrigo Damazio <rdamazio@google.com>
parents:
40293
diff
changeset
|
47 @command('split', |
35455 | 48 [('r', 'rev', '', _("revision to split"), _('REV')), |
49 ('', 'rebase', True, _('rebase descendants after split')), | |
50 ] + cmdutil.commitopts2, | |
40293
c303d65d2e34
help: assigning categories to existing commands
rdamazio@google.com
parents:
38424
diff
changeset
|
51 _('hg split [--no-rebase] [[-r] REV]'), |
40295
fa88170c10bb
help: adding a proper declaration for shortlist/basic commands (API)
Rodrigo Damazio <rdamazio@google.com>
parents:
40293
diff
changeset
|
52 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, helpbasic=True) |
35455 | 53 def split(ui, repo, *revs, **opts): |
54 """split a changeset into smaller ones | |
55 | |
56 Repeatedly prompt changes and commit message for new changesets until there | |
57 is nothing left in the original changeset. | |
58 | |
59 If --rev was not given, split the working directory parent. | |
60 | |
61 By default, rebase connected non-obsoleted descendants onto the new | |
62 changeset. Use --no-rebase to avoid the rebase. | |
63 """ | |
38069
5ba0cf22e4d0
py3: fix kwargs handling in hgext/split.py
Pulkit Goyal <7895pulkit@gmail.com>
parents:
36408
diff
changeset
|
64 opts = pycompat.byteskwargs(opts) |
35455 | 65 revlist = [] |
66 if opts.get('rev'): | |
67 revlist.append(opts.get('rev')) | |
68 revlist.extend(revs) | |
69 with repo.wlock(), repo.lock(), repo.transaction('split') as tr: | |
70 revs = scmutil.revrange(repo, revlist or ['.']) | |
71 if len(revs) > 1: | |
72 raise error.Abort(_('cannot split multiple revisions')) | |
73 | |
74 rev = revs.first() | |
75 ctx = repo[rev] | |
76 if rev is None or ctx.node() == nullid: | |
77 ui.status(_('nothing to split\n')) | |
78 return 1 | |
79 if ctx.node() is None: | |
80 raise error.Abort(_('cannot split working directory')) | |
81 | |
82 # rewriteutil.precheck is not very useful here because: | |
83 # 1. null check is done above and it's more friendly to return 1 | |
84 # instead of abort | |
85 # 2. mergestate check is done below by cmdutil.bailifchanged | |
86 # 3. unstable check is more complex here because of --rebase | |
87 # | |
88 # So only "public" check is useful and it's checked directly here. | |
89 if ctx.phase() == phases.public: | |
90 raise error.Abort(_('cannot split public changeset'), | |
91 hint=_("see 'hg help phases' for details")) | |
92 | |
93 descendants = list(repo.revs('(%d::) - (%d)', rev, rev)) | |
94 alloworphaned = obsolete.isenabled(repo, obsolete.allowunstableopt) | |
95 if opts.get('rebase'): | |
96 # Skip obsoleted descendants and their descendants so the rebase | |
97 # won't cause conflicts for sure. | |
98 torebase = list(repo.revs('%ld - (%ld & obsolete())::', | |
99 descendants, descendants)) | |
100 if not alloworphaned and len(torebase) != len(descendants): | |
101 raise error.Abort(_('split would leave orphaned changesets ' | |
102 'behind')) | |
103 else: | |
104 if not alloworphaned and descendants: | |
105 raise error.Abort( | |
106 _('cannot split changeset with children without rebase')) | |
107 torebase = () | |
108 | |
109 if len(ctx.parents()) > 1: | |
110 raise error.Abort(_('cannot split a merge changeset')) | |
111 | |
112 cmdutil.bailifchanged(repo) | |
113 | |
114 # Deactivate bookmark temporarily so it won't get moved unintentionally | |
115 bname = repo._activebookmark | |
116 if bname and repo._bookmarks[bname] != ctx.node(): | |
117 bookmarks.deactivate(repo) | |
118 | |
119 wnode = repo['.'].node() | |
120 top = None | |
121 try: | |
122 top = dosplit(ui, repo, tr, ctx, opts) | |
123 finally: | |
124 # top is None: split failed, need update --clean recovery. | |
125 # wnode == ctx.node(): wnode split, no need to update. | |
126 if top is None or wnode != ctx.node(): | |
127 hg.clean(repo, wnode, show_stats=False) | |
128 if bname: | |
129 bookmarks.activate(repo, bname) | |
130 if torebase and top: | |
131 dorebase(ui, repo, torebase, top) | |
132 | |
133 def dosplit(ui, repo, tr, ctx, opts): | |
134 committed = [] # [ctx] | |
135 | |
136 # Set working parent to ctx.p1(), and keep working copy as ctx's content | |
137 # NOTE: if we can have "update without touching working copy" API, the | |
138 # revert step could be cheaper. | |
139 hg.clean(repo, ctx.p1().node(), show_stats=False) | |
140 parents = repo.changelog.parents(ctx.node()) | |
141 ui.pushbuffer() | |
142 cmdutil.revert(ui, repo, ctx, parents) | |
143 ui.popbuffer() # discard "reverting ..." messages | |
144 | |
145 # Any modified, added, removed, deleted result means split is incomplete | |
146 incomplete = lambda repo: any(repo.status()[:4]) | |
147 | |
148 # Main split loop | |
149 while incomplete(repo): | |
150 if committed: | |
151 header = (_('HG: Splitting %s. So far it has been split into:\n') | |
152 % short(ctx.node())) | |
153 for c in committed: | |
154 firstline = c.description().split('\n', 1)[0] | |
155 header += _('HG: - %s: %s\n') % (short(c.node()), firstline) | |
156 header += _('HG: Write commit message for the next split ' | |
157 'changeset.\n') | |
158 else: | |
159 header = _('HG: Splitting %s. Write commit message for the ' | |
160 'first split changeset.\n') % short(ctx.node()) | |
161 opts.update({ | |
162 'edit': True, | |
163 'interactive': True, | |
164 'message': header + ctx.description(), | |
165 }) | |
36400
7b86aa31b004
py3: fix handling of keyword arguments at more places
Pulkit Goyal <7895pulkit@gmail.com>
parents:
35455
diff
changeset
|
166 commands.commit(ui, repo, **pycompat.strkwargs(opts)) |
35455 | 167 newctx = repo['.'] |
168 committed.append(newctx) | |
169 | |
170 if not committed: | |
171 raise error.Abort(_('cannot split an empty revision')) | |
172 | |
173 scmutil.cleanupnodes(repo, {ctx.node(): [c.node() for c in committed]}, | |
38424
4f885770c4a2
split: preserve phase of commit that is being split
Martin von Zweigbergk <martinvonz@google.com>
parents:
38069
diff
changeset
|
174 operation='split', fixphase=True) |
35455 | 175 |
176 return committed[-1] | |
177 | |
36408
83bade6206d4
split: use ctx.rev() instead of %d % ctx
Gregory Szorc <gregory.szorc@gmail.com>
parents:
36400
diff
changeset
|
178 def dorebase(ui, repo, src, destctx): |
35455 | 179 rebase.rebase(ui, repo, rev=[revsetlang.formatspec('%ld', src)], |
36408
83bade6206d4
split: use ctx.rev() instead of %d % ctx
Gregory Szorc <gregory.szorc@gmail.com>
parents:
36400
diff
changeset
|
180 dest=revsetlang.formatspec('%d', destctx.rev())) |