gitignore icon indicating copy to clipboard operation
gitignore copied to clipboard

Improve macOS Icon pattern to avoid line-ending corruption

Open silentip404 opened this issue 1 month ago • 2 comments

Reasons for making this change

The current Icon\r pattern in macOS.gitignore has a reliability issue: the invisible carriage return (CR) character is silently corrupted by editors' default behavior, causing the rule to fail even when developers believe they haven't modified the file.

Core Problem: Silent Line-Ending Conversion

The literal \r character in the .gitignore file is automatically processed by modern editors during save operations, even when file content is ultimately unchanged.

Actual corruption observed (real git diff output):

diff --git a/Global/macOS.gitignore b/Global/macOS.gitignore
--- a/Global/macOS.gitignore
+++ b/Global/macOS.gitignore
@@ -3,7 +3,8 @@
 __MACOSX/
 .AppleDouble
 .LSOverride
-Icon[^M]
+Icon[
+]

What happened:

  • Original rule: Icon followed by carriage return (displayed as ^M in some environments)
  • After corruption: CR is replaced by LF (\n), splitting the pattern into two lines
  • Result: Pattern becomes Icon[ + new line ], completely broken
  • macOS Icon\r files are no longer matched and start appearing in git status

Precise Reproduction Steps (Verified)

Test Environment:

  • Editor: VS Code (without EditorConfig enabled)
  • File: Global/macOS.gitignore

Reproduction Steps:

  1. Open .gitignore file containing the Icon\r rule
  2. Modify other content in the file (e.g., add a comment line)
  3. Save the file (Ctrl+S / Cmd+S)
  4. Undo the modification (Ctrl+Z / Cmd+Z), reverting to original state
  5. Save the file again
  6. Check changes with git diff

Expected Result:

  • File content has been reverted, there should be no diff

Actual Result:

  • The diff above appears: Icon\r is split into two lines
  • Git shows the file as modified, even though the developer made no intentional changes

Analysis:

  • VS Code silently converted \r\n during the first save
  • Even though the developer undid all content changes, the line-ending conversion persisted
  • This is VS Code's default line-ending normalization behavior
  • Developers cannot perceive this change from the editor interface (requires checking git diff)

Why This Is a Serious Problem

High stealth:

  • Developers believe they "didn't modify the file" (content was indeed reverted)
  • Git shows the file as changed (line endings were converted)
  • Difficult to diagnose that the \r character caused the issue

Wide impact:

  • Once someone opens and edits this file with VS Code
  • The rule becomes permanently broken
  • Other macOS users' Icon files start appearing in version control

No error indication:

  • No warnings that the gitignore rule is corrupted
  • File appears "normal" (no visual anomalies in the editor)

Solution: Use Character Class Pattern

Replace Icon\r with Icon[^!-~] (Icon followed by any non-printable character):

Technical Rationale:

  • [^!-~] matches all characters outside the ASCII printable range (33-126)
  • Includes all control characters: CR(13), LF(10), TAB(9), etc. (ASCII 0-32)
  • Includes DEL character (ASCII 127)

Why This Works:

  • Completely immune to line-ending processing: Character class syntax [^!-~] contains only printable characters and won't be modified by editors' line-ending normalization
  • Won't be split into multiple lines: Entire pattern is single-line printable text
  • Stable and reliable: Pattern remains unchanged regardless of how many times you save/undo
  • Visible: Clear in code reviews and diffs, no invisible characters
  • Functionally equivalent: Covers all real macOS Icon files (Icon\r)

Backward Compatibility:

  • ✅ 100% matches all legitimate macOS Icon\r files
  • ⚠️ Theoretically matches Icon + any control character combinations
    • Practical impact: negligible. macOS Finder only creates Icon\r files
    • If files with other control characters exist, they are abnormally named and should be ignored anyway
  • ✅ Won't false-match normal files:
    • Icon.png, IconSet, Icon!, Icon123 etc. are explicitly excluded
    • ! (ASCII 33) to ~ (ASCII 126) defines the printable character range boundary

Why Character Class Is the Best Solution

