Skip to content

Commit 3cff1e0

Browse files
zzl0meta-codesync[bot]
authored andcommitted
backout: abort backout of subtree commits
Summary: Subtree commits (copy/merge) carry metadata that would be lost if backed out. This adds a check in `_dobackout` that aborts early when the target commit has subtree metadata. The hint message is configurable via the `subtree.backout-hint` config option, allowing teams to customize the guidance shown to users. Differential Revision: D103686530 fbshipit-source-id: b6c76bd6da4b265a97e880c16c31adca1b2ed80a
1 parent f4a87ba commit 3cff1e0

3 files changed

Lines changed: 105 additions & 0 deletions

File tree

eden/scm/sapling/commands/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,8 @@ def _dobackout(ui, repo, node=None, rev=None, **opts):
854854
raise error.Abort(_("cannot use --parent on non-merge changeset"))
855855
parent = p1
856856

857+
subtreeutil.check_commit_backoutable(repo, node)
858+
857859
rctx = scmutil.revsingle(repo, hex(parent))
858860
if not opts.get("merge") and op1 != node:
859861
dsguard = dirstateguard.dirstateguard(repo, "backout")

eden/scm/sapling/utils/subtreeutil.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,26 @@ def check_commit_splitability(repo, node):
794794
raise error.Abort(_("cannot split subtree copy/merge commits"))
795795

796796

797+
def check_commit_backoutable(repo, node):
798+
"""Check if the given commit can be backed out.
799+
800+
Subtree commits (copy/merge) cannot be backed out because the backout
801+
would lose subtree metadata.
802+
"""
803+
extra = repo[node].extra()
804+
if get_subtree_metadata(extra):
805+
hint = _(
806+
"use '@prog@ subtree copy -r <good-commit>' to overwrite the path,"
807+
" then graft changes on top if needed"
808+
)
809+
if extra_hint := repo.ui.config("subtree", "backout-hint"):
810+
hint = f"{hint}. {extra_hint}"
811+
raise error.Abort(
812+
_("cannot backout subtree copy/merge commits"),
813+
hint=hint,
814+
)
815+
816+
797817
def get_or_clone_git_repo(ui, url, from_rev=None):
798818
def try_reuse_git_repo(git_repo_dir):
799819
"""try to reuse an existing git repo, otherwise return None"""
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
$ setconfig diff.git=True
2+
$ setconfig subtree.allow-any-source-commit=True
3+
$ setconfig subtree.min-path-depth=1
4+
5+
test backout of subtree commit is aborted
6+
$ newclientrepo
7+
$ drawdag <<'EOS'
8+
> B # B/foo/x = 1a\n2\n3\n
9+
> |
10+
> A # A/foo/x = 1\n2\n3\n
11+
> EOS
12+
$ sl go -q $B
13+
$ sl subtree copy -r $A --from-path foo --to-path foo2 -m "subtree copy foo to foo2"
14+
copying foo to foo2
15+
$ sl backout -r .
16+
abort: cannot backout subtree copy/merge commits
17+
(use 'sl subtree copy -r <good-commit>' to overwrite the path, then graft changes on top if needed)
18+
[255]
19+
20+
test backout of subtree commit shows configurable hint
21+
$ sl backout -r . --config subtree.backout-hint="see https://example.com for details"
22+
abort: cannot backout subtree copy/merge commits
23+
(use 'sl subtree copy -r <good-commit>' to overwrite the path, then graft changes on top if needed. see https://example.com for details)
24+
[255]
25+
26+
test backout subtree merge: backout fails, subtree copy overwrites, future merge works
27+
$ newclientrepo
28+
$ drawdag <<'EOS'
29+
> B # B/foo/x = 1a\n2\n3\n
30+
> |
31+
> A # A/foo/x = 1\n2\n3\n
32+
> EOS
33+
$ sl go -q $B
34+
$ sl subtree copy -r $A --from-path foo --to-path bar -m "subtree copy foo to bar"
35+
copying foo to bar
36+
37+
step 1: create a subtree merge commit
38+
$ echo "change1" >> foo/x && sl ci -m "update foo with change1"
39+
$ sl subtree merge --from-path foo --to-path bar | grep "merge base:"
40+
merge base: b4cb27eee4e2
41+
$ sl ci -m "subtree merge foo to bar"
42+
$ cat bar/x
43+
1a
44+
2
45+
3
46+
change1
47+
48+
step 2: backout the subtree merge commit does not work
49+
$ sl backout -r .
50+
abort: cannot backout subtree copy/merge commits
51+
(use 'sl subtree copy -r <good-commit>' to overwrite the path, then graft changes on top if needed)
52+
[255]
53+
54+
step 3: subtree copy overwrites the dest path to a good state
55+
$ sl log -G -T '{node|short} {desc|firstline}'
56+
@ 26d65d502329 subtree merge foo to bar
57+
58+
o 49d9ca68c22d update foo with change1
59+
60+
o ee6785824a72 subtree copy foo to bar
61+
62+
o c4fbbcdf676b B
63+
64+
o b4cb27eee4e2 A
65+
$ sl subtree copy -r .^ --from-path bar --to-path bar --force -m "overwrite bar to pre-merge state"
66+
removing bar/x
67+
copying bar to bar
68+
$ cat bar/x
69+
1
70+
2
71+
3
72+
73+
step 4: future subtree merge finds correct merge base and merges the changes back
74+
$ echo "change2" >> foo/x && sl ci -m "update foo with change2"
75+
$ sl subtree merge --from-path foo --to-path bar | grep "merge base:"
76+
merge base: b4cb27eee4e2
77+
$ sl ci -m "subtree merge foo to bar again"
78+
$ cat bar/x
79+
1a
80+
2
81+
3
82+
change1
83+
change2

0 commit comments

Comments
 (0)