Mercurial - race condition when pushing with subrepos - mercurial

With a fairly basic subrepo structure like this:
parent
parent/subrepo1
parent/subrepo2
Race conditions tend to occur when multiple users push changes at the same time. Simple example:
User 1 & 2 begin pushing.
User 1 skips parent/subrepo1, no changes.
User 2 pushes to parent/subrepo1, with changes.
User 1 pushes to parent/subrepo2, with changes.
User 2 tries to push to parent/subrepo2, but cannot because it will create multiple heads.
User 1 finishes pushing parent.
The result of this is that User 1 managed to push everything successfully, but User 2 partially pushed and then failed. This left a head on parent/subrepo1 which doesn't have a corresponding commit in parent, which will cause problems for the next person to push parent/subrepo1 unless User 2 discovers their error, pulls parent, merges, commits, and then attempts to push (but there's no guarantee another user won't attempt to push at the same time again!).
My question: Is there a way within Mercurial to force users to wait while a push operation is underway on a Mercurial repository with other subrepos (to effectively implement a mutex) OR is there a way to undo a failed push on the target's changed subrepos when a user fails to push.

Related

Avoiding conflicts when two workflows push to the same branch in GitHub Actions

I have two workflows that are triggered at the same time and both push a commit to a branch.
While there's no risk of a merge conflict because both commits affect different files, the problem is that there's no merge at all. The two simultaneous commits result in diverging history on that branch, and only one of the commits is retained in the branch. The other is lost.
E.g. when branch gh-pages is at commit A, and two simultaneous workflows push commits B and C respectively:
What I'd like is for both commits to be retained on the branch. It doesn't matter what order they're in so long as they're there.
I can think of a couple of ways this might be possible:
The workflows need to be run in series rather than in parallel. I found a couple of Actions that could be used for this, e.g. https://github.com/lewagon/wait-on-check-action, but these actions seem to require that the specified workfow be run or have finished running while the other action takes place. I can't guarantee this - either of my two actions could also be started individually.
There might be a Git-based way of getting around the issue - e.g. if I can find out the SHA of both commits, I can subsequently create a merge commit. But the two workflows, while started simultaneously, don't know about each others' existence... right?
Using concurrency and assigning both workflows to the same concurrency group such that only one can run at a time. But, the maximum queue length is 1 - there can only be one pending workflow at a time (any existing pending workflow is cancelled). This seems safe for 2 workflows, but does it scale to a hypothetical case of 3 or more simultaneous workflows (or even 3 workflows initiated within a few minutes of each other, depending on how long they take to complete)?
Given that the two workflows both create a commit on a given branch, and can be started simultaneously but could also be started individually, how can I make sure that both of those commits end up in the history of that branch?
I think the most modern approach is concurrency - using a proper key for it you can put as many workflows as you need into a queue.
Before concurrency, the best approach that was working for me was to detect problems on the GIT level.
You run all those workflows and let them push - there is nothing like simultaneous commits - one of them will be first the second one will be rejected.
I was implementing proper rejection handling - if git refuses to take a commit - you just retry it.
In case you need to regenerate your data taking into account master branch state - just git reset, git pull - try again - until it works.

Automatically merge new heads on push

Is it possible to react to a push to a repository with a hook and merge all new heads that are created by this push?
My use case for this is the following:
I'm trying to design a repository that would only allow very specific changes with a commit hook. The basic idea is that it is only allowed to replace existing files with better files (better is something that can be checked by an external program). Each user could clone a central repository and commit changes to the clone. When a user pushes his changes to the central repository, a hook checks whether the quality increases along each branch and rejects the push otherwise. If users push out of sync, this will create multiple heads that could in theory be merged automatically (taking the best version of each file).
Your hook can do whatever you want, in principle. Write a program that handles the evaluation and merging you describe, and associate it with the changegroup hook, which is triggered whenever someone pushes a group of changesets to the repo, or the incoming hook, which is executed separately for each arriving changeset.

