##// END OF EJS Templates
graft: add a `--to` flag grafting in memory...
marmoute -
r53402:68dc6cec default
parent child Browse files
Show More
@@ -11,7 +11,14 from typing import (
11
11
12 from ..i18n import _
12 from ..i18n import _
13
13
14 from .. import cmdutil, error, logcmdutil, merge as mergemod, state as statemod
14 from .. import (
15 cmdutil,
16 context,
17 error,
18 logcmdutil,
19 merge as mergemod,
20 state as statemod,
21 )
15
22
16
23
17 if typing.TYPE_CHECKING:
24 if typing.TYPE_CHECKING:
@@ -33,8 +40,11 def cmd_graft(ui, repo, *revs, **opts) -
33 return _stopgraft(ui, repo, graftstate)
40 return _stopgraft(ui, repo, graftstate)
34 elif action == "GRAFT":
41 elif action == "GRAFT":
35 return _graft_revisions(ui, repo, graftstate, *args)
42 return _graft_revisions(ui, repo, graftstate, *args)
43 elif action == "GRAFT-TO":
44 return _graft_revisions_in_memory(ui, repo, graftstate, *args)
36 else:
45 else:
37 raise error.ProgrammingError('unknown action: %s' % action)
46 msg = b'unknown action: %s' % action.encode('ascii')
47 raise error.ProgrammingError(msg)
38
48
39
49
40 def _process_args(
50 def _process_args(
@@ -61,11 +71,20 def _process_args(
61 statedata[b'newnodes'] = []
71 statedata[b'newnodes'] = []
62
72
63 # argument incompatible with followup from an interrupted operation
73 # argument incompatible with followup from an interrupted operation
64 commit_args = ['edit', 'log', 'user', 'date', 'currentdate', 'currentuser']
74 commit_args = [
75 'edit',
76 'log',
77 'user',
78 'date',
79 'currentdate',
80 'currentuser',
81 'to',
82 ]
65 nofollow_args = commit_args + ['base', 'rev']
83 nofollow_args = commit_args + ['base', 'rev']
66
84
67 arg_compatibilities = [
85 arg_compatibilities = [
68 ('no_commit', commit_args),
86 ('no_commit', commit_args),
87 ('continue', ['to']),
69 ('stop', nofollow_args),
88 ('stop', nofollow_args),
70 ('abort', nofollow_args),
89 ('abort', nofollow_args),
71 ]
90 ]
@@ -79,6 +98,12 def _process_args(
79
98
80 graftstate = statemod.cmdstate(repo, b'graftstate')
99 graftstate = statemod.cmdstate(repo, b'graftstate')
81
100
101 if opts.get('to'):
102 toctx = logcmdutil.revsingle(repo, opts['to'], None)
103 statedata[b'to'] = toctx.hex()
104 else:
105 toctx = repo[None].p1()
106
82 if opts.get('stop'):
107 if opts.get('stop'):
83 return "STOP", graftstate, None
108 return "STOP", graftstate, None
84 elif opts.get('abort'):
109 elif opts.get('abort'):
@@ -102,6 +127,7 def _process_args(
102 raise error.InputError(_(b'no revisions specified'))
127 raise error.InputError(_(b'no revisions specified'))
103 else:
128 else:
104 cmdutil.checkunfinished(repo)
129 cmdutil.checkunfinished(repo)
130 if not opts.get('to'):
105 cmdutil.bailifchanged(repo)
131 cmdutil.bailifchanged(repo)
106 revs = logcmdutil.revrange(repo, revs)
132 revs = logcmdutil.revrange(repo, revs)
107
133
@@ -142,7 +168,7 def _process_args(
142 # already, they'd have been in the graftstate.
168 # already, they'd have been in the graftstate.
143 if not (cont or opts.get('force')) and basectx is None:
169 if not (cont or opts.get('force')) and basectx is None:
144 # check for ancestors of dest branch
170 # check for ancestors of dest branch
145 ancestors = repo.revs(b'%ld & (::.)', revs)
171 ancestors = repo.revs(b'%ld & (::%d)', revs, toctx.rev())
146 for rev in ancestors:
172 for rev in ancestors:
147 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
173 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
148
174
@@ -164,7 +190,7 def _process_args(
164
190
165 # The only changesets we can be sure doesn't contain grafts of any
191 # The only changesets we can be sure doesn't contain grafts of any
166 # revs, are the ones that are common ancestors of *all* revs:
192 # revs, are the ones that are common ancestors of *all* revs:
167 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
193 for rev in repo.revs(b'only(%d,ancestor(%ld))', toctx.rev(), revs):
168 ctx = repo[rev]
194 ctx = repo[rev]
169 n = ctx.extra().get(b'source')
195 n = ctx.extra().get(b'source')
170 if n in ids:
196 if n in ids:
@@ -216,6 +242,8 def _process_args(
216 editor = cmdutil.getcommiteditor(editform=b'graft', **opts)
242 editor = cmdutil.getcommiteditor(editform=b'graft', **opts)
217 dry_run = bool(opts.get("dry_run"))
243 dry_run = bool(opts.get("dry_run"))
218 tool = opts.get('tool', b'')
244 tool = opts.get('tool', b'')
245 if opts.get("to"):
246 return "GRAFT-TO", graftstate, (statedata, revs, editor, dry_run, tool)
219 return "GRAFT", graftstate, (statedata, revs, editor, cont, dry_run, tool)
247 return "GRAFT", graftstate, (statedata, revs, editor, cont, dry_run, tool)
220
248
221
249
@@ -247,6 +275,65 def _build_meta(ui, repo, ctx, statedata
247 return (user, date, message, extra)
275 return (user, date, message, extra)
248
276
249
277
278 def _graft_revisions_in_memory(
279 ui,
280 repo,
281 graftstate,
282 statedata,
283 revs,
284 editor,
285 dry_run,
286 tool=b'',
287 ):
288 """graft revisions in memory
289
290 Abort on unresolved conflicts.
291 """
292 with repo.lock(), repo.transaction(b"graft"):
293 target = repo[statedata[b"to"]]
294 for r in revs:
295 ctx = repo[r]
296 ui.status(_build_progress(ui, repo, ctx))
297 if dry_run:
298 # we might want to actually perform the grafting to detect
299 # potential conflict in the dry run.
300 continue
301 wctx = context.overlayworkingctx(repo)
302 wctx.setbase(target)
303 if b'base' in statedata:
304 base = repo[statedata[b'base']]
305 else:
306 base = ctx.p1()
307
308 (user, date, message, extra) = _build_meta(ui, repo, ctx, statedata)
309
310 # perform the graft merge with p1(rev) as 'ancestor'
311 try:
312 overrides = {(b'ui', b'forcemerge'): tool}
313 with ui.configoverride(overrides, b'graft'):
314 mergemod.graft(
315 repo,
316 ctx,
317 base,
318 wctx=wctx,
319 )
320 except error.InMemoryMergeConflictsError as e:
321 raise error.Abort(
322 b'cannot graft in memory: merge conflicts',
323 hint=_(bytes(e)),
324 )
325 mctx = wctx.tomemctx(
326 message,
327 user=user,
328 date=date,
329 extra=extra,
330 editor=editor,
331 )
332 node = repo.commitctx(mctx)
333 target = repo[node]
334 return 0
335
336
250 def _graft_revisions(
337 def _graft_revisions(
251 ui,
338 ui,
252 repo,
339 repo,
@@ -2971,6 +2971,12 def forget(ui, repo, *pats, **opts):
2971 _(b'base revision when doing the graft merge (ADVANCED)'),
2971 _(b'base revision when doing the graft merge (ADVANCED)'),
2972 _(b'REV'),
2972 _(b'REV'),
2973 ),
2973 ),
2974 (
2975 b'',
2976 b'to',
2977 b'',
2978 _(b'graft to this destination, in memory (EXPERIMENTAL)'),
2979 ),
2974 (b'c', b'continue', False, _(b'resume interrupted graft')),
2980 (b'c', b'continue', False, _(b'resume interrupted graft')),
2975 (b'', b'stop', False, _(b'stop interrupted graft')),
2981 (b'', b'stop', False, _(b'stop interrupted graft')),
2976 (b'', b'abort', False, _(b'abort interrupted graft')),
2982 (b'', b'abort', False, _(b'abort interrupted graft')),
@@ -3061,6 +3067,16 def graft(ui, repo, *revs, **opts):
3061
3067
3062 .. container:: verbose
3068 .. container:: verbose
3063
3069
3070 The experimental --to option allow to graft a revision in memory,
3071 independently from the working copy. Merge conflict are not currenly
3072 supported and the operation will be aborted if the configured tool
3073 cannot handle the conflict that might be encountered.
3074
3075 As the operation is performence in memory, the on disk file will not be
3076 modified and some hooks might not be run.
3077
3078 .. container:: verbose
3079
3064 Examples:
3080 Examples:
3065
3081
3066 - copy a single change to the stable branch and edit its description::
3082 - copy a single change to the stable branch and edit its description::
@@ -360,7 +360,7 Show all commands + options
360 export: bookmark, output, switch-parent, rev, text, git, binary, nodates, template
360 export: bookmark, output, switch-parent, rev, text, git, binary, nodates, template
361 files: rev, print0, include, exclude, template, subrepos
361 files: rev, print0, include, exclude, template, subrepos
362 forget: interactive, include, exclude, dry-run
362 forget: interactive, include, exclude, dry-run
363 graft: rev, base, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run
363 graft: rev, base, to, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run
364 grep: print0, all, diff, text, follow, ignore-case, files-with-matches, line-number, rev, all-files, user, date, template, include, exclude
364 grep: print0, all, diff, text, follow, ignore-case, files-with-matches, line-number, rev, all-files, user, date, template, include, exclude
365 heads: rev, topo, active, closed, style, template
365 heads: rev, topo, active, closed, style, template
366 help: extension, command, keyword, system
366 help: extension, command, keyword, system
@@ -882,6 +882,8 Empty graft
882 grafting 23:72d9c7c75bcc "24"
882 grafting 23:72d9c7c75bcc "24"
883 note: graft of 23:72d9c7c75bcc created no changes to commit
883 note: graft of 23:72d9c7c75bcc created no changes to commit
884
884
885 $ pwd
886 $TESTTMP/a
885 $ cd ..
887 $ cd ..
886
888
887 Graft to duplicate a commit
889 Graft to duplicate a commit
@@ -921,3 +923,329 Graft to duplicate a commit twice
921 |/
923 |/
922 o 0
924 o 0
923
925
926 $ cd ../
927
928 In memory graft with --to
929 =========================
930
931
932 setup a repository
933
934 $ hg init base-to
935 $ cd base-to
936 $ hg debugbuilddag -m ".:base..:dst*base.:src*base..:wc"
937 $ hg up "wc"
938 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
939 $ hg log -G -T '{rev}:{desc} {tags}\n'
940 @ 7:r7 tip wc
941 |
942 o 6:r6
943 |
944 o 5:r5
945 |
946 | o 4:r4 src
947 | |
948 | o 3:r3
949 |/
950 | o 2:r2 dst
951 | |
952 | o 1:r1
953 |/
954 o 0:r0 base
955
956
957 $ cd ..
958
959 Simple test
960 -----------
961
962 As few special case as possible
963
964 $ cp -R base-to test-to-simple
965 $ cd test-to-simple
966 $ hg graft --rev src --to dst
967 grafting 4:4178b3134f52 "r4" (src)
968 merging mf
969 $ hg log -G -T '{rev}:{desc} {tags}\n'
970 o 8:r4 tip
971 |
972 | @ 7:r7 wc
973 | |
974 | o 6:r6
975 | |
976 | o 5:r5
977 | |
978 | | o 4:r4 src
979 | | |
980 | | o 3:r3
981 | |/
982 o | 2:r2 dst
983 | |
984 o | 1:r1
985 |/
986 o 0:r0 base
987
988 $ cd ..
989
990 Single changeset, local changes
991 -------------------------------
992
993 Run "graft --to" with local changes
994
995 $ cp -R base-to test-to-local-change
996 $ cd test-to-local-change
997 $ hg st --all
998 C mf
999 $ echo foo >> mf
1000 $ hg status
1001 M mf
1002 $ hg graft --rev src --to dst
1003 grafting 4:4178b3134f52 "r4" (src)
1004 merging mf
1005
1006 local file should not have been touched.
1007
1008 $ hg status
1009 M mf
1010 $ hg log -G -T '{rev}:{desc} {tags}\n'
1011 o 8:r4 tip
1012 |
1013 | @ 7:r7 wc
1014 | |
1015 | o 6:r6
1016 | |
1017 | o 5:r5
1018 | |
1019 | | o 4:r4 src
1020 | | |
1021 | | o 3:r3
1022 | |/
1023 o | 2:r2 dst
1024 | |
1025 o | 1:r1
1026 |/
1027 o 0:r0 base
1028
1029 $ cd ..
1030
1031 Multiple linear changesets
1032 --------------------------
1033
1034 grafting multiple linear changesets
1035
1036 $ cp -R base-to test-to-multiple-linear
1037 $ cd test-to-multiple-linear
1038 $ hg graft --rev 'src~1::src' --to dst
1039 grafting 3:181578a106da "r3"
1040 merging mf
1041 grafting 4:4178b3134f52 "r4" (src)
1042 merging mf
1043 $ hg log -G -T '{rev}:{desc} {tags}\n'
1044 o 9:r4 tip
1045 |
1046 o 8:r3
1047 |
1048 | @ 7:r7 wc
1049 | |
1050 | o 6:r6
1051 | |
1052 | o 5:r5
1053 | |
1054 | | o 4:r4 src
1055 | | |
1056 | | o 3:r3
1057 | |/
1058 o | 2:r2 dst
1059 | |
1060 o | 1:r1
1061 |/
1062 o 0:r0 base
1063
1064 $ cd ..
1065
1066 Multiple unrelated changesets
1067 --------------------------
1068
1069 Grafting multiple changesets on different branch
1070
1071 The order specified on the command line should be preserved.
1072 The result should be linear.
1073
1074 $ cp -R base-to test-to-multiple-unrelated
1075 $ cd test-to-multiple-unrelated
1076 $ hg graft 'src' 'wc~1' 'src~1' --to dst
1077 grafting 4:4178b3134f52 "r4" (src)
1078 merging mf
1079 grafting 6:735f0f7a080b "r6"
1080 merging mf
1081 grafting 3:181578a106da "r3"
1082 merging mf
1083 $ hg log -G -T '{rev}:{desc} {tags}\n'
1084 o 10:r3 tip
1085 |
1086 o 9:r6
1087 |
1088 o 8:r4
1089 |
1090 | @ 7:r7 wc
1091 | |
1092 | o 6:r6
1093 | |
1094 | o 5:r5
1095 | |
1096 | | o 4:r4 src
1097 | | |
1098 | | o 3:r3
1099 | |/
1100 o | 2:r2 dst
1101 | |
1102 o | 1:r1
1103 |/
1104 o 0:r0 base
1105
1106 $ cd ..
1107
1108 with base
1109 ---------
1110
1111 $ cp -R base-to test-to-base
1112 $ cd test-to-base
1113 $ hg graft --base base src --to dst
1114 grafting 4:4178b3134f52 "r4" (src)
1115 merging mf
1116 $ hg log -G -T '{rev}:{desc} {tags}\n'
1117 o 8:r4 tip
1118 |
1119 | @ 7:r7 wc
1120 | |
1121 | o 6:r6
1122 | |
1123 | o 5:r5
1124 | |
1125 | | o 4:r4 src
1126 | | |
1127 | | o 3:r3
1128 | |/
1129 o | 2:r2 dst
1130 | |
1131 o | 1:r1
1132 |/
1133 o 0:r0 base
1134
1135 $ hg diff --from base --to src
1136 diff -r 93cbaf5e6529 -r 4178b3134f52 mf
1137 --- a/mf Thu Jan 01 00:00:00 1970 +0000
1138 +++ b/mf Thu Jan 01 00:00:04 1970 +0000
1139 @@ -4,9 +4,9 @@
1140 3
1141 4
1142 5
1143 -6
1144 +6 r3
1145 7
1146 -8
1147 +8 r4
1148 9
1149 10
1150 11
1151 $ hg export src
1152 # HG changeset patch
1153 # User debugbuilddag
1154 # Date 4 0
1155 # Thu Jan 01 00:00:04 1970 +0000
1156 # Node ID 4178b3134f5224d297d3b9e0e98b983f42e53d55
1157 # Parent 181578a106daabea66d4465f4883f7f8552bbc9d
1158 r4
1159
1160 diff -r 181578a106da -r 4178b3134f52 mf
1161 --- a/mf Thu Jan 01 00:00:03 1970 +0000
1162 +++ b/mf Thu Jan 01 00:00:04 1970 +0000
1163 @@ -6,7 +6,7 @@
1164 5
1165 6 r3
1166 7
1167 -8
1168 +8 r4
1169 9
1170 10
1171 11
1172 $ hg export tip
1173 # HG changeset patch
1174 # User debugbuilddag
1175 # Date 4 0
1176 # Thu Jan 01 00:00:04 1970 +0000
1177 # Node ID 40112ab60ecb01882916c1a4439c798746e34165
1178 # Parent 37d4c1cec295ddfa401f4a365e15a82a1974b056
1179 r4
1180
1181 diff -r 37d4c1cec295 -r 40112ab60ecb mf
1182 --- a/mf Thu Jan 01 00:00:02 1970 +0000
1183 +++ b/mf Thu Jan 01 00:00:04 1970 +0000
1184 @@ -4,9 +4,9 @@
1185 3
1186 4 r2
1187 5
1188 -6
1189 +6 r3
1190 7
1191 -8
1192 +8 r4
1193 9
1194 10
1195 11
1196 $ cd ..
1197
1198 with conflict
1199 -------------
1200
1201 We should abort in case of conflict and rollback any grafted procress
1202
1203 $ cp -R base-to test-to-conflict
1204 $ cd test-to-conflict
1205 $ hg up src
1206 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1207 $ echo this-will-conflict >> mf
1208 $ hg ci -m 'this-will-conflict'
1209 $ hg up dst
1210 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1211 $ echo conflict-this-will-conflict >> mf
1212 $ hg ci -m 'conflict-this-will'
1213 $ hg up wc
1214 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1215 $ hg graft --to 'max(dst::)' src:: --dry-run
1216 grafting 4:4178b3134f52 "r4" (src)
1217 grafting 8:9fa2d3fe2323 "this-will-conflict"
1218 $ hg graft --to 'max(dst::)' src::
1219 grafting 4:4178b3134f52 "r4" (src)
1220 merging mf
1221 grafting 8:9fa2d3fe2323 "this-will-conflict"
1222 merging mf
1223 transaction abort!
1224 rollback completed
1225 abort: cannot graft in memory: merge conflicts
1226 (in-memory merge does not support merge conflicts)
1227 [255]
1228 $ hg log -G -T '{rev}:{desc} {tags}\n'
1229 o 9:conflict-this-will tip
1230 |
1231 | o 8:this-will-conflict
1232 | |
1233 | | @ 7:r7 wc
1234 | | |
1235 | | o 6:r6
1236 | | |
1237 | | o 5:r5
1238 | | |
1239 | o | 4:r4 src
1240 | | |
1241 | o | 3:r3
1242 | |/
1243 o | 2:r2 dst
1244 | |
1245 o | 1:r1
1246 |/
1247 o 0:r0 base
1248
1249 $ cd ..
1250
1251
General Comments 0
You need to be logged in to leave comments. Login now