#!/usr/bin/env bash
# Integration tests for: gl drop
set -euo pipefail
source "$(dirname "$0")/helpers.sh"

# ── PRECONDITIONS ─────────────────────────────────────────────────────────────

describe "precond: unknown full hash is rejected"
setup_repo_with_remote
gl_capture drop "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" --yes
assert_exit_fail "$CODE" "precond_unknown_hash"

describe "precond: unknown target name is rejected"
setup_repo_with_remote
gl_capture drop "nonexistent-xyz-9999" --yes
assert_exit_fail "$CODE" "precond_unknown_name"

# ── DROP COMMIT — BY GIT REFERENCE ────────────────────────────────────────────

describe "drop HEAD commit by full hash"
setup_repo_with_remote
commit_file "Keep this" "keep.txt"
commit_file "Drop this" "drop.txt"
drop_hash="$(head_hash)"
out=$(gl drop "$drop_hash" --yes)
assert_exit_ok $? "drop_head_full_ok"
assert_commit_not_in_log "$drop_hash"  "drop_head_full_gone"
assert_head_msg "Keep this"            "drop_head_full_msg"

describe "drop HEAD commit by partial hash"
setup_repo_with_remote
commit_file "Keep partial" "keep-p.txt"
commit_file "Drop partial" "drop-p.txt"
drop_hash="$(head_hash)"
partial="${drop_hash:0:7}"
out=$(gl drop "$partial" --yes)
assert_exit_ok $? "drop_head_partial_ok"
assert_commit_not_in_log "$drop_hash" "drop_head_partial_gone"
assert_head_msg "Keep partial"         "drop_head_partial_msg"

describe "drop non-HEAD commit (middle of stack)"
setup_repo_with_remote
commit_file "First"  "first.txt"
drop_hash="$(head_hash)"
commit_file "Second" "second.txt"
commit_file "Third"  "third.txt"
out=$(gl drop "$drop_hash" --yes)
assert_exit_ok $? "drop_middle_ok"
assert_commit_not_in_log "$drop_hash" "drop_middle_gone"
assert_head_msg "Third"  "drop_middle_top"
assert_msg_at 1 "Second" "drop_middle_second"

describe "drop preserves file content of surviving commits"
setup_repo_with_remote
commit_file "Keeper commit" "keeper.txt"
commit_file "Victim commit" "victim.txt"
victim_hash="$(head_hash)"
out=$(gl drop "$victim_hash" --yes)
assert_exit_ok $? "drop_preserve_ok"
assert_file_content "keeper.txt" "Keeper commit" "drop_preserve_file"

describe "drop commit updates descendant hashes"
setup_repo_with_remote
commit_file "Bottom drop" "bottom-dep.txt"
drop_hash="$(head_hash)"
commit_file "Top dep" "top-dep.txt"
top_before="$(head_hash)"
out=$(gl drop "$drop_hash" --yes)
assert_exit_ok $? "drop_descendant_ok"
top_after="$(head_hash)"
assert_ne "$top_before" "$top_after" "drop_descendant_rehashed"
assert_head_msg "Top dep" "drop_descendant_msg"

describe "drop commit on a woven feature branch"
setup_repo_with_remote
create_feature_branch "g-partial-drop"
switch_to g-partial-drop
commit_file "A1 keep" "a1.txt"
commit_file "A2 drop" "a2.txt"
drop_hash="$(head_hash)"
commit_file "A3 keep" "a3.txt"
switch_to integration
weave_branch "g-partial-drop"
out=$(gl drop "$drop_hash" --yes)
assert_exit_ok $? "drop_branch_commit_ok"
assert_commit_not_in_log "$drop_hash"  "drop_branch_commit_gone"
assert_log_contains "A1 keep"          "drop_branch_a1_remains"
assert_log_contains "A3 keep"          "drop_branch_a3_remains"
assert_log_not_contains "A2 drop"      "drop_branch_a2_gone"

# ── DROP COMMIT — BY SHORT ID ─────────────────────────────────────────────────

