mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-10-06 17:07:29 +03:00
Merge pull request #3721 from gitbutlerapp/add-commit-id
Add change-id property to commit
This commit is contained in:
commit
4b683828cd
@ -144,11 +144,18 @@
|
|||||||
<div class="commit__header" on:click={toggleFiles} on:keyup={onKeyup} role="button" tabindex="0">
|
<div class="commit__header" on:click={toggleFiles} on:keyup={onKeyup} role="button" tabindex="0">
|
||||||
<div class="commit__message">
|
<div class="commit__message">
|
||||||
{#if $advancedCommitOperations}
|
{#if $advancedCommitOperations}
|
||||||
{#if !showFiles}
|
<div class="commit__id">
|
||||||
<div class="commit__id">
|
<code>
|
||||||
<code>{commit.id.substring(0, 6)}</code>
|
{#if commit.isSigned}
|
||||||
</div>
|
<span class="text-xs">🔒</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if commit.changeId}
|
||||||
|
{commit.changeId.split('-')[0]}
|
||||||
|
{:else}
|
||||||
|
{commit.id.substring(0, 6)}
|
||||||
|
{/if}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="commit__row">
|
<div class="commit__row">
|
||||||
{#if isUndoable}
|
{#if isUndoable}
|
||||||
|
@ -162,6 +162,8 @@ export class Commit {
|
|||||||
files!: LocalFile[];
|
files!: LocalFile[];
|
||||||
parentIds!: string[];
|
parentIds!: string[];
|
||||||
branchId!: string;
|
branchId!: string;
|
||||||
|
changeId!: string;
|
||||||
|
isSigned!: boolean;
|
||||||
|
|
||||||
get isLocal() {
|
get isLocal() {
|
||||||
return !this.isRemote && !this.isIntegrated;
|
return !this.isRemote && !this.isIntegrated;
|
||||||
@ -196,6 +198,8 @@ export class RemoteCommit {
|
|||||||
description!: string;
|
description!: string;
|
||||||
@Transform((obj) => new Date(obj.value * 1000))
|
@Transform((obj) => new Date(obj.value * 1000))
|
||||||
createdAt!: Date;
|
createdAt!: Date;
|
||||||
|
changeId!: string;
|
||||||
|
isSigned!: boolean;
|
||||||
|
|
||||||
get isLocal() {
|
get isLocal() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
const gitConfig = getContext(GitConfigService);
|
const gitConfig = getContext(GitConfigService);
|
||||||
const authService = getContext(AuthService);
|
const authService = getContext(AuthService);
|
||||||
|
|
||||||
let signCommits = false;
|
|
||||||
let annotateCommits = true;
|
let annotateCommits = true;
|
||||||
let sshKey = '';
|
let sshKey = '';
|
||||||
|
|
||||||
@ -25,15 +24,9 @@
|
|||||||
gitConfig.set('gitbutler.gitbutlerCommitter', annotateCommits ? '1' : '0');
|
gitConfig.set('gitbutler.gitbutlerCommitter', annotateCommits ? '1' : '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleSigningSetting() {
|
|
||||||
signCommits = !signCommits;
|
|
||||||
gitConfig.set('gitbutler.signCommits', signCommits ? 'true' : 'false');
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
sshKey = await authService.getPublicKey();
|
sshKey = await authService.getPublicKey();
|
||||||
annotateCommits = (await gitConfig.get('gitbutler.gitbutlerCommitter')) == '1';
|
annotateCommits = (await gitConfig.get('gitbutler.gitbutlerCommitter')) == '1';
|
||||||
signCommits = (await gitConfig.get('gitbutler.signCommits')) == 'true';
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -82,22 +75,4 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</SectionCard>
|
</SectionCard>
|
||||||
|
|
||||||
<SectionCard labelFor="signingSetting" orientation="row">
|
|
||||||
<svelte:fragment slot="title">Sign commits with the above SSH key</svelte:fragment>
|
|
||||||
<svelte:fragment slot="caption">
|
|
||||||
If you want GitButler to sign your commits with the SSH key we generated, then you can add
|
|
||||||
that key to GitHub as a signing key to have those commits verified.
|
|
||||||
<Link
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
href="https://docs.gitbutler.com/features/virtual-branches/verifying-commits"
|
|
||||||
>
|
|
||||||
Learn more
|
|
||||||
</Link>
|
|
||||||
</svelte:fragment>
|
|
||||||
<svelte:fragment slot="actions">
|
|
||||||
<Toggle id="signingSetting" checked={signCommits} on:change={toggleSigningSetting} />
|
|
||||||
</svelte:fragment>
|
|
||||||
</SectionCard>
|
|
||||||
</ContentWrapper>
|
</ContentWrapper>
|
||||||
|
@ -8,9 +8,8 @@ publish = false
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
once_cell = "1.19"
|
once_cell = "1.19"
|
||||||
pretty_assertions = "1.4"
|
pretty_assertions = "1.4"
|
||||||
tempfile = "3.10"
|
|
||||||
gitbutler-testsupport.workspace = true
|
gitbutler-testsupport.workspace = true
|
||||||
gitbutler-git = { workspace = true, features = ["test-askpass-path" ]}
|
gitbutler-git = { workspace = true, features = ["test-askpass-path"] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
toml = "0.8.12"
|
toml = "0.8.12"
|
||||||
@ -42,6 +41,7 @@ ssh-key = { version = "0.6.6", features = [ "alloc", "ed25519" ] }
|
|||||||
ssh2 = { version = "0.9.4", features = ["vendored-openssl"] }
|
ssh2 = { version = "0.9.4", features = ["vendored-openssl"] }
|
||||||
strum = { version = "0.26", features = ["derive"] }
|
strum = { version = "0.26", features = ["derive"] }
|
||||||
log = "^0.4"
|
log = "^0.4"
|
||||||
|
tempfile = "3.10"
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
tokio = { workspace = true, features = [ "rt-multi-thread", "rt", "macros" ] }
|
tokio = { workspace = true, features = [ "rt-multi-thread", "rt", "macros" ] }
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
|
@ -71,6 +71,22 @@ impl<'repo> Commit<'repo> {
|
|||||||
self.commit.committer().into()
|
self.commit.committer().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn change_id(&self) -> Option<String> {
|
||||||
|
let cid = self.commit.header_field_bytes("change-id").ok()?;
|
||||||
|
if cid.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
// convert the Buf to a string
|
||||||
|
let ch_id = std::str::from_utf8(&cid).ok()?.to_owned();
|
||||||
|
Some(ch_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_signed(&self) -> bool {
|
||||||
|
let cid = self.commit.header_field_bytes("gpgsig").ok();
|
||||||
|
cid.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn raw_header(&self) -> Option<&str> {
|
pub fn raw_header(&self) -> Option<&str> {
|
||||||
self.commit.raw_header()
|
self.commit.raw_header()
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
use std::{io::Write, path::Path, str};
|
|
||||||
|
|
||||||
use git2::{BlameOptions, Submodule};
|
|
||||||
use git2_hooks::HookResult;
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Blob, Branch, Commit, Config, Index, Oid, Reference, Refname, Remote, Result, Signature, Tree,
|
Blob, Branch, Commit, Config, Index, Oid, Reference, Refname, Remote, Result, Signature, Tree,
|
||||||
TreeBuilder, Url,
|
TreeBuilder, Url,
|
||||||
};
|
};
|
||||||
use crate::{keys, path::Normalize};
|
use crate::path::Normalize;
|
||||||
|
use git2::{BlameOptions, Submodule};
|
||||||
|
use git2_hooks::HookResult;
|
||||||
|
#[cfg(unix)]
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
use std::process::Stdio;
|
||||||
|
use std::{io::Write, path::Path, str};
|
||||||
|
|
||||||
// wrapper around git2::Repository to get control over how it's used.
|
// wrapper around git2::Repository to get control over how it's used.
|
||||||
pub struct Repository(git2::Repository);
|
pub struct Repository(git2::Repository);
|
||||||
@ -242,6 +243,7 @@ impl Repository {
|
|||||||
self.0.blob(data).map(Into::into).map_err(Into::into)
|
self.0.blob(data).map(Into::into).map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn commit(
|
pub fn commit(
|
||||||
&self,
|
&self,
|
||||||
update_ref: Option<&Refname>,
|
update_ref: Option<&Refname>,
|
||||||
@ -250,51 +252,183 @@ impl Repository {
|
|||||||
message: &str,
|
message: &str,
|
||||||
tree: &Tree<'_>,
|
tree: &Tree<'_>,
|
||||||
parents: &[&Commit<'_>],
|
parents: &[&Commit<'_>],
|
||||||
|
change_id: Option<&str>,
|
||||||
) -> Result<Oid> {
|
) -> Result<Oid> {
|
||||||
let parents: Vec<&git2::Commit> = parents
|
let parents: Vec<&git2::Commit> = parents
|
||||||
.iter()
|
.iter()
|
||||||
.map(|c| c.to_owned().into())
|
.map(|c| c.to_owned().into())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
self.0
|
|
||||||
.commit(
|
|
||||||
update_ref.map(ToString::to_string).as_deref(),
|
|
||||||
author.into(),
|
|
||||||
committer.into(),
|
|
||||||
message,
|
|
||||||
tree.into(),
|
|
||||||
&parents,
|
|
||||||
)
|
|
||||||
.map(Into::into)
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn commit_signed(
|
|
||||||
&self,
|
|
||||||
author: &Signature<'_>,
|
|
||||||
message: &str,
|
|
||||||
tree: &Tree<'_>,
|
|
||||||
parents: &[&Commit<'_>],
|
|
||||||
key: &keys::PrivateKey,
|
|
||||||
) -> Result<Oid> {
|
|
||||||
let parents: Vec<&git2::Commit> = parents
|
|
||||||
.iter()
|
|
||||||
.map(|c| c.to_owned().into())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let commit_buffer = self.0.commit_create_buffer(
|
let commit_buffer = self.0.commit_create_buffer(
|
||||||
author.into(),
|
author.into(),
|
||||||
// author and committer must be the same
|
committer.into(),
|
||||||
// for signed commits
|
|
||||||
author.into(),
|
|
||||||
message,
|
message,
|
||||||
tree.into(),
|
tree.into(),
|
||||||
&parents,
|
&parents,
|
||||||
)?;
|
)?;
|
||||||
let commit_buffer = str::from_utf8(&commit_buffer).unwrap();
|
|
||||||
let signature = key.sign(commit_buffer.as_bytes())?;
|
let commit_buffer = Self::inject_change_id(&commit_buffer, change_id)?;
|
||||||
self.0
|
|
||||||
.commit_signed(commit_buffer, &signature, None)
|
let oid = self.commit_buffer(commit_buffer)?;
|
||||||
.map(Into::into)
|
|
||||||
.map_err(Into::into)
|
// update reference
|
||||||
|
if let Some(refname) = update_ref {
|
||||||
|
self.0.reference(&refname.to_string(), oid, true, message)?;
|
||||||
|
}
|
||||||
|
Ok(oid.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// takes raw commit data and commits it to the repository
|
||||||
|
/// - if the git config commit.gpgSign is set, it will sign the commit
|
||||||
|
/// returns an oid of the new commit object
|
||||||
|
pub fn commit_buffer(&self, buffer: String) -> Result<git2::Oid> {
|
||||||
|
// check git config for gpg.signingkey
|
||||||
|
let should_sign = self.0.config()?.get_string("commit.gpgSign");
|
||||||
|
if should_sign.unwrap_or("false".to_string()) != "false" {
|
||||||
|
// TODO: support gpg.ssh.defaultKeyCommand to get the signing key if this value doesn't exist
|
||||||
|
let signing_key = self.0.config()?.get_string("user.signingkey");
|
||||||
|
if let Ok(signing_key) = signing_key {
|
||||||
|
let sign_format = self.0.config()?.get_string("gpg.format");
|
||||||
|
let is_ssh = if let Ok(sign_format) = sign_format {
|
||||||
|
sign_format == "ssh"
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_ssh {
|
||||||
|
// write commit data to a temp file so we can sign it
|
||||||
|
let mut signature_storage = tempfile::NamedTempFile::new()?;
|
||||||
|
signature_storage.write_all(buffer.as_ref())?;
|
||||||
|
let buffer_file_to_sign_path = signature_storage.into_temp_path();
|
||||||
|
|
||||||
|
let gpg_program = self.0.config()?.get_string("gpg.ssh.program");
|
||||||
|
let mut cmd =
|
||||||
|
std::process::Command::new(gpg_program.unwrap_or("ssh-keygen".to_string()));
|
||||||
|
cmd.args(["-Y", "sign", "-n", "git", "-f"]);
|
||||||
|
|
||||||
|
let output;
|
||||||
|
// support literal ssh key
|
||||||
|
if let (true, signing_key) = Self::is_literal_ssh_key(&signing_key) {
|
||||||
|
// write the key to a temp file
|
||||||
|
let mut key_storage = tempfile::NamedTempFile::new()?;
|
||||||
|
key_storage.write_all(signing_key.as_bytes())?;
|
||||||
|
|
||||||
|
// if on unix
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
// make sure the tempfile permissions are acceptable for a private ssh key
|
||||||
|
let mut permissions = key_storage.as_file().metadata()?.permissions();
|
||||||
|
permissions.set_mode(0o600);
|
||||||
|
key_storage.as_file().set_permissions(permissions)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let key_file_path = key_storage.into_temp_path();
|
||||||
|
|
||||||
|
cmd.arg(&key_file_path);
|
||||||
|
cmd.arg("-U");
|
||||||
|
cmd.arg(&buffer_file_to_sign_path);
|
||||||
|
cmd.stdout(Stdio::piped());
|
||||||
|
|
||||||
|
let child = cmd.spawn()?;
|
||||||
|
output = child.wait_with_output()?;
|
||||||
|
} else {
|
||||||
|
cmd.arg(signing_key);
|
||||||
|
cmd.arg(&buffer_file_to_sign_path);
|
||||||
|
cmd.stdout(Stdio::piped());
|
||||||
|
|
||||||
|
let child = cmd.spawn()?;
|
||||||
|
output = child.wait_with_output()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if output.status.success() {
|
||||||
|
// read signed_storage path plus .sig
|
||||||
|
let signature_path = buffer_file_to_sign_path.with_extension("sig");
|
||||||
|
let sig_data = std::fs::read(signature_path)?;
|
||||||
|
let signature = String::from_utf8_lossy(&sig_data);
|
||||||
|
let oid = self
|
||||||
|
.0
|
||||||
|
.commit_signed(&buffer, &signature, None)
|
||||||
|
.map(Into::into)
|
||||||
|
.map_err(Into::into);
|
||||||
|
return oid;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// is gpg
|
||||||
|
let gpg_program = self.0.config()?.get_string("gpg.program");
|
||||||
|
let mut cmd =
|
||||||
|
std::process::Command::new(gpg_program.unwrap_or("gpg".to_string()));
|
||||||
|
cmd.args(["--status-fd=2", "-bsau", &signing_key])
|
||||||
|
//.arg(&signed_storage)
|
||||||
|
.arg("-")
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stdin(Stdio::piped());
|
||||||
|
|
||||||
|
let mut child = cmd.spawn()?;
|
||||||
|
child
|
||||||
|
.stdin
|
||||||
|
.take()
|
||||||
|
.expect("configured")
|
||||||
|
.write_all(buffer.to_string().as_ref())?;
|
||||||
|
|
||||||
|
let output = child.wait_with_output()?;
|
||||||
|
if output.status.success() {
|
||||||
|
// read stdout
|
||||||
|
let signature = String::from_utf8_lossy(&output.stdout);
|
||||||
|
let oid = self
|
||||||
|
.0
|
||||||
|
.commit_signed(&buffer, &signature, None)
|
||||||
|
.map(Into::into)
|
||||||
|
.map_err(Into::into);
|
||||||
|
return oid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let oid = self
|
||||||
|
.0
|
||||||
|
.odb()?
|
||||||
|
.write(git2::ObjectType::Commit, buffer.as_bytes())?;
|
||||||
|
|
||||||
|
Ok(oid)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_literal_ssh_key(string: &str) -> (bool, &str) {
|
||||||
|
if let Some(key) = string.strip_prefix("key::") {
|
||||||
|
return (true, key);
|
||||||
|
}
|
||||||
|
if string.starts_with("ssh-") {
|
||||||
|
return (true, string);
|
||||||
|
}
|
||||||
|
(false, string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// in commit_buffer, inject a line right before the first `\n\n` that we see:
|
||||||
|
// `change-id: <id>`
|
||||||
|
fn inject_change_id(commit_buffer: &[u8], change_id: Option<&str>) -> Result<String> {
|
||||||
|
// if no change id, generate one
|
||||||
|
let change_id = change_id
|
||||||
|
.map(|id| id.to_string())
|
||||||
|
.unwrap_or_else(|| format!("{}", uuid::Uuid::new_v4()));
|
||||||
|
|
||||||
|
let commit_ends_in_newline = commit_buffer.ends_with(b"\n");
|
||||||
|
let commit_buffer = str::from_utf8(commit_buffer).unwrap();
|
||||||
|
let lines = commit_buffer.lines();
|
||||||
|
let mut new_buffer = String::new();
|
||||||
|
let mut found = false;
|
||||||
|
for line in lines {
|
||||||
|
if line.is_empty() && !found {
|
||||||
|
new_buffer.push_str(&format!("change-id {}\n", change_id));
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
new_buffer.push_str(line);
|
||||||
|
new_buffer.push('\n');
|
||||||
|
}
|
||||||
|
if !commit_ends_in_newline {
|
||||||
|
// strip last \n
|
||||||
|
new_buffer.pop();
|
||||||
|
}
|
||||||
|
Ok(new_buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn config(&self) -> Result<Config> {
|
pub fn config(&self) -> Result<Config> {
|
||||||
|
@ -11,7 +11,6 @@ use crate::error::{AnyhowContextExt, Code, ErrorWithContext};
|
|||||||
use crate::{
|
use crate::{
|
||||||
askpass, error,
|
askpass, error,
|
||||||
git::{self, credentials::HelpError, Url},
|
git::{self, credentials::HelpError, Url},
|
||||||
keys,
|
|
||||||
projects::{self, AuthKey},
|
projects::{self, AuthKey},
|
||||||
ssh, users,
|
ssh, users,
|
||||||
virtual_branches::{Branch, BranchId},
|
virtual_branches::{Branch, BranchId},
|
||||||
@ -335,18 +334,12 @@ impl Repository {
|
|||||||
message: &str,
|
message: &str,
|
||||||
tree: &git::Tree,
|
tree: &git::Tree,
|
||||||
parents: &[&git::Commit],
|
parents: &[&git::Commit],
|
||||||
signing_key: Option<&keys::PrivateKey>,
|
change_id: Option<&str>,
|
||||||
) -> Result<git::Oid> {
|
) -> Result<git::Oid> {
|
||||||
let (author, committer) = self.git_signatures(user)?;
|
let (author, committer) = self.git_signatures(user)?;
|
||||||
if let Some(key) = signing_key {
|
self.git_repository
|
||||||
self.git_repository
|
.commit(None, &author, &committer, message, tree, parents, change_id)
|
||||||
.commit_signed(&author, message, tree, parents, key)
|
.context("failed to commit")
|
||||||
.context("failed to commit signed")
|
|
||||||
} else {
|
|
||||||
self.git_repository
|
|
||||||
.commit(None, &author, &committer, message, tree, parents)
|
|
||||||
.context("failed to commit")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_to_gitbutler_server(
|
pub fn push_to_gitbutler_server(
|
||||||
|
@ -12,7 +12,6 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
git::{self, diff},
|
git::{self, diff},
|
||||||
keys,
|
|
||||||
project_repository::{self, LogUntil},
|
project_repository::{self, LogUntil},
|
||||||
projects::FetchResult,
|
projects::FetchResult,
|
||||||
users,
|
users,
|
||||||
@ -342,7 +341,6 @@ fn _print_tree(repo: &git2::Repository, tree: &git2::Tree) -> Result<()> {
|
|||||||
pub fn update_base_branch(
|
pub fn update_base_branch(
|
||||||
project_repository: &project_repository::Repository,
|
project_repository: &project_repository::Repository,
|
||||||
user: Option<&users::User>,
|
user: Option<&users::User>,
|
||||||
signing_key: Option<&keys::PrivateKey>,
|
|
||||||
) -> Result<(), errors::UpdateBaseBranchError> {
|
) -> Result<(), errors::UpdateBaseBranchError> {
|
||||||
if project_repository.is_resolving() {
|
if project_repository.is_resolving() {
|
||||||
return Err(errors::UpdateBaseBranchError::Conflict(
|
return Err(errors::UpdateBaseBranchError::Conflict(
|
||||||
@ -508,12 +506,12 @@ pub fn update_base_branch(
|
|||||||
"Merged {}/{} into {}",
|
"Merged {}/{} into {}",
|
||||||
target.branch.remote(),
|
target.branch.remote(),
|
||||||
target.branch.branch(),
|
target.branch.branch(),
|
||||||
branch.name
|
branch.name,
|
||||||
)
|
)
|
||||||
.as_str(),
|
.as_str(),
|
||||||
&branch_head_merge_tree,
|
&branch_head_merge_tree,
|
||||||
&[&branch_head_commit, &new_target_commit],
|
&[&branch_head_commit, &new_target_commit],
|
||||||
signing_key,
|
None,
|
||||||
)
|
)
|
||||||
.context("failed to commit merge")?;
|
.context("failed to commit merge")?;
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ use super::{
|
|||||||
target, target_to_base_branch, BaseBranch, RemoteBranchFile, VirtualBranchesHandle,
|
target, target_to_base_branch, BaseBranch, RemoteBranchFile, VirtualBranchesHandle,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
git, keys, project_repository,
|
git, project_repository,
|
||||||
projects::{self, ProjectId},
|
projects::{self, ProjectId},
|
||||||
users,
|
users,
|
||||||
};
|
};
|
||||||
@ -26,7 +26,6 @@ use crate::{
|
|||||||
pub struct Controller {
|
pub struct Controller {
|
||||||
projects: projects::Controller,
|
projects: projects::Controller,
|
||||||
users: users::Controller,
|
users: users::Controller,
|
||||||
keys: keys::Controller,
|
|
||||||
helper: git::credentials::Helper,
|
helper: git::credentials::Helper,
|
||||||
|
|
||||||
by_project_id: Arc<tokio::sync::Mutex<HashMap<ProjectId, ControllerInner>>>,
|
by_project_id: Arc<tokio::sync::Mutex<HashMap<ProjectId, ControllerInner>>>,
|
||||||
@ -36,7 +35,6 @@ impl Controller {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
projects: projects::Controller,
|
projects: projects::Controller,
|
||||||
users: users::Controller,
|
users: users::Controller,
|
||||||
keys: keys::Controller,
|
|
||||||
helper: git::credentials::Helper,
|
helper: git::credentials::Helper,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -44,7 +42,6 @@ impl Controller {
|
|||||||
|
|
||||||
projects,
|
projects,
|
||||||
users,
|
users,
|
||||||
keys,
|
|
||||||
helper,
|
helper,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,9 +51,7 @@ impl Controller {
|
|||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.entry(*project_id)
|
.entry(*project_id)
|
||||||
.or_insert_with(|| {
|
.or_insert_with(|| ControllerInner::new(&self.projects, &self.users, &self.helper))
|
||||||
ControllerInner::new(&self.projects, &self.users, &self.keys, &self.helper)
|
|
||||||
})
|
|
||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,7 +425,6 @@ struct ControllerInner {
|
|||||||
|
|
||||||
projects: projects::Controller,
|
projects: projects::Controller,
|
||||||
users: users::Controller,
|
users: users::Controller,
|
||||||
keys: keys::Controller,
|
|
||||||
helper: git::credentials::Helper,
|
helper: git::credentials::Helper,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,14 +432,12 @@ impl ControllerInner {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
projects: &projects::Controller,
|
projects: &projects::Controller,
|
||||||
users: &users::Controller,
|
users: &users::Controller,
|
||||||
keys: &keys::Controller,
|
|
||||||
helper: &git::credentials::Helper,
|
helper: &git::credentials::Helper,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
semaphore: Arc::new(Semaphore::new(1)),
|
semaphore: Arc::new(Semaphore::new(1)),
|
||||||
projects: projects.clone(),
|
projects: projects.clone(),
|
||||||
users: users.clone(),
|
users: users.clone(),
|
||||||
keys: keys.clone(),
|
|
||||||
helper: helper.clone(),
|
helper: helper.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -461,23 +453,11 @@ impl ControllerInner {
|
|||||||
let _permit = self.semaphore.acquire().await;
|
let _permit = self.semaphore.acquire().await;
|
||||||
|
|
||||||
self.with_verify_branch(project_id, |project_repository, user| {
|
self.with_verify_branch(project_id, |project_repository, user| {
|
||||||
let signing_key = project_repository
|
|
||||||
.config()
|
|
||||||
.sign_commits()
|
|
||||||
.context("failed to get sign commits option")?
|
|
||||||
.then(|| {
|
|
||||||
self.keys
|
|
||||||
.get_or_create()
|
|
||||||
.context("failed to get private key")
|
|
||||||
})
|
|
||||||
.transpose()?;
|
|
||||||
|
|
||||||
let result = super::commit(
|
let result = super::commit(
|
||||||
project_repository,
|
project_repository,
|
||||||
branch_id,
|
branch_id,
|
||||||
message,
|
message,
|
||||||
ownership,
|
ownership,
|
||||||
signing_key.as_ref(),
|
|
||||||
user,
|
user,
|
||||||
run_hooks,
|
run_hooks,
|
||||||
)
|
)
|
||||||
@ -546,22 +526,8 @@ impl ControllerInner {
|
|||||||
let _permit = self.semaphore.acquire().await;
|
let _permit = self.semaphore.acquire().await;
|
||||||
|
|
||||||
self.with_verify_branch(project_id, |project_repository, user| {
|
self.with_verify_branch(project_id, |project_repository, user| {
|
||||||
let signing_key = project_repository
|
let result =
|
||||||
.config()
|
super::create_virtual_branch_from_branch(project_repository, branch, user)?;
|
||||||
.sign_commits()
|
|
||||||
.context("failed to get sign commits option")?
|
|
||||||
.then(|| {
|
|
||||||
self.keys
|
|
||||||
.get_or_create()
|
|
||||||
.context("failed to get private key")
|
|
||||||
})
|
|
||||||
.transpose()?;
|
|
||||||
let result = super::create_virtual_branch_from_branch(
|
|
||||||
project_repository,
|
|
||||||
branch,
|
|
||||||
signing_key.as_ref(),
|
|
||||||
user,
|
|
||||||
)?;
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -619,24 +585,8 @@ impl ControllerInner {
|
|||||||
let _permit = self.semaphore.acquire().await;
|
let _permit = self.semaphore.acquire().await;
|
||||||
|
|
||||||
self.with_verify_branch(project_id, |project_repository, user| {
|
self.with_verify_branch(project_id, |project_repository, user| {
|
||||||
let signing_key = project_repository
|
let result = super::merge_virtual_branch_upstream(project_repository, branch_id, user)
|
||||||
.config()
|
.map_err(Into::into);
|
||||||
.sign_commits()
|
|
||||||
.context("failed to get sign commits option")?
|
|
||||||
.then(|| {
|
|
||||||
self.keys
|
|
||||||
.get_or_create()
|
|
||||||
.context("failed to get private key")
|
|
||||||
})
|
|
||||||
.transpose()?;
|
|
||||||
|
|
||||||
let result = super::merge_virtual_branch_upstream(
|
|
||||||
project_repository,
|
|
||||||
branch_id,
|
|
||||||
signing_key.as_ref(),
|
|
||||||
user,
|
|
||||||
)
|
|
||||||
.map_err(Into::into);
|
|
||||||
let _ = project_repository
|
let _ = project_repository
|
||||||
.project()
|
.project()
|
||||||
.create_snapshot(SnapshotDetails::new(OperationType::MergeUpstream));
|
.create_snapshot(SnapshotDetails::new(OperationType::MergeUpstream));
|
||||||
@ -648,19 +598,7 @@ impl ControllerInner {
|
|||||||
let _permit = self.semaphore.acquire().await;
|
let _permit = self.semaphore.acquire().await;
|
||||||
|
|
||||||
self.with_verify_branch(project_id, |project_repository, user| {
|
self.with_verify_branch(project_id, |project_repository, user| {
|
||||||
let signing_key = project_repository
|
let result = super::update_base_branch(project_repository, user).map_err(Into::into);
|
||||||
.config()
|
|
||||||
.sign_commits()
|
|
||||||
.context("failed to get sign commits option")?
|
|
||||||
.then(|| {
|
|
||||||
self.keys
|
|
||||||
.get_or_create()
|
|
||||||
.context("failed to get private key")
|
|
||||||
})
|
|
||||||
.transpose()?;
|
|
||||||
|
|
||||||
let result = super::update_base_branch(project_repository, user, signing_key.as_ref())
|
|
||||||
.map_err(Into::into);
|
|
||||||
let _ = project_repository
|
let _ = project_repository
|
||||||
.project()
|
.project()
|
||||||
.create_snapshot(SnapshotDetails::new(OperationType::UpdateWorkspaceBase));
|
.create_snapshot(SnapshotDetails::new(OperationType::UpdateWorkspaceBase));
|
||||||
@ -701,21 +639,7 @@ impl ControllerInner {
|
|||||||
let _permit = self.semaphore.acquire().await;
|
let _permit = self.semaphore.acquire().await;
|
||||||
|
|
||||||
self.with_verify_branch(project_id, |project_repository, user| {
|
self.with_verify_branch(project_id, |project_repository, user| {
|
||||||
let signing_key = project_repository
|
super::apply_branch(project_repository, branch_id, user).map_err(Into::into)
|
||||||
.config()
|
|
||||||
.sign_commits()
|
|
||||||
.context("failed to get sign commits option")?
|
|
||||||
.then(|| {
|
|
||||||
self.keys
|
|
||||||
.get_or_create()
|
|
||||||
.context("failed to get private key")
|
|
||||||
})
|
|
||||||
.transpose()?;
|
|
||||||
|
|
||||||
let result =
|
|
||||||
super::apply_branch(project_repository, branch_id, signing_key.as_ref(), user)
|
|
||||||
.map_err(Into::into);
|
|
||||||
result
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1060,24 +984,8 @@ impl ControllerInner {
|
|||||||
let _permit = self.semaphore.acquire().await;
|
let _permit = self.semaphore.acquire().await;
|
||||||
|
|
||||||
self.with_verify_branch(project_id, |project_repository, user| {
|
self.with_verify_branch(project_id, |project_repository, user| {
|
||||||
let signing_key = project_repository
|
let result = super::move_commit(project_repository, target_branch_id, commit_oid, user)
|
||||||
.config()
|
.map_err(Into::into);
|
||||||
.sign_commits()
|
|
||||||
.context("failed to get sign commits option")?
|
|
||||||
.then(|| {
|
|
||||||
self.keys
|
|
||||||
.get_or_create()
|
|
||||||
.context("failed to get private key")
|
|
||||||
})
|
|
||||||
.transpose()?;
|
|
||||||
let result = super::move_commit(
|
|
||||||
project_repository,
|
|
||||||
target_branch_id,
|
|
||||||
commit_oid,
|
|
||||||
user,
|
|
||||||
signing_key.as_ref(),
|
|
||||||
)
|
|
||||||
.map_err(Into::into);
|
|
||||||
let _ = project_repository
|
let _ = project_repository
|
||||||
.project()
|
.project()
|
||||||
.create_snapshot(SnapshotDetails::new(OperationType::MoveCommit));
|
.create_snapshot(SnapshotDetails::new(OperationType::MoveCommit));
|
||||||
|
@ -89,6 +89,7 @@ pub fn get_workspace_head(
|
|||||||
WORKSPACE_HEAD,
|
WORKSPACE_HEAD,
|
||||||
&workspace_tree,
|
&workspace_tree,
|
||||||
branch_head_refs.as_slice(),
|
branch_head_refs.as_slice(),
|
||||||
|
None,
|
||||||
)?;
|
)?;
|
||||||
Ok(workspace_head_id)
|
Ok(workspace_head_id)
|
||||||
}
|
}
|
||||||
@ -219,6 +220,7 @@ pub fn update_gitbutler_integration(
|
|||||||
&message,
|
&message,
|
||||||
&integration_commit.tree()?,
|
&integration_commit.tree()?,
|
||||||
&[&target_commit],
|
&[&target_commit],
|
||||||
|
None,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Create or replace the integration branch reference, then set as HEAD.
|
// Create or replace the integration branch reference, then set as HEAD.
|
||||||
@ -257,6 +259,7 @@ pub fn update_gitbutler_integration(
|
|||||||
&message,
|
&message,
|
||||||
&wip_tree,
|
&wip_tree,
|
||||||
&[&branch_head],
|
&[&branch_head],
|
||||||
|
None,
|
||||||
)?;
|
)?;
|
||||||
branch_head = repo.find_commit(branch_head_oid)?;
|
branch_head = repo.find_commit(branch_head_oid)?;
|
||||||
}
|
}
|
||||||
@ -351,6 +354,7 @@ fn verify_head_is_clean(
|
|||||||
&commit.message().to_str_lossy(),
|
&commit.message().to_str_lossy(),
|
||||||
&commit.tree().unwrap(),
|
&commit.tree().unwrap(),
|
||||||
&[&new_branch_head],
|
&[&new_branch_head],
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.context(format!(
|
.context(format!(
|
||||||
"failed to rebase commit {} onto new branch",
|
"failed to rebase commit {} onto new branch",
|
||||||
|
@ -34,7 +34,6 @@ use crate::{
|
|||||||
diff::{self},
|
diff::{self},
|
||||||
Commit, Refname, RemoteRefname,
|
Commit, Refname, RemoteRefname,
|
||||||
},
|
},
|
||||||
keys,
|
|
||||||
project_repository::{self, conflicts, LogUntil},
|
project_repository::{self, conflicts, LogUntil},
|
||||||
reader, users,
|
reader, users,
|
||||||
};
|
};
|
||||||
@ -99,6 +98,8 @@ pub struct VirtualBranchCommit {
|
|||||||
pub is_integrated: bool,
|
pub is_integrated: bool,
|
||||||
pub parent_ids: Vec<git::Oid>,
|
pub parent_ids: Vec<git::Oid>,
|
||||||
pub branch_id: BranchId,
|
pub branch_id: BranchId,
|
||||||
|
pub change_id: Option<String>,
|
||||||
|
pub is_signed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// this struct is a mapping to the view `File` type in Typescript
|
// this struct is a mapping to the view `File` type in Typescript
|
||||||
@ -213,7 +214,6 @@ pub fn normalize_branch_name(name: &str) -> String {
|
|||||||
pub fn apply_branch(
|
pub fn apply_branch(
|
||||||
project_repository: &project_repository::Repository,
|
project_repository: &project_repository::Repository,
|
||||||
branch_id: &BranchId,
|
branch_id: &BranchId,
|
||||||
signing_key: Option<&keys::PrivateKey>,
|
|
||||||
user: Option<&users::User>,
|
user: Option<&users::User>,
|
||||||
) -> Result<(), errors::ApplyBranchError> {
|
) -> Result<(), errors::ApplyBranchError> {
|
||||||
if project_repository.is_resolving() {
|
if project_repository.is_resolving() {
|
||||||
@ -357,7 +357,7 @@ pub fn apply_branch(
|
|||||||
.as_str(),
|
.as_str(),
|
||||||
&merged_branch_tree,
|
&merged_branch_tree,
|
||||||
&[&head_commit, &target_commit],
|
&[&head_commit, &target_commit],
|
||||||
signing_key,
|
None,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// ok, update the virtual branch
|
// ok, update the virtual branch
|
||||||
@ -426,7 +426,7 @@ pub fn apply_branch(
|
|||||||
.as_str(),
|
.as_str(),
|
||||||
&merge_tree,
|
&merge_tree,
|
||||||
&[&head_commit, &target_commit],
|
&[&head_commit, &target_commit],
|
||||||
signing_key,
|
None,
|
||||||
)
|
)
|
||||||
.context("failed to commit merge")?;
|
.context("failed to commit merge")?;
|
||||||
|
|
||||||
@ -1044,6 +1044,8 @@ fn commit_to_vbranch_commit(
|
|||||||
is_integrated,
|
is_integrated,
|
||||||
parent_ids,
|
parent_ids,
|
||||||
branch_id: branch.id,
|
branch_id: branch.id,
|
||||||
|
change_id: commit.change_id(),
|
||||||
|
is_signed: commit.is_signed(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(commit)
|
Ok(commit)
|
||||||
@ -1163,7 +1165,6 @@ pub fn create_virtual_branch(
|
|||||||
pub fn merge_virtual_branch_upstream(
|
pub fn merge_virtual_branch_upstream(
|
||||||
project_repository: &project_repository::Repository,
|
project_repository: &project_repository::Repository,
|
||||||
branch_id: &BranchId,
|
branch_id: &BranchId,
|
||||||
signing_key: Option<&keys::PrivateKey>,
|
|
||||||
user: Option<&users::User>,
|
user: Option<&users::User>,
|
||||||
) -> Result<(), errors::MergeVirtualBranchUpstreamError> {
|
) -> Result<(), errors::MergeVirtualBranchUpstreamError> {
|
||||||
if conflicts::is_conflicting::<&Path>(project_repository, None)? {
|
if conflicts::is_conflicting::<&Path>(project_repository, None)? {
|
||||||
@ -1356,7 +1357,7 @@ pub fn merge_virtual_branch_upstream(
|
|||||||
.as_str(),
|
.as_str(),
|
||||||
&merge_tree,
|
&merge_tree,
|
||||||
&[&head_commit, &upstream_commit],
|
&[&head_commit, &upstream_commit],
|
||||||
signing_key,
|
None,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// checkout the merge tree
|
// checkout the merge tree
|
||||||
@ -2313,7 +2314,6 @@ pub fn commit(
|
|||||||
branch_id: &BranchId,
|
branch_id: &BranchId,
|
||||||
message: &str,
|
message: &str,
|
||||||
ownership: Option<&branch::BranchOwnershipClaims>,
|
ownership: Option<&branch::BranchOwnershipClaims>,
|
||||||
signing_key: Option<&keys::PrivateKey>,
|
|
||||||
user: Option<&users::User>,
|
user: Option<&users::User>,
|
||||||
run_hooks: bool,
|
run_hooks: bool,
|
||||||
) -> Result<git::Oid, errors::CommitError> {
|
) -> Result<git::Oid, errors::CommitError> {
|
||||||
@ -2416,12 +2416,12 @@ pub fn commit(
|
|||||||
message,
|
message,
|
||||||
&tree,
|
&tree,
|
||||||
&[&parent_commit, &merge_parent],
|
&[&parent_commit, &merge_parent],
|
||||||
signing_key,
|
None,
|
||||||
)?;
|
)?;
|
||||||
conflicts::clear(project_repository).context("failed to clear conflicts")?;
|
conflicts::clear(project_repository).context("failed to clear conflicts")?;
|
||||||
commit_oid
|
commit_oid
|
||||||
}
|
}
|
||||||
None => project_repository.commit(user, message, &tree, &[&parent_commit], signing_key)?,
|
None => project_repository.commit(user, message, &tree, &[&parent_commit], None)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
if run_hooks {
|
if run_hooks {
|
||||||
@ -2879,6 +2879,7 @@ pub fn move_commit_file(
|
|||||||
let new_from_tree = &repo
|
let new_from_tree = &repo
|
||||||
.find_tree(new_from_tree_oid)
|
.find_tree(new_from_tree_oid)
|
||||||
.map_err(|_error| errors::VirtualBranchError::GitObjectNotFound(new_from_tree_oid))?;
|
.map_err(|_error| errors::VirtualBranchError::GitObjectNotFound(new_from_tree_oid))?;
|
||||||
|
let change_id = from_commit.change_id();
|
||||||
let new_from_commit_oid = repo
|
let new_from_commit_oid = repo
|
||||||
.commit(
|
.commit(
|
||||||
None,
|
None,
|
||||||
@ -2887,6 +2888,7 @@ pub fn move_commit_file(
|
|||||||
&from_commit.message().to_str_lossy(),
|
&from_commit.message().to_str_lossy(),
|
||||||
new_from_tree,
|
new_from_tree,
|
||||||
&[&from_parent],
|
&[&from_parent],
|
||||||
|
change_id.as_deref(),
|
||||||
)
|
)
|
||||||
.map_err(|_error| errors::VirtualBranchError::CommitFailed)?;
|
.map_err(|_error| errors::VirtualBranchError::CommitFailed)?;
|
||||||
|
|
||||||
@ -2957,6 +2959,7 @@ pub fn move_commit_file(
|
|||||||
let parents = amend_commit
|
let parents = amend_commit
|
||||||
.parents()
|
.parents()
|
||||||
.context("failed to find head commit parents")?;
|
.context("failed to find head commit parents")?;
|
||||||
|
let change_id = amend_commit.change_id();
|
||||||
let commit_oid = project_repository
|
let commit_oid = project_repository
|
||||||
.git_repository
|
.git_repository
|
||||||
.commit(
|
.commit(
|
||||||
@ -2966,6 +2969,7 @@ pub fn move_commit_file(
|
|||||||
&amend_commit.message().to_str_lossy(),
|
&amend_commit.message().to_str_lossy(),
|
||||||
&new_tree,
|
&new_tree,
|
||||||
&parents.iter().collect::<Vec<_>>(),
|
&parents.iter().collect::<Vec<_>>(),
|
||||||
|
change_id.as_deref(),
|
||||||
)
|
)
|
||||||
.context("failed to create commit")?;
|
.context("failed to create commit")?;
|
||||||
|
|
||||||
@ -3154,6 +3158,7 @@ pub fn amend(
|
|||||||
&amend_commit.message().to_str_lossy(),
|
&amend_commit.message().to_str_lossy(),
|
||||||
&new_tree,
|
&new_tree,
|
||||||
&parents.iter().collect::<Vec<_>>(),
|
&parents.iter().collect::<Vec<_>>(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.context("failed to create commit")?;
|
.context("failed to create commit")?;
|
||||||
|
|
||||||
@ -3501,6 +3506,8 @@ fn cherry_rebase_group(
|
|||||||
.find_tree(merge_tree_oid)
|
.find_tree(merge_tree_oid)
|
||||||
.context("failed to find merge tree")?;
|
.context("failed to find merge tree")?;
|
||||||
|
|
||||||
|
let change_id = to_rebase.change_id();
|
||||||
|
|
||||||
let commit_oid = project_repository
|
let commit_oid = project_repository
|
||||||
.git_repository
|
.git_repository
|
||||||
.commit(
|
.commit(
|
||||||
@ -3510,6 +3517,7 @@ fn cherry_rebase_group(
|
|||||||
&to_rebase.message().to_str_lossy(),
|
&to_rebase.message().to_str_lossy(),
|
||||||
&merge_tree,
|
&merge_tree,
|
||||||
&[&head],
|
&[&head],
|
||||||
|
change_id.as_deref(),
|
||||||
)
|
)
|
||||||
.context("failed to create commit")?;
|
.context("failed to create commit")?;
|
||||||
|
|
||||||
@ -3524,60 +3532,6 @@ fn cherry_rebase_group(
|
|||||||
Ok(Some(new_head_id))
|
Ok(Some(new_head_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
// runs a simple libgit2 based in-memory rebase on a commit range onto a target commit
|
|
||||||
// possibly not used in favor of cherry_rebase
|
|
||||||
pub fn simple_rebase(
|
|
||||||
project_repository: &project_repository::Repository,
|
|
||||||
target_commit_oid: git::Oid,
|
|
||||||
start_commit_oid: git::Oid,
|
|
||||||
end_commit_oid: git::Oid,
|
|
||||||
) -> Result<Option<git::Oid>, anyhow::Error> {
|
|
||||||
let repo = &project_repository.git_repository;
|
|
||||||
let (_, committer) = project_repository.git_signatures(None)?;
|
|
||||||
|
|
||||||
let mut rebase_options = git2::RebaseOptions::new();
|
|
||||||
rebase_options.quiet(true);
|
|
||||||
rebase_options.inmemory(true);
|
|
||||||
let mut rebase = repo
|
|
||||||
.rebase(
|
|
||||||
Some(end_commit_oid),
|
|
||||||
Some(start_commit_oid),
|
|
||||||
Some(target_commit_oid),
|
|
||||||
Some(&mut rebase_options),
|
|
||||||
)
|
|
||||||
.context("failed to rebase")?;
|
|
||||||
|
|
||||||
let mut rebase_success = true;
|
|
||||||
// check to see if these commits have already been pushed
|
|
||||||
let mut last_rebase_head = target_commit_oid;
|
|
||||||
while rebase.next().is_some() {
|
|
||||||
let index = rebase
|
|
||||||
.inmemory_index()
|
|
||||||
.context("failed to get inmemory index")?;
|
|
||||||
if index.has_conflicts() {
|
|
||||||
rebase_success = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(commit_id) = rebase.commit(None, &committer.clone().into(), None) {
|
|
||||||
last_rebase_head = commit_id.into();
|
|
||||||
} else {
|
|
||||||
rebase_success = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if rebase_success {
|
|
||||||
// rebase worked out, rewrite the branch head
|
|
||||||
rebase.finish(None).context("failed to finish rebase")?;
|
|
||||||
Ok(Some(last_rebase_head))
|
|
||||||
} else {
|
|
||||||
// rebase failed, do a merge commit
|
|
||||||
rebase.abort().context("failed to abort rebase")?;
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cherry_pick(
|
pub fn cherry_pick(
|
||||||
project_repository: &project_repository::Repository,
|
project_repository: &project_repository::Repository,
|
||||||
branch_id: &BranchId,
|
branch_id: &BranchId,
|
||||||
@ -3661,6 +3615,7 @@ pub fn cherry_pick(
|
|||||||
"wip cherry picking commit",
|
"wip cherry picking commit",
|
||||||
&wip_tree,
|
&wip_tree,
|
||||||
&[&branch_head_commit],
|
&[&branch_head_commit],
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.context("failed to commit wip work")?;
|
.context("failed to commit wip work")?;
|
||||||
project_repository
|
project_repository
|
||||||
@ -3724,6 +3679,7 @@ pub fn cherry_pick(
|
|||||||
.find_commit(branch.head)
|
.find_commit(branch.head)
|
||||||
.context("failed to find branch head commit")?;
|
.context("failed to find branch head commit")?;
|
||||||
|
|
||||||
|
let change_id = target_commit.change_id();
|
||||||
let commit_oid = project_repository
|
let commit_oid = project_repository
|
||||||
.git_repository
|
.git_repository
|
||||||
.commit(
|
.commit(
|
||||||
@ -3733,6 +3689,7 @@ pub fn cherry_pick(
|
|||||||
&target_commit.message().to_str_lossy(),
|
&target_commit.message().to_str_lossy(),
|
||||||
&merge_tree,
|
&merge_tree,
|
||||||
&[&branch_head_commit],
|
&[&branch_head_commit],
|
||||||
|
change_id.as_deref(),
|
||||||
)
|
)
|
||||||
.context("failed to create commit")?;
|
.context("failed to create commit")?;
|
||||||
|
|
||||||
@ -3848,6 +3805,9 @@ pub fn squash(
|
|||||||
.parents()
|
.parents()
|
||||||
.context("failed to find head commit parents")?;
|
.context("failed to find head commit parents")?;
|
||||||
|
|
||||||
|
// use the squash commit's change id
|
||||||
|
let change_id = commit_to_squash.change_id();
|
||||||
|
|
||||||
let new_commit_oid = project_repository
|
let new_commit_oid = project_repository
|
||||||
.git_repository
|
.git_repository
|
||||||
.commit(
|
.commit(
|
||||||
@ -3861,6 +3821,7 @@ pub fn squash(
|
|||||||
),
|
),
|
||||||
&commit_to_squash.tree().context("failed to find tree")?,
|
&commit_to_squash.tree().context("failed to find tree")?,
|
||||||
&parents.iter().collect::<Vec<_>>(),
|
&parents.iter().collect::<Vec<_>>(),
|
||||||
|
change_id.as_deref(),
|
||||||
)
|
)
|
||||||
.context("failed to commit")?;
|
.context("failed to commit")?;
|
||||||
|
|
||||||
@ -3971,6 +3932,8 @@ pub fn update_commit_message(
|
|||||||
.parents()
|
.parents()
|
||||||
.context("failed to find head commit parents")?;
|
.context("failed to find head commit parents")?;
|
||||||
|
|
||||||
|
let change_id = target_commit.change_id();
|
||||||
|
|
||||||
let new_commit_oid = project_repository
|
let new_commit_oid = project_repository
|
||||||
.git_repository
|
.git_repository
|
||||||
.commit(
|
.commit(
|
||||||
@ -3980,6 +3943,7 @@ pub fn update_commit_message(
|
|||||||
message,
|
message,
|
||||||
&target_commit.tree().context("failed to find tree")?,
|
&target_commit.tree().context("failed to find tree")?,
|
||||||
&parents.iter().collect::<Vec<_>>(),
|
&parents.iter().collect::<Vec<_>>(),
|
||||||
|
change_id.as_deref(),
|
||||||
)
|
)
|
||||||
.context("failed to commit")?;
|
.context("failed to commit")?;
|
||||||
|
|
||||||
@ -4016,7 +3980,6 @@ pub fn move_commit(
|
|||||||
target_branch_id: &BranchId,
|
target_branch_id: &BranchId,
|
||||||
commit_oid: git::Oid,
|
commit_oid: git::Oid,
|
||||||
user: Option<&users::User>,
|
user: Option<&users::User>,
|
||||||
signing_key: Option<&keys::PrivateKey>,
|
|
||||||
) -> Result<(), errors::MoveCommitError> {
|
) -> Result<(), errors::MoveCommitError> {
|
||||||
if project_repository.is_resolving() {
|
if project_repository.is_resolving() {
|
||||||
return Err(errors::MoveCommitError::Conflicted(
|
return Err(errors::MoveCommitError::Conflicted(
|
||||||
@ -4163,6 +4126,7 @@ pub fn move_commit(
|
|||||||
.find_tree(new_destination_tree_oid)
|
.find_tree(new_destination_tree_oid)
|
||||||
.context("failed to find tree")?;
|
.context("failed to find tree")?;
|
||||||
|
|
||||||
|
let change_id = source_branch_head.change_id();
|
||||||
let new_destination_head_oid = project_repository
|
let new_destination_head_oid = project_repository
|
||||||
.commit(
|
.commit(
|
||||||
user,
|
user,
|
||||||
@ -4172,7 +4136,7 @@ pub fn move_commit(
|
|||||||
.git_repository
|
.git_repository
|
||||||
.find_commit(destination_branch.head)
|
.find_commit(destination_branch.head)
|
||||||
.context("failed to get dst branch head commit")?],
|
.context("failed to get dst branch head commit")?],
|
||||||
signing_key,
|
change_id.as_deref(),
|
||||||
)
|
)
|
||||||
.context("failed to commit")?;
|
.context("failed to commit")?;
|
||||||
|
|
||||||
@ -4189,7 +4153,6 @@ pub fn move_commit(
|
|||||||
pub fn create_virtual_branch_from_branch(
|
pub fn create_virtual_branch_from_branch(
|
||||||
project_repository: &project_repository::Repository,
|
project_repository: &project_repository::Repository,
|
||||||
upstream: &git::Refname,
|
upstream: &git::Refname,
|
||||||
signing_key: Option<&keys::PrivateKey>,
|
|
||||||
user: Option<&users::User>,
|
user: Option<&users::User>,
|
||||||
) -> Result<BranchId, errors::CreateVirtualBranchFromBranchError> {
|
) -> Result<BranchId, errors::CreateVirtualBranchFromBranchError> {
|
||||||
if !matches!(upstream, git::Refname::Local(_) | git::Refname::Remote(_)) {
|
if !matches!(upstream, git::Refname::Local(_) | git::Refname::Remote(_)) {
|
||||||
@ -4333,7 +4296,7 @@ pub fn create_virtual_branch_from_branch(
|
|||||||
.project()
|
.project()
|
||||||
.snapshot_branch_creation(branch_name);
|
.snapshot_branch_creation(branch_name);
|
||||||
|
|
||||||
match apply_branch(project_repository, &branch.id, signing_key, user) {
|
match apply_branch(project_repository, &branch.id, user) {
|
||||||
Ok(()) => Ok(branch.id),
|
Ok(()) => Ok(branch.id),
|
||||||
Err(errors::ApplyBranchError::BranchConflicts(_)) => {
|
Err(errors::ApplyBranchError::BranchConflicts(_)) => {
|
||||||
// if branch conflicts with the workspace, it's ok. keep it unapplied
|
// if branch conflicts with the workspace, it's ok. keep it unapplied
|
||||||
|
@ -3,14 +3,13 @@ use super::*;
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn twice() {
|
async fn twice() {
|
||||||
let data_dir = paths::data_dir();
|
let data_dir = paths::data_dir();
|
||||||
let keys = keys::Controller::from_path(data_dir.path());
|
|
||||||
let projects = projects::Controller::from_path(data_dir.path());
|
let projects = projects::Controller::from_path(data_dir.path());
|
||||||
let users = users::Controller::from_path(data_dir.path());
|
let users = users::Controller::from_path(data_dir.path());
|
||||||
let helper = git::credentials::Helper::from_path(data_dir.path());
|
let helper = git::credentials::Helper::from_path(data_dir.path());
|
||||||
|
|
||||||
let test_project = TestProject::default();
|
let test_project = TestProject::default();
|
||||||
|
|
||||||
let controller = Controller::new(projects.clone(), users, keys, helper);
|
let controller = Controller::new(projects.clone(), users, helper);
|
||||||
|
|
||||||
{
|
{
|
||||||
let project = projects
|
let project = projects
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::{fs, path, str::FromStr};
|
use std::{fs, path, str::FromStr};
|
||||||
|
|
||||||
use gitbutler_core::{
|
use gitbutler_core::{
|
||||||
git, keys,
|
git,
|
||||||
projects::{self, ProjectId},
|
projects::{self, ProjectId},
|
||||||
users,
|
users,
|
||||||
virtual_branches::{branch, errors, Controller},
|
virtual_branches::{branch, errors, Controller},
|
||||||
@ -29,7 +29,6 @@ impl Drop for Test {
|
|||||||
impl Default for Test {
|
impl Default for Test {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let data_dir = paths::data_dir();
|
let data_dir = paths::data_dir();
|
||||||
let keys = keys::Controller::from_path(data_dir.path());
|
|
||||||
let projects = projects::Controller::from_path(data_dir.path());
|
let projects = projects::Controller::from_path(data_dir.path());
|
||||||
let users = users::Controller::from_path(data_dir.path());
|
let users = users::Controller::from_path(data_dir.path());
|
||||||
let helper = git::credentials::Helper::from_path(data_dir.path());
|
let helper = git::credentials::Helper::from_path(data_dir.path());
|
||||||
@ -42,7 +41,7 @@ impl Default for Test {
|
|||||||
Self {
|
Self {
|
||||||
repository: test_project,
|
repository: test_project,
|
||||||
project_id: project.id,
|
project_id: project.id,
|
||||||
controller: Controller::new(projects.clone(), users, keys, helper),
|
controller: Controller::new(projects.clone(), users, helper),
|
||||||
projects,
|
projects,
|
||||||
data_dir: Some(data_dir),
|
data_dir: Some(data_dir),
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ async fn move_file_down() {
|
|||||||
.create_commit(project_id, &branch_id, "commit one", None, false)
|
.create_commit(project_id, &branch_id, "commit one", None, false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let commit1 = repository.find_commit(commit1_id).unwrap();
|
||||||
|
|
||||||
// create commit
|
// create commit
|
||||||
fs::write(repository.path().join("file2.txt"), "content2").unwrap();
|
fs::write(repository.path().join("file2.txt"), "content2").unwrap();
|
||||||
@ -33,6 +34,7 @@ async fn move_file_down() {
|
|||||||
.create_commit(project_id, &branch_id, "commit two", None, false)
|
.create_commit(project_id, &branch_id, "commit two", None, false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let commit2 = repository.find_commit(commit2_id).unwrap();
|
||||||
|
|
||||||
// amend another hunk
|
// amend another hunk
|
||||||
let to_amend: branch::BranchOwnershipClaims = "file2.txt:1-2".parse().unwrap();
|
let to_amend: branch::BranchOwnershipClaims = "file2.txt:1-2".parse().unwrap();
|
||||||
@ -50,6 +52,13 @@ async fn move_file_down() {
|
|||||||
.find(|b| b.id == branch_id)
|
.find(|b| b.id == branch_id)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
// shas changed but change_id is the same
|
||||||
|
assert_eq!(&commit1.change_id(), &branch.commits[1].change_id);
|
||||||
|
assert_ne!(&commit1.id(), &branch.commits[1].id);
|
||||||
|
assert_eq!(&commit2.change_id(), &branch.commits[0].change_id);
|
||||||
|
assert_ne!(&commit2.id(), &branch.commits[0].id);
|
||||||
|
|
||||||
|
assert_eq!(branch.commits[0].files.len(), 1);
|
||||||
assert_eq!(branch.commits.len(), 2);
|
assert_eq!(branch.commits.len(), 2);
|
||||||
assert_eq!(branch.commits[0].files.len(), 1);
|
assert_eq!(branch.commits[0].files.len(), 1);
|
||||||
assert_eq!(branch.commits[1].files.len(), 2); // this now has both file changes
|
assert_eq!(branch.commits[1].files.len(), 2); // this now has both file changes
|
||||||
|
@ -42,6 +42,8 @@ async fn head() {
|
|||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
|
let commit_three = repository.find_commit(commit_three_oid).unwrap();
|
||||||
|
let before_change_id = &commit_three.change_id();
|
||||||
|
|
||||||
controller
|
controller
|
||||||
.update_commit_message(
|
.update_commit_message(
|
||||||
@ -68,6 +70,13 @@ async fn head() {
|
|||||||
.map(|c| c.description.clone())
|
.map(|c| c.description.clone())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// get the last commit
|
||||||
|
let commit = repository.find_commit(branch.head).unwrap();
|
||||||
|
|
||||||
|
// make sure the SHA changed, but the change ID did not
|
||||||
|
assert_ne!(&commit_three.id(), &commit.id());
|
||||||
|
assert_eq!(before_change_id, &commit.change_id());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
descriptions,
|
descriptions,
|
||||||
vec!["commit three updated", "commit two", "commit one"]
|
vec!["commit three updated", "commit two", "commit one"]
|
||||||
|
@ -64,7 +64,6 @@ fn commit_on_branch_then_change_file_then_get_status() -> Result<()> {
|
|||||||
"test commit",
|
"test commit",
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -88,55 +87,6 @@ fn commit_on_branch_then_change_file_then_get_status() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn signed_commit() -> Result<()> {
|
|
||||||
let suite = Suite::default();
|
|
||||||
let Case {
|
|
||||||
project,
|
|
||||||
project_repository,
|
|
||||||
..
|
|
||||||
} = &suite.new_case_with_files(HashMap::from([
|
|
||||||
(PathBuf::from("test.txt"), "line1\nline2\nline3\nline4\n"),
|
|
||||||
(PathBuf::from("test2.txt"), "line5\nline6\nline7\nline8\n"),
|
|
||||||
]));
|
|
||||||
|
|
||||||
set_test_target(project_repository)?;
|
|
||||||
|
|
||||||
let branch1_id = create_virtual_branch(project_repository, &BranchCreateRequest::default())
|
|
||||||
.expect("failed to create virtual branch")
|
|
||||||
.id;
|
|
||||||
|
|
||||||
std::fs::write(
|
|
||||||
Path::new(&project.path).join("test.txt"),
|
|
||||||
"line0\nline1\nline2\nline3\nline4\n",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut config = project_repository
|
|
||||||
.git_repository
|
|
||||||
.config()
|
|
||||||
.with_context(|| "failed to get config")?;
|
|
||||||
config.set_str("gitbutler.signCommits", "true")?;
|
|
||||||
|
|
||||||
// commit
|
|
||||||
commit(
|
|
||||||
project_repository,
|
|
||||||
&branch1_id,
|
|
||||||
"test commit",
|
|
||||||
None,
|
|
||||||
Some(suite.keys.get_or_create()?).as_ref(),
|
|
||||||
None,
|
|
||||||
false,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let (branches, _) = virtual_branches::list_virtual_branches(project_repository).unwrap();
|
|
||||||
let commit_id = &branches[0].commits[0].id;
|
|
||||||
let commit_obj = project_repository.git_repository.find_commit(*commit_id)?;
|
|
||||||
// check the raw_header contains the string "SSH SIGNATURE"
|
|
||||||
assert!(commit_obj.raw_header().unwrap().contains("SSH SIGNATURE"));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn track_binary_files() -> Result<()> {
|
fn track_binary_files() -> Result<()> {
|
||||||
let suite = Suite::default();
|
let suite = Suite::default();
|
||||||
@ -212,7 +162,6 @@ fn track_binary_files() -> Result<()> {
|
|||||||
"test commit",
|
"test commit",
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -244,7 +193,6 @@ fn track_binary_files() -> Result<()> {
|
|||||||
"test commit",
|
"test commit",
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -718,6 +666,89 @@ fn add_new_hunk_to_the_end() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn commit_id_can_be_generated_or_specified() -> Result<()> {
|
||||||
|
let suite = Suite::default();
|
||||||
|
let Case {
|
||||||
|
project_repository,
|
||||||
|
project,
|
||||||
|
..
|
||||||
|
} = &suite.new_case();
|
||||||
|
|
||||||
|
let file_path = Path::new("test.txt");
|
||||||
|
std::fs::write(
|
||||||
|
Path::new(&project.path).join(file_path),
|
||||||
|
"line1\nline2\nline3\nline4\n",
|
||||||
|
)?;
|
||||||
|
commit_all(&project_repository.git_repository);
|
||||||
|
|
||||||
|
// lets make sure a change id is generated
|
||||||
|
let target_oid = project_repository
|
||||||
|
.git_repository
|
||||||
|
.head()
|
||||||
|
.unwrap()
|
||||||
|
.target()
|
||||||
|
.unwrap();
|
||||||
|
let target = project_repository
|
||||||
|
.git_repository
|
||||||
|
.find_commit(target_oid)
|
||||||
|
.unwrap();
|
||||||
|
let change_id = target.change_id();
|
||||||
|
|
||||||
|
// make sure we created a change-id
|
||||||
|
assert!(change_id.is_some());
|
||||||
|
|
||||||
|
// ok, make another change and specify a change-id
|
||||||
|
let file_path = Path::new("test.txt");
|
||||||
|
std::fs::write(
|
||||||
|
Path::new(&project.path).join(file_path),
|
||||||
|
"line1\nline2\nline3\nline4\nline5\n",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let repository = &project_repository.git_repository;
|
||||||
|
let mut index = repository.index().expect("failed to get index");
|
||||||
|
index
|
||||||
|
.add_all(["."], git2::IndexAddOption::DEFAULT, None)
|
||||||
|
.expect("failed to add all");
|
||||||
|
index.write().expect("failed to write index");
|
||||||
|
let oid = index.write_tree().expect("failed to write tree");
|
||||||
|
let signature = gitbutler_core::git::Signature::now("test", "test@email.com").unwrap();
|
||||||
|
let head = repository.head().expect("failed to get head");
|
||||||
|
repository
|
||||||
|
.commit(
|
||||||
|
Some(&head.name().unwrap()),
|
||||||
|
&signature,
|
||||||
|
&signature,
|
||||||
|
"some commit",
|
||||||
|
&repository.find_tree(oid).expect("failed to find tree"),
|
||||||
|
&[&repository
|
||||||
|
.find_commit(
|
||||||
|
repository
|
||||||
|
.refname_to_id("HEAD")
|
||||||
|
.expect("failed to get head"),
|
||||||
|
)
|
||||||
|
.expect("failed to find commit")],
|
||||||
|
Some("my-change-id"),
|
||||||
|
)
|
||||||
|
.expect("failed to commit");
|
||||||
|
|
||||||
|
let target_oid = project_repository
|
||||||
|
.git_repository
|
||||||
|
.head()
|
||||||
|
.unwrap()
|
||||||
|
.target()
|
||||||
|
.unwrap();
|
||||||
|
let target = project_repository
|
||||||
|
.git_repository
|
||||||
|
.find_commit(target_oid)
|
||||||
|
.unwrap();
|
||||||
|
let change_id = target.change_id();
|
||||||
|
|
||||||
|
// the change id should be what we specified, rather than randomly generated
|
||||||
|
assert_eq!(change_id, Some("my-change-id".to_string()));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn merge_vbranch_upstream_clean_rebase() -> Result<()> {
|
fn merge_vbranch_upstream_clean_rebase() -> Result<()> {
|
||||||
let suite = Suite::default();
|
let suite = Suite::default();
|
||||||
@ -814,12 +845,7 @@ fn merge_vbranch_upstream_clean_rebase() -> Result<()> {
|
|||||||
assert_eq!(branch1.commits.len(), 1);
|
assert_eq!(branch1.commits.len(), 1);
|
||||||
// assert_eq!(branch1.upstream.as_ref().unwrap().commits.len(), 1);
|
// assert_eq!(branch1.upstream.as_ref().unwrap().commits.len(), 1);
|
||||||
|
|
||||||
merge_virtual_branch_upstream(
|
merge_virtual_branch_upstream(project_repository, &branch1.id, None)?;
|
||||||
project_repository,
|
|
||||||
&branch1.id,
|
|
||||||
Some(suite.keys.get_or_create()?).as_ref(),
|
|
||||||
None,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let (branches, _) = virtual_branches::list_virtual_branches(project_repository)?;
|
let (branches, _) = virtual_branches::list_virtual_branches(project_repository)?;
|
||||||
let branch1 = &branches[0];
|
let branch1 = &branches[0];
|
||||||
@ -934,7 +960,7 @@ fn merge_vbranch_upstream_conflict() -> Result<()> {
|
|||||||
assert_eq!(branch1.commits.len(), 1);
|
assert_eq!(branch1.commits.len(), 1);
|
||||||
// assert_eq!(branch1.upstream.as_ref().unwrap().commits.len(), 1);
|
// assert_eq!(branch1.upstream.as_ref().unwrap().commits.len(), 1);
|
||||||
|
|
||||||
merge_virtual_branch_upstream(project_repository, &branch1.id, None, None)?;
|
merge_virtual_branch_upstream(project_repository, &branch1.id, None)?;
|
||||||
|
|
||||||
let (branches, _) = virtual_branches::list_virtual_branches(project_repository)?;
|
let (branches, _) = virtual_branches::list_virtual_branches(project_repository)?;
|
||||||
let branch1 = &branches[0];
|
let branch1 = &branches[0];
|
||||||
@ -966,7 +992,6 @@ fn merge_vbranch_upstream_conflict() -> Result<()> {
|
|||||||
"fix merge conflict",
|
"fix merge conflict",
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -1098,7 +1123,7 @@ fn unapply_branch() -> Result<()> {
|
|||||||
assert_eq!(branch.files.len(), 1);
|
assert_eq!(branch.files.len(), 1);
|
||||||
assert!(!branch.active);
|
assert!(!branch.active);
|
||||||
|
|
||||||
apply_branch(project_repository, &branch1_id, None, None)?;
|
apply_branch(project_repository, &branch1_id, None)?;
|
||||||
let contents = std::fs::read(Path::new(&project.path).join(file_path))?;
|
let contents = std::fs::read(Path::new(&project.path).join(file_path))?;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"line1\nline2\nline3\nline4\nbranch1\n",
|
"line1\nline2\nline3\nline4\nbranch1\n",
|
||||||
@ -1171,11 +1196,11 @@ fn apply_unapply_added_deleted_files() -> Result<()> {
|
|||||||
// check that file3 is gone
|
// check that file3 is gone
|
||||||
assert!(!Path::new(&project.path).join(file_path3).exists());
|
assert!(!Path::new(&project.path).join(file_path3).exists());
|
||||||
|
|
||||||
apply_branch(project_repository, &branch2_id, None, None)?;
|
apply_branch(project_repository, &branch2_id, None)?;
|
||||||
// check that file2 is gone
|
// check that file2 is gone
|
||||||
assert!(!Path::new(&project.path).join(file_path2).exists());
|
assert!(!Path::new(&project.path).join(file_path2).exists());
|
||||||
|
|
||||||
apply_branch(project_repository, &branch3_id, None, None)?;
|
apply_branch(project_repository, &branch3_id, None)?;
|
||||||
// check that file3 is back
|
// check that file3 is back
|
||||||
let contents = std::fs::read(Path::new(&project.path).join(file_path3))?;
|
let contents = std::fs::read(Path::new(&project.path).join(file_path3))?;
|
||||||
assert_eq!("file3\n", String::from_utf8(contents)?);
|
assert_eq!("file3\n", String::from_utf8(contents)?);
|
||||||
@ -1464,7 +1489,6 @@ fn upstream_integrated_vbranch() -> Result<()> {
|
|||||||
"integrated commit",
|
"integrated commit",
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
commit(
|
commit(
|
||||||
@ -1473,7 +1497,6 @@ fn upstream_integrated_vbranch() -> Result<()> {
|
|||||||
"non-integrated commit",
|
"non-integrated commit",
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -1534,7 +1557,6 @@ fn commit_same_hunk_twice() -> Result<()> {
|
|||||||
"first commit to test.txt",
|
"first commit to test.txt",
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -1570,7 +1592,6 @@ fn commit_same_hunk_twice() -> Result<()> {
|
|||||||
"second commit to test.txt",
|
"second commit to test.txt",
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -1629,7 +1650,6 @@ fn commit_same_file_twice() -> Result<()> {
|
|||||||
"first commit to test.txt",
|
"first commit to test.txt",
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -1665,7 +1685,6 @@ fn commit_same_file_twice() -> Result<()> {
|
|||||||
"second commit to test.txt",
|
"second commit to test.txt",
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -1724,7 +1743,6 @@ fn commit_partial_by_hunk() -> Result<()> {
|
|||||||
"first commit to test.txt",
|
"first commit to test.txt",
|
||||||
Some(&"test.txt:1-6".parse::<BranchOwnershipClaims>().unwrap()),
|
Some(&"test.txt:1-6".parse::<BranchOwnershipClaims>().unwrap()),
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -1743,7 +1761,6 @@ fn commit_partial_by_hunk() -> Result<()> {
|
|||||||
"second commit to test.txt",
|
"second commit to test.txt",
|
||||||
Some(&"test.txt:16-22".parse::<BranchOwnershipClaims>().unwrap()),
|
Some(&"test.txt:16-22".parse::<BranchOwnershipClaims>().unwrap()),
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -1802,7 +1819,6 @@ fn commit_partial_by_file() -> Result<()> {
|
|||||||
"branch1 commit",
|
"branch1 commit",
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -1870,7 +1886,6 @@ fn commit_add_and_delete_files() -> Result<()> {
|
|||||||
"branch1 commit",
|
"branch1 commit",
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -1936,7 +1951,6 @@ fn commit_executable_and_symlinks() -> Result<()> {
|
|||||||
"branch1 commit",
|
"branch1 commit",
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -2113,7 +2127,6 @@ fn pre_commit_hook_rejection() -> Result<()> {
|
|||||||
&branch1_id,
|
&branch1_id,
|
||||||
"test commit",
|
"test commit",
|
||||||
None,
|
None,
|
||||||
Some(suite.keys.get_or_create()?).as_ref(),
|
|
||||||
None,
|
None,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
@ -2178,7 +2191,6 @@ fn post_commit_hook() -> Result<()> {
|
|||||||
&branch1_id,
|
&branch1_id,
|
||||||
"test commit",
|
"test commit",
|
||||||
None,
|
None,
|
||||||
Some(suite.keys.get_or_create()?).as_ref(),
|
|
||||||
None,
|
None,
|
||||||
true,
|
true,
|
||||||
)?;
|
)?;
|
||||||
@ -2227,7 +2239,6 @@ fn commit_msg_hook_rejection() -> Result<()> {
|
|||||||
&branch1_id,
|
&branch1_id,
|
||||||
"test commit",
|
"test commit",
|
||||||
None,
|
None,
|
||||||
Some(suite.keys.get_or_create()?).as_ref(),
|
|
||||||
None,
|
None,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
@ -28,8 +28,14 @@ test-askpass-path = []
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
serde = { workspace = true, optional = true }
|
serde = { workspace = true, optional = true }
|
||||||
tokio = { workspace = true, optional = true, features = ["process", "time", "io-util", "net", "fs"] }
|
tokio = { workspace = true, optional = true, features = [
|
||||||
uuid = { workspace = true, features = [ "v4", "fast-rng" ] }
|
"process",
|
||||||
|
"time",
|
||||||
|
"io-util",
|
||||||
|
"net",
|
||||||
|
"fs",
|
||||||
|
] }
|
||||||
|
uuid = { workspace = true, features = ["v4", "fast-rng"] }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
futures = "0.3.30"
|
futures = "0.3.30"
|
||||||
sysinfo = "0.30.11"
|
sysinfo = "0.30.11"
|
||||||
|
@ -133,7 +133,6 @@ fn main() {
|
|||||||
app_handle.manage(gitbutler_core::virtual_branches::controller::Controller::new(
|
app_handle.manage(gitbutler_core::virtual_branches::controller::Controller::new(
|
||||||
projects_controller.clone(),
|
projects_controller.clone(),
|
||||||
users_controller.clone(),
|
users_controller.clone(),
|
||||||
keys_controller.clone(),
|
|
||||||
git_credentials_controller.clone(),
|
git_credentials_controller.clone(),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -173,6 +173,7 @@ pub fn test_repository() -> (gitbutler_core::git::Repository, TempDir) {
|
|||||||
"Initial commit",
|
"Initial commit",
|
||||||
&repository.find_tree(oid).expect("failed to find tree"),
|
&repository.find_tree(oid).expect("failed to find tree"),
|
||||||
&[],
|
&[],
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.expect("failed to commit");
|
.expect("failed to commit");
|
||||||
(repository, tmp)
|
(repository, tmp)
|
||||||
@ -201,6 +202,7 @@ pub fn commit_all(repository: &gitbutler_core::git::Repository) -> gitbutler_cor
|
|||||||
.expect("failed to get head"),
|
.expect("failed to get head"),
|
||||||
)
|
)
|
||||||
.expect("failed to find commit")],
|
.expect("failed to find commit")],
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.expect("failed to commit");
|
.expect("failed to commit");
|
||||||
commit_oid
|
commit_oid
|
||||||
|
@ -43,6 +43,7 @@ impl Default for TestProject {
|
|||||||
.find_tree(oid)
|
.find_tree(oid)
|
||||||
.expect("failed to find tree"),
|
.expect("failed to find tree"),
|
||||||
&[],
|
&[],
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.expect("failed to commit");
|
.expect("failed to commit");
|
||||||
|
|
||||||
@ -237,6 +238,7 @@ impl TestProject {
|
|||||||
&format!("Merge pull request from {}", branch_name),
|
&format!("Merge pull request from {}", branch_name),
|
||||||
&merge_tree,
|
&merge_tree,
|
||||||
&[&master_branch_commit, &branch_commit],
|
&[&master_branch_commit, &branch_commit],
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
@ -312,6 +314,7 @@ impl TestProject {
|
|||||||
.expect("failed to get head"),
|
.expect("failed to get head"),
|
||||||
)
|
)
|
||||||
.expect("failed to find commit")],
|
.expect("failed to find commit")],
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.expect("failed to commit")
|
.expect("failed to commit")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user