logging mercurial transactions - mercurial

this is a small addition to the previous script, and this time I would like to log details for the backup.
script /tmp/commit-push-log
# add all files to the repository
for REPOSITORY in $#
do
cd $REPOSITORY
# commit the changes
hg commit -A -m "Commit changes `date`"
# push the changes to the remote repository
if hg push
then
logger hg push completed without failure
else
logger hg push fails
fi
done
exit
cat /tmp/commit-push-log | logger
rm /tmp/commit-push-log
the problem is that i don't see any mercurial messages in the log. What can go wrong in my script?

You should not use static tmp filenames. Use mktemp, it's far safer.
You should cd "$REPOSITORY" instead of "cd $REPOSITORY" or things will get funny when REPOSITORY will contain any spaces or special characters.
You should not write automated commit comments. See here for the great article on this topic.
hg probably outputs errors to stderr. Use hg commit -A -m "$comment" 2>&1 and hg push 2>&1

my current version
for REPOSITORY in $#
do
# new temp file
OUTPUT_LOG=`tempfile`
echo -n > $OUTPUT_LOG
# checks whether $REPO is a repo
if [ ! -d $REPOSITORY/.hg ]; then
echo "Not a repository: $REPOSITORY"
exit 1;
fi
# change to that dir
cd "$REPOSITORY"
logger "Repository: $REPOSITORY"
# commit the changes
hg commit -A -m "Commit changes `date`" 2>&1 >> $OUTPUT_LOG
# push the changes to the remote repository
if hg push 2>&1 >> $OUTPUT_LOG
then
logger hg push completed without failure
else
logger hg push fails
exit 1;
fi
# log the contents and delete the tempfile
cat $OUTPUT_LOG | logger
rm -f $OUTLOG_LOG
done
exit 0

Related

Amending a commit with the effects of hg cp --after

