mercurial (hg) modify incoming changeset date in a hook on a push - mercurial

Using Mercurial.
Does anyone know if there is a good way to modify a changeset's commit date via a hook, such as pretxnchangegroup, during an incoming push ?
Once a change comes in to a repo the changeset dates are always the commit dates. I'd like to changes/override these dates to the 'push' dates instead -- via a hook during a push.
It seems that ctx.date() is a tuple and immutable.
Thank you.

Commit date, author as well as the patch content itself make up the changesetID. Thus changing either will change the changesetID - and as such they cannot be modified on an existing changeset.
Mercurial, like most modern VCS, is a distributed version control system, thus the commit date is recorded as the time received to any arbitrary repository is arbitrary at best while the commit time and date are global as they are recorded once only and don't change on propagation through different repositories.
You should ask yourself why you need that. If you worked on a patch series which you then want to push to an authorative project repo, do the changes before you push by updating (commit --amend) the still-draft changesets on the author's side. You can even integrate a hook server-side to enforce this policy which checks the age of the commits received and deny push when they are older than XX minutes.
However if you really want to realise the bad idea you ask about:
If you operate on a non-publishing repository, and if you receive draft changesets, then you might get away with creating new commits with an updated timestamp from the changesets you just received, obsoleting the old ones. Using the evolve extension might make this work a lot easier.
If you receive public changesets, or if your repository is publishing, then doing these things with rebase and commit --amend would seem very troublesome and I would not enjoy contributing as I would end up with everything I wrote and pushed duplicate in my local repository.

Related

Mercurial: amend previous commit dates

