1
+ use crate :: branch:: checkout:: UncommitedWorktreeChanges ;
1
2
use std:: borrow:: Cow ;
2
3
3
4
/// Returned by [function::apply()].
@@ -39,7 +40,7 @@ pub enum IntegrationMode {
39
40
MergeIfNeeded ,
40
41
}
41
42
42
- /// What to do if the applied branch conflicts?
43
+ /// What to do if the applied branch conflicts with the existing branches ?
43
44
#[ derive( Default , Debug , Copy , Clone ) ]
44
45
pub enum OnWorkspaceConflict {
45
46
/// Provide additional information about the stack that conflicted and the files involved in it,
@@ -63,18 +64,26 @@ pub enum WorkspaceReferenceNaming {
63
64
pub struct Options {
64
65
/// how the branch should be brought into the workspace.
65
66
pub integration_mode : IntegrationMode ,
66
- /// Decide how to deal with conflicts.
67
+ /// Decide how to deal with conflicts when creating the workspace merge commit to bring in each stack .
67
68
pub on_workspace_conflict : OnWorkspaceConflict ,
68
69
/// How the workspace reference should be named should it be created.
69
70
/// The creation is always needed if there are more than one branch applied.
70
71
pub workspace_reference_naming : WorkspaceReferenceNaming ,
72
+ /// How the worktree checkout should behave int eh light of uncommitted changes in the worktree.
73
+ pub uncommitted_changes : UncommitedWorktreeChanges ,
74
+ /// If not `None`, the applied branch should be merged into the workspace commit at the N'th parent position.
75
+ /// This is useful if the tip of a branc (at a specific position) was unapplied, and a segment within that branch
76
+ /// should now be re-applied, but of course, be placed at the same spot and not end up at the end of the workspace.
77
+ pub order : Option < usize > ,
71
78
}
72
79
73
80
pub ( crate ) mod function {
74
81
use super :: { Options , Outcome , WorkspaceReferenceNaming } ;
82
+ use crate :: branch:: checkout;
75
83
use crate :: ref_info:: WorkspaceExt ;
76
- use anyhow:: bail;
84
+ use anyhow:: { Context , bail} ;
77
85
use but_core:: RefMetadata ;
86
+ use but_graph:: init:: Overlay ;
78
87
use but_graph:: projection:: WorkspaceKind ;
79
88
use std:: borrow:: Cow ;
80
89
@@ -91,33 +100,64 @@ pub(crate) mod function {
91
100
///
92
101
/// On `error`, neither `repo` nor `meta` will have been changed, but `repo` may contain in-memory objects.
93
102
/// Otherwise, objects will have been persisted, and references and metadata will have been updated.
94
- pub fn apply < ' graph , T : RefMetadata > (
103
+ pub fn apply < ' graph > (
95
104
branch : & gix:: refs:: FullNameRef ,
96
105
workspace : & but_graph:: projection:: Workspace < ' graph > ,
97
106
repo : & mut gix:: Repository ,
98
- _meta : & mut T ,
107
+ meta : & mut impl RefMetadata ,
99
108
Options {
100
109
integration_mode : _,
101
110
on_workspace_conflict : _,
102
111
workspace_reference_naming,
112
+ uncommitted_changes,
113
+ order : _to_be_used_in_merge,
103
114
} : Options ,
104
115
) -> anyhow:: Result < Outcome < ' graph > > {
116
+ if repo
117
+ . try_find_reference ( branch) ?
118
+ . is_some_and ( |r| matches ! ( r. target( ) , gix:: refs:: TargetRef :: Symbolic ( _) ) )
119
+ {
120
+ bail ! (
121
+ "Refusing to apply symbolic ref '{}' due to potential ambiguity" ,
122
+ branch. shorten( )
123
+ ) ;
124
+ }
105
125
if workspace. is_reachable_from_entrypoint ( branch) {
106
- if workspace. is_entrypoint ( )
107
- || workspace
108
- . stacks
109
- . iter ( )
110
- . flat_map ( |s| s. segments . iter ( ) . filter_map ( |s| s. ref_name . as_ref ( ) ) )
111
- . any ( |rn| rn. as_ref ( ) == branch)
112
- {
113
- return Ok ( Outcome {
114
- graph : Cow :: Borrowed ( workspace. graph ) ,
115
- workspace_ref_created : false ,
116
- } ) ;
117
- }
126
+ return Ok ( Outcome {
127
+ graph : Cow :: Borrowed ( workspace. graph ) ,
128
+ workspace_ref_created : false ,
129
+ } ) ;
118
130
} else if workspace. refname_is_segment ( branch) {
119
- todo ! ( "checkout workspace so the to-be-applied branch becomes visible" )
120
- }
131
+ // This means our workspace encloses the desired branch, but it's not checked out yet.
132
+ let commit_to_checkout = workspace
133
+ . tip_commit ( )
134
+ . context ( "Workspace must point to a commit to check out" ) ?;
135
+ let current_head_commit = workspace
136
+ . graph
137
+ . lookup_entrypoint ( ) ?
138
+ . commit
139
+ . context ( "The entrypoint must have a commit - it's equal to HEAD" ) ?;
140
+ crate :: branch:: safe_checkout (
141
+ current_head_commit. id ,
142
+ commit_to_checkout. id ,
143
+ repo,
144
+ checkout:: Options {
145
+ uncommitted_changes,
146
+ } ,
147
+ ) ?;
148
+ let graph = workspace. graph . redo_traversal_with_overlay (
149
+ repo,
150
+ meta,
151
+ Overlay :: default ( ) . with_entrypoint (
152
+ commit_to_checkout. id ,
153
+ workspace. ref_name ( ) . map ( |rn| rn. to_owned ( ) ) ,
154
+ ) ,
155
+ ) ?;
156
+ return Ok ( Outcome {
157
+ graph : Cow :: Owned ( graph) ,
158
+ workspace_ref_created : false ,
159
+ } ) ;
160
+ } ;
121
161
122
162
if let Some ( ws_ref_name) = workspace. ref_name ( )
123
163
&& repo. try_find_reference ( ws_ref_name) ?. is_none ( )
@@ -133,6 +173,12 @@ pub(crate) mod function {
133
173
bail ! ( "Refusing to work on workspace whose workspace commit isn't at the top" ) ;
134
174
}
135
175
176
+ if meta. workspace_opt ( branch) ?. is_some ( ) {
177
+ bail ! (
178
+ "Refusing to apply a reference that already is a workspace: '{}'" ,
179
+ branch. shorten( )
180
+ ) ;
181
+ }
136
182
// In general, we only have to deal with one branch to apply. But when we are on an adhoc workspace,
137
183
// we need to assure both branches go into the existing or the new workspace.
138
184
let ( _workspace_ref_name_to_update, _branches_to_apply) = match & workspace. kind {
0 commit comments