summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSadeep Madurange <sadeep@asciimx.com>2026-04-18 10:10:04 +0800
committerSadeep Madurange <sadeep@asciimx.com>2026-04-18 10:10:04 +0800
commita4258d636fec97688f3db95bd5d1cdf0bd1344f7 (patch)
tree98f507d95fbc9ce44f03c0ce5b23b523beae22ec
parente9b0bd8e247c4f0e63acde2e898771527708fd2b (diff)
downloadurn-a4258d636fec97688f3db95bd5d1cdf0bd1344f7.tar.gz
Show command.
-rw-r--r--vcx79
1 files changed, 79 insertions, 0 deletions
diff --git a/vcx b/vcx
index df8ab0d..951faeb 100644
--- a/vcx
+++ b/vcx
@@ -46,6 +46,12 @@ if ($cmd eq 'init') {
run_commit($m);
} elsif ($cmd eq 'log') {
run_log();
+} elsif ($cmd eq 'show') {
+ # Usage: vcx show HEAD main.c
+ my $rev = shift @args;
+ my $file = shift @args;
+ die "Usage: $0 show <rev_id|HEAD> <file_path>\n" unless defined $rev && defined $file;
+ run_show($rev, $file);
} else {
print "Usage: $0 [init|status|add|commit|log]\n";
exit 1;
@@ -429,6 +435,79 @@ sub run_log {
select($old_fh);
}
+sub run_show {
+ my ($rev_id, $file_path) = @_;
+
+ if (!defined $rev_id || lc($rev_id) eq 'head') {
+ $rev_id = read_head();
+ }
+
+ die "Usage: $0 show <rev_id|HEAD> <file_path>\n" unless $rev_id && $file_path;
+
+ my $rev_file = File::Spec->catfile(REV_DIR, $rev_id);
+ die "Error: Revision $rev_id not found.\n" unless -f $rev_file;
+
+ # Extract metadata from revision
+ my ($tree_hash, $patch_bundle_hash) = ("", "");
+ open my $rfh, '<', $rev_file or die $!;
+ while (<$rfh>) {
+ if (/^tree:(.*)$/) { $tree_hash = $1; }
+ elsif (/^patch:(.*)$/) { $patch_bundle_hash = $1; }
+ }
+ close $rfh;
+
+ # Locate file in tree
+ my $it = stream_tree_file($tree_hash);
+ my $target_node;
+ while (my $node = $it->()) {
+ if ($node->{path} eq $file_path) {
+ $target_node = $node;
+ last;
+ }
+ }
+ die "Error: File '$file_path' not found in revision $rev_id.\n" unless $target_node;
+
+ my $obj_path = get_obj_path($target_node->{hash});
+ die "Error: Object $target_node->{hash} missing.\n" unless -f $obj_path;
+
+ my $content = read_file($obj_path);
+
+ # Apply patch
+ if ($patch_bundle_hash) {
+ my $bundle_path = get_obj_path($patch_bundle_hash);
+ if (-f $bundle_path) {
+ open my $bfh, '<:raw', $bundle_path or die $!;
+ my $raw_bundle = do { local $/; <$bfh> };
+ close $bfh;
+
+ # Handle magic bytes (1f 8b = Gzip)
+ my $tar_data = (substr($raw_bundle, 0, 2) eq "\x1f\x8b")
+ ? Compress::Zlib::uncompress($raw_bundle)
+ : $raw_bundle;
+
+ my $tar = Archive::Tar->new;
+ $tar->read($tar_data);
+
+ my $patch_name = "$file_path.patch";
+ if ($tar->contains_file($patch_name)) {
+ my $patch_data = $tar->get_content($patch_name);
+ if ($patch_data =~ /^\d+(?:,\d+)?[adc]\d+/) {
+ my ($tfh, $tpath) = tempfile(DIR => TMP_DIR, UNLINK => 1);
+ binmode $tfh, ":raw";
+ print $tfh $content;
+ close $tfh;
+ $content = qx(patch -s -f $tpath -o - <<'EOF'\n$patch_data\nEOF\n);
+ } else {
+ $content = apply_bin_patch($content, $patch_data);
+ }
+ }
+ }
+ }
+
+ binmode STDOUT, ":raw";
+ print $content;
+}
+
sub make_bin_patch {
my ($new_file, $old_file) = @_;