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
31 changes: 31 additions & 0 deletions src/item_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,37 @@ pub(crate) enum RefKind {
Remote(String),
}

impl RefKind {
/// Convert to fully qualified refname (e.g., "refs/heads/main", "refs/tags/v1.0.0")
pub(crate) fn to_full_refname(&self) -> String {
match self {
RefKind::Branch(name) => format!("refs/heads/{}", name),
RefKind::Tag(name) => format!("refs/tags/{}", name),
RefKind::Remote(name) => format!("refs/remotes/{}", name),
}
}

/// Get the shorthand name without refs/ prefix
pub(crate) fn shorthand(&self) -> &str {
match self {
RefKind::Branch(name) | RefKind::Tag(name) | RefKind::Remote(name) => name,
}
}

/// Convert a git2::Reference to RefKind, returning None if the reference has no shorthand
pub(crate) fn from_reference(reference: &git2::Reference<'_>) -> Option<Self> {
let shorthand = reference.shorthand()?.to_string();

Some(if reference.is_branch() {
RefKind::Branch(shorthand)
} else if reference.is_tag() {
RefKind::Tag(shorthand)
} else {
RefKind::Remote(shorthand)
})
}
}

#[derive(Clone, Debug)]
pub(crate) enum SectionHeader {
Remote(String),
Expand Down
70 changes: 56 additions & 14 deletions src/ops/merge.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use super::{Action, OpTrait, selected_rev};
use super::{Action, OpTrait};
use crate::item_data::RefKind;
use crate::{
Res,
app::{App, PromptParams, State},
app::{App, State},
error::Error,
item_data::ItemData,
menu::arg::Arg,
picker::PickerState,
term::Term,
};

use std::{process::Command, rc::Rc};

pub(crate) fn init_args() -> Vec<Arg> {
Expand Down Expand Up @@ -64,18 +68,56 @@ fn merge(app: &mut App, term: &mut Term, rev: &str) -> Res<()> {

pub(crate) struct Merge;
impl OpTrait for Merge {
fn get_action(&self, _target: &ItemData) -> Option<Action> {
Some(Rc::new(|app: &mut App, term: &mut Term| {
let rev = app.prompt(
term,
&PromptParams {
prompt: "Merge",
create_default_value: Box::new(selected_rev),
..Default::default()
},
)?;

merge(app, term, &rev)?;
fn get_action(&self, target: &ItemData) -> Option<Action> {
// Extract default ref from target if it's a Reference
let default_ref = if let ItemData::Reference { kind, .. } = target {
Some(kind.clone())
} else {
None
};

Some(Rc::new(move |app: &mut App, term: &mut Term| {
// Get current HEAD reference to exclude it from picker
let exclude_ref = {
let head = app.state.repo.head().map_err(Error::GetHead)?;
RefKind::from_reference(&head)
};

// Collect all branches (local and remote)
let branches = app
.state
.repo
.branches(None)
.map_err(Error::ListGitReferences)?
.filter_map(|branch| {
let (branch, _) = branch.ok()?;
RefKind::from_reference(branch.get())
});

// Collect all tags
let tags: Vec<RefKind> = app
.state
.repo
.tag_names(None)
.map_err(Error::ListGitReferences)?
.into_iter()
.flatten()
.map(|tag_name| RefKind::Tag(tag_name.to_string()))
.collect();

let all_refs: Vec<RefKind> = branches.chain(tags).collect();

// Allow custom input to support commit hashes, relative refs (e.g., HEAD~3),
// and other git revisions not in the predefined list
let picker =
PickerState::with_refs("Merge", all_refs, exclude_ref, default_ref.clone(), true);
let result = app.picker(term, picker)?;

if let Some(data) = result {
let rev = data.display();
merge(app, term, rev)?;
}

Ok(())
}))
}
Expand Down
Loading