Skip to content

Commit 05c26ab

Browse files
committed
A low-level way to add dependent branches with single-branch support.
This means it's just taking care of the essential pieces, and will need at least one layer on top to be suitable to be called by the various application layers.
1 parent 9598ee7 commit 05c26ab

File tree

7 files changed

+123
-21
lines changed

7 files changed

+123
-21
lines changed

crates/but-graph/tests/graph/init/utils.rs

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use but_core::ref_metadata::StackId;
22
use but_graph::VirtualBranchesTomlMetadata;
33
use but_graph::virtual_branches_legacy_types::{Stack, StackBranch, Target};
4-
use gix::Repository;
54

65
pub fn read_only_in_memory_scenario(
76
name: &str,
@@ -113,19 +112,6 @@ pub fn add_stack_with_segments(
113112
stack_id
114113
}
115114

116-
pub fn id_at<'repo>(repo: &'repo Repository, name: &str) -> (gix::Id<'repo>, gix::refs::FullName) {
117-
let mut rn = repo
118-
.find_reference(name)
119-
.expect("statically known reference exists");
120-
let id = rn.peel_to_id_in_place().expect("must be valid reference");
121-
(id, rn.inner.name)
122-
}
123-
124-
pub fn id_by_rev<'repo>(repo: &'repo gix::Repository, rev: &str) -> gix::Id<'repo> {
125-
repo.rev_parse_single(rev)
126-
.expect("well-known revspec when testing")
127-
}
128-
129115
pub fn standard_options() -> but_graph::init::Options {
130116
but_graph::init::Options {
131117
collect_tags: true,
@@ -145,3 +131,5 @@ pub fn standard_options_with_extra_target(
145131
..standard_options()
146132
}
147133
}
134+
135+
pub use but_testsupport::{id_at, id_by_rev};

crates/but-testsupport/src/lib.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Utilities for testing.
22
#![deny(rust_2018_idioms, missing_docs)]
33

4+
use gix::Repository;
45
use gix::bstr::{BStr, ByteSlice};
56
use gix::config::tree::Key;
67
pub use gix_testtools;
@@ -236,6 +237,22 @@ pub fn visualize_disk_tree_skip_dot_git(_root: &Path) -> anyhow::Result<termtree
236237
anyhow::bail!("BUG: must not run on Windows - results won't be desirable");
237238
}
238239

240+
/// Produce the id at the reference with `name` (short-name is fine), and also return the full name
241+
/// of the reference.
242+
pub fn id_at<'repo>(repo: &'repo Repository, name: &str) -> (gix::Id<'repo>, gix::refs::FullName) {
243+
let mut rn = repo
244+
.find_reference(name)
245+
.expect("statically known reference exists");
246+
let id = rn.peel_to_id_in_place().expect("must be valid reference");
247+
(id, rn.inner.name)
248+
}
249+
250+
/// Return the commit by the given `revspec`.
251+
pub fn id_by_rev<'repo>(repo: &'repo gix::Repository, revspec: &str) -> gix::Id<'repo> {
252+
repo.rev_parse_single(revspec)
253+
.expect("well-known revspec when testing")
254+
}
255+
239256
mod graph {
240257
use but_graph::SegmentMetadata;
241258
use but_graph::{EntryPoint, Graph, SegmentIndex};

crates/but-workspace/src/branch.rs

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@
131131
//! For instance, if a references is `refs/heads/gitbutler/workspace/one`, then the stash reference is in
132132
//! `<namespace: gitbutler-stashes>/refs/heads/gitbutler/workspace/one`. The reason for this is that they will not
133133
//! be garbage-collected that way.
134-
//! Note that any ref, like `refs/heads/feature` can carry a stash, so *workspace references* aren't special.
134+
//! Note that any ref, like `refs/heads/gitbutler/workspace` can carry one or more stashes, so *workspace references* aren't special.
135135
//!
136136
//! *This also makes stashes enumerable*. It's notable that it's entirely possible for stashes to become *orphaned*,
137137
//! i.e. their workspace commit (that the stash commit sits on top of) doesn't have a reference *with the same name*
@@ -530,7 +530,55 @@ impl Stack {
530530
}
531531
}
532532

