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.
It looks like it can be done using hooks and an auxiliary tool (and a little chewing gum and baling wire):
Get David Hardeman's Metastore, which saves and restores file metadata.
Alter the sources so it will ignore directory .hg as well as .git.
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.
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.
For the specific case of the /etc directory, etckeeper looks interesting.
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
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).
Related
I'm trying to use mercurial file sets to add all the files in a directory tree, excluding very large files and any binary files. Cribbing from the mercurial documentation, this command should do it:
hg init
hg add 'set: size("<1M") and not binary()'
However this returns a status code of 0, and hasn't added anything to my new, empty repo. I've tried just 'set: not binary()' and that didn't work either.
The frustrating thing is that although I can google for mercurial file sets, and find lots of examples, I can't find anything to help troubleshoot when it doesn't work!
I don't have a .hgignore file, and it's a fresh empty repo. Mercurial 4.2.2.
The directory where I'm testing this has a couple of artificially created files for the purpose of testing. In my real use case, I inherit a multi-gigbyte tarball of assorted sources and binaries from a client, and I want to get all the sources into mercurial before I start hacking to fix their problems, hence the need to exclude the binaries and large files that otherwise choke mercurial.
Here's my little test script:
#!/bin/sh -ex
dd if=/dev/urandom of=binary_1k bs=1 count=1024
dd if=/dev/urandom of=binary_2M bs=1 count=2097152
echo "This. Is, a SMALL text file." > text_small
hexdump binary_1k > text_1k
hexdump binary_2M > text_2M
ls -lh
file binary_1k
file binary_2M
file text_1k
file text_2M
hg init
hg add 'set: size("<1M") and not binary()'
hg status -a
hg add 'set: not binary()'
hg status -a
hg add 'set: size("<1M")'
hg status -a
At the end of this, each status command reports no files in the repo, and the add commands report no errors.
The problem is that file sets do a query of Mercurial's repository data base, which knows only about files that are part of the repository or have been added.
One solution is to add all, and then to get rid of the files that you don't like, e.g.:
hg forget 'set:size(">1M") or binary()'
This works, because the query also requires recently added files, even if they haven't been committed yet.
I would like to have Mercurial list all tracked files in the repo that are supposed to be symlinks. I can get a list of tracked symlinks that are symlinks in the working copy with hg files "set:symlink()", but that doesn't capture the tracked symlinks that aren't symlinks in the working copy.
What I want instead is a command that looks at the tracked file's properties in the manifest and tells me if the symlink bit is set. I've tried to use hg manifest -v, which the documentation says will "print file permissions, symlink and executable bits". I've tried that, but the symlinks are indistinguishable from non-link files whose executable bit is set. I get lots of:
755 * path/to/file
If I try to use the leading markers (hg manifest -v | grep '755 *'), the files I want are listed, but I also get lots of false positives.
Background: Adding and committing symbolic links seems to work for us, but Mercurial is refusing to create the symlinks when someone else pulls those changesets. I understand why Mercurial is refusing: we run afoul of an undocumented rule. So now we end up with files where the symlinks are supposed to be, and the content of the file is the path to which the symlink is supposed to point. If I can get Mercurial to tell me which files are supposed to be symlinks, I can easily replace the files with the appropriate links.
Thank you!
Edit: In my repository, the symlinks had not been committed properly due to a bug in the client, which is why hg manifest -v wasn't showing the committed files as links.
I've learned a few things, and I wasn't asking the right question, but let's get the close-enough answer out of the way first:
hg manifest -v | grep ' # '
Caveat: I will guess that it's possible for manifest to put multiple symbols between the permissions number and the path, so ' # ' as the grep expression may not work perfectly if you have an exotic set of bits on a file. I needed the spaces around the # because our repository has many files with # in their names.
The rest of the story: After some experimentation, I was able to determine that a particular mercurial GUI (SourceTree) wasn't committing new symlinks correctly, which turned out to be a longstanding issue with the Mac version. I created a new repo (hg init) and added/committed one target file and one symlink (Link\link.txt) with command-line hg. I then added/committed a second symlink (Link\STlink.txt) with SourceTree. hg manifest -v shows that the links were not treated the same by the different clients:
755 * Link/STlink.txt
644 # Link/link.txt
644 Target/target.txt
If I clone that repo, even with SourceTree, the Link/link.txt symlink is reproduced faithfully, but the Link\STlink.txt link is not. So the real fix for me is to replace all of the files with links and commit again from the command line and to try to remember not to use SourceTree to commit new symlinks again in the future.
To assess which files changed from symlink to 'normal files', you could try to throw some bash at it and compare the output of the fileset of the modified and unmodified versions. Maybe something like
diff <(hg files "set:symlink()" -r.) <(hg files "set:symlink()")
Example:
In a repository where eeg-link was added, foo22-link changed to a normal file and foo edited (and foo-link kept pointing at it):
$ hg diff
diff --git a/eeg-link b/eeg-link
new file mode 120000
--- /dev/null
+++ b/eeg-link
## -0,0 +1,1 ##
+eeg
\ No newline at end of file
diff --git a/foo b/foo
--- a/foo
+++ b/foo
## -1,2 +1,3 ##
foo is boo!
And this change, too!
+And even this.
diff --git a/foo22-link b/foo22-link
old mode 120000
new mode 100644
--- a/foo22-link
+++ b/foo22-link
## -1,1 +1,1 ##
-foo22
\ No newline at end of file
+This is no symlink anymore
we get a nice and clean output of
$ diff <(hg files "set:symlink()" -r.) <(hg files "set:symlink()")
0a1
> eeg-link
2d2
< foo22-link
showing the newly-added symlinks (eeg-link) and the removed symlink (foo22-link).
I can get a list of tracked symlinks that are symlinks in the working copy with hg files "set:symlink()", but that doesn't capture the tracked symlinks that aren't symlinks in the working copy.
In case it’s not clear from the other answers, the basic answer to the question is (ignoring any complications induced by bugs outside of Mercurial’s control) to add the -r option to hg files, e,g,
hg -r tip files 'set:symlink()'
So here's the problem. I have my configuration file in my home directory ~ under Mercurial control. Part of the
|-~
|.hg/...
|-Dev
|-Project1/...
|-Project2/...
.hgrc
.hgignore
I have Dev directory excluded from the source control in .hgignore file.
However when I am in the directory ~/Dev/Project1 Mercurial thinks that I am in the under the source control. If I type hg root in any directory that is in the .hgignore or its sub-directory hg still considers it being a part of repository.
Is it a bug or a feature ?
UPDATE
So, here's the simple experiment one could do from the command line:
% mkdir -p /var/HgTest
% cd /var/HgTest
% hg init
% echo "this is a repository file" >> test.txt
% hg commit -Am "added repo file"
% cat <<EOT >> .hgignore
heredoc> syntax:re
heredoc>
heredoc> ^Dev
heredoc> EOT
% hg commit -Am "added .hgignore"
% echo "This is not in repository" >> Dev/notinrepo.txt
Now, Dev directory not in repository, if you type hg st anywhere under /var/HgTest it shows you that repo is clean. However if you go into Dev directory and type hg root it will output /var/HgTest. This is perhaps desired result. However, since the path should be ignored, I would think that hg root should effectively exit with -1 return code and message "not in repository" or something like that.
In my case, having HOME directory under source control effectively makes some of the tools consider every new directory (even under ignored paths) as a part of Mercurial repository located in the HOME directory.
It's a feature for when you are in ~/Dev/Project1/deeply/nested and want to keep mercurial commands within the scope of Project1.
A workaround is to hg init in ~/Dev/Project1. Part of the problem is the bad practice of putting your home directory under version control; I can see no benefit to be gained from it and much cost. As an example, almost everything you do with a browser, or music player, or many other programs is going to alter files in ~/.groovy-game/config or ~/.browser/cache-files; there is no meaningful way to choose a commit point. Because of this it would be better to establish good, incremental snapshot backups for $HOME, even if they are stored on the same machine.
This is not to say that dot-directories in your home should never be versioned. for example, suppose I hack on my ~/.vim files because I am working on the ultimate editing environment, cd ~/.vim; hg init can certainly be useful.
Put another way — so long as there is an .hg repository somewhere in the tree above you, Mercurial will seek it out and read the ignore file and not take action on ignored paths. However, hg root only looks for an .hg directory. In your case, there is always a root, you are in your ~ repository by definition. I don't see how it could be done otherwise; you can't find the ignore file until you've inspected the root.
I have a directory in my repository which has a different name/case in the repository manifest as in the file system.
(This is a Windows machine, using the hg that comes with TortoiseHg, and cygwin's bash as the command line interface.)
Here we see what it looks like on the file system:
$find . | grep -i cadimport.vbproj
./WIZARDS/CADIMPORT/CADIMPORT.NET/CADIMPORT.vbproj
And we wee that the case of the CADIMPORT.NET directory is different in the manifest:
$hg manifest | grep -i cadimport.vbproj
WIZARDS/CADIMPORT/CADImport.NET/CADIMPORT.vbproj
So, I figure that I'll just delete the directory and have mercurial bring it back, and it should come back right.
$rm -rf WIZARDS/CADIMPORT/CADIMPORT.NET
$hg update -C
getting changed largefiles
0 largefiles updated, 0 removed
18 files updated, 0 files merged, 0 files removed, 0 files unresolved
But much to my dismay, I discover that it came back wrong, just the way that it was before:
$find . | grep -i cadimport.vbproj
./WIZARDS/CADIMPORT/CADIMPORT.NET/CADIMPORT.vbproj
What is going on here? Why is it creating a directory with the wrong case? What can I do to get it to give me the right one?
(This case issue is causing problems when I try to create a new repository based on the code from the old one. The difference in the case of this directory makes it impossible to import patches from the old repository into the new one.)
A little while ago I noticed that hg started creating unversioned copies of files in the repository at seemingly random times when I update between branches. I can't for the life of me think of what I might have changed for this to start happening. There is nothing in the verbose or trace output to indicate that these files are being created.
The new unversioned filenames all end with what seems to be a random string added to the end of the extension:
file1.txt-23121dd1
someotherfile.sql-bc769bd2
bizarrofile.cs-40a93ed0
hgisinvadingurhead.ppt-f8e9015a
When trying to determine the pattern of this happening I've noticed the following:
The added characters in the filenames do not correspond with any changeset ID in the repository. I have done a grep -i to the output of hg history and the string in the filename does not appear anywhere in the output.
In all cases the files existed in the branch I was working on but do not exist in the branch I update to.
Sometimes it's only one or two files, sometimes it's several.
It is never the case that these are all of the files that exist in one branch but not the other.
It is never the case that it is the same set of unversioned files between updates.
Others on my team who are cloning the same repositories do not seem to be experiencing this
I thought maybe it was something within the repository but it also happens in other existing repositories and in brand new ones as well.
For example, I have done this (hg output omitted except for hg status output at the end, but no errors come from the output):
c:\> mkdir repo
c:\> cd repo
c:\repo\> hg init
c:\repo\> echo default > default.txt
c:\repo\> hg add
c:\repo\> hg commit -m "Commit default"
c:\repo\> hg branch branch1
c:\repo\> echo branch1 > branch1.txt
c:\repo\> hg add
c:\repo\> hg commit -m "Commit branch1"
c:\repo\> hg update default
c:\repo\> hg status
? branch1.txt-23121dd1
This is not repeatable every time. I could repeat these steps and sometimes the unversioned file will be there at the end and sometimes it won't. It's very sporadic. In larger repositories, though, I almost always see at least one unversioned file between branch updates.
Full output of hg update default follows. The output always displays as such whether or not the unversioned file is created.
resolving manifests
calling hook preupdate.eol: <function preupdate at 0x0000000002571668>
removing branch1.txt
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
I was using an older version of hg when I first noticed it but the problem still exists after updating to 2.3.2. I am using Windows 7 Pro x64 with TortoiseHG 2.5.1 x64. I don't think it's related to Tortoise, however, because I can replicate the problem by just using hg from the command line.
The contents of my mercurial.ini file are:
[ui]
username=myname <myname#mydomain.com>
ignore=C:\users\myusername\.hgignore
verbose=true
trace=true
[eol]
native = CRLF
only-consistent = False
[extensions]
purge =
eol =
I can live with it, but it's a pain to make sure I'm not accidentally adding these files to the repository in changesets with other new files.
If someone has seen this and could point me to the culprit I'd be most appreciative!
If a file is in use when updating between changesets, the in-use file is renamed with the added numbers so the update can succeed.
Does disabling the eol extension help matters? I noticed that your test did not use a .hgeol file as well (that's one of the things associated with this extension). There's another thread hereabouts that is dedicated to some problems with this extension.