47

I've seen a number of blog posts, and have experienced for myself, that Mercurial does not preserve the permissions on files pushed from one repo to another. Does anyone know of a Mercurial extension that would preserve the permissions? I'm assuming it can't be done with a hook, because what does a hook know about permissions at the originating repo?

Requested elaboration:

  • If the only change to a file is a change in permissions (e.g., chmod o+r filename), attempts to commit the file fail with a message saying that the file has not changed.

  • If I commit a file with permissions 600 (rw-------), then clone the repo, the same file in the clone has permissions 664 (rw-rw-r--):

    : nr@yorkie 6522 ; hg clone one two
    updating working directory
    1 files updated, 0 files merged, 0 files removed, 0 files unresolved
    : nr@yorkie 6523 ; ls -l one two
    one:
    total 4
    -rw------- 1 nr nr 8 Aug 18 21:50 foo
    
    two:
    total 4
    -rw-rw-r-- 1 nr nr 8 Aug 18 21:51 foo
    

This examples shows that hg clone does not preserve permissions, but hg push does not preserve them either.

In my application, one repo is on a publically accessible path, and it's of major importance that

  • Multiple users have the right to change the repo

  • Files in the public repo become readable only when explicitly made readable.

tshepang
  • 12,111
  • 21
  • 91
  • 136
Norman Ramsey
  • 198,648
  • 61
  • 360
  • 533
  • Can you elaborate a little on this? I can change permissions on my local files, commit the change, push them to a clone, and the clone will, when I update it, change the permissions. The key there is that I have to both commit and update. Do you want the pushed to clone to change permissions just from the push? To notice local changes without the commit? To infer all *existing* permissions? What's the exact issue / what am I missing? – quark Aug 18 '09 at 22:48
  • @quark: How is it done? I'm using hg 1.2.1 on Debian Linux and I can't even get it to acknowledge that a change in permissinos is a change worth committing. (N.B. I have elaborated as per your request.) – Norman Ramsey Aug 19 '09 at 01:57
  • Norman: turns out what I'm missing is the fact that you want to hold on to change to "r" and "w", and what I'm talking about is changes to "x" (which Mercurial has tracked since 0.6). I should've realized that you meant read/write not executable. – quark Aug 19 '09 at 17:53
  • And I agree that not being able to manage remote permissions easily can be a real pain. At work get around many of our permissions issues by using `setfacl`, but it (a) requires that you get on the server to actually make the change, and (b) changing permissions is still only the realm of the owner of the file. (b) means that even if hg did allow recording changes to "rw" it would still need to run `setuid` to make the change on the remote side, for everyone but the repository's owner. – quark Aug 19 '09 at 18:03

5 Answers5

32

It looks like it can be done using hooks and an auxiliary tool (and a little chewing gum and baling wire):

  1. Get David Hardeman's Metastore, which saves and restores file metadata.

  2. Alter the sources so it will ignore directory .hg as well as .git.

  3. Use the following Mercurial hooks:

     precommit.meta = metastore -s
    
     changegroup.update = hg update
     update.meta   = /usr/unsup/nr/bin/metastore -a
    

You have to add the .metadata file to the repo.

This lashup will work most of the time, but if you change only permissions and want to propagate it, you'll have to run metastore -s in order to push those changes into the .metadata file where hg will see the change; otherwise the commit thinks nothing is new.

tshepang
  • 12,111
  • 21
  • 91
  • 136
Norman Ramsey
  • 198,648
  • 61
  • 360
  • 533
17

What about using this solution from the Mercurial FAQ:

If you're using Mercurial for config file management, you might want to track file properties (ownership and permissions) too. Mercurial only tracks the executable bit of each file.

Here is an example of how to save the properties along with the files (works on Linux if you've the acl package installed):

# cd /etc && getfacl -R . >/tmp/acl.$$ && mv /tmp/acl.$$ .acl
# hg commit

This is far from perfect, but you get the idea. For a more sophisticated solution, check out etckeeper.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Alexei Tenitski
  • 9,030
  • 6
  • 41
  • 50
1

For the specific case of the /etc directory, etckeeper looks interesting.

tshepang
  • 12,111
  • 21
  • 91
  • 136
Omnifarious
  • 54,333
  • 19
  • 131
  • 194
0

I assumed that metastore was abandonware due to the dead git link on author's site so I whipped the below which is placed directly in repo's .hc/hgrc configuration file:

[paths]
default = ...

[hooks]
# NOTE: precommit is different than pre-commit, see https://www.mercurial-scm.org/repo/hg/help/hgrc for list of hooks
pre-commit  =
        # export permissions
        hg st -camn0 | sort -z | xargs -0 getfacl > .hg.hook.pre-commit.acl.export
        hg add .hg.hook.pre-commit.acl.export

        # export timestamps
        hg st -camn0 | sort -z | xargs -0 stat > .hg.hook.pre-commit.stat.export
        hg add .hg.hook.pre-commit.stat.export

update =
        # import permissions
        setfacl --restore=.hg.hook.pre-commit.acl.export

        # import timestamps
        # TODO: use touch to restore timestamps
Daniel Sokolowski
  • 11,982
  • 4
  • 69
  • 55
-1

It is not good idea to store permissions in VCS. However, Mercurial supports "executable" flag (that is not the same as permissions, although in Unix executable flag is part of permissions).

stepancheg
  • 4,262
  • 2
  • 33
  • 38
  • 3
    It may not be a good idea in general, but in my application, permissions are critical metadata, and they have to be right. DVCS is not the ideal tool but seems the best available. – Norman Ramsey Aug 18 '09 at 02:22