@@ -102,147 +102,141 @@ pub fn check_repo_state(
102102 } ;
103103
104104 return Ok ( Some ( VcsInfo { git, path_in_vcs } ) ) ;
105+ }
105106
106- fn git (
107- pkg : & Package ,
108- gctx : & GlobalContext ,
109- src_files : & [ PathBuf ] ,
110- repo : & git2:: Repository ,
111- opts : & PackageOpts < ' _ > ,
112- ) -> CargoResult < Option < GitVcsInfo > > {
113- // This is a collection of any dirty or untracked files. This covers:
114- // - new/modified/deleted/renamed/type change (index or worktree)
115- // - untracked files (which are "new" worktree files)
116- // - ignored (in case the user has an `include` directive that
117- // conflicts with .gitignore).
118- let mut dirty_files = Vec :: new ( ) ;
119- collect_statuses ( repo, & mut dirty_files) ?;
120- // Include each submodule so that the error message can provide
121- // specifically *which* files in a submodule are modified.
122- status_submodules ( repo, & mut dirty_files) ?;
123-
124- // Find the intersection of dirty in git, and the src_files that would
125- // be packaged. This is a lazy n^2 check, but seems fine with
126- // thousands of files.
127- let cwd = gctx. cwd ( ) ;
128- let mut dirty_src_files: Vec < _ > = src_files
129- . iter ( )
130- . filter ( |src_file| dirty_files. iter ( ) . any ( |path| src_file. starts_with ( path) ) )
131- . chain ( dirty_metadata_paths ( pkg, repo) ?. iter ( ) )
132- . map ( |path| {
133- pathdiff:: diff_paths ( path, cwd)
134- . as_ref ( )
135- . unwrap_or ( path)
136- . display ( )
137- . to_string ( )
138- } )
139- . collect ( ) ;
140- let dirty = !dirty_src_files. is_empty ( ) ;
141- if !dirty || opts. allow_dirty {
142- // Must check whetherthe repo has no commit firstly, otherwise `revparse_single` would fail on bare commit repo.
143- // Due to lacking the `sha1` field, it's better not record the `GitVcsInfo` for consistency.
144- if repo. is_empty ( ) ? {
145- return Ok ( None ) ;
146- }
147- let rev_obj = repo. revparse_single ( "HEAD" ) ?;
148- Ok ( Some ( GitVcsInfo {
149- sha1 : rev_obj. id ( ) . to_string ( ) ,
150- dirty,
151- } ) )
152- } else {
153- dirty_src_files. sort_unstable ( ) ;
154- anyhow:: bail!(
155- "{} files in the working directory contain changes that were \
156- not yet committed into git:\n \n {}\n \n \
157- to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag",
158- dirty_src_files. len( ) ,
159- dirty_src_files. join( "\n " )
160- )
107+ fn git (
108+ pkg : & Package ,
109+ gctx : & GlobalContext ,
110+ src_files : & [ PathBuf ] ,
111+ repo : & git2:: Repository ,
112+ opts : & PackageOpts < ' _ > ,
113+ ) -> CargoResult < Option < GitVcsInfo > > {
114+ // This is a collection of any dirty or untracked files. This covers:
115+ // - new/modified/deleted/renamed/type change (index or worktree)
116+ // - untracked files (which are "new" worktree files)
117+ // - ignored (in case the user has an `include` directive that
118+ // conflicts with .gitignore).
119+ let mut dirty_files = Vec :: new ( ) ;
120+ collect_statuses ( repo, & mut dirty_files) ?;
121+ // Include each submodule so that the error message can provide
122+ // specifically *which* files in a submodule are modified.
123+ status_submodules ( repo, & mut dirty_files) ?;
124+
125+ // Find the intersection of dirty in git, and the src_files that would
126+ // be packaged. This is a lazy n^2 check, but seems fine with
127+ // thousands of files.
128+ let cwd = gctx. cwd ( ) ;
129+ let mut dirty_src_files: Vec < _ > = src_files
130+ . iter ( )
131+ . filter ( |src_file| dirty_files. iter ( ) . any ( |path| src_file. starts_with ( path) ) )
132+ . chain ( dirty_metadata_paths ( pkg, repo) ?. iter ( ) )
133+ . map ( |path| {
134+ pathdiff:: diff_paths ( path, cwd)
135+ . as_ref ( )
136+ . unwrap_or ( path)
137+ . display ( )
138+ . to_string ( )
139+ } )
140+ . collect ( ) ;
141+ let dirty = !dirty_src_files. is_empty ( ) ;
142+ if !dirty || opts. allow_dirty {
143+ // Must check whetherthe repo has no commit firstly, otherwise `revparse_single` would fail on bare commit repo.
144+ // Due to lacking the `sha1` field, it's better not record the `GitVcsInfo` for consistency.
145+ if repo. is_empty ( ) ? {
146+ return Ok ( None ) ;
161147 }
148+ let rev_obj = repo. revparse_single ( "HEAD" ) ?;
149+ Ok ( Some ( GitVcsInfo {
150+ sha1 : rev_obj. id ( ) . to_string ( ) ,
151+ dirty,
152+ } ) )
153+ } else {
154+ dirty_src_files. sort_unstable ( ) ;
155+ anyhow:: bail!(
156+ "{} files in the working directory contain changes that were \
157+ not yet committed into git:\n \n {}\n \n \
158+ to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag",
159+ dirty_src_files. len( ) ,
160+ dirty_src_files. join( "\n " )
161+ )
162162 }
163+ }
163164
164- /// Checks whether files at paths specified in `package.readme` and
165- /// `package.license-file` have been modified.
166- ///
167- /// This is required because those paths may link to a file outside the
168- /// current package root, but still under the git workdir, affecting the
169- /// final packaged `.crate` file.
170- fn dirty_metadata_paths ( pkg : & Package , repo : & git2:: Repository ) -> CargoResult < Vec < PathBuf > > {
171- let mut dirty_files = Vec :: new ( ) ;
172- let workdir = repo. workdir ( ) . unwrap ( ) ;
173- let root = pkg. root ( ) ;
174- let meta = pkg. manifest ( ) . metadata ( ) ;
175- for path in [ & meta. license_file , & meta. readme ] {
176- let Some ( path) = path. as_deref ( ) . map ( Path :: new) else {
177- continue ;
178- } ;
179- let abs_path = paths:: normalize_path ( & root. join ( path) ) ;
180- if paths:: strip_prefix_canonical ( abs_path. as_path ( ) , root) . is_ok ( ) {
181- // Inside package root. Don't bother checking git status.
182- continue ;
183- }
184- if let Ok ( rel_path) = paths:: strip_prefix_canonical ( abs_path. as_path ( ) , workdir) {
185- // Outside package root but under git workdir,
186- if repo. status_file ( & rel_path) ? != git2:: Status :: CURRENT {
187- dirty_files. push ( if abs_path. is_symlink ( ) {
188- // For symlinks, shows paths to symlink sources
189- workdir. join ( rel_path)
190- } else {
191- abs_path
192- } ) ;
193- }
165+ /// Checks whether files at paths specified in `package.readme` and
166+ /// `package.license-file` have been modified.
167+ ///
168+ /// This is required because those paths may link to a file outside the
169+ /// current package root, but still under the git workdir, affecting the
170+ /// final packaged `.crate` file.
171+ fn dirty_metadata_paths ( pkg : & Package , repo : & git2:: Repository ) -> CargoResult < Vec < PathBuf > > {
172+ let mut dirty_files = Vec :: new ( ) ;
173+ let workdir = repo. workdir ( ) . unwrap ( ) ;
174+ let root = pkg. root ( ) ;
175+ let meta = pkg. manifest ( ) . metadata ( ) ;
176+ for path in [ & meta. license_file , & meta. readme ] {
177+ let Some ( path) = path. as_deref ( ) . map ( Path :: new) else {
178+ continue ;
179+ } ;
180+ let abs_path = paths:: normalize_path ( & root. join ( path) ) ;
181+ if paths:: strip_prefix_canonical ( abs_path. as_path ( ) , root) . is_ok ( ) {
182+ // Inside package root. Don't bother checking git status.
183+ continue ;
184+ }
185+ if let Ok ( rel_path) = paths:: strip_prefix_canonical ( abs_path. as_path ( ) , workdir) {
186+ // Outside package root but under git workdir,
187+ if repo. status_file ( & rel_path) ? != git2:: Status :: CURRENT {
188+ dirty_files. push ( if abs_path. is_symlink ( ) {
189+ // For symlinks, shows paths to symlink sources
190+ workdir. join ( rel_path)
191+ } else {
192+ abs_path
193+ } ) ;
194194 }
195195 }
196- Ok ( dirty_files)
197196 }
197+ Ok ( dirty_files)
198+ }
198199
199- // Helper to collect dirty statuses for a single repo.
200- fn collect_statuses (
201- repo : & git2:: Repository ,
202- dirty_files : & mut Vec < PathBuf > ,
203- ) -> CargoResult < ( ) > {
204- let mut status_opts = git2:: StatusOptions :: new ( ) ;
205- // Exclude submodules, as they are being handled manually by recursing
206- // into each one so that details about specific files can be
207- // retrieved.
208- status_opts
209- . exclude_submodules ( true )
210- . include_ignored ( true )
211- . include_untracked ( true ) ;
212- let repo_statuses = repo. statuses ( Some ( & mut status_opts) ) . with_context ( || {
213- format ! (
214- "failed to retrieve git status from repo {}" ,
215- repo. path( ) . display( )
216- )
217- } ) ?;
218- let workdir = repo. workdir ( ) . unwrap ( ) ;
219- let this_dirty = repo_statuses. iter ( ) . filter_map ( |entry| {
220- let path = entry. path ( ) . expect ( "valid utf-8 path" ) ;
221- if path. ends_with ( "Cargo.lock" ) && entry. status ( ) == git2:: Status :: IGNORED {
222- // It is OK to include Cargo.lock even if it is ignored.
223- return None ;
224- }
225- // Use an absolute path, so that comparing paths is easier
226- // (particularly with submodules).
227- Some ( workdir. join ( path) )
228- } ) ;
229- dirty_files. extend ( this_dirty) ;
230- Ok ( ( ) )
231- }
200+ /// Helper to collect dirty statuses for a single repo.
201+ fn collect_statuses ( repo : & git2:: Repository , dirty_files : & mut Vec < PathBuf > ) -> CargoResult < ( ) > {
202+ let mut status_opts = git2:: StatusOptions :: new ( ) ;
203+ // Exclude submodules, as they are being handled manually by recursing
204+ // into each one so that details about specific files can be
205+ // retrieved.
206+ status_opts
207+ . exclude_submodules ( true )
208+ . include_ignored ( true )
209+ . include_untracked ( true ) ;
210+ let repo_statuses = repo. statuses ( Some ( & mut status_opts) ) . with_context ( || {
211+ format ! (
212+ "failed to retrieve git status from repo {}" ,
213+ repo. path( ) . display( )
214+ )
215+ } ) ?;
216+ let workdir = repo. workdir ( ) . unwrap ( ) ;
217+ let this_dirty = repo_statuses. iter ( ) . filter_map ( |entry| {
218+ let path = entry. path ( ) . expect ( "valid utf-8 path" ) ;
219+ if path. ends_with ( "Cargo.lock" ) && entry. status ( ) == git2:: Status :: IGNORED {
220+ // It is OK to include Cargo.lock even if it is ignored.
221+ return None ;
222+ }
223+ // Use an absolute path, so that comparing paths is easier
224+ // (particularly with submodules).
225+ Some ( workdir. join ( path) )
226+ } ) ;
227+ dirty_files. extend ( this_dirty) ;
228+ Ok ( ( ) )
229+ }
232230
233- // Helper to collect dirty statuses while recursing into submodules.
234- fn status_submodules (
235- repo : & git2:: Repository ,
236- dirty_files : & mut Vec < PathBuf > ,
237- ) -> CargoResult < ( ) > {
238- for submodule in repo. submodules ( ) ? {
239- // Ignore submodules that don't open, they are probably not initialized.
240- // If its files are required, then the verification step should fail.
241- if let Ok ( sub_repo) = submodule. open ( ) {
242- status_submodules ( & sub_repo, dirty_files) ?;
243- collect_statuses ( & sub_repo, dirty_files) ?;
244- }
231+ /// Helper to collect dirty statuses while recursing into submodules.
232+ fn status_submodules ( repo : & git2:: Repository , dirty_files : & mut Vec < PathBuf > ) -> CargoResult < ( ) > {
233+ for submodule in repo. submodules ( ) ? {
234+ // Ignore submodules that don't open, they are probably not initialized.
235+ // If its files are required, then the verification step should fail.
236+ if let Ok ( sub_repo) = submodule. open ( ) {
237+ status_submodules ( & sub_repo, dirty_files) ?;
238+ collect_statuses ( & sub_repo, dirty_files) ?;
245239 }
246- Ok ( ( ) )
247240 }
241+ Ok ( ( ) )
248242}
0 commit comments