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.
Related
It occurs quite often that I want to know the full status of my local copy of a project, compared to the remote repository. By full status, I mean the following:
Are there some uncommitted changes locally?
Are there some unpushed commits locally?
Are there some unpulled commits remotely?
Am I on head of default branch?
I know that I can use some graphical tool such as HgView or TortoiseHg, or even my IDE to deal with Mercurial repositories, but I find it more convenient to use CLI when working with several projects/repos at the same time.
The way I am doing currently is by using an alias
alias hg_full='hg incoming; hg outgoing; hg status'
If everything is fine (i.e. local synchronized with remote), I then ensure being on head of default by
hg update default
This approach is perfectly working, but when I work with a slow remote repository, it is quite annoying to wait for both the incoming and outgoing command to return before performing the update.
Is there some way (by the mean of an extension or a more advanced command) to get a full status summary of the local copy compare to remote repository without performing hg in and hg out sequentially?
I think hg summary --remote might be exactly what you're looking for:
$ hg summary --remote
parent: 1:c15d3f90697a tip
commit message here
branch: default
commit: 1 modified
update: (current)
remote: 1 or more incoming, 1 outgoing
You can save yourself some network traffic by doing hg incoming --bundle <filename>, which fetches the incoming changesets and stores them in a bundle file. You can then run hg outgoing (or hg pull) against the bundle file, which doesn't use the network at all.
hg incoming --bundle incoming.bundle # Creates the bundle
hg outgoing incoming.bundle
hg pull incoming.bundle
hg update default
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.
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
I use named branches in Mercurial.
In doing so I have created one branch called playground where I can try out various wacky experiments. I never intend to merge this branch into any others and I never want to push it to our main repository.
Since creating it, every time I do a push I am told I have added a new branch and I have to use the --new-branch flag. At this point hg push -b default (or whatever branch I'm pushing) works fine but it's annoying. Is there any way to suppress that message by letting Hg know that I am not interested in pushing that branch ever?
Starting with Mercurial 2.1 (released in February 2012), you can mark your changesets secret to keep them from being pushed to another repository. You use the new hg phase command to do this:
$ hg phase --force --secret .
This mark the current working directory parent revision (.) as being in the secret phase. Secret changesets are local to your repository: they wont be pushed or pulled. Pushing now looks like this:
$ hg push
pushing to /home/mg/tmp/repo
searching for changes
no changes to push but 2 secret changesets
There is no equivalent mechanism in older versions of Mercurial. There your best bet is to create a local clone for the changesets you don't want to push.
Update:
Mercurial 2.1 introduced the hg phase command which allows users to control what change sets are exchanged with remote repositories. #MartinGeisler answer to this question details this method.
Original Answer:
If you want to create a local branch of your code you have a couple options. You can hg clone the repository which will locally create a branch of the entire repository in your filesystem. The other alternative is you can try to use a Mercurial extension like LocalbranchExtension.
There are many ways to branch in Mercurial without using a named branch. Just find a method that suits your needs.
Further reading: http://stevelosh.com/blog/2009/08/a-guide-to-branching-in-mercurial/
In addition to the excellent answer above concerning phases, you can also specify 'default-path' (in the [paths] section of your .hgrc) to refer to the local repository:
[paths]
default = ...
default-push = .
This will cause all outgoing changesets to be compared to the specified repository. In this case, comparing outgoing changesets in your local repository TO your local repository results in nothing to push.
You can still pull/update/merge from the main repository, but no push will ever send anything back to that main repository.
If you work on multiple machines/repositories, you can set one up as described above, and configure the others to specify the 'default' path to point to the server that pushes to itself. In this way, the other machines can push/pull to your local central repository, and these changesets will never escape your carefully configured collection of repositories.
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.