I'm trying to create a Mercurial repository with a changegroup or pretxnchangegroup hook that inspects the commit, possibly makes some changes to it, then commits the changes. My hook does something similar to this:
#!/bin/sh
if ! grep -q foobar foobar; then
echo foobar >> foobar
hg add foobar
hg commit -m 'added foobar to foobar'
fi
But when I push to the remote repository which has the hook it hangs waiting on a lock and I have to kill it:
$ hg push
pushing to /tmp/a
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
foobar already tracked!
waiting for lock on repository /tmp/a held by 'duck:18140'
^Ctransaction abort!
rollback completed
interrupted!
interrupted!
This makes sense because I understand that the changegroup must hold a write lock on the repository, and so must the commit that is inside of the hook. But how can I work around this problem?
I can think of two methods that might work, but don't know if they are possible:
If there was a hook that runs after the changegroup is complete and the locks are released, I could run the commit there.
Append a changeset to the incoming changegroup.
You shouldn't try to modify a changeset in a hook since it is a external process that couldn't handle conflicts etc. Besides, I think that it isn't even possible or desirable. The original changeset would remain intact and it would cause duplication problems some day.
What you can do is approve or not a incoming changeset based in some criteria. If a changeset isn't approved, it's up to the developer modify it in his repository and then push it again.
I figured out that only the pretxnchangegroup hook locks the repository. The changegroup hook does not lock the repository, and performing a commit in that hook is okay.
Related
Is there a way to edit a commit message in Mercurial on a commit after other commits have occured using TortoiseHg? I've read these posts:
How to edit incorrect commit message in Mercurial?
Mercurial: how to amend the last commit?
and have been able to update a "regular" commit message when it is the latest commit on a branch (using TortoiseHg). However, I haven't been able to figure out how to edit a commit message when other commits have occurred after the one I want to edit. It always edits the last commit.
Based on Ed Cottrell's comment, I did a test where I made two commits without pushing to the central repo, and I still have the same issue - only the last commit message can be edited.
EDIT: I should clarify that I am looking to update a changeset that has been pushed.
Histedit extension (bundled with TortoiseHG now) has a mess command for changing the commit message of historical changesets.
Unfortunately, this command is not supported by the TortoiseHG GUI so you need to run the command from command line.
As long as the change in question is local and hasn't been pushed anywhere, it is possible.
The commit message is used to compute the globally unique hash id that is used for all repositories to determine whether or not they already have a changeset. If you change the commit message, you change the unique hash id and every repo will see it as a "new" changeset. All other repositories that had the old changeset will try to get the new one and ask that you merge it with itself.... This is not a good thing, so the short answer to your question is "don't do it".
If you could definitively purge that change from all other repos, so that only the local copy is left you could essentially get to the "draft" state. Note that if any repo has the "old" changeset, it will be pushed to the central repo someday and cause the mess that we are trying to avoid.
If the changeset is still local (e.g. in draft status), you can use hg commit --amend if it is the parent of the working directory.
If there are changes after it, I would use mq and hg qimport all the changes down to and including the one where you want to edit the commit message. hg qpop -a and then hg qpush to get to the patch that represents the changeset you want to edit. Then hg qrefresh -e and make your changes. Then just hg qfin -a and you should be good to go.
The advice from Edward is good — if you've pushed your changes to another repository, you should consider them set in stone and not update the commit message or any other aspect of them.
However, we're working on changing this in Mercurial. There is an experimental extension that will allow you to do more extensive history editing and push those edits to other repositories. It is called the Evolve Extension and it enables some behavior that is partly in the core of Mercurial and partly outside core.
When using evolve, you can edit the second-to-last commit message like this
$ hg update .^
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg commit --amend -m 'new commit message'
1 new unstable changesets
$ hg stabilize
more:[5] old tip changeset
atop:[6] new commit message
The extension allows you to do this as long as the changesets are in the draft phase. To keep them in the draft phase after pushing them somewhere, the repository you push to need to be configured as a non-publishing repository. You can read more about this in the Changeset Evolution Documentation.
I have an issue when trying to clone the repository to local machine via LAN.
At some point an error occurs and all downloaded data are erased during rollback.
Is it possible to turn off the mercurial's automatic rollback on error even though the downloaded data may be corrupted?
You can't stop it from rolling back -- it won't leave it in an inconsistent state, however you can do the clone incrementally.
Rather than:
hg clone http://path/to/your/repo
do:
hg clone -r 100 http://path/to/your/repo
hg pull -r 200 http://path/to/your/repo
hg pull -r 200 http://path/to/your/repo
... and so on until done ...
That gets 100 changesets at a time. If you have a network failure you'll only have to re-un the last command and eventually you'll get through.
As a note, once you've cloned this to a machine, even once, you never have to do it again. Instead clone from your local repo if you want another clone.
hg clone myclone my-other-clone
I've accidentally committed bunch of files locally, but didn't pushed.
So basically what I want to do is to remove this my commit, then push some other changes.
I can remove the commit by Backout-ing, then I've to commit locally so it is actually removed locally.
So my question is following, if I do a push, will my accidental commit and its corresponding revertion log be visible publically ?
It sounds like hg rollback is the command you want.
hg backout <REV> creates a new commit that reverses the changes made in <REV>. Both the original commit and the backout commit will remain in your history. This is one of the few options you have to fix a bad commit after you have pushed it to a public location.
However in this case you have not pushed to the public yet, so there are better solutions.
If the bad commit is the last commit that was done (i.e. your tip) then you can use hg rollback (under the Repository menu in TortoiseHg). This command is like "undo" for commits.
If the bad commit is elsewhere in your history (but still has not been pushed to a public repo), you can also use the mq extension to rewrite that part of your history.
You could use hg strip -r . --keep instead of hg rollback. hg backout would be necessary only if you had pushed your commits.
The simple answer to your question is Yes.
If you perform a Backout, then it will show up in your history.
You want to perform a Strip, as previously suggested.
This is an extension to mercurial.
https://www.mercurial-scm.org/wiki/StripExtension
I'm trying to grasp the Mercurial basics so please bear with me. :) My current workflow is as follows:
do some work until I'm ready to commit or need the others' changes
pull
at this point I'd like to merge my work with the latest changesets and commit, however Mercurial insists on me committing before merging
so it goes like "commit, merge, commit" and I'm basically committing everything twice, writing the same notes in both changesets and pushing two changesets at a time
Is it intended to be so? Is it possible to have just one changeset coming from me with every merge? Is it indeed desirable?
I've read a lot of online manuals but still feel I do not have solid understanding of the process. All comments are welcome. Thanks!
EDIT: Turns out I didn't know that update could merge incoming changes with uncommitted edits.
Merging always creates a separate changeset in Mercurial.
Plus, merging is not possible as long as you have uncommitted stuff in your local repository.
So the solution is to commit first, and pull and merge afterwards.
This will always result in two changesets, not one.
(...because merging always creates a separate changeset)
But you don't commit the same stuff twice, and especially you shouldn't write the same commit message twice:
The first commit is what you actually changed ("fixed a bug in the foo bar").
The second commit is just the merge (TortoiseHG actually pre-populates the commit message with "Merge", 99% of the time I just leave it like that).
This workflow will prevent a merge in the history, but you still do a merge as noted below:
Do some work until you are ready to commit or need another's changes.
hg pull
hg update (Note: hg pull -u does this and the previous in one step.
During hg update, your uncommitted changes will be merged with the new tip of your current branch. You will still have to resolve any conflicts.
hg commit when ready.
I still recommend if you have extensive changes to commit first before pulling/merging because it is easier to start over by updating to that changeset if the merge goes badly.
Keeping the hg pull and hg update separate allows you to look at the incoming changesets and predict how the merge will go.
The reason is feels strange is that you delay your commit until you want to integrate with the others.
A big feature of distributed version control is that commits are local. Because they're local you should commit often — commit every time you have a small consistent chunk of work done. Your commits are not inflicted on others immediately so you wont interrupt them by making many small commits.
If you begin making more commits you'll see that your workflow becomes:
$ hg commit -m "Refactoring for Issue123"
$ hg commit -m "Basic functionality for Issue123"
$ hg commit -m "Fixed off-by-one error (Issue123)"
$ hg commit -m "Finished implementing Issue123"
$ hg commit -m "Added more tests for Issue123"
$ hg commit -m "Begin use new function from Issue123"
$ hg pull
$ hg merge
$ hg commit -m "Merge"
Here the ratio of merge commits to "real" commits is much more sensible.
Many people (myself included) like to use the rebase extension to avoid the merge completely. That extension linearizes the commits by faking the history so that it looks like you did your four commits after the changesets you pulled down with hg pull. The only change in workflow is that you hg rebase instead of hg merge above and then skip the final commit.
How do I view commits that are about to be pushed?
I'd made a local commit. Pull a change. And no it requires a merge.
I prefer not to merge and would like to undo the commit,
Pull,
Update changes,
Then commit again.
How do I do it since rollback only undo the last command which is pull?
That's really the way Mercurial works, and you shouldnt fight it in the name of a straight linear history, but there are tools that can edit history. Enable the rebase extension and just run hg rebase after your pull. It will move your local commit to the tip automatically in the simple case you described.
How do I view commits that are about to be pushed?
Use hg outgoing. That shows what hg push would have sent to the server. The opposite command is hg incoming, which shows what hg pull would have retrieved.
I'd made a local commit. Pull a change. And no it requires a merge. I prefer not to merge and would like to undo the commit, Pull, Update changes, Then commit again.
Like Mark says, you're looking for the rebase extension. Enable it with
[extensions]
rebase =
in your config file and then run
$ hg pull
$ hg rebase
to move your local work (this can be multiple changesets, not just a single as in your work around!) on top of the changesets you just pulled down.
How do I do it since rollback only undo the last command which is pull?
Please don't use hg rollback as a general undo mechanism. It's a low-level command that should not be used as much as it is, especially not by new users. The rollback command removes the last transaction from the repository — a transaction in Mercurial is typically the last changeset you made or the last changesets (plural) you pulled into the repository.