I have a file before.txt, which I want to split into two smaller files. What I should have done was
$ hg cp before.txt part1.txt # Then edit part1.txt
$ hg mv before.txt part2.txt # Then edit part2.txt
$ hg commit
Then, both part1.txt and part2.txt will have before.txt as part of their history, so the diff will show as deleting parts of a larger file, rather than deleting one file and creating a new one with some of its contents.
However, what I actually did was
$ cp before.txt part1.txt # Then edit part1.txt
$ hg mv before.txt part2.txt # Then edit part2.txt
$ hg commit
So before.txt is only present in the history of one of my two files. If I hadn't run hg commit, it seems clear to me that I could solve my problem with
$ hg cp --after before.txt part1.txt
or something similar to that. And I haven't pushed this commit upstream, so I should be able to edit it however I like. But I can't figure out how to do it. When I run that hg cp, I see:
$ hg cp --after before.txt part1.txt
before.txt: No such file or directory
before.txt: No such file or directory
abort: no files to copy
This makes sense: it can't record that edit as part of a new commit, because the source file doesn't exist. I want to record it as part of the previous commit, but I don't know how to do that except by recording it as part of a new commit and then amending it into the previous commit.
Here's one way to fix that situation:
$ hg shelve # get any miscellaneous, unrelated changes out of the way
$ hg up <parent of revision with the mistake in it>
$ hg cp before.txt part1.txt
$ hg mv before.txt part2.txt
$ hg revert -r <revision with the mistake in it> --all
$ hg commit
$ hg strip <revision with the mistake in it>
(I didn't actually try all these commands, hopefully no typos!)
The first step is optional depending on the state of your working directories.
Now part1.txt and part2.txt should have the correct contents. The use of revert was just to save having to manually re-edit the file changes. But you could also just redo it manually if that seems easier.
The use of revert to pull into the working folder the effects of another changeset is a trick I use a lot. Its like a manual way of amending which gives you total flexibility. But it only works well when the revision you are reverting your working copy to is closely related to the revision which is the parent of the working copy. Otherwise it will create numerous nuisance changes.
based on #DaveInCaz answer, here is a MCVE
mkdir tmpdir
cd tmpdir
hg init
echo line1 > before.txt
echo line2 >> before.txt
hg add before.txt
hg commit -m "my before"
cp before.txt part1.txt
hg add part1.txt
hg mv before.txt part2.txt
echo line1 > part1.txt
echo line2 > part2.txt
hg commit -m "my bad"
hg shelve
hg up -r -2
hg cp before.txt part1.txt
hg cp before.txt part2.txt
hg revert -r -1 --all
hg commit -m "my good"
hg strip -r -2
some remarks:
twice hg cp, because the later revert will delete the file before.txt for us, otherwise it would complain about missing it
revert --all at least in my version it requires to specify what is being reverted
the hg shelve is to be safe, before switching over to a different revision
the hg up -r -2 could have -C since previous shelve made us safe, that way you can retry final steps with different approaches and see what suits you better

Create a Mercurial alias that runs two commands and takes one paramater

I'm trying to create an alias and allows me to commit my changes and push all changesets for the current branch.
I'm running this from a Windows command prompt.
I've read this question and this question and so far have this:
ci-push = !hg ci -m $1 && hg push -b .
When I try this i get the error:
abort: Commit: The system cannot find the file specified
If I try:
ci-push = !hg ci -m %1 && hg push -b .
then it appears to work (prompts for auth and pushes the commit), but my commit message is:
%1
Is this even possible from a Windows cmd prompt?
On Windows, %USERPROFILE%\mercurial.ini:
[alias]
ll = log -l$1
Testing:
>hg ll
abort: too few arguments for command alias
> hg ll 5
changeset:...
Shell alias (%USERPROFILE%\mercurial.ini):
[alias]
ld = !hg log -r $1 && hg diff -r $1
Testing:
>hg ld 154
changeset: 154:5bb3aba44eab
....
diff -r 5bb3aba44eab ....
P.S.
When using $N with spaces you should use quotes (!hg ci -m "$1" ... in aliases).

Can I disable the default behaviour `hg add` with no arguments?

When I run hg add with no arguments, it is always by mistake, and the result, adding all of the files and directories recursively, is horribly annoying and difficult to undo, especially when other files have been (correctly) added since the last commit. Is there any way to make a plain hg add just print an error message?
Try putting this in your ~/.hgrc:
[defaults]
add = -X .
That tells hg add that unless specifically named it should ignore all files (got matches all). Here's an example:
(df)Ry4ans-MacBook-Air:~ ry4an$ hg init test
(df)Ry4ans-MacBook-Air:~ ry4an$ cd test/
(df)Ry4ans-MacBook-Air:test ry4an$ vi ~/.hgrc # added the section above
(df)Ry4ans-MacBook-Air:test ry4an$ hg status
(df)Ry4ans-MacBook-Air:test ry4an$ echo this > that
(df)Ry4ans-MacBook-Air:test ry4an$ hg add # nothing added
(df)Ry4ans-MacBook-Air:test ry4an$ hg status
? that
(df)Ry4ans-MacBook-Air:test ry4an$ hg add that
(df)Ry4ans-MacBook-Air:test ry4an$ hg status
A that
In general though, you should just make your .hgignore robust enough to ignore all the files you don't want added
In your user config file (~/.hgrc), add the following to your [alias] section:
[alias]
realadd = add
add = add --dry-run
Now, just hg add will always do a dry-run. To actually add, you have to use hg realadd. Note that you could redefine the add alias to do anything, it doesn't have to be add --dry-run.
I don't know a way to do this purely with Mercurial configuration, but if you're willing to tune your bash profile, then you can redefine the hg command as a function. The function would either detect hg add and fail or otherwise do a passthrough to the real hg command.
function hg() {
if [ "$#" -eq 1 ] && [ "$1" = "add" ]; then
echo "hg add with no arguments denied" 1>&2
false # sets exit code to 1, but doesn't close process like exit would
else
command hg $#
fi
}
Here is what it looks like in action after I source in the new function from my profile:
hg > /dev/null; echo $?
0
hg add > /dev/null; echo $?
hg add with no arguments denied
1
hg add . > /dev/null; echo $?
0
hg status
touch afile
hg add afile
hg status
A afile
You can undo a global add using the following command:
hg forget $(hg status -an)
Here, hg status -an will list all added files. hg forget will then remove those files from the list of added files.
You can also create an alias for this in your .hgrc, e.g.:
[alias]
unadd = !$HG forget $($HG status -an)
Note that this will also delist all previously added files that you did mean to add, so you may have to redo that.
Also, operating systems and shells have limits for how many arguments can be passed to a command. If you run into this limit because you accidentally added more than a few thousand files, you can use xargs instead:
hg status -an | xargs hg forget
Or, as an alias:
[alias]
unadd = !$HG status -an | xargs $HG forget

How do you check if a mercurial repo is in a clean state?

As a user, I usually use hg st to check the status of a repo, and verify that it is in a clean state, with no modified files.
Here I would like to do this programmatically. I know I can also use hg st for that, but the output is less than ideal for consumption by a computer program. Is there a better way to check whether a mercurial repo is in a clean state?
If you issue the hg identify --id command, it will suffix the ID with a + character when the repository has modified files. (Note: this flag does not report untracked files.)
If you grep the output of this command for the + character, you can use the exit status to determine whether there are modifications or no:
$ hg init
$ hg identify --id | grep --quiet + ; echo $?
1
$ touch a
$ hg identify --id | grep --quiet + ; echo $?
1
$ hg add a
$ hg identify --id | grep --quiet + ; echo $?
0
You should use hg summary:
$ hg init
$ echo blablabla > test.txt
$ hg summary
parent: -1:000000000000 tip (empty repository)
branch: default
commit: 1 unknown (clean)
update: (current)
Most major programming languages have HG APIs you can access.
This answer might be useful for other people searching this topic:
I agree to #SteveKayes comment above that hg status is a good command for programmatic consumption.
Here is an example how to use it in a bash script:
#!/bin/bash
set -e
cd /path/to/hg-repo
repo_status=`hg status | wc -l`
if [ $repo_status -ne 0 ]; then
echo "Repo is not clean"
else
echo "Repo is clean"
fi

Tortoise HG - Add a tag on commit

At the moment, I only know how to add a tag after a commit. This means that a get a second commit that just contains the tag. Is it possible to add a tag on commit?
No, because a tag is an entry in the .hgtags file at the root of your repository containing the changeset id and the tag name, and this file itself is under revision control. The changeset id isn't known until the changeset is created, so tagging creates another changeset to check in the .hgtags file recording the tag for that changeset.
According to mercurial wiki, it is impossible. Just like Mark said.
https://www.mercurial-scm.org/wiki/Tag
But then, i just wondering. Why not mercurial ignoring the .hgtags file altogether ? just like it ignores .hg/ folder.
So mercurial won't include .hgtags everytime it's generating a changeset ID.
Would be great if .hgtags, .hgignores, etc is resides inside .hg/
For my point of view, Hg tagging system is a bit messy because creating a tag changes the history and needs merging and committing even if no project file has changed. Tagging can burden a history graph very quickly.
I was used of SVN tagging which was done on a separate branch, which has the advantage not to change working branch history. Moreover, tagging can be done from any branch, because Hg takes tags from the .hgtags files on the heads of all branches.
The little script below creates a branch "tagging" and puts tags in it. It merges the current branch in "tagging" branch, so it's easy to see the changeset tag was done from (it especially avoids long refreshes when switching branch).
It can probably be improved, but it suits my needs.
I strongly recommend making a clone of your project before testing this script, in case it does not behave as you expect!
#!/bin/bash
function echo_red()
{
echo -n -e "\e[01;31m"
echo -n "$1"
echo -e "\e[00m"
}
export -f echo_red
# Display the help and exit
function show_help {
echo "Usage: $0 [hg_tag_options ...]"
echo " tags a version (current if not specified) in the 'tagging' branch."
echo " Options are the 'hg tag' ones, plus"
echo " -?, -h, --help Show (this) help"
exit 1
}
# Parse the command-line arguments
function parse_args {
for arg in "${commandline_args[#]}"
do
case "$arg" in #(
'-?' | -h | --help )
show_help
;;
esac
done
}
commandline_args=("$#")
if [ "$commandline_args" = "" ]
then
show_help
fi
parse_args
VER=`hg id | sed 's#\([0-9a-z]*\).*#\1#g'`
BRANCH=`hg branch`
# Check for clean directory
TEST=`hg st -S -q`
if [ "$TEST" != "" ]
then
echo_red "Directory contains unresolved files !"
exit 1
fi
hg update --check >/dev/null
if [ $? -ne 0 ]
then
echo_red "Directory contains unresolved files !"
exit 1
fi
# Switch to tagging branch
hg update tagging >/dev/null
if [ $? -ne 0 ]
then
echo "Creating new 'tagging' branch."
hg update default >/dev/null
hg branch tagging
fi
# Merge if changes detected
TEST=`hg diff -r $VER -X .hgtags --stat`
if [ "$TEST" != "" ]
then
#take only the 'tagging' version of hgtags
cp .hgtags .hgtags.bak
hg merge -r $VER --tool internal:other >/dev/null
rm .hgtags
mv .hgtags.bak .hgtags
hg commit -m Merged
fi
# Tag and Switch back to original
hg tag -r $VER $#
hg update $BRANCH >/dev/null
hg update $VER >/dev/null
Example Usage:
hg_tag.sh [-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] test_v1_5
Not sure if this is what you are looking for, but you can move the tag to a different changeset.
hg com -m "moving tag to this changeset"
hg tag 0.1 -f
hg push