Mercurial : hook to limit pulls to one branch only - mercurial

I'm stumped trying to ensure that a deployment repository only updates a specific branch off the central Mercurial server.
Specifically I need to ensure that anyone pulling changes to the deployment repository is doing the equivalent of
hg pull -b deployment <central-repo-url>
rather than
hg pull <central-repo-url>
I assume I need to use the pre-pull hook, but I'm not sure how to do that.

Additionally, same technique, but more user-friendly way;
ACL extension, in which you define [acl.deny.branches] and [acl.allow.branches] for your workflow

The easiest thing might be to define an alias in that repository's hgrc that defines pull as pull -b deployment. Then, any attempt to pull will do what you want. However, it would be very easy to screw this up in such a way that would make any attempt to pull an infinite loop.
I would suggest a pretxnchangegroup hook. That lets you examine the changesets that are being imported, and reject them if you don't like them. I suspect that the rule you want to enforce is that the tip should always be on the deployment branch (the changegroup might drag in changesets from other branches which have been merged into deployment, but the tip should always come from deployment). That would be a hook like:
[hooks]
pretxncommit.deployment = hg log -r tip --template '{branch}\n' | grep '^deployment$'

Related

repo cloned from one remote branch needs to be commited to a different remote branch

We have a master repository located on a separate server. I originally cloned the default branch and made my changes locally. I have locally commited those changes. However, there has been a branch created on the master repository that I would like to push my changes to. Below is the description of my attempt at getting this accomplished.
I have cloned the branch. I am trying to export my changes from local default like so:
C:\hg\default>hg export -g -o mypatch -r tip
and when trying to import them into the clone of the new branch, I get the following:
C:\hg\newBranch>hg import C:\hg\default\mypatch
applying C:\hg\Fill1\mypatch
patching file .hgignore
Hunk #1 FAILED at 11
1 out of 1 hunks FAILED -- saving rejects to file .hgignore.rej
abort: patch failed to apply
I can manually fix the .hgingore.rej file just fine. The problem is that the patch also contains files that were moved. Instead of the files showing as moved, I get the following when running hg status:
C:\hg\newBranch>hg status -C
M someOtherFilesThatLookAsExpected.txt
! originalLocaion\fileA.txt
? newLocation\fileA.txt
This missing and new status is for all files that were moved in the commit contained the applied patch. Am I doing something wrong? Do I always have to manually move files when applying a patch? Is there an easier way to accomplish this branch transfer?
That's a bit difficult to answer without knowing more about your repository structure, but here's how I'd go about it without knowing more. I'm assuming that the reason for the conflict is that there are conflicting changes in the same branch of the repository.
First, get the contents of the newBranch repository:
cd c:\hg\default
hg pull c:\hg\newBranch
Then, either merge or rebase your changes on top. If you are working on the same branch, then just using
hg pull --rebase c:\hg\newBranch
in lieu of the regular pull should do (assuming you have rebasing enabled). Otherwise, do an explicit merge or rebase of the two heads that you need to reconcile. Finally, do:
hg push -r tip c:\hg\newBranch
in order to get your (now reconciled) changes back into newBranch.
Unless you have very specific and unusual requirements, push and pull should be your normal way to sync repositories or part of them (note that using -r will only push/pull the respective branch). Export/import are rather low-level mechanisms that may not give you the benefits of the standard machinery that handles renames, three-way merging logic, etc.

In Mercurial, is there a way to save/commit an unfinished change but not available for others via clone/pull/push?

I often find myself in the situation that I need to switch to work on a different change before the one that I am currently working on is done. I want to find out if there's a way, in Mercurial, that I can save/commit my unfinished change, which is not available for others (ie. not clonable/pushable/pullable).
Mercurial phases may be the answer to this.
Starting with Mercurial v2.1, you can configure mq changesets to automatically be marked secret. secret changesets are ignored by incoming/pull and outgoing/push commands.
To enable this behavior, you need to add the following to your config:
[mq]
secret = True
Once enabled, it behaves as follows:
$ hg qpush --all
applying my-patch
now at: my-patch
$ hg phase -r .
16873: secret
$ hg outgoing
comparing with https://www.mercurial-scm.org/repo/hg
searching for changes
no changes found (ignored 1 secret changesets)
Take a look at the Shelve Extension. This gives you the basics and might be more than enough for what you need.
There is also the Mercurial Queues Extension, but I find this can be a little odd to work with.
As a final alternative, you could always commit your changes onto another branch so that they don't affect mainline development, but I think these may still be visible.
You can clone your repo to a new place to work on new changes. That way your pending changes are kept on your local machine and never pushed. Of course, this depends on the size of your repo. If it's too big, cloning becomes a little prohibitive.
As others have suggested, you can mark your unavailable changes to be on a private branch. When you push, you can push an explicit branch using the -b argument. So, if your private branch is TimPrivateBranch, and other changes are on default:
hg push -b default
TimPrivateBranch stays on your local computer. Of course, this requires you to remember the -b argument every time you push.
When you're done with your private branch, just merge back into default:
hg up default
hg merge TimPrivateBranch

Mercurial: enforce "hg pull -u" before "hg commit"

I have in some cases a need to enforce that Mercurial-users have run hg pull -u before any hg commit can be allowed, i.e., hg pull will mean that the incoming queue is empty — and furthermore I also want that the person is using the head version of the branch.
How can I set up such a restriction?
(I am fully aware that this goes against parts of the DVCS design core)
You could ask your developers to install
[hooks]
pre-commit = hg pull -u
in their config files (it should probably be installed in the per-repository .hg/hgrc file since this workflow is repository specific).
This makes Mercurial a little Subversion-like: your developers will only have one outstanding changeset. But note as soon as someone pushes to the server, hg pull -u cannot update to the new branch tip since it will cross branches (topological branches) to do so. So a proper merge will be needed at that point (or a rebase, see hg pull --rebase).
Normally mercurial will NOT let you push an open head to the server without using the -f flag (force). You can write a hook to pull automatically but that can not be enforced server side due to the server not knowing what you have. There is an article on mercurial's website about this scenario:
https://www.mercurial-scm.org/wiki/TipsAndTricks?highlight=%28heads%29#Prevent_a_push_that_would_create_multiple_heads
As Adam says, perhaps what you really need to do is prevent multiple heads (per branch). This is what we do, using the 'forbid_2head' hook from Netbeans (linked from here https://www.mercurial-scm.org/wiki/TipsAndTricks#Prevent_a_push_that_would_create_multiple_heads)
The result is that the hook prevents any push that creates multiple heads on a branch (so one on the anonymous/default branch plus one each on named branches). This effectively forces a pull before commit because you have to pull, get the two heads locally, then merge or rebase to remove it.
note, the hook is on the server/master repo

Can I mark a branch as 'not going to push'?

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.

How to restrict "-f" option while making hg push

Is there any option to restrict a user to use hg push -f? Because it will remove intermediate commits by other users.
First of all, doing hg push -f cannot remove intermediate commits. Mercurial is built around an append-only history model and you cannot delete stuff from a server by pushing to it.
When a user does hg push -f, then he tells Mercurial to go ahead with the push even though it creates new remote heads. To prevent this, you need a hook on the server that forbids more than a single head. There are listed several such hooks here.
With Mercurial 1.6, it is no longer necessary to use -f when you push a new named branch. You should instead use hg push --new-branch. This is safer since it only allows the creation of new branches, not the creation of multiple remote heads.