summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSadeep Madurange <sadeep@asciimx.com>2026-04-03 18:12:38 +0800
committerSadeep Madurange <sadeep@asciimx.com>2026-04-03 18:32:40 +0800
commit2203cfea1a05c274da879301e2cc48bd748bb05c (patch)
treedc75461726e3f230b4ea1e4bc0d9d7b7abe44a2f
parent1231e1b6d7a74aca4af6a40dddf50c603d6c11c7 (diff)
downloadcvn-2203cfea1a05c274da879301e2cc48bd748bb05c.tar.gz
Empty file as pointer to tree and built-in compare().
-rw-r--r--vcx88
1 files changed, 36 insertions, 52 deletions
diff --git a/vcx b/vcx
index db94101..38d2355 100644
--- a/vcx
+++ b/vcx
@@ -5,6 +5,7 @@ use warnings;
use File::Path qw(make_path);
use File::Copy qw(copy);
use File::Find;
+use File::Compare;
use File::Basename;
use File::Glob qw(:bsd_glob);
use File::Spec;
@@ -12,18 +13,15 @@ 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 TREE_DIR => VCX_DIR . '/trees'; # Trees
-use constant HEAD_FILE => VCX_DIR . '/head'; # Current commit ID
# Staging area
use constant TMP_DIR => VCX_DIR . '/index';
use constant TMP_TREE => TMP_DIR . '/tree';
use constant TMP_DIFF => TMP_DIR . '/deltas';
-
-use constant TMP_META_FILE => VCX_DIR . '/meta';
-use constant TMP_TREE_FILE => VCX_DIR . '/tree';
+use constant TMP_META_FILE => TMP_DIR . '/meta';
my $cmd = shift @ARGV // '';
my @args = @ARGV;
@@ -48,33 +46,32 @@ if ($cmd eq 'init') {
}
sub run_init {
- make_path(OBJ_DIR, REV_DIR, TREE_DIR);
+ make_path(OBJ_DIR, REV_DIR);
my $initial_hex = to_hex_id(0);
my $rev0_dir = File::Spec->catfile(REV_DIR, $initial_hex);
make_path($rev0_dir);
- write_file(HEAD_FILE, "$initial_hex\n");
+ write_file(HEAD, "$initial_hex\n");
# Baseline tree (empty)
my $empty_tree_hash = sha1_hex("");
- write_file(File::Spec->catfile($rev0_dir, "tree"), "$empty_tree_hash\n");
- make_path(File::Spec->catdir(TREE_DIR, $empty_tree_hash));
+ my $empty_tree_file = File::Spec->catfile($rev0_dir, "tree-$empty_tree_hash");
+ open my $fh, '>', $empty_tree_file or die $!; close $fh;
+ make_path(File::Spec->catdir(OBJ_DIR, $empty_tree_hash));
open my $mfh, '>', File::Spec->catfile($rev0_dir, "message"); close $mfh;
-
print "Initialized repository.\n";
}
sub run_status {
- open my $fh, '<', HEAD_FILE or die "VCX not initialized.\n";
+ open my $fh, '<', HEAD or die "VCX not initialized.\n";
my $head = <$fh>; chomp $head; close $fh;
print "On revision [$head]\n";
- my $tree_hash_path = File::Spec->catfile(REV_DIR, $head, "tree");
- open my $tp_fh, '<', $tree_hash_path or die $!;
- my $tree_hash = <$tp_fh>; chomp $tree_hash; close $tp_fh;
- my $latest_tree_dir = File::Spec->catdir(TREE_DIR, $tree_hash);
+ my ($tree_ptr) = bsd_glob(File::Spec->catfile(REV_DIR, $head, "tree-*"));
+ my ($tree_hash) = $tree_ptr =~ /tree-([a-f0-9]{40})$/;
+ my $latest_tree_dir = File::Spec->catdir(OBJ_DIR, $tree_hash);
# Pass 1: Workspace -> History (Detects New and Modified)
find({
@@ -89,7 +86,7 @@ sub run_status {
if (-e $base_in_tree || -l $base_in_tree) {
my $obj_in_store = readlink($base_in_tree);
- if (!compare_files($File::Find::name, $obj_in_store)) {
+ if (compare($_, $obj_in_store) != 0) {
my $staged = check_staged_status($rel, 'M') ? " (staged)" : "";
print "[M] $rel$staged\n";
}
@@ -147,15 +144,15 @@ sub check_staged_status {
sub run_add {
my @targets = @_;
- make_path(TMP_TREE, TMP_DIFF);
- open my $fh_h, '<', HEAD_FILE or die $!;
+ make_path(TMP_TREE);
+
+ open my $fh_h, '<', HEAD or die $!;
my $head = <$fh_h>; chomp $head; close $fh_h;
- my $latest_tree_hash_path = File::Spec->catfile(REV_DIR, $head, "tree");
- open my $tp_fh, '<', $latest_tree_hash_path or die $!;
- my $latest_tree_hash = <$tp_fh>; chomp $latest_tree_hash; close $tp_fh;
- my $latest_tree_dir = File::Spec->catdir(TREE_DIR, $latest_tree_hash);
+ my ($latest_tree_ptr) = bsd_glob(File::Spec->catfile(REV_DIR, $head, "tree-*"));
+ my ($latest_tree_hash) = $latest_tree_ptr =~ /tree-([a-f0-9]{40})$/;
+ my $latest_tree_dir = File::Spec->catdir(OBJ_DIR, $latest_tree_hash);
my $next_id_hex = to_hex_id(from_hex_id($head) + 1);
@@ -183,12 +180,12 @@ sub run_add {
my $prev_link = File::Spec->catfile($latest_tree_dir, $rel);
# CASE 1: Regular File
- if (-f $File::Find::name && !-l $File::Find::name) {
+ if (-f $_ && !-l _) {
return if -e $staged_path; # Already staged
if (-l $prev_link) {
my $obj_in_head = readlink($prev_link);
- return if compare_files($File::Find::name, $obj_in_head);
+ return if compare($_, $obj_in_head) == 0;
}
# Generate deltas if modified
@@ -198,8 +195,10 @@ sub run_add {
if (-e $obj_path) {
my $p_path = File::Spec->catfile(TMP_DIFF, "$obj_name.$next_id_hex.patch");
if (-T $_) {
- system("diff -u '$obj_path' '$_' > '$p_path'")
- if system("diff -q '$obj_path' '$_' > /dev/null") != 0;
+ if (compare($_, $obj_path) != 0) {
+ unless (-d TMP_DIFF) { make_path(TMP_DIFF); }
+ system("diff -u '$obj_path' '$_' > '$p_path'");
+ }
} else {
make_bin_patch($_, $obj_path, $p_path);
}
@@ -232,16 +231,18 @@ sub run_add {
my $tree_data = join("\n", sort @entries);
my $tree_hash = sha1_hex($tree_data);
- write_file(TMP_TREE_FILE, $tree_hash);
+ my $tree_file = File::Spec->catfile(TMP_DIR, "tree-$tree_hash");
+ open my $fh, '>', $tree_file or die $!; close $fh;
}
sub run_commit {
my ($message) = @_;
my $content_changed = -s TMP_META_FILE;
- open my $th_fh, '<', TMP_TREE_FILE or die $!;
- my $staged_hash = <$th_fh>; chomp $staged_hash; close $th_fh;
- my $tree_path = File::Spec->catdir(TREE_DIR, $staged_hash);
+
+ my ($staged_tree_ptr) = bsd_glob(File::Spec->catfile(TMP_DIR, "tree-*"));
+ my ($tree_hash) = $staged_tree_ptr =~ /tree-([a-f0-9]{40})$/;
+ my $tree_path = File::Spec->catdir(OBJ_DIR, $tree_hash);
my $tree_exists = -d $tree_path;
if ($tree_exists && !$content_changed) {
@@ -256,7 +257,7 @@ sub run_commit {
}
# Prepare IDs
- open my $fh_h, '<', HEAD_FILE or die "Not a repository.\n";
+ open my $fh_h, '<', HEAD or die "Not a repository.\n";
my $old_head = <$fh_h>; chomp $old_head; close $fh_h;
my $next_id_hex = to_hex_id(from_hex_id($old_head) + 1);
my $rev_dir = File::Spec->catfile(REV_DIR, $next_id_hex);
@@ -280,7 +281,7 @@ sub run_commit {
rename(TMP_TREE, $tree_path) or die "Failed to save directories: $!";
}
- rename(TMP_TREE_FILE, File::Spec->catfile($rev_dir, "tree"))
+ rename($staged_tree_ptr, File::Spec->catfile($rev_dir, "tree-$tree_hash"))
or die "Failed to save tree pointer to revision: $!";
# Move deltas
@@ -291,7 +292,7 @@ sub run_commit {
}
write_file(File::Spec->catfile($rev_dir, "message"), "$message\n");
- write_file(HEAD_FILE, "$next_id_hex\n"); # Update head
+ write_file(HEAD, "$next_id_hex\n"); # Update head
File::Path::remove_tree(TMP_DIR) if -d TMP_DIR;
@@ -300,7 +301,7 @@ sub run_commit {
}
sub run_log {
- open my $fh_h, '<', HEAD_FILE or die "Not a repository.\n";
+ open my $fh_h, '<', HEAD or die "Not a repository.\n";
my $head = <$fh_h>; chomp $head; close $fh_h;
# Setup pager
@@ -370,7 +371,7 @@ sub stage_link {
sub stage_deletions {
my ($target) = @_;
- open my $fh, '<', HEAD_FILE or die "Not a repository.\n";
+ open my $fh, '<', HEAD or die "Not a repository.\n";
my $head = <$fh>; chomp $head; close $fh;
my $latest_tree = File::Spec->catfile(REV_DIR, $head, "tree");
@@ -457,23 +458,6 @@ sub apply_bin_patch {
close $ptch_fh;
}
-sub compare_files {
- my ($file1, $file2) = @_;
- return 0 unless -s $file1 == -s $file2;
-
- open my $fh1, '<:raw', $file1 or return 0;
- open my $fh2, '<:raw', $file2 or return 0;
-
- my $blk_size = 4096;
- while (1) {
- my $read1 = sysread($fh1, my $buf1, $blk_size);
- my $read2 = sysread($fh2, my $buf2, $blk_size);
- return 0 if $buf1 ne $buf2;
- last if $read1 == 0;
- }
- return 1;
-}
-
# Convert decimal to a padded 7-char hex string
sub to_hex_id { sprintf("%07x", $_[0]) }