Skip to content

branch merge from upstream #384

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Feb 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions asyncgit/src/sync/branch/merge.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
//! merging from upstream

use super::BranchType;
use crate::{
error::{Error, Result},
sync::utils,
};
use scopetime::scope_time;

///
pub fn branch_merge_upstream_fastforward(
repo_path: &str,
branch: &str,
) -> Result<()> {
scope_time!("branch_merge_upstream");

let repo = utils::repo(repo_path)?;

let branch = repo.find_branch(branch, BranchType::Local)?;
let upstream = branch.upstream()?;

let upstream_commit =
upstream.into_reference().peel_to_commit()?;

let annotated =
repo.find_annotated_commit(upstream_commit.id())?;

let (analysis, _) = repo.merge_analysis(&[&annotated])?;

if !analysis.is_fast_forward() {
return Err(Error::Generic(
"fast forward merge not possible".into(),
));
}

if analysis.is_unborn() {
return Err(Error::Generic("head is unborn".into()));
}

repo.checkout_tree(upstream_commit.as_object(), None)?;

repo.head()?.set_target(annotated.id(), "")?;

Ok(())
}

#[cfg(test)]
mod test {
use super::*;
use crate::sync::{
commit, fetch_origin,
remotes::push::push,
stage_add_file,
tests::{
debug_cmd_print, get_commit_ids, repo_clone,
repo_init_bare,
},
CommitId,
};
use git2::Repository;
use std::{fs::File, io::Write, path::Path};

// write, stage and commit a file
fn write_commit_file(
repo: &Repository,
file: &str,
content: &str,
commit_name: &str,
) -> CommitId {
File::create(
repo.workdir().unwrap().join(file).to_str().unwrap(),
)
.unwrap()
.write_all(content.as_bytes())
.unwrap();

stage_add_file(
repo.workdir().unwrap().to_str().unwrap(),
Path::new(file),
)
.unwrap();

commit(repo.workdir().unwrap().to_str().unwrap(), commit_name)
.unwrap()
}

#[test]
fn test_merge() {
let (r1_dir, _repo) = repo_init_bare().unwrap();

let (clone1_dir, clone1) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();

let (clone2_dir, clone2) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();

// clone1

let commit1 =
write_commit_file(&clone1, "test.txt", "test", "commit1");

push(
clone1_dir.path().to_str().unwrap(),
"origin",
"master",
false,
None,
None,
)
.unwrap();

// clone2
debug_cmd_print(
clone2_dir.path().to_str().unwrap(),
"git pull --ff",
);

let commit2 = write_commit_file(
&clone2,
"test2.txt",
"test",
"commit2",
);

push(
clone2_dir.path().to_str().unwrap(),
"origin",
"master",
false,
None,
None,
)
.unwrap();

// clone1 again

let bytes = fetch_origin(
clone1_dir.path().to_str().unwrap(),
"master",
)
.unwrap();
assert!(bytes > 0);

let bytes = fetch_origin(
clone1_dir.path().to_str().unwrap(),
"master",
)
.unwrap();
assert_eq!(bytes, 0);

branch_merge_upstream_fastforward(
clone1_dir.path().to_str().unwrap(),
"master",
)
.unwrap();

let commits = get_commit_ids(&clone1, 10);
assert_eq!(commits.len(), 2);
assert_eq!(commits[1], commit1);
assert_eq!(commits[0], commit2);
}
}
67 changes: 4 additions & 63 deletions asyncgit/src/sync/branch.rs → asyncgit/src/sync/branch/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
//!
//! branch functions

pub mod merge;
pub mod rename;

use super::{
remotes::get_default_remote_in_repo, utils::bytes2string,
Expand Down Expand Up @@ -182,22 +185,6 @@ pub fn delete_branch(
Ok(())
}

/// Rename the branch reference
pub fn rename_branch(
repo_path: &str,
branch_ref: &str,
new_name: &str,
) -> Result<()> {
scope_time!("delete_branch");

let repo = utils::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)?;

Ok(())
}

/// 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<()> {
scope_time!("create_branch");
Expand Down Expand Up @@ -404,49 +391,3 @@ mod test_delete_branch {
);
}
}