Remove a bad commit from branch in mercurial

I've got the following problem:
I have 2 named branches which are 2 completely versions of the product (e.g A and B) than at some moment I was needed to introduce changes from A to B so I started the following command:
hg merge A
after that commit and push ... and instead of 2 branches I've got 1 that was unexpected for me since I wanted just update B with changes in A. Somehow I managed to get again 2 branches and now I want to delete last commit in branch B and cant find the best way (I'd say any way) to do it.
Thanks.
Just continue commiting to the branch that you want stil alive. There is a command to explicitely kill a branch but if you don't use it, all branches are still alive in parallel. If you don't see them it's only because the tool to see them don't show them all if they are not different, but they're still present. Just continue to work with those branches and it will be fine.
Now to remove a changeset, you "can't" as in really removing it, but you can commit an exact inverse of that specific changeset. The command to do this is backout.

Branch by cloning - Must I make modification on default in order to see the branch?

I am coming from CVS background.
I try to perform branch by cloning.
The current default tree looks like this from hello project.
I try to clone a project out from 'hello' to 'hello-branch-by-clone'.
I did modification on 'hello-branch-by-clone' and commit.
I did not do any modification on 'hello'.
I perform push from 'hello-branch-by-clone' to 'hello'.
I expect to see a branch but I didn't.
This time, I try another way around.
I did modification on 'hello-branch-by-clone' and commit.
I did modification on 'hello' and commit.
I need to pull from 'hello' to 'hello-branch-by-clone', and merge.
I perform push from 'hello-branch-by-clone' to 'hello'.
This time, then only I can see the branch
By applying cloning technique, is there any way I can have a branch view, without having explicitly modify the default repository (hello)
There's no fork because no two changesets share a parent.
A cloned repository isn't special in any way. It's identical to the original. Commits to it are identical to commits in the original repo. They aren't notionally tagged as being on a branch. A clone is just a nice way of having another work area that you can do some work (including commits) without effecting the original.
A fork occurs when two or more commits have the same parent. Often this happens when using clones, but it may not. If there's only one changeset with the same parent, there is no fork.
After your first sequence you've introduced just one changeset (4) which is which has the old tip (3) as it's parent, so it's still a straight line. Only when you introduce a second changeset parented by (3) will you see a fork.
Now remember, even though you 'push'ed the changeset back, and the original "Hello" repo contains all 4 changesets, it's working directory is still pointing to changeset (3). It will stay that way until you run 'hg update' inside it. This means that if you were to make a commit in "Hello" it will be based upon (3), and then a fork will appear. It doesn't matter when this commit is made.
This is what you did in your second sequence.
Hope that helps.
I've tried to use the term 'fork' in this, because 'branch' has lots of meanings, including the 'hg branch' command which does some slightly different things.

Mercurial - Merge parent changes into child fork as individual commits?

My desire is to keep my fork up to date with the parent, and ideally record the parent's individual commits + messages, so that I have a full history in the fork, of what changed in the parent.
So essentially, when you start a fork and you see the parent's entire commit history as the "starting point" for your fork, I would like to keep the parent's commit history ongoing in the fork, with my fork-specific commits interspersed (with conflict resolution as needed).
Is this possible?
Or if that's not possible, is there a way to script an automatic pull & merge of parent changes into the child fork, while coalescing the parent's commit messages into the new merge's commit message? (so all individual messages from imported commits, would be combined into one massive merge message)
If I am not mistaken, mercurial and git share a lot in terms of data structures used for history and, in git at least, merging does just what you ask for. Just make sure that your merge commit (or changeset, as hg calls it) actually links to both parent commit (the mostrecent parent project commit and your fork's most recent commit), and that's it.
After merging, check upon the tree with, for example, hgk. It should look like:
http://wiki.genunix.org/wiki/images/3/3f/Screenshot-hgk.png