describe "drop commit by short ID (woven branch)"
setup_repo_with_remote
create_feature_branch "g-sid-drop"
switch_to g-sid-drop
commit_file "SID drop target" "sid-drop.txt"
commit_file "SID keep above"  "sid-keep.txt"
switch_to integration
weave_branch "g-sid-drop"
commit_sid=$(commit_sid_from_status 'SID drop target')
full_hash=$(git -C "$WORK" log --pretty=%H --all -- sid-drop.txt | head -1)
out=$(gl drop "$commit_sid" --yes)
assert_exit_ok $? "drop_commit_sid_ok"
assert_commit_not_in_log "$full_hash"  "drop_commit_sid_gone"
assert_log_contains "SID keep above"   "drop_commit_sid_sibling_remains"
assert_log_not_contains "SID drop target" "drop_commit_sid_target_gone"

# ── DROP LAST COMMIT ON BRANCH (BRANCH SURVIVES, EMPTY) ───────────────────────

describe "drop last commit on woven branch leaves the branch empty at the base"
setup_repo_with_remote
base_hash=$(git -C "$WORK" rev-parse @{u})
create_feature_branch "g-solo-commit"
switch_to g-solo-commit
commit_file "Solo commit" "solo.txt"
solo_hash="$(head_hash)"
switch_to integration
weave_branch "g-solo-commit"
out=$(gl drop "$solo_hash" --yes)
assert_exit_ok $? "drop_solo_ok"
assert_contains "$out" "branch g-solo-commit now empty" "drop_solo_msg"
assert_commit_not_in_log "$solo_hash" "drop_solo_commit_gone"
assert_branch_exists "g-solo-commit" "drop_solo_branch_kept"
assert_eq "$(git -C "$WORK" rev-parse g-solo-commit)" "$base_hash" "drop_solo_branch_parked"
assert_eq "$(head_hash)" "$base_hash" "drop_solo_merge_gone"

describe "drop last commit by short ID leaves the branch empty"
setup_repo_with_remote
create_feature_branch "g-solo-sid"
switch_to g-solo-sid
commit_file "Solo SID commit" "solo-sid.txt"
switch_to integration
weave_branch "g-solo-sid"
commit_sid=$(commit_sid_from_status 'Solo SID commit')
full_hash=$(git -C "$WORK" log --pretty=%H --all -- solo-sid.txt | head -1)
out=$(gl drop "$commit_sid" --yes)
assert_exit_ok $? "drop_solo_sid_ok"
assert_commit_not_in_log "$full_hash" "drop_solo_sid_commit_gone"
assert_branch_exists "g-solo-sid" "drop_solo_sid_branch_kept"

# ── DROP WOVEN BRANCH — BY FULL NAME ──────────────────────────────────────────

describe "drop woven branch removes all its commits, merge commit, and ref"
setup_repo_with_remote
create_feature_branch "g-woven-drop"
switch_to g-woven-drop
commit_file "Woven A1" "wa1.txt"
commit_file "Woven A2" "wa2.txt"
switch_to integration
weave_branch "g-woven-drop"
out=$(gl drop g-woven-drop --yes)
assert_exit_ok $? "drop_woven_ok"
assert_contains "$out" "and its 2 commits"   "drop_woven_count_msg"
assert_branch_not_exists "g-woven-drop"      "drop_woven_ref_gone"
assert_log_not_contains  "Woven A1"          "drop_woven_a1_gone"
assert_log_not_contains  "Woven A2"          "drop_woven_a2_gone"
assert_log_not_contains  "Merge g-woven-drop" "drop_woven_merge_gone"

# The branch is based on an integration commit, which the weave keeps: the
# count must be the section size, not everything down to the merge-base.
describe "drop woven branch counts only the commits it removes"
setup_repo_with_remote
commit_file "Integration C1" "int1.txt"
git -C "$WORK" branch g-based-on-int HEAD
switch_to g-based-on-int
commit_file "Based A1" "ba1.txt"
commit_file "Based A2" "ba2.txt"
switch_to integration
weave_branch "g-based-on-int"
out=$(gl drop g-based-on-int --yes)
assert_exit_ok $? "drop_based_on_int_ok"
assert_contains "$out" "and its 2 commits" "drop_based_on_int_count_msg"
assert_log_contains     "Integration C1" "drop_based_on_int_kept"
assert_log_not_contains "Based A1"       "drop_based_on_int_a1_gone"
assert_log_not_contains "Based A2"       "drop_based_on_int_a2_gone"