Comparison of possible approaches:

Approach Viable Issue
Keep Icon\r Cannot resist editor line-ending normalization, will be silently corrupted
Use Icon[\r] The \r inside brackets is still a literal byte, will be converted the same way
Use Icon? Would false-match Icon1, Icona and other normal files
Use Icon* Would match all Icon-prefixed files
Use Icon[^!-~] Pure printable character syntax, immune to line-ending processing; precise function; stable after undo

Links to documentation supporting these rule changes

N/A - This change fixes an editor compatibility issue discovered through testing. The pattern syntax is standard gitignore format documented in the Git documentation.

If this is a new template

N/A - This PR modifies an existing template (Global/macOS.gitignore), not adding a new one.

Merge and Approval Steps

  • [x] Confirm that you've read the contribution guidelines and ensured your PR aligns
  • [ ] Ensure CI is passing
  • [ ] Get a review and Approval from one of the maintainers

silentip404 avatar Nov 10 '25 14:11 silentip404

======================================  
  Icon[^!-~] Full Validation Test  
======================================  

📝 Creating test files...  

✓ Creating control character files (should be ignored):  
  - Icon\r (CR-13) ← target file  
  - Icon\t (TAB-9)  
  - Icon\n (LF-10)  
  - Icon\f (FF-12)  
  - Icon\x1B (ESC-27)  
  - Icon space (SP-32)  
  - Icon\x7F (DEL-127)  

✓ Creating printable character files (should not be ignored):  
  - Icon! (boundary-33)  
  - Icon" (34)  
  - Icon# (35)  
  - Icon$ (36)  
  - Icon~ (boundary-126)  
  - Icon.png  
  - Icon.svg  
  - Icon.ico  
  - Icon.icns  
  - Icona  
  - Icon1  
  - Icon123  
  - IconSet  
  - Icon_old  
  - Icon-new  
  - Icon.backup  
  - MyIcon  
  - icon.png (lowercase)  

✓ Creating multi-character test files:  
  - Icon\r\r (double carriage return)  
  - Icon\r\n (Windows line ending)  

📋 File list (with byte display):  
Icon\t  
Icon\n  
Icon\f  
Icon\r  
Icon\r\n  
Icon\r\r  
Icon\033  
Icon   
Icon!  
Icon\"  
Icon#  
Icon$  
Icon-new  
Icon.backup  
Icon.icns  
Icon.ico  
Icon.png  
Icon.svg  
Icon1  
Icon123  
IconSet  
Icon_old  
Icona  
Icon~  
Icon\177  
MyIcon  

🔍 Inspecting byte code of Icon\r:  
00000000: 4963 6f6e 0d                             Icon.  

======================================  
  Begin testing gitignore rule  
======================================  

[Test 1] Files that should be ignored (control characters):  
--------------------------------------  
  ✅ Icon\r (CR-13) correctly ignored  
  ✅ Icon\t (TAB-9) correctly ignored  
  ✅ Icon\n (LF-10) correctly ignored  
  ✅ Icon\f (FF-12) correctly ignored  
  ✅ Icon\x1B (ESC-27) correctly ignored  
  ✅ Icon space (SP-32) correctly ignored  
  ✅ Icon\x7F (DEL-127) correctly ignored  

[Test 2] Files that should not be ignored (printable characters):  
--------------------------------------  
  ✅ Icon! (boundary-33) not ignored  
  ✅ Icon" (34) not ignored  
  ✅ Icon# (35) not ignored  
  ✅ Icon~ (boundary-126) not ignored  
  ✅ Icon.png not ignored  
  ✅ Icon.svg not ignored  
  ✅ Icona not ignored  
  ✅ Icon1 not ignored  
  ✅ Icon123 not ignored  
  ✅ IconSet not ignored  
  ✅ Icon_old not ignored  
  ✅ Icon-new not ignored  
  ✅ MyIcon not ignored  
  ✅ icon.png (lowercase) not ignored  

[Test 3] Multi-character boundary cases:  
--------------------------------------  
  ✅ Icon\r\r (should not match) not ignored  
  ✅ Icon\r\n (should not match) not ignored  

