Zum Inhalt springen

Why `git diff` sometimes hangs for 10 seconds on Windows (it’s Defender’s behavioral analysis, and file exclusions won’t help)

Originally posted in r/git.

TL;DR: Git commands like git diff, git log, and git blame randomly stall for 10 seconds on Windows. It’s Microsoft Defender analyzing how Git spawns its pager through named pipes/PTY emulation – not scanning files, which is why exclusions don’t help. After analysis, the same commands run instantly for ~30 seconds, then stall again. The fix: disable pagers for specific commands or pipe manually. This happens in PowerShell, Git Bash, and any terminal using Git for Windows.

The Mystery

For months, I’ve been haunted by a bizarre Git performance issue on Windows 11:

  • git diff hangs for 10 seconds before showing anything
  • Running it again immediately: instant
  • Wait a minute and run it again: 10 seconds
  • But git diff | cat is ALWAYS instant

The pattern was consistent across git log, git blame, any Git command that uses a pager. After about 30 seconds of inactivity, the delay returns.

The Investigation

What Didn’t Work

The fact that git diff | cat was always instant should have been a clue – if it was file cache or scanning, piping wouldn’t help. But I went down the obvious path anyway:

  • Added git.exe to Windows Defender exclusions
  • Added less.exe to exclusions
  • Excluded entire Git installation folder
  • Excluded my repository folders

Result: No improvement. Still the same 10-second delay on first run.

The First Clue: It’s Not Just Git

Opening new tabs in Windows Terminal revealed the pattern extends beyond Git:

  • PowerShell tab: always instant
  • First Git Bash tab: 10 seconds to open
  • Second Git Bash tab immediately after: instant
  • Wait 30 seconds, open another Git Bash tab: 10 seconds again

This wasn’t about Git specifically, it was about Unix-style process creation on Windows.

The Smoking Gun: Process Patterns

Testing with different pagers proved it’s pattern-based:

# Cold start git -c core.pager=less diff # 10 seconds git -c core.pager=head diff # Instant! (cached) # After cache expires (~30 seconds) git -c core.pager=head diff # 10 seconds git -c core.pager=less diff # Instant! (cached) 

The specific pager being launched doesn’t matter. Windows Defender is analyzing the pattern of HOW Git spawns child processes, not which program gets spawned.

The Real Culprit: PTY Emulation

When Git launches a pager on Windows, it:

  1. Allocates a pseudo-terminal (PTY) pair
  2. Sets up bidirectional I/O redirection
  3. Spawns the pager with this complex console setup

This Unix-style PTY pattern triggers Microsoft Defender’s behavioral analysis. When launching terminal tabs, Git Bash needs this same PTY emulation while PowerShell uses native console APIs.

Why Exclusions Don’t Work

File exclusions prevent scanning file contents for known malware signatures.

Behavioral analysis monitors HOW processes interact: spawning patterns, I/O redirection, PTY allocation. You can’t „exclude“ a behavior pattern.

Windows Defender sees: „Process creating pseudo-terminal and spawning child with redirected I/O“ This looks suspicious. After 10 seconds of analysis, it determines: „This is safe Git behavior“. Caches approval for around 30 seconds (observed in my tests).

The 10-Second Timeout

The delay precisely matches Microsoft Defender’s documented „cloud block timeout“, the time it waits for a cloud verdict on suspicious behavior. Default: 10 seconds. [1]

Test It Yourself

Here’s the exact test showing the ~30 second cache:

$ sleep 35; time git diff; sleep 20; time git diff; sleep 35; time git diff real 0m10.105s user 0m0.015s sys 0m0.000s real 0m0.045s user 0m0.015s sys 0m0.015s real 0m10.103s user 0m0.000s sys 0m0.062s 

There’s a delay in the cold case even though there’s no changes in the repo (empty output).

After 35 seconds: slow (10s). After 20 seconds: fast (cached). After 35 seconds: slow again.

Solutions

1. Disable Pager for git diff

Configure Git to bypass the pager for diff:

git config --global pager.diff false # Then pipe manually when you need pagination: # git diff | less 

2. Manual Piping

Skip Git’s internal pager entirely:

git diff --color=always | less -R 

3. Alias for Common Commands

alias gd='git diff --color=always | less -R' 

4. Switch to WSL2

WSL2 runs in a VM where Defender doesn’t monitor internal process behavior

Update 1: Tested Git commands in PowerShell – they’re also affected by the 10-second delay:

PS > foreach ($sleep in 35, 20, 35) { Start-Sleep $sleep $t = Get-Date git diff "After {0}s wait: {1:F1}s" -f $sleep, ((Get-Date) - $t).TotalSeconds } After 35s wait: 10.2s After 20s wait: 0.1s After 35s wait: 10.3s 

This makes sense: Git for Windows still creates PTYs for pagers regardless of which shell calls it. The workarounds remain the same – disable pagers or pipe manually.

Update 2: Thanks to u/bitzap_sr for clarifying what Defender actually sees: MSYS2 implements PTYs using Windows named pipes. So from Defender’s perspective, it’s analyzing Git creating named pipes with complex bidirectional I/O and spawning a child, that’s the suspicious pattern.

Environment: Windows 11 24H2, Git for Windows 2.49.0

[1] https://learn.microsoft.com/en-us/defender-endpoint/configure-cloud-block-timeout-period-microsoft-defender-antivirus

submitted by /u/Resident_Gap_3008
[link] [comments]

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert