Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 90 additions & 13 deletions src/events/git_push.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
use crate::bgit_error::{BGitError, BGitErrorWorkflowType, NO_RULE, NO_STEP};
use crate::rules::Rule;
use git2::{Cred, CredentialType, Repository};
use log::debug;

pub struct GitPush {
pub pre_check_rules: Vec<Box<dyn Rule + Send + Sync>>,
pub force: bool,
pub force_with_lease: bool,
pub set_upstream: bool,
}

Expand All @@ -18,7 +19,7 @@
{
GitPush {
pre_check_rules: vec![],
force: false,
force_with_lease: false,

Check warning on line 22 in src/events/git_push.rs

View check run for this annotation

Codecov / codecov/patch

src/events/git_push.rs#L22

Added line #L22 was not covered by tests
set_upstream: false,
}
}
Expand Down Expand Up @@ -89,21 +90,24 @@
// Prepare push options with authentication
let mut push_options = Self::create_push_options();

// Check if we need to force push and validate state
if !self.force {
// Validation
if self.force_with_lease {
self.validate_force_with_lease(&repo, &head, branch_name)?;

Check warning on line 95 in src/events/git_push.rs

View check run for this annotation

Codecov / codecov/patch

src/events/git_push.rs#L93-L95

Added lines #L93 - L95 were not covered by tests
} else {
self.validate_push_safety(&repo, &head, branch_name)?;
}

// Determine refspec
let refspec = if self.set_upstream {
format!("refs/heads/{}:refs/heads/{}", branch_name, branch_name)
} else {
format!("refs/heads/{}", branch_name)
};

// Perform the push
let refspecs = if self.force {
vec![format!("+{}", refspec)]
// Perform the push with force-with-lease if enabled
let refspecs = if self.force_with_lease {
let force_lease_refspec =
self.build_force_with_lease_refspec(&repo, branch_name, &refspec)?;
vec![force_lease_refspec]

Check warning on line 110 in src/events/git_push.rs

View check run for this annotation

Codecov / codecov/patch

src/events/git_push.rs#L107-L110

Added lines #L107 - L110 were not covered by tests
} else {
vec![refspec]
};
Expand Down Expand Up @@ -131,23 +135,96 @@
}

impl GitPush {
pub fn set_force(&mut self, force: bool) -> &mut Self {
self.force = force;
pub fn with_force_with_lease(&mut self, force_with_lease: bool) -> &mut Self {
self.force_with_lease = force_with_lease;

Check warning on line 139 in src/events/git_push.rs

View check run for this annotation

Codecov / codecov/patch

src/events/git_push.rs#L138-L139

Added lines #L138 - L139 were not covered by tests
self
}

pub fn set_upstream_flag(&mut self, set_upstream: bool) -> &mut Self {
pub fn with_upstream_flag(&mut self, set_upstream: bool) -> &mut Self {

Check warning on line 143 in src/events/git_push.rs

View check run for this annotation

Codecov / codecov/patch

src/events/git_push.rs#L143

Added line #L143 was not covered by tests
self.set_upstream = set_upstream;
self
}

/// Validate force-with-lease conditions
fn validate_force_with_lease(
&self,
repo: &Repository,
head: &git2::Reference,
branch_name: &str,
) -> Result<(), Box<BGitError>> {
let local_commit = head.peel_to_commit().map_err(|e| {
Box::new(BGitError::new(
"BGitError",
&format!("Failed to get local commit: {}", e),
BGitErrorWorkflowType::AtomicEvent,
NO_STEP,
self.get_name(),
NO_RULE,
))
})?;

Check warning on line 164 in src/events/git_push.rs

View check run for this annotation

Codecov / codecov/patch

src/events/git_push.rs#L149-L164

Added lines #L149 - L164 were not covered by tests

// Check if remote branch exists and validate
if let Ok(remote_ref) = repo.find_reference(&format!("refs/remotes/origin/{}", branch_name))

Check warning on line 167 in src/events/git_push.rs

View check run for this annotation

Codecov / codecov/patch

src/events/git_push.rs#L167

Added line #L167 was not covered by tests
{
let remote_commit = remote_ref.peel_to_commit().map_err(|e| {
Box::new(BGitError::new(
"BGitError",
&format!("Failed to get remote commit: {}", e),
BGitErrorWorkflowType::AtomicEvent,
NO_STEP,
self.get_name(),
NO_RULE,
))
})?;

Check warning on line 178 in src/events/git_push.rs

View check run for this annotation

Codecov / codecov/patch

src/events/git_push.rs#L169-L178

Added lines #L169 - L178 were not covered by tests

if local_commit.id() == remote_commit.id() {
debug!("Local branch is up to date with remote, no force-with-lease needed");
return Ok(());
}
}

Check warning on line 184 in src/events/git_push.rs

View check run for this annotation

Codecov / codecov/patch

src/events/git_push.rs#L180-L184

Added lines #L180 - L184 were not covered by tests

Ok(())
}

Check warning on line 187 in src/events/git_push.rs

View check run for this annotation

Codecov / codecov/patch

src/events/git_push.rs#L186-L187

Added lines #L186 - L187 were not covered by tests

fn build_force_with_lease_refspec(
&self,
repo: &Repository,
branch_name: &str,
base_refspec: &str,
) -> Result<String, Box<BGitError>> {

Check warning on line 194 in src/events/git_push.rs

View check run for this annotation

Codecov / codecov/patch

src/events/git_push.rs#L189-L194

Added lines #L189 - L194 were not covered by tests
// Force-with-lease using the current remote tracking branch as the expected value
if let Ok(remote_ref) = repo.find_reference(&format!("refs/remotes/origin/{}", branch_name))

Check warning on line 196 in src/events/git_push.rs

View check run for this annotation

Codecov / codecov/patch

src/events/git_push.rs#L196

Added line #L196 was not covered by tests
{
let remote_oid = remote_ref.target().ok_or_else(|| {
Box::new(BGitError::new(
"BGitError",
"Failed to get remote reference target for force-with-lease",
BGitErrorWorkflowType::AtomicEvent,
NO_STEP,
self.get_name(),
NO_RULE,
))
})?;

Check warning on line 207 in src/events/git_push.rs

View check run for this annotation

Codecov / codecov/patch

src/events/git_push.rs#L198-L207

Added lines #L198 - L207 were not covered by tests

Ok(format!("+{}^{{{}}}", base_refspec, remote_oid))

Check warning on line 209 in src/events/git_push.rs

View check run for this annotation

Codecov / codecov/patch

src/events/git_push.rs#L209

Added line #L209 was not covered by tests
} else {
Err(Box::new(BGitError::new(
"BGitError",
"Cannot perform force-with-lease: no remote tracking branch found",
BGitErrorWorkflowType::AtomicEvent,
NO_STEP,
self.get_name(),
NO_RULE,
)))

Check warning on line 218 in src/events/git_push.rs

View check run for this annotation

Codecov / codecov/patch

src/events/git_push.rs#L211-L218

Added lines #L211 - L218 were not covered by tests
}
}

Check warning on line 220 in src/events/git_push.rs

View check run for this annotation

Codecov / codecov/patch

src/events/git_push.rs#L220

Added line #L220 was not covered by tests

fn validate_push_safety(
&self,
repo: &Repository,
head: &git2::Reference,
branch_name: &str,
) -> Result<(), Box<BGitError>> {
// Check if we're up to date with remote
if let Ok(remote_ref) = repo.find_reference(&format!("refs/remotes/origin/{}", branch_name))
{
let local_commit = head.peel_to_commit().map_err(|e| {
Expand Down Expand Up @@ -194,7 +271,7 @@
if merge_base == local_commit.id() && local_commit.id() != remote_commit.id() {
return Err(Box::new(BGitError::new(
"BGitError",
"Local branch is behind remote. Pull changes first or use --force",
"Local branch is behind remote. Pull changes first",

Check warning on line 274 in src/events/git_push.rs

View check run for this annotation

Codecov / codecov/patch

src/events/git_push.rs#L274

Added line #L274 was not covered by tests
Comment thread
rootCircle marked this conversation as resolved.
BGitErrorWorkflowType::AtomicEvent,
NO_STEP,
self.get_name(),
Expand Down
4 changes: 3 additions & 1 deletion src/workflows/default/action/ta09_pull_push.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@
// Pull successful, now attempt push
let mut git_push = GitPush::new();
// Configure push options - you can customize these as needed
git_push.set_force(false).set_upstream_flag(false);
git_push
.with_force_with_lease(false)
.with_upstream_flag(false);

Check warning on line 45 in src/workflows/default/action/ta09_pull_push.rs

View check run for this annotation

Codecov / codecov/patch

src/workflows/default/action/ta09_pull_push.rs#L43-L45

Added lines #L43 - L45 were not covered by tests

match git_push.execute() {
Ok(_) => {
Expand Down