describe "drop woven branch preserves commits on other branches"
setup_repo_with_remote
create_feature_branch "g-keep-woven"
switch_to g-keep-woven
commit_file "Keep woven commit" "kw.txt"
switch_to integration
weave_branch "g-keep-woven"

create_feature_branch "h-drop-woven"
switch_to h-drop-woven
commit_file "Drop woven commit" "dw.txt"
switch_to integration
weave_branch "h-drop-woven"

out=$(gl drop h-drop-woven --yes)
assert_exit_ok $? "drop_woven_preserve_ok"
assert_branch_not_exists "h-drop-woven"     "drop_woven_preserve_dropped_ref"
assert_branch_exists     "g-keep-woven"     "drop_woven_preserve_kept_ref"
assert_log_contains      "Keep woven commit" "drop_woven_preserve_kept_msg"
assert_log_not_contains  "Drop woven commit" "drop_woven_preserve_gone_msg"

# ── DROP WOVEN BRANCH — BY SHORT ID ───────────────────────────────────────────

describe "drop woven branch by branch short ID"
setup_repo_with_remote
create_feature_branch "g-woven-sid"
switch_to g-woven-sid
commit_file "Woven SID commit" "wsid.txt"
switch_to integration
weave_branch "g-woven-sid"
branch_sid=$(branch_sid_from_status 'g-woven-sid')
out=$(gl drop "$branch_sid" --yes)
assert_exit_ok $? "drop_woven_sid_ok"
assert_branch_not_exists "g-woven-sid"     "drop_woven_sid_ref_gone"
assert_log_not_contains  "Woven SID commit" "drop_woven_sid_commit_gone"

describe "drop by branch short ID equals drop by full name"
setup_repo_with_remote
create_feature_branch "g-sid-equiv"
switch_to g-sid-equiv
commit_file "SID equiv commit" "sid-equiv.txt"
switch_to integration
weave_branch "g-sid-equiv"
# Drop by full name as baseline
out=$(gl drop g-sid-equiv --yes)
assert_exit_ok $? "drop_sid_equiv_ok"
assert_branch_not_exists "g-sid-equiv"    "drop_sid_equiv_ref_gone"
assert_log_not_contains  "SID equiv commit" "drop_sid_equiv_commit_gone"

# ── DROP NON-WOVEN BRANCH ─────────────────────────────────────────────────────

describe "drop non-woven branch removes its commits and deletes ref"
setup_repo_with_remote
create_feature_branch "g-nonwoven"
switch_to g-nonwoven
commit_file "Non-woven X" "nwx.txt"
commit_file "Non-woven Y" "nwy.txt"
switch_to integration
git -C "$WORK" merge -q --ff-only g-nonwoven
out=$(gl drop g-nonwoven --yes)
assert_exit_ok $? "drop_nonwoven_ok"
assert_contains "$out" "and its 2 commits" "drop_nonwoven_count_msg"
assert_branch_not_exists "g-nonwoven"   "drop_nonwoven_ref_gone"
assert_log_not_contains  "Non-woven X"  "drop_nonwoven_x_gone"
assert_log_not_contains  "Non-woven Y"  "drop_nonwoven_y_gone"

# ── DROP CO-LOCATED WOVEN BRANCH ──────────────────────────────────────────────

describe "drop co-located woven branch preserves commits for sibling"
setup_repo_with_remote
create_feature_branch "g-coloc-a"
switch_to g-coloc-a
commit_file "Coloc shared commit" "coloc-shared.txt"
switch_to integration
git -C "$WORK" branch h-coloc-b g-coloc-a
weave_branch "g-coloc-a"
out=$(gl drop g-coloc-a --yes)
assert_exit_ok $? "drop_coloc_woven_ok"
assert_contains "$out" "its commits stay on h-coloc-b" "drop_coloc_woven_keeper_msg"
assert_branch_not_exists "g-coloc-a"           "drop_coloc_woven_a_gone"
assert_branch_exists     "h-coloc-b"           "drop_coloc_woven_b_survives"
assert_log_contains      "Coloc shared commit"  "drop_coloc_woven_commits_preserved"