533-
/// Return all stack segments within the given `stack`.
534-
pub fn stack_segments(stack: Stack) -> anyhow::Result<Vec<ref_info::ui::Segment>> {
533+
mod create_reference {
534+
/// For use in [`ReferenceAnchor`].
535+
#[derive(Debug, Clone, Copy)]
536+
pub enum ReferencePosition {
537+
/// The new dependent branch will appear above its anchor.
538+
Above,
539+
/// The new dependent branch will appear below its anchor.
540+
Below,
541+
}
542+
543+
/// For use in [`create_reference()`].
544+
#[derive(Debug, Clone)]
545+
pub enum ReferenceAnchor {
546+
/// Create the reference above this commit.
547+
/// This will fail if there already is a named segment
548+
/// at exactly this commit.
549+
AtCommit {
550+
/// The commit that `position` relates to.
551+
commit: gix::ObjectId,
552+
/// The reference points at `commit` when [`ReferencePosition::Above`],
553+
/// or the first parent of `commit` when [`ReferencePosition::Below`].
554+
position: ReferencePosition,
555+
},
556+
/// Create a dependent reference in relation to the commit pointed to by another reference.
557+
AtReference {
558+
/// The reference that `position` relates to.
559+
ref_name: gix::refs::FullName,
560+
/// The reference points at the commit of `ref_name` when [`ReferencePosition::Above`],
561+
/// or the first parent the first commit of `ref_name` when [`ReferencePosition::Below`].
562+
position: ReferencePosition,
563+
},
564+
}
565+
}
566+
pub use create_reference::*;
567+
568+
/// Create a new reference named `ref_name` in relation to `anchor`, which is expected to be contained
569+
/// in any stack in `workspace`, and place it into `repo`.
570+
/// Fail if the reference already exists *and* points somewhere else.
571+
/// Note that `workspace` is frozen in time and won't contain the newly created reference or new segment.
572+
/// `meta` will be created for `ref_name` as well, or its data update if it did exist already.
573+
///
574+
/// - if there is no managed workspace, then dependent branches must be exclusive on each commit to identify ordering
575+
/// - if there is a workspace, we store the order in workspace metadata
576+
pub fn create_reference(
577+
repo: &gix::Repository,
578+
ref_name: &gix::refs::FullNameRef,
579+
anchor: ReferenceAnchor,
580+
workspace: &but_graph::projection::Workspace<'_>,
581+
meta: &mut impl RefMetadata,
582+
) -> anyhow::Result<()> {
535583
todo!()
536584
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
mod create_reference {
2+
use crate::ref_info::with_workspace_commit::utils::named_read_only_in_memory_scenario;
3+
use but_graph::init::Options;
4+
use but_testsupport::{id_at, visualize_commit_graph_all};
5+
use but_workspace::branch::{ReferenceAnchor, ReferencePosition};
6+
7+
#[test]
8+
fn error_creating_below_first_commit() -> anyhow::Result<()> {
9+
let (repo, mut meta) =
10+
named_read_only_in_memory_scenario("with-remotes-no-workspace", "remote")?;
11+
insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r"
12+
* 89cc2d3 (A) change in A
13+
* d79bba9 new file in A
14+
* c166d42 (HEAD -> main) init-integration
15+
");
16+
17+
let graph = but_graph::Graph::from_head(&repo, &*meta, Options::limited())?;
18+
let ws = graph.to_workspace()?;
19+
let (id, ref_name) = id_at(&repo, "main");
20+
for anchor in [
21+
ReferenceAnchor::AtCommit {
22+
commit: id.detach(),
23+
position: ReferencePosition::Below,
24+
},
25+
ReferenceAnchor::AtReference {
26+
ref_name: ref_name.clone(),
27+
position: ReferencePosition::Below,
28+
},
29+
] {
30+
let err = but_workspace::branch::create_reference(
31+
&repo,
32+
// existing name, but that's not the problem
33+
ref_name.as_ref(),
34+
anchor,
35+
&ws,
36+
&mut *meta,
37+
)
38+
.unwrap_err();
39+
assert_eq!(
40+
err.to_string(),
41+
"TBD",
42+
"it's not possible to show anything before the beginning of time"
43+
);
44+
}
45+
Ok(())
46+
}
47+
}

crates/but-workspace/tests/workspace/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use but_workspace::{DiffSpec, HunkHeader, flatten_diff_specs};
22

3+
mod branch;
34
mod branch_details;
45
mod commit_engine;
56
mod flatten_diff_specs;

crates/but-workspace/tests/workspace/ui.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod changes_in_branch {
22
use crate::ref_info::with_workspace_commit::utils::read_only_in_memory_scenario;
3+
use crate::utils::r;
34
use but_graph::init::Options;
45
use but_testsupport::visualize_commit_graph_all;
56
use but_workspace::ui;
@@ -137,8 +138,4 @@ mod changes_in_branch {
137138
);
138139
Ok(())
139140
}
140-
141-
fn r(name: &str) -> &gix::refs::FullNameRef {
142-
name.try_into().expect("statically known valid ref-name")
143-
}
144141
}

crates/but-workspace/tests/workspace/utils.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,3 +361,7 @@ pub fn hunk_header(old: &str, new: &str) -> HunkHeader {
361361
new_lines,
362362
}
363363
}
364+
365+
pub fn r(name: &str) -> &gix::refs::FullNameRef {
366+
name.try_into().expect("statically known valid ref-name")
367+
}

0 commit comments

Comments
 (0)