Skip to content

Commit 6ab1bf0

Browse files
committed
Basic apply and unapply
The idea is to develop both at the same time so it's easier to test that 'unapply' truly goes back to the (mostly) original state.
1 parent 6ab5a68 commit 6ab1bf0

File tree

7 files changed

+157
-5
lines changed

7 files changed

+157
-5
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/// Returned by [function::apply()].
2+
#[derive(Debug)]
3+
pub struct Outcome {
4+
/// The newly created graph, useful to project a workspace and see how the workspace looks like with the branch applied.
5+
pub graph: but_graph::Graph,
6+
/// `true` if we created the given workspace ref as it didn't exist yet.
7+
pub workspace_ref_created: bool,
8+
}
9+
10+
/// How the newly applied branch should be integrated into the workspace.
11+
#[derive(Default, Debug, Copy, Clone)]
12+
pub enum IntegrationMode {
13+
/// Do nothing, just merge it into
14+
#[default]
15+
Merge,
16+
}
17+
18+
/// What to do if the applied branch conflicts?
19+
#[derive(Default, Debug, Copy, Clone)]
20+
pub enum OnWorkspaceConflict {}
21+
22+
/// Options for [function::apply()].
23+
#[derive(Default, Debug, Copy, Clone)]
24+
pub struct Options {
25+
/// how the branch should be brought into the workspace.
26+
pub integration_mode: IntegrationMode,
27+
/// Decide how to deal with conflicts.
28+
pub on_workspace_conflict: OnWorkspaceConflict,
29+
}
30+
31+
pub(crate) mod function {
32+
use super::{Options, Outcome};
33+
use but_core::RefMetadata;
34+
35+
/// Apply `branch` to the given `workspace`, and possibly create the workspace reference in `repo`
36+
/// along with its `meta`-data if it doesn't exist yet.
37+
/// Otherwise, add it to the existing `workspace`, and update its metadata accordingly.
38+
///
39+
/// On `error`, neither `repo` nor `meta` will have been changed.
40+
/// Otherwise, objects will have been persisted, and references and metadata will have been updated.
41+
pub fn apply<T: RefMetadata>(
42+
branch: &gix::refs::FullNameRef,
43+
workspace: &but_graph::projection::Workspace,
44+
repo: &mut gix::Repository,
45+
meta: &mut T,
46+
Options { integration_mode }: Options,
47+
) -> anyhow::Result<Outcome> {
48+
todo!()
49+
}
50+
}

crates/but-workspace/src/branch/create_reference.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,14 @@ pub(super) mod function {
218218
ref_name.shorten()
219219
);
220220
};
221-
position.resolve_commit(segment.commits.first().context(
222-
"BUG: empty segments aren't possible without workspace metadata",
223-
)?.into(), ws_base)?
221+
position.resolve_commit(
222+
segment
223+
.commits
224+
.first()
225+
.context("Cannot create reference on unborn branch")?
226+
.into(),
227+
ws_base,
228+
)?
224229
};
225230
(
226231
validate_id,

crates/but-workspace/src/branch/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,10 @@ impl Stack {
456456
}
457457
}
458458