[Test 4] git check-ignore -v detailed check:  
--------------------------------------  
.gitignore:1:Icon[^!-~] "Icon\r"  

======================================  
  Test summary  
======================================  
✅ All tests passed! Icon[^!-~] rule works as expected  

Validation summary:  
  ✓ Icon\r (macOS Icon file) correctly ignored  
  ✓ All control character files ignored  
  ✓ All normal files unaffected  
  ✓ Boundary characters (!, ~) handled correctly  

silentip404 avatar Nov 10 '25 19:11 silentip404

Ued7ed7ee7e7e7eeuee7euesueueueueueu4

Dorian Gurazdowski - Chat @ Spike [3did87]

On November 10, 2025 at 19:32 GMT, silentip404 @.***> wrote:

silentip404 left a comment (github/gitignore#4753) ====================================== Icon[^!-~] Full Validation Test ====================================== 📝 Creating test files... ✓ Creating control character files (should be ignored): - Icon\r (CR-13) ← target file - Icon\t (TAB-9) - Icon\n (LF-10) - Icon\f (FF-12) - Icon\x1B (ESC-27) - Icon space (SP-32) - Icon\x7F (DEL-127) ✓ Creating printable character files (should not be ignored): - Icon! (boundary-33) - Icon" (34) - Icon# (35) - Icon$ (36) - Icon~ (boundary-126) - Icon.png - Icon.svg - Icon.ico - Icon.icns - Icona - Icon1 - Icon123 - IconSet - Icon_old - Icon-new - Icon.backup - MyIcon - icon.png (lowercase) ✓ Creating multi-character test files: - Icon\r\r (double carriage return) - Icon\r\n (Windows line ending) 📋 File list (with byte display): Icon\t Icon\n Icon\f Icon\r Icon\r\n Icon\r\r Icon\033 Icon Icon! Icon" Icon# Icon$ Icon-new Icon.backup Icon.icns Icon.ico Icon.png Icon.svg Icon1 Icon123 IconSet Icon_old Icona Icon~ Icon\177 MyIcon 🔍 Inspecting byte code of Icon\r: 00000000: 4963 6f6e 0d Icon. ====================================== Begin testing gitignore rule ====================================== [Test 1] Files that should be ignored (control characters): -------------------------------------- ✅ Icon\r (CR-13) correctly ignored ✅ Icon\t (TAB-9) correctly ignored ✅ Icon\n (LF-10) correctly ignored ✅ Icon\f (FF-12) correctly ignored ✅ Icon\x1B (ESC-27) correctly ignored ✅ Icon space (SP-32) correctly ignored ✅ Icon\x7F (DEL-127) correctly ignored [Test 2] Files that should not be ignored (printable characters): -------------------------------------- ✅ Icon! (boundary-33) not ignored ✅ Icon" (34) not ignored ✅ Icon# (35) not ignored ✅ Icon~ (boundary-126) not ignored ✅ Icon.png not ignored ✅ Icon.svg not ignored ✅ Icona not ignored ✅ Icon1 not ignored ✅ Icon123 not ignored ✅ IconSet not ignored ✅ Icon_old not ignored ✅ Icon-new not ignored ✅ MyIcon not ignored ✅ icon.png (lowercase) not ignored [Test 3] Multi-character boundary cases: -------------------------------------- ✅ Icon\r\r (should not match) not ignored ✅ Icon\r\n (should not match) not ignored [Test 4] git check-ignore -v detailed check: -------------------------------------- .gitignore:1:Icon[^!-~] "Icon\r" ====================================== Test summary ====================================== ✅ All tests passed! Icon[^!-~] rule works as expected Validation summary: ✓ Icon\r (macOS Icon file) correctly ignored ✓ All control character files ignored ✓ All normal files unaffected ✓ Boundary characters (!, ~) handled correctly

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you are subscribed to this thread.Message ID: @.***>

doriangurazdowski0-prog avatar Nov 10 '25 20:11 doriangurazdowski0-prog