Reputation: 9912
I have a gitlab repo that contains several shell files and also what I think are executables. (I know built executables should better not be included in repos but I didn't build the repo) (oh and a mk file too)
When I git clone the repo from a linux machine and I do
ls the/path/I/am/interested -l
I can see that both sh files and also the executables have the x permission. (and the mk file too! although I don't know if mk files should)
However when I git clone the repo from a windows bash shell (To clone it I did
git clone -c core.symlinks=true -c core.filemode=false therepo.git
and I do the same I got
The sh files keep their x permissions
The executable (and the mk file) lose the permissions.
I would like for all the files to keep their X permissions. What can I do?
For reference my git config has
core.autocrlf=input
core.fscache=true
core.symlinks=true
core.editor=nano.exe
core.autocrlf=input
core.symlinks=true
core.filemode=false
core.repositoryformatversion=0
core.filemode=false
core.bare=false
core.logallrefupdates=true
core.ignorecase=true
core.symlinks=true
core.filemode=false
P.S. Is there a way to check permissions in the Gitlab repository itself?
Upvotes: 3
Views: 4245
Reputation: 487755
I would like for all the files to keep their X permissions. What can I do?
Probably not much, except perhaps to install and use WSL (or Cygwin, but note that it does not play well with other NTFS users). Be sure to use NFTS, not FAT. (Or, just install and use Linux, perhaps in a VM.) Read on for more painful ways to work with this stuff.
P.S. Is there a way to check permissions in the Gitlab repository itself?
There is a way to inspect the permissions in each file in each commit, and to update them, even if you aren't using WSL. It's a little bit painful though.
To understand how this works, let's start with how the Unix/Linux permissions themselves really work, on a real Linux/Unix file system. There are three groupings of three "mode bits": r
ead, w
rite, and ex
ecute are the mode bits, and u
ser, g
roup, and o
ther are the groupings.
A grouped-triple of rwx
means that whoever gets this, has all three permissions on the underlying file: they can read, write, and execute the file.
A grouped-triple of ---
means that whoever gets this has no permissions at all.
Typically, the permissions are either rw-
or rwx
for the user: the file is readable and writable but not executable, or has full permissions.
Typically, for most everyone else, the permissions are either r--
or r-x
: readable, but neither writable nor executable; or read-only.
In between, members of a "group"—each file is owned by one user-ID and one group-ID—might be r--
or r-x
, meaning the group is treated like everyone not the user; or rw-
or rwx
, meaning the group is treated like the owner. This group-ID stuff has limited usefulness: most modern systems use ACLs instead. But this explains the Git modes, so it's worth mentioning.
These three groups of three permissions get presented in the order: u
ser (the file's owner), g
roup, and o
ther. (Note: o
for owner
would make more sense here, but o
got used for other
, so this became u
for user
.) Hence files are most typically either rw-r--r--
, meaning read/write for the user but read-only for everyone else, or rwxr-xr-x
, meaning read/write/execute for the user, but read-and-execute-only for everyone else.
Now, here is the tricky bit: the three groups of bits get expressed in octal, or base-8 notation, with bit 2 (value 4) representing the r
bit, bit 1 (value 2) representing the w
bit, and bit 0 (value 1) representing the x
bit. So for file whose u
ser permissions are rwx
, the value is 7 (4+2+1); for one whose u
ser permissions are rw-
, the value is 6 (4+2). For a file whose g
roup permissions are r-x
, the value is 5 (4+1); for r--
, it is 4. For a file whose o
ther permissions are r-x
, the value is again 5, and for r--
it is again 4`.
When we put these together, in this order, we get:
755 (rwxr-xr-x)
644 (rw-r--r--)
These are the two most common numbers found in lstat
system call results, on a Linux/Unix-style file system, for the permission bits.1 But any combination is technically possible: a user can set up their files so that only everyone else can read and write them, for instance (---rw-rw-
or 066
). This is not very sensible, but it is allowed.
1Note that for a directory, read permission means that you can list the names in the directory, while execute permission means that you can use the name in the directory, to try to lstat
or open
the file for instance. Hence directories that lack execute permission are frustrating: you can see the names in them, but never open any of those files, regardless of their permissions! This particular quirk has a weird and sometimes useful effect in certain security models, but mount points allow users to bypass the quirk, so one must be very careful when trying to use this model.
When Linus Torvalds wrote Git initially, he decided to support exactly this model for the permissions for each file in a Git commit. Git's index therefore stores, for each file, a mode, as one of several possible numbers, usually expressed in octal. The mode has a few extra higher-order bits that distinguish between regular files (mode 100xxx
) and symbolic links (mode 120xxx
), which he copied directly from the internal Unix/Linux model as well.
This let you store files that, when checked out, couldn't be used by yourself, only by everyone else (mode 100066
for instance) since Git would create the files with that exact mode: 066
or ---rw-rw-
. As already noted, this is basically useless in the file system; it also turned out to be useless and harmful in Git, so Git was modified, before the initial 1.0 release I think—certainly before Git 1.5—to prohibit this. The only modes allowed now for regular files are 100644
, or rw-r--r--
, and 100755
, or rwx-r-xr-x
. But the weird numbering still shows up.
If you run git ls-tree -r HEAD
, for instance, to see how the files in the HEAD
commit are stored, you will see these very same numbers. Here are some of the files in a commit in the Git repository for Git:
...
100644 blob 311841f9bed577151a81f87174ccdd6939aa3eee .gitignore
100644 blob cbeebdab7a5e2c6afec338c3534930f569c90f63 .gitmodules
...
100755 blob 9c125f298aea6863a9370c16a0f70b7cccc73b2d GIT-VERSION-GEN
100644 blob 66389ce05915d776e5c8ff49aea40e20bc40eb62 INSTALL
100644 blob d38b1b92bdb2893eb4505667375563f2d6d4086b LGPL-2.1
100644 blob c3565fc0f8f7df81232deca663bf55e14737ae97 Makefile
Note the 100755
in the middle here: that's an executable file. The remaining files are all 100644
, i.e., non-executable. These mode strings are literally how Git stores the X
permissions, and you can see them directly in any existing commit, using git ls-tree
(remember to use -r
and a commit hash ID; see the documentation for more about this).
Now, this only shows the modes of files stored in commits. The mode for a file that's not yet in a commit is stored in Git's index (aka staging area, aka cache). To see these entries, use the git ls-files
command, with the --stage
or -s
option (two spellings for the same thing). This, too, prints out the mode: 100644
or 100755
.
The mode entry in the staging entry determines what will be in the next commit. The actual mode of the file in your own file system is not relevant. However, the git add
command can change the mode stored in the staging area.
git add
affects the staging modeWhen you run git add file
, or git add -u
, or git add .
, or whatever, Git will update the index copy of each file to match the working tree copy of that file. This gets a little bit complicated:
First, suppose the file is deleted from the file system. In this case, git add
also deletes the file from the index. Everything else becomes irrelevant. But if not, we go on to maybe update the file's mode.
Suppose that core.filemode
(or core.fileMode
) is true
. Then Git looks at the mode as reported by the lstat
system call. If that mode has any x
bits set, Git will set the index's mode to 100755
. Otherwise, Git will set the index's mode to 100644
.
This relies on your lstat
system call reporting the executable permissions correctly. If your lstat
system call doesn't report correctly, you should have core.filemode
set to false
. Git now ignores the lstat
mode results: the mode of the entry in the index remains unchanged. Whatever git ls-files --stage
said before, that's still the mode.
Should you wish to change the mode, you have two options:
git add --chmod=...
will update the index from your working tree copy and set the mode you specify. This overrides the lstat
result, and also overrides core.filemode
.
git update-index
will let you override the mode. There are numerous ways to do this with git update-index
; the one that only overrides the mode is git update-index --chmod=...
.
The argument to the --chmod
command option should be either +x
or -x
. The +x
option means set the mode to 100755
and the -x
option means set the mode to 100644
.
core.filemode
Whenever you create a new repository, Git will probe the file system to see how it behaves. The precise way that Git does this is a bit peculiar, but essentially, it tries to see whether chmod
s that set and remove executable-ness on a file-system file will "stick", i.e., be reflected back by a later lstat
system call. If they will, Git sets core.filemode
to true
for you. If they won't, Git sets core.filemode
to false
for you. This ensures that Git behaves correctly.
If you override this core.filemode
setting, Git may begin behaving strangely. If you do change this setting, be sure you understand all of the above.
Upvotes: 6