#[cfg(test)]
mod test_rename_branch {
use super::*;
use crate::sync::tests::repo_init;

#[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();

create_branch(repo_path, "branch1").unwrap();

checkout_branch(repo_path, "refs/heads/branch1").unwrap();

assert_eq!(
repo.branches(None)
.unwrap()
.nth(0)
.unwrap()
.unwrap()
.0
.name()
.unwrap()
.unwrap(),
"branch1"
);

rename_branch(repo_path, "refs/heads/branch1", "AnotherName")
.unwrap();

assert_eq!(
repo.branches(None)
.unwrap()
.nth(0)
.unwrap()
.unwrap()
.0
.name()
.unwrap()
.unwrap(),
"AnotherName"
);
}
}
67 changes: 67 additions & 0 deletions asyncgit/src/sync/branch/rename.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//! renaming of branches

use crate::{error::Result, sync::utils};
use scopetime::scope_time;

/// Rename the branch reference
pub fn rename_branch(
repo_path: &str,
branch_ref: &str,
new_name: &str,
) -> Result<()> {
scope_time!("delete_branch");

let repo = utils::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)?;

Ok(())
}

#[cfg(test)]
mod test {
use super::super::*;
use super::rename_branch;
use crate::sync::tests::repo_init;

#[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();

create_branch(repo_path, "branch1").unwrap();

checkout_branch(repo_path, "refs/heads/branch1").unwrap();

assert_eq!(
repo.branches(None)
.unwrap()
.nth(0)
.unwrap()
.unwrap()
.0
.name()
.unwrap()
.unwrap(),
"branch1"
);

rename_branch(repo_path, "refs/heads/branch1", "AnotherName")
.unwrap();

assert_eq!(
repo.branches(None)
.unwrap()
.nth(0)
.unwrap()
.unwrap()
.0
.name()
.unwrap()
.unwrap(),
"AnotherName"
);
}
}
40 changes: 37 additions & 3 deletions asyncgit/src/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@ mod logwalker;
pub mod remotes;
mod reset;
mod stash;
mod state;
pub mod status;
mod tags;
pub mod utils;

pub use branch::{
branch_compare_upstream, checkout_branch, create_branch,
delete_branch, get_branches_info, rename_branch, BranchCompare,
BranchInfo,
delete_branch, get_branches_info,
merge::branch_merge_upstream_fastforward, rename::rename_branch,
BranchCompare, BranchInfo,
};
pub use commit::{amend, commit, tag};
pub use commit_details::{
Expand All @@ -42,6 +44,7 @@ pub use logwalker::LogWalker;
pub use remotes::{fetch_origin, get_default_remote, get_remotes};
pub use reset::{reset_stage, reset_workdir};
pub use stash::{get_stashes, stash_apply, stash_drop, stash_save};
pub use state::{repo_state, RepoState};
pub use tags::{get_tags, CommitTags, Tags};
pub use utils::{
get_head, get_head_tuple, is_bare_repo, is_repo, stage_add_all,
Expand All @@ -50,7 +53,10 @@ pub use utils::{

#[cfg(test)]
mod tests {
use super::status::{get_status, StatusType};
use super::{
status::{get_status, StatusType},
CommitId, LogWalker,
};
use crate::error::Result;
use git2::Repository;
use std::process::Command;
Expand Down Expand Up @@ -120,6 +126,23 @@ mod tests {
Ok((td, repo))
}

///
pub fn repo_clone(p: &str) -> Result<(TempDir, Repository)> {
sandbox_config_files();

let td = TempDir::new()?;

let td_path = td.path().as_os_str().to_str().unwrap();

let repo = Repository::clone(p, td_path).unwrap();

let mut config = repo.config()?;
config.set_str("user.name", "name")?;
config.set_str("user.email", "email")?;

Ok((td, repo))
}

/// Same as repo_init, but the repo is a bare repo (--bare)
pub fn repo_init_bare() -> Result<(TempDir, Repository)> {
let tmp_repo_dir = TempDir::new()?;
Expand All @@ -145,6 +168,17 @@ mod tests {
eprintln!("\n----\n{}", cmd);
}

/// helper to fetch commmit details using log walker
pub fn get_commit_ids(
r: &Repository,
max_count: usize,
) -> Vec<CommitId> {
let mut commit_ids = Vec::<CommitId>::new();
LogWalker::new(r).read(&mut commit_ids, max_count).unwrap();

commit_ids
}

fn debug_cmd(path: &str, cmd: &str) -> String {
let output = if cfg!(target_os = "windows") {
Command::new("cmd")
Expand Down
Loading