From cf421aeff4faa15a2a80495345ae76023830ca86 Mon Sep 17 00:00:00 2001 From: Sadeep Madurange Date: Sat, 4 Apr 2026 11:55:08 +0800 Subject: Unit tests and fix hash collisions, deletions. --- vcx | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) (limited to 'vcx') diff --git a/vcx b/vcx index 11ba10d..e872961 100644 --- a/vcx +++ b/vcx @@ -2,7 +2,6 @@ use strict; use warnings; - use File::Path qw(make_path); use File::Copy qw(copy); use File::Find; @@ -15,9 +14,9 @@ use Digest::SHA qw(sha1_hex); use POSIX qw(strftime); use constant VCX_DIR => '.vcx'; -use constant HEAD => VCX_DIR . '/head'; # Current commit ID -use constant OBJ_DIR => VCX_DIR . '/objs'; # Latest version of a file -use constant REV_DIR => VCX_DIR . '/revs'; # Commits +use constant HEAD => VCX_DIR . '/head'; # Current commit ID +use constant OBJ_DIR => VCX_DIR . '/obj'; # Latest version of a file +use constant REV_DIR => VCX_DIR . '/rev'; # Commits # Staging area use constant TMP_DIR => VCX_DIR . '/index'; @@ -160,10 +159,10 @@ sub run_add { my $next_id_hex = to_hex_id(from_hex_id($head) + 1); - my @entries; + my %entries; open my $afh, '>>', TMP_META_FILE or die $!; - init_stage($latest_tree_dir, \@entries); + init_stage($latest_tree_dir, \%entries); foreach my $input (@targets) { my @expanded = bsd_glob($input); @@ -178,7 +177,7 @@ sub run_add { return if -d $_; my $rel = $File::Find::name =~ s|^\./||r; - push @entries, $rel; + $entries{$rel} = 1; my $staged_path = File::Spec->catfile(TMP_TREE, $rel); my $prev_link = File::Spec->catfile($latest_tree_dir, $rel); @@ -197,7 +196,7 @@ sub run_add { my $obj_path = File::Spec->catfile(OBJ_DIR, $obj_name); if (-e $obj_path) { - my $p_path = File::Spec->catfile(TMP_DIFF, "$obj_name.$next_id_hex.patch"); + my $p_path = File::Spec->catfile(TMP_DIFF, "$obj_name.patch"); if (-T $_) { if (compare($_, $obj_path) != 0) { unless (-d TMP_DIFF) { make_path(TMP_DIFF); } @@ -231,9 +230,32 @@ sub run_add { }, $t); } } + + # Pass 2: History -> Workspace (Detects Deletions) + foreach my $path (keys %entries) { + if (!-e $path && !-l $path) { + delete $entries{$path}; + my $staged_path = File::Spec->catfile(TMP_TREE, $path); + if (-e $staged_path || -l $staged_path) { + unlink($staged_path) or die "Could not unlink staged $path: $!"; + my $parent = dirname($staged_path); + while ($parent ne TMP_TREE && -d $parent) { + last if bsd_glob("$parent/*"); # Stop if not empty + rmdir($parent); + $parent = dirname($parent); + } + } + print "[D] $path (staged for deletion)\n"; + } + } + close $afh; - my $tree_data = join("\n", sort @entries); + my @sorted_paths = sort keys %entries; + my $tree_ents = join("\n", @sorted_paths); + my $tree_header = "tree " . scalar(@sorted_paths) . "\n"; + my $tree_data = $tree_header . $tree_ents; + my $tree_hash = sha1_hex($tree_data); my $tree_file = File::Spec->catfile(TMP_DIR, "tree-$tree_hash"); open my $fh, '>', $tree_file or die $!; close $fh; @@ -514,7 +536,7 @@ sub init_stage { my $target = readlink($_); symlink($target, $staged_path) or die "Failed to link $rel: $!"; - push @$entries_ref, $rel; + $entries_ref->{$rel} = 1; }, no_chdir => 1 }, $latest_tree_dir); -- cgit v1.2.3