I’m trying to create a hook that blocks pushes to a remote repository if you are trying to push more than once branch.
Here’s the hook:
#!/bin/bash
HG_EXE="/opt/csw/bin/hg"
CHANGESETS=`${HG_EXE} log -r $1:tip --template '{node} '`
i=0
for changeset in ${CHANGESETS}
do
BRANCH=`${HG_EXE} log -r ${changeset} --template '{branches}'`
if [ "${BRANCH}" == "" ]
then
BRANCH="default"
fi
BRANCHES[$i]=${BRANCH}
i=$i+1
done
items=${#BRANCHES[*]}
if [ $items -gt 1 ]
then
i=0
while [ "${BRANCHES[${i}+1]}" != "" ]
do
if [ "${BRANCHES[${i}]}" != "${BRANCHES[${i}+1]}" ]
then
echo "ERROR: You are trying to push more than one branch, use \"hg push -b [branch_name]\""
exit 1
fi
i=$i+1
done
fi
The problem:
If I’ve committed on two branches:
changeset: 58:8d2bebe08dd9
user: keshurj <Jay.Keshur#monitisegroup.com>
date: Thu May 26 16:36:49 2011 +0100
summary: commit on default
changeset: 59:43be74e39a44
branch: branch1
tag: tip
user: keshurj <Jay.Keshur#monitisegroup.com>
date: Thu May 26 16:40:25 2011 +0100
summary: commit on branch1
and try to push using hg push –b branch1, the hook still sees ${HG_NODE} as 8d2bebe08dd9, which is on default.
Is there any way to ensure a push is done to only one branch at a time, via a remote hook?
Open to any and all suggestions ( re: this workflow :) )
Have you considered just using an alias like hg nudge:
http://hgtip.com/tips/advanced/2009-09-28-nudge-a-gentler-push/
which is just:
[alias]
nudge = push --rev .
That makes sure you only push your current parent revision and its ancestors. which given the assumptions in your script above would probably all be in the same branch (or requires to push that anyway). You need to create a new habit, but it's pretty direct.
$1 in your assignment to $CHANGESETS is not defined when the script is run - replace it with $HG_NODE
http://www.selenic.com/mercurial/hgrc.5.html#hooks
This assumes you are running this as a pretxnchangegroup hook. (Tested with hg 1.8.1, but I'm reasonably certain that hasn't changed lately).
turns out i was doing something silly....changeset 58 is the parent of changeset 59.
Thanks guys.
Related
We're using Mercurial for source control and would like to introduce automatic merging and changelog for our QA process.
For the purpose of this discussion, let's just assume that we use a simple 3-branch flow like the below:
\ = forward merge
| = back merge of fix
Default --O--A--*--B--*---
\ ^ \
\ | \
QA -----M--X--C--*--N---
^ \
| \
Master --------H--------*---
So the question is:
Is it possible to construct a revset query that, at merge N, preferably before but otherwise after actually performing it, will result in a log of all commits on Default branch, which aren't yet in QA branch (On the above drawing that would be A and B).
Alternatively formulation:
Is it possible to construct a revset query that will return all the changesets, which would get merged if we merged Default into QA.
I'm only interested in commits on Default branch, not individual commits in feature branches already merged into Default (Feature branches not included on the drawing). I am also not interested in commits on the QA branch.
The repository is pretty well maintained, but a couple of years old and contains about 13000 nodes.
I've been experimenting with ancestor and ancestors, but either I get X as the latest common ancestor, or some very very old node. I understand the logic of getting X, but I'm really after O.
The most luck I've had is with variations of this:
hg log --rev "((heads(branch('Default')) and !closed()) % heads(branch('QA'))) and branch('Default') and p2(!branch('QA'))"
hg log --rev "((heads(branch('Default')) and !closed()) % heads(branch('QA'))) and branch('Default') and !children(branch('QA'))"
Update
Using "hg merge --preview --rev XYZ", as suggested by Tom, generates output like this:
changeset: 13070:7e59fc16aa4e
branch: Default
parent: 13068:5b9409ad504f
parent: 13069:849bd43d2023
user: *******
date: Mon Dec 18 18:40:46 2017 +0200
summary: Merged Feature branch A
changeset: 13071:07470ff919ff
branch: Feature branch B
parent: 13061:540eda2c959b
parent: 13068:5b9409ad504f
user: *****
date: Mon Dec 18 18:49:42 2017 +0200
summary: Merge with Default
changeset: 13072:a53260ffabca
branch: Feature branch B
user: *****
date: Mon Dec 18 18:58:05 2017 +0200
summary: Some text
changeset: 13073:37c895f2abf0
branch: Default
parent: 13070:7e59fc16aa4e
parent: 13072:a53260ffabca
user: *******
date: Mon Dec 18 18:58:05 2017 +0200
summary: Merged Feature branch B
Not too bad, but not super fun either. At least it requires more logic than simple grep/sls to parse.
Thanks in advance,
John
I found a solution that mostly does what I want:
hg log --rev "branch('Default') & !ancestors(branch('QA')) & ancestors(heads(branch('Default')) & !closed()) & !children(branch('QA'))"
The only thing now missing is to exclude multiple head merges, since they're just noise in a changelog.
Update:
So I picked this project up again and found some great answers elsewhere on SO.
The following produces output similar to hg merge --preview, but can be templated:
hg log -r "ancestors('$sourceBranch') - ancestors(.)" --template $template
This filters out all commits not on source branch:
hg log -r "(ancestors('$sourceBranch') - ancestors(.)) and (branch('$sourceBranch'))" --template $template
I did a series of dumb steps with my local copy of our shared repository, and I'm looking for a way how to fix it. The steps are:
I used bookmarks to have multiple heads of a development branch, that other people use:
-o---o---o-----o--- <- dev branch
\----1----1------ <- another head of the dev branch,
where I committed stuff
I created a new branch, still local, some time later
/---------------x <- new branch
-o---o---o-----o--- <- dev branch
\----1----1------ <- another head of the dev branch,
where I committed stuff
for one head, that contains only my code, I did a rebase on another branch
/-1'--1'-- <- rebase
/---------------x <- new branch
-o---o---o-----o--- <- dev branch
\----1----1------ <- another head of the dev branch,
where I committed stuff
then, I merged the rebase, and then, a couple of commits later, I merged default
----------d-\ <-default
\
/-1'--1'\ \
/---------------x--------x--x-x-x-- <- new branch
-o---o---o-----o---
\----1----1------
Now, I wanted to push my new branch to the server (hg push --new-branch -b newBranch), but I get abort: push creates new remote head, since commits 1' belong to dev branch.
What is the right thing to do? I would like to avoid creating this additional head.
Update:
Per request, this is the output of hg heads:
changeset: 839:f2033d695fcd <- want to push this
branch: newBranch
tag: tip
user: me
date: Wed Oct 31 13:05:51 2012 +0100
changeset: 826:7fde19d7f467
branch: devBranch
user: my-collegue
date: Tue Oct 23 14:59:42 2012 +0200
changeset: 820:23853bbf68df <- the part after rebase that got merged
branch: devBranch
user: me
date: Mon Oct 22 15:36:26 2012 +0200
changeset: 807:899344cfb145 <- obsolete (branch with 1's)
branch: devBranch
parent: 711:454f29c03fb1
user: me
date: Mon Oct 22 15:36:26 2012 +0200
changeset: 712:d5e8a62a7f5f <- default, needs to stay
parent: 648:2bbcc01aa191
user: me
date: Wed Aug 22 16:21:09 2012 +0200
You can push only the one head you're interested in mercurial. For you that would mean doing:
hg push -r f2033d695fcd
If the destination repo has been updated you need to pull, merge, and re-push:
hg pull
hg up -r <remote head>
hg merge -r f2033d695fcd
hg ci
hg push
I solved the problem, without pushing another head to the repo, and without merging 23853bbf68df onto newBranch. This is probably not the cleanest way to do it, but I will leave it as a reference. In a nutshell, I reconstructed the whole branch by taking all commits and re-applying them.
First, I killed the newBranch'es head 899344cfb145, by using strip on the first diverging revision I did:
hg strip -r 646
Then, I produced email patches (couldn't play with mq) for all commits that are in newBranch, since it's inception:
hg export -g -r 797:808 -r 810 -r 815:822 -r 824:830 -o "%n-%m.patch"
797:808 are the patches in newBranch that were in the rebased part of devBranch (1' commits from the original figure).
810 and 815:822 are other patchs in newBranch. 811:814 belong to a different branch, so I had to exclude those.
823 is a merge commit with default, so I skipped this one.
824:830 are all the commits after the merge with default.
Now, I imported these patches on a new head of newBranch:
hg up -r 796
# there are 29 patches, applying till merge
hg import --bypass {01..21}*.patch
hg up tip
hg merge default
hg ci -m 'merging in default'
hg import --bypass {22..28}*.patch
Finally, I just stripped of the original head of newBranch.
hg strip -r 797
This may not work in every situation. During the merge, I had to resolve some conflicts, too, but pretty benign. Hope this helps someone.
I'm using Mercurial and I have a diff thanks to this command : hg diff
Here's my diff (example) :
diff -r 17899716342e config.js
--- a/config.js Sat Mar 17 14:01:53 2012 +0100
+++ b/config.js Sat Mar 17 18:15:16 2012 +0100
## -8,6 +8,6 ##
];
config.hostname = 'localhost';
-config.port = '3000';
+config.port = '8080';
-module.exports = canfig;
+module.exports = config;
In another repository, I would like to test is this diff can be applied. I know that hg import can import the diff in my current repository.
From the man :
-f --force skip check for outstanding uncommitted changes
--no-commit don't commit, just update the working directory
--bypass apply patch without touching the working
directory
But there's no argument to just check if the patch can be applied without modify my repository (and my working tree).
How can I do that ?
I suggest making a local throw-away clone and apply the patch to it if you don't want to modify your current working directory or repository. I don't think there is a way to do it otherwise.
With extensions you could shelve current changes, apply patch, update clean back to original parent, unshelve changes. Cloning seems simpler.
I like #Mark Tolonen's answer the most -- in Mercurial a new local clone is nearly instantaneous and takes up almost no space due to the use of hard links.
However, if you just can't abide doing so then use the --no-commit option and then revert.
hg import --no-commit the.diff # check if that succeeds or fails
hg revert --all # undo the attempted import
Commited work. Did a rollback when I wasn't on the tip of the repo. Lost files. Looking for git reflog equivalent to save my work.
Long:
So I finish work, then commit.
$ hg commit -A
[...]
adding media/js/fbfriends.js
removing media/js/streams.js
adding media/js/templetizer.js
[...]
$ hg log
$ hg log
changeset: 10:ce3ad416d9a5
tag: tip
user: Mihai
date: Sat Sep 10 22:34:35 2011 -0700
summary: Save all.
changeset: 9:ceacb7b2192a
user: Mihai
date: Sat Sep 10 22:25:34 2011 -0700
summary: Mootools update.
[...]
Some browsing:
$ hg checkout 8
Okay. Then I wanted to change revision 10. Stupidly, I do:
$ hg rollback
rolling back to revision 9 (undo commit)
$ hg status
[...]
M media/js/mootools.js
R media/js/streams.js
! media/js/fbfriends.js
! media/js/templetizer.js
[...]
WTF? Where did my two files go????
$ hg log
changeset: 9:ceacb7b2192a
tag: tip
user: Mihai
date: Sat Sep 10 22:25:34 2011 -0700
summary: Mootools update.
[...]
Okay, i'll figure out the .orig files:
$ hg revert --all
[...]
undeleting media/js/streams.js
forgetting media/js/templetizer.js
reverting media/js/mootools.js
[...] // media/js/fbfriends.js doesn't even show up
$ hg status
? media/js/mootools.js.orig
$ hg history
changeset: 9:ceacb7b2192a
tag: tip
user: Mihai
date: Sat Sep 10 22:25:34 2011 -0700
summary: Mootools update.
NOOOOOO! I've been royally fscked for being on commit 8 when i did the rollback.
Recovery attempts fail:
$ hg checkout 10
8 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ find -name temp
$ hg checkout 9
30 files updated, 0 files merged, 63 files removed, 0 files unresolved
$ find -name temp
$ hg checkout ce3ad416d9a5
abort: unknown revision 'ce3ad416d9a5'!
$ hg checkout ceacb7b2192a
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ find -name temp
$
But, but.. I pushed it in! It should still be somewhere in .hg? Help?
git reflog, git checkout blah saved me in a couple instances but are there any mercurial equivalents?
EDIT: Made summary clear.
EDIT2: Better title.
Sorry, they are gone.
The help summary for the rollback command is:
roll back the last transaction (dangerous)
and goes on to say:
This command should be used with care. There is only one level of
rollback, and there is no way to undo a rollback. It will also restore the
dirstate at the time of the last transaction, losing any dirstate changes
since that time. This command does not alter the working directory.
Rollback is the only non-extension mercurial command that removes history, and there's been talk about moving it to a disabled-by-default extension like it's similarly destructive cousins strip and purge.
Not that it helps you now, but in the future clone before doing a rollback. Or better yet do a backout instead. Backout adds the inverse of a change, yielding the same resulting tip revision, but with a more accurate history ("I did this", "I undid it").
I need to use the hg keyword extension to embed the build date and revision into a source file. Leaving aside the whole "you really don't want to be doing that" argument, how can I do this?
Here's what my source file (lib/foo/version.rb) looks like (which happens to be Ruby, but that's only relevant from the point of view that I don't have a "compile" step in my build which I could do a -DREVISION="$(hg id)" in):
module Foo
VERSION = {
:date => "$Date$",
:changeset => "$Revision$"
}
end
The problem is that $Revision$ and $Date$ are expanded with the changeset and commit date of that file, whereas what I need is the tip changeset and commit date of the whole repository.
I don't see an obvious template I can use in hg help templates, nor does the keyword extension mention anything with global scope. Is what I'm trying to do possible?
You can install a post-commit hook that updates the file:
[hooks]
post-commit = sed -i lib/foo/version.rb \
-e "s|\$Date.*\$|\$Date: $(date)\$|" \
-e "s|\$Version.*\$|\$Version: $(hg id -i)\$|"
You should then probably add the version file to the .hgignore file -- it will change after every commit and thus always be dirty. You could also add a encode filter that will clean up the version file:
[encode]
lib/foo/version.rb = sed -e "s|\$Date.*\$|\$Date\$|" \
-e "s|\$Version.*\$|\$Version\$|"
This script will make Mercurial see the file as clean -- no matter what date and changeset has it really contains, Mercurial will see it as containing un-expanded $Date$ and $Version$ keywords:
$ hg commit -m test
$ hg tip
changeset: 7:df81c9ddc9ad
tag: tip
user: Martin Geisler
date: Wed Apr 06 14:39:26 2011 +0200
summary: test
$ hg status
$ hg cat version.py
date = "$Date$"
version = "$Version$"
$ cat version.py
date = "$Date: Wed Apr 6 14:39:26 CEST 2011$"
version = "$Version: df81c9ddc9ad$"
If you're running your code from a checkout you can invoke hg directly and cache the value. Something like:
module Foo
VERSION = {
:version => system("hg log --template '{note|short}-{latesttag}-{latesttagdistance}' -r .")
}
end
and if you're not running the code from inside a checkout on a system with Mercurial installed, then your deploy script can easily get/use the value -- perhaps by using hg archive to get the tarball to send which then automatically includes a .hg_archive.txt.
I guarantee you there's a prettier way to do this than the keywords extension no matter what your setup is.