Is there a way of changing the commit dates of multiple draft changesets? In particular, changesets before the last one.
I can change the commit date of the last commit with hg ci --amend -d xxx, but can't do so for any earlier ones.
This is obviously non-trivial, since the changeset date is one of the inputs to the changeset hash (where is this authoritatively documented, by the way?), and so a change here would change the hashes of all of a changeset's descendants. Since these are draft changesets, however, that would be OK.
It doesn't seem possible to do this using histedit.
I can guess that rebase might be able to do this sort of thing, but the associated help-text doesn't give any pointers, which suggests that it's at least exotic.
(The problem I'm trying to deal with is that some code is being edited and committed on a machine which is, deliberately, not network connected and which is frequently rebooted; that means that its notion of the system time can be wrong, and sometimes very wrong – as in 1970! – unless someone remembers to set the system date by hand to a reasonable value. While this doesn't matter to the topology of the commit graph, it would be at least nice, for everyone's sake, if the dates bore some relation to reality. Hence my wish to fix this in a ‘review before push’ step.)
Well, there's one approach which you already lined-out yourself basically, but it requires activation of the evolve extension: Set the date for one commit, and rebase all subsequent ones, and repeat until you have set the date for each. Assuming linear history:
hg update -rOLDEST_UNCHANGED
hg commit --amend --date DATA
hg rebase -b(OLDEST_UNCHANGED+1) -dtip
rinse and repeat with increasing the changesetID by one in each step. If your history is not linear, you will have to pay attention to the revisions you rebase and which you update to.
The second alternative, but not much better, is is making use of the evolve command from the evolve extension. That helps you with this process, making the steps outlined above slightly easier:
Still you have to start with the first of those commits you want to refresh:
hg update --rev OLDEST_UNCHANGED
hg commit --amend --date DATE
hg evolve --all
And repeat this process again with increasing changesetID until each commit has the date you want.
(If there is some way to set/refresh the commit date using evolve for all commits it evolves, I'm happy to learn - it definitely is a feature request otherwise)
For those using TortoiseHG (which doesn't know the evolve extension, and even if it did, there's a simpler way),
Use the mq extension.
Make sure your working directory is clean, (protip: use the shelve extension)
Import all changesets into MQ that you want to change,
Double-click on any changeset that you want to change, so it becomes the top applied one,
Use the Options button to the left of the QRefresh button to set a new date/time,
Hit QRefresh,
Repeat from 3.
Don't forget to uncheck the date change in the options dialog, lest bad things happen.

Mercurial: Pull changes from remote repository without public phase (non-publishing server, "abort: can't rebase immutable changeset")

Background
Mercurial now has Phases which are a great mechanism to keep people from altering history that should not be altered. When a changeset is pushed to remote repository it is made public and can no longer be rebased. This is normally a good thing, if the repository is public and you don't want others altering your changesets.
However, if you have your own temporary repository for sharing draft changesets only with yourself, it can get very annoying. Mercurial has a option to turn this off. Adding this to the .hg/hgrc file of the remote repository.
[phases]
publish = False
This will prevent changesets pushed to the remote repository from moving from draft phase to public phase (both the local changeset and the one just pushed to the remote repository. However, pulling this changeset down still will always pulls it down in the public phase.
Question
I am hoping to pull down changesets in the draft phase. I simply want to push a changeset to my personal server then pull it down at home. After I pull it I will rebase my temporary commit on top of any commits that I have pulled down from our true publishing server.
Any way to avoid the auto-moving of a pulled changeset to public would be great. This remote repository is a complete and total draft server for myself. Being forced to manually move the changesets back to draft after an unsuccessful rebase attempt is really starting to wear on the nerves.
References
https://www.mercurial-scm.org/wiki/Phases
Introduction to Mercurial Phases
How do I tell (locally) mercurial that a server is non-publishing?
After pushing to a review repository, "abort: can't rebase immutable changeset" on rebase
That seems like a bug. What version of Mercurial are you using? Have you tried filing a bug at https://bz.mercurial-scm.org ?
I got this resolved through help on Bugzilla. Here is a snippet of my final post there (thanks for putting me on the right track djc).
It looks like we can chalk it up to User Error/Bad Test Cases (of
course). If there is anyone to blame (besides myself) it could be
TortoiseHg. My test cases consisted of stripping commits and from one
of the two repositories and then changing the phase of that commit in
the other repository and push/pull again. It seems like TortoiseHg
would pull in the wrong phase sometimes (possibly a caching issue). I
can still reproduce this when I switch my server back and forth
between publishing and non-publishing (but not quite as consistently
before).
However, doing a command line pull seems to get it working every time.
I was using command line to check outgoing/incoming, do the phase
changes and such, but possibly never used it for the actual pull.
Sorry to waste everybody's time. I resolved it as invalid, not sure if
someone wants to change it to a better category. Let me know if there
is anything else you want me to check or expand upon.
I'll edit in any updates, let me know if anyone is still having trouble with this.

Pull commits on repo post-rebase

I'm looking for a simple way to pull in additional commits after rebasing or a good reason to tell someone not to rebase.
Essentially we have a project, crons. I make changes to this frequently, and the maintainer of the project pulls in changes when I request it and rebases every time.
This is usually okay, but it can lead to problems in two scenarios:
Releasing from two branches simultaneously
Having to release an additional commit afterwards.
For example, I commit revision 1000. Maintainer pulls and rebases to create revision 1000', but at around the same time I realize a horrible mistake and create revision 1001 (child of 1000). Since 1000 doesn't exist in the target branch, this creates an unusable merge, and the maintainer usually laughs at me and tells me to try again (which requires me getting a fresh checkout of the main branch at 1000' and creating and importing a patch manually from the other checkout). I'm sure you can see how the same problem could occur with me trying to release from two separate branches simultaneously as well.
Anyway, once the main branch has 1000', is there anything that can be done to pull in 1001 without having to merge the same changes again? Or does rebasing ruin this? Regardless is there anything I can say to get Maintainer to stop rebasing? Is he using it incorrectly?
Tell your maintainer to stop being a jacka**.
Rebasing is something that should only be done by you, the one that created the changesets you want to rebase, and not done to changesets that are:
already shared with someone else
gotten from someone else
Your maintainer probably wants a non-distributed version control system, like Subversion, where changesets follows a straight line, instead of the branchy nature of a DVCS. In that respect, the choice of Mercurial is wrong, or the usage of Mercurial is wrong.
Also note that rebasing is one way of changing history, and since Mercurial discourages that (changing history), rebasing is only available as an extension, not available "out of the box" of a vanilla Mercurial configuration.
So to answer your question: No, since your maintainer insists on breaking the nature of a DVCS, the tools will fight against you (and him), and you're going to have a hard time getting the tools to cooperate with you.
Tell your maintainer to embrace how a DVCS really works. Now, he may still insist on not accepting new branches or heads in his repository, and insist on you pulling and merging before pushing back a single head to his repository, but that's OK.
Rebasing shared changesets, however, is not.
If you really want to use rebasing, the correct way to do it is like this:
You pull the latest changes from some source repository
You commit a lot of changesets locally, fixing bugs, adding new features, whatnot
You then try to push, gets told that this will create new heads in the target repository. This tells you that there are new changesets in the target repository that you did not get when you last pulled, because they have been added after that
Instead, you pull, this will add a new head in your local repository. Now you have the head that was created from your new changesets, and the head that was retrieved from the source repository created by others.
You then rebase your changesets on top of the ones you got from the source repository, in essence moving your changesets in the history to appear that you started your work from the latest changeset in the current source repository
You then attempt a new push, succeeding
The end result is that the target repository, and your own repository, will have a more linear changeset history, instead of a branch and then a merge.
However, since multiple branches is perfectly fine in a DVCS, you don't have to go through all of this. You can just merge, and continue working. This is how a DVCS is supposed to work. Rebasing is just an extra tool you can use if you really want to.

Mercurial: what are the consequences of changing a phase?

In Mercurial, the phase of a revision can be changed arbitrarily. What are the consequences of a phase change for all possible transitions (public, draft, secret) x (public, draft, secret)? Which phase changes are safe? Which may cause troubles and which kind of troubles? Which are more or less no-ops?
The default phase for a commit is draft, that's how they were treated when the phases didn't exist. When pushing a draft changeset, mercurial automatically changes its phase to public. You can use it to know which changesets you have already published or not.
But the real issue is that when the changeset is in public phase, mercurial won't let you change them with history editing extensions (like mq, rebase, etc). That's really important, because the history editing happens only in the local repository, they don't propagate with pull/push operations. So, once a changeset is published, it's out of control, it's dangerous to change it.
You can change from any phase to any other phase. The "normal flow" is to move to a higher phase (secret->draft->public), but Mercurial allows the change to a lower phase with the --force option. A phase change alone is harmless. For example, the only thing that happens when moving from public to draft or secret is that the protection of history editing is dropped, nothing else, pull and push will still work normally, Mercurial could never be confused about the changesets because they have unique identifiers. The history editing action that takes place after a phase change like this is what can cause problems. And that's why Mercurial gives the warning in the phase change and requires the --force option, as a confirmation that that's what you really want.
In general published commits shoudn't be modified, that's what the phases try to ensure. But maybe you do have control over all repositories. Or maybe you pushed something and you know that no one else pulled it yet. Whatever is the reason, you have the option of forcing the phase of the changesets back to draft and edit them. But this edition must be done in every repository that has the changesets.
A public revision can't be edited with history-editing tools (i.e. mq, rebase).
A secret revision can't be pushed to another repository (it will be ignored when you give the push command, or when another repository attempts to pull).
A draft revision allows both, but will automatically change to public if it gets pushed to another repository.
The purpose of the phase system is to prevent you from modifying a revision after you've pushed it to another repository, which is a bad idea unless you're able to delete the old version of the revision from all repositories that it's been pushed to.
All phase changes are more or less no-ops; it's just a marker to indicate what is or isn't safe to do with a revision.

HG workflow : how to pick/abandon single changesets from branch clones

I think it is common practice of many development teams to create new features or bugfixes in a separate feature clone repository and pull and merge back if the feature/bugfix is ready.
However, sometimes I don't want to include all of incoming changes. To harness all the power of DVCS to review incoming changes I think it is necessary to be able to fully modify and select single aspects of the incoming changes.
What's the preferred workflow for this scenario?
As far as I know transplant extension offers the possibility to pick single changesets but I would also like to prune/pick on a path/file base e.g. to exclude some test scripts or stuff like that, that isn't required for the final feature anymore.
Since transplant is an extension, what's the official way to have a "clone, change, ... review" cycle in HG?
Mercurial is changeset-centric VCS, thus - operational object is changeset, cherry-picking inside changeset not possible at all
In Mercurial you can't discard some of incoming changesets in bundle (but can do in Git, AFAICR)
It's not official way in any form, just personal POV: because pull produce anonymous branch, before merging this branch with mainline you can edit branch content in order to exclude unwanted parts, using
histedit - delete changests, join
MQ - remove changeset from branch, edit content of changeset (convert to MQ-patch, edit, finish, reorder)