@@ -5,13 +5,14 @@ use std::os::windows::process::CommandExt;
55use std:: { io:: Write , path:: Path , process:: Stdio , str} ;
66
77use anyhow:: { anyhow, bail, Context , Result } ;
8- use bstr:: BString ;
8+ use bstr:: { BString , ByteSlice } ;
99use git2:: { BlameOptions , Tree } ;
1010use gitbutler_branch:: { gix_to_git2_signature, SignaturePurpose } ;
1111use gitbutler_commit:: { commit_buffer:: CommitBuffer , commit_headers:: CommitHeadersV2 } ;
1212use gitbutler_config:: git:: { GbConfig , GitConfig } ;
1313use gitbutler_error:: error:: Code ;
1414use gitbutler_reference:: { Refname , RemoteRefname } ;
15+ use gix:: status:: plumbing:: index_as_worktree:: { Change , EntryStatus } ;
1516use tracing:: instrument;
1617
1718use crate :: { Config , LogUntil } ;
@@ -147,14 +148,71 @@ impl RepositoryExt for git2::Repository {
147148 }
148149 }
149150
150- /// Note that this will add all untracked files in the worktree to the index,
151- /// and write a tree from it.
152- /// The index won't be stored though.
151+ /// Note that this will add all untracked and modified files in the worktree to
152+ /// the object database, and create a tree from it.
153+ ///
154+ /// Note that right now, it doesn't skip big files.
153155 #[ instrument( level = tracing:: Level :: DEBUG , skip( self ) , err( Debug ) ) ]
154156 fn create_wd_tree ( & self ) -> Result < Tree > {
155- let mut index = self . index ( ) ?;
156- index. add_all ( [ "*" ] , git2:: IndexAddOption :: DEFAULT , None ) ?;
157- let oid = index. write_tree ( ) ?;
157+ let repo = gix:: open ( self . path ( ) ) ?;
158+ let workdir = repo. work_dir ( ) . context ( "Need non-bare repository" ) ?;
159+ let mut head_tree = repo. edit_tree ( repo. head_tree_id ( ) ?) ?;
160+ let status_changes = repo
161+ . status ( gix:: progress:: Discard ) ?
162+ . index_worktree_rewrites ( None )
163+ . index_worktree_submodules ( None )
164+ . into_index_worktree_iter ( None :: < BString > ) ?;
165+ for change in status_changes {
166+ let change = change?;
167+ match change {
168+ // modified or tracked files are unconditionally added as blob.
169+ gix:: status:: index_worktree:: iter:: Item :: Modification {
170+ rela_path,
171+ status : EntryStatus :: Change ( Change :: Type | Change :: Modification { .. } ) ,
172+ ..
173+ }
174+ | gix:: status:: index_worktree:: iter:: Item :: DirectoryContents {
175+ entry : gix:: dir:: Entry { rela_path, .. } ,
176+ ..
177+ } => {
178+ let path = workdir. join ( gix:: path:: from_bstr ( & rela_path) ) ;
179+ let Ok ( md) = std:: fs:: symlink_metadata ( & path) else {
180+ continue ;
181+ } ;
182+ let ( id, kind) = if md. is_symlink ( ) {
183+ let target = std:: fs:: read_link ( & path) . with_context ( || {
184+ format ! (
185+ "Failed to read link at '{}' for adding to the object database" ,
186+ path. display( )
187+ )
188+ } ) ?;
189+ let id = repo. write_blob ( gix:: path:: into_bstr ( target) . as_bytes ( ) ) ?;
190+ ( id, gix:: object:: tree:: EntryKind :: Link )
191+ } else {
192+ let mut file = std:: fs:: File :: open ( & path) . with_context ( || {
193+ format ! (
194+ "Could not open file at '{}' for adding it to the object database" ,
195+ path. display( )
196+ )
197+ } ) ?;
198+ let kind = if gix:: fs:: is_executable ( & md) {
199+ gix:: object:: tree:: EntryKind :: BlobExecutable
200+ } else {
201+ gix:: object:: tree:: EntryKind :: Blob
202+ } ;
203+ ( repo. write_blob_stream ( & mut file) ?, kind)
204+ } ;
205+
206+ head_tree. upsert ( rela_path, kind, id) ?;
207+ }
208+ gix:: status:: index_worktree:: iter:: Item :: Rewrite { .. } => {
209+ unreachable ! ( "disabled" )
210+ }
211+ _ => { }
212+ }
213+ }
214+
215+ let oid = git2:: Oid :: from_bytes ( head_tree. write ( ) ?. as_bytes ( ) ) ?;
158216 self . find_tree ( oid) . map ( Into :: into) . map_err ( Into :: into)
159217 }
160218
0 commit comments