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
2 changes: 1 addition & 1 deletion src/command/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1058,7 +1058,7 @@ async fn resolve_positional_revisions(args: &mut DiffArgs) -> Result<(), DiffErr
let dashdash = !args.after_dashdash.is_empty() || bare_dashdash_in_diff_argv();
// Post-`--` tokens are pathspecs verbatim (no existence check, matching
// `git diff -- nosuch` → empty diff). Fold them in up front.
let trailing_paths: Vec<String> = args.after_dashdash.drain(..).collect();
let trailing_paths = std::mem::take(&mut args.after_dashdash);

if args.old.is_some() || args.new.is_some() {
args.pathspec.extend(trailing_paths);
Expand Down
5 changes: 2 additions & 3 deletions src/command/for_each_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2347,10 +2347,9 @@ fn short_refname(refname: &str) -> String {
fn refname_strip_atom(token: &str, refname: &str) -> Option<String> {
let (from_left, num) = if let Some(n) = token.strip_prefix("refname:lstrip=") {
(true, n)
} else if let Some(n) = token.strip_prefix("refname:rstrip=") {
(false, n)
} else {
return None;
let n = token.strip_prefix("refname:rstrip=")?;
(false, n)
};
Some(strip_ref_components(refname, from_left, num.parse().ok()?))
}
Expand Down
42 changes: 15 additions & 27 deletions src/command/merge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use git_internal::{
use serde::{Deserialize, Serialize};

use super::{
get_target_commit, load_object, log, reset,
get_target_commit, load_object, reset,
restore::{self, RestoreArgs},
save_object, status, switch,
};
Expand All @@ -35,6 +35,7 @@ use crate::{
config::ConfigKv,
db::get_db_conn_instance,
head::Head,
merge_base,
reflog::{ReflogAction, ReflogContext, with_reflog},
tree_plumbing,
},
Expand Down Expand Up @@ -958,7 +959,6 @@ async fn run_merge_for_pull_inner(
})?;

let lca = lca_commit(&current_commit, &target_commit)
.await
.map_err(|error| PullMergeError::History(error.to_string()))?;

let lca = lca.ok_or(PullMergeError::UnrelatedHistories)?;
Expand Down Expand Up @@ -1545,32 +1545,20 @@ async fn resolve_merge_target(target_ref: &str) -> Result<ObjectHash, Box<dyn st
get_target_commit(target_ref).await
}

async fn lca_commit(lhs: &Commit, rhs: &Commit) -> Result<Option<Commit>, CliError> {
let lhs_reachable = log::get_reachable_commits(lhs.id.to_string(), None).await?;
let rhs_reachable = log::get_reachable_commits(rhs.id.to_string(), None).await?;

// Commit `eq` is based on tree_id, so we shouldn't use it here

for commit in lhs_reachable.iter() {
if commit.id == rhs.id {
return Ok(Some(commit.to_owned()));
}
}

for commit in rhs_reachable.iter() {
if commit.id == lhs.id {
return Ok(Some(commit.to_owned()));
}
}
fn lca_commit(lhs: &Commit, rhs: &Commit) -> Result<Option<Commit>, CliError> {
let Some(base_id) = merge_base::merge_base(&lhs.id, &rhs.id).map_err(|error| {
CliError::fatal(format!("failed to compute merge base: {error}"))
.with_stable_code(StableErrorCode::RepoCorrupt)
})?
else {
return Ok(None);
};

for lhs_parent in lhs_reachable.iter() {
for rhs_parent in rhs_reachable.iter() {
if lhs_parent.id == rhs_parent.id {
return Ok(Some(lhs_parent.to_owned()));
}
}
}
Ok(None)
let base = load_object::<Commit>(&base_id).map_err(|error| {
CliError::fatal(format!("failed to load merge base {base_id}: {error}"))
.with_stable_code(StableErrorCode::RepoCorrupt)
})?;
Ok(Some(base))
}

async fn apply_fast_forward_merge(
Expand Down
43 changes: 35 additions & 8 deletions src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,19 +133,24 @@ pub mod switch;
pub mod web_assets;
pub mod write_tree;

use std::{io, io::Write, path::Path};
use std::{
fs::File,
io::{self, Read, Write},
path::Path,
};

use git_internal::{
errors::GitError,
hash::ObjectHash,
internal::object::{ObjectTrait, blob::Blob},
utils::HashAlgorithm,
};
use rpassword::read_password;

use crate::{
internal::protocol::https_client::BasicAuth,
utils,
utils::{client_storage::ClientStorage, error::emit_warning, object_ext::BlobExt, util},
utils::{client_storage::ClientStorage, error::emit_warning, util},
};

// impl load for all objects
Expand Down Expand Up @@ -253,13 +258,35 @@ pub fn ask_basic_auth() -> BasicAuth {
/// Calculate the hash of a file blob
/// - for `lfs` file: calculate hash of the pointer data
pub fn calc_file_blob_hash(path: impl AsRef<Path>) -> io::Result<ObjectHash> {
let blob = if utils::lfs::is_lfs_tracked(&path) {
if utils::lfs::is_lfs_tracked(&path) {
let (pointer, _) = utils::lfs::generate_pointer_file(&path);
Blob::from_content(&pointer)
} else {
Blob::from_file(&path)
};
Ok(blob.id)
return Ok(Blob::from_content(&pointer).id);
}

stream_file_blob_hash(path)
}

fn stream_file_blob_hash(path: impl AsRef<Path>) -> io::Result<ObjectHash> {
let path = path.as_ref();
let file = File::open(path)?;
let len = file.metadata()?.len();
let mut reader = io::BufReader::new(file);
let mut hasher = HashAlgorithm::new();

hasher.update(b"blob ");
hasher.update(len.to_string().as_bytes());
hasher.update(b"\0");

let mut buffer = [0_u8; 64 * 1024];
loop {
let read = reader.read(&mut buffer)?;
if read == 0 {
break;
}
hasher.update(&buffer[..read]);
}

ObjectHash::from_bytes(&hasher.finalize()).map_err(io::Error::other)
}

/// Get the commit hash from branch name or commit hash, support remote branch
Expand Down
Loading
Loading