describe "drop co-located non-woven branch only deletes ref"
setup_repo_with_remote
create_feature_branch "g-coloc-nw-a"
switch_to g-coloc-nw-a
commit_file "Coloc NW commit" "coloc-nw.txt"
switch_to integration
git -C "$WORK" branch h-coloc-nw-b g-coloc-nw-a
git -C "$WORK" merge -q --ff-only g-coloc-nw-a
out=$(gl drop g-coloc-nw-a --yes)
assert_exit_ok $? "drop_coloc_nw_ok"
assert_contains "$out" "its commits stay on h-coloc-nw-b" "drop_coloc_nw_keeper_msg"
assert_branch_not_exists "g-coloc-nw-a"    "drop_coloc_nw_a_gone"
assert_branch_exists     "h-coloc-nw-b"    "drop_coloc_nw_b_survives"
assert_log_contains      "Coloc NW commit" "drop_coloc_nw_commits_preserved"

# ── DROP STACKED INNER BRANCH ─────────────────────────────────────────────────

describe "drop stacked inner branch only deletes ref"
setup_repo_with_remote
create_feature_branch "g-stack-inner"
switch_to g-stack-inner
commit_file "Stack inner commit" "stack-inner.txt"
git -C "$WORK" branch h-stack-outer g-stack-inner
switch_to h-stack-outer
commit_file "Stack outer commit" "stack-outer.txt"
switch_to integration
weave_branch "h-stack-outer"
head_before=$(head_hash)
out=$(gl drop g-stack-inner --yes)
assert_exit_ok $? "drop_stack_inner_ok"
assert_contains "$out" "its commits stay on h-stack-outer" "drop_stack_inner_keeper_msg"
assert_branch_not_exists "g-stack-inner"  "drop_stack_inner_gone"
assert_branch_exists     "h-stack-outer"  "drop_stack_inner_outer_survives"
assert_eq "$(head_hash)" "$head_before" "drop_stack_inner_no_rewrite"
assert_log_contains      "Stack inner commit" "drop_stack_inner_commit_preserved"

# ── DROP BRANCH AT MERGE-BASE (NO COMMITS) ────────────────────────────────────

describe "drop branch at merge-base deletes ref, no rebase needed"
setup_repo_with_remote
create_feature_branch "g-empty-branch"
# Dirty working tree is allowed for this case (no rebase is run)
write_file "dirty.txt" "not staged"
out=$(gl drop g-empty-branch --yes)
assert_exit_ok $? "drop_empty_branch_ok"
assert_contains "$out" "Dropped empty branch" "drop_empty_branch_msg"
assert_branch_not_exists "g-empty-branch" "drop_empty_branch_ref_gone"
assert_file_content "dirty.txt" "not staged" "drop_empty_branch_wt_intact"

# Without -y: no prompt, so it succeeds even with stdin not a terminal
describe "drop branch at merge-base needs no confirmation"
setup_repo_with_remote
create_feature_branch "g-empty-noconfirm"
out=$(gl drop g-empty-noconfirm < /dev/null)
assert_exit_ok $? "drop_empty_noconfirm_ok"
assert_contains "$out" "Dropped empty branch" "drop_empty_noconfirm_msg"
assert_branch_not_exists "g-empty-noconfirm" "drop_empty_noconfirm_ref_gone"

# ── DROP BRANCH OUT OF INTEGRATION RANGE ──────────────────────────────────────

describe "drop branch outside integration range is rejected with helpful message"
setup_repo_with_remote
git -C "$WORK" checkout -q -b g-out-of-range
commit_file "Out-of-range commit" "oor.txt"
switch_to integration
gl_capture drop g-out-of-range --yes
assert_exit_fail "$CODE"                          "drop_oor_fail"
assert_contains  "$OUT" "not woven into the integration branch" "drop_oor_msg"

