mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-11-30 11:32:04 +03:00
let integration commit also respect author and committer times.
This commit is contained in:
parent
76d687b55c
commit
a5bc727705
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1997,6 +1997,7 @@ dependencies = [
|
||||
"gitbutler-id",
|
||||
"gitbutler-reference",
|
||||
"gitbutler-serde",
|
||||
"gix",
|
||||
"hex",
|
||||
"itertools 0.13.0",
|
||||
"lazy_static",
|
||||
|
@ -2,7 +2,7 @@ use std::path::PathBuf;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use git2::Commit;
|
||||
use gitbutler_branch::{Branch, BranchExt, BranchId};
|
||||
use gitbutler_branch::{Branch, BranchExt, BranchId, SignaturePurpose};
|
||||
use gitbutler_commit::commit_headers::CommitHeadersV2;
|
||||
use gitbutler_oplog::SnapshotExt;
|
||||
use gitbutler_project::access::WorktreeWritePermission;
|
||||
@ -14,7 +14,6 @@ use crate::{
|
||||
conflicts::{self},
|
||||
ensure_selected_for_changes, get_applied_status,
|
||||
hunk::VirtualBranchHunk,
|
||||
integration::get_integration_commiter,
|
||||
VirtualBranchesExt,
|
||||
};
|
||||
|
||||
@ -168,14 +167,15 @@ impl BranchManager<'_> {
|
||||
message.push_str("\n\n");
|
||||
|
||||
// Commit wip commit
|
||||
let committer = get_integration_commiter()?;
|
||||
let committer = gitbutler_branch::signature(SignaturePurpose::Committer)?;
|
||||
let author = gitbutler_branch::signature(SignaturePurpose::Author)?;
|
||||
let parent = branch.get().peel_to_commit()?;
|
||||
|
||||
let commit_headers = CommitHeadersV2::new();
|
||||
|
||||
let commit_oid = repo.commit_with_signature(
|
||||
Some(&branch.try_into()?),
|
||||
&committer,
|
||||
&author,
|
||||
&committer,
|
||||
&message,
|
||||
&tree,
|
||||
|
@ -3,8 +3,7 @@ use std::{path::PathBuf, vec};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use bstr::ByteSlice;
|
||||
use gitbutler_branch::{
|
||||
self, Branch, BranchCreateRequest, VirtualBranchesHandle,
|
||||
GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL, GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME,
|
||||
self, Branch, BranchCreateRequest, SignaturePurpose, VirtualBranchesHandle,
|
||||
GITBUTLER_INTEGRATION_REFERENCE,
|
||||
};
|
||||
use gitbutler_command_context::CommandContext;
|
||||
@ -18,13 +17,6 @@ use crate::{branch_manager::BranchManagerExt, conflicts, VirtualBranchesExt};
|
||||
const WORKSPACE_HEAD: &str = "Workspace Head";
|
||||
const GITBUTLER_INTEGRATION_COMMIT_TITLE: &str = "GitButler Integration Commit";
|
||||
|
||||
pub(crate) fn get_integration_commiter<'a>() -> Result<git2::Signature<'a>> {
|
||||
Ok(git2::Signature::now(
|
||||
GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME,
|
||||
GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL,
|
||||
)?)
|
||||
}
|
||||
|
||||
// Creates and returns a merge commit of all active branch heads.
|
||||
//
|
||||
// This is the base against which we diff the working directory to understand
|
||||
@ -65,9 +57,8 @@ pub(crate) fn get_workspace_head(ctx: &CommandContext) -> Result<git2::Oid> {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(mg): Can we make this a constant?
|
||||
let committer = get_integration_commiter()?;
|
||||
|
||||
let committer = gitbutler_branch::signature(SignaturePurpose::Committer)?;
|
||||
let author = gitbutler_branch::signature(SignaturePurpose::Author)?;
|
||||
let mut heads: Vec<git2::Commit<'_>> = virtual_branches
|
||||
.iter()
|
||||
.filter(|b| b.head != target.sha)
|
||||
@ -85,7 +76,7 @@ pub(crate) fn get_workspace_head(ctx: &CommandContext) -> Result<git2::Oid> {
|
||||
|
||||
let workspace_head_id = repo.commit(
|
||||
None,
|
||||
&committer,
|
||||
&author,
|
||||
&committer,
|
||||
WORKSPACE_HEAD,
|
||||
&workspace_tree,
|
||||
@ -199,7 +190,8 @@ pub fn update_gitbutler_integration(
|
||||
message.push_str("For more information about what we're doing here, check out our docs:\n");
|
||||
message.push_str("https://docs.gitbutler.com/features/virtual-branches/integration-branch\n");
|
||||
|
||||
let committer = get_integration_commiter()?;
|
||||
let committer = gitbutler_branch::signature(SignaturePurpose::Committer)?;
|
||||
let author = gitbutler_branch::signature(SignaturePurpose::Author)?;
|
||||
|
||||
// It would be nice if we could pass an `update_ref` parameter to this function, but that
|
||||
// requires committing to the tip of the branch, and we're mostly replacing the tip.
|
||||
@ -209,7 +201,7 @@ pub fn update_gitbutler_integration(
|
||||
|
||||
let final_commit = repo.commit(
|
||||
None,
|
||||
&committer,
|
||||
&author,
|
||||
&committer,
|
||||
&message,
|
||||
&workspace_tree,
|
||||
|
@ -21,19 +21,6 @@ git init remote
|
||||
git add . && git commit -m "init"
|
||||
)
|
||||
|
||||
git clone remote single-branch-no-vbranch
|
||||
|
||||
git clone remote single-branch-no-vbranch-one-commit
|
||||
(cd single-branch-no-vbranch-one-commit
|
||||
echo change >> file && git add . && git commit -m "local change"
|
||||
)
|
||||
|
||||
git clone remote single-branch-no-vbranch-multi-remote
|
||||
(cd single-branch-no-vbranch-multi-remote
|
||||
git remote add other-origin ../remote
|
||||
git fetch other-origin
|
||||
)
|
||||
|
||||
export GITBUTLER_CLI_DATA_DIR=./user/gitbutler/app-data
|
||||
git clone remote one-vbranch-on-integration
|
||||
(cd one-vbranch-on-integration
|
||||
@ -47,6 +34,7 @@ git clone remote one-vbranch-on-integration-one-commit
|
||||
$CLI branch create virtual
|
||||
echo change >> file
|
||||
echo in-index > new && git add new
|
||||
tick
|
||||
$CLI branch commit virtual -m "virtual branch change in index and worktree"
|
||||
)
|
||||
|
||||
|
@ -8,6 +8,7 @@ publish = false
|
||||
[dependencies]
|
||||
anyhow = "1.0.86"
|
||||
git2.workspace = true
|
||||
gix.workspace = true
|
||||
gitbutler-reference.workspace = true
|
||||
gitbutler-serde.workspace = true
|
||||
gitbutler-id.workspace = true
|
||||
|
@ -1,5 +1,8 @@
|
||||
mod branch;
|
||||
|
||||
use anyhow::Context;
|
||||
pub use branch::{Branch, BranchCreateRequest, BranchId, BranchUpdateRequest};
|
||||
use bstr::ByteSlice;
|
||||
mod branch_ext;
|
||||
pub use branch_ext::BranchExt;
|
||||
mod reference_ext;
|
||||
@ -22,5 +25,53 @@ lazy_static! {
|
||||
gitbutler_reference::LocalRefname::new("gitbutler/integration", None);
|
||||
}
|
||||
|
||||
pub const GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME: &str = "GitButler";
|
||||
pub const GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL: &str = "gitbutler@gitbutler.com";
|
||||
pub const GITBUTLER_COMMIT_AUTHOR_NAME: &str = "GitButler";
|
||||
pub const GITBUTLER_COMMIT_AUTHOR_EMAIL: &str = "gitbutler@gitbutler.com";
|
||||
|
||||
pub enum SignaturePurpose {
|
||||
Author,
|
||||
Committer,
|
||||
}
|
||||
|
||||
/// Provide a signature with the GitButler author, and the current time or the time overridden
|
||||
/// depending on the value for `purpose`.
|
||||
pub fn signature(purpose: SignaturePurpose) -> anyhow::Result<git2::Signature<'static>> {
|
||||
let signature = gix::actor::SignatureRef {
|
||||
name: GITBUTLER_COMMIT_AUTHOR_NAME.into(),
|
||||
email: GITBUTLER_COMMIT_AUTHOR_EMAIL.into(),
|
||||
time: commit_time(match purpose {
|
||||
SignaturePurpose::Author => "GIT_AUTHOR_DATE",
|
||||
SignaturePurpose::Committer => "GIT_COMMITTER_DATE",
|
||||
}),
|
||||
};
|
||||
gix_to_git2_signature(signature)
|
||||
}
|
||||
|
||||
/// Convert `actor` to a `git2` representation or fail if that's not possible.
|
||||
/// Note that the current time as provided by `gix` is also used as it.
|
||||
pub fn gix_to_git2_signature(
|
||||
actor: gix::actor::SignatureRef<'_>,
|
||||
) -> anyhow::Result<git2::Signature<'static>> {
|
||||
let offset_in_minutes = actor.time.offset / 60;
|
||||
let time = git2::Time::new(actor.time.seconds, offset_in_minutes);
|
||||
Ok(git2::Signature::new(
|
||||
actor
|
||||
.name
|
||||
.to_str()
|
||||
.with_context(|| format!("Could not process actor name: {}", actor.name))?,
|
||||
actor
|
||||
.email
|
||||
.to_str()
|
||||
.with_context(|| format!("Could not process actor email: {}", actor.email))?,
|
||||
&time,
|
||||
)?)
|
||||
}
|
||||
|
||||
/// Return the time of a commit as `now` unless the `overriding_variable_name` contains a parseable date,
|
||||
/// which is used instead.
|
||||
fn commit_time(overriding_variable_name: &str) -> gix::date::Time {
|
||||
std::env::var(overriding_variable_name)
|
||||
.ok()
|
||||
.and_then(|time| gix::date::parse(&time, Some(std::time::SystemTime::now())).ok())
|
||||
.unwrap_or_else(gix::date::Time::now_local_or_utc)
|
||||
}
|
||||
|
@ -8,10 +8,7 @@ use std::{
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use git2::{DiffOptions, FileMode};
|
||||
use gitbutler_branch::{
|
||||
Branch, VirtualBranchesHandle, VirtualBranchesState, GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL,
|
||||
GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME,
|
||||
};
|
||||
use gitbutler_branch::{Branch, SignaturePurpose, VirtualBranchesHandle, VirtualBranchesState};
|
||||
use gitbutler_diff::{hunks_by_filepath, FileDiff};
|
||||
use gitbutler_project::{
|
||||
access::{WorktreeReadPermission, WorktreeWritePermission},
|
||||
@ -463,19 +460,16 @@ fn commit_snapshot(
|
||||
.and_then(|head_id| repo.find_commit(head_id).ok());
|
||||
|
||||
// Construct a new commit
|
||||
let signature = git2::Signature::now(
|
||||
GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME,
|
||||
GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL,
|
||||
)
|
||||
.unwrap();
|
||||
let committer = gitbutler_branch::signature(SignaturePurpose::Committer)?;
|
||||
let author = gitbutler_branch::signature(SignaturePurpose::Author)?;
|
||||
let parents = oplog_head_commit
|
||||
.as_ref()
|
||||
.map(|head| vec![head])
|
||||
.unwrap_or_default();
|
||||
let snapshot_commit_id = repo.commit(
|
||||
None,
|
||||
&signature,
|
||||
&signature,
|
||||
&author,
|
||||
&committer,
|
||||
&details.to_string(),
|
||||
&snapshot_tree,
|
||||
parents.as_slice(),
|
||||
|
@ -1,9 +1,7 @@
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use gitbutler_branch::{
|
||||
GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL, GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME,
|
||||
};
|
||||
use gitbutler_branch::{GITBUTLER_COMMIT_AUTHOR_EMAIL, GITBUTLER_COMMIT_AUTHOR_NAME};
|
||||
use gitbutler_fs::write;
|
||||
use gix::config::tree::Key;
|
||||
|
||||
@ -83,8 +81,8 @@ fn branch_creation_message(commit_id_hex: &str) -> String {
|
||||
|
||||
fn standard_signature() -> gix::actor::SignatureRef<'static> {
|
||||
gix::actor::SignatureRef {
|
||||
name: GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME.into(),
|
||||
email: GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL.into(),
|
||||
name: GITBUTLER_COMMIT_AUTHOR_NAME.into(),
|
||||
email: GITBUTLER_COMMIT_AUTHOR_EMAIL.into(),
|
||||
time: gix::date::Time::now_local_or_utc(),
|
||||
}
|
||||
}
|
||||
@ -155,8 +153,7 @@ mod set_target_ref {
|
||||
use tempfile::tempdir;
|
||||
|
||||
use super::{
|
||||
set_reference_to_oplog, GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL,
|
||||
GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME,
|
||||
set_reference_to_oplog, GITBUTLER_COMMIT_AUTHOR_EMAIL, GITBUTLER_COMMIT_AUTHOR_NAME,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@ -352,8 +349,8 @@ mod set_target_ref {
|
||||
}
|
||||
|
||||
fn assert_signature(sig: gix::actor::SignatureRef<'_>) {
|
||||
assert_eq!(sig.name, GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME);
|
||||
assert_eq!(sig.email, GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL);
|
||||
assert_eq!(sig.name, GITBUTLER_COMMIT_AUTHOR_NAME);
|
||||
assert_eq!(sig.email, GITBUTLER_COMMIT_AUTHOR_EMAIL);
|
||||
assert_ne!(
|
||||
sig.time.seconds, 0,
|
||||
"we don't accidentally use the default time as it would caues GC as well"
|
||||
|
@ -1,11 +1,7 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use bstr::ByteSlice;
|
||||
use gitbutler_branch::{
|
||||
Branch, BranchId, GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL,
|
||||
GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME,
|
||||
};
|
||||
use gitbutler_branch::{gix_to_git2_signature, Branch, BranchId, SignaturePurpose};
|
||||
use gitbutler_command_context::CommandContext;
|
||||
use gitbutler_commit::commit_headers::CommitHeadersV2;
|
||||
use gitbutler_error::error::Code;
|
||||
@ -450,58 +446,23 @@ impl RepoActionsExt for CommandContext {
|
||||
let author = repo
|
||||
.author()
|
||||
.transpose()?
|
||||
.unwrap_or_else(|| default_actor_with_commit_time("GIT_AUTHOR_DATE"));
|
||||
.map(gitbutler_branch::gix_to_git2_signature)
|
||||
.unwrap_or_else(|| gitbutler_branch::signature(SignaturePurpose::Author))?;
|
||||
|
||||
let config: Config = self.repository().into();
|
||||
let committer = if config.user_real_comitter()? {
|
||||
repo.committer()
|
||||
.transpose()?
|
||||
.unwrap_or_else(|| default_actor_with_commit_time("GIT_COMMITTER_DATE"))
|
||||
.map(gix_to_git2_signature)
|
||||
.unwrap_or_else(|| gitbutler_branch::signature(SignaturePurpose::Committer))
|
||||
} else {
|
||||
default_actor_with_commit_time("GIT_COMMITTER_DATE")
|
||||
};
|
||||
gitbutler_branch::signature(SignaturePurpose::Committer)
|
||||
}?;
|
||||
|
||||
Ok((
|
||||
actor_to_git2_signature(author)?,
|
||||
actor_to_git2_signature(committer)?,
|
||||
))
|
||||
Ok((author, committer))
|
||||
}
|
||||
}
|
||||
|
||||
fn default_actor_with_commit_time(
|
||||
overriding_variable_name: &str,
|
||||
) -> gix::actor::SignatureRef<'static> {
|
||||
gix::actor::SignatureRef {
|
||||
name: GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME.into(),
|
||||
email: GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL.into(),
|
||||
time: std::env::var(overriding_variable_name)
|
||||
.ok()
|
||||
.and_then(|time| gix::date::parse(&time, Some(std::time::SystemTime::now())).ok())
|
||||
.unwrap_or_else(|| gix::date::Time::now_local_or_utc()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert `actor` to a `git2` representation or fail if that's not possible.
|
||||
/// Note that the current time as provided by `gix` is also use as it
|
||||
/// respects the `GIT_AUTHOR|COMMITTER_DATE` environment variables.
|
||||
fn actor_to_git2_signature(
|
||||
actor: gix::actor::SignatureRef<'_>,
|
||||
) -> Result<git2::Signature<'static>> {
|
||||
let offset_in_minutes = actor.time.offset / 60;
|
||||
let time = git2::Time::new(actor.time.seconds, offset_in_minutes);
|
||||
Ok(git2::Signature::new(
|
||||
actor
|
||||
.name
|
||||
.to_str()
|
||||
.with_context(|| format!("Could not process actor name: {}", actor.name))?,
|
||||
actor
|
||||
.email
|
||||
.to_str()
|
||||
.with_context(|| format!("Could not process actor email: {}", actor.email))?,
|
||||
&time,
|
||||
)?)
|
||||
}
|
||||
|
||||
type OidFilter = dyn Fn(&git2::Commit) -> Result<bool>;
|
||||
|
||||
pub enum LogUntil {
|
||||
|
Loading…
Reference in New Issue
Block a user