From 1a04c15b1f77f908b1dd3983a27ee49c41b3a3e5 Mon Sep 17 00:00:00 2001 From: Todd Zullinger Date: Mon, 24 May 2021 17:34:42 -0400 Subject: [PATCH] improve index mode for files with executable bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fix for #430 in bebc4f56 (Use correct mode for executable files, 2016-05-19) is incomplete. It fails (in most cases) when files have modes which are not exactly 0644 or 0755. Git only cares whether the executable bit is set (or not). Ensure the mode we set for the index is either 100644 or 100755 based on whether the executable bit is set for the file owner. Do this similarly to how upstream git does it in cache.h¹. Add a test covering various file modes to help catch regressions. Fixes #1253 ¹ https://github.com/git/git/blob/v2.31.1/cache.h#L247 --- git/index/fun.py | 3 ++- test/test_fun.py | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/git/index/fun.py b/git/index/fun.py index f40928c33..1012f4801 100644 --- a/git/index/fun.py +++ b/git/index/fun.py @@ -11,6 +11,7 @@ S_ISDIR, S_IFMT, S_IFREG, + S_IXUSR, ) import subprocess @@ -115,7 +116,7 @@ def stat_mode_to_index_mode(mode): return S_IFLNK if S_ISDIR(mode) or S_IFMT(mode) == S_IFGITLINK: # submodules return S_IFGITLINK - return S_IFREG | 0o644 | (mode & 0o111) # blobs with or without executable bit + return S_IFREG | (mode & S_IXUSR and 0o755 or 0o644) # blobs with or without executable bit def write_cache(entries: Sequence[Union[BaseIndexEntry, 'IndexEntry']], stream: IO[bytes], diff --git a/test/test_fun.py b/test/test_fun.py index a7fb8f8bc..e3d07194b 100644 --- a/test/test_fun.py +++ b/test/test_fun.py @@ -1,5 +1,5 @@ from io import BytesIO -from stat import S_IFDIR, S_IFREG, S_IFLNK +from stat import S_IFDIR, S_IFREG, S_IFLNK, S_IXUSR from os import stat import os.path as osp from unittest import SkipTest @@ -7,7 +7,8 @@ from git import Git from git.index import IndexFile from git.index.fun import ( - aggressive_tree_merge + aggressive_tree_merge, + stat_mode_to_index_mode, ) from git.objects.fun import ( traverse_tree_recursive, @@ -206,6 +207,16 @@ def assert_entries(entries, num_entries, has_conflict=False): assert_entries(aggressive_tree_merge(odb, trees), 2, True) # END handle ours, theirs + def test_stat_mode_to_index_mode(self): + modes = ( + 0o600, 0o611, 0o640, 0o641, 0o644, 0o650, 0o651, + 0o700, 0o711, 0o740, 0o744, 0o750, 0o751, 0o755, + ) + for mode in modes: + expected_mode = S_IFREG | (mode & S_IXUSR and 0o755 or 0o644) + assert stat_mode_to_index_mode(mode) == expected_mode + # END for each mode + def _assert_tree_entries(self, entries, num_trees): for entry in entries: assert len(entry) == num_trees