@@ -206,13 +206,130 @@ fn all_hooks_skipped_multiple_priority_groups() -> Result<()> {
206206 assert ! ( stdout. contains( "priority-20" ) && stdout. contains( "Skipped" ) ) ;
207207 assert ! ( stdout. contains( "priority-30" ) && stdout. contains( "Skipped" ) ) ;
208208
209- // Regression test for #1335: only 1 get_diff call (initial baseline)
210- // Without fix: 4 calls (1 initial + 3 per priority group)
209+ // Stashed workflow + all hooks skipped => no diff calls.
211210 let stderr = String :: from_utf8_lossy ( & output. stderr ) ;
212211 let get_diff_calls = stderr. matches ( "get_diff" ) . count ( ) ;
212+ assert_eq ! (
213+ get_diff_calls, 0 ,
214+ "Expected 0 get_diff calls when all hooks skip in stashed workflow.\n \
215+ Found {get_diff_calls} get_diff calls.\n \
216+ Trace output:\n {stderr}"
217+ ) ;
218+
219+ Ok ( ( ) )
220+ }
221+
222+ /// When stashed, use `has_worktree_changes` first and fall back to `get_diff` after changes.
223+ #[ test]
224+ fn uses_has_worktree_changes_when_stashed ( ) -> Result < ( ) > {
225+ let context = TestContext :: new ( ) ;
226+ context. init_project ( ) ;
227+
228+ let cwd = context. work_dir ( ) ;
229+
230+ // Hook that modifies files (triggers modification detection)
231+ context. write_pre_commit_config ( indoc:: indoc! { r#"
232+ repos:
233+ - repo: local
234+ hooks:
235+ - id: modifier
236+ name: modifier
237+ language: system
238+ entry: python3 -c "open('file.txt', 'a').write('modified')"
239+ files: \.txt$
240+ "# } ) ;
241+
242+ cwd. child ( "file.txt" ) . write_str ( "original" ) ?;
243+ context. git_add ( "." ) ;
244+
245+ let output = context. run ( ) . env ( "RUST_LOG" , "prek::git=trace" ) . output ( ) ?;
246+
247+ // Hook should fail because it modified files
248+ assert ! (
249+ !output. status. success( ) ,
250+ "hook should fail due to file modification"
251+ ) ;
252+
253+ let stderr = String :: from_utf8_lossy ( & output. stderr ) ;
254+
255+ // First check uses has_worktree_changes; get_diff only after changes are detected.
256+ let has_worktree_changes_calls = stderr. matches ( "has_worktree_changes" ) . count ( ) ;
257+ let get_diff_calls = stderr. matches ( "get_diff" ) . count ( ) ;
258+
259+ assert ! (
260+ has_worktree_changes_calls >= 1 ,
261+ "Expected has_worktree_changes to be called for first modification check.\n \
262+ Found {has_worktree_changes_calls} has_worktree_changes calls, {get_diff_calls} get_diff calls.\n \
263+ Trace output:\n {stderr}"
264+ ) ;
265+
266+ // One get_diff call after change detection.
213267 assert_eq ! (
214268 get_diff_calls, 1 ,
215- "Expected 1 get_diff call (initial baseline) when all hooks skip, found {get_diff_calls}.\n \
269+ "Expected 1 get_diff call (to capture state after change detection).\n \
270+ Found {get_diff_calls} get_diff calls.\n \
271+ Trace output:\n {stderr}"
272+ ) ;
273+
274+ Ok ( ( ) )
275+ }
276+
277+ /// With --all-files (no stash), use full diff comparison to detect new changes.
278+ #[ test]
279+ fn uses_get_diff_when_all_files ( ) -> Result < ( ) > {
280+ let context = TestContext :: new ( ) ;
281+ context. init_project ( ) ;
282+
283+ let cwd = context. work_dir ( ) ;
284+
285+ // Hook that modifies files
286+ context. write_pre_commit_config ( indoc:: indoc! { r#"
287+ repos:
288+ - repo: local
289+ hooks:
290+ - id: modifier
291+ name: modifier
292+ language: system
293+ entry: python3 -c "open('file.txt', 'a').write('modified')"
294+ files: \.txt$
295+ "# } ) ;
296+
297+ cwd. child ( "file.txt" ) . write_str ( "original" ) ?;
298+ // Stage and commit so file is tracked, then use --all-files
299+ context. git_add ( "." ) ;
300+ context. configure_git_author ( ) ;
301+ context. git_commit ( "initial" ) ;
302+
303+ let output = context
304+ . run ( )
305+ . arg ( "--all-files" )
306+ . env ( "RUST_LOG" , "prek::git=trace" )
307+ . output ( ) ?;
308+
309+ // Hook should fail because it modified files
310+ assert ! (
311+ !output. status. success( ) ,
312+ "hook should fail due to file modification"
313+ ) ;
314+
315+ let stderr = String :: from_utf8_lossy ( & output. stderr ) ;
316+
317+ // With --all-files (no stash), should use get_diff for comparison
318+ let get_diff_calls = stderr. matches ( "get_diff" ) . count ( ) ;
319+ let has_worktree_changes_calls = stderr. matches ( "has_worktree_changes" ) . count ( ) ;
320+
321+ assert ! (
322+ get_diff_calls >= 1 ,
323+ "Expected get_diff to be called for --all-files workflow.\n \
324+ Found {get_diff_calls} get_diff calls, {has_worktree_changes_calls} has_worktree_changes calls.\n \
325+ Trace output:\n {stderr}"
326+ ) ;
327+
328+ // has_worktree_changes should NOT be used in --all-files workflow
329+ assert_eq ! (
330+ has_worktree_changes_calls, 0 ,
331+ "Expected 0 has_worktree_changes calls in --all-files workflow.\n \
332+ Found {has_worktree_changes_calls} has_worktree_changes calls.\n \
216333 Trace output:\n {stderr}"
217334 ) ;
218335
0 commit comments