12

I work on a Mercurial repository that is checked out onto an Unix filesystem such as ext3 on some machines, and FAT32 on others.

In Subversion, I can set the svn:executable property to control whether a file should be marked executable when checked out on a platform that supports such a bit. I can do this regardless of the platform I'm running SVN on or the filesystem containing my working copy.

In Mercurial, I can chmod +x to get the same effect if the clone is on a Unix filesystem. But how can I set (or remove) the executable bit on a file on a FAT filesystem?

3 Answers3

9

For the time being you cannot change the execute bit if the filesystem doesn't support it (I have plan to support it in the future).

tonfa
  • 24,151
  • 2
  • 35
  • 41
  • The issue 3659 was closed as duplicate, the older one is: https://bz.mercurial-scm.org/show_bug.cgi?id=2020 – maxschlepzig Apr 24 '16 at 20:41
  • 4
    This is a showstopper with Windows + Linux cooperation, and I find it mindboggling that nobody has written an extension to work this out. Even this question is six years old... – joonas.fi Jun 16 '16 at 13:11
9

Mercurial tracks the execute bit as part of the file metdata. There's no way to explictly set it in mercurial, but it tracks changes made by chmod on unix. Files added on windows will have the execute bit set by default, but the windows attrib command doesn't let you set them.

If you do a hg log -p --git you'll see the patch format that shows the altering of the execute bit, which looks like this:

$ hg log --git -p
changeset:   1:0d9a70aadc0a
tag:         tip
user:        Ry4an Brase <ry4an-hg@ry4an.org>
date:        Sat Apr 24 10:05:23 2010 -0500
summary:     added execute

diff --git a/that b/that
old mode 100644
new mode 100755

changeset:   0:06e25cb66089
user:        Ry4an Brase <ry4an-hg@ry4an.org>
date:        Sat Apr 24 10:05:09 2010 -0500
summary:     added no execute

diff --git a/that b/that
new file mode 100644
--- /dev/null
+++ b/that
@@ -0,0 +1,1 @@
+this

If you're not able to get on to a unix system to set them, you could probably fake up a patch like that and hg import it, but that's definitely sub optimal.

Ry4an Brase
  • 78,112
  • 7
  • 148
  • 169
  • I tried using this technique when adding a file and it did not work. I added the file, created the patch, reverted the repo, removed the file, edited the patch replacing 0644 with 0755, and imported the patch. When pulling on a Unix system, the mode is still 0644. Mercurial 1.9.1. – Jason R. Coombs Aug 08 '11 at 20:57
  • The pull doesn't create the file, the update does. After you've pulled and you do a 'hg log --git -p' do you see the 100755 in the patch? When you update that execute bit could be unset if the umask prohibits it, the file system is mounted no-execute, or a few other unlikely things. – Ry4an Brase Aug 09 '11 at 00:27
  • In looking at the log, it shows the 0644 mode, so for some reason, it didn't accept the mode from the edited patch. – Jason R. Coombs Aug 09 '11 at 10:51
  • Hrm, and you used the actual 'hg import' command, right? Not the command line 'patch' tool? – Ry4an Brase Aug 09 '11 at 14:17
  • Correct. I'm going to have to try to reproduce it. – Jason R. Coombs Aug 09 '11 at 16:31
  • @Ry4an: I tried this solution a while ago. Unfortunately, Mercurial on Windows simply ignores file mode changes in patches, there is really no way to set the executable bit. – Wladimir Palant Sep 13 '11 at 06:31
  • Here is evidence in the form of [a transcript](http://paste.jaraco.com/F7wjP) to confirm my findings that this technique will not work. – Jason R. Coombs Jul 30 '12 at 20:07
5

For Windows you need to create a patch file and then apply it, like Ry4an has said, but with the --bypass argument to hg import. This could be done by creating a Powershell script file called SetFileExecutable.ps1 with the text below inside it

param (
  [String]$comment = "+execbit",
  [Parameter(Mandatory=$true)][string]$filePathRelativeTo,
  [Parameter(Mandatory=$true)][string]$repositoryRoot
)

if( Test-Path -Path "$($repositoryRoot)\.hg" -PathType Container )
{
  if( Test-Path -Path "$($repositoryRoot)\$($filePathRelativeTo)" -PathType Leaf )
  {
    $filePathRelativeTo = $filePathRelativeTo.Replace( '\', '/' )

    $diff = "$comment" + [System.Environment]::NewLine +
      [System.Environment]::NewLine +
      "diff --git a/$filePathRelativeTo b/$filePathRelativeTo" + [System.Environment]::NewLine +
      "old mode 100644" + [System.Environment]::NewLine +
      "new mode 100755"

    Push-Location
    cd $repositoryRoot
    $diff | Out-File -Encoding 'utf8' $env:tmp\exebit.diff
    hg import --bypass -m "$comment" $env:tmp\exebit.diff
    Pop-Location
  }
  else
  {
    Write-Host "filePathRelativeTo must the location of a file relative to repositoryRoot"
  }
}
else
{
  Write-Host "repositoryRoot must be the location of the .hg folder"
}

execute it as follows:

.\SetFileExecutable.ps1" -comment "Marking file as executable" -filePathRelativeTo mvnw -repositoryRoot "c:\myrepo"

The uses the solution provided by Matt Harbison in Mercurial's Bugzilla

WhiteKnight
  • 4,938
  • 5
  • 37
  • 41
  • 1
    There's a typo in your script, need to change `filePathRelativeTo` -> `fileRelativePath`, otherwise it does its job, thanks :) – joonas.fi Aug 28 '18 at 12:19
  • @joonas.fi sorry I forgot to update this after I fix that issue. – WhiteKnight Aug 28 '18 at 13:28
  • 1
    No worries, happens to all of us :) p.s. in the "execute it as follows" it's still the wrong way around (`fileRelativePathTo ` vs `filePathRelativeTo` in the script) – joonas.fi Aug 28 '18 at 14:39