# ── DROP FILE ─────────────────────────────────────────────────────────────────

describe "drop tracked modified file restores it to committed state"
setup_repo_with_remote
commit_file "Original content" "restore-me.txt"
write_file "restore-me.txt" "modified content"
git -C "$WORK" add restore-me.txt
out=$(gl drop restore-me.txt --yes)
assert_exit_ok $? "drop_file_modified_ok"
assert_contains "$out" "Restored"                    "drop_file_modified_msg"
assert_file_content "restore-me.txt" "Original content" "drop_file_modified_restored"

describe "drop staged new file removes it from index and disk"
setup_repo_with_remote
write_file "new-staged.txt" "brand new file"
git -C "$WORK" add new-staged.txt
out=$(gl drop new-staged.txt --yes)
assert_exit_ok $? "drop_file_staged_new_ok"
assert_contains "$out" "Deleted" "drop_file_staged_new_msg"
[[ ! -f "$WORK/new-staged.txt" ]] \
    || fail "[drop_file_staged_new_gone] file still exists after drop"

describe "drop untracked file deletes it from disk"
setup_repo_with_remote
write_file "untracked-drop.txt" "untracked content"
out=$(gl drop untracked-drop.txt --yes)
assert_exit_ok $? "drop_file_untracked_ok"
assert_contains "$out" "Deleted" "drop_file_untracked_msg"
[[ ! -f "$WORK/untracked-drop.txt" ]] \
    || fail "[drop_file_untracked_gone] file still exists after drop"

describe "drop file does not affect other files"
setup_repo_with_remote
commit_file "Untouched file" "untouched.txt"
write_file "to-drop.txt" "drop me"
out=$(gl drop to-drop.txt --yes)
assert_exit_ok $? "drop_file_isolated_ok"
assert_file_content "untouched.txt" "Untouched file" "drop_file_isolated_untouched"

describe "drop several files at once restores and deletes them together"
setup_repo_with_remote
commit_file "Original content" "keep-a.txt"
write_file "keep-a.txt" "modified content"
write_file "new-b.txt" "untracked"
out=$(gl drop keep-a.txt new-b.txt --yes)
assert_exit_ok $? "drop_files_ok"
assert_contains "$out" "Restored" "drop_files_restored_msg"
assert_contains "$out" "Deleted"  "drop_files_deleted_msg"
assert_file_content "keep-a.txt" "Original content" "drop_files_restored"
[[ ! -f "$WORK/new-b.txt" ]] \
    || fail "[drop_files_deleted] file still exists after drop"

describe "drop . discards everything under the repo root"
setup_repo_with_remote
commit_file "Original content" "root-a.txt"
write_file "root-a.txt" "modified content"
write_file "root-b.txt" "untracked"
out=$(gl drop . --yes)
assert_exit_ok $? "drop_dot_ok"
assert_file_content "root-a.txt" "Original content" "drop_dot_restored"
[[ ! -f "$WORK/root-b.txt" ]] \
    || fail "[drop_dot_deleted] file still exists after drop ."

describe "drop several targets refuses anything but files"
setup_repo_with_remote
commit_file "Keep" "keep.txt"
keep_hash="$(head_hash)"
write_file "other.txt" "untracked"
gl_capture drop other.txt "$keep_hash" --yes
assert_exit_fail "$CODE" "drop_files_mixed_fail"
assert_contains "$OUT" "Only files can be dropped together" "drop_files_mixed_msg"
[[ -f "$WORK/other.txt" ]] \
    || fail "[drop_files_mixed_untouched] file was deleted despite the error"

# ── DROP ZZ (ALL LOCAL CHANGES) ───────────────────────────────────────────────

describe "drop zz discards staged changes, unstaged changes, and untracked files"
setup_repo_with_remote
commit_file "Base file" "base.txt"
write_file "base.txt" "modified base"
git -C "$WORK" add base.txt
write_file "new-untracked.txt" "untracked"
out=$(gl drop zz --yes)
assert_exit_ok $? "drop_zz_ok"
assert_contains "$out" "Discarded all local changes" "drop_zz_msg"
assert_file_content "base.txt" "Base file" "drop_zz_tracked_restored"
[[ ! -f "$WORK/new-untracked.txt" ]] \
    || fail "[drop_zz_untracked_gone] untracked file still exists after drop zz"

