I'm attempting to disallow pushes to a Mercurial repository if a certain condition holds true. However, it is essential that if the user uses push --force, the push goes through regardless.
I know that it's easy enough to do this on the machine that's doing the push by using the pre-push hook, which passes in the command line arguments to the hook. However, since hooks aren't propagated, I'd have to somehow distribute the hook to every single user of the repository and rely on them not messing with it.
Therefore, I thought the way to go would be to have a prechangegroup hook on the repository server which checked the condition and aborted the push if necessary, but I can't figure out a way to obtain the command line arguments the user used while pushing from this hook. Is there a way to accomplish this just by using a hook on the repository server?
I know that a possible workaround would be using the pretxnchangegroup hook instead and allowing the push if the commit message of the latest changeset follows a certain pattern. However, the --force option seems much easier from a repository user's perspective, since it wouldn't force them to potentially do a dummy commit to get the message right.
Sorry, the --force command line option isn't ever sent on the wire, so it won't be available on the server side at all. You'll need to figure out some way to signal "I really mean it!" out of band, be it special usernames, special commit messages, or the like.
Consider just having a 2nd server side repo that dosn't have the banning hook and have pushers use it only when they really mean it. Something like:
hg push http://your-server/repo
.. rejected due to hook failure
hg push http://your-server/repo-and-I-really-mean-it
where on the server side the repo-and-I-really-mean-it repo doesn't have the hook and automatically pushes to the plain repo.
Related
I have a utility script that will configure the local developer instance to mimic a specific production one - copy over the correct connection strings, client files, etc - thereby making it easy to investigate certain issues.
Because this is all done locally this changes files in the working directory. The first thing the script does therefore is print out a big fat warning to caution developers not to commit and revert changes when they are done. However it is possible to forget or miss this warning as cruft from my build system scrolls by and I wonder if its possible to go further.
Is it possible to do something to the hg repo so that a commit will be rejected and the developer would have to revert first?
I realize there are some variables to the question as far as revert what and commit what but given that I'm not even sure that this is possible, I am willing to take close-with-caveats for an answer.
This is exactly what hooks are for. You would need a precommit hook on every repo or possibly the pretxnchangegroup.
By creating a precommit hook (script) that checks for a specific file or a specific change, you can fail the commit and print out whatever warning you need. The return value of the script indicates to mercurial if the transaction is valid or not.
Use the following generic sample to check for the presence of certain files that would be committed, before accepting or rejecting the changesets.
Despite the decentralized nature of Mercurial, we have a centralized server that we all push to and that does nightly builds, packaging, etc...
Here's what we want to achieve: One of the files that is source controlled contains the major+minor version numbers which ideally would have to be increased with every commit. Since centralized numbering is not possible on developer's machines, we were thinking of a precommit script on the main server that would write a new minor version number to that file for each commit that is pushed. The question is/are:
since it's precommit, can this file change be part of the same commit?
if not, can precommit cause another commit and how do you prevent it from cascading/recursing?
how would one do that?
is there a better solution?
A "precommit" script is triggered only at commit time. By the time users are pushing to the "central" server they have already committed, and it's too late for a precommit hook to do anything at all. You can have changegroup and incoming hooks that are triggered to run on the "central" server when the developers push, but those can't modify the commits -- the commits are already committed/baked/done at that point, they can only react to them.
As a suggestion don't actually put the version string in the file -- having a file that changes with every commit just makes merging a pain. Instead do one or more of these:
have a CI server (like Jenkins) do builds on every push and use the Jenkins build number, which can be passed into your build script
use the Mercurial nodeid (hash) as part of your version string so you can always knew exactly what revision is in a build -- and don't put it in a file, just query for it in your build (or deploy) script
use a changegroup hook to automatically tag-on-push, which applies a pretty (possibly sequential) name to the commits (note, this pretty much doubles your number of commits since every tag is a commit)
Personally, I use something like this in my build script:
build.sh --version_string=$(hg log -r . --template '{latesttag}.{latesttagdistance}-{node|short}')
That gets me version strings that look like "1.0.3-5fd8ed67272e" which can be roughly read as "built from the changeset three commits since version 1.0 was tagged with nodeid 5fd8ed67272e", which is pretty darn good -- and it's never saved into a file it's either baked into the compile (for compiled languages) or written into a VERSION file when my deploy script uploads it to the server.
See this page in the Mercurial documentation for some comments and ideas about this issue. See also How to expand some version keywords in Mercurial? and the other SO answers referenced there.
I need to setup a hook on a repository where people can push, that would run some validation (the goal is to reject the push if validation fails). I already have some hooks setup to auto-update after a successful push, and prevent multiple heads.
I have no problem writing a validation script (for example a shell script that runs unit tests), but it needs to run on the full working copy.
My problem is that, if I just put it in a pretxnchangegroup hook, it does not operate on updated files. If I try to hg update inside the hook, this leads to repository corruption whenever validation fails and the push is rollbacked.
My current solution is to clone the repository to some temporary folder and run the actual validation there. But I find this a bit ugly, and it is less efficient that an in-place validation doing just updates. Is this kind of hook possible to setup ?
There are ways to do this, but before I start, are you sure you want to do this? Doing blocking, maybe-slow, push-rejecting changes in a hook annoys the hell out of people, and it holds the repository writelock the whole time, so no one else can be pushing while the validation script is running. One can pretty easy toss half the productivity advantages of a DVCS out the window in an attempt to gain a little control.
You could avoid most of those disadvantages with a two-tier repository setup. Something like project-push where people can push w/o passing validation and project-pull that only has changesets which passed some validation. Then you have an out-of-band (cron or hook triggered) script that moves changesets from project-push to project-pull only after validation is confirmed. Because that test is done out of band pushes aren't blocked but non-validating changesets never make it into project-pull. You can have it email the author when their push threw project-pull into a non-validating state. Users with their .hg/hgrc configured like this wouldn't have to think about their being two repositories at all:
[paths]
default=http://host//path/to/project-pull
default-push=http://host//path/to/project-push
They could just use hg push and hg pull and things would "just work".
That said if your validation doesn't need access to all the updated files at the same time you can do your test with something like this in an external pretxnchangegroup hook:
for thefile in $(hg manifest -r tip) ; do
if ! hg cat -r tip $thefile | ./validate_on_file.sh ; then
exit
fi
done
If you do need to operate on all the files at once (compiling, etc.) and you're not willing to do a two-repo structure that frees people to push quickly, then you do need a separate repo for testing, but you don't have to clone/create it each time. Something like this in a pretxnchangegroup hook should do:
hg push /scratch/long-lived-testrepo ## Warning: may not work,
## if pretxnchangegroup locks the repo and
## blocks this push to testrepo
hg -R /scratch/long-lived-testrepo update
cd /scratch/long-lived-testrepo
./validation.sh
we're moving from Subversion to Mercurial now. In Subversion there was possibility to add custom column into log (e.g. bug id) and force user to fill this column on every commit.
Is it possible to implement such feature in Mercurial?
Yes it's possible.
But before you go and do that, why isn't it enough to require bug fix commit messages to uphold to a certain pattern?
i.e. util: rename the util.localpath that uses url to urllocalpath (issue2875) (taken from Mercurial's repo)
Then you can install a hook on your central repository that scans incoming commit messages, and does whatever is needed when that pattern is found.
Furthermore, why would you want to force this on every commit? Is this for a QA team that should only commit bug fixes? If that's the case, a pre-commit hook that greps the commit message for the pattern sounds appropriate.
If you still want the extra field: when Mercurial commits something, it is possible to pass it a dictionary of strings, which you can fill with anything. See the transplant extension on how you might do that. You would also need to wrap the commit command and add a new command line option to it.
But I strongly suggest you think twice before doing this, because aside from the time consuming work involved in coding, testing (and maintaining this between Mercurial releases), you would also need to ensure that it is deployed on every environment where Mercurial is used.
I'd like to write a script that examines the incoming changesets on a push to a mercurial server and rejects the push if the changesets do not conform to a particular standard. It seems like my options are the prechangegroup, pre-changegroup, and pretxnchangegroup hooks. Unfortunately, the prechangegroup and pre-changegroup hooks do not appear to have access to the incoming changesets, so I would need pretxnchangegroup. But according to the documentation at http://hgbook.red-bean.com/read/handling-repository-events-with-hooks.html#sec:hook:pretxnchangegroup, this can lead to inconsistent state for people using the repository while the hook is executing:
"While this hook is running, if other Mercurial processes access this repository, they will be able to see the almost-added changesets as if they are permanent. This may lead to race conditions if you do not take steps to avoid them."
I'm really not crazy about random weirdness happening if someone does a pull while my script is in the process of rejecting a changeset. Is there another hook that I can use? If not, what are the "steps to avoid them" that I need to take? Is there a way I can lock the repository during my hook?
If you expand the comments for the quoted paragraph, Meister Geisler confirmed some users' observation that the issue was resolved since hg 1.2, such that the not-yet-permanent incoming changesets are not visible, thus will not be pulled.