support bare repos (#1028)

This commit is contained in:
Stephan Dilly 2021-12-05 00:35:45 +01:00 committed by GitHub
parent ecabee02da
commit 006cdd6373
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
79 changed files with 1506 additions and 801 deletions

View File

@ -30,6 +30,7 @@ The way this works got changed and simplified ([See docs](https://github.com/ext
- dedicated fuzzy finder up/down keys to allow vim overrides ([#993](https://github.com/extrawurst/gitui/pull/993))
- pull will also download tags ([#1013](https://github.com/extrawurst/gitui/pull/1013))
- allow editing file from filetree ([#989](https://github.com/extrawurst/gitui/pull/989))
- support bare repos (new `workdir` argument) ([#1026](https://github.com/extrawurst/gitui/pull/1026))
### Fixed
- honor options (for untracked files) in `stage_all` command ([#933](https://github.com/extrawurst/gitui/issues/933))

View File

@ -2,7 +2,7 @@
.PHONY: debug build-release release-linux-musl test clippy clippy-pedantic install install-debug
ARGS=-l
# ARGS=-l -d <some_path>
# ARGS=-l -d ~/code/git-bare-test.git -w ~/code/git-bare-test
profile:
cargo run --features=timing,pprof -- ${ARGS}

View File

@ -1,8 +1,8 @@
use crate::{
error::Result,
hash,
sync::{self, FileBlame},
AsyncGitNotification, CWD,
sync::{self, FileBlame, RepoPath},
AsyncGitNotification,
};
use crossbeam_channel::Sender;
use std::{
@ -34,12 +34,17 @@ pub struct AsyncBlame {
last: Arc<Mutex<Option<LastResult<BlameParams, FileBlame>>>>,
sender: Sender<AsyncGitNotification>,
pending: Arc<AtomicUsize>,
repo: RepoPath,
}
impl AsyncBlame {
///
pub fn new(sender: &Sender<AsyncGitNotification>) -> Self {
pub fn new(
repo: RepoPath,
sender: &Sender<AsyncGitNotification>,
) -> Self {
Self {
repo,
current: Arc::new(Mutex::new(Request(0, None))),
last: Arc::new(Mutex::new(None)),
sender: sender.clone(),
@ -96,11 +101,13 @@ impl AsyncBlame {
let arc_last = Arc::clone(&self.last);
let sender = self.sender.clone();
let arc_pending = Arc::clone(&self.pending);
let repo = self.repo.clone();
self.pending.fetch_add(1, Ordering::Relaxed);
rayon_core::spawn(move || {
let notify = Self::get_blame_helper(
&repo,
params,
&arc_last,
&arc_current,
@ -130,6 +137,7 @@ impl AsyncBlame {
}
fn get_blame_helper(
repo_path: &RepoPath,
params: BlameParams,
arc_last: &Arc<
Mutex<Option<LastResult<BlameParams, FileBlame>>>,
@ -138,7 +146,7 @@ impl AsyncBlame {
hash: u64,
) -> Result<bool> {
let file_blame =
sync::blame::blame_file(CWD, &params.file_path)?;
sync::blame::blame_file(repo_path, &params.file_path)?;
let mut notify = false;
{

View File

@ -1,28 +1,27 @@
use crate::{
error::Result,
sync::{self, branch::get_branch_name},
sync::{self, branch::get_branch_name, RepoPathRef},
};
use sync::Head;
///
pub struct BranchName {
last_result: Option<(Head, String)>,
repo_path: String,
repo: RepoPathRef,
}
impl BranchName {
///
pub fn new(path: &str) -> Self {
pub const fn new(repo: RepoPathRef) -> Self {
Self {
repo_path: path.to_string(),
repo,
last_result: None,
}
}
///
pub fn lookup(&mut self) -> Result<String> {
let current_head =
sync::get_head_tuple(self.repo_path.as_str())?;
let current_head = sync::get_head_tuple(&self.repo.borrow())?;
if let Some((last_head, branch_name)) =
self.last_result.as_ref()
@ -41,7 +40,7 @@ impl BranchName {
}
fn fetch(&mut self, head: Head) -> Result<String> {
let name = get_branch_name(self.repo_path.as_str())?;
let name = get_branch_name(&self.repo.borrow())?;
self.last_result = Some((head, name.clone()));
Ok(name)
}

View File

@ -1,7 +1,7 @@
use crate::{
error::Result,
sync::{self, CommitId},
AsyncGitNotification, StatusItem, CWD,
sync::{self, CommitId, RepoPath},
AsyncGitNotification, StatusItem,
};
use crossbeam_channel::Sender;
use std::sync::{
@ -42,12 +42,17 @@ pub struct AsyncCommitFiles {
Arc<Mutex<Option<Request<CommitFilesParams, ResultType>>>>,
sender: Sender<AsyncGitNotification>,
pending: Arc<AtomicUsize>,
repo: RepoPath,
}
impl AsyncCommitFiles {
///
pub fn new(sender: &Sender<AsyncGitNotification>) -> Self {
pub fn new(
repo: RepoPath,
sender: &Sender<AsyncGitNotification>,
) -> Self {
Self {
repo,
current: Arc::new(Mutex::new(None)),
sender: sender.clone(),
pending: Arc::new(AtomicUsize::new(0)),
@ -89,11 +94,12 @@ impl AsyncCommitFiles {
let arc_current = Arc::clone(&self.current);
let sender = self.sender.clone();
let arc_pending = Arc::clone(&self.pending);
let repo = self.repo.clone();
self.pending.fetch_add(1, Ordering::Relaxed);
rayon_core::spawn(move || {
Self::fetch_helper(params, &arc_current)
Self::fetch_helper(&repo, params, &arc_current)
.expect("failed to fetch");
arc_pending.fetch_sub(1, Ordering::Relaxed);
@ -107,13 +113,17 @@ impl AsyncCommitFiles {
}
fn fetch_helper(
repo_path: &RepoPath,
params: CommitFilesParams,
arc_current: &Arc<
Mutex<Option<Request<CommitFilesParams, ResultType>>>,
>,
) -> Result<()> {
let res =
sync::get_commit_files(CWD, params.id, params.other)?;
let res = sync::get_commit_files(
repo_path,
params.id,
params.other,
)?;
log::trace!("get_commit_files: {:?} ({})", params, res.len());

View File

@ -1,8 +1,8 @@
use crate::{
error::Result,
hash,
sync::{self, diff::DiffOptions, CommitId},
AsyncGitNotification, FileDiff, CWD,
sync::{self, diff::DiffOptions, CommitId, RepoPath},
AsyncGitNotification, FileDiff,
};
use crossbeam_channel::Sender;
use std::{
@ -51,12 +51,17 @@ pub struct AsyncDiff {
last: Arc<Mutex<Option<LastResult<DiffParams, FileDiff>>>>,
sender: Sender<AsyncGitNotification>,
pending: Arc<AtomicUsize>,
repo: RepoPath,
}
impl AsyncDiff {
///
pub fn new(sender: &Sender<AsyncGitNotification>) -> Self {
pub fn new(
repo: RepoPath,
sender: &Sender<AsyncGitNotification>,
) -> Self {
Self {
repo,
current: Arc::new(Mutex::new(Request(0, None))),
last: Arc::new(Mutex::new(None)),
sender: sender.clone(),
@ -109,11 +114,13 @@ impl AsyncDiff {
let arc_last = Arc::clone(&self.last);
let sender = self.sender.clone();
let arc_pending = Arc::clone(&self.pending);
let repo = self.repo.clone();
self.pending.fetch_add(1, Ordering::Relaxed);
rayon_core::spawn(move || {
let notify = Self::get_diff_helper(
&repo,
params,
&arc_last,
&arc_current,
@ -143,6 +150,7 @@ impl AsyncDiff {
}
fn get_diff_helper(
repo_path: &RepoPath,
params: DiffParams,
arc_last: &Arc<
Mutex<Option<LastResult<DiffParams, FileDiff>>>,
@ -152,24 +160,24 @@ impl AsyncDiff {
) -> Result<bool> {
let res = match params.diff_type {
DiffType::Stage => sync::diff::get_diff(
CWD,
repo_path,
&params.path,
true,
Some(params.options),
)?,
DiffType::WorkDir => sync::diff::get_diff(
CWD,
repo_path,
&params.path,
false,
Some(params.options),
)?,
DiffType::Commit(id) => sync::diff::get_diff_commit(
CWD,
repo_path,
id,
params.path.clone(),
)?,
DiffType::Commits(ids) => sync::diff::get_diff_commits(
CWD,
repo_path,
ids,
params.path.clone(),
)?,

View File

@ -3,9 +3,9 @@
use crate::{
asyncjob::{AsyncJob, RunParams},
error::Result,
sync::cred::BasicAuthCredential,
sync::remotes::fetch_all,
AsyncGitNotification, ProgressPercent, CWD,
sync::{cred::BasicAuthCredential, RepoPath},
AsyncGitNotification, ProgressPercent,
};
use std::sync::{Arc, Mutex};
@ -16,18 +16,21 @@ enum JobState {
}
///
#[derive(Clone, Default)]
#[derive(Clone)]
pub struct AsyncFetchJob {
state: Arc<Mutex<Option<JobState>>>,
repo: RepoPath,
}
///
impl AsyncFetchJob {
///
pub fn new(
repo: RepoPath,
basic_credential: Option<BasicAuthCredential>,
) -> Self {
Self {
repo,
state: Arc::new(Mutex::new(Some(JobState::Request(
basic_credential,
)))),
@ -61,8 +64,11 @@ impl AsyncJob for AsyncFetchJob {
*state = state.take().map(|state| match state {
JobState::Request(basic_credentials) => {
//TODO: support progress
let result =
fetch_all(CWD, &basic_credentials, &None);
let result = fetch_all(
&self.repo,
&basic_credentials,
&None,
);
JobState::Response(result)
}

View File

@ -94,9 +94,6 @@ pub enum AsyncGitNotification {
Fetch,
}
/// current working directory `./`
pub static CWD: &str = "./";
/// helper function to calculate the hash of an arbitrary type that implements the `Hash` trait
pub fn hash<T: Hash + ?Sized>(v: &T) -> u64 {
let mut hasher = DefaultHasher::new();

View File

@ -3,8 +3,9 @@ use crate::{
sync::{
cred::BasicAuthCredential,
remotes::{fetch, push::ProgressNotification},
RepoPath,
},
AsyncGitNotification, RemoteProgress, CWD,
AsyncGitNotification, RemoteProgress,
};
use crossbeam_channel::{unbounded, Sender};
use std::{
@ -33,12 +34,17 @@ pub struct AsyncPull {
last_result: Arc<Mutex<Option<(usize, String)>>>,
progress: Arc<Mutex<Option<ProgressNotification>>>,
sender: Sender<AsyncGitNotification>,
repo: RepoPath,
}
impl AsyncPull {
///
pub fn new(sender: &Sender<AsyncGitNotification>) -> Self {
pub fn new(
repo: RepoPath,
sender: &Sender<AsyncGitNotification>,
) -> Self {
Self {
repo,
state: Arc::new(Mutex::new(None)),
last_result: Arc::new(Mutex::new(None)),
progress: Arc::new(Mutex::new(None)),
@ -79,6 +85,7 @@ impl AsyncPull {
let arc_res = Arc::clone(&self.last_result);
let arc_progress = Arc::clone(&self.progress);
let sender = self.sender.clone();
let repo = self.repo.clone();
thread::spawn(move || {
let (progress_sender, receiver) = unbounded();
@ -91,7 +98,7 @@ impl AsyncPull {
);
let res = fetch(
CWD,
&repo,
&params.branch,
params.basic_credential,
Some(progress_sender.clone()),

View File

@ -2,9 +2,9 @@ use crate::{
error::{Error, Result},
sync::{
cred::BasicAuthCredential, remotes::push::push,
remotes::push::ProgressNotification,
remotes::push::ProgressNotification, RepoPath,
},
AsyncGitNotification, RemoteProgress, CWD,
AsyncGitNotification, RemoteProgress,
};
use crossbeam_channel::{unbounded, Sender};
use std::{
@ -37,12 +37,17 @@ pub struct AsyncPush {
last_result: Arc<Mutex<Option<String>>>,
progress: Arc<Mutex<Option<ProgressNotification>>>,
sender: Sender<AsyncGitNotification>,
repo: RepoPath,
}
impl AsyncPush {
///
pub fn new(sender: &Sender<AsyncGitNotification>) -> Self {
pub fn new(
repo: RepoPath,
sender: &Sender<AsyncGitNotification>,
) -> Self {
Self {
repo,
state: Arc::new(Mutex::new(None)),
last_result: Arc::new(Mutex::new(None)),
progress: Arc::new(Mutex::new(None)),
@ -83,6 +88,7 @@ impl AsyncPush {
let arc_res = Arc::clone(&self.last_result);
let arc_progress = Arc::clone(&self.progress);
let sender = self.sender.clone();
let repo = self.repo.clone();
thread::spawn(move || {
let (progress_sender, receiver) = unbounded();
@ -95,7 +101,7 @@ impl AsyncPush {
);
let res = push(
CWD,
&repo,
params.remote.as_str(),
params.branch.as_str(),
params.force,

View File

@ -3,8 +3,9 @@ use crate::{
sync::{
cred::BasicAuthCredential,
remotes::tags::{push_tags, PushTagsProgress},
RepoPath,
},
AsyncGitNotification, RemoteProgress, CWD,
AsyncGitNotification, RemoteProgress,
};
use crossbeam_channel::{unbounded, Sender};
use std::{
@ -31,12 +32,17 @@ pub struct AsyncPushTags {
last_result: Arc<Mutex<Option<String>>>,
progress: Arc<Mutex<Option<PushTagsProgress>>>,
sender: Sender<AsyncGitNotification>,
repo: RepoPath,
}
impl AsyncPushTags {
///
pub fn new(sender: &Sender<AsyncGitNotification>) -> Self {
pub fn new(
repo: RepoPath,
sender: &Sender<AsyncGitNotification>,
) -> Self {
Self {
repo,
state: Arc::new(Mutex::new(None)),
last_result: Arc::new(Mutex::new(None)),
progress: Arc::new(Mutex::new(None)),
@ -77,6 +83,7 @@ impl AsyncPushTags {
let arc_res = Arc::clone(&self.last_result);
let arc_progress = Arc::clone(&self.progress);
let sender = self.sender.clone();
let repo = self.repo.clone();
thread::spawn(move || {
let (progress_sender, receiver) = unbounded();
@ -89,7 +96,7 @@ impl AsyncPushTags {
);
let res = push_tags(
CWD,
&repo,
params.remote.as_str(),
params.basic_credential.clone(),
Some(progress_sender),

View File

@ -4,8 +4,11 @@ use crate::{
asyncjob::{AsyncJob, RunParams},
error::Result,
sync::cred::BasicAuthCredential,
sync::remotes::{get_default_remote, tags_missing_remote},
AsyncGitNotification, CWD,
sync::{
remotes::{get_default_remote, tags_missing_remote},
RepoPath,
},
AsyncGitNotification,
};
use std::sync::{Arc, Mutex};
@ -16,18 +19,21 @@ enum JobState {
}
///
#[derive(Clone, Default)]
#[derive(Clone)]
pub struct AsyncRemoteTagsJob {
state: Arc<Mutex<Option<JobState>>>,
repo: RepoPath,
}
///
impl AsyncRemoteTagsJob {
///
pub fn new(
repo: RepoPath,
basic_credential: Option<BasicAuthCredential>,
) -> Self {
Self {
repo,
state: Arc::new(Mutex::new(Some(JobState::Request(
basic_credential,
)))),
@ -60,10 +66,10 @@ impl AsyncJob for AsyncRemoteTagsJob {
if let Ok(mut state) = self.state.lock() {
*state = state.take().map(|state| match state {
JobState::Request(basic_credential) => {
let result =
get_default_remote(CWD).and_then(|remote| {
let result = get_default_remote(&self.repo)
.and_then(|remote| {
tags_missing_remote(
CWD,
&self.repo,
&remote,
basic_credential,
)

View File

@ -1,7 +1,7 @@
use crate::{
error::Result,
sync::{utils::repo, CommitId, LogWalker, LogWalkerFilter},
AsyncGitNotification, CWD,
sync::{repo, CommitId, LogWalker, LogWalkerFilter, RepoPath},
AsyncGitNotification,
};
use crossbeam_channel::Sender;
use git2::Oid;
@ -33,6 +33,7 @@ pub struct AsyncLog {
pending: Arc<AtomicBool>,
background: Arc<AtomicBool>,
filter: Option<LogWalkerFilter>,
repo: RepoPath,
}
static LIMIT_COUNT: usize = 3000;
@ -42,10 +43,12 @@ static SLEEP_BACKGROUND: Duration = Duration::from_millis(1000);
impl AsyncLog {
///
pub fn new(
repo: RepoPath,
sender: &Sender<AsyncGitNotification>,
filter: Option<LogWalkerFilter>,
) -> Self {
Self {
repo,
current: Arc::new(Mutex::new(Vec::new())),
sender: sender.clone(),
pending: Arc::new(AtomicBool::new(false)),
@ -102,7 +105,7 @@ impl AsyncLog {
///
fn head_changed(&self) -> Result<bool> {
if let Ok(head) = repo(CWD)?.head() {
if let Ok(head) = repo(&self.repo)?.head() {
if let Some(head) = head.target() {
return Ok(head != self.current_head()?.into());
}
@ -128,15 +131,16 @@ impl AsyncLog {
let sender = self.sender.clone();
let arc_pending = Arc::clone(&self.pending);
let arc_background = Arc::clone(&self.background);
let filter = self.filter.clone();
let repo = self.repo.clone();
self.pending.store(true, Ordering::Relaxed);
let filter = self.filter.clone();
rayon_core::spawn(move || {
scope_time!("async::revlog");
Self::fetch_helper(
&repo,
&arc_current,
&arc_background,
&sender,
@ -153,13 +157,14 @@ impl AsyncLog {
}
fn fetch_helper(
repo_path: &RepoPath,
arc_current: &Arc<Mutex<Vec<CommitId>>>,
arc_background: &Arc<AtomicBool>,
sender: &Sender<AsyncGitNotification>,
filter: Option<LogWalkerFilter>,
) -> Result<()> {
let mut entries = Vec::with_capacity(LIMIT_COUNT);
let r = repo(CWD)?;
let r = repo(repo_path)?;
let mut walker =
LogWalker::new(&r, LIMIT_COUNT)?.filter(filter);
loop {

View File

@ -1,8 +1,10 @@
use crate::{
error::Result,
hash,
sync::{self, status::StatusType, ShowUntrackedFilesConfig},
AsyncGitNotification, StatusItem, CWD,
sync::{
self, status::StatusType, RepoPath, ShowUntrackedFilesConfig,
},
AsyncGitNotification, StatusItem,
};
use crossbeam_channel::Sender;
use std::{
@ -56,12 +58,17 @@ pub struct AsyncStatus {
last: Arc<Mutex<Status>>,
sender: Sender<AsyncGitNotification>,
pending: Arc<AtomicUsize>,
repo: RepoPath,
}
impl AsyncStatus {
///
pub fn new(sender: Sender<AsyncGitNotification>) -> Self {
pub fn new(
repo: RepoPath,
sender: Sender<AsyncGitNotification>,
) -> Self {
Self {
repo,
current: Arc::new(Mutex::new(Request(0, None))),
last: Arc::new(Mutex::new(Status::default())),
sender,
@ -115,11 +122,13 @@ impl AsyncStatus {
let arc_pending = Arc::clone(&self.pending);
let status_type = params.status_type;
let config = params.config;
let repo = self.repo.clone();
self.pending.fetch_add(1, Ordering::Relaxed);
rayon_core::spawn(move || {
let ok = Self::fetch_helper(
&repo,
status_type,
config,
hash_request,
@ -141,13 +150,14 @@ impl AsyncStatus {
}
fn fetch_helper(
repo: &RepoPath,
status_type: StatusType,
config: Option<ShowUntrackedFilesConfig>,
hash_request: u64,
arc_current: &Arc<Mutex<Request<u64, Status>>>,
arc_last: &Arc<Mutex<Status>>,
) -> Result<()> {
let res = Self::get_status(status_type, config)?;
let res = Self::get_status(repo, status_type, config)?;
log::trace!(
"status fetched: {} (type: {:?})",
hash_request,
@ -170,12 +180,13 @@ impl AsyncStatus {
}
fn get_status(
repo: &RepoPath,
status_type: StatusType,
config: Option<ShowUntrackedFilesConfig>,
) -> Result<Status> {
Ok(Status {
items: sync::status::get_status(
CWD,
repo,
status_type,
config,
)?,

View File

@ -1,9 +1,9 @@
//! Sync git API for fetching a file blame
use super::{utils, CommitId};
use super::{utils, CommitId, RepoPath};
use crate::{
error::{Error, Result},
sync::get_commits_info,
sync::{get_commits_info, repository::repo},
};
use scopetime::scope_time;
use std::collections::{HashMap, HashSet};
@ -54,12 +54,12 @@ fn fixup_windows_path(path: &str) -> String {
///
pub fn blame_file(
repo_path: &str,
repo_path: &RepoPath,
file_path: &str,
) -> Result<FileBlame> {
scope_time!("blame_file");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let commit_id = utils::get_head_repo(&repo)?;
@ -142,9 +142,9 @@ pub fn blame_file(
#[cfg(test)]
mod tests {
use super::*;
use crate::error::Result;
use crate::sync::{
commit, stage_add_file, tests::repo_init_empty,
use crate::{
error::Result,
sync::{commit, stage_add_file, tests::repo_init_empty},
};
use std::{
fs::{File, OpenOptions},
@ -157,7 +157,8 @@ mod tests {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty()?;
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
assert!(matches!(blame_file(&repo_path, "foo"), Err(_)));
@ -237,7 +238,8 @@ mod tests {
let file_path = Path::new("bar\\foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
std::fs::create_dir(&root.join("bar")).unwrap();

View File

@ -3,7 +3,7 @@
use super::BranchType;
use crate::{
error::{Error, Result},
sync::{merge_msg, utils, CommitId},
sync::{merge_msg, repository::repo, CommitId, RepoPath},
};
use git2::Commit;
use scopetime::scope_time;
@ -12,12 +12,12 @@ use scopetime::scope_time;
/// if we did not create conflicts we create a merge commit and return the commit id.
/// Otherwise we return `None`
pub fn merge_upstream_commit(
repo_path: &str,
repo_path: &RepoPath,
branch_name: &str,
) -> Result<Option<CommitId>> {
scope_time!("merge_upstream_commit");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let branch = repo.find_branch(branch_name, BranchType::Local)?;
let upstream = branch.upstream()?;
@ -130,7 +130,7 @@ mod test {
);
push(
clone1_dir.path().to_str().unwrap(),
&clone1_dir.path().to_str().unwrap().into(),
"origin",
"master",
false,
@ -152,28 +152,36 @@ mod test {
//push should fail since origin diverged
assert!(push(
clone2_dir, "origin", "master", false, false, None, None,
&clone2_dir.into(),
"origin",
"master",
false,
false,
None,
None.into(),
)
.is_err());
//lets fetch from origin
let bytes = fetch(clone2_dir, "master", None, None).unwrap();
let bytes =
fetch(&clone2_dir.into(), "master", None, None).unwrap();
assert!(bytes > 0);
//we should be one commit behind
assert_eq!(
branch_compare_upstream(clone2_dir, "master")
branch_compare_upstream(&clone2_dir.into(), "master")
.unwrap()
.behind,
1
);
let merge_commit =
merge_upstream_commit(clone2_dir, "master")
merge_upstream_commit(&clone2_dir.into(), "master")
.unwrap()
.unwrap();
let state = crate::sync::repo_state(clone2_dir).unwrap();
let state =
crate::sync::repo_state(&clone2_dir.into()).unwrap();
assert_eq!(state, RepoState::Clean);
assert!(!clone2.head_detached().unwrap());
@ -185,9 +193,11 @@ mod test {
assert_eq!(commits[2], commit1);
//verify commit msg
let details =
crate::sync::get_commit_details(clone2_dir, merge_commit)
.unwrap();
let details = crate::sync::get_commit_details(
&clone2_dir.into(),
merge_commit.into(),
)
.unwrap();
assert_eq!(
details.message.unwrap().combine(),
String::from("Merge remote-tracking branch 'refs/remotes/origin/master'")
@ -214,12 +224,12 @@ mod test {
);
debug_cmd_print(
clone2_dir.path().to_str().unwrap(),
&clone2_dir.path().to_str().unwrap().into(),
"git status",
);
push(
clone1_dir.path().to_str().unwrap(),
&clone1_dir.path().to_str().unwrap().into(),
"origin",
"master",
false,
@ -239,7 +249,7 @@ mod test {
);
let bytes = fetch(
clone2_dir.path().to_str().unwrap(),
&clone2_dir.path().to_str().unwrap().into(),
"master",
None,
None,
@ -248,7 +258,7 @@ mod test {
assert!(bytes > 0);
let res = merge_upstream_commit(
clone2_dir.path().to_str().unwrap(),
&clone2_dir.path().to_str().unwrap().into(),
"master",
)
.unwrap();
@ -257,7 +267,7 @@ mod test {
assert_eq!(res, None);
let state = crate::sync::repo_state(
clone2_dir.path().to_str().unwrap(),
&clone2_dir.path().to_str().unwrap().into(),
)
.unwrap();

View File

@ -3,18 +3,18 @@
use super::BranchType;
use crate::{
error::{Error, Result},
sync::utils,
sync::{repository::repo, RepoPath},
};
use scopetime::scope_time;
///
pub fn branch_merge_upstream_fastforward(
repo_path: &str,
repo_path: &RepoPath,
branch: &str,
) -> Result<()> {
scope_time!("branch_merge_upstream");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let branch = repo.find_branch(branch, BranchType::Local)?;
let upstream = branch.upstream()?;
@ -76,7 +76,7 @@ pub mod test {
write_commit_file(&clone1, "test.txt", "test", "commit1");
push(
clone1_dir.path().to_str().unwrap(),
&clone1_dir.path().to_str().unwrap().into(),
"origin",
"master",
false,
@ -88,7 +88,7 @@ pub mod test {
// clone2
debug_cmd_print(
clone2_dir.path().to_str().unwrap(),
&clone2_dir.path().to_str().unwrap().into(),
"git pull --ff",
);
@ -100,7 +100,7 @@ pub mod test {
);
push(
clone2_dir.path().to_str().unwrap(),
&clone2_dir.path().to_str().unwrap().into(),
"origin",
"master",
false,
@ -113,7 +113,7 @@ pub mod test {
// clone1 again
let bytes = fetch(
clone1_dir.path().to_str().unwrap(),
&clone1_dir.path().to_str().unwrap().into(),
"master",
None,
None,
@ -122,7 +122,7 @@ pub mod test {
assert!(bytes > 0);
let bytes = fetch(
clone1_dir.path().to_str().unwrap(),
&clone1_dir.path().to_str().unwrap().into(),
"master",
None,
None,
@ -131,7 +131,7 @@ pub mod test {
assert_eq!(bytes, 0);
branch_merge_upstream_fastforward(
clone1_dir.path().to_str().unwrap(),
&clone1_dir.path().to_str().unwrap().into(),
"master",
)
.unwrap();

View File

@ -2,19 +2,22 @@
use crate::{
error::{Error, Result},
sync::{rebase::conflict_free_rebase, utils, CommitId},
sync::{
rebase::conflict_free_rebase, repository::repo, CommitId,
RepoPath,
},
};
use git2::BranchType;
use scopetime::scope_time;
/// trys merging current branch with its upstrema using rebase
pub fn merge_upstream_rebase(
repo_path: &str,
repo_path: &RepoPath,
branch_name: &str,
) -> Result<CommitId> {
scope_time!("merge_upstream_rebase");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
if super::get_branch_name_repo(&repo)? != branch_name {
return Err(Error::Generic(String::from(
"can only rebase in head branch",
@ -47,7 +50,7 @@ mod test {
fn get_commit_msgs(r: &Repository) -> Vec<String> {
let commits = get_commit_ids(r, 10);
get_commits_info(
r.workdir().unwrap().to_str().unwrap(),
&r.workdir().unwrap().to_str().unwrap().into(),
&commits,
10,
)
@ -79,7 +82,13 @@ mod test {
assert_eq!(clone1.head_detached().unwrap(), false);
push(
clone1_dir, "origin", "master", false, false, None, None,
&clone1_dir.into(),
"origin",
"master",
false,
false,
None,
None,
)
.unwrap();
@ -103,7 +112,13 @@ mod test {
assert_eq!(clone2.head_detached().unwrap(), false);
push(
clone2_dir, "origin", "master", false, false, None, None,
&clone2_dir.into(),
"origin",
"master",
false,
false,
None,
None,
)
.unwrap();
@ -122,12 +137,13 @@ mod test {
assert_eq!(clone1.head_detached().unwrap(), false);
//lets fetch from origin
let bytes = fetch(clone1_dir, "master", None, None).unwrap();
let bytes =
fetch(&clone1_dir.into(), "master", None, None).unwrap();
assert!(bytes > 0);
//we should be one commit behind
assert_eq!(
branch_compare_upstream(clone1_dir, "master")
branch_compare_upstream(&clone1_dir.into(), "master")
.unwrap()
.behind,
1
@ -137,11 +153,12 @@ mod test {
assert_eq!(clone1.head_detached().unwrap(), false);
merge_upstream_rebase(clone1_dir, "master").unwrap();
merge_upstream_rebase(&clone1_dir.into(), "master").unwrap();
debug_cmd_print(clone1_dir, "git log");
debug_cmd_print(&clone1_dir.into(), "git log");
let state = crate::sync::repo_state(clone1_dir).unwrap();
let state =
crate::sync::repo_state(&clone1_dir.into()).unwrap();
assert_eq!(state, RepoState::Clean);
let commits = get_commit_msgs(&clone1);
@ -177,7 +194,13 @@ mod test {
);
push(
clone1_dir, "origin", "master", false, false, None, None,
&clone1_dir.into(),
"origin",
"master",
false,
false,
None,
None,
)
.unwrap();
@ -197,7 +220,13 @@ mod test {
);
push(
clone2_dir, "origin", "master", false, false, None, None,
&clone2_dir.into(),
"origin",
"master",
false,
false,
None,
None,
)
.unwrap();
@ -220,13 +249,14 @@ mod test {
//lets fetch from origin
fetch(clone1_dir, "master", None, None).unwrap();
fetch(&clone1_dir.into(), "master", None, None).unwrap();
merge_upstream_rebase(clone1_dir, "master").unwrap();
merge_upstream_rebase(&clone1_dir.into(), "master").unwrap();
debug_cmd_print(clone1_dir, "git log");
debug_cmd_print(&clone1_dir.into(), "git log");
let state = crate::sync::repo_state(clone1_dir).unwrap();
let state =
crate::sync::repo_state(&clone1_dir.into()).unwrap();
assert_eq!(state, RepoState::Clean);
let commits = get_commit_msgs(&clone1);
@ -258,7 +288,13 @@ mod test {
write_commit_file(&clone1, "test.txt", "test", "commit1");
push(
clone1_dir, "origin", "master", false, false, None, None,
&clone1_dir.into(),
"origin",
"master",
false,
false,
None,
None,
)
.unwrap();
@ -277,7 +313,13 @@ mod test {
);
push(
clone2_dir, "origin", "master", false, false, None, None,
&clone2_dir.into(),
"origin",
"master",
false,
false,
None,
None,
)
.unwrap();
@ -286,20 +328,22 @@ mod test {
let _commit3 =
write_commit_file(&clone1, "test2.txt", "foo", "commit3");
let bytes = fetch(clone1_dir, "master", None, None).unwrap();
let bytes =
fetch(&clone1_dir.into(), "master", None, None).unwrap();
assert!(bytes > 0);
assert_eq!(
branch_compare_upstream(clone1_dir, "master")
branch_compare_upstream(&clone1_dir.into(), "master")
.unwrap()
.behind,
1
);
let res = merge_upstream_rebase(clone1_dir, "master");
let res = merge_upstream_rebase(&clone1_dir.into(), "master");
assert!(res.is_err());
let state = crate::sync::repo_state(clone1_dir).unwrap();
let state =
crate::sync::repo_state(&clone1_dir.into()).unwrap();
assert_eq!(state, RepoState::Clean);

View File

@ -5,23 +5,24 @@ pub mod merge_ff;
pub mod merge_rebase;
pub mod rename;
use std::collections::HashSet;
use super::{
remotes::get_default_remote_in_repo, utils::bytes2string,
RepoPath,
};
use crate::{
error::{Error, Result},
sync::{utils, CommitId},
sync::{repository::repo, utils::get_head_repo, CommitId},
};
use git2::{Branch, BranchType, Repository};
use scopetime::scope_time;
use utils::get_head_repo;
use std::collections::HashSet;
/// returns the branch-name head is currently pointing to
/// this might be expensive, see `cached::BranchName`
pub(crate) fn get_branch_name(repo_path: &str) -> Result<String> {
let repo = utils::repo(repo_path)?;
pub(crate) fn get_branch_name(
repo_path: &RepoPath,
) -> Result<String> {
let repo = repo(repo_path)?;
get_branch_name_repo(&repo)
}
@ -111,12 +112,12 @@ pub fn validate_branch_name(name: &str) -> Result<bool> {
/// returns a list of `BranchInfo` with a simple summary on each branch
/// `local` filters for local branches otherwise remote branches will be returned
pub fn get_branches_info(
repo_path: &str,
repo_path: &RepoPath,
local: bool,
) -> Result<Vec<BranchInfo>> {
scope_time!("get_branches_info");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let (filter, remotes_with_tracking) = if local {
(BranchType::Local, HashSet::default())
@ -214,10 +215,10 @@ pub(crate) fn branch_set_upstream(
/// returns remote of the upstream tracking branch for `branch`
pub fn get_branch_remote(
repo_path: &str,
repo_path: &RepoPath,
branch: &str,
) -> Result<Option<String>> {
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let branch = repo.find_branch(branch, BranchType::Local)?;
let reference = bytes2string(branch.get().name_bytes())?;
let remote_name = repo.branch_upstream_remote(&reference).ok();
@ -229,8 +230,8 @@ pub fn get_branch_remote(
}
/// returns whether the pull merge strategy is set to rebase
pub fn config_is_pull_rebase(repo_path: &str) -> Result<bool> {
let repo = utils::repo(repo_path)?;
pub fn config_is_pull_rebase(repo_path: &RepoPath) -> Result<bool> {
let repo = repo(repo_path)?;
let config = repo.config()?;
if let Ok(rebase) = config.get_entry("pull.rebase") {
@ -244,12 +245,12 @@ pub fn config_is_pull_rebase(repo_path: &str) -> Result<bool> {
///
pub fn branch_compare_upstream(
repo_path: &str,
repo_path: &RepoPath,
branch: &str,
) -> Result<BranchCompare> {
scope_time!("branch_compare_upstream");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let branch = repo.find_branch(branch, BranchType::Local)?;
@ -269,14 +270,14 @@ pub fn branch_compare_upstream(
/// Modify HEAD to point to a branch then checkout head, does not work if there are uncommitted changes
pub fn checkout_branch(
repo_path: &str,
repo_path: &RepoPath,
branch_ref: &str,
) -> Result<()> {
scope_time!("checkout_branch");
// This defaults to a safe checkout, so don't delete anything that
// hasn't been committed or stashed, in this case it will Err
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let cur_ref = repo.head()?;
let statuses = repo.statuses(Some(
git2::StatusOptions::new().include_ignored(false),
@ -302,12 +303,12 @@ pub fn checkout_branch(
///
pub fn checkout_remote_branch(
repo_path: &str,
repo_path: &RepoPath,
branch: &BranchInfo,
) -> Result<()> {
scope_time!("checkout_remote_branch");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let cur_ref = repo.head()?;
if !repo
@ -345,12 +346,12 @@ pub fn checkout_remote_branch(
/// The user must not be on the branch for the branch to be deleted
pub fn delete_branch(
repo_path: &str,
repo_path: &RepoPath,
branch_ref: &str,
) -> Result<()> {
scope_time!("delete_branch");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let branch_as_ref = repo.find_reference(branch_ref)?;
let mut branch = git2::Branch::wrap(branch_as_ref);
if branch.is_head() {
@ -361,10 +362,13 @@ pub fn delete_branch(
}
/// creates a new branch pointing to current HEAD commit and updating HEAD to new branch
pub fn create_branch(repo_path: &str, name: &str) -> Result<String> {
pub fn create_branch(
repo_path: &RepoPath,
name: &str,
) -> Result<String> {
scope_time!("create_branch");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let head_id = get_head_repo(&repo)?;
let head_commit = repo.find_commit(head_id.into())?;
@ -386,7 +390,8 @@ mod tests_branch_name {
fn test_smoke() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
assert_eq!(
get_branch_name(repo_path).unwrap().as_str(),
@ -398,7 +403,8 @@ mod tests_branch_name {
fn test_empty_repo() {
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
assert!(matches!(
get_branch_name(repo_path),
@ -416,7 +422,8 @@ mod tests_create_branch {
fn test_smoke() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
create_branch(repo_path, "branch1").unwrap();
@ -436,7 +443,8 @@ mod tests_branch_compare {
fn test_smoke() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
create_branch(repo_path, "test").unwrap();
@ -462,7 +470,8 @@ mod tests_branches {
fn test_smoke() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
assert_eq!(
get_branches_info(repo_path, true)
@ -478,7 +487,8 @@ mod tests_branches {
fn test_multiple() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
create_branch(repo_path, "test").unwrap();
@ -497,9 +507,18 @@ mod tests_branches {
let dir = dir.path().to_str().unwrap();
write_commit_file(&repo, "f1.txt", "foo", "c1");
rename_branch(dir, "refs/heads/master", branch_name).unwrap();
push(dir, "origin", branch_name, false, false, None, None)
rename_branch(&dir.into(), "refs/heads/master", branch_name)
.unwrap();
push(
&dir.into(),
"origin",
branch_name,
false,
false,
None,
None,
)
.unwrap();
}
#[test]
@ -516,7 +535,8 @@ mod tests_branches {
clone_branch_commit_push(r2_path, "r2branch");
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
//add the remotes
repo.remote("r1", r1_path).unwrap();
@ -588,7 +608,8 @@ mod tests_branches {
fn test_branch_remote_no_upstream() {
let (_r, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
assert_eq!(
get_branch_remote(repo_path, "master").unwrap(),
@ -600,7 +621,8 @@ mod tests_branches {
fn test_branch_remote_no_branch() {
let (_r, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
assert!(get_branch_remote(repo_path, "foo").is_err());
}
@ -615,7 +637,8 @@ mod tests_checkout {
fn test_smoke() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
assert!(
checkout_branch(repo_path, "refs/heads/master").is_ok()
@ -629,7 +652,8 @@ mod tests_checkout {
fn test_multiple() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
create_branch(repo_path, "test").unwrap();
@ -650,7 +674,8 @@ mod test_delete_branch {
fn test_delete_branch() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
create_branch(repo_path, "branch1").unwrap();
create_branch(repo_path, "branch2").unwrap();
@ -720,16 +745,30 @@ mod test_remote_branches {
write_commit_file(&clone1, "test.txt", "test", "commit1");
push(
clone1_dir, "origin", "master", false, false, None, None,
&clone1_dir.into(),
"origin",
"master",
false,
false,
None,
None,
)
.unwrap();
create_branch(clone1_dir, "foo").unwrap();
create_branch(&clone1_dir.into(), "foo").unwrap();
write_commit_file(&clone1, "test.txt", "test2", "commit2");
push(clone1_dir, "origin", "foo", false, false, None, None)
.unwrap();
push(
&clone1_dir.into(),
"origin",
"foo",
false,
false,
None,
None,
)
.unwrap();
// clone2
@ -739,11 +778,12 @@ mod test_remote_branches {
let clone2_dir = clone2_dir.path().to_str().unwrap();
let local_branches =
get_branches_info(clone2_dir, true).unwrap();
get_branches_info(&clone2_dir.into(), true).unwrap();
assert_eq!(local_branches.len(), 1);
let branches = get_branches_info(clone2_dir, false).unwrap();
let branches =
get_branches_info(&clone2_dir.into(), false).unwrap();
assert_eq!(dbg!(&branches).len(), 3);
assert_eq!(&branches[0].name, "origin/HEAD");
assert_eq!(&branches[1].name, "origin/foo");
@ -762,13 +802,27 @@ mod test_remote_branches {
write_commit_file(&clone1, "test.txt", "test", "commit1");
push(
clone1_dir, "origin", "master", false, false, None, None,
&clone1_dir.into(),
"origin",
"master",
false,
false,
None,
None,
)
.unwrap();
create_branch(clone1_dir, "foo").unwrap();
create_branch(&clone1_dir.into(), "foo").unwrap();
write_commit_file(&clone1, "test.txt", "test2", "commit2");
push(clone1_dir, "origin", "foo", false, false, None, None)
.unwrap();
push(
&clone1_dir.into(),
"origin",
"foo",
false,
false,
None,
None,
)
.unwrap();
// clone2
@ -778,21 +832,28 @@ mod test_remote_branches {
let clone2_dir = clone2_dir.path().to_str().unwrap();
let local_branches =
get_branches_info(clone2_dir, true).unwrap();
get_branches_info(&clone2_dir.into(), true).unwrap();
assert_eq!(local_branches.len(), 1);
let branches = get_branches_info(clone2_dir, false).unwrap();
let branches =
get_branches_info(&clone2_dir.into(), false).unwrap();
// checkout origin/foo
checkout_remote_branch(clone2_dir, &branches[1]).unwrap();
checkout_remote_branch(&clone2_dir.into(), &branches[1])
.unwrap();
assert_eq!(
get_branches_info(clone2_dir, true).unwrap().len(),
get_branches_info(&clone2_dir.into(), true)
.unwrap()
.len(),
2
);
assert_eq!(&get_branch_name(clone2_dir).unwrap(), "foo");
assert_eq!(
&get_branch_name(&clone2_dir.into()).unwrap(),
"foo"
);
}
#[test]
@ -809,13 +870,19 @@ mod test_remote_branches {
write_commit_file(&clone1, "test.txt", "test", "commit1");
push(
clone1_dir, "origin", "master", false, false, None, None,
&clone1_dir.into(),
"origin",
"master",
false,
false,
None,
None,
)
.unwrap();
create_branch(clone1_dir, branch_name).unwrap();
create_branch(&clone1_dir.into(), branch_name).unwrap();
write_commit_file(&clone1, "test.txt", "test2", "commit2");
push(
clone1_dir,
&clone1_dir.into(),
"origin",
branch_name,
false,
@ -831,12 +898,14 @@ mod test_remote_branches {
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let clone2_dir = clone2_dir.path().to_str().unwrap();
let branches = get_branches_info(clone2_dir, false).unwrap();
let branches =
get_branches_info(&clone2_dir.into(), false).unwrap();
checkout_remote_branch(clone2_dir, &branches[1]).unwrap();
checkout_remote_branch(&clone2_dir.into(), &branches[1])
.unwrap();
assert_eq!(
&get_branch_name(clone2_dir).unwrap(),
&get_branch_name(&clone2_dir.into()).unwrap(),
branch_name
);
}
@ -853,16 +922,30 @@ mod test_remote_branches {
write_commit_file(&clone1, "test.txt", "test", "commit1");
push(
clone1_dir, "origin", "master", false, false, None, None,
&clone1_dir.into(),
"origin",
"master",
false,
false,
None,
None,
)
.unwrap();
create_branch(clone1_dir, "foo").unwrap();
create_branch(&clone1_dir.into(), "foo").unwrap();
write_commit_file(&clone1, "test.txt", "test2", "commit2");
push(clone1_dir, "origin", "foo", false, false, None, None)
.unwrap();
push(
&clone1_dir.into(),
"origin",
"foo",
false,
false,
None,
None,
)
.unwrap();
let branches_1 =
get_branches_info(clone1_dir, false).unwrap();
get_branches_info(&clone1_dir.into(), false).unwrap();
assert!(branches_1[0].remote_details().unwrap().has_tracking);
assert!(branches_1[1].remote_details().unwrap().has_tracking);
@ -875,7 +958,7 @@ mod test_remote_branches {
let clone2_dir = clone2_dir.path().to_str().unwrap();
let branches_2 =
get_branches_info(clone2_dir, false).unwrap();
get_branches_info(&clone2_dir.into(), false).unwrap();
assert!(
!branches_2[0].remote_details().unwrap().has_tracking

View File

@ -1,17 +1,20 @@
//! renaming of branches
use crate::{error::Result, sync::utils};
use crate::{
error::Result,
sync::{repository::repo, RepoPath},
};
use scopetime::scope_time;
/// Rename the branch reference
pub fn rename_branch(
repo_path: &str,
repo_path: &RepoPath,
branch_ref: &str,
new_name: &str,
) -> Result<()> {
scope_time!("delete_branch");
scope_time!("rename_branch");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let branch_as_ref = repo.find_reference(branch_ref)?;
let mut branch = git2::Branch::wrap(branch_as_ref);
branch.rename(new_name, true)?;
@ -29,7 +32,8 @@ mod test {
fn test_rename_branch() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
create_branch(repo_path, "branch1").unwrap();

View File

@ -1,11 +1,14 @@
use super::{utils::repo, CommitId};
use crate::{error::Result, sync::utils::get_head_repo};
use super::{CommitId, RepoPath};
use crate::{
error::Result,
sync::{repository::repo, utils::get_head_repo},
};
use git2::{ErrorCode, ObjectType, Repository, Signature};
use scopetime::scope_time;
///
pub fn amend(
repo_path: &str,
repo_path: &RepoPath,
id: CommitId,
msg: &str,
) -> Result<CommitId> {
@ -58,7 +61,7 @@ pub(crate) fn signature_allow_undefined_name(
}
/// this does not run any git hooks
pub fn commit(repo_path: &str, msg: &str) -> Result<CommitId> {
pub fn commit(repo_path: &RepoPath, msg: &str) -> Result<CommitId> {
scope_time!("commit");
let repo = repo(repo_path)?;
@ -93,7 +96,7 @@ pub fn commit(repo_path: &str, msg: &str) -> Result<CommitId> {
/// This function will return an `Err(…)` variant if the tags name is refused
/// by git or if the tag already exists.
pub fn tag(
repo_path: &str,
repo_path: &RepoPath,
commit_id: &CommitId,
tag: &str,
) -> Result<CommitId> {
@ -113,6 +116,7 @@ pub fn tag(
mod tests {
use crate::error::Result;
use crate::sync::RepoPath;
use crate::sync::{
commit, get_commit_details, get_commit_files, stage_add_file,
tags::get_tags,
@ -136,7 +140,8 @@ mod tests {
let file_path = Path::new("foo");
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))
.unwrap()
@ -159,7 +164,8 @@ mod tests {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
assert_eq!(get_statuses(repo_path), (0, 0));
@ -185,7 +191,8 @@ mod tests {
let file_path2 = Path::new("foo2");
let (_td, repo) = repo_init_empty()?;
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path1))?.write_all(b"test1")?;
@ -221,7 +228,8 @@ mod tests {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?
.write_all(b"test\nfoo")?;
@ -265,7 +273,8 @@ mod tests {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?
.write_all(b"test\nfoo")?;
@ -300,7 +309,8 @@ mod tests {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?
.write_all(b"test\nfoo")?;

View File

@ -1,5 +1,5 @@
use super::{commits_info::get_message, utils::repo, CommitId};
use crate::error::Result;
use super::{commits_info::get_message, CommitId, RepoPath};
use crate::{error::Result, sync::repository::repo};
use git2::Signature;
use scopetime::scope_time;
@ -89,7 +89,7 @@ impl CommitDetails {
///
pub fn get_commit_details(
repo_path: &str,
repo_path: &RepoPath,
id: CommitId,
) -> Result<CommitDetails> {
scope_time!("get_commit_details");
@ -121,11 +121,12 @@ pub fn get_commit_details(
#[cfg(test)]
mod tests {
use super::{get_commit_details, CommitMessage};
use crate::error::Result;
use crate::sync::{
commit, stage_add_file, tests::repo_init_empty,
use crate::{
error::Result,
sync::{
commit, stage_add_file, tests::repo_init_empty, RepoPath,
},
};
use std::{fs::File, io::Write, path::Path};
@ -134,7 +135,8 @@ mod tests {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
@ -144,7 +146,6 @@ mod tests {
let res = get_commit_details(repo_path, id).unwrap();
dbg!(&res.message.as_ref().unwrap().subject);
assert_eq!(
res.message
.as_ref()

View File

@ -1,15 +1,15 @@
use std::cmp::Ordering;
use super::{stash::is_stash_commit, utils::repo, CommitId};
use super::{stash::is_stash_commit, CommitId, RepoPath};
use crate::{
error::Error, error::Result, StatusItem, StatusItemType,
error::Result, sync::repository::repo, StatusItem, StatusItemType,
};
use git2::{Diff, DiffOptions, Repository};
use scopetime::scope_time;
/// get all files that are part of a commit
pub fn get_commit_files(
repo_path: &str,
repo_path: &RepoPath,
id: CommitId,
other: Option<CommitId>,
) -> Result<Vec<StatusItem>> {
@ -20,7 +20,7 @@ pub fn get_commit_files(
let diff = if let Some(other) = other {
get_compare_commits_diff(&repo, (id, other), None)?
} else {
get_commit_diff(&repo, id, None)?
get_commit_diff(repo_path, &repo, id, None)?
};
let res = diff
@ -81,11 +81,12 @@ pub fn get_compare_commits_diff(
}
#[allow(clippy::redundant_pub_crate)]
pub(crate) fn get_commit_diff(
repo: &Repository,
pub(crate) fn get_commit_diff<'a>(
repo_path: &RepoPath,
repo: &'a Repository,
id: CommitId,
pathspec: Option<String>,
) -> Result<Diff<'_>> {
) -> Result<Diff<'a>> {
// scope_time!("get_commit_diff");
let commit = repo.find_commit(id.into())?;
@ -111,15 +112,10 @@ pub(crate) fn get_commit_diff(
Some(&mut opts),
)?;
if is_stash_commit(
repo.path().to_str().map_or_else(
|| Err(Error::Generic("repo path utf8 err".to_owned())),
Ok,
)?,
&id,
)? {
if is_stash_commit(repo_path, &id)? {
if let Ok(untracked_commit) = commit.parent_id(2) {
let untracked_diff = get_commit_diff(
repo_path,
repo,
CommitId::new(untracked_commit),
pathspec,
@ -140,6 +136,7 @@ mod tests {
sync::{
commit, stage_add_file, stash_save,
tests::{get_statuses, repo_init},
RepoPath,
},
StatusItemType,
};
@ -150,7 +147,8 @@ mod tests {
let file_path = Path::new("file1.txt");
let (_td, repo) = repo_init()?;
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?
.write_all(b"test file1 content")?;
@ -172,7 +170,8 @@ mod tests {
let file_path = Path::new("file1.txt");
let (_td, repo) = repo_init()?;
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?
.write_all(b"test file1 content")?;
@ -193,7 +192,8 @@ mod tests {
let file_path2 = Path::new("file2.txt");
let (_td, repo) = repo_init()?;
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path1))?.write_all(b"test")?;
stage_add_file(repo_path, file_path1)?;

View File

@ -1,5 +1,5 @@
use super::utils::repo;
use crate::error::Result;
use super::RepoPath;
use crate::{error::Result, sync::repository::repo};
use git2::{Commit, Error, Oid};
use scopetime::scope_time;
use unicode_truncate::UnicodeTruncateStr;
@ -62,7 +62,7 @@ pub struct CommitInfo {
///
pub fn get_commits_info(
repo_path: &str,
repo_path: &RepoPath,
ids: &[CommitId],
message_length_limit: usize,
) -> Result<Vec<CommitInfo>> {
@ -97,7 +97,7 @@ pub fn get_commits_info(
///
pub fn get_commit_info(
repo_path: &str,
repo_path: &RepoPath,
commit_id: &CommitId,
) -> Result<CommitInfo> {
scope_time!("get_commit_info");
@ -136,10 +136,12 @@ pub fn get_message(
#[cfg(test)]
mod tests {
use super::get_commits_info;
use crate::error::Result;
use crate::sync::{
commit, stage_add_file, tests::repo_init_empty,
utils::get_head_repo,
use crate::{
error::Result,
sync::{
commit, stage_add_file, tests::repo_init_empty,
utils::get_head_repo, RepoPath,
},
};
use std::{fs::File, io::Write, path::Path};
@ -148,7 +150,8 @@ mod tests {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
@ -173,7 +176,8 @@ mod tests {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
@ -192,7 +196,8 @@ mod tests {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();

View File

@ -1,8 +1,9 @@
use super::utils::repo;
use crate::error::Result;
use git2::Repository;
use scopetime::scope_time;
use super::{repository::repo, RepoPath};
// see https://git-scm.com/docs/git-config#Documentation/git-config.txt-statusshowUntrackedFiles
/// represents the `status.showUntrackedFiles` git config state
#[derive(Hash, Copy, Clone, PartialEq)]
@ -57,7 +58,7 @@ pub fn untracked_files_config_repo(
///
pub fn untracked_files_config(
repo_path: &str,
repo_path: &RepoPath,
) -> Result<ShowUntrackedFilesConfig> {
let repo = repo(repo_path)?;
untracked_files_config_repo(&repo)
@ -65,7 +66,7 @@ pub fn untracked_files_config(
/// get string from config
pub fn get_config_string(
repo_path: &str,
repo_path: &RepoPath,
key: &str,
) -> Result<Option<String>> {
let repo = repo(repo_path)?;
@ -103,18 +104,21 @@ mod tests {
#[test]
fn test_get_config() {
let bad_dir_cfg =
get_config_string("oodly_noodly", "this.doesnt.exist");
let bad_dir_cfg = get_config_string(
&"oodly_noodly".into(),
"this.doesnt.exist",
);
assert!(bad_dir_cfg.is_err());
let (_td, repo) = repo_init().unwrap();
let path = repo.path();
let rpath = path.as_os_str().to_str().unwrap();
let bad_cfg = get_config_string(rpath, "this.doesnt.exist");
let bad_cfg =
get_config_string(&rpath.into(), "this.doesnt.exist");
assert!(bad_cfg.is_ok());
assert!(bad_cfg.unwrap().is_none());
// repo init sets user.name
let good_cfg = get_config_string(rpath, "user.name");
let good_cfg = get_config_string(&rpath.into(), "user.name");
assert!(good_cfg.is_ok());
assert!(good_cfg.unwrap().is_some());
}

View File

@ -1,10 +1,9 @@
//! credentials git helper
use super::remotes::get_default_remote_in_repo;
use crate::{
error::{Error, Result},
CWD,
use super::{
remotes::get_default_remote_in_repo, repository::repo, RepoPath,
};
use crate::error::{Error, Result};
use git2::{Config, CredentialHelper};
/// basic Authentication Credentials
@ -31,8 +30,8 @@ impl BasicAuthCredential {
}
/// know if username and password are needed for this url
pub fn need_username_password() -> Result<bool> {
let repo = crate::sync::utils::repo(CWD)?;
pub fn need_username_password(repo_path: &RepoPath) -> Result<bool> {
let repo = repo(repo_path)?;
let url = repo
.find_remote(&get_default_remote_in_repo(&repo)?)?
.url()
@ -43,8 +42,10 @@ pub fn need_username_password() -> Result<bool> {
}
/// extract username and password
pub fn extract_username_password() -> Result<BasicAuthCredential> {
let repo = crate::sync::utils::repo(CWD)?;
pub fn extract_username_password(
repo_path: &RepoPath,
) -> Result<BasicAuthCredential> {
let repo = repo(repo_path)?;
let url = repo
.find_remote(&get_default_remote_in_repo(&repo)?)?
.url()
@ -88,9 +89,9 @@ mod tests {
},
remotes::DEFAULT_REMOTE_NAME,
tests::repo_init,
RepoPath,
};
use serial_test::serial;
use std::env;
#[test]
fn test_credential_complete() {
@ -160,13 +161,15 @@ mod tests {
fn test_need_username_password_if_https() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
env::set_current_dir(repo_path).unwrap();
//TODO:
// env::set_current_dir(repo_path).unwrap();
repo.remote(DEFAULT_REMOTE_NAME, "http://user@github.com")
.unwrap();
assert_eq!(need_username_password().unwrap(), true);
assert_eq!(need_username_password(repo_path).unwrap(), true);
}
#[test]
@ -174,13 +177,15 @@ mod tests {
fn test_dont_need_username_password_if_ssh() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
env::set_current_dir(repo_path).unwrap();
//TODO:
// env::set_current_dir(repo_path).unwrap();
repo.remote(DEFAULT_REMOTE_NAME, "git@github.com:user/repo")
.unwrap();
assert_eq!(need_username_password().unwrap(), false);
assert_eq!(need_username_password(repo_path).unwrap(), false);
}
#[test]
@ -190,11 +195,13 @@ mod tests {
) {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
env::set_current_dir(repo_path).unwrap();
//TODO:
// env::set_current_dir(repo_path).unwrap();
need_username_password().unwrap();
need_username_password(repo_path).unwrap();
}
#[test]
@ -202,9 +209,11 @@ mod tests {
fn test_extract_username_password_from_repo() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
env::set_current_dir(repo_path).unwrap();
//TODO:
// env::set_current_dir(repo_path).unwrap();
repo.remote(
DEFAULT_REMOTE_NAME,
"http://user:pass@github.com",
@ -212,7 +221,7 @@ mod tests {
.unwrap();
assert_eq!(
extract_username_password().unwrap(),
extract_username_password(repo_path).unwrap(),
BasicAuthCredential::new(
Some("user".to_owned()),
Some("pass".to_owned())
@ -225,14 +234,16 @@ mod tests {
fn test_extract_username_from_repo() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
env::set_current_dir(repo_path).unwrap();
//TODO:
// env::set_current_dir(repo_path).unwrap();
repo.remote(DEFAULT_REMOTE_NAME, "http://user@github.com")
.unwrap();
assert_eq!(
extract_username_password().unwrap(),
extract_username_password(repo_path).unwrap(),
BasicAuthCredential::new(Some("user".to_owned()), None)
);
}
@ -244,10 +255,12 @@ mod tests {
) {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
env::set_current_dir(repo_path).unwrap();
//TODO: not needed anymore?
// env::set_current_dir(repo_path).unwrap();
extract_username_password().unwrap();
extract_username_password(repo_path).unwrap();
}
}

View File

@ -2,10 +2,12 @@
use super::{
commit_files::{get_commit_diff, get_compare_commits_diff},
utils::{self, get_head_repo, work_dir},
CommitId,
utils::{get_head_repo, work_dir},
CommitId, RepoPath,
};
use crate::{
error::Error, error::Result, hash, sync::repository::repo,
};
use crate::{error::Error, error::Result, hash};
use easy_cast::Conv;
use git2::{
Delta, Diff, DiffDelta, DiffFormat, DiffHunk, Patch, Repository,
@ -192,14 +194,14 @@ pub(crate) fn get_diff_raw<'a>(
/// returns diff of a specific file either in `stage` or workdir
pub fn get_diff(
repo_path: &str,
repo_path: &RepoPath,
p: &str,
stage: bool,
options: Option<DiffOptions>,
) -> Result<FileDiff> {
scope_time!("get_diff");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let work_dir = work_dir(&repo)?;
let diff = get_diff_raw(&repo, p, stage, false, options)?;
@ -209,28 +211,28 @@ pub fn get_diff(
/// returns diff of a specific file inside a commit
/// see `get_commit_diff`
pub fn get_diff_commit(
repo_path: &str,
repo_path: &RepoPath,
id: CommitId,
p: String,
) -> Result<FileDiff> {
scope_time!("get_diff_commit");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let work_dir = work_dir(&repo)?;
let diff = get_commit_diff(&repo, id, Some(p))?;
let diff = get_commit_diff(repo_path, &repo, id, Some(p))?;
raw_diff_to_file_diff(&diff, work_dir)
}
/// get file changes of a diff between two commits
pub fn get_diff_commits(
repo_path: &str,
repo_path: &RepoPath,
ids: (CommitId, CommitId),
p: String,
) -> Result<FileDiff> {
scope_time!("get_diff_commits");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let work_dir = work_dir(&repo)?;
let diff =
get_compare_commits_diff(&repo, (ids.0, ids.1), Some(p))?;
@ -403,11 +405,14 @@ fn new_file_content(path: &Path) -> Option<Vec<u8>> {
#[cfg(test)]
mod tests {
use super::{get_diff, get_diff_commit};
use crate::error::Result;
use crate::sync::{
commit, stage_add_file,
status::{get_status, StatusType},
tests::{get_statuses, repo_init, repo_init_empty},
use crate::{
error::Result,
sync::{
commit, stage_add_file,
status::{get_status, StatusType},
tests::{get_statuses, repo_init, repo_init_empty},
RepoPath,
},
};
use std::{
fs::{self, File},
@ -419,7 +424,8 @@ mod tests {
fn test_untracked_subfolder() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
assert_eq!(get_statuses(repo_path), (0, 0));
@ -443,7 +449,8 @@ mod tests {
let file_path = Path::new("foo.txt");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
assert_eq!(get_statuses(repo_path), (0, 0));
@ -499,7 +506,8 @@ mod tests {
fn test_hunks() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
assert_eq!(get_statuses(repo_path), (0, 0));
@ -551,7 +559,7 @@ mod tests {
.unwrap();
let diff = get_diff(
sub_path.to_str().unwrap(),
&sub_path.to_str().unwrap().into(),
file_path.to_str().unwrap(),
false,
None,
@ -566,7 +574,8 @@ mod tests {
let file_path = Path::new("bar");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?.write_all(b"\x00")?;
@ -597,7 +606,8 @@ mod tests {
let file_path = Path::new("bar");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?
.write_all(b"\x00\xc7")?;
@ -622,7 +632,8 @@ mod tests {
let file_path = Path::new("bar");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?.write_all(b"\x00")?;

View File

@ -1,4 +1,4 @@
use super::utils::{repo, work_dir};
use super::{repository::repo, utils::work_dir, RepoPath};
use crate::error::{Error, Result};
use scopetime::scope_time;
use std::{
@ -18,7 +18,7 @@ const HOOK_COMMIT_MSG_TEMP_FILE: &str = ".git/COMMIT_EDITMSG";
/// the commit message at `.git/COMMIT_EDITMSG` and pass it's relative path as the only
/// parameter to the hook script.
pub fn hooks_commit_msg(
repo_path: &str,
repo_path: &RepoPath,
msg: &mut String,
) -> Result<HookResult> {
scope_time!("hooks_commit_msg");
@ -48,7 +48,7 @@ pub fn hooks_commit_msg(
/// this hook is documented here <https://git-scm.com/docs/githooks#_pre_commit>
///
pub fn hooks_pre_commit(repo_path: &str) -> Result<HookResult> {
pub fn hooks_pre_commit(repo_path: &RepoPath) -> Result<HookResult> {
scope_time!("hooks_pre_commit");
let work_dir = work_dir_as_string(repo_path)?;
@ -60,7 +60,7 @@ pub fn hooks_pre_commit(repo_path: &str) -> Result<HookResult> {
}
}
///
pub fn hooks_post_commit(repo_path: &str) -> Result<HookResult> {
pub fn hooks_post_commit(repo_path: &RepoPath) -> Result<HookResult> {
scope_time!("hooks_post_commit");
let work_dir = work_dir_as_string(repo_path)?;
@ -73,7 +73,7 @@ pub fn hooks_post_commit(repo_path: &str) -> Result<HookResult> {
}
}
fn work_dir_as_string(repo_path: &str) -> Result<String> {
fn work_dir_as_string(repo_path: &RepoPath) -> Result<String> {
let repo = repo(repo_path)?;
work_dir(&repo)?
.to_str()
@ -163,7 +163,8 @@ mod tests {
fn test_smoke() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
let mut msg = String::from("test");
let res = hooks_commit_msg(repo_path, &mut msg).unwrap();
@ -195,7 +196,8 @@ mod tests {
fn test_hooks_commit_msg_ok() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
let hook = b"#!/bin/sh
exit 0
@ -215,7 +217,8 @@ exit 0
fn test_pre_commit_sh() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
let hook = b"#!/bin/sh
exit 0
@ -230,7 +233,8 @@ exit 0
fn test_pre_commit_fail_sh() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
let hook = b"#!/bin/sh
echo 'rejected'
@ -246,7 +250,8 @@ exit 1
fn test_pre_commit_py() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
// mirror how python pre-commmit sets itself up
#[cfg(not(windows))]
@ -269,7 +274,8 @@ sys.exit(0)
fn test_pre_commit_fail_py() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
// mirror how python pre-commmit sets itself up
#[cfg(not(windows))]
@ -292,7 +298,8 @@ sys.exit(1)
fn test_hooks_commit_msg_reject() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
let hook = b"#!/bin/sh
echo 'msg' > $1
@ -331,9 +338,11 @@ exit 1
fs::create_dir_all(&subfolder).unwrap();
let mut msg = String::from("test");
let res =
hooks_commit_msg(subfolder.to_str().unwrap(), &mut msg)
.unwrap();
let res = hooks_commit_msg(
&subfolder.to_str().unwrap().into(),
&mut msg,
)
.unwrap();
assert_eq!(
res,
@ -347,7 +356,8 @@ exit 1
fn test_commit_msg_no_block_but_alter() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
let hook = b"#!/bin/sh
echo 'msg' > $1
@ -379,7 +389,8 @@ exit 1
fs::create_dir_all(&subfolder).unwrap();
let res =
hooks_post_commit(subfolder.to_str().unwrap()).unwrap();
hooks_post_commit(&subfolder.to_str().unwrap().into())
.unwrap();
assert_eq!(
res,

View File

@ -1,17 +1,18 @@
use super::{
diff::{get_diff_raw, HunkHeader},
utils::repo,
RepoPath,
};
use crate::{
error::{Error, Result},
hash,
sync::repository::repo,
};
use git2::{ApplyLocation, ApplyOptions, Diff};
use scopetime::scope_time;
///
pub fn stage_hunk(
repo_path: &str,
repo_path: &RepoPath,
file_path: &str,
hunk_hash: u64,
) -> Result<()> {
@ -36,7 +37,7 @@ pub fn stage_hunk(
/// this will fail for an all untracked file
pub fn reset_hunk(
repo_path: &str,
repo_path: &RepoPath,
file_path: &str,
hunk_hash: u64,
) -> Result<()> {
@ -94,7 +95,7 @@ fn find_hunk_index(diff: &Diff, hunk_hash: u64) -> Option<usize> {
///
pub fn unstage_hunk(
repo_path: &str,
repo_path: &RepoPath,
file_path: &str,
hunk_hash: u64,
) -> Result<bool> {
@ -162,15 +163,16 @@ mod tests {
let file_path = Path::new("foo/foo.txt");
let (_td, repo) = repo_init_empty()?;
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
let sub_path = root.join("foo/");
fs::create_dir_all(&sub_path)?;
File::create(&root.join(file_path))?.write_all(b"test")?;
let sub_path: &RepoPath = &sub_path.to_str().unwrap().into();
let diff = get_diff(
sub_path.to_str().unwrap(),
sub_path,
file_path.to_str().unwrap(),
false,
None,

View File

@ -1,5 +1,8 @@
use super::utils::{repo, work_dir};
use crate::error::{Error, Result};
use super::{utils::work_dir, RepoPath};
use crate::{
error::{Error, Result},
sync::repository::repo,
};
use scopetime::scope_time;
use std::{
fs::{File, OpenOptions},
@ -11,7 +14,7 @@ static GITIGNORE: &str = ".gitignore";
/// add file or path to root ignore file
pub fn add_to_ignore(
repo_path: &str,
repo_path: &RepoPath,
path_to_ignore: &str,
) -> Result<()> {
scope_time!("add_to_ignore");
@ -71,7 +74,8 @@ mod tests {
let file_path = Path::new("foo.txt");
let (_td, repo) = repo_init()?;
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?.write_all(b"test")?;
@ -98,7 +102,8 @@ mod tests {
let file_path = Path::new("foo.txt");
let (_td, repo) = repo_init()?;
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?.write_all(b"test")?;
File::create(&root.join(ignore_file_path))?
@ -119,7 +124,8 @@ mod tests {
let file_path = Path::new("foo.txt");
let (_td, repo) = repo_init()?;
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?.write_all(b"test")?;
File::create(&root.join(ignore_file_path))?
@ -139,7 +145,8 @@ mod tests {
let ignore_file_path = Path::new(".gitignore");
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
repo_write_file(&repo, ".gitignore", "#foo").unwrap();

View File

@ -108,6 +108,7 @@ impl<'a> LogWalker<'a> {
mod tests {
use super::*;
use crate::error::Result;
use crate::sync::RepoPath;
use crate::sync::{
commit, commit_files::get_commit_diff, get_commits_info,
stage_add_file, tests::repo_init_empty,
@ -120,7 +121,8 @@ mod tests {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
@ -144,7 +146,8 @@ mod tests {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
@ -177,28 +180,31 @@ mod tests {
let second_file_path = Path::new("baz");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: RepoPath =
root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
stage_add_file(&repo_path, file_path).unwrap();
let _first_commit_id = commit(repo_path, "commit1").unwrap();
let _first_commit_id = commit(&repo_path, "commit1").unwrap();
File::create(&root.join(second_file_path))?
.write_all(b"a")?;
stage_add_file(repo_path, second_file_path).unwrap();
stage_add_file(&repo_path, second_file_path).unwrap();
let second_commit_id = commit(repo_path, "commit2").unwrap();
let second_commit_id = commit(&repo_path, "commit2").unwrap();
File::create(&root.join(file_path))?.write_all(b"b")?;
stage_add_file(repo_path, file_path).unwrap();
stage_add_file(&repo_path, file_path).unwrap();
let _third_commit_id = commit(repo_path, "commit3").unwrap();
let _third_commit_id = commit(&repo_path, "commit3").unwrap();
let diff_contains_baz = |repo: &Repository,
commit_id: &CommitId|
-> Result<bool> {
let repo_path_clone = repo_path.clone();
let diff_contains_baz = move |repo: &Repository,
commit_id: &CommitId|
-> Result<bool> {
let diff = get_commit_diff(
&repo_path_clone,
&repo,
*commit_id,
Some("baz".into()),
@ -222,10 +228,12 @@ mod tests {
assert_eq!(items.len(), 0);
let diff_contains_bar = |repo: &Repository,
commit_id: &CommitId|
-> Result<bool> {
let repo_path_clone = repo_path.clone();
let diff_contains_bar = move |repo: &Repository,
commit_id: &CommitId|
-> Result<bool> {
let diff = get_commit_diff(
&repo_path_clone,
&repo,
*commit_id,
Some("bar".into()),

View File

@ -5,19 +5,23 @@ use crate::{
rebase::{
abort_rebase, continue_rebase, get_rebase_progress,
},
reset_stage, reset_workdir, utils, CommitId,
repository::repo,
reset_stage, reset_workdir, CommitId,
},
};
use git2::{BranchType, Commit, MergeOptions, Repository};
use scopetime::scope_time;
use super::rebase::{RebaseProgress, RebaseState};
use super::{
rebase::{RebaseProgress, RebaseState},
RepoPath,
};
///
pub fn mergehead_ids(repo_path: &str) -> Result<Vec<CommitId>> {
pub fn mergehead_ids(repo_path: &RepoPath) -> Result<Vec<CommitId>> {
scope_time!("mergehead_ids");
let mut repo = utils::repo(repo_path)?;
let mut repo = repo(repo_path)?;
let mut ids: Vec<CommitId> = Vec::new();
repo.mergehead_foreach(|id| {
@ -32,10 +36,10 @@ pub fn mergehead_ids(repo_path: &str) -> Result<Vec<CommitId>> {
/// * reset all staged changes,
/// * revert all changes in workdir
/// * cleanup repo merge state
pub fn abort_merge(repo_path: &str) -> Result<()> {
pub fn abort_merge(repo_path: &RepoPath) -> Result<()> {
scope_time!("cleanup_state");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
reset_stage(repo_path, "*")?;
reset_workdir(repo_path, "*")?;
@ -47,13 +51,13 @@ pub fn abort_merge(repo_path: &str) -> Result<()> {
///
pub fn merge_branch(
repo_path: &str,
repo_path: &RepoPath,
branch: &str,
branch_type: BranchType,
) -> Result<()> {
scope_time!("merge_branch");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
merge_branch_repo(&repo, branch, branch_type)?;
@ -61,30 +65,32 @@ pub fn merge_branch(
}
///
pub fn rebase_progress(repo_path: &str) -> Result<RebaseProgress> {
pub fn rebase_progress(
repo_path: &RepoPath,
) -> Result<RebaseProgress> {
scope_time!("rebase_progress");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
get_rebase_progress(&repo)
}
///
pub fn continue_pending_rebase(
repo_path: &str,
repo_path: &RepoPath,
) -> Result<RebaseState> {
scope_time!("continue_pending_rebase");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
continue_rebase(&repo)
}
///
pub fn abort_pending_rebase(repo_path: &str) -> Result<()> {
pub fn abort_pending_rebase(repo_path: &RepoPath) -> Result<()> {
scope_time!("abort_pending_rebase");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
abort_rebase(&repo)
}
@ -115,10 +121,10 @@ pub fn merge_branch_repo(
}
///
pub fn merge_msg(repo_path: &str) -> Result<String> {
pub fn merge_msg(repo_path: &RepoPath) -> Result<String> {
scope_time!("merge_msg");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let content = repo.message()?;
Ok(content)
@ -126,13 +132,13 @@ pub fn merge_msg(repo_path: &str) -> Result<String> {
///
pub fn merge_commit(
repo_path: &str,
repo_path: &RepoPath,
msg: &str,
ids: &[CommitId],
) -> Result<CommitId> {
scope_time!("merge_commit");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let mut commits: Vec<Commit> = Vec::new();
@ -151,6 +157,7 @@ mod tests {
use crate::sync::{
create_branch,
tests::{repo_init, write_commit_file},
RepoPath,
};
use pretty_assertions::assert_eq;
@ -158,7 +165,8 @@ mod tests {
fn test_smoke() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
let c1 =
write_commit_file(&repo, "test.txt", "test", "commit1");

View File

@ -20,6 +20,7 @@ mod merge;
mod patches;
mod rebase;
pub mod remotes;
mod repository;
mod reset;
mod staging;
mod stash;
@ -68,6 +69,8 @@ pub use remotes::{
get_default_remote, get_remotes, push::AsyncProgress,
tags::PushTagsProgress,
};
pub(crate) use repository::repo;
pub use repository::{RepoPath, RepoPathRef};
pub use reset::{reset_stage, reset_workdir};
pub use staging::{discard_lines, stage_lines};
pub use stash::{
@ -80,17 +83,19 @@ pub use tags::{
};
pub use tree::{tree_file_content, tree_files, TreeFile};
pub use utils::{
get_head, get_head_tuple, is_bare_repo, is_repo, repo_dir,
stage_add_all, stage_add_file, stage_addremoved, Head,
get_head, get_head_tuple, is_repo, repo_dir, stage_add_all,
stage_add_file, stage_addremoved, Head,
};
#[cfg(test)]
mod tests {
use super::{
commit, stage_add_file,
commit,
repository::repo,
stage_add_file,
status::{get_status, StatusType},
utils::{get_head_repo, repo, repo_write_file},
CommitId, LogWalker,
utils::{get_head_repo, repo_write_file},
CommitId, LogWalker, RepoPath,
};
use crate::error::Result;
use git2::Repository;
@ -129,13 +134,16 @@ mod tests {
repo_write_file(repo, file, content).unwrap();
stage_add_file(
repo.workdir().unwrap().to_str().unwrap(),
&repo.workdir().unwrap().to_str().unwrap().into(),
Path::new(file),
)
.unwrap();
commit(repo.workdir().unwrap().to_str().unwrap(), commit_name)
.unwrap()
commit(
&repo.workdir().unwrap().to_str().unwrap().into(),
commit_name,
)
.unwrap()
}
/// write, stage and commit a file giving the commit a specific timestamp
@ -148,7 +156,8 @@ mod tests {
) -> CommitId {
repo_write_file(repo, file, content).unwrap();
let path = repo.workdir().unwrap().to_str().unwrap();
let path: &RepoPath =
&repo.workdir().unwrap().to_str().unwrap().into();
stage_add_file(path, Path::new(file)).unwrap();
@ -156,7 +165,7 @@ mod tests {
}
fn commit_at(
repo_path: &str,
repo_path: &RepoPath,
msg: &str,
time: git2::Time,
) -> CommitId {
@ -258,7 +267,7 @@ mod tests {
}
/// helper returning amount of files with changes in the (wd,stage)
pub fn get_statuses(repo_path: &str) -> (usize, usize) {
pub fn get_statuses(repo_path: &RepoPath) -> (usize, usize) {
(
get_status(repo_path, StatusType::WorkingDir, None)
.unwrap()
@ -270,7 +279,7 @@ mod tests {
}
///
pub fn debug_cmd_print(path: &str, cmd: &str) {
pub fn debug_cmd_print(path: &RepoPath, cmd: &str) {
let cmd = debug_cmd(path, cmd);
eprintln!("\n----\n{}", cmd);
}
@ -289,18 +298,18 @@ mod tests {
commit_ids
}
fn debug_cmd(path: &str, cmd: &str) -> String {
fn debug_cmd(path: &RepoPath, cmd: &str) -> String {
let output = if cfg!(target_os = "windows") {
Command::new("cmd")
.args(&["/C", cmd])
.current_dir(path)
.current_dir(path.gitpath())
.output()
.unwrap()
} else {
Command::new("sh")
.arg("-c")
.arg(cmd)
.current_dir(path)
.current_dir(path.gitpath())
.output()
.unwrap()
};

View File

@ -3,20 +3,20 @@ use scopetime::scope_time;
use crate::{
error::{Error, Result},
sync::utils,
sync::repository::repo,
};
use super::CommitId;
use super::{CommitId, RepoPath};
/// rebase current HEAD on `branch`
pub fn rebase_branch(
repo_path: &str,
repo_path: &RepoPath,
branch: &str,
branch_type: BranchType,
) -> Result<RebaseState> {
scope_time!("rebase_branch");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
rebase_branch_repo(&repo, branch, branch_type)
}
@ -189,8 +189,9 @@ mod test_conflict_free_rebase {
checkout_branch, create_branch,
rebase::{rebase_branch, RebaseState},
repo_state,
repository::repo,
tests::{repo_init, write_commit_file},
utils, CommitId, RepoState,
CommitId, RepoPath, RepoState,
};
use git2::{BranchType, Repository};
@ -209,10 +210,10 @@ mod test_conflict_free_rebase {
///
fn test_rebase_branch_repo(
repo_path: &str,
repo_path: &RepoPath,
branch_name: &str,
) -> CommitId {
let repo = utils::repo(repo_path).unwrap();
let repo = repo(repo_path).unwrap();
let branch =
repo.find_branch(branch_name, BranchType::Local).unwrap();
@ -228,7 +229,8 @@ mod test_conflict_free_rebase {
fn test_smoke() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
let c1 =
write_commit_file(&repo, "test1.txt", "test", "commit1");
@ -256,7 +258,8 @@ mod test_conflict_free_rebase {
fn test_conflict() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
write_commit_file(&repo, "test.txt", "test1", "commit1");
@ -289,7 +292,7 @@ mod test_rebase {
},
rebase_branch, repo_state,
tests::{repo_init, write_commit_file},
RepoState,
RepoPath, RepoState,
};
use git2::BranchType;
@ -297,7 +300,8 @@ mod test_rebase {
fn test_conflicted_abort() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
write_commit_file(&repo, "test.txt", "test1", "commit1");

View File

@ -8,7 +8,7 @@ use crate::{
error::{Error, Result},
sync::{
cred::BasicAuthCredential,
remotes::push::ProgressNotification, utils,
remotes::push::ProgressNotification, repository::repo, utils,
},
ProgressPercent,
};
@ -20,14 +20,16 @@ use utils::bytes2string;
pub use callbacks::Callbacks;
pub use tags::tags_missing_remote;
use super::RepoPath;
/// origin
pub const DEFAULT_REMOTE_NAME: &str = "origin";
///
pub fn get_remotes(repo_path: &str) -> Result<Vec<String>> {
pub fn get_remotes(repo_path: &RepoPath) -> Result<Vec<String>> {
scope_time!("get_remotes");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let remotes = repo.remotes()?;
let remotes: Vec<String> =
remotes.iter().flatten().map(String::from).collect();
@ -37,8 +39,8 @@ pub fn get_remotes(repo_path: &str) -> Result<Vec<String>> {
/// tries to find origin or the only remote that is defined if any
/// in case of multiple remotes and none named *origin* we fail
pub fn get_default_remote(repo_path: &str) -> Result<String> {
let repo = utils::repo(repo_path)?;
pub fn get_default_remote(repo_path: &RepoPath) -> Result<String> {
let repo = repo(repo_path)?;
get_default_remote_in_repo(&repo)
}
@ -78,12 +80,12 @@ pub(crate) fn get_default_remote_in_repo(
///
fn fetch_from_remote(
repo_path: &str,
repo_path: &RepoPath,
remote: &str,
basic_credential: Option<BasicAuthCredential>,
progress_sender: Option<Sender<ProgressNotification>>,
) -> Result<()> {
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let mut remote = repo.find_remote(remote)?;
@ -99,13 +101,13 @@ fn fetch_from_remote(
/// updates/prunes all branches from all remotes
pub fn fetch_all(
repo_path: &str,
repo_path: &RepoPath,
basic_credential: &Option<BasicAuthCredential>,
progress_sender: &Option<Sender<ProgressPercent>>,
) -> Result<()> {
scope_time!("fetch_all");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let remotes = repo
.remotes()?
.iter()
@ -133,14 +135,14 @@ pub fn fetch_all(
/// fetches from upstream/remote for local `branch`
pub(crate) fn fetch(
repo_path: &str,
repo_path: &RepoPath,
branch: &str,
basic_credential: Option<BasicAuthCredential>,
progress_sender: Option<Sender<ProgressNotification>>,
) -> Result<usize> {
scope_time!("fetch");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let branch_ref = repo
.find_branch(branch, BranchType::Local)?
.into_reference();
@ -171,7 +173,12 @@ mod tests {
let (remote_dir, _remote) = repo_init().unwrap();
let remote_path = remote_dir.path().to_str().unwrap();
let (repo_dir, _repo) = repo_clone(remote_path).unwrap();
let repo_path = repo_dir.path().as_os_str().to_str().unwrap();
let repo_path: &RepoPath = &repo_dir
.into_path()
.as_os_str()
.to_str()
.unwrap()
.into();
let remotes = get_remotes(repo_path).unwrap();
@ -185,7 +192,12 @@ mod tests {
let (remote_dir, _remote) = repo_init().unwrap();
let remote_path = remote_dir.path().to_str().unwrap();
let (repo_dir, _repo) = repo_clone(remote_path).unwrap();
let repo_path = repo_dir.path().as_os_str().to_str().unwrap();
let repo_path: &RepoPath = &repo_dir
.into_path()
.as_os_str()
.to_str()
.unwrap()
.into();
debug_cmd_print(
repo_path,
@ -199,10 +211,9 @@ mod tests {
vec![String::from("origin"), String::from("second")]
);
let first = get_default_remote_in_repo(
&utils::repo(repo_path).unwrap(),
)
.unwrap();
let first =
get_default_remote_in_repo(&repo(repo_path).unwrap())
.unwrap();
assert_eq!(first, String::from("origin"));
}
@ -211,7 +222,12 @@ mod tests {
let (remote_dir, _remote) = repo_init().unwrap();
let remote_path = remote_dir.path().to_str().unwrap();
let (repo_dir, _repo) = repo_clone(remote_path).unwrap();
let repo_path = repo_dir.path().as_os_str().to_str().unwrap();
let repo_path: &RepoPath = &repo_dir
.into_path()
.as_os_str()
.to_str()
.unwrap()
.into();
debug_cmd_print(
repo_path,
@ -231,10 +247,9 @@ mod tests {
vec![String::from("alternate"), String::from("origin")]
);
let first = get_default_remote_in_repo(
&utils::repo(repo_path).unwrap(),
)
.unwrap();
let first =
get_default_remote_in_repo(&repo(repo_path).unwrap())
.unwrap();
assert_eq!(first, String::from("origin"));
}
@ -243,7 +258,12 @@ mod tests {
let (remote_dir, _remote) = repo_init().unwrap();
let remote_path = remote_dir.path().to_str().unwrap();
let (repo_dir, _repo) = repo_clone(remote_path).unwrap();
let repo_path = repo_dir.path().as_os_str().to_str().unwrap();
let repo_path: &RepoPath = &repo_dir
.into_path()
.as_os_str()
.to_str()
.unwrap()
.into();
debug_cmd_print(
repo_path,
@ -264,9 +284,8 @@ mod tests {
]
);
let res = get_default_remote_in_repo(
&utils::repo(repo_path).unwrap(),
);
let res =
get_default_remote_in_repo(&repo(repo_path).unwrap());
assert_eq!(res.is_err(), true);
assert!(matches!(res, Err(Error::NoDefaultRemoteFound)));
}

View File

@ -1,10 +1,9 @@
use super::utils;
use crate::{
error::{Error, Result},
progress::ProgressPercent,
sync::{
branch::branch_set_upstream, cred::BasicAuthCredential,
remotes::Callbacks, CommitId,
remotes::Callbacks, repository::repo, CommitId, RepoPath,
},
};
use crossbeam_channel::Sender;
@ -91,7 +90,7 @@ impl AsyncProgress for ProgressNotification {
#[allow(clippy::redundant_pub_crate)]
pub(crate) fn push(
repo_path: &str,
repo_path: &RepoPath,
remote: &str,
branch: &str,
force: bool,
@ -101,7 +100,7 @@ pub(crate) fn push(
) -> Result<()> {
scope_time!("push");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let mut remote = repo.find_remote(remote)?;
let mut options = PushOptions::new();
@ -178,13 +177,13 @@ mod tests {
writeln!(tmp_repo_file, "TempSomething").unwrap();
sync::commit(
tmp_repo_dir.path().to_str().unwrap(),
&tmp_repo_dir.path().to_str().unwrap().into(),
"repo_1_commit",
)
.unwrap();
push(
tmp_repo_dir.path().to_str().unwrap(),
&tmp_repo_dir.path().to_str().unwrap().into(),
"origin",
"master",
false,
@ -201,7 +200,7 @@ mod tests {
writeln!(tmp_other_repo_file, "TempElse").unwrap();
sync::commit(
tmp_other_repo_dir.path().to_str().unwrap(),
&tmp_other_repo_dir.path().to_str().unwrap().into(),
"repo_2_commit",
)
.unwrap();
@ -210,7 +209,7 @@ mod tests {
// should fail as branches diverged
assert_eq!(
push(
tmp_other_repo_dir.path().to_str().unwrap(),
&tmp_other_repo_dir.path().to_str().unwrap().into(),
"origin",
"master",
false,
@ -226,7 +225,7 @@ mod tests {
// should work as it forces the push through
assert_eq!(
push(
tmp_other_repo_dir.path().to_str().unwrap(),
&tmp_other_repo_dir.path().to_str().unwrap().into(),
"origin",
"master",
true,
@ -269,13 +268,13 @@ mod tests {
writeln!(tmp_repo_file, "TempSomething").unwrap();
sync::stage_add_file(
tmp_repo_dir.path().to_str().unwrap(),
&tmp_repo_dir.path().to_str().unwrap().into(),
Path::new("temp_file.txt"),
)
.unwrap();
let repo_1_commit = sync::commit(
tmp_repo_dir.path().to_str().unwrap(),
&tmp_repo_dir.path().to_str().unwrap().into(),
"repo_1_commit",
)
.unwrap();
@ -283,7 +282,7 @@ mod tests {
//NOTE: make sure the commit actually contains that file
assert_eq!(
sync::get_commit_files(
tmp_repo_dir.path().to_str().unwrap(),
&tmp_repo_dir.path().to_str().unwrap().into(),
repo_1_commit,
None
)
@ -296,7 +295,7 @@ mod tests {
assert!(commits.contains(&repo_1_commit));
push(
tmp_repo_dir.path().to_str().unwrap(),
&tmp_repo_dir.path().to_str().unwrap().into(),
"origin",
"master",
false,
@ -313,13 +312,13 @@ mod tests {
writeln!(tmp_other_repo_file, "TempElse").unwrap();
sync::stage_add_file(
tmp_other_repo_dir.path().to_str().unwrap(),
&tmp_other_repo_dir.path().to_str().unwrap().into(),
Path::new("temp_file.txt"),
)
.unwrap();
let repo_2_commit = sync::commit(
tmp_other_repo_dir.path().to_str().unwrap(),
&tmp_other_repo_dir.path().to_str().unwrap().into(),
"repo_2_commit",
)
.unwrap();
@ -339,7 +338,7 @@ mod tests {
// should fail as branches diverged
assert_eq!(
push(
tmp_other_repo_dir.path().to_str().unwrap(),
&tmp_other_repo_dir.path().to_str().unwrap().into(),
"origin",
"master",
false,
@ -360,7 +359,7 @@ mod tests {
// should work as it forces the push through
push(
tmp_other_repo_dir.path().to_str().unwrap(),
&tmp_other_repo_dir.path().to_str().unwrap().into(),
"origin",
"master",
true,
@ -407,7 +406,7 @@ mod tests {
assert!(commits.contains(&commit_1));
push(
tmp_repo_dir.path().to_str().unwrap(),
&tmp_repo_dir.path().to_str().unwrap().into(),
"origin",
"master",
false,
@ -419,14 +418,14 @@ mod tests {
// Create the local branch
sync::create_branch(
tmp_repo_dir.path().to_str().unwrap(),
&tmp_repo_dir.path().to_str().unwrap().into(),
"test_branch",
)
.unwrap();
// Push the local branch
push(
tmp_repo_dir.path().to_str().unwrap(),
&tmp_repo_dir.path().to_str().unwrap().into(),
"origin",
"test_branch",
false,
@ -452,7 +451,7 @@ mod tests {
// Delete the remote branch
assert_eq!(
push(
tmp_repo_dir.path().to_str().unwrap(),
&tmp_repo_dir.path().to_str().unwrap().into(),
"origin",
"test_branch",
false,

View File

@ -1,10 +1,13 @@
//!
use super::{push::AsyncProgress, utils};
use super::push::AsyncProgress;
use crate::{
error::Result,
progress::ProgressPercent,
sync::{cred::BasicAuthCredential, remotes::Callbacks},
sync::{
cred::BasicAuthCredential, remotes::Callbacks,
repository::repo, RepoPath,
},
};
use crossbeam_channel::Sender;
use git2::{Direction, PushOptions};
@ -44,13 +47,13 @@ impl AsyncProgress for PushTagsProgress {
/// lists the remotes tags
fn remote_tag_refs(
repo_path: &str,
repo_path: &RepoPath,
remote: &str,
basic_credential: Option<BasicAuthCredential>,
) -> Result<Vec<String>> {
scope_time!("remote_tags");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let mut remote = repo.find_remote(remote)?;
let callbacks = Callbacks::new(None, basic_credential);
let conn = remote.connect_auth(
@ -73,13 +76,13 @@ fn remote_tag_refs(
/// lists the remotes tags missing
pub fn tags_missing_remote(
repo_path: &str,
repo_path: &RepoPath,
remote: &str,
basic_credential: Option<BasicAuthCredential>,
) -> Result<Vec<String>> {
scope_time!("tags_missing_remote");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let tags = repo.tag_names(None)?;
let mut local_tags = tags
@ -98,7 +101,7 @@ pub fn tags_missing_remote(
///
pub fn push_tags(
repo_path: &str,
repo_path: &RepoPath,
remote: &str,
basic_credential: Option<BasicAuthCredential>,
progress_sender: Option<Sender<PushTagsProgress>>,
@ -115,7 +118,7 @@ pub fn push_tags(
basic_credential.clone(),
)?;
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let mut remote = repo.find_remote(remote)?;
let total = tags_missing.len();
@ -165,11 +168,13 @@ mod tests {
let (clone1_dir, clone1) = repo_clone(r1_dir).unwrap();
let clone1_dir = clone1_dir.path().to_str().unwrap();
let clone1_dir: &RepoPath =
&clone1_dir.path().to_str().unwrap().into();
let (clone2_dir, clone2) = repo_clone(r1_dir).unwrap();
let clone2_dir = clone2_dir.path().to_str().unwrap();
let clone2_dir: &RepoPath =
&clone2_dir.path().to_str().unwrap().into();
// clone1
@ -211,11 +216,13 @@ mod tests {
let (clone1_dir, clone1) = repo_clone(r1_dir).unwrap();
let clone1_dir = clone1_dir.path().to_str().unwrap();
let clone1_dir: &RepoPath =
&clone1_dir.path().to_str().unwrap().into();
let (clone2_dir, _clone2) = repo_clone(r1_dir).unwrap();
let clone2_dir = clone2_dir.path().to_str().unwrap();
let clone2_dir: &RepoPath =
&clone2_dir.path().to_str().unwrap().into();
// clone1
@ -248,7 +255,8 @@ mod tests {
let (clone1_dir, clone1) = repo_clone(r1_dir).unwrap();
let clone1_dir = clone1_dir.path().to_str().unwrap();
let clone1_dir: &RepoPath =
&clone1_dir.path().to_str().unwrap().into();
// clone1
@ -281,7 +289,8 @@ mod tests {
let r1_dir = r1_dir.path().to_str().unwrap();
let (clone1_dir, clone1) = repo_clone(r1_dir).unwrap();
let clone1_dir = clone1_dir.path().to_str().unwrap();
let clone1_dir: &RepoPath =
&clone1_dir.path().to_str().unwrap().into();
let commit1 =
write_commit_file(&clone1, "test.txt", "test", "commit1");
@ -291,7 +300,8 @@ mod tests {
.unwrap();
let (clone2_dir, _clone2) = repo_clone(r1_dir).unwrap();
let clone2_dir = clone2_dir.path().to_str().unwrap();
let clone2_dir: &RepoPath =
&clone2_dir.path().to_str().unwrap().into();
// clone1 - creates tag
@ -319,7 +329,8 @@ mod tests {
let r1_dir = r1_dir.path().to_str().unwrap();
let (clone1_dir, clone1) = repo_clone(r1_dir).unwrap();
let clone1_dir = clone1_dir.path().to_str().unwrap();
let clone1_dir: &RepoPath =
&clone1_dir.path().to_str().unwrap().into();
let commit1 =
write_commit_file(&clone1, "test.txt", "test", "commit1");
@ -329,7 +340,8 @@ mod tests {
.unwrap();
let (clone2_dir, _clone2) = repo_clone(r1_dir).unwrap();
let clone2_dir = clone2_dir.path().to_str().unwrap();
let clone2_dir: &RepoPath =
&clone2_dir.path().to_str().unwrap().into();
// clone1 - creates tag

View File

@ -0,0 +1,63 @@
use std::{
cell::RefCell,
path::{Path, PathBuf},
};
use git2::{Repository, RepositoryOpenFlags};
use crate::error::Result;
///
pub type RepoPathRef = RefCell<RepoPath>;
///
#[derive(Clone)]
pub enum RepoPath {
///
Path(PathBuf),
///
Workdir {
///
gitdir: PathBuf,
///
workdir: PathBuf,
},
}
impl RepoPath {
///
pub fn gitpath(&self) -> &Path {
match self {
Self::Path(p) => p.as_path(),
Self::Workdir { gitdir, .. } => gitdir.as_path(),
}
}
///
pub fn workdir(&self) -> Option<&Path> {
match self {
Self::Path(_) => None,
Self::Workdir { workdir, .. } => Some(workdir.as_path()),
}
}
}
impl From<&str> for RepoPath {
fn from(p: &str) -> Self {
Self::Path(PathBuf::from(p))
}
}
pub fn repo(repo_path: &RepoPath) -> Result<Repository> {
let repo = Repository::open_ext(
repo_path.gitpath(),
RepositoryOpenFlags::empty(),
Vec::<&Path>::new(),
)?;
if let Some(workdir) = repo_path.workdir() {
repo.set_workdir(workdir, false)?;
}
Ok(repo)
}

View File

@ -1,10 +1,10 @@
use super::utils::{get_head_repo, repo};
use crate::error::Result;
use super::{utils::get_head_repo, RepoPath};
use crate::{error::Result, sync::repository::repo};
use git2::{build::CheckoutBuilder, ObjectType};
use scopetime::scope_time;
///
pub fn reset_stage(repo_path: &str, path: &str) -> Result<()> {
pub fn reset_stage(repo_path: &RepoPath, path: &str) -> Result<()> {
scope_time!("reset_stage");
let repo = repo(repo_path)?;
@ -22,7 +22,7 @@ pub fn reset_stage(repo_path: &str, path: &str) -> Result<()> {
}
///
pub fn reset_workdir(repo_path: &str, path: &str) -> Result<()> {
pub fn reset_workdir(repo_path: &RepoPath, path: &str) -> Result<()> {
scope_time!("reset_workdir");
let repo = repo(repo_path)?;
@ -49,6 +49,7 @@ mod tests {
debug_cmd_print, get_statuses, repo_init, repo_init_empty,
},
utils::{stage_add_all, stage_add_file},
RepoPath,
};
use std::{
fs::{self, File},
@ -86,7 +87,8 @@ mod tests {
fn test_reset_only_unstaged() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
let res = get_status(repo_path, StatusType::WorkingDir, None)
.unwrap();
@ -130,7 +132,8 @@ mod tests {
fn test_reset_untracked_in_subdir() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
{
fs::create_dir(&root.join("foo")).unwrap();
@ -155,7 +158,8 @@ mod tests {
fn test_reset_folder() -> Result<()> {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
{
fs::create_dir(&root.join("foo"))?;
@ -200,7 +204,8 @@ mod tests {
fn test_reset_untracked_in_subdir_and_index() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
let file = "foo/bar.txt";
{
@ -240,7 +245,8 @@ mod tests {
let file_path = Path::new("foo.txt");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))
.unwrap()
@ -262,7 +268,8 @@ mod tests {
fn test_reset_untracked_in_subdir_with_cwd_in_subdir() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
{
fs::create_dir(&root.join("foo")).unwrap();
@ -277,7 +284,7 @@ mod tests {
assert_eq!(get_statuses(repo_path), (1, 0));
reset_workdir(
&root.join("foo").as_os_str().to_str().unwrap(),
&root.join("foo").as_os_str().to_str().unwrap().into(),
"foo/bar.txt",
)
.unwrap();
@ -291,7 +298,8 @@ mod tests {
fn test_reset_untracked_subdir() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
{
fs::create_dir_all(&root.join("foo/bar")).unwrap();

View File

@ -1,15 +1,17 @@
use super::{apply_selection, load_file};
use crate::error::Result;
use crate::sync::{
diff::DiffLinePosition,
patches::get_file_diff_patch_and_hunklines,
utils::{repo, repo_write_file},
use crate::{
error::Result,
sync::{
diff::DiffLinePosition,
patches::get_file_diff_patch_and_hunklines, repository::repo,
utils::repo_write_file, RepoPath,
},
};
use scopetime::scope_time;
/// discards specific lines in an unstaged hunk of a diff
pub fn discard_lines(
repo_path: &str,
repo_path: &RepoPath,
file_path: &str,
lines: &[DiffLinePosition],
) -> Result<()> {
@ -69,7 +71,7 @@ mod test {
";
let (path, repo) = repo_init().unwrap();
let path = path.path().to_str().unwrap();
let path: &RepoPath = &path.path().to_str().unwrap().into();
write_commit_file(&repo, "test.txt", FILE_1, "c1");
@ -114,7 +116,7 @@ end
";
let (path, repo) = repo_init().unwrap();
let path = path.path().to_str().unwrap();
let path: &RepoPath = &path.path().to_str().unwrap().into();
write_commit_file(&repo, "test.txt", FILE_1, "c1");
@ -153,7 +155,7 @@ end
";
let (path, repo) = repo_init().unwrap();
let path = path.path().to_str().unwrap();
let path: &RepoPath = &path.path().to_str().unwrap().into();
write_commit_file(&repo, "test.txt", FILE_1, "c1");
@ -200,7 +202,7 @@ end
";
let (path, repo) = repo_init().unwrap();
let path = path.path().to_str().unwrap();
let path: &RepoPath = &path.path().to_str().unwrap().into();
write_commit_file(&repo, "test.txt", FILE_1, "c1");
@ -243,7 +245,7 @@ end
";
let (path, repo) = repo_init().unwrap();
let path = path.path().to_str().unwrap();
let path: &RepoPath = &path.path().to_str().unwrap().into();
write_commit_file(&repo, "test.txt", FILE_1, "c1");
@ -288,7 +290,7 @@ end
";
let (path, repo) = repo_init().unwrap();
let path = path.path().to_str().unwrap();
let path: &RepoPath = &path.path().to_str().unwrap().into();
write_commit_file(&repo, "test.txt", FILE_1, "c1");
@ -321,7 +323,7 @@ end
";
let (path, repo) = repo_init().unwrap();
let path = path.path().to_str().unwrap();
let path: &RepoPath = &path.path().to_str().unwrap().into();
write_commit_file(&repo, "test.txt", FILE_1, "c1");

View File

@ -3,7 +3,8 @@ use crate::{
error::{Error, Result},
sync::{
diff::DiffLinePosition,
patches::get_file_diff_patch_and_hunklines, utils::repo,
patches::get_file_diff_patch_and_hunklines, repository::repo,
RepoPath,
},
};
use easy_cast::Conv;
@ -12,7 +13,7 @@ use std::path::Path;
///
pub fn stage_lines(
repo_path: &str,
repo_path: &RepoPath,
file_path: &str,
is_stage: bool,
lines: &[DiffLinePosition],
@ -80,7 +81,7 @@ mod test {
";
let (path, repo) = repo_init().unwrap();
let path = path.path().to_str().unwrap();
let path: &RepoPath = &path.path().to_str().unwrap().into();
write_commit_file(&repo, "test.txt", FILE_1, "c1");
@ -113,7 +114,7 @@ b = 3
c = 4";
let (path, repo) = repo_init().unwrap();
let path = path.path().to_str().unwrap();
let path: &RepoPath = &path.path().to_str().unwrap().into();
write_commit_file(&repo, "test.txt", FILE_1, "c1");
@ -154,7 +155,7 @@ c = 4";
";
let (path, repo) = repo_init().unwrap();
let path = path.path().to_str().unwrap();
let path: &RepoPath = &path.path().to_str().unwrap().into();
write_commit_file(&repo, "test.txt", FILE_1, "c1");

View File

@ -1,5 +1,8 @@
use super::{utils::repo, CommitId};
use crate::error::{Error, Result};
use super::{CommitId, RepoPath};
use crate::{
error::{Error, Result},
sync::repository::repo,
};
use git2::{
build::CheckoutBuilder, Oid, Repository, StashApplyOptions,
StashFlags,
@ -7,7 +10,7 @@ use git2::{
use scopetime::scope_time;
///
pub fn get_stashes(repo_path: &str) -> Result<Vec<CommitId>> {
pub fn get_stashes(repo_path: &RepoPath) -> Result<Vec<CommitId>> {
scope_time!("get_stashes");
let mut repo = repo(repo_path)?;
@ -24,7 +27,7 @@ pub fn get_stashes(repo_path: &str) -> Result<Vec<CommitId>> {
/// checks whether a given commit is a stash commit.
pub fn is_stash_commit(
repo_path: &str,
repo_path: &RepoPath,
id: &CommitId,
) -> Result<bool> {
let stashes = get_stashes(repo_path)?;
@ -32,7 +35,10 @@ pub fn is_stash_commit(
}
///
pub fn stash_drop(repo_path: &str, stash_id: CommitId) -> Result<()> {
pub fn stash_drop(
repo_path: &RepoPath,
stash_id: CommitId,
) -> Result<()> {
scope_time!("stash_drop");
let mut repo = repo(repo_path)?;
@ -45,7 +51,10 @@ pub fn stash_drop(repo_path: &str, stash_id: CommitId) -> Result<()> {
}
///
pub fn stash_pop(repo_path: &str, stash_id: CommitId) -> Result<()> {
pub fn stash_pop(
repo_path: &RepoPath,
stash_id: CommitId,
) -> Result<()> {
scope_time!("stash_pop");
let mut repo = repo(repo_path)?;
@ -59,7 +68,7 @@ pub fn stash_pop(repo_path: &str, stash_id: CommitId) -> Result<()> {
///
pub fn stash_apply(
repo_path: &str,
repo_path: &RepoPath,
stash_id: CommitId,
allow_conflicts: bool,
) -> Result<()> {
@ -101,7 +110,7 @@ fn get_stash_index(
///
pub fn stash_save(
repo_path: &str,
repo_path: &RepoPath,
message: Option<&str>,
include_untracked: bool,
keep_index: bool,
@ -143,7 +152,8 @@ mod tests {
fn test_smoke() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
assert_eq!(
stash_save(repo_path, None, true, false).is_ok(),
@ -157,7 +167,8 @@ mod tests {
fn test_stashing() -> Result<()> {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join("foo.txt"))?
.write_all(b"test\nfoo")?;
@ -175,7 +186,8 @@ mod tests {
fn test_stashes() -> Result<()> {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join("foo.txt"))?
.write_all(b"test\nfoo")?;
@ -198,7 +210,8 @@ mod tests {
fn test_stash_nothing_untracked() -> Result<()> {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join("foo.txt"))?
.write_all(b"test\nfoo")?;
@ -215,7 +228,8 @@ mod tests {
let file_path1 = Path::new("file1.txt");
let (_td, repo) = repo_init()?;
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path1))?.write_all(b"test")?;
stage_add_file(repo_path, file_path1)?;
@ -242,7 +256,8 @@ mod tests {
fn test_stash_apply_conflict() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
repo_write_file(&repo, "test.txt", "test").unwrap();
@ -260,7 +275,8 @@ mod tests {
fn test_stash_apply_conflict2() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
write_commit_file(&repo, "test.txt", "test", "c1");
@ -280,7 +296,8 @@ mod tests {
fn test_stash_apply_creating_conflict() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
write_commit_file(&repo, "test.txt", "test", "c1");
@ -304,7 +321,8 @@ mod tests {
fn test_stash_pop_no_conflict() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
write_commit_file(&repo, "test.txt", "test", "c1");
@ -326,7 +344,8 @@ mod tests {
fn test_stash_pop_conflict() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
repo_write_file(&repo, "test.txt", "test").unwrap();
@ -348,7 +367,8 @@ mod tests {
fn test_stash_pop_conflict_after_commit() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
write_commit_file(&repo, "test.txt", "test", "c1");

View File

@ -1,4 +1,5 @@
use crate::{error::Result, sync::utils};
use super::RepoPath;
use crate::{error::Result, sync::repository::repo};
use git2::RepositoryState;
use scopetime::scope_time;
@ -22,7 +23,7 @@ impl From<RepositoryState> for RepoState {
RepositoryState::Merge => Self::Merge,
RepositoryState::RebaseMerge => Self::Rebase,
_ => {
log::debug!("state not supported yet: {:?}", state);
log::warn!("state not supported yet: {:?}", state);
Self::Other
}
}
@ -30,10 +31,10 @@ impl From<RepositoryState> for RepoState {
}
///
pub fn repo_state(repo_path: &str) -> Result<RepoState> {
pub fn repo_state(repo_path: &RepoPath) -> Result<RepoState> {
scope_time!("repo_state");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let state = repo.state();

View File

@ -3,13 +3,13 @@
use crate::{
error::Error,
error::Result,
sync::{config::untracked_files_config_repo, utils},
sync::{config::untracked_files_config_repo, repository::repo},
};
use git2::{Delta, Status, StatusOptions, StatusShow};
use scopetime::scope_time;
use std::path::Path;
use super::ShowUntrackedFilesConfig;
use super::{RepoPath, ShowUntrackedFilesConfig};
///
#[derive(Copy, Clone, Hash, PartialEq, Debug)]
@ -96,13 +96,13 @@ impl From<StatusType> for StatusShow {
/// gurantees sorting
pub fn get_status(
repo_path: &str,
repo_path: &RepoPath,
status_type: StatusType,
show_untracked: Option<ShowUntrackedFilesConfig>,
) -> Result<Vec<StatusItem>> {
scope_time!("get_status");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let show_untracked = if let Some(config) = show_untracked {
config

View File

@ -1,5 +1,5 @@
use super::{get_commits_info, utils::repo, CommitId};
use crate::error::Result;
use super::{get_commits_info, CommitId, RepoPath};
use crate::{error::Result, sync::repository::repo};
use scopetime::scope_time;
use std::collections::{BTreeMap, HashMap, HashSet};
@ -25,7 +25,7 @@ pub struct TagWithMetadata {
static MAX_MESSAGE_WIDTH: usize = 100;
/// returns `Tags` type filled with all tags found in repo
pub fn get_tags(repo_path: &str) -> Result<Tags> {
pub fn get_tags(repo_path: &RepoPath) -> Result<Tags> {
scope_time!("get_tags");
let mut res = Tags::new();
@ -67,7 +67,7 @@ pub fn get_tags(repo_path: &str) -> Result<Tags> {
///
pub fn get_tags_with_metadata(
repo_path: &str,
repo_path: &RepoPath,
) -> Result<Vec<TagWithMetadata>> {
scope_time!("get_tags_with_metadata");
@ -119,7 +119,10 @@ pub fn get_tags_with_metadata(
}
///
pub fn delete_tag(repo_path: &str, tag_name: &str) -> Result<()> {
pub fn delete_tag(
repo_path: &RepoPath,
tag_name: &str,
) -> Result<()> {
scope_time!("delete_tag");
let repo = repo(repo_path)?;
@ -138,7 +141,8 @@ mod tests {
fn test_smoke() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
assert_eq!(get_tags(repo_path).unwrap().is_empty(), true);
}
@ -147,7 +151,8 @@ mod tests {
fn test_multitags() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
let sig = repo.signature().unwrap();
let head_id = repo.head().unwrap().target().unwrap();

View File

@ -1,7 +1,7 @@
use super::CommitId;
use super::{CommitId, RepoPath};
use crate::{
error::{Error, Result},
sync::utils::repo,
sync::repository::repo,
};
use git2::{Oid, Repository, Tree};
use scopetime::scope_time;
@ -23,7 +23,7 @@ pub struct TreeFile {
/// guarantees sorting the result
pub fn tree_files(
repo_path: &str,
repo_path: &RepoPath,
commit: CommitId,
) -> Result<Vec<TreeFile>> {
scope_time!("tree_files");
@ -73,7 +73,7 @@ fn path_cmp(a: &Path, b: &Path) -> Ordering {
/// will only work on utf8 content
pub fn tree_file_content(
repo_path: &str,
repo_path: &RepoPath,
file: &TreeFile,
) -> Result<String> {
scope_time!("tree_file_content");
@ -130,7 +130,8 @@ mod tests {
fn test_smoke() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
let c1 =
write_commit_file(&repo, "test.txt", "content", "c1");

View File

@ -1,6 +1,8 @@
//! sync git api (various methods)
use super::{CommitId, ShowUntrackedFilesConfig};
use super::{
repository::repo, CommitId, RepoPath, ShowUntrackedFilesConfig,
};
use crate::{
error::{Error, Result},
sync::config::untracked_files_config_repo,
@ -23,54 +25,28 @@ pub struct Head {
}
///
pub fn is_repo(repo_path: &str) -> bool {
pub fn is_repo(repo_path: &RepoPath) -> bool {
Repository::open_ext(
repo_path,
repo_path.gitpath(),
RepositoryOpenFlags::empty(),
Vec::<&Path>::new(),
)
.is_ok()
}
/// checks if the git repo at path `repo_path` is a bare repo
pub fn is_bare_repo(repo_path: &str) -> Result<bool> {
let repo = Repository::open_ext(
repo_path,
RepositoryOpenFlags::empty(),
Vec::<&Path>::new(),
)?;
Ok(repo.is_bare())
}
///
pub(crate) fn repo(repo_path: &str) -> Result<Repository> {
let repo = Repository::open_ext(
repo_path,
RepositoryOpenFlags::empty(),
Vec::<&Path>::new(),
)?;
if repo.is_bare() {
return Err(Error::Generic("bare repo".to_string()));
}
Ok(repo)
}
///
pub(crate) fn work_dir(repo: &Repository) -> Result<&Path> {
repo.workdir().ok_or(Error::NoWorkDir)
}
/// path to .git folder
pub fn repo_dir(repo_path: &str) -> Result<PathBuf> {
pub fn repo_dir(repo_path: &RepoPath) -> Result<PathBuf> {
let repo = repo(repo_path)?;
Ok(repo.path().to_owned())
}
///
pub fn repo_work_dir(repo_path: &str) -> Result<String> {
pub fn repo_work_dir(repo_path: &RepoPath) -> Result<String> {
let repo = repo(repo_path)?;
work_dir(&repo)?.to_str().map_or_else(
|| Err(Error::Generic("invalid workdir".to_string())),
@ -79,13 +55,13 @@ pub fn repo_work_dir(repo_path: &str) -> Result<String> {
}
///
pub fn get_head(repo_path: &str) -> Result<CommitId> {
pub fn get_head(repo_path: &RepoPath) -> Result<CommitId> {
let repo = repo(repo_path)?;
get_head_repo(&repo)
}
///
pub fn get_head_tuple(repo_path: &str) -> Result<Head> {
pub fn get_head_tuple(repo_path: &RepoPath) -> Result<Head> {
let repo = repo(repo_path)?;
let id = get_head_repo(&repo)?;
let name = get_head_refname(&repo)?;
@ -111,7 +87,10 @@ pub fn get_head_repo(repo: &Repository) -> Result<CommitId> {
}
/// add a file diff from workingdir to stage (will not add removed files see `stage_addremoved`)
pub fn stage_add_file(repo_path: &str, path: &Path) -> Result<()> {
pub fn stage_add_file(
repo_path: &RepoPath,
path: &Path,
) -> Result<()> {
scope_time!("stage_add_file");
let repo = repo(repo_path)?;
@ -126,7 +105,7 @@ pub fn stage_add_file(repo_path: &str, path: &Path) -> Result<()> {
/// like `stage_add_file` but uses a pattern to match/glob multiple files/folders
pub fn stage_add_all(
repo_path: &str,
repo_path: &RepoPath,
pattern: &str,
stage_untracked: Option<ShowUntrackedFilesConfig>,
) -> Result<()> {
@ -158,7 +137,7 @@ pub fn stage_add_all(
}
/// Undo last commit in repo
pub fn undo_last_commit(repo_path: &str) -> Result<()> {
pub fn undo_last_commit(repo_path: &RepoPath) -> Result<()> {
let repo = repo(repo_path)?;
let previous_commit = repo.revparse_single("HEAD~")?;
@ -173,7 +152,10 @@ pub fn undo_last_commit(repo_path: &str) -> Result<()> {
}
/// stage a removed file
pub fn stage_addremoved(repo_path: &str, path: &Path) -> Result<()> {
pub fn stage_addremoved(
repo_path: &RepoPath,
path: &Path,
) -> Result<()> {
scope_time!("stage_addremoved");
let repo = repo(repo_path)?;
@ -250,7 +232,7 @@ mod tests {
let repo_path = root.as_os_str().to_str().unwrap();
assert_eq!(
stage_add_file(repo_path, file_path).is_ok(),
stage_add_file(&repo_path.into(), file_path).is_ok(),
false
);
}
@ -260,7 +242,8 @@ mod tests {
let file_path = Path::new("file1.txt");
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))
.unwrap()
@ -283,7 +266,8 @@ mod tests {
fn test_staging_folder() -> Result<()> {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
let status_count = |s: StatusType| -> usize {
get_status(repo_path, s, None).unwrap().len()
@ -311,7 +295,8 @@ mod tests {
fn test_undo_commit_empty_repo() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
// expect to fail
assert!(undo_last_commit(repo_path).is_err());
@ -321,7 +306,8 @@ mod tests {
fn test_undo_commit() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
// write commit file test.txt
let c1 =
@ -346,7 +332,8 @@ mod tests {
fn test_not_staging_untracked_folder() -> Result<()> {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
fs::create_dir_all(&root.join("a/d"))?;
File::create(&root.join(Path::new("a/d/f1.txt")))?
@ -374,7 +361,8 @@ mod tests {
let file_path = Path::new("file1.txt");
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
let status_count = |s: StatusType| -> usize {
get_status(repo_path, s, None).unwrap().len()
@ -408,7 +396,8 @@ mod tests {
fn test_staging_sub_git_folder() -> Result<()> {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
let status_count = |s: StatusType| -> usize {
get_status(repo_path, s, None).unwrap().len()
@ -418,7 +407,10 @@ mod tests {
fs::create_dir_all(sub)?;
debug_cmd_print(sub.to_str().unwrap(), "git init subgit");
debug_cmd_print(
&sub.to_str().unwrap().into(),
"git init subgit",
);
File::create(sub.join("subgit/foo.txt"))
.unwrap()
@ -437,7 +429,8 @@ mod tests {
fn test_head_empty() -> Result<()> {
let (_td, repo) = repo_init_empty()?;
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
assert_eq!(get_head(repo_path).is_ok(), false);
@ -448,7 +441,8 @@ mod tests {
fn test_head() -> Result<()> {
let (_td, repo) = repo_init()?;
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
assert_eq!(get_head(repo_path).is_ok(), true);

View File

@ -1,8 +1,8 @@
use crate::{
error::Result,
hash,
sync::{self},
AsyncGitNotification, CWD,
sync::{self, RepoPath},
AsyncGitNotification,
};
use crossbeam_channel::Sender;
use std::{
@ -26,12 +26,17 @@ pub struct AsyncTags {
last: Arc<Mutex<Option<(Instant, TagsResult)>>>,
sender: Sender<AsyncGitNotification>,
pending: Arc<AtomicUsize>,
repo: RepoPath,
}
impl AsyncTags {
///
pub fn new(sender: &Sender<AsyncGitNotification>) -> Self {
pub fn new(
repo: RepoPath,
sender: &Sender<AsyncGitNotification>,
) -> Self {
Self {
repo,
last: Arc::new(Mutex::new(None)),
sender: sender.clone(),
pending: Arc::new(AtomicUsize::new(0)),
@ -81,9 +86,10 @@ impl AsyncTags {
let arc_pending = Arc::clone(&self.pending);
self.pending.fetch_add(1, Ordering::Relaxed);
let repo = self.repo.clone();
rayon_core::spawn(move || {
let notify = Self::getter(&arc_last, outdated)
let notify = Self::getter(&repo, &arc_last, outdated)
.expect("error getting tags");
arc_pending.fetch_sub(1, Ordering::Relaxed);
@ -101,10 +107,11 @@ impl AsyncTags {
}
fn getter(
repo: &RepoPath,
arc_last: &Arc<Mutex<Option<(Instant, TagsResult)>>>,
outdated: bool,
) -> Result<bool> {
let tags = sync::get_tags(CWD)?;
let tags = sync::get_tags(repo)?;
let hash = hash(&tags);

View File

@ -23,7 +23,10 @@ use crate::{
AsyncAppNotification, AsyncNotification,
};
use anyhow::{bail, Result};
use asyncgit::{sync, AsyncGitNotification, CWD};
use asyncgit::{
sync::{self, RepoPathRef},
AsyncGitNotification,
};
use crossbeam_channel::Sender;
use crossterm::event::{Event, KeyEvent};
use std::{
@ -41,6 +44,7 @@ use tui::{
/// the main app type
pub struct App {
repo: RepoPathRef,
do_quit: bool,
help: HelpComponent,
msg: MsgComponent,
@ -85,6 +89,7 @@ impl App {
///
#[allow(clippy::too_many_lines)]
pub fn new(
repo: RepoPathRef,
sender: &Sender<AsyncGitNotification>,
sender_app: &Sender<AsyncAppNotification>,
input: Input,
@ -104,11 +109,13 @@ impl App {
key_config.clone(),
),
commit: CommitComponent::new(
repo.clone(),
queue.clone(),
theme.clone(),
key_config.clone(),
),
blame_file_popup: BlameFileComponent::new(
&repo,
&queue,
sender,
&strings::blame_title(&key_config),
@ -116,23 +123,27 @@ impl App {
key_config.clone(),
),
revision_files_popup: RevisionFilesPopup::new(
repo.clone(),
&queue,
sender_app,
theme.clone(),
key_config.clone(),
),
stashmsg_popup: StashMsgComponent::new(
repo.clone(),
queue.clone(),
theme.clone(),
key_config.clone(),
),
inspect_commit_popup: InspectCommitComponent::new(
&repo,
&queue,
sender,
theme.clone(),
key_config.clone(),
),
compare_commits_popup: CompareCommitsComponent::new(
&repo,
&queue,
sender,
theme.clone(),
@ -143,50 +154,59 @@ impl App {
key_config.clone(),
),
push_popup: PushComponent::new(
&repo,
&queue,
sender,
theme.clone(),
key_config.clone(),
),
push_tags_popup: PushTagsComponent::new(
&repo,
&queue,
sender,
theme.clone(),
key_config.clone(),
),
pull_popup: PullComponent::new(
&repo,
&queue,
sender,
theme.clone(),
key_config.clone(),
),
fetch_popup: FetchComponent::new(
repo.clone(),
&queue,
sender,
theme.clone(),
key_config.clone(),
),
tag_commit_popup: TagCommitComponent::new(
repo.clone(),
queue.clone(),
theme.clone(),
key_config.clone(),
),
create_branch_popup: CreateBranchComponent::new(
repo.clone(),
queue.clone(),
theme.clone(),
key_config.clone(),
),
rename_branch_popup: RenameBranchComponent::new(
repo.clone(),
queue.clone(),
theme.clone(),
key_config.clone(),
),
select_branch_popup: BranchListComponent::new(
repo.clone(),
queue.clone(),
theme.clone(),
key_config.clone(),
),
tags_popup: TagListComponent::new(
repo.clone(),
&queue,
sender,
theme.clone(),
@ -215,12 +235,14 @@ impl App {
msg: MsgComponent::new(theme.clone(), key_config.clone()),
tab: 0,
revlog: Revlog::new(
&repo,
&queue,
sender,
theme.clone(),
key_config.clone(),
),
status_tab: Status::new(
repo.clone(),
&queue,
sender,
theme.clone(),
@ -228,17 +250,20 @@ impl App {
options,
),
stashing_tab: Stashing::new(
&repo,
sender,
&queue,
theme.clone(),
key_config.clone(),
),
stashlist_tab: StashList::new(
repo.clone(),
&queue,
theme.clone(),
key_config.clone(),
),
files_tab: FilesTab::new(
repo.clone(),
sender_app,
&queue,
theme.clone(),
@ -249,6 +274,7 @@ impl App {
key_config,
requires_redraw: Cell::new(false),
file_to_open: None,
repo,
}
}
@ -342,6 +368,7 @@ impl App {
let result = match self.file_to_open.take() {
Some(path) => {
ExternalEditorComponent::open_file_in_editor(
&self.repo.borrow(),
Path::new(&path),
)
}
@ -775,7 +802,10 @@ impl App {
}
}
Action::StashDrop(_) | Action::StashPop(_) => {
if let Err(e) = StashList::action_confirmed(&action) {
if let Err(e) = StashList::action_confirmed(
&self.repo.borrow(),
&action,
) {
self.queue.push(InternalEvent::ShowErrorMsg(
e.to_string(),
));
@ -784,16 +814,22 @@ impl App {
flags.insert(NeedsUpdate::ALL);
}
Action::ResetHunk(path, hash) => {
sync::reset_hunk(CWD, &path, hash)?;
sync::reset_hunk(&self.repo.borrow(), &path, hash)?;
flags.insert(NeedsUpdate::ALL);
}
Action::ResetLines(path, lines) => {
sync::discard_lines(CWD, &path, &lines)?;
sync::discard_lines(
&self.repo.borrow(),
&path,
&lines,
)?;
flags.insert(NeedsUpdate::ALL);
}
Action::DeleteLocalBranch(branch_ref) => {
if let Err(e) = sync::delete_branch(CWD, &branch_ref)
{
if let Err(e) = sync::delete_branch(
&self.repo.borrow(),
&branch_ref,
) {
self.queue.push(InternalEvent::ShowErrorMsg(
e.to_string(),
));
@ -824,7 +860,9 @@ impl App {
self.select_branch_popup.update_branches()?;
}
Action::DeleteTag(tag_name) => {
if let Err(error) = sync::delete_tag(CWD, &tag_name) {
if let Err(error) =
sync::delete_tag(&self.repo.borrow(), &tag_name)
{
self.queue.push(InternalEvent::ShowErrorMsg(
error.to_string(),
));

View File

@ -1,5 +1,6 @@
use crate::bug_report;
use anyhow::{anyhow, Result};
use asyncgit::sync::RepoPath;
use clap::{
crate_authors, crate_description, crate_name, crate_version,
App as ClapApp, Arg,
@ -13,6 +14,7 @@ use std::{
pub struct CliArgs {
pub theme: PathBuf,
pub repo_path: RepoPath,
}
pub fn process_cmdline() -> Result<CliArgs> {
@ -41,10 +43,17 @@ pub fn process_cmdline() -> Result<CliArgs> {
)
.arg(
Arg::with_name("directory")
.help("Set the working directory")
.help("Set the git directory")
.short("d")
.long("directory")
.takes_value(true),
)
.arg(
Arg::with_name("workdir")
.help("Set the working directory")
.short("w")
.long("workdir")
.takes_value(true),
);
let arg_matches = app.get_matches();
@ -55,20 +64,31 @@ pub fn process_cmdline() -> Result<CliArgs> {
if arg_matches.is_present("logging") {
setup_logging()?;
}
if arg_matches.is_present("directory") {
let directory =
arg_matches.value_of("directory").unwrap_or(".");
env::set_current_dir(directory)?;
}
let workdir = arg_matches.value_of("workdir").map(PathBuf::from);
let gitdir = arg_matches
.value_of("directory")
.map_or_else(|| PathBuf::from("."), PathBuf::from);
#[allow(clippy::option_if_let_else)]
let repo_path = if let Some(w) = workdir {
RepoPath::Workdir { gitdir, workdir: w }
} else {
RepoPath::Path(gitdir)
};
let arg_theme =
arg_matches.value_of("theme").unwrap_or("theme.ron");
if get_app_config_path()?.join(arg_theme).is_file() {
Ok(CliArgs {
theme: get_app_config_path()?.join(arg_theme),
repo_path,
})
} else {
Ok(CliArgs {
theme: get_app_config_path()?.join("theme.ron"),
repo_path,
})
}
}

View File

@ -11,7 +11,7 @@ use crate::{
};
use anyhow::Result;
use asyncgit::{
sync::{BlameHunk, CommitId, FileBlame},
sync::{BlameHunk, CommitId, FileBlame, RepoPathRef},
AsyncBlame, AsyncGitNotification, BlameParams,
};
use crossbeam_channel::Sender;
@ -244,6 +244,7 @@ impl Component for BlameFileComponent {
impl BlameFileComponent {
///
pub fn new(
repo: &RepoPathRef,
queue: &Queue,
sender: &Sender<AsyncGitNotification>,
title: &str,
@ -253,7 +254,10 @@ impl BlameFileComponent {
Self {
title: String::from(title),
theme,
async_blame: AsyncBlame::new(sender),
async_blame: AsyncBlame::new(
repo.borrow().clone(),
sender,
),
queue: queue.clone(),
visible: false,
file_path: None,

View File

@ -19,9 +19,9 @@ use asyncgit::{
RemoteBranch,
},
checkout_branch, get_branches_info, BranchInfo, BranchType,
CommitId, RepoState,
CommitId, RepoPathRef, RepoState,
},
AsyncGitNotification, CWD,
AsyncGitNotification,
};
use crossterm::event::Event;
use std::{cell::Cell, convert::TryInto};
@ -39,6 +39,7 @@ use unicode_truncate::UnicodeTruncateStr;
///
pub struct BranchListComponent {
repo: RepoPathRef,
branches: Vec<BranchInfo>,
local: bool,
visible: bool,
@ -324,6 +325,7 @@ impl Component for BranchListComponent {
impl BranchListComponent {
pub fn new(
repo: RepoPathRef,
queue: Queue,
theme: SharedTheme,
key_config: SharedKeyConfig,
@ -338,6 +340,7 @@ impl BranchListComponent {
theme,
key_config,
current_height: Cell::new(0),
repo,
}
}
@ -352,7 +355,8 @@ impl BranchListComponent {
/// fetch list of branches
pub fn update_branches(&mut self) -> Result<()> {
if self.is_visible() {
self.branches = get_branches_info(CWD, self.local)?;
self.branches =
get_branches_info(&self.repo.borrow(), self.local)?;
//remove remote branch called `HEAD`
if !self.local {
self.branches
@ -386,7 +390,7 @@ impl BranchListComponent {
self.branches.get(usize::from(self.selection))
{
sync::merge_branch(
CWD,
&self.repo.borrow(),
&branch.name,
self.get_branch_type(),
)?;
@ -402,7 +406,7 @@ impl BranchListComponent {
self.branches.get(usize::from(self.selection))
{
sync::rebase_branch(
CWD,
&self.repo.borrow(),
&branch.name,
self.get_branch_type(),
)?;
@ -425,7 +429,8 @@ impl BranchListComponent {
self.hide();
self.queue.push(InternalEvent::Update(NeedsUpdate::ALL));
if sync::repo_state(CWD)? != RepoState::Clean {
if sync::repo_state(&self.repo.borrow())? != RepoState::Clean
{
self.queue.push(InternalEvent::TabSwitch);
}
@ -614,13 +619,13 @@ impl BranchListComponent {
if self.local {
checkout_branch(
asyncgit::CWD,
&self.repo.borrow(),
&self.branches[self.selection as usize].reference,
)?;
self.hide();
} else {
checkout_remote_branch(
CWD,
&self.repo.borrow(),
&self.branches[self.selection as usize],
)?;
self.local = true;

View File

@ -11,13 +11,17 @@ use crate::{
ui::style::SharedTheme,
};
use anyhow::Result;
use asyncgit::{sync, StatusItem, StatusItemType, CWD};
use asyncgit::{
sync::{self, RepoPathRef},
StatusItem, StatusItemType,
};
use crossterm::event::Event;
use std::path::Path;
use tui::{backend::Backend, layout::Rect, Frame};
///
pub struct ChangesComponent {
repo: RepoPathRef,
files: StatusTreeComponent,
is_working_dir: bool,
queue: Queue,
@ -27,7 +31,10 @@ pub struct ChangesComponent {
impl ChangesComponent {
///
//TODO: fix
#[allow(clippy::too_many_arguments)]
pub fn new(
repo: RepoPathRef,
title: &str,
focus: bool,
is_working_dir: bool,
@ -48,6 +55,7 @@ impl ChangesComponent {
queue,
key_config,
options,
repo,
}
}
@ -85,9 +93,15 @@ impl ChangesComponent {
let path = Path::new(i.path.as_str());
match i.status {
StatusItemType::Deleted => {
sync::stage_addremoved(CWD, path)?;
sync::stage_addremoved(
&self.repo.borrow(),
path,
)?;
}
_ => sync::stage_add_file(CWD, path)?,
_ => sync::stage_add_file(
&self.repo.borrow(),
path,
)?,
};
if self.is_empty() {
@ -103,7 +117,7 @@ impl ChangesComponent {
//TODO: check if we can handle the one file case with it aswell
sync::stage_add_all(
CWD,
&self.repo.borrow(),
tree_item.info.full_path.as_str(),
config,
)?;
@ -112,7 +126,7 @@ impl ChangesComponent {
}
let path = tree_item.info.full_path.as_str();
sync::reset_stage(CWD, path)?;
sync::reset_stage(&self.repo.borrow(), path)?;
return Ok(true);
}
@ -122,7 +136,7 @@ impl ChangesComponent {
fn index_add_all(&mut self) -> Result<()> {
let config = self.options.borrow().status_show_untracked;
sync::stage_add_all(CWD, "*", config)?;
sync::stage_add_all(&self.repo.borrow(), "*", config)?;
self.queue.push(InternalEvent::Update(NeedsUpdate::ALL));
@ -130,7 +144,7 @@ impl ChangesComponent {
}
fn stage_remove_all(&mut self) -> Result<()> {
sync::reset_stage(CWD, "*")?;
sync::reset_stage(&self.repo.borrow(), "*")?;
self.queue.push(InternalEvent::Update(NeedsUpdate::ALL));
@ -155,9 +169,10 @@ impl ChangesComponent {
fn add_to_ignore(&mut self) -> bool {
if let Some(tree_item) = self.selection() {
if let Err(e) =
sync::add_to_ignore(CWD, &tree_item.info.full_path)
{
if let Err(e) = sync::add_to_ignore(
&self.repo.borrow(),
&tree_item.info.full_path,
) {
self.queue.push(InternalEvent::ShowErrorMsg(
format!(
"ignore error:\n{}\nfile:\n{:?}",

View File

@ -13,9 +13,9 @@ use anyhow::Result;
use asyncgit::{
cached, message_prettify,
sync::{
self, get_config_string, CommitId, HookResult, RepoState,
self, get_config_string, CommitId, HookResult, RepoPathRef,
RepoState,
},
CWD,
};
use crossterm::event::Event;
use easy_cast::Cast;
@ -37,6 +37,7 @@ enum Mode {
}
pub struct CommitComponent {
repo: RepoPathRef,
input: TextInputComponent,
mode: Mode,
queue: Queue,
@ -51,6 +52,7 @@ const FIRST_LINE_LIMIT: usize = 50;
impl CommitComponent {
///
pub fn new(
repo: RepoPathRef,
queue: Queue,
theme: SharedTheme,
key_config: SharedKeyConfig,
@ -58,7 +60,6 @@ impl CommitComponent {
Self {
queue,
mode: Mode::Normal,
input: TextInputComponent::new(
theme.clone(),
key_config.clone(),
@ -67,9 +68,10 @@ impl CommitComponent {
true,
),
key_config,
git_branch_name: cached::BranchName::new(CWD),
git_branch_name: cached::BranchName::new(repo.clone()),
commit_template: None,
theme,
repo,
}
}
@ -126,7 +128,8 @@ impl CommitComponent {
}
pub fn show_editor(&mut self) -> Result<()> {
let file_path = sync::repo_dir(CWD)?.join("COMMIT_EDITMSG");
let file_path = sync::repo_dir(&self.repo.borrow())?
.join("COMMIT_EDITMSG");
{
let mut file = File::create(&file_path)?;
@ -140,7 +143,10 @@ impl CommitComponent {
)?;
}
ExternalEditorComponent::open_file_in_editor(&file_path)?;
ExternalEditorComponent::open_file_in_editor(
&self.repo.borrow(),
&file_path,
)?;
let mut message = String::new();
@ -157,11 +163,12 @@ impl CommitComponent {
}
fn commit(&mut self) -> Result<()> {
let gpgsign = get_config_string(CWD, "commit.gpgsign")
.ok()
.flatten()
.and_then(|path| path.parse::<bool>().ok())
.unwrap_or_default();
let gpgsign =
get_config_string(&self.repo.borrow(), "commit.gpgsign")
.ok()
.flatten()
.and_then(|path| path.parse::<bool>().ok())
.unwrap_or_default();
if gpgsign {
anyhow::bail!("config commit.gpgsign=true detected.\ngpg signing not supported.\ndeactivate in your repo/gitconfig to be able to commit without signing.");
@ -173,7 +180,9 @@ impl CommitComponent {
}
fn commit_with_msg(&mut self, msg: String) -> Result<()> {
if let HookResult::NotOk(e) = sync::hooks_pre_commit(CWD)? {
if let HookResult::NotOk(e) =
sync::hooks_pre_commit(&self.repo.borrow())?
{
log::error!("pre-commit hook error: {}", e);
self.queue.push(InternalEvent::ShowErrorMsg(format!(
"pre-commit hook error:\n{}",
@ -183,7 +192,7 @@ impl CommitComponent {
}
let mut msg = message_prettify(msg, Some(b'#'))?;
if let HookResult::NotOk(e) =
sync::hooks_commit_msg(CWD, &mut msg)?
sync::hooks_commit_msg(&self.repo.borrow(), &mut msg)?
{
log::error!("commit-msg hook error: {}", e);
self.queue.push(InternalEvent::ShowErrorMsg(format!(
@ -194,9 +203,13 @@ impl CommitComponent {
}
let res = match &self.mode {
Mode::Normal => sync::commit(CWD, &msg),
Mode::Amend(amend) => sync::amend(CWD, *amend, &msg),
Mode::Merge(ids) => sync::merge_commit(CWD, &msg, ids),
Mode::Normal => sync::commit(&self.repo.borrow(), &msg),
Mode::Amend(amend) => {
sync::amend(&self.repo.borrow(), *amend, &msg)
}
Mode::Merge(ids) => {
sync::merge_commit(&self.repo.borrow(), &msg, ids)
}
};
if let Err(e) = res {
@ -208,7 +221,9 @@ impl CommitComponent {
return Ok(());
}
if let HookResult::NotOk(e) = sync::hooks_post_commit(CWD)? {
if let HookResult::NotOk(e) =
sync::hooks_post_commit(&self.repo.borrow())?
{
log::error!("post-commit hook error: {}", e);
self.queue.push(InternalEvent::ShowErrorMsg(format!(
"post-commit hook error:\n{}",
@ -229,7 +244,7 @@ impl CommitComponent {
fn can_amend(&self) -> bool {
matches!(self.mode, Mode::Normal)
&& sync::get_head(CWD).is_ok()
&& sync::get_head(&self.repo.borrow()).is_ok()
&& (self.is_empty() || !self.is_changed())
}
@ -244,10 +259,11 @@ impl CommitComponent {
fn amend(&mut self) -> Result<()> {
if self.can_amend() {
let id = sync::get_head(CWD)?;
let id = sync::get_head(&self.repo.borrow())?;
self.mode = Mode::Amend(id);
let details = sync::get_commit_details(CWD, id)?;
let details =
sync::get_commit_details(&self.repo.borrow(), id)?;
self.input.set_title(strings::commit_title_amend());
@ -360,17 +376,22 @@ impl Component for CommitComponent {
self.mode = Mode::Normal;
self.mode = if sync::repo_state(CWD)? == RepoState::Merge {
let ids = sync::mergehead_ids(CWD)?;
self.mode = if sync::repo_state(&self.repo.borrow())?
== RepoState::Merge
{
let ids = sync::mergehead_ids(&self.repo.borrow())?;
self.input.set_title(strings::commit_title_merge());
self.input.set_text(sync::merge_msg(CWD)?);
self.input
.set_text(sync::merge_msg(&self.repo.borrow())?);
Mode::Merge(ids)
} else {
self.commit_template =
get_config_string(CWD, "commit.template")
.ok()
.flatten()
.and_then(|path| read_to_string(path).ok());
self.commit_template = get_config_string(
&self.repo.borrow(),
"commit.template",
)
.ok()
.flatten()
.and_then(|path| read_to_string(path).ok());
if self.is_empty() {
if let Some(s) = &self.commit_template {

View File

@ -12,10 +12,7 @@ use crate::{
ui::style::SharedTheme,
};
use anyhow::Result;
use asyncgit::{
sync::{self, CommitDetails, CommitId},
CWD,
};
use asyncgit::sync::{self, CommitDetails, CommitId, RepoPathRef};
use crossterm::event::Event;
use tui::{
backend::Backend,
@ -25,6 +22,7 @@ use tui::{
};
pub struct CompareDetailsComponent {
repo: RepoPathRef,
data: Option<(CommitDetails, CommitDetails)>,
theme: SharedTheme,
focused: bool,
@ -32,18 +30,27 @@ pub struct CompareDetailsComponent {
impl CompareDetailsComponent {
///
pub const fn new(theme: SharedTheme, focused: bool) -> Self {
pub const fn new(
repo: RepoPathRef,
theme: SharedTheme,
focused: bool,
) -> Self {
Self {
data: None,
theme,
focused,
repo,
}
}
pub fn set_commits(&mut self, ids: Option<(CommitId, CommitId)>) {
self.data = ids.and_then(|ids| {
let c1 = sync::get_commit_details(CWD, ids.0).ok();
let c2 = sync::get_commit_details(CWD, ids.1).ok();
let c1 =
sync::get_commit_details(&self.repo.borrow(), ids.0)
.ok();
let c2 =
sync::get_commit_details(&self.repo.borrow(), ids.1)
.ok();
c1.and_then(|c1| {
c2.map(|c2| {

View File

@ -11,9 +11,8 @@ use crate::{
ui::style::SharedTheme,
};
use anyhow::Result;
use asyncgit::{
sync::{self, CommitDetails, CommitId, CommitMessage},
CWD,
use asyncgit::sync::{
self, CommitDetails, CommitId, CommitMessage, RepoPathRef,
};
use crossterm::event::Event;
use std::clone::Clone;
@ -30,6 +29,7 @@ use tui::{
use super::style::Detail;
pub struct DetailsComponent {
repo: RepoPathRef,
data: Option<CommitDetails>,
tags: Vec<String>,
theme: SharedTheme,
@ -46,11 +46,13 @@ type WrappedCommitMessage<'a> =
impl DetailsComponent {
///
pub const fn new(
repo: RepoPathRef,
theme: SharedTheme,
key_config: SharedKeyConfig,
focused: bool,
) -> Self {
Self {
repo,
data: None,
tags: Vec::new(),
theme,
@ -69,8 +71,9 @@ impl DetailsComponent {
) {
self.tags.clear();
self.data =
id.and_then(|id| sync::get_commit_details(CWD, id).ok());
self.data = id.and_then(|id| {
sync::get_commit_details(&self.repo.borrow(), id).ok()
});
self.scroll.reset();

View File

@ -12,8 +12,8 @@ use crate::{
};
use anyhow::Result;
use asyncgit::{
sync::CommitTags, AsyncCommitFiles, AsyncGitNotification,
CommitFilesParams,
sync::{CommitTags, RepoPathRef},
AsyncCommitFiles, AsyncGitNotification, CommitFilesParams,
};
use compare_details::CompareDetailsComponent;
use crossbeam_channel::Sender;
@ -40,6 +40,7 @@ impl CommitDetailsComponent {
///
pub fn new(
repo: &RepoPathRef,
queue: &Queue,
sender: &Sender<AsyncGitNotification>,
theme: SharedTheme,
@ -47,15 +48,20 @@ impl CommitDetailsComponent {
) -> Self {
Self {
single_details: DetailsComponent::new(
repo.clone(),
theme.clone(),
key_config.clone(),
false,
),
compare_details: CompareDetailsComponent::new(
repo.clone(),
theme.clone(),
false,
),
git_commit_files: AsyncCommitFiles::new(sender),
git_commit_files: AsyncCommitFiles::new(
repo.borrow().clone(),
sender,
),
file_tree: StatusTreeComponent::new(
"",
false,

View File

@ -9,9 +9,9 @@ use crate::{
};
use anyhow::Result;
use asyncgit::{
sync::{self, diff::DiffOptions, CommitId},
sync::{self, diff::DiffOptions, CommitId, RepoPathRef},
AsyncDiff, AsyncGitNotification, CommitFilesParams, DiffParams,
DiffType, CWD,
DiffType,
};
use crossbeam_channel::Sender;
use crossterm::event::Event;
@ -23,6 +23,7 @@ use tui::{
};
pub struct CompareCommitsComponent {
repo: RepoPathRef,
commit_ids: Option<(CommitId, CommitId)>,
diff: DiffComponent,
details: CommitDetailsComponent,
@ -156,26 +157,30 @@ impl CompareCommitsComponent {
///
pub fn new(
repo: &RepoPathRef,
queue: &Queue,
sender: &Sender<AsyncGitNotification>,
theme: SharedTheme,
key_config: SharedKeyConfig,
) -> Self {
Self {
repo: repo.clone(),
details: CommitDetailsComponent::new(
repo,
queue,
sender,
theme.clone(),
key_config.clone(),
),
diff: DiffComponent::new(
repo.clone(),
queue.clone(),
theme,
key_config.clone(),
true,
),
commit_ids: None,
git_diff: AsyncDiff::new(sender),
git_diff: AsyncDiff::new(repo.borrow().clone(), sender),
visible: false,
key_config,
}
@ -190,7 +195,7 @@ impl CompareCommitsComponent {
let other = if let Some(other) = other {
other
} else {
sync::get_head_tuple(CWD)?.id
sync::get_head_tuple(&self.repo.borrow())?.id
};
self.commit_ids = Some((id, other));
self.show()?;

View File

@ -10,7 +10,7 @@ use crate::{
ui::style::SharedTheme,
};
use anyhow::Result;
use asyncgit::{sync, CWD};
use asyncgit::sync::{self, RepoPathRef};
use crossterm::event::Event;
use easy_cast::Cast;
use tui::{
@ -18,6 +18,7 @@ use tui::{
};
pub struct CreateBranchComponent {
repo: RepoPathRef,
input: TextInputComponent,
queue: Queue,
key_config: SharedKeyConfig,
@ -95,6 +96,7 @@ impl Component for CreateBranchComponent {
impl CreateBranchComponent {
///
pub fn new(
repo: RepoPathRef,
queue: Queue,
theme: SharedTheme,
key_config: SharedKeyConfig,
@ -110,6 +112,7 @@ impl CreateBranchComponent {
),
theme,
key_config,
repo,
}
}
@ -122,7 +125,10 @@ impl CreateBranchComponent {
///
pub fn create_branch(&mut self) {
let res = sync::create_branch(CWD, self.input.get_text());
let res = sync::create_branch(
&self.repo.borrow(),
self.input.get_text(),
);
self.input.clear();
self.hide();

View File

@ -13,8 +13,8 @@ use crate::{
use anyhow::Result;
use asyncgit::{
hash,
sync::{self, diff::DiffLinePosition},
DiffLine, DiffLineType, FileDiff, CWD,
sync::{self, diff::DiffLinePosition, RepoPathRef},
DiffLine, DiffLineType, FileDiff,
};
use bytesize::ByteSize;
use crossterm::event::Event;
@ -100,6 +100,7 @@ impl Selection {
///
pub struct DiffComponent {
repo: RepoPathRef,
diff: Option<FileDiff>,
pending: bool,
selection: Selection,
@ -117,6 +118,7 @@ pub struct DiffComponent {
impl DiffComponent {
///
pub fn new(
repo: RepoPathRef,
queue: Queue,
theme: SharedTheme,
key_config: SharedKeyConfig,
@ -135,6 +137,7 @@ impl DiffComponent {
theme,
key_config,
is_immutable,
repo,
}
}
///
@ -458,7 +461,11 @@ impl DiffComponent {
if let Some(diff) = &self.diff {
if let Some(hunk) = self.selected_hunk {
let hash = diff.hunks[hunk].header_hash;
sync::unstage_hunk(CWD, &self.current.path, hash)?;
sync::unstage_hunk(
&self.repo.borrow(),
&self.current.path,
hash,
)?;
self.queue_update();
}
}
@ -471,12 +478,16 @@ impl DiffComponent {
if let Some(hunk) = self.selected_hunk {
if diff.untracked {
sync::stage_add_file(
CWD,
&self.repo.borrow(),
Path::new(&self.current.path),
)?;
} else {
let hash = diff.hunks[hunk].header_hash;
sync::stage_hunk(CWD, &self.current.path, hash)?;
sync::stage_hunk(
&self.repo.borrow(),
&self.current.path,
hash,
)?;
}
self.queue_update();
@ -524,7 +535,7 @@ impl DiffComponent {
self,
"(un)stage lines:",
sync::stage_lines(
CWD,
&self.repo.borrow(),
&self.current.path,
self.is_stage(),
&selected_lines,

View File

@ -8,9 +8,8 @@ use crate::{
ui::{self, style::SharedTheme},
};
use anyhow::{anyhow, bail, Result};
use asyncgit::{
sync::{get_config_string, utils::repo_work_dir},
CWD,
use asyncgit::sync::{
get_config_string, utils::repo_work_dir, RepoPath,
};
use crossterm::{
event::Event,
@ -49,8 +48,11 @@ impl ExternalEditorComponent {
}
/// opens file at given `path` in an available editor
pub fn open_file_in_editor(path: &Path) -> Result<()> {
let work_dir = repo_work_dir(CWD)?;
pub fn open_file_in_editor(
repo: &RepoPath,
path: &Path,
) -> Result<()> {
let work_dir = repo_work_dir(repo)?;
let path = if path.is_relative() {
Path::new(&work_dir).join(path)
@ -71,7 +73,9 @@ impl ExternalEditorComponent {
let editor = env::var(environment_options[0])
.ok()
.or_else(|| get_config_string(CWD, "core.editor").ok()?)
.or_else(|| {
get_config_string(repo, "core.editor").ok()?
})
.or_else(|| env::var(environment_options[1]).ok())
.or_else(|| env::var(environment_options[2]).ok())
.unwrap_or_else(|| String::from("vi"));

View File

@ -11,9 +11,12 @@ use crate::{
use anyhow::Result;
use asyncgit::{
asyncjob::AsyncSingleJob,
sync::cred::{
extract_username_password, need_username_password,
BasicAuthCredential,
sync::{
cred::{
extract_username_password, need_username_password,
BasicAuthCredential,
},
RepoPathRef,
},
AsyncFetchJob, AsyncGitNotification, ProgressPercent,
};
@ -29,6 +32,7 @@ use tui::{
///
pub struct FetchComponent {
repo: RepoPathRef,
visible: bool,
async_fetch: AsyncSingleJob<AsyncFetchJob>,
progress: Option<ProgressPercent>,
@ -42,6 +46,7 @@ pub struct FetchComponent {
impl FetchComponent {
///
pub fn new(
repo: RepoPathRef,
queue: &Queue,
sender: &Sender<AsyncGitNotification>,
theme: SharedTheme,
@ -59,15 +64,16 @@ impl FetchComponent {
),
theme,
key_config,
repo,
}
}
///
pub fn fetch(&mut self) -> Result<()> {
self.show()?;
if need_username_password()? {
let cred =
extract_username_password().unwrap_or_else(|_| {
if need_username_password(&self.repo.borrow())? {
let cred = extract_username_password(&self.repo.borrow())
.unwrap_or_else(|_| {
BasicAuthCredential::new(None, None)
});
if cred.is_complete() {
@ -87,7 +93,10 @@ impl FetchComponent {
self.pending = true;
self.progress = None;
self.progress = Some(ProgressPercent::empty());
self.async_fetch.spawn(AsyncFetchJob::new(cred));
self.async_fetch.spawn(AsyncFetchJob::new(
self.repo.borrow().clone(),
cred,
));
}
///

View File

@ -12,7 +12,7 @@ use crate::{
};
use anyhow::Result;
use asyncgit::{
sync::{diff::DiffOptions, CommitId, CommitTags},
sync::{diff::DiffOptions, CommitId, CommitTags, RepoPathRef},
AsyncDiff, AsyncGitNotification, CommitFilesParams, DiffParams,
DiffType,
};
@ -176,6 +176,7 @@ impl InspectCommitComponent {
///
pub fn new(
repo: &RepoPathRef,
queue: &Queue,
sender: &Sender<AsyncGitNotification>,
theme: SharedTheme,
@ -184,12 +185,14 @@ impl InspectCommitComponent {
Self {
queue: queue.clone(),
details: CommitDetailsComponent::new(
repo,
queue,
sender,
theme.clone(),
key_config.clone(),
),
diff: DiffComponent::new(
repo.clone(),
queue.clone(),
theme,
key_config.clone(),
@ -197,7 +200,7 @@ impl InspectCommitComponent {
),
commit_id: None,
tags: None,
git_diff: AsyncDiff::new(sender),
git_diff: AsyncDiff::new(repo.borrow().clone(), sender),
visible: false,
key_config,
}

View File

@ -17,10 +17,9 @@ use asyncgit::{
extract_username_password, need_username_password,
BasicAuthCredential,
},
get_default_remote,
get_default_remote, RepoPathRef,
},
AsyncGitNotification, AsyncPull, FetchRequest, RemoteProgress,
CWD,
};
use crossbeam_channel::Sender;
use crossterm::event::Event;
@ -34,6 +33,7 @@ use tui::{
///
pub struct PullComponent {
repo: RepoPathRef,
visible: bool,
git_fetch: AsyncPull,
progress: Option<RemoteProgress>,
@ -48,17 +48,19 @@ pub struct PullComponent {
impl PullComponent {
///
pub fn new(
repo: &RepoPathRef,
queue: &Queue,
sender: &Sender<AsyncGitNotification>,
theme: SharedTheme,
key_config: SharedKeyConfig,
) -> Self {
Self {
repo: repo.clone(),
queue: queue.clone(),
pending: false,
visible: false,
branch: String::new(),
git_fetch: AsyncPull::new(sender),
git_fetch: AsyncPull::new(repo.borrow().clone(), sender),
progress: None,
input_cred: CredComponent::new(
theme.clone(),
@ -73,9 +75,9 @@ impl PullComponent {
pub fn fetch(&mut self, branch: String) -> Result<()> {
self.branch = branch;
self.show()?;
if need_username_password()? {
let cred =
extract_username_password().unwrap_or_else(|_| {
if need_username_password(&self.repo.borrow())? {
let cred = extract_username_password(&self.repo.borrow())
.unwrap_or_else(|_| {
BasicAuthCredential::new(None, None)
});
if cred.is_complete() {
@ -96,7 +98,7 @@ impl PullComponent {
self.pending = true;
self.progress = None;
self.git_fetch.request(FetchRequest {
remote: get_default_remote(CWD)?,
remote: get_default_remote(&self.repo.borrow())?,
branch: self.branch.clone(),
basic_credential: cred,
})?;
@ -144,11 +146,13 @@ impl PullComponent {
// check if something is incoming and try a ff merge then
fn try_ff_merge(&mut self) -> Result<()> {
let branch_compare =
sync::branch_compare_upstream(CWD, &self.branch)?;
let branch_compare = sync::branch_compare_upstream(
&self.repo.borrow(),
&self.branch,
)?;
if branch_compare.behind > 0 {
let ff_res = sync::branch_merge_upstream_fastforward(
CWD,
&self.repo.borrow(),
&self.branch,
);
if let Err(err) = ff_res {
@ -167,13 +171,19 @@ impl PullComponent {
try_or_popup!(
self,
"rebase failed:",
sync::merge_upstream_rebase(CWD, &self.branch)
sync::merge_upstream_rebase(
&self.repo.borrow(),
&self.branch
)
);
} else {
try_or_popup!(
self,
"merge failed:",
sync::merge_upstream_commit(CWD, &self.branch)
sync::merge_upstream_commit(
&self.repo.borrow(),
&self.branch
)
);
}
}
@ -182,8 +192,10 @@ impl PullComponent {
self.queue.push(InternalEvent::ConfirmAction(
Action::PullMerge {
incoming,
rebase: sync::config_is_pull_rebase(CWD)
.unwrap_or_default(),
rebase: sync::config_is_pull_rebase(
&self.repo.borrow(),
)
.unwrap_or_default(),
},
));
self.hide();

View File

@ -15,10 +15,10 @@ use asyncgit::{
extract_username_password, need_username_password,
BasicAuthCredential,
},
get_branch_remote, get_default_remote,
get_branch_remote, get_default_remote, RepoPathRef,
},
AsyncGitNotification, AsyncPush, PushRequest, RemoteProgress,
RemoteProgressState, CWD,
RemoteProgressState,
};
use crossbeam_channel::Sender;
use crossterm::event::Event;
@ -50,6 +50,7 @@ impl PushComponentModifier {
///
pub struct PushComponent {
repo: RepoPathRef,
modifier: PushComponentModifier,
visible: bool,
git_push: AsyncPush,
@ -65,18 +66,20 @@ pub struct PushComponent {
impl PushComponent {
///
pub fn new(
repo: &RepoPathRef,
queue: &Queue,
sender: &Sender<AsyncGitNotification>,
theme: SharedTheme,
key_config: SharedKeyConfig,
) -> Self {
Self {
repo: repo.clone(),
queue: queue.clone(),
modifier: PushComponentModifier::None,
pending: false,
visible: false,
branch: String::new(),
git_push: AsyncPush::new(sender),
git_push: AsyncPush::new(repo.borrow().clone(), sender),
progress: None,
input_cred: CredComponent::new(
theme.clone(),
@ -104,9 +107,9 @@ impl PushComponent {
self.show()?;
if need_username_password()? {
let cred =
extract_username_password().unwrap_or_else(|_| {
if need_username_password(&self.repo.borrow())? {
let cred = extract_username_password(&self.repo.borrow())
.unwrap_or_else(|_| {
BasicAuthCredential::new(None, None)
});
if cred.is_complete() {
@ -126,13 +129,13 @@ impl PushComponent {
force: bool,
) -> Result<()> {
let remote = if let Ok(Some(remote)) =
get_branch_remote(CWD, &self.branch)
get_branch_remote(&self.repo.borrow(), &self.branch)
{
log::info!("push: branch '{}' has upstream for remote '{}' - using that",self.branch,remote);
remote
} else {
log::info!("push: branch '{}' has no upstream - looking up default remote",self.branch);
let remote = get_default_remote(CWD)?;
let remote = get_default_remote(&self.repo.borrow())?;
log::info!(
"push: branch '{}' to remote '{}'",
self.branch,

View File

@ -16,8 +16,9 @@ use asyncgit::{
BasicAuthCredential,
},
get_default_remote, AsyncProgress, PushTagsProgress,
RepoPathRef,
},
AsyncGitNotification, AsyncPushTags, PushTagsRequest, CWD,
AsyncGitNotification, AsyncPushTags, PushTagsRequest,
};
use crossbeam_channel::Sender;
use crossterm::event::Event;
@ -31,6 +32,7 @@ use tui::{
///
pub struct PushTagsComponent {
repo: RepoPathRef,
visible: bool,
git_push: AsyncPushTags,
progress: Option<PushTagsProgress>,
@ -44,16 +46,21 @@ pub struct PushTagsComponent {
impl PushTagsComponent {
///
pub fn new(
repo: &RepoPathRef,
queue: &Queue,
sender: &Sender<AsyncGitNotification>,
theme: SharedTheme,
key_config: SharedKeyConfig,
) -> Self {
Self {
repo: repo.clone(),
queue: queue.clone(),
pending: false,
visible: false,
git_push: AsyncPushTags::new(sender),
git_push: AsyncPushTags::new(
repo.borrow().clone(),
sender,
),
progress: None,
input_cred: CredComponent::new(
theme.clone(),
@ -67,9 +74,9 @@ impl PushTagsComponent {
///
pub fn push_tags(&mut self) -> Result<()> {
self.show()?;
if need_username_password()? {
let cred =
extract_username_password().unwrap_or_else(|_| {
if need_username_password(&self.repo.borrow())? {
let cred = extract_username_password(&self.repo.borrow())
.unwrap_or_else(|_| {
BasicAuthCredential::new(None, None)
});
if cred.is_complete() {
@ -90,7 +97,7 @@ impl PushTagsComponent {
self.pending = true;
self.progress = None;
self.git_push.request(PushTagsRequest {
remote: get_default_remote(CWD)?,
remote: get_default_remote(&self.repo.borrow())?,
basic_credential: cred,
})?;
Ok(())

View File

@ -10,14 +10,12 @@ use crate::{
ui::style::SharedTheme,
};
use anyhow::Result;
use asyncgit::{
sync::{self},
CWD,
};
use asyncgit::sync::{self, RepoPathRef};
use crossterm::event::Event;
use tui::{backend::Backend, layout::Rect, Frame};
pub struct RenameBranchComponent {
repo: RepoPathRef,
input: TextInputComponent,
branch_ref: Option<String>,
queue: Queue,
@ -92,11 +90,13 @@ impl Component for RenameBranchComponent {
impl RenameBranchComponent {
///
pub fn new(
repo: RepoPathRef,
queue: Queue,
theme: SharedTheme,
key_config: SharedKeyConfig,
) -> Self {
Self {
repo,
queue,
input: TextInputComponent::new(
theme,
@ -127,8 +127,11 @@ impl RenameBranchComponent {
///
pub fn rename_branch(&mut self) {
if let Some(br) = &self.branch_ref {
let res =
sync::rename_branch(CWD, br, self.input.get_text());
let res = sync::rename_branch(
&self.repo.borrow(),
br,
self.input.get_text(),
);
match res {
Ok(_) => {

View File

@ -11,10 +11,7 @@ use crate::{
AsyncAppNotification, AsyncNotification,
};
use anyhow::Result;
use asyncgit::{
sync::{self, CommitId, TreeFile},
CWD,
};
use asyncgit::sync::{self, CommitId, RepoPathRef, TreeFile};
use crossbeam_channel::Sender;
use crossterm::event::Event;
use filetreelist::{FileTree, FileTreeItem};
@ -37,6 +34,7 @@ enum Focus {
}
pub struct RevisionFilesComponent {
repo: RepoPathRef,
queue: Queue,
theme: SharedTheme,
//TODO: store TreeFiles in `tree`
@ -52,6 +50,7 @@ pub struct RevisionFilesComponent {
impl RevisionFilesComponent {
///
pub fn new(
repo: RepoPathRef,
queue: &Queue,
sender: &Sender<AsyncAppNotification>,
theme: SharedTheme,
@ -62,6 +61,7 @@ impl RevisionFilesComponent {
tree: FileTree::default(),
scroll: VerticalScroll::new(),
current_file: SyntaxTextComponent::new(
repo.clone(),
sender,
key_config.clone(),
theme.clone(),
@ -71,6 +71,7 @@ impl RevisionFilesComponent {
revision: None,
focus: Focus::Tree,
key_config,
repo,
}
}
@ -79,7 +80,8 @@ impl RevisionFilesComponent {
let same_id =
self.revision.map(|c| c == commit).unwrap_or_default();
if !same_id {
self.files = sync::tree_files(CWD, commit)?;
self.files =
sync::tree_files(&self.repo.borrow(), commit)?;
let filenames: Vec<&Path> =
self.files.iter().map(|f| f.path.as_path()).collect();
self.tree = FileTree::new(&filenames, &BTreeSet::new())?;

View File

@ -13,7 +13,7 @@ use crate::{
AsyncAppNotification, AsyncNotification,
};
use anyhow::Result;
use asyncgit::sync::CommitId;
use asyncgit::sync::{CommitId, RepoPathRef};
use crossbeam_channel::Sender;
use crossterm::event::Event;
use tui::{backend::Backend, layout::Rect, widgets::Clear, Frame};
@ -27,6 +27,7 @@ pub struct RevisionFilesPopup {
impl RevisionFilesPopup {
///
pub fn new(
repo: RepoPathRef,
queue: &Queue,
sender: &Sender<AsyncAppNotification>,
theme: SharedTheme,
@ -34,6 +35,7 @@ impl RevisionFilesPopup {
) -> Self {
Self {
files: RevisionFilesComponent::new(
repo,
queue,
sender,
theme,

View File

@ -11,11 +11,12 @@ use crate::{
ui::style::SharedTheme,
};
use anyhow::Result;
use asyncgit::{sync, CWD};
use asyncgit::sync::{self, RepoPathRef};
use crossterm::event::Event;
use tui::{backend::Backend, layout::Rect, Frame};
pub struct StashMsgComponent {
repo: RepoPathRef,
options: StashingOptions,
input: TextInputComponent,
queue: Queue,
@ -63,8 +64,8 @@ impl Component for StashMsgComponent {
if let Event::Key(e) = ev {
if e == self.key_config.keys.enter {
match sync::stash_save(
CWD,
let result = sync::stash_save(
&self.repo.borrow(),
if self.input.get_text().is_empty() {
None
} else {
@ -72,7 +73,8 @@ impl Component for StashMsgComponent {
},
self.options.stash_untracked,
self.options.keep_index,
) {
);
match result {
Ok(_) => {
self.input.clear();
self.hide();
@ -123,6 +125,7 @@ impl Component for StashMsgComponent {
impl StashMsgComponent {
///
pub fn new(
repo: RepoPathRef,
queue: Queue,
theme: SharedTheme,
key_config: SharedKeyConfig,
@ -138,6 +141,7 @@ impl StashMsgComponent {
true,
),
key_config,
repo,
}
}

View File

@ -15,8 +15,8 @@ use crate::{
use anyhow::Result;
use asyncgit::{
asyncjob::AsyncSingleJob,
sync::{self, TreeFile},
ProgressPercent, CWD,
sync::{self, RepoPathRef, TreeFile},
ProgressPercent,
};
use crossbeam_channel::Sender;
use crossterm::event::Event;
@ -32,6 +32,7 @@ use tui::{
};
pub struct SyntaxTextComponent {
repo: RepoPathRef,
current_file: Option<(String, Either<ui::SyntaxText, String>)>,
async_highlighting: AsyncSingleJob<AsyncSyntaxJob>,
syntax_progress: Option<ProgressPercent>,
@ -44,6 +45,7 @@ pub struct SyntaxTextComponent {
impl SyntaxTextComponent {
///
pub fn new(
repo: RepoPathRef,
sender: &Sender<AsyncAppNotification>,
key_config: SharedKeyConfig,
theme: SharedTheme,
@ -56,6 +58,7 @@ impl SyntaxTextComponent {
focused: false,
key_config,
theme,
repo,
}
}
@ -110,7 +113,7 @@ impl SyntaxTextComponent {
if !already_loaded {
//TODO: fetch file content async aswell
match sync::tree_file_content(CWD, item) {
match sync::tree_file_content(&self.repo.borrow(), item) {
Ok(content) => {
let content = tabs_to_spaces(content);
self.syntax_progress =

View File

@ -10,14 +10,12 @@ use crate::{
ui::style::SharedTheme,
};
use anyhow::Result;
use asyncgit::{
sync::{self, CommitId},
CWD,
};
use asyncgit::sync::{self, CommitId, RepoPathRef};
use crossterm::event::Event;
use tui::{backend::Backend, layout::Rect, Frame};
pub struct TagCommitComponent {
repo: RepoPathRef,
input: TextInputComponent,
commit_id: Option<CommitId>,
queue: Queue,
@ -92,6 +90,7 @@ impl Component for TagCommitComponent {
impl TagCommitComponent {
///
pub fn new(
repo: RepoPathRef,
queue: Queue,
theme: SharedTheme,
key_config: SharedKeyConfig,
@ -107,6 +106,7 @@ impl TagCommitComponent {
),
commit_id: None,
key_config,
repo,
}
}
@ -121,7 +121,12 @@ impl TagCommitComponent {
///
pub fn tag(&mut self) {
if let Some(commit_id) = self.commit_id {
match sync::tag(CWD, &commit_id, self.input.get_text()) {
let result = sync::tag(
&self.repo.borrow(),
&commit_id,
self.input.get_text(),
);
match result {
Ok(_) => {
self.input.clear();
self.hide();

View File

@ -18,8 +18,8 @@ use asyncgit::{
extract_username_password, need_username_password,
BasicAuthCredential,
},
sync::{get_tags_with_metadata, TagWithMetadata},
AsyncGitNotification, CWD,
sync::{get_tags_with_metadata, RepoPathRef, TagWithMetadata},
AsyncGitNotification,
};
use crossbeam_channel::Sender;
use crossterm::event::Event;
@ -38,6 +38,7 @@ use ui::style::SharedTheme;
///
pub struct TagListComponent {
repo: RepoPathRef,
theme: SharedTheme,
queue: Queue,
tags: Option<Vec<TagWithMetadata>>,
@ -249,6 +250,7 @@ impl Component for TagListComponent {
impl TagListComponent {
pub fn new(
repo: RepoPathRef,
queue: &Queue,
sender: &Sender<AsyncGitNotification>,
theme: SharedTheme,
@ -265,6 +267,7 @@ impl TagListComponent {
missing_remote_tags: None,
async_remote_tags: AsyncSingleJob::new(sender.clone()),
key_config,
repo,
}
}
@ -273,17 +276,19 @@ impl TagListComponent {
self.table_state.get_mut().select(Some(0));
self.show()?;
let basic_credential = if need_username_password()? {
let credential = extract_username_password()?;
let basic_credential =
if need_username_password(&self.repo.borrow())? {
let credential =
extract_username_password(&self.repo.borrow())?;
if credential.is_complete() {
Some(credential)
if credential.is_complete() {
Some(credential)
} else {
None
}
} else {
None
}
} else {
None
};
};
self.basic_credential = basic_credential;
@ -320,7 +325,7 @@ impl TagListComponent {
/// fetch list of tags
pub fn update_tags(&mut self) -> Result<()> {
let tags = get_tags_with_metadata(CWD)?;
let tags = get_tags_with_metadata(&self.repo.borrow())?;
self.tags = Some(tags);
@ -329,6 +334,7 @@ impl TagListComponent {
pub fn update_missing_remote_tags(&mut self) {
self.async_remote_tags.spawn(AsyncRemoteTagsJob::new(
self.repo.borrow().clone(),
self.basic_credential.clone(),
));
}

View File

@ -38,7 +38,7 @@ mod version;
use crate::{app::App, args::process_cmdline};
use anyhow::{bail, Result};
use asyncgit::AsyncGitNotification;
use asyncgit::{sync::RepoPath, AsyncGitNotification};
use backtrace::Backtrace;
use crossbeam_channel::{tick, unbounded, Receiver, Select};
use crossterm::{
@ -55,6 +55,7 @@ use scopeguard::defer;
use scopetime::scope_time;
use spinner::Spinner;
use std::{
cell::RefCell,
io::{self, Write},
panic, process,
time::{Duration, Instant},
@ -104,7 +105,7 @@ fn main() -> Result<()> {
asyncgit::register_tracing_logging();
if !valid_path()? {
if !valid_path(&cliargs.repo_path) {
eprintln!("invalid path\nplease run gitui inside of a non-bare git repository");
return Ok(());
}
@ -134,8 +135,14 @@ fn main() -> Result<()> {
let ticker = tick(TICK_INTERVAL);
let spinner_ticker = tick(SPINNER_INTERVAL);
let mut app =
App::new(&tx_git, &tx_app, input, theme, key_config);
let mut app = App::new(
RefCell::new(cliargs.repo_path),
&tx_git,
&tx_app,
input,
theme,
key_config,
);
let mut spinner = Spinner::default();
let mut first_update = true;
@ -238,9 +245,8 @@ fn draw<B: Backend>(
Ok(())
}
fn valid_path() -> Result<bool> {
Ok(asyncgit::sync::is_repo(asyncgit::CWD)
&& !asyncgit::sync::is_bare_repo(asyncgit::CWD)?)
fn valid_path(repo_path: &RepoPath) -> bool {
asyncgit::sync::is_repo(repo_path)
}
fn select_event(

View File

@ -17,10 +17,11 @@ use crate::{
AsyncAppNotification, AsyncNotification,
};
use anyhow::Result;
use asyncgit::{sync, CWD};
use asyncgit::sync::{self, RepoPathRef};
use crossbeam_channel::Sender;
pub struct FilesTab {
repo: RepoPathRef,
visible: bool,
theme: SharedTheme,
key_config: SharedKeyConfig,
@ -30,6 +31,7 @@ pub struct FilesTab {
impl FilesTab {
///
pub fn new(
repo: RepoPathRef,
sender: &Sender<AsyncAppNotification>,
queue: &Queue,
theme: SharedTheme,
@ -38,6 +40,7 @@ impl FilesTab {
Self {
visible: false,
files: RevisionFilesComponent::new(
repo.clone(),
queue,
sender,
theme.clone(),
@ -45,13 +48,14 @@ impl FilesTab {
),
theme,
key_config,
repo,
}
}
///
pub fn update(&mut self) -> Result<()> {
if self.is_visible() {
if let Ok(head) = sync::get_head(CWD) {
if let Ok(head) = sync::get_head(&self.repo.borrow()) {
self.files.set_commit(head)?;
}
}

View File

@ -12,9 +12,9 @@ use crate::{
use anyhow::Result;
use asyncgit::{
cached,
sync::{self, CommitId},
sync::{self, CommitId, RepoPathRef},
AsyncGitNotification, AsyncLog, AsyncTags, CommitFilesParams,
FetchStatus, CWD,
FetchStatus,
};
use crossbeam_channel::Sender;
use crossterm::event::Event;
@ -30,6 +30,7 @@ const SLICE_SIZE: usize = 1200;
///
pub struct Revlog {
repo: RepoPathRef,
commit_details: CommitDetailsComponent,
list: CommitList,
git_log: AsyncLog,
@ -43,14 +44,17 @@ pub struct Revlog {
impl Revlog {
///
pub fn new(
repo: &RepoPathRef,
queue: &Queue,
sender: &Sender<AsyncGitNotification>,
theme: SharedTheme,
key_config: SharedKeyConfig,
) -> Self {
Self {
repo: repo.clone(),
queue: queue.clone(),
commit_details: CommitDetailsComponent::new(
repo,
queue,
sender,
theme.clone(),
@ -61,10 +65,14 @@ impl Revlog {
theme,
key_config.clone(),
),
git_log: AsyncLog::new(sender, None),
git_tags: AsyncTags::new(sender),
git_log: AsyncLog::new(
repo.borrow().clone(),
sender,
None,
),
git_tags: AsyncTags::new(repo.borrow().clone(), sender),
visible: false,
branch_name: cached::BranchName::new(CWD),
branch_name: cached::BranchName::new(repo.clone()),
key_config,
}
}
@ -139,7 +147,7 @@ impl Revlog {
self.list.selection().saturating_sub(SLICE_SIZE / 2);
let commits = sync::get_commits_info(
CWD,
&self.repo.borrow(),
&self.git_log.get_slice(want_min, SLICE_SIZE)?,
self.list.current_size().0.into(),
);

View File

@ -12,8 +12,8 @@ use crate::{
};
use anyhow::Result;
use asyncgit::{
sync::{self, status::StatusType},
AsyncGitNotification, AsyncStatus, StatusParams, CWD,
sync::{self, status::StatusType, RepoPathRef},
AsyncGitNotification, AsyncStatus, StatusParams,
};
use crossbeam_channel::Sender;
use crossterm::event::Event;
@ -31,6 +31,7 @@ pub struct StashingOptions {
}
pub struct Stashing {
repo: RepoPathRef,
index: StatusTreeComponent,
visible: bool,
options: StashingOptions,
@ -45,12 +46,14 @@ impl Stashing {
///
pub fn new(
repo: &RepoPathRef,
sender: &Sender<AsyncGitNotification>,
queue: &Queue,
theme: SharedTheme,
key_config: SharedKeyConfig,
) -> Self {
Self {
repo: repo.clone(),
index: StatusTreeComponent::new(
&strings::stashing_files_title(&key_config),
true,
@ -64,7 +67,10 @@ impl Stashing {
stash_untracked: true,
},
theme,
git_status: AsyncStatus::new(sender.clone()),
git_status: AsyncStatus::new(
repo.borrow().clone(),
sender.clone(),
),
queue: queue.clone(),
key_config,
}
@ -258,7 +264,7 @@ impl Component for Stashing {
fn show(&mut self) -> Result<()> {
let config_untracked_files =
sync::untracked_files_config(CWD)?;
sync::untracked_files_config(&self.repo.borrow())?;
self.options.stash_untracked =
!config_untracked_files.include_none();

View File

@ -9,13 +9,11 @@ use crate::{
ui::style::SharedTheme,
};
use anyhow::Result;
use asyncgit::{
sync::{self, CommitId},
CWD,
};
use asyncgit::sync::{self, CommitId, RepoPath, RepoPathRef};
use crossterm::event::Event;
pub struct StashList {
repo: RepoPathRef,
list: CommitList,
visible: bool,
queue: Queue,
@ -25,6 +23,7 @@ pub struct StashList {
impl StashList {
///
pub fn new(
repo: RepoPathRef,
queue: &Queue,
theme: SharedTheme,
key_config: SharedKeyConfig,
@ -38,15 +37,19 @@ impl StashList {
),
queue: queue.clone(),
key_config,
repo,
}
}
///
pub fn update(&mut self) -> Result<()> {
if self.is_visible() {
let stashes = sync::get_stashes(CWD)?;
let commits =
sync::get_commits_info(CWD, stashes.as_slice(), 100)?;
let stashes = sync::get_stashes(&self.repo.borrow())?;
let commits = sync::get_commits_info(
&self.repo.borrow(),
stashes.as_slice(),
100,
)?;
self.list.set_count_total(commits.len());
self.list.items().set_items(0, commits);
@ -57,7 +60,8 @@ impl StashList {
fn apply_stash(&mut self) {
if let Some(e) = self.list.selected_entry() {
match sync::stash_apply(CWD, e.id, false) {
match sync::stash_apply(&self.repo.borrow(), e.id, false)
{
Ok(_) => {
self.queue.push(InternalEvent::TabSwitch);
}
@ -97,26 +101,29 @@ impl StashList {
}
/// Called when a pending stash action has been confirmed
pub fn action_confirmed(action: &Action) -> Result<()> {
pub fn action_confirmed(
repo: &RepoPath,
action: &Action,
) -> Result<()> {
match action {
Action::StashDrop(ids) => Self::drop(ids)?,
Action::StashPop(id) => Self::pop(*id)?,
Action::StashDrop(ids) => Self::drop(repo, ids)?,
Action::StashPop(id) => Self::pop(repo, *id)?,
_ => (),
};
Ok(())
}
fn drop(ids: &[CommitId]) -> Result<()> {
fn drop(repo: &RepoPath, ids: &[CommitId]) -> Result<()> {
for id in ids {
sync::stash_drop(CWD, *id)?;
sync::stash_drop(repo, *id)?;
}
Ok(())
}
fn pop(id: CommitId) -> Result<()> {
sync::stash_pop(CWD, id)?;
fn pop(repo: &RepoPath, id: CommitId) -> Result<()> {
sync::stash_pop(repo, id)?;
Ok(())
}
}

View File

@ -14,10 +14,12 @@ use crate::{
use anyhow::Result;
use asyncgit::{
cached,
sync::{self, status::StatusType, RepoState},
sync::{
self, status::StatusType, RepoPath, RepoPathRef, RepoState,
},
sync::{BranchCompare, CommitId},
AsyncDiff, AsyncGitNotification, AsyncStatus, DiffParams,
DiffType, StatusParams, CWD,
DiffType, StatusParams,
};
use crossbeam_channel::Sender;
use crossterm::event::Event;
@ -56,6 +58,7 @@ enum DiffTarget {
}
pub struct Status {
repo: RepoPathRef,
visible: bool,
focus: Focus,
diff_target: DiffTarget,
@ -79,7 +82,7 @@ impl DrawableComponent for Status {
f: &mut tui::Frame<B>,
rect: tui::layout::Rect,
) -> Result<()> {
let repo_unclean = Self::repo_state_unclean();
let repo_unclean = self.repo_state_unclean();
let rects = if repo_unclean {
Layout::default()
.direction(Direction::Vertical)
@ -134,7 +137,7 @@ impl DrawableComponent for Status {
self.draw_branch_state(f, &left_chunks);
if repo_unclean {
Self::draw_repo_state(f, rects[1]);
self.draw_repo_state(f, rects[1]);
}
Ok(())
@ -146,18 +149,21 @@ impl Status {
///
pub fn new(
repo: RepoPathRef,
queue: &Queue,
sender: &Sender<AsyncGitNotification>,
theme: SharedTheme,
key_config: SharedKeyConfig,
options: SharedOptions,
) -> Self {
let repo_clone = repo.borrow().clone();
Self {
queue: queue.clone(),
visible: true,
focus: Focus::WorkDir,
diff_target: DiffTarget::WorkingDir,
index_wd: ChangesComponent::new(
repo.clone(),
&strings::title_status(&key_config),
true,
true,
@ -167,6 +173,7 @@ impl Status {
options.clone(),
),
index: ChangesComponent::new(
repo.clone(),
&strings::title_index(&key_config),
false,
false,
@ -176,19 +183,27 @@ impl Status {
options.clone(),
),
diff: DiffComponent::new(
repo.clone(),
queue.clone(),
theme,
key_config.clone(),
false,
),
git_diff: AsyncDiff::new(sender),
git_status_workdir: AsyncStatus::new(sender.clone()),
git_status_stage: AsyncStatus::new(sender.clone()),
git_diff: AsyncDiff::new(repo_clone.clone(), sender),
git_status_workdir: AsyncStatus::new(
repo_clone.clone(),
sender.clone(),
),
git_status_stage: AsyncStatus::new(
repo_clone,
sender.clone(),
),
git_action_executed: false,
git_branch_state: None,
git_branch_name: cached::BranchName::new(CWD),
git_branch_name: cached::BranchName::new(repo.clone()),
key_config,
options,
repo,
}
}
@ -232,11 +247,11 @@ impl Status {
}
}
fn repo_state_text(state: &RepoState) -> String {
fn repo_state_text(repo: &RepoPath, state: &RepoState) -> String {
match state {
RepoState::Merge => {
let ids =
sync::mergehead_ids(CWD).unwrap_or_default();
sync::mergehead_ids(repo).unwrap_or_default();
format!(
"Commits: {}",
@ -246,7 +261,7 @@ impl Status {
)
}
RepoState::Rebase => {
if let Ok(p) = sync::rebase_progress(CWD) {
if let Ok(p) = sync::rebase_progress(repo) {
format!(
"Step: {}/{} Current Commit: {}",
p.current + 1,
@ -265,12 +280,16 @@ impl Status {
}
fn draw_repo_state<B: tui::backend::Backend>(
&self,
f: &mut tui::Frame<B>,
r: tui::layout::Rect,
) {
if let Ok(state) = sync::repo_state(CWD) {
if let Ok(state) = sync::repo_state(&self.repo.borrow()) {
if state != RepoState::Clean {
let txt = Self::repo_state_text(&state);
let txt = Self::repo_state_text(
&self.repo.borrow(),
&state,
);
let w = Paragraph::new(txt)
.block(
@ -290,8 +309,8 @@ impl Status {
}
}
fn repo_state_unclean() -> bool {
if let Ok(state) = sync::repo_state(CWD) {
fn repo_state_unclean(&self) -> bool {
if let Ok(state) = sync::repo_state(&self.repo.borrow()) {
if state != RepoState::Clean {
return true;
}
@ -497,7 +516,10 @@ impl Status {
/// called after confirmation
pub fn reset(&mut self, item: &ResetItem) -> bool {
if let Err(e) = sync::reset_workdir(CWD, item.path.as_str()) {
if let Err(e) = sync::reset_workdir(
&self.repo.borrow(),
item.path.as_str(),
) {
self.queue.push(InternalEvent::ShowErrorMsg(format!(
"reset failed:\n{}",
e
@ -542,15 +564,18 @@ impl Status {
try_or_popup!(
self,
"undo commit failed:",
sync::utils::undo_last_commit(CWD)
sync::utils::undo_last_commit(&self.repo.borrow())
);
}
fn branch_compare(&mut self) {
self.git_branch_state =
self.git_branch_name.last().and_then(|branch| {
sync::branch_compare_upstream(CWD, branch.as_str())
.ok()
sync::branch_compare_upstream(
&self.repo.borrow(),
branch.as_str(),
)
.ok()
});
}
@ -560,25 +585,31 @@ impl Status {
.map_or(true, |state| state.ahead > 0)
}
fn can_abort_merge() -> bool {
sync::repo_state(CWD).unwrap_or(RepoState::Clean)
fn can_abort_merge(&self) -> bool {
sync::repo_state(&self.repo.borrow())
.unwrap_or(RepoState::Clean)
== RepoState::Merge
}
fn pending_rebase() -> bool {
sync::repo_state(CWD).unwrap_or(RepoState::Clean)
fn pending_rebase(&self) -> bool {
sync::repo_state(&self.repo.borrow())
.unwrap_or(RepoState::Clean)
== RepoState::Rebase
}
pub fn abort_merge(&self) {
try_or_popup!(self, "abort merge", sync::abort_merge(CWD));
try_or_popup!(
self,
"abort merge",
sync::abort_merge(&self.repo.borrow())
);
}
pub fn abort_rebase(&self) {
try_or_popup!(
self,
"abort rebase",
sync::abort_pending_rebase(CWD)
sync::abort_pending_rebase(&self.repo.borrow())
);
}
@ -586,7 +617,7 @@ impl Status {
try_or_popup!(
self,
"continue rebase",
sync::continue_pending_rebase(CWD)
sync::continue_pending_rebase(&self.repo.borrow())
);
}
@ -637,7 +668,7 @@ impl Status {
fn can_commit(&self) -> bool {
self.index.focused()
&& !self.index.is_empty()
&& !Self::pending_rebase()
&& !self.pending_rebase()
}
}
@ -694,25 +725,25 @@ impl Component for Status {
out.push(CommandInfo::new(
strings::commands::undo_commit(&self.key_config),
true,
(!Self::pending_rebase() && !focus_on_diff)
(!self.pending_rebase() && !focus_on_diff)
|| force_all,
));
out.push(CommandInfo::new(
strings::commands::abort_merge(&self.key_config),
true,
Self::can_abort_merge() || force_all,
self.can_abort_merge() || force_all,
));
out.push(CommandInfo::new(
strings::commands::continue_rebase(&self.key_config),
true,
Self::pending_rebase() || force_all,
self.pending_rebase() || force_all,
));
out.push(CommandInfo::new(
strings::commands::abort_rebase(&self.key_config),
true,
Self::pending_rebase() || force_all,
self.pending_rebase() || force_all,
));
}
@ -819,7 +850,7 @@ impl Component for Status {
));
Ok(EventState::Consumed)
} else if k == self.key_config.keys.abort_merge
&& Self::can_abort_merge()
&& self.can_abort_merge()
{
self.queue.push(InternalEvent::ConfirmAction(
Action::AbortMerge,
@ -827,7 +858,7 @@ impl Component for Status {
Ok(EventState::Consumed)
} else if k == self.key_config.keys.abort_merge
&& Self::pending_rebase()
&& self.pending_rebase()
{
self.queue.push(InternalEvent::ConfirmAction(
Action::AbortRebase,
@ -835,7 +866,7 @@ impl Component for Status {
Ok(EventState::Consumed)
} else if k == self.key_config.keys.rebase_branch
&& Self::pending_rebase()
&& self.pending_rebase()
{
self.continue_rebase();
self.queue.push(InternalEvent::Update(