describe "drop zz with no local changes fails"
setup_repo_with_remote
gl_capture drop zz --yes
assert_exit_fail "$CODE"                       "drop_zz_no_changes_fail"
assert_contains  "$OUT" "No local changes"     "drop_zz_no_changes_msg"

# ── WORKING TREE PRESERVATION ─────────────────────────────────────────────────

describe "drop commit preserves staged changes"
setup_repo_with_remote
commit_file "Victim staged" "victim-staged.txt"
victim_hash="$(head_hash)"
commit_file "Survivor staged" "survivor-staged.txt"
write_file "wt-staged.txt" "staged worktree file"
git -C "$WORK" add wt-staged.txt
out=$(gl drop "$victim_hash" --yes)
assert_exit_ok $? "drop_wt_staged_ok"
git -C "$WORK" diff --cached --name-only | grep -qF "wt-staged.txt" \
    || fail "[drop_wt_staged_restored] staged file not in index after drop"

describe "drop commit preserves unstaged modifications"
setup_repo_with_remote
commit_file "Victim unstaged" "victim-unstaged.txt"
victim_hash="$(head_hash)"
commit_file "Survivor unstaged" "survivor-unstaged.txt"
write_file "survivor-unstaged.txt" "dirty modification"
out=$(gl drop "$victim_hash" --yes)
assert_exit_ok $? "drop_wt_unstaged_ok"
assert_file_content "survivor-unstaged.txt" "dirty modification" "drop_wt_unstaged_preserved"

describe "drop woven branch preserves staged changes"
setup_repo_with_remote
create_feature_branch "g-wt-branch"
switch_to g-wt-branch
commit_file "WT branch commit" "wt-branch.txt"
switch_to integration
weave_branch "g-wt-branch"
write_file "wt-staged-branch.txt" "staged during branch drop"
git -C "$WORK" add wt-staged-branch.txt
out=$(gl drop g-wt-branch --yes)
assert_exit_ok $? "drop_branch_wt_ok"
git -C "$WORK" diff --cached --name-only | grep -qF "wt-staged-branch.txt" \
    || fail "[drop_branch_wt_restored] staged file not in index after branch drop"

describe "drop the only commit of two stacked branches leaves them empty"
setup_repo_with_remote
base_hash=$(git -C "$WORK" rev-parse @{u})
create_feature_branch "inner"
switch_to inner
commit_file "Inner I1" "inner1.txt"
i1_hash=$(head_hash)
git -C "$WORK" branch inner-too inner
git -C "$WORK" branch outer inner
switch_to outer
commit_file "Outer O1" "outer1.txt"
switch_to integration
weave_branch "outer"
out=$(gl drop "$i1_hash" --yes)
assert_exit_ok $? "drop_inner_ok"
assert_contains "$out" "branches inner, inner-too now empty" "drop_inner_msg"
assert_eq "$(git -C "$WORK" rev-parse inner)" "$base_hash" "drop_inner_parked"
assert_eq "$(git -C "$WORK" rev-parse inner-too)" "$base_hash" "drop_inner_too_parked"
assert_eq "$(git -C "$WORK" log --format=%s -1 outer)" "Outer O1" "drop_inner_outer_kept"
assert_eq "$(git -C "$WORK" rev-parse outer^)" "$base_hash" "drop_inner_gone_from_outer"

describe "drop the only commit of a lone stacked branch leaves it empty"
setup_repo_with_remote
base_hash=$(git -C "$WORK" rev-parse @{u})
create_feature_branch "inner"
switch_to inner
commit_file "Inner I1" "inner1.txt"
i1_hash=$(head_hash)
git -C "$WORK" branch outer inner
switch_to outer
commit_file "Outer O1" "outer1.txt"
switch_to integration
weave_branch "outer"
out=$(gl drop "$i1_hash" --yes)
assert_exit_ok $? "drop_lone_inner_ok"
assert_contains "$out" "branch inner now empty" "drop_lone_inner_msg"
assert_branch_exists "inner" "drop_lone_inner_kept"
assert_eq "$(git -C "$WORK" rev-parse inner)" "$base_hash" "drop_lone_inner_parked"
assert_eq "$(git -C "$WORK" rev-parse outer^)" "$base_hash" "drop_lone_inner_gone_from_outer"

