86

I frequently add bash scripts to my Git repository, and the scripts have executable permissions in the Linux filesystem prior to the git add. But after pushing the added files to a remote repository and pulling in another location, the files show up with non-executable permissions. There seem to be two ways to correct the problem:

  1. chmod u+x $script 
    git commit -am "fixing the script permissions... again..."
    
  2. git update-index --chmod=+x $script
    

Instead of fixing up the permissions every time, is there a way to have Git simply look at the file permissions on the script during git add, recognize "Hey, this here is an executable file!" and add it to the repository with the executable permissions directly?

Michael
  • 8,362
  • 6
  • 61
  • 88
Byron Hawkins
  • 2,536
  • 2
  • 24
  • 34
  • Is the other location using `filemode=false`? – ezod Jan 11 '13 at 00:25
  • Both locations are running linux, so there's no `filemode=false` issue. I think this is the normal behavior of git--I'm just looking for some way to skip the manual fixup step. – Byron Hawkins Jan 11 '13 at 05:07
  • The githooks manpage has pointers. – jthill Jan 13 '13 at 09:10
  • Well, it looks like quite an effort to identify the additions of the last commit, then check whether they are executable, then execute the fixup. Any change in the output format of `git log` would break the hook, etc. It seems more like a bug to me, dropping file permissions on the way into the repository. I don't see how it's different from dropping characters out of the file, or changing the filename. But a workaround has to be tight, otherwise the manual fixup turns out to be a much better solution. – Byron Hawkins Jan 13 '13 at 17:47
  • @ByronHawkins You should really check the `filemode=false` (it's not just for non-linux issues). This setting tells Git whether or not to track the executable bit of the file you're committing, or to ignore it, which sounds exactly like the problem you're having. `git config core.filemode true` will set it for you. Be sure to check the setting on both machines involved. – Jonathan Wren Jan 22 '13 at 07:34
  • @Duotrigesimal Unfortunately, `filemode=false` does precisely the opposite of what I want--this configuration option tells git to ignore differences between the working tree and the index. I want git to _recognize_ the differences, not ignore them. I currently have `filemode=true`, and git is still ignoring the filemode from my working tree. I want git to look at the filemode in my working tree and take it like it is. – Byron Hawkins Jan 24 '13 at 23:05
  • @ByronHawkins Sorry, my comment was poorly worded. I was saying that you should check the setting that was mentioned earlier. And you should be sure to check the setting not only on your own computer, but each computer you're using the repo on. Mainly, I have a similar situation and Git always handles the x bit very well. – Jonathan Wren Jan 24 '13 at 23:26
  • Ah, no problem :-) The setting is `filemode=true` on both of my machines, so I don't see any reason why git discarding the `x` bit. Both machines are running ordinary installs of Ubuntu 12.04 on Intel. The filesystem for one machine is NFS, and the other is a shared folder in VirtualBox, if that makes any difference. Hopefully somebody will wander by and give me a clue. – Byron Hawkins Jan 25 '13 at 03:37
  • 3
    Soon with git 2.9/2.10 (Q3 2016), a simple `git add --chmod=+x` will be enough! See [my answer below](http://stackoverflow.com/a/38285435/6309). – VonC Jul 09 '16 at 19:15
  • 5
    It would be awesome if this were part of the .gitattributes spec. Something like... `*.sh text eol=lf chmod=+x` – Anthony Mastrean Mar 01 '19 at 14:36

7 Answers7

47

git 2.9.X/2.10 (Q3 2016) brings chmod to git add itself!

See commit 4e55ed3 (31 May 2016) by Edward Thomson (ethomson).
Helped-by: Johannes Schindelin (dscho).
(Merged by Junio C Hamano -- gitster -- in commit c8b080a, 06 Jul 2016)

add: add --chmod=+x / --chmod=-x options

The executable bit will not be detected (and therefore will not be set) for paths in a repository with core.filemode set to false, though the users may still wish to add files as executable for compatibility with other users who do have core.filemode functionality.
For example, Windows users adding shell scripts may wish to add them as executable for compatibility with users on non-Windows.

Although this can be done with a plumbing command (git update-index --add --chmod=+x foo), teaching the git-add command allows users to set a file executable with a command that they're already familiar with.

You can see the origin of this new feature in "How to create file execute mode permissions in Git on Windows?" (Feb. 2011)

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
24

There are several ways to do that.

  1. Git aliases
  2. Bash aliases
  3. Or even combine bash and git aliases
  1. Git aliases

    You can always use bash within your git alias.

    • Open your git config:

      vim ~/.gitconfig

    • Add an aliases section to it (if one does not exist):

      [alias]
          addscr = !sh -c 'if [[ ${0: -3} == ".sh" ]]; then git update-index --chmod=+x $0; git add $0'
      
  2. Bash aliases

    • Edit your bash profile file:

      vim ~/.bashrc

    • Add this at the end of the file:

      function gitadd(){
          if [[ ${1: -3} == ".sh" ]]
              then git update-index --chmod=+x $1
          fi
          git add $1
       }
       alias gitadd='gitadd'
      
  3. Combine git and bash aliases

    • Edit your bash profile file:

      vim ~/.bashrc

    • Add this to the end of the file:

      function checkShellFile(){
          return ${1: -3} == ".sh"
      }
      alias gitadd='checkShellFile ? git addsrcipt "$1" : && git add "$1"'
      
    • Edit your git config file:

      vim ~/.gitconfig

    • Add an aliases section to it (if one does not exist):

      [alias]
          addscript = !sh -c 'git update-index --chmod=+x $0 && git add $0'
      

None of the above has been tested

Michael
  • 8,362
  • 6
  • 61
  • 88
PassTeT
  • 517
  • 5
  • 8
  • 1
    Without googling: Is it possible to do this on a per-repo basis? In case of build agents, I'd like to know beforehand that all checkouts assures correct permissions... :) – helmesjo May 12 '17 at 09:06
  • Does this mean that you must use the alias `addscr` ie `git addscr` instead of `git add` from that point on? – Steve Gon Jun 19 '22 at 22:03
9

Here is a script to automatically apply git update-index --chmod+x to executable files:

for f in $(find . -name '*.sh' -o -regex './s?bin/[^/]+' -o -regex './usr/sbin/[^/]+' -o -regex './usr/lib/[^/]+'); do
     (cd "$(dirname $f)" && git update-index --chmod=+x "$(basename $f)")
done
Chris McKee
  • 4,298
  • 10
  • 48
  • 83
l0pan
  • 476
  • 7
  • 11
9

A solution without fancy bash scripting:

  1. Set fileMode = true in your .git/config file (or by running git config core.filemode true as others have pointed out)
  2. Change the executable bit on the file's permissions and commit this change. ( chmod u+x $script as you pointed out). You only have to do this once.
  3. Push to the remote

The next time you pull from there, git will set the committed executable bit on the file. I also had similar problems, this solved them.

fileMode = true tells git to track the only thing about permissions it can: the executable bit. This means that changes to the executable bit will be recognized by git as changes in the working tree and those changes will be stored in the repo with your next commit.

Once you committed the desired executable bit you can also reset your fileMode to false so next time git won't bother you with such changes when you don't want to commit them.

A. Sallai
  • 871
  • 3
  • 11
  • 20
2

I don't think this can be done on the git add command, but you might be able to run a script just right after you run the git commit command, but before the commit is actually created.

Take a look at the pre-commit hook.

http://git-scm.com/book/en/Customizing-Git-Git-Hooks

Basically just create a file in your .git/hooks/ folder called pre-commit. (There should be samples already in your hooks folder, rename them to remove the ".sample" at the end to activate one.)

Theres a gotcha. Make sure your script runs git stash -q first so that your working on the actual staged versions of files.

eddiemoya
  • 6,713
  • 1
  • 24
  • 34
  • https://github.com/pre-commit/pre-commit-hooks/blob/main/pre_commit_hooks/check_shebang_scripts_are_executable.py – Eric Roller Jun 20 '22 at 20:09
0

I just added thru' Tortoise Git folder update. Right click on all files and update the execute permission checkbox to true and commit/push with message. Git Command line add/commit should also work.

Smart Coder
  • 1,435
  • 19
  • 19
0

If you are on windows/powershell, just change path and filter as accordingly:

Get-ChildItem -Path . -Filter '*.py' -Recurse | Select-Object { git -update-index --chmod=+x $_.Name }
MortenB
  • 2,749
  • 1
  • 31
  • 35