11use std:: {
2- path:: { self , Path , PathBuf } ,
2+ path:: { Path , PathBuf } ,
33 time,
44} ;
55
@@ -14,7 +14,7 @@ use crate::default_true::DefaultTrue;
1414pub enum AuthKey {
1515 GitCredentialsHelper ,
1616 Local {
17- private_key_path : path :: PathBuf ,
17+ private_key_path : PathBuf ,
1818 } ,
1919 // There used to be more auth option variants that we are deprecating and replacing with this
2020 #[ serde( other) ]
@@ -78,8 +78,20 @@ pub struct Project {
7878 pub title : String ,
7979 pub description : Option < String > ,
8080 /// The worktree directory of the project's repository.
81+ // TODO: Make it optional for bare repo support!
82+ // TODO: Do not actually store it, but obtain it on the fly by using a repository!
8183 #[ serde( rename = "path" ) ]
82- pub ( crate ) worktree_dir : path:: PathBuf ,
84+ // TODO(1.0): enable the line below to clear the value from storage - we only want the git dir,
85+ // but need to remain compatible. The frontend shouldn't care, so we may need a specific type for that
86+ // which already exists, but… needs cleanup.
87+ // #[serde(skip_serializing_if = "Option::is_none")]
88+ pub ( crate ) worktree_dir : PathBuf ,
89+ /// The storage location of the Git repository itself.
90+ /// This is the only value we need to access everything related to the Git repository.
91+ ///
92+ // TODO(1.0): remove the `default` which is just needed while there is project files without it.
93+ #[ serde( default ) ]
94+ pub ( crate ) git_dir : PathBuf ,
8395 #[ serde( default ) ]
8496 pub preferred_key : AuthKey ,
8597 /// if ok_with_force_push is true, we'll not try to avoid force pushing
@@ -117,6 +129,8 @@ impl Project {
117129 worktree_dir,
118130 ..Default :: default ( )
119131 }
132+ . migrated ( )
133+ . unwrap ( )
120134 }
121135
122136 /// A special constructor needed as `worktree_dir` isn't accessible anymore.
@@ -126,6 +140,28 @@ impl Project {
126140 preferred_key,
127141 ..Default :: default ( )
128142 }
143+ . migrated ( )
144+ . unwrap ( )
145+ }
146+ }
147+
148+ impl Project {
149+ pub ( crate ) fn migrated ( mut self ) -> anyhow:: Result < Self > {
150+ self . migrate ( ) ?;
151+ Ok ( self )
152+ }
153+
154+ pub ( crate ) fn migrate ( & mut self ) -> anyhow:: Result < ( ) > {
155+ if !self . git_dir . as_os_str ( ) . is_empty ( ) {
156+ return Ok ( ( ) ) ;
157+ }
158+ let repo = gix:: open_opts ( & self . worktree_dir , gix:: open:: Options :: isolated ( ) )
159+ . context ( "BUG: worktree is supposed to be valid here for migration" ) ?;
160+ self . git_dir = repo. git_dir ( ) . to_owned ( ) ;
161+ // NOTE: we set the worktree so the frontend is happier until this usage can be reviewed,
162+ // probably for supporting bare repositories.
163+ self . worktree_dir = repo. workdir ( ) . context ( "BUG: we currently only support non-bare repos, yet this one didn't have a worktree dir" ) ?. to_owned ( ) ;
164+ Ok ( ( ) )
129165 }
130166}
131167
@@ -143,7 +179,8 @@ impl Project {
143179 } )
144180 }
145181 /// Finds an existing project by its path. Errors out if not found.
146- pub fn find_by_path ( path : & Path ) -> anyhow:: Result < Project > {
182+ // TODO: find by git-dir instead!
183+ pub fn find_by_worktree_dir ( worktree_dir : & Path ) -> anyhow:: Result < Project > {
147184 let mut projects = crate :: list ( ) ?;
148185 // Sort projects by longest pathname to shortest.
149186 // We need to do this because users might have one gitbutler project
@@ -157,10 +194,12 @@ impl Project {
157194 // longest first
158195 . reverse ( )
159196 } ) ;
160- let resolved_path = if path. is_relative ( ) {
161- path. canonicalize ( ) . context ( "Failed to canonicalize path" ) ?
197+ let resolved_path = if worktree_dir. is_relative ( ) {
198+ worktree_dir
199+ . canonicalize ( )
200+ . context ( "Failed to canonicalize path" ) ?
162201 } else {
163- path . to_path_buf ( )
202+ worktree_dir . to_path_buf ( )
164203 } ;
165204 let project = projects
166205 . into_iter ( )
@@ -176,6 +215,20 @@ impl Project {
176215 }
177216}
178217
218+ /// Repository helpers.
219+ impl Project {
220+ /// Open an isolated repository, one that didn't read options beyond `.git/config` and
221+ /// knows no environment variables.
222+ ///
223+ /// Use it for fastest-possible access, when incomplete configuration is acceptable.
224+ pub fn open_isolated ( & self ) -> anyhow:: Result < gix:: Repository > {
225+ Ok ( gix:: open_opts (
226+ & self . git_dir ,
227+ gix:: open:: Options :: isolated ( ) ,
228+ ) ?)
229+ }
230+ }
231+
179232impl Project {
180233 /// Determines if the project Operations log will be synched with the GitButHub
181234 pub fn oplog_sync_enabled ( & self ) -> bool {
@@ -207,34 +260,34 @@ impl Project {
207260 ///
208261 /// Normally this is `.git/gitbutler` in the project's repository.
209262 pub fn gb_dir ( & self ) -> PathBuf {
210- // TODO(ST): store the gitdir instead. This needs a migration to switch existing `worktree_dir` fields over.
211- self . worktree_dir . join ( ".git" ) . join ( "gitbutler" )
263+ self . git_dir . join ( "gitbutler" )
212264 }
213265
214266 pub fn snapshot_lines_threshold ( & self ) -> usize {
215267 self . snapshot_lines_threshold . unwrap_or ( 20 )
216268 }
217269
218- // TODO(ST): for bare repo support, make this optional, but store the gitdir instead.
270+ // TODO(ST): Actually remove this - people should use the `gix::Repository` for worktree handling (which makes it optional, too)
219271 pub fn worktree_dir ( & self ) -> PathBuf {
220272 self . worktree_dir . clone ( )
221273 }
222274
223275 /// Set the worktree directory to `worktree_dir`.
224- pub fn set_worktree_dir ( & mut self , worktree_dir : path:: PathBuf ) {
276+ pub fn set_worktree_dir ( & mut self , worktree_dir : PathBuf ) -> anyhow:: Result < ( ) > {
277+ let repo = gix:: open_opts ( & worktree_dir, gix:: open:: Options :: isolated ( ) ) ?;
225278 self . worktree_dir = worktree_dir;
279+ self . git_dir = repo. git_dir ( ) . to_owned ( ) ;
280+ Ok ( ( ) )
226281 }
227282
228283 /// Return the path to the directory that holds the repository data and that is associated with the current worktree.
229- // TODO(ST): store this directory in future, as everything else can be obtained from it: worktree_dir, common_dir.
230- pub fn git_dir ( & self ) -> anyhow:: Result < path:: PathBuf > {
231- let repo = gix:: open_opts ( & self . worktree_dir , gix:: open:: Options :: isolated ( ) ) ?;
232- Ok ( repo. git_dir ( ) . to_owned ( ) )
284+ pub fn git_dir ( & self ) -> & Path {
285+ & self . git_dir
233286 }
234287
235288 /// Return the path to the Git directory of the 'prime' repository, the one that holds all worktrees.
236- pub fn common_git_dir ( & self ) -> anyhow:: Result < path :: PathBuf > {
237- let repo = gix :: open_opts ( & self . worktree_dir , gix :: open :: Options :: isolated ( ) ) ?;
289+ pub fn common_git_dir ( & self ) -> anyhow:: Result < PathBuf > {
290+ let repo = self . open_isolated ( ) ?;
238291 Ok ( repo. common_dir ( ) . to_owned ( ) )
239292 }
240293}
0 commit comments