We have a simple mercurial hook that runs every time we pull remote changes. We use changegroup hook. Our hook rebuilds some dlls and copy them to a folder. We automatically rebase when we do a pull. This causes our hook to be run two times, first when we do the pull, then after the automatic rebase.
Is there any easy way to detect if there's going to be a rebase and only run the hook once at the end of the rebase?
Thanks,
Could you show your original hook?
Anyway, if you check the source parameter, you could choose if the hook should run or not:
def changegroup_hook(ui, repo, source, **kwargs):
if source != 'strip': # rebase operation uses 'strip' as source
return 0 # SUCCESS
# continue rebase processing
Related
I want to check what files have been modified in current commit and base on that I want to modify or not specified file and add it to that commit.
I was thinking about using script on pretxncommit hook who gives me information that I need but I don't see any possible way to add new modified file to that commit.
Is there any way to do that?
it would not be at pretxncommit because at that point the changeset is already built
you'd need to hook earlier, likely on precommit
precommit: Controlling. This is run before starting a commit.
pretxncommit: Controlling. This is run after a new changeset has been created in the local repository, but before the transaction that will make it permanent completes.
https://book.mercurial-scm.org/read/hook.html
Our team uses Bitbucket for hosting our Mercurial repository. We have a single repo which is cloned to our dev VM's upon provisioning them. We use a fairly typical feature branch -> pull request -> review -> merge feature into default from the PR workflow.
What we'd like: to be able to restrict things such that one cannot push to the default branch by command-line (to avoid accidental commits to that branch). Ie - we want to enforce it so that the only way default is modified is via a pull request.
Note that forking isn't really an option due to the VM setup (we'd have to add complexity to the VM provisioning to do the fork, and set all that up on the provisioned VM, and even then that just means that when someone accidentally pushes to default they're just messing up their fork).
Branch Restrictions seem promising, and while we can set it up that nobody can push via the command line, it then means only a single named user or group can do the actual merge of the PR (which we don't want, ideally anyone on the team can merge, just only through a Bitbucket PR).
Is this possible? Any suggestions?
So I ended up solving this with Mercurial hooks. Specifically I created the following file, which I named prevent_default_push.py and put in the .hg directory of my clone.
# branches to prevent being pushed to by command line.
# Separate branches by spaces
restricted_branches = "default".lower().split()
def branch_name(repo):
return repo[None].branch().lower()
def is_restricted(branch):
return branch in restricted_branches
def prevent_default(ui, repo, *args, **kwargs):
if is_restricted(branch_name(repo)):
print("Preventing push to default branch")
return True
return False
def prevent_commit(ui, repo, *args, **kwargs):
branch = branch_name(repo)
if is_restricted(branch):
print("You're on a restricted branch (%s), are you sure you want to commit? [YN]" % branch)
res = raw_input().strip().lower()
return res != "y"
return False
And then edited the .hg/hgrc file to use these hooks:
[hooks]
pre-push = python:.hg/prevent_default_push.py:prevent_default
pre-commit = python:.hg/prevent_default_push.py:prevent_commit
Then when trying to do a commit while on the default branch, you get a confirmation prompt, and if you try doing a push to default it's straight up rejected.
Example run:
$ hg commit
You're on a restricted branch (default), are you sure you want to commit? [YN]
n
abort: pre-commit hook failed
Where "n" is what I typed.
The plus side of this is that while the stuff in your .hg directory isn't under version control (so a clone won't get it), we can incorporate into our provisioning mechansims the automation of putting these hooks in place on a provisioned VM.
I want to use mercurial hooks to trigger regression builds (in Jenkins) when developers push from their local repos to a central, remote repo.
In path/to/repo/.hg/hgrc
[hooks]
changegroup = python:jenkins.py:trigger_build
And jenkins.py:
def trigger_build(ui, repo, source, hooktype, node, **Kwargs):
...
changeset_to_build = node
...
But in this case, node refers to the earliest changeset in the changegroup, I want to kick off building and testing against the most recent. I have a workaround that uses:
def trigger_build(ui, repo, source, hooktype, node, **Kwargs):
...
changeset_to_build = repo['default'].hex()
...
This gets the appropriate changeset, but I'm not sure it's the best way of doing this. Is there a more standard idiom that I'm missing?
Thanks
it seems to me that repo['default'] is always the head of the default branch. that could be a problem if developers expect builds for other branches or the default branch is not named default.
in a hook based on bash and revsets, i'd use the following:
#!/bin/bash
changeset_to_build=$(hg log --rev "heads(${HG_NODE}:)" --limit 1 --template "{node}")
that would be the first node without child changeset between HG_NODE and tip; so even if the changegroup does not start in the default branch, it is the head of the changegroup that jenkins should build.
I'd like for a build to be done (on the server) each time a push is made to our central Mercurial repository.
Builds are usually kicked off on our build server by running a Visual Build file either manually or via a scheduled task.
What are the ways of achieving this?
Simple, low impact solutions are preferred.
As Pablo suggested, you can do this with a hook, but you'll need an incoming hook on the server side. This hook runs "after a changeset has been pulled, pushed, or unbundled into the local repository" (hgrc manpage).
Edit the .hg/hgrc file of the repository located on the server and define your build hook as follows:
[hooks]
incoming = /path/to/executable-build-script
Of course, the build script called here just needs to be a trigger for whatever build process you actually use.
Note that an incoming hook runs for every single changeset in a push. If you don't want this, use a changegroup hook -- it runs only once for each push, no matter how many changesets it carries.
Another way, in addition to the hooks that Pablo mentions, is to set up a continuous integration server, like TeamCity. Then you could ask TeamCity to monitor your repository, pull new changesets and start the visual build script for you.
Disclaimer
These findings are for tortoisehg client and mercurial server behind apache on win32.
Try #1
The naive solution would be to make your push kick off the build.
In .hg\hgrc
[hooks]
incoming=.hg\build.py
In build.py
os.system('\Progra~2\Micros~2.0\Common7\IDE\devenv /build release project.sln > logfile')
Problem
What you'll find is that, after a push, the tortoise hg client won't return until your os.system call returns. This may or not be acceptable. In my shop a build took about 20 minutes, and my boss deemed that unacceptable.
Try #2
My solution was for the hook to return immediately after creating a REQUESTBUILD file to the root directory.
In .hg\hgrc
[hooks]
incoming = .hg\write_buildrequest_file.bat
In .hg\write_buildrequest_file.bat
echo REQUESTBUILD > \REQUESTBUILD
Meanwhile, I had a python script running in an infinite loop, checking for the presence of REQUESTBUILD.
In .hg\monitor_buildrequest_file.py
import popen2, time, os
import subprocess
while True:
if os.path.exists("\REQUESTBUILD"):
os.system("del \REQUESTBUILD")
os.chdir("/yourrepo/.hg")
retcode = subprocess.call("\python27\python.exe build.py")
else:
time.sleep(10)
build.py would generate an HTML file of results, which the submitter would have to pull via their web browser.
There are other issues (pushes while a build is commencing, saving historical results, building out of the working directory vs copying elsewhere) but this is the general idea.
You need to handle repository events with hooks.
So, after commit event you need to run a script that will perform your build accordingly.
Being very familiar with the subversion workflow and that fact that 99.9% of the time my computer is connected to the internet, I don't like doing 'hg ci' and 'hg push' separately.
I remember bzr had a 'checkout' command that would bind subsequent 'commit' commands to automatically commit directly to the server ('push').
Does mercurial have something similar to this?
PS: Writing a shell script or alias that runs 'hg ci $* && hg push' would be the last thing I'd do.
You could add a hook to run push after a successful commit.
EDIT: I just tried it out and it seems to work fine. I added the following to the .hg/hgrc file of the repository I wanted to activate automatic pushing for:
[hooks]
commit.autopush = hg push
EDIT 2: Also, you don't have to worry about something like this:
You're in a repository that you don't want to automatically push.
You use hg -R ~/another-repo-that-autopushes commit to commit in a different repo that does automatically push.
Will the hg push hook end up pushing the changes in the current directory instead of the one you're committing in?
No, it won't. According to the page I linked:
An executable hook is always run with its current directory set to a repository's root directory.
It's an edge case, but Mercurial handles it correctly.