459+
/// Functions and types related to applying a workspace branch.
460+
pub mod apply;
461+
pub use apply::function::apply;
462+
459463
/// related types for removing a workspace reference.
460464
pub mod remove_reference;
461465
pub use remove_reference::function::remove_reference;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env bash
2+
3+
set -eu -o pipefail
4+
5+
source "${BASH_SOURCE[0]%/*}/shared.sh"
6+
7+
### Description
8+
# A newly initialized git repository, but with a known remote that has an object.
9+
10+
git init remote
11+
(cd remote
12+
commit "M1"
13+
)
14+
15+
git init unborn
16+
(cd unborn
17+
git remote add orphan ../remote
18+
git fetch orphan
19+
)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use crate::ref_info::with_workspace_commit::utils::named_read_only_in_memory_scenario;
2+
use crate::utils::r;
3+
use but_graph::init::Options;
4+
use but_testsupport::{graph_workspace, visualize_commit_graph_all};
5+
6+
#[test]
7+
#[ignore = "TBD: idempotent"]
8+
fn apply_branch_already_in_workspace() -> anyhow::Result<()> {
9+
Ok(())
10+
}
11+
12+
#[test]
13+
#[ignore = "TBD: idempotent"]
14+
fn unapply_branch_not_in_workspace() -> anyhow::Result<()> {
15+
Ok(())
16+
}
17+
18+
#[test]
19+
fn unborn_apply_needs_base() -> anyhow::Result<()> {
20+
let (mut repo, mut meta) =
21+
named_read_only_in_memory_scenario("unborn-empty-detached-remote", "unborn")?;
22+
insta::assert_snapshot!(visualize_commit_graph_all(&mut repo)?, @"* 3183e43 (orphan/main) M1");
23+
let graph = but_graph::Graph::from_head(&repo, &*meta, Options::limited())?;
24+
let ws = graph.to_workspace()?;
25+
insta::assert_snapshot!(graph_workspace(&ws), @r"
26+
⌂:0:main <> ✓!
27+
└── ≡:0:main
28+
└── :0:main
29+
");
30+
31+
// Cannot apply branch without a base.
32+
let err = but_workspace::branch::apply(
33+
r("refs/remotes/orphan/main"),
34+
&ws,
35+
&mut repo,
36+
&mut *meta,
37+
Default::default(),
38+
)
39+
.unwrap_err();
40+
assert_eq!(err.to_string(), "Cannot create reference on unborn branch");
41+
Ok(())
42+
}
43+
44+
#[test]
45+
#[ignore = "TBD"]
46+
fn apply_branch_resting_on_base() -> anyhow::Result<()> {
47+
// THis can't work, but should fail gracefully.
48+
Ok(())
49+
}

crates/but-workspace/tests/workspace/branch/create_reference.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use but_core::ref_metadata::ValueInfo;
77
use but_graph::init::Options;
88
use but_testsupport::{graph_workspace, id_at, id_by_rev, visualize_commit_graph_all};
99
use but_workspace::branch::create_reference::{Anchor, Position::*};
10+
use std::borrow::Cow;
1011

1112
mod with_workspace {
1213
use crate::ref_info::with_workspace_commit::utils::{
@@ -1066,6 +1067,30 @@ mod with_workspace {
10661067

10671068
#[test]
10681069
fn errors() -> anyhow::Result<()> {
1070+
let (repo, mut meta) = named_read_only_in_memory_scenario("unborn-empty", "")?;
1071+
let graph = but_graph::Graph::from_head(&repo, &*meta, Options::limited())?;
1072+
let ws = graph.to_workspace()?;
1073+
insta::assert_snapshot!(graph_workspace(&ws), @r"
1074+
⌂:0:main <> ✓!
1075+
└── ≡:0:main
1076+
└── :0:main
1077+
");
1078+
1079+
// Below first in history
1080+
let new_name = r("refs/heads/does-not-matter");
1081+
let err = but_workspace::branch::create_reference(
1082+
new_name,
1083+
Anchor::AtSegment {
1084+
ref_name: Cow::Borrowed(r("refs/heads/main")),
1085+
position: Above,
1086+
},
1087+
&repo,
1088+
&ws,
1089+
&mut *meta,
1090+
)
1091+
.unwrap_err();
1092+
assert_eq!(err.to_string(), "Cannot create reference on unborn branch");
1093+
10691094
let (repo, mut meta) =
10701095
named_read_only_in_memory_scenario("with-remotes-no-workspace", "remote")?;
10711096
insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r"
@@ -1085,7 +1110,6 @@ fn errors() -> anyhow::Result<()> {
10851110
");
10861111

10871112
let (id, ref_name) = id_at(&repo, "main");
1088-
let new_name = r("refs/heads/does-not-matter");
10891113
for anchor in [
10901114
Anchor::at_id(id, Below),
10911115
Anchor::at_segment(ref_name.as_ref(), Below),
@@ -1252,7 +1276,7 @@ fn errors() -> anyhow::Result<()> {
12521276
}
12531277

12541278
#[test]
1255-
fn journey() -> anyhow::Result<()> {
1279+
fn journey_with_commits() -> anyhow::Result<()> {
12561280
let (_tmp, repo, mut meta) = named_writable_scenario("single-branch-with-3-commits")?;
12571281
insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r"
12581282
* 281da94 (HEAD -> main) 3
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
mod apply_unapply;
12
mod create_reference;
23
mod remove_reference;

0 commit comments

Comments
 (0)