describe "dropping the branch itself takes the commit with it"
setup_repo_with_remote
create_feature_branch "solo"
switch_to solo
commit_file "Solo S1" "solo1.txt"
switch_to integration
weave_branch "solo"
out=$(gl drop solo --yes)
assert_exit_ok $? "drop_solo_branch_ok"
assert_branch_not_exists "solo" "drop_solo_branch_gone"

# ── CONTINUE / ABORT ──────────────────────────────────────────────────────────
# Shared conflict setup: C1 changes A→B, C2 changes B→C.
# Dropping C1 forces C2 to cherry-pick onto A → 3-way merge conflict.

describe "drop commit: conflict → continue → commit dropped"
setup_repo_with_remote
printf "A\n" > "$WORK/shared.txt"; git -C "$WORK" add shared.txt; git -C "$WORK" commit -q -m "Base"
printf "B\n" > "$WORK/shared.txt"; git -C "$WORK" add shared.txt; git -C "$WORK" commit -q -m "C1"
printf "C\n" > "$WORK/shared.txt"; git -C "$WORK" add shared.txt; git -C "$WORK" commit -q -m "C2"
c1_hash=$(git -C "$WORK" rev-parse HEAD~1)
gl_capture drop "$c1_hash" --yes
assert_state_file   "drop_cont_state"
assert_contains "$OUT" "loom continue" "drop_cont_hint"
printf "resolved\n" > "$WORK/shared.txt"; git -C "$WORK" add shared.txt
gl_capture continue
assert_exit_ok  "$CODE" "drop_cont_ok"
assert_no_state_file   "drop_cont_state_removed"
assert_contains "$OUT" "Dropped commit" "drop_cont_msg"
assert_log_not_contains "C1" "drop_cont_c1_gone"

describe "drop commit: conflict → abort → original state restored"
setup_repo_with_remote
printf "A\n" > "$WORK/shared.txt"; git -C "$WORK" add shared.txt; git -C "$WORK" commit -q -m "Base"
printf "B\n" > "$WORK/shared.txt"; git -C "$WORK" add shared.txt; git -C "$WORK" commit -q -m "C1"
printf "C\n" > "$WORK/shared.txt"; git -C "$WORK" add shared.txt; git -C "$WORK" commit -q -m "C2"
c1_hash=$(git -C "$WORK" rev-parse HEAD~1)
old_head=$(head_hash)
gl_capture drop "$c1_hash" --yes
assert_state_file "drop_abort_state"
gl_capture abort
assert_exit_ok  "$CODE" "drop_abort_ok"
assert_contains "$OUT" "Aborted" "drop_abort_msg"
assert_no_state_file   "drop_abort_state_removed"
assert_eq "$old_head" "$(head_hash)" "drop_abort_head_restored"
assert_log_contains "C1" "drop_abort_c1_preserved"

# ── STAGED MODIFICATION SURVIVES THE REBASE ──────────────────────────────────

# A staged *new* file keeps its index entry through the autostash; a staged
# modification does not, which is the shape the restore exists for.
describe "drop keeps a staged modification staged"
setup_repo_with_remote
commit_file "Base" "base.txt"
commit_file "Drop me" "drop-me.txt"
drop_hash="$(head_hash)"
commit_file "Keep" "keep.txt"
printf 'base\nstaged edit\n' > "$WORK/base.txt"
git -C "$WORK" add base.txt
gl drop "$drop_hash" --yes >/dev/null
assert_exit_ok $? "drop_staged_mod_ok"
assert_contains "$(git -C "$WORK" status --porcelain)" "M  base.txt" \
    "drop_staged_mod_still_staged"
assert_contains "$(git -C "$WORK" show :base.txt)" "staged edit" "drop_staged_mod_content"

pass
