|
| 1 | +use colored::Colorize; |
| 2 | +use gitbutler_oplog::entry::OperationKind; |
| 3 | +use gitbutler_project::Project; |
| 4 | +use std::path::Path; |
| 5 | + |
| 6 | +pub(crate) fn show_oplog(repo_path: &Path, json: bool, since: Option<&str>) -> anyhow::Result<()> { |
| 7 | + let project = Project::find_by_path(repo_path)?; |
| 8 | + |
| 9 | + let snapshots = if let Some(since_sha) = since { |
| 10 | + // Get all snapshots first to find the starting point |
| 11 | + let all_snapshots = but_api::undo::list_snapshots(project.id, 1000, None, None)?; // Get a large number to find the SHA |
| 12 | + let mut found_index = None; |
| 13 | + |
| 14 | + // Find the snapshot that matches the since SHA (partial match supported) |
| 15 | + for (index, snapshot) in all_snapshots.iter().enumerate() { |
| 16 | + let snapshot_sha = snapshot.commit_id.to_string(); |
| 17 | + if snapshot_sha.starts_with(since_sha) { |
| 18 | + found_index = Some(index); |
| 19 | + break; |
| 20 | + } |
| 21 | + } |
| 22 | + |
| 23 | + match found_index { |
| 24 | + Some(index) => { |
| 25 | + // Take 20 entries starting from the found index |
| 26 | + all_snapshots.into_iter().skip(index).take(20).collect() |
| 27 | + } |
| 28 | + None => { |
| 29 | + return Err(anyhow::anyhow!( |
| 30 | + "No oplog entry found matching SHA: {}", |
| 31 | + since_sha |
| 32 | + )); |
| 33 | + } |
| 34 | + } |
| 35 | + } else { |
| 36 | + but_api::undo::list_snapshots(project.id, 20, None, None)? |
| 37 | + }; |
| 38 | + |
| 39 | + if snapshots.is_empty() { |
| 40 | + if json { |
| 41 | + println!("[]"); |
| 42 | + } else { |
| 43 | + println!("No operations found in history."); |
| 44 | + } |
| 45 | + return Ok(()); |
| 46 | + } |
| 47 | + |
| 48 | + if json { |
| 49 | + // Output JSON format |
| 50 | + let json_output = serde_json::to_string_pretty(&snapshots)?; |
| 51 | + println!("{json_output}"); |
| 52 | + } else { |
| 53 | + // Output human-readable format |
| 54 | + println!("{}", "Operations History".blue().bold()); |
| 55 | + println!("{}", "─".repeat(50).dimmed()); |
| 56 | + |
| 57 | + for snapshot in snapshots { |
| 58 | + let time_string = chrono::DateTime::from_timestamp(snapshot.created_at.seconds(), 0) |
| 59 | + .ok_or(anyhow::anyhow!("Could not parse timestamp"))? |
| 60 | + .format("%Y-%m-%d %H:%M:%S") |
| 61 | + .to_string(); |
| 62 | + |
| 63 | + let commit_id = format!( |
| 64 | + "{}{}", |
| 65 | + &snapshot.commit_id.to_string()[..7].blue().underline(), |
| 66 | + &snapshot.commit_id.to_string()[7..12].blue().dimmed() |
| 67 | + ); |
| 68 | + |
| 69 | + let (operation_type, title) = if let Some(details) = &snapshot.details { |
| 70 | + let op_type = match details.operation { |
| 71 | + OperationKind::CreateCommit => "CREATE", |
| 72 | + OperationKind::CreateBranch => "BRANCH", |
| 73 | + OperationKind::AmendCommit => "AMEND", |
| 74 | + OperationKind::UndoCommit => "UNDO", |
| 75 | + OperationKind::SquashCommit => "SQUASH", |
| 76 | + OperationKind::UpdateCommitMessage => "REWORD", |
| 77 | + OperationKind::MoveCommit => "MOVE", |
| 78 | + OperationKind::RestoreFromSnapshot => "RESTORE", |
| 79 | + OperationKind::ReorderCommit => "REORDER", |
| 80 | + OperationKind::InsertBlankCommit => "INSERT", |
| 81 | + OperationKind::MoveHunk => "MOVE_HUNK", |
| 82 | + OperationKind::ReorderBranches => "REORDER_BRANCH", |
| 83 | + OperationKind::UpdateWorkspaceBase => "UPDATE_BASE", |
| 84 | + OperationKind::UpdateBranchName => "RENAME", |
| 85 | + OperationKind::GenericBranchUpdate => "BRANCH_UPDATE", |
| 86 | + OperationKind::ApplyBranch => "APPLY", |
| 87 | + OperationKind::UnapplyBranch => "UNAPPLY", |
| 88 | + OperationKind::DeleteBranch => "DELETE", |
| 89 | + OperationKind::DiscardChanges => "DISCARD", |
| 90 | + _ => "OTHER", |
| 91 | + }; |
| 92 | + (op_type, details.title.clone()) |
| 93 | + } else { |
| 94 | + ("UNKNOWN", "Unknown operation".to_string()) |
| 95 | + }; |
| 96 | + |
| 97 | + let operation_colored = match operation_type { |
| 98 | + "CREATE" => operation_type.green(), |
| 99 | + "AMEND" | "REWORD" => operation_type.yellow(), |
| 100 | + "UNDO" | "RESTORE" => operation_type.red(), |
| 101 | + "BRANCH" | "CHECKOUT" => operation_type.purple(), |
| 102 | + "MOVE" | "REORDER" | "MOVE_HUNK" => operation_type.cyan(), |
| 103 | + _ => operation_type.normal(), |
| 104 | + }; |
| 105 | + |
| 106 | + println!( |
| 107 | + "{} {} [{}] {}", |
| 108 | + commit_id, |
| 109 | + time_string.dimmed(), |
| 110 | + operation_colored, |
| 111 | + title |
| 112 | + ); |
| 113 | + } |
| 114 | + } |
| 115 | + |
| 116 | + Ok(()) |
| 117 | +} |
0 commit comments