Skip to content

Conversation

@geri4
Copy link
Contributor

@geri4 geri4 commented Jan 26, 2026

Summary

  • Added XDG global gitignore fallback: go-git's LoadGlobalPatterns() only reads from core.excludesfile in ~/.gitconfig, but git also checks ~/.config/git/ignore by default
  • Fixed incorrect untracked file detection in HasChangesOtherThan(): go-git sets both Staging and Worktree to Untracked for untracked files, but the code incorrectly checked for Staging == Unmodified

Problem

Files ignored via global gitignore (~/.config/git/ignore) were reported as uncommitted changes, causing "worktree has uncommitted changes" errors when trying to create branches.

Test plan

  • Unit tests pass
  • Verified .claude/settings.local.json (ignored via ~/.config/git/ignore) is now correctly identified as ignored
  • Verified HasChangesOtherThan() returns false when only globally-gitignored files exist

Two issues fixed:

1. go-git's LoadGlobalPatterns() only reads core.excludesfile from
   ~/.gitconfig, but git also checks the default XDG location
   (~/.config/git/ignore) even without core.excludesfile being set.
   Added loadXDGGlobalPatterns() fallback for this case.

2. HasChangesOtherThan() had incorrect condition for detecting untracked
   files. go-git sets BOTH Staging and Worktree to Untracked ('?') for
   untracked files, but the code checked for Staging == Unmodified (' ').
   Simplified condition to just check Worktree == Untracked.
@geri4 geri4 requested a review from umputun as a code owner January 26, 2026 23:38
Copilot AI review requested due to automatic review settings January 26, 2026 23:38
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves gitignore handling to better match Git’s behavior and fixes detection of untracked files when checking for other changes before creating a feature branch.

Changes:

  • Extend IsIgnored to consider local .gitignore, global gitignore (including XDG default ~/.config/git/ignore), and system gitignore patterns.
  • Add an XDG-specific loader loadXDGGlobalPatterns to compensate for go-git’s lack of default XDG support.
  • Fix HasChangesOtherThan to correctly treat untracked files by checking Worktree == git.Untracked and consulting IsIgnored for them.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +300 to +303
} else {
// fallback to default XDG location if core.excludesfile not set
// git uses $XDG_CONFIG_HOME/git/ignore (defaults to ~/.config/git/ignore)
patterns = append(patterns, loadXDGGlobalPatterns()...)
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fallback to XDG global gitignore currently triggers whenever LoadGlobalPatterns returns no patterns, even when a core.excludesfile may be explicitly configured but its file is empty. Git itself only consults $XDG_CONFIG_HOME/git/ignore when core.excludesfile is unset, so this behavior can cause divergence from git by additionally applying XDG patterns when a user has intentionally configured an empty global excludes file. Consider basing the fallback condition on whether core.excludesfile is present in the global config (e.g., via config.LoadConfig(config.GlobalScope)), and only calling loadXDGGlobalPatterns when no explicit excludes file is configured.

Suggested change
} else {
// fallback to default XDG location if core.excludesfile not set
// git uses $XDG_CONFIG_HOME/git/ignore (defaults to ~/.config/git/ignore)
patterns = append(patterns, loadXDGGlobalPatterns()...)
}
// fallback to default XDG location only when no explicit core.excludesfile
// is configured in the global git config. Git uses $XDG_CONFIG_HOME/git/ignore
// (defaults to ~/.config/git/ignore) when core.excludesfile is unset.
if globalCfg, err := config.LoadConfig(config.GlobalScope); err != nil {
// If the global config cannot be loaded, treat it as having no
// explicit excludesfile and apply the XDG fallback.
patterns = append(patterns, loadXDGGlobalPatterns()...)
} else {
coreSection := globalCfg.Raw.Section("core")
if coreSection.Option("excludesfile") == "" {
patterns = append(patterns, loadXDGGlobalPatterns()...)
}

Copilot uses AI. Check for mistakes.
Comment on lines +316 to +321
// loadXDGGlobalPatterns loads gitignore patterns from the default XDG location.
// Git checks $XDG_CONFIG_HOME/git/ignore, defaulting to ~/.config/git/ignore.
func loadXDGGlobalPatterns() []gitignore.Pattern {
// check XDG_CONFIG_HOME first, fall back to ~/.config
configHome := os.Getenv("XDG_CONFIG_HOME")
if configHome == "" {
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loadXDGGlobalPatterns introduces new behavior that is not currently covered by tests, even though IsIgnored already has unit tests in pkg/git/git_test.go. To avoid regressions and environment-dependent behavior, consider adding tests that (1) set XDG_CONFIG_HOME to a temporary directory with a git/ignore file and verify that patterns are parsed correctly, and (2) exercise the XDG fallback path in IsIgnored so that globally-ignored files are validated under test rather than only manually.

Copilot uses AI. Check for mistakes.
@umputun
Copy link
Owner

umputun commented Jan 26, 2026

lgtm, thx. clean fix for the XDG global gitignore fallback.

one minor note: loadXDGGlobalPatterns doesn't have dedicated tests (Copilot mentioned this too). not blocking since the integration is tested via IsIgnored, but a test with XDG_CONFIG_HOME pointing to temp dir would be nice for completeness.

Copy link
Owner

@umputun umputun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@umputun umputun merged commit b693f5d into umputun:master Jan 27, 2026
2 checks passed
umputun added a commit that referenced this pull request Jan 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants