Skip to content

Commit 523129b

Browse files
committed
ensure cannot fold into an inserted branch
1 parent 755494b commit 523129b

2 files changed

Lines changed: 352 additions & 18 deletions

File tree

internal/tui/modifyview/model.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,8 @@ func (m *Model) toggleDrop() {
718718
// Check if any other branch has a fold targeting this branch.
719719
// A fold-up targets the branch above (lower index), fold-down
720720
// targets the branch below (higher index).
721+
// Also check if dropping this branch would cause a fold to
722+
// retarget to an inserted branch.
721723
for i, other := range m.nodes {
722724
if other.PendingAction == nil || i == m.cursor {
723725
continue
@@ -727,6 +729,25 @@ func (m *Model) toggleDrop() {
727729
for j := i - 1; j >= 0; j-- {
728730
if !m.nodes[j].Removed && !m.nodes[j].Ref.IsMerged() {
729731
if j == m.cursor {
732+
// This branch is the current target. Check what the
733+
// next target would be after dropping it.
734+
nextTarget := -1
735+
for k := j - 1; k >= 0; k-- {
736+
if !m.nodes[k].Removed && !m.nodes[k].Ref.IsMerged() {
737+
nextTarget = k
738+
break
739+
}
740+
}
741+
if nextTarget < 0 {
742+
m.statusMessage = fmt.Sprintf("Cannot drop: %s is folding into this branch", other.Ref.Branch)
743+
m.statusIsError = true
744+
return
745+
}
746+
if m.nodes[nextTarget].IsInserted {
747+
m.statusMessage = fmt.Sprintf("Cannot drop: %s would fold into an inserted branch", other.Ref.Branch)
748+
m.statusIsError = true
749+
return
750+
}
730751
m.statusMessage = fmt.Sprintf("Cannot drop: %s is folding into this branch", other.Ref.Branch)
731752
m.statusIsError = true
732753
return
@@ -740,6 +761,25 @@ func (m *Model) toggleDrop() {
740761
for j := i + 1; j < len(m.nodes); j++ {
741762
if !m.nodes[j].Removed && !m.nodes[j].Ref.IsMerged() {
742763
if j == m.cursor {
764+
// This branch is the current target. Check what the
765+
// next target would be after dropping it.
766+
nextTarget := -1
767+
for k := j + 1; k < len(m.nodes); k++ {
768+
if !m.nodes[k].Removed && !m.nodes[k].Ref.IsMerged() {
769+
nextTarget = k
770+
break
771+
}
772+
}
773+
if nextTarget < 0 {
774+
m.statusMessage = fmt.Sprintf("Cannot drop: %s is folding into this branch", other.Ref.Branch)
775+
m.statusIsError = true
776+
return
777+
}
778+
if m.nodes[nextTarget].IsInserted {
779+
m.statusMessage = fmt.Sprintf("Cannot drop: %s would fold into an inserted branch", other.Ref.Branch)
780+
m.statusIsError = true
781+
return
782+
}
743783
m.statusMessage = fmt.Sprintf("Cannot drop: %s is folding into this branch", other.Ref.Branch)
744784
m.statusIsError = true
745785
return
@@ -816,6 +856,38 @@ func (m *Model) fold(action ActionType) {
816856
return
817857
}
818858

859+
// Check if the current node is the target of another fold — folding
860+
// a branch that is receiving commits from another fold is not allowed.
861+
for i, other := range m.nodes {
862+
if other.PendingAction == nil || i == m.cursor {
863+
continue
864+
}
865+
if other.PendingAction.Type == ActionFoldUp {
866+
for j := i - 1; j >= 0; j-- {
867+
if !m.nodes[j].Removed && !m.nodes[j].Ref.IsMerged() {
868+
if j == m.cursor {
869+
m.statusMessage = fmt.Sprintf("Cannot fold: %s is folding into this branch", other.Ref.Branch)
870+
m.statusIsError = true
871+
return
872+
}
873+
break
874+
}
875+
}
876+
}
877+
if other.PendingAction.Type == ActionFoldDown {
878+
for j := i + 1; j < len(m.nodes); j++ {
879+
if !m.nodes[j].Removed && !m.nodes[j].Ref.IsMerged() {
880+
if j == m.cursor {
881+
m.statusMessage = fmt.Sprintf("Cannot fold: %s is folding into this branch", other.Ref.Branch)
882+
m.statusIsError = true
883+
return
884+
}
885+
break
886+
}
887+
}
888+
}
889+
}
890+
819891
// Find the target branch
820892
var targetIdx int
821893
var found bool
@@ -834,6 +906,11 @@ func (m *Model) fold(action ActionType) {
834906
m.statusIsError = true
835907
return
836908
}
909+
if m.nodes[targetIdx].IsInserted {
910+
m.statusMessage = "Cannot fold into an inserted branch"
911+
m.statusIsError = true
912+
return
913+
}
837914
} else {
838915
// Fold up: target is the previous non-removed, non-merged node away from trunk (lower index)
839916
for i := m.cursor - 1; i >= 0; i-- {
@@ -848,6 +925,19 @@ func (m *Model) fold(action ActionType) {
848925
m.statusIsError = true
849926
return
850927
}
928+
if m.nodes[targetIdx].IsInserted {
929+
m.statusMessage = "Cannot fold into an inserted branch"
930+
m.statusIsError = true
931+
return
932+
}
933+
}
934+
935+
// Check if the target is already folding (mutual fold / chain fold)
936+
target := &m.nodes[targetIdx]
937+
if target.PendingAction != nil && (target.PendingAction.Type == ActionFoldDown || target.PendingAction.Type == ActionFoldUp) {
938+
m.statusMessage = fmt.Sprintf("Cannot fold: %s is already folding in the opposite direction", target.Ref.Branch)
939+
m.statusIsError = true
940+
return
851941
}
852942

853943
// Check if this would remove the last active original branch
@@ -913,6 +1003,44 @@ func (m *Model) startInsert(direction ActionType) {
9131003
return
9141004
}
9151005

1006+
// Compute where the node would be inserted
1007+
insertIdx := m.cursor
1008+
if direction == ActionInsertBelow {
1009+
insertIdx = m.cursor + 1
1010+
}
1011+
1012+
// Check if inserting here would place the new branch between a
1013+
// folding branch and its target, making it the new fold target.
1014+
for i, other := range m.nodes {
1015+
if other.PendingAction == nil {
1016+
continue
1017+
}
1018+
if other.PendingAction.Type == ActionFoldDown {
1019+
for j := i + 1; j < len(m.nodes); j++ {
1020+
if !m.nodes[j].Removed && !m.nodes[j].Ref.IsMerged() {
1021+
if insertIdx > i && insertIdx <= j {
1022+
m.statusMessage = fmt.Sprintf("Cannot insert here: %s is folding into %s", other.Ref.Branch, m.nodes[j].Ref.Branch)
1023+
m.statusIsError = true
1024+
return
1025+
}
1026+
break
1027+
}
1028+
}
1029+
}
1030+
if other.PendingAction.Type == ActionFoldUp {
1031+
for j := i - 1; j >= 0; j-- {
1032+
if !m.nodes[j].Removed && !m.nodes[j].Ref.IsMerged() {
1033+
if insertIdx > j && insertIdx <= i {
1034+
m.statusMessage = fmt.Sprintf("Cannot insert here: %s is folding into %s", other.Ref.Branch, m.nodes[j].Ref.Branch)
1035+
m.statusIsError = true
1036+
return
1037+
}
1038+
break
1039+
}
1040+
}
1041+
}
1042+
}
1043+
9161044
m.insertMode = true
9171045
m.insertDirection = direction
9181046
m.insertInput.SetValue("")

0 commit comments

Comments
 (0)