11use anyhow:: Context ;
22use gitbutler_error:: error:: Code ;
33use gix:: prelude:: ObjectIdExt ;
4+ use std:: io:: Write ;
45
56use crate :: { GitConfigSettings , commit:: TreeKind } ;
67
@@ -14,6 +15,13 @@ pub trait RepositoryExt {
1415 fn commit_signatures ( & self ) -> anyhow:: Result < ( gix:: actor:: Signature , gix:: actor:: Signature ) > ;
1516 /// Return labels that would be written into the conflict markers when merging blobs.
1617 fn default_merge_labels ( & self ) -> gix:: merge:: blob:: builtin_driver:: text:: Labels < ' static > ;
18+
19+ /// Return the configuration freshly loaded from `.git/config` so that it can be changed in memory,
20+ /// and possibly written back with [Self::write_local_config()].
21+ fn local_common_config_for_editing ( & self ) -> anyhow:: Result < gix:: config:: File < ' static > > ;
22+ /// Write the given `local_config` to `.git/config` of the common repository.
23+ /// Note that we never write linked worktree-local configuration.
24+ fn write_local_common_config ( & self , local_config : & gix:: config:: File ) -> anyhow:: Result < ( ) > ;
1725 /// Cherry-pick the changes in the tree of `to_rebase_commit_id` onto `new_base_commit_id`.
1826 /// This method deals with the presence of conflicting commits to select the correct trees
1927 /// for the cheery-pick merge.
@@ -105,9 +113,40 @@ impl RepositoryExt for gix::Repository {
105113 let config = repo. config_snapshot ( ) ;
106114 GitConfigSettings :: try_from_snapshot ( & config)
107115 }
116+
108117 fn set_git_settings ( & self , settings : & GitConfigSettings ) -> anyhow:: Result < ( ) > {
109118 settings. persist_to_local_config ( self )
110119 }
120+
121+ fn local_common_config_for_editing ( & self ) -> anyhow:: Result < gix:: config:: File < ' static > > {
122+ let local_config_path = self . common_dir ( ) . join ( "config" ) ;
123+ let config = gix:: config:: File :: from_path_no_includes (
124+ local_config_path. clone ( ) ,
125+ gix:: config:: Source :: Local ,
126+ ) ?;
127+ Ok ( config)
128+ }
129+
130+ fn write_local_common_config ( & self , local_config : & gix:: config:: File ) -> anyhow:: Result < ( ) > {
131+ // Note: we don't use a lock file here to not risk changing the mode, and it's what Git does.
132+ // But we lock the file so there is no raciness.
133+ let local_config_path = self . common_dir ( ) . join ( "config" ) ;
134+ let _lock = gix:: lock:: Marker :: acquire_to_hold_resource (
135+ & local_config_path,
136+ gix:: lock:: acquire:: Fail :: Immediately ,
137+ None ,
138+ ) ?;
139+ let mut config_file = std:: io:: BufWriter :: new (
140+ std:: fs:: File :: options ( )
141+ . write ( true )
142+ . truncate ( true )
143+ . create ( false )
144+ . open ( local_config_path) ?,
145+ ) ;
146+ local_config. write_to ( & mut config_file) ?;
147+ config_file. flush ( ) ?;
148+ Ok ( ( ) )
149+ }
111150}
112151
113152const GITBUTLER_COMMIT_AUTHOR_NAME : & str = "GitButler" ;
0 commit comments