Skip to content

fix(update): preserve executable bit for new files#2683

Closed
gcomneno wants to merge 1 commit into
copier-org:masterfrom
gcomneno:contrib/issue-2630-new-exec-bit-filemode-false
Closed

fix(update): preserve executable bit for new files#2683
gcomneno wants to merge 1 commit into
copier-org:masterfrom
gcomneno:contrib/issue-2630-new-exec-bit-filemode-false

Conversation

@gcomneno

@gcomneno gcomneno commented May 21, 2026

Copy link
Copy Markdown
Contributor

Fixes #2630.

When a template update introduces a new executable file in a destination repository with core.fileMode=false, Git ignores the on-disk executable bit. As a result, the user's later git add records the new file as 100644 instead of 100755.

This change handles new executable files in the same executable-bit sync path used for existing files:

  • if the destination has core.fileMode=false;
  • and the rendered file is executable;
  • and the file is not tracked yet;

Copier first creates an empty intent-to-add index entry, then updates only that entry's mode via git update-index --cacheinfo. This prepares the executable bit without staging the rendered file contents.

Added regression coverage for a template update that introduces a new executable file, verifying that:

  • the file is executable on disk after update;
  • the intermediate index entry has mode 100755;
  • the rendered file contents are not staged;
  • a later user git add records the file as 100755.

Also added coverage for new non-executable files with core.fileMode=false, ensuring Copier leaves them untracked and does not create an unnecessary intent-to-add index entry.

Tests run:

  • pytest -q tests/test_updatediff.py -k "new_files_with_filemode_false or new_non_executable_files" -rs
  • LC_ALL=C pytest -q tests/test_updatediff.py -k "executable_bit or exec_bit or new_non_executable_files" -rs
  • LC_ALL=C pytest -q tests/test_updatediff.py -rs
  • LC_ALL=C pytest -q tests/test_updatediff.py tests/test_copy.py -rs
  • ruff format --check copier/_main.py tests/test_updatediff.py
  • ruff check copier/_main.py tests/test_updatediff.py

@codecov

codecov Bot commented May 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.24%. Comparing base (6a0db30) to head (1ff0ae8).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2683      +/-   ##
==========================================
+ Coverage   97.19%   97.24%   +0.05%     
==========================================
  Files          60       60              
  Lines        7237     7299      +62     
==========================================
+ Hits         7034     7098      +64     
+ Misses        203      201       -2     
Flag Coverage Δ
unittests 97.24% <100.00%> (+0.05%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@gcomneno
gcomneno force-pushed the contrib/issue-2630-new-exec-bit-filemode-false branch from e54de87 to 1ff0ae8 Compare May 21, 2026 14:58
@sisp

sisp commented May 21, 2026

Copy link
Copy Markdown
Member

Isn't this the same as #2640? 🤔 (I haven't made up my mind about the best path forward yet because #2376 will resolve the same problem more fundamentally.)

@gcomneno

Copy link
Copy Markdown
Contributor Author

Thanks for pointing that out, @sisp.

Yes, after looking at #2640, this is essentially the same direction: create an intent-to-add entry for new executable files with core.fileMode=false, then preserve the executable bit through the later user git add.

I started from #2630 and missed the existing PR, sorry for the duplicate work. #2640 is older and more complete, so I’m happy to close this PR to avoid adding noise.

If useful, feel free to reuse anything from the additional non-executable-file regression test here, but otherwise I’ll consider this superseded by #2640 / the larger #2376 direction.

@willemkokke

willemkokke commented May 31, 2026

Copy link
Copy Markdown
Contributor

Haha, just checking in on my PR (#2640) and I got really confused for a minute ;) Great minds think alike and all that!

#2376 is definitely the better solution

@gcomneno

gcomneno commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for checking and confirming! Since #2376 is the better solution, I'm closing this PR to avoid duplicate work.

@gcomneno gcomneno closed this Jun 1, 2026
@gcomneno
gcomneno deleted the contrib/issue-2630-new-exec-bit-filemode-false branch June 1, 2026 07:35
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.

copier update loses executable bit on NEW files introduced by the template when core.fileMode=false

3 participants