Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,11 @@ The following features are for Prolly tree library for Version 0.2.0:

The following features are for Prolly tree library for Version 0.2.1:
- [X] tree diffing and merging examples
- [ ] show history of changes of the Prolly tree (git logs style) using `gitoxide` crate
- [ ] build database index using Prolly Tree
- [X] show history of changes of the Prolly tree (git logs style) using `gitoxide` crate
- [ ] support gluesql as a kv store

The following features are for Prolly tree library for Version 0.2.2:
- [ ] version-controlled databases
- [ ] prolly tree backend for git databases using `gitoxide` crate
- [ ] support for IPFS (InterPlanetary File System) for distributed storage
- [ ] advanced probabilistic splitting algorithms
- [ ] decentralized databases
Expand Down
203 changes: 22 additions & 181 deletions src/bin/git-prolly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,31 +86,12 @@ enum Commands {

/// Show KV state at specific commit
Show {
#[arg(help = "Commit to show")]
commit: String,
#[arg(help = "Commit to show (defaults to HEAD)")]
commit: Option<String>,
#[arg(long, help = "Show only keys")]
keys_only: bool,
},

/// KV-aware log with summaries
Log {
#[arg(long, help = "Show KV change summary")]
kv_summary: bool,
#[arg(long, help = "Filter by key pattern")]
keys: Option<String>,
#[arg(long, help = "Limit number of commits")]
limit: Option<usize>,
},

/// List all branches
Branch,

/// Switch to a branch or commit
Checkout {
#[arg(help = "Branch or commit to checkout")]
target: String,
},

/// Merge another branch
Merge {
#[arg(help = "Branch to merge")]
Expand All @@ -119,13 +100,7 @@ enum Commands {
strategy: Option<String>,
},

/// Revert a commit
Revert {
#[arg(help = "Commit to revert")]
commit: String,
},

/// Show repository statistics
/// Show KV store statistics
Stats {
#[arg(help = "Commit to analyze (defaults to HEAD)")]
commit: Option<String>,
Expand Down Expand Up @@ -168,25 +143,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Commands::Show { commit, keys_only } => {
handle_show(commit, keys_only)?;
}
Commands::Log {
kv_summary,
keys,
limit,
} => {
handle_log(kv_summary, keys, limit)?;
}
Commands::Branch => {
handle_branch()?;
}
Commands::Checkout { target } => {
handle_checkout(target)?;
}
Commands::Merge { branch, strategy } => {
handle_merge(branch, strategy)?;
}
Commands::Revert { commit } => {
handle_revert(commit)?;
}
Commands::Stats { commit } => {
handle_stats(commit)?;
}
Expand All @@ -196,7 +155,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}

fn handle_init(path: Option<PathBuf>) -> Result<(), Box<dyn std::error::Error>> {
let target_path = path.unwrap_or_else(|| env::current_dir().unwrap());
let target_path =
path.unwrap_or_else(|| env::current_dir().unwrap_or_else(|_| PathBuf::from(".")));

println!("Initializing ProllyTree KV store in {target_path:?}...");

Expand All @@ -213,13 +173,9 @@ fn handle_set(key: String, value: String) -> Result<(), Box<dyn std::error::Erro
let current_dir = env::current_dir()?;
let mut store = VersionedKvStore::<32>::open(&current_dir)?;

// Clone the strings before moving them into insert
let key_display = key.clone();
let value_display = value.clone();

store.insert(key.into_bytes(), value.into_bytes())?;
store.insert(key.as_bytes().to_vec(), value.as_bytes().to_vec())?;

println!("✓ Staged: {key_display} = \"{value_display}\"");
println!("✓ Staged: {key} = \"{value}\"");
println!(" (Use 'git prolly commit' to save changes)");

Ok(())
Expand Down Expand Up @@ -467,15 +423,16 @@ fn handle_diff(
Ok(())
}

fn handle_show(commit: String, keys_only: bool) -> Result<(), Box<dyn std::error::Error>> {
fn handle_show(commit: Option<String>, keys_only: bool) -> Result<(), Box<dyn std::error::Error>> {
let current_dir = env::current_dir()?;
let store = VersionedKvStore::<32>::open(&current_dir)?;
let ops = GitOperations::new(store);

let details = ops.show(&commit)?;
let commit_ref = commit.unwrap_or_else(|| "HEAD".to_string());
let details = ops.show(&commit_ref)?;

if keys_only {
println!("Keys at commit {commit}:");
println!("Keys at commit {commit_ref}:");
for change in details.changes {
let key_str = String::from_utf8_lossy(&change.key);
println!(" {key_str}");
Expand Down Expand Up @@ -517,117 +474,6 @@ fn handle_show(commit: String, keys_only: bool) -> Result<(), Box<dyn std::error
Ok(())
}

fn handle_log(
kv_summary: bool,
_keys: Option<String>,
limit: Option<usize>,
) -> Result<(), Box<dyn std::error::Error>> {
let current_dir = env::current_dir()?;
let store = VersionedKvStore::<32>::open(&current_dir)?;

let mut history = store.log()?;

if let Some(limit) = limit {
history.truncate(limit);
}

// Check current branch for the first commit (HEAD)
let current_branch = store.current_branch();
let head_commit_id = store.git_repo().head_id().ok();

for (index, commit) in history.iter().enumerate() {
let date = chrono::DateTime::from_timestamp(commit.timestamp, 0).unwrap_or_default();

// Format like git log: "Wed Jul 16 22:27:36 2025 -0700"
let formatted_date = date.format("%a %b %d %H:%M:%S %Y %z");

// Add branch reference for HEAD commit
let branch_ref = if index == 0
&& head_commit_id.as_ref().map(|id| id.as_ref()) == Some(commit.id.as_ref())
{
format!(" (HEAD -> {current_branch})")
} else {
String::new()
};

if kv_summary {
// Get changes for this commit - create a new store instance
let ops_store = VersionedKvStore::<32>::open(&current_dir)?;
let ops = GitOperations::new(ops_store);
let changes = match ops.show(&commit.id.to_string()) {
Ok(details) => details.changes,
Err(_) => vec![],
};

let added = changes
.iter()
.filter(|c| matches!(c.operation, DiffOperation::Added(_)))
.count();
let removed = changes
.iter()
.filter(|c| matches!(c.operation, DiffOperation::Removed(_)))
.count();
let modified = changes
.iter()
.filter(|c| matches!(c.operation, DiffOperation::Modified { .. }))
.count();

println!("commit {}{}", commit.id, branch_ref);
println!("Author: {}", commit.author);
println!("Date: {formatted_date}");
println!();
println!(
" {} (+{} ~{} -{})",
commit.message, added, modified, removed
);
println!();
} else {
println!("commit {}{}", commit.id, branch_ref);
println!("Author: {}", commit.author);
println!("Date: {formatted_date}");
println!();
println!(" {}", commit.message);
println!();
}
}

Ok(())
}

fn handle_branch() -> Result<(), Box<dyn std::error::Error>> {
let current_dir = env::current_dir()?;
let store = VersionedKvStore::<32>::open(&current_dir)?;

let branches = store.list_branches()?;
let current_branch = store.current_branch();

if branches.is_empty() {
println!("No branches found");
return Ok(());
}

for branch in branches {
if branch == current_branch {
println!("* {branch}");
} else {
println!(" {branch}");
}
}

Ok(())
}

fn handle_checkout(target: String) -> Result<(), Box<dyn std::error::Error>> {
let current_dir = env::current_dir()?;
let mut store = VersionedKvStore::<32>::open(&current_dir)?;

store.checkout(&target)?;

println!("✓ Switched to: {target}");

Ok(())
}

fn handle_merge(
branch: String,
_strategy: Option<String>,
Expand All @@ -648,29 +494,24 @@ fn handle_merge(
println!(" Merge commit: {commit_id}");
}
MergeResult::Conflict(conflicts) => {
println!("⚠ Merge conflicts detected:");
for conflict in conflicts {
println!(" {conflict}");
// Check if this is our "manual merge needed" indicator
if conflicts.len() == 1 && conflicts[0].key == b"<merge>" {
println!("⚠ Cannot automatically merge branches");
println!(" The branches have diverged and require manual merging");
println!(" Use 'git merge {branch}' to perform a manual merge");
} else {
println!("⚠ Merge conflicts detected:");
for conflict in conflicts {
println!(" {conflict}");
}
println!("\nResolve conflicts and run 'git prolly commit' to complete the merge");
}
println!("\nResolve conflicts and run 'git prolly commit' to complete the merge");
}
}

Ok(())
}

fn handle_revert(commit: String) -> Result<(), Box<dyn std::error::Error>> {
let current_dir = env::current_dir()?;
let store = VersionedKvStore::<32>::open(&current_dir)?;
let mut ops = GitOperations::new(store);

ops.revert(&commit)?;

println!("✓ Reverted commit: {commit}");

Ok(())
}

fn handle_stats(commit: Option<String>) -> Result<(), Box<dyn std::error::Error>> {
let current_dir = env::current_dir()?;
let store = VersionedKvStore::<32>::open(&current_dir)?;
Expand Down
Loading