From 01c4be5d8819edfa2090da47ccec79b31244b853 Mon Sep 17 00:00:00 2001 From: Sadeep Madurange Date: Sat, 18 Apr 2026 18:11:17 +0800 Subject: Detect staged deletions in status. --- vcx | 74 ++++++++++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 43 insertions(+), 31 deletions(-) (limited to 'vcx') diff --git a/vcx b/vcx index 8c3cbc3..cf850ae 100644 --- a/vcx +++ b/vcx @@ -70,69 +70,81 @@ sub run_status { my $it_idx = stream_index(); my $it_wrk = stream_tree("."); - my $head = read_head() // '0'; + # Get the base tree from the last commit + my $head = read_head() // to_hex_id(0); + my $parent_tree_hash = ""; + if ($head ne to_hex_id(0)) { + my $rev_path = File::Spec->catfile(REV_DIR, $head); + if (open my $fh, '<', $rev_path) { + while (<$fh>) { if (/^tree:(.*)$/) { $parent_tree_hash = $1; last; } } + close $fh; + } + } + my $it_old = stream_tree_file($parent_tree_hash); + print "On Revision: $head\n\n"; my $idx = $it_idx->(); my $wrk = $it_wrk->(); + my $old = $it_old->(); my $found_changes = 0; - while ($idx || $wrk) { - # Determine walk direction: -1 (Index only), 1 (Disk only), 0 (Both) - my $cmp = !defined $idx ? 1 - : !defined $wrk ? -1 - : $idx->{path} cmp $wrk->{path}; + # Iterate while any of the three sources have entries + while ($idx || $wrk || $old) { + # Find the alphabetically "lowest" path among the three streams + my $path = (sort grep { defined } ($idx->{path}, $wrk->{path}, $old->{path}))[0]; my $flag = ""; my $suffix = ""; - my $path = ""; - if ($cmp < 0) { - # File exists in Index but is missing from the Disk - $path = $idx->{path}; + # Logical check for Deletions (In Old Tree, but missing elsewhere) + my $in_old = (defined $old && $old->{path} eq $path); + my $in_idx = (defined $idx && $idx->{path} eq $path); + my $in_wrk = (defined $wrk && $wrk->{path} eq $path); + + if ($in_old && !$in_idx) { + # File existed in last commit but is gone from index -> Staged for Deletion + $flag = "[D]"; + $suffix = "(staged)"; + } + elsif ($in_idx && !$in_wrk) { + # In index but missing from disk -> Deleted but not staged $flag = "[D]"; - $idx = $it_idx->(); } - elsif ($cmp > 0) { - # File is on Disk but not yet known to the Index - $path = $wrk->{path}; + elsif ($in_wrk && !$in_idx) { + # On disk but not in index -> New file (Unstaged) $flag = "[N]"; - $wrk = $it_wrk->(); } - else { + elsif ($in_idx && $in_wrk) { # Path exists in both; check for modifications - $path = $idx->{path}; my $stg_path = File::Spec->catfile(TMP_DIR, $path); my $is_staged = -e $stg_path; - - # Compare workspace metadata against the metadata stored in the Index - # (The Index metadata was updated during the last 'add' or 'commit') my $workspace_changed = ($wrk->{mtime} != $idx->{mtime} || $wrk->{size} != $idx->{size}); if ($is_staged) { if ($workspace_changed) { - # It's staged, but the workspace has been touched since the 'add' - $flag = "[M]"; + $flag = "[M]"; $suffix = "(dirty)"; } else { - # It's staged and matches the workspace exactly - # Use hash comparison to see if it's a brand new file - $flag = ($idx->{s_hash} eq $idx->{b_hash}) ? "[N]" : "[M]"; + # Compare against c_hash (Committed Hash) to see if it's new or modified + $flag = ($idx->{c_hash} eq "-") ? "[N]" : "[M]"; $suffix = "(staged)"; } - } elsif ($workspace_changed) { - # Not staged, but differs from the last commit + } elsif ($workspace_changed || ($in_old && $idx->{c_hash} ne $old->{hash})) { + # Not staged, but differs from last commit $flag = "[M]"; } - - $idx = $it_idx->(); - $wrk = $it_wrk->(); } if ($flag ne "") { - printf "%s %s %s\n", $flag, $path, $suffix; + printf "%s %s %s\n", $flag, $path, $suffix; $found_changes = 1; } + + # Advance iterators if they matched the current path + $old = $it_old->() if $in_old; + $idx = $it_idx->() if $in_idx; + $wrk = $it_wrk->() if $in_wrk; } print "No changes detected.\n" unless $found_changes; -- cgit v1.2.3