Mercurial: multiline commit message on the command line? - command-line

How can I specify a multiline commit message for mercurial on the command line?
hg commit -m "* add foo\n* fix bar"
does not work. The log shows:
changeset: 13:f2c6526e5911
tag: tip
date: Fri Jan 23 23:22:36 2009 +0100
files: foobar.cpp
description:
* add foo\n*fix bar

Mercurial: multiline commit message on
the command line?
Hit enter.
$ hg commit -m "Did some work
> Now I'm done"
One of the things is that only the first line shows up in hg log:
$ hg log
changeset: 0:d2dc019450a2
tag: tip
user: Aaron Maenpaa <zacherates#gmail.com>
date: Sat Jan 24 07:46:23 2009 -0500
summary: Did some work
... but if you fire up "hg view" you can see that the whole message is there.
Edited to add:
... but hg -v log shows the whole message:
$ hg -v log
changeset: 0:d2dc019450a2
tag: tip
user: Aaron Maenpaa <zacherates#gmail.com>
date: Sat Jan 24 07:46:23 2009 -0500
files: work
description:
Did some work
Now I'm done

From Windows cmd, do this command for a multiline commit.
hg commit -l con
This allows you to enter a multiple line commit message straight from the command line. To end your message, hit Enter, and on a line by itself hit Ctrl + Z and Enter again.
Why? The -l option to hg commit says to read the commit message from a file, and con specifies that the file is actually the console.

If you're doing it interactively (vs. from a script), just do hg commit without the -m flag. I'm not sure what the behavior is on Linux or Mac, but on Windows it pops up Notepad with a file that you fill out and save for a multiline message.

I know, there is already a solution for linux users, but I needed another solution for windows command line, so I was looking for it...
And I found one:
https://www.mercurial-scm.org/pipermail/mercurial/2011-November/040855.html
hg commit -l filename.txt
I hope, it's useful for someone out there ,)
[EDIT]
oO - it has already been added to the help
-l --logfile FILE read commit message from file

Here's another way that is more close to what you tried at first:
hg commit -m "$(echo -e 'foo\nbar')"

In bash (since version 2.0):
hg commit -m $'foo\nbar'
(I.e. put a dollar sign before an opening single quote to make it parse escape sequences (like \n) within the string — note that it doesn't work with double quotes.)

For Windows with PowerShell:
hg commit --message "foo`nbar"

Related

Why is the file I expect to be present missing after the final merge commit in Mercurial?

I ran into a situation in Mercurial at work where I would expect a file to exist, but it doesn't and I'd like to better understand why. I've put together a repro of the issue. In this repro I would expect the file foo.txt to exist on default after the final merge, since one parent of the merge does not have the file present because it was removed earlier, and the other parent is adding it back because of a commit that happened after the commit that removed the file. Instead, the file remains deleted, why?
Here's an image of the sequence of commits:
And here's the actual commands to go from an empty directory, to having a Mercurial repo in this state.
hg init
echo foo > foo.txt
echo bar > bar.txt
hg add foo.txt bar.txt
hg commit -m "Add foo.txt and bar.txt"
hg branch feature
hg remove foo.txt
hg commit -m "Remove foo.txt"
echo barbar > bar.txt
hg commit -m "Modify bar.txt"
hg update default
echo baz > baz.txt
hg add baz.txt
hg commit -m "Add baz.txt"
hg update feature
hg merge default
hg commit -m "Merge default"
echo foo > foo.txt
hg add foo.txt
hg commit -m "Restore foo.txt"
hg update default
echo bazbaz > baz.txt
hg commit -m "Modify baz.txt"
hg update 0
hg merge 2
hg commit -m "Merge feature"
hg merge
hg commit -m "Merge"
hg merge feature
hg commit -m "Merge feature"
State of the working directory after the final merge:
> ls
bar.txt baz.txt
EDIT:
This appears to affect hg versions 5.9.3 and 6.3.2 but not 5.0.2
EDIT:
Based on discussion on the libera.chat #mercurial channel, modifying the sequence of commands in the repro to include an edit to the file after its creation does not change the outcome. foo.txt is still not present. https://gist.github.com/OneGeek/6fa5dcd4c2b3db6649310de1167449f9
I just pasted the steps from the repro into a terminal here and tried it (twice), and it doesn't reproduce the problem as described: foo.txt exists.
The end result is a working directory that has:
C:\Users\abcde\source\test>dir
...
Directory of C:\Users\abcde\source\test
01/29/2023 04:08 PM <DIR> .
01/29/2023 04:07 PM <DIR> ..
01/29/2023 04:08 PM <DIR> .hg
01/29/2023 04:07 PM 0 .hgignore
01/29/2023 04:08 PM 9 bar.txt
01/29/2023 04:08 PM 9 baz.txt
01/29/2023 04:08 PM 6 foo.txt
4 File(s) 24 bytes
The graph in THG looks the same as in the question.
I happen to be using HG 5.0.2 on Windows.

Mercurial - how to see the history for a specific line of code

I have a CSS file with thousands of lines of code. I want to see when a specific line/chunk of code was changed, without going back and reviewing each revision that changed this file (that will take a looooong time!)
Is there a way, using either TortoiseHg, Eclipse with a Mercurial plugin, or command-line, to view the history of a specific piece of code?
The correct answer is hg grep (Mercurial grep page).
More deep:
hg grep --all "PATTERN" FILENAME
Sample output:
>hg grep --all "textdomain" functions.php
functions.php:2:-:load_theme_textdomain('fiver', get_template_directory() . '/translation');
functions.php:2:+:load_theme_textdomain('fiver', get_template_directory() . '/languages');
functions.php:1:+:load_theme_textdomain('fiver', get_template_directory() . '/translation');
(in order - filename, revision, action, string in this revision)
You can use:
hg annotate <file>
to find out in which revision line was changed and then use same command with -r <revision> at the end to go backwards through revisions.
I don't think there is an option to view a specific part of a file. But to see the differences of the total file over several revisions you can use hg diff:
hg diff -r firstrevisionnumber:otherrevnumber filename
For example, hg diff -r 0:8 screen.css
Or the command hg log screen.css.
Use hg histgrep --all PATTERN FILENAME (used to be hg grep in the older versions, and that doesn't work anymore)

How to find out what commit a checked out file came from

When I check out a file with git checkout $commit $filename and I forget $commit but still remember $filename, how do I find out what $commit was?
First a non-git answer. Check your shell command history. Well, if you didn't use a shell with command history then you don't...
The git answer. You generally cannot find THE $commit. Generally the same contents might have been part of many commits and I don't think git keeps a log of what single file you have checked out (it keeps a log of previous values of HEAD)
Here is a brute force script git-find-by-contents. Call it with your $filename as parameter, and it will show you all commits where this file was included. As the name says
it searches by contents. So it will find files with any name, as long as the contents matches.
#! /bin/sh
tmpdir=/tmp/$(basename $0)
mkdir $tmpdir 2>/dev/null
rm $tmpdir/* 2>/dev/null
hash=$(git hash-object $1)
echo "finding $hash"
allrevs=$(git rev-list --all)
# well, nearly all revs, we could still check the log if we have
# dangling commits and we could include the index to be perfect...
for rev in $allrevs
do
git ls-tree --full-tree -r $rev >$tmpdir/$rev
done
cd $tmpdir
grep $hash *
rm -r $tmpdir
I would not be surprised if there is a more elegant way, but this has worked for me a couple of times in similar situations.
EDIT: a more techy version of the same problem appears here: Which commit has this blob?
I don't think you can. Git just loads that version of the file into the index and your working dir. There is no reference keeping track of what version it loaded
If this is something that you do often, you could write up a script that could do it using git diff --cached and march through all the commits that changed that file.
You can use
git log <filename>
to find out which commit you want to checkout
If you're lucky, you could maybe try a non-git way:
history | grep "git checkout.*filename"
Try this (untested ;):
$ for commit in $(git log --format=%h $filename); do
> if diff <(git show $commit:$filename) $filename >/dev/null; then
> echo $commit
> fi
> done
Simple and elegant:
$ git log -S"$(cat /path/to/file)"
Only works if the content is unique, then again that's the same for the hash-comparison answers that came before.
It also displays only the first version that matches, rather than all.
Here are the details of a script I polished up as the answer to a similar question, and here you can see it in action:
(source: adamspiers.org)

How to print the contents of all revisions of a particular file in Mercurial?

Let's say I have 5 revisions of a README file. How do I view them all in Mercurial?
It would be nice to be able to limit the output, similar to:
hg log -l 10
I'm using PowerShell, so combined solutions are also welcome.
I don't know Powershell syntax, but you're looking for the hg cat command. Combined with the answer to your other question I would do it like this in a Unix shell (zsh in my case):
for r in $(hg log --template '{rev} ' README); do hg cat -r $r README; done
I first get all the revisions in which README was changed. They will be put into a big string like this:
% hg log --template '{rev} ' README
822 804 688 681 629 539 538
You then iterate over these revision numbers and call hg cat on each.

How do I get a list of commit comments from CVS since last tagged version?

I have made a bunch of changes to a number of files in a project. Every commit (usually at the file level) was accompanied by a comment of what was changed.
Is there a way to get a list from CVS of these comments on changes since the last tagged version?
Bonus if I can do this via the eclipse CVS plugin.
UPDATE: I'd love to accept an answer here, but unfortunately none of the answers are what I am looking for. Frankly I don' think it is actually possible, which is a pity really as this could be a great way to create a change list between versions (Assuming all commits are made at a sensible granularity and contain meaningful comments).
I think
cvs -q log -SN -rtag1:::tag2
or
cvs -q log -SN -dfromdate<todate
will do what you want. This lists all the versions and comments for all changes made between the two tags or dates, only for files that have changed. In the tag case, the three colons exclude the comments for the first tag. See cvs -H log for more information.
The options for the cvs log command are available here. Specifically, to get all the commits since a specific tag (lets call it VERSION_1_0)
cvs log -rVERSION_1_0:
If your goal is to have a command that works without having to know the name of the last tag I believe you will need to write a script that grabs the log for the current branch, parses through to find the tag, then issues the log command against that tag, but I migrated everything off of CVS quite a while ago, so my memory might be a bit rusty.
If you want to get a quick result on a single file, the cvs log command is good. If you want something more comprehensive, the best tool I've found for this is a perl script called cvs2cl.pl. This can generate a change list in several different formats. It has many different options, but I've used the tag-to-tag options like this:
cvs2cl.pl --delta dev_release_1_2_3:dev_release_1_6_8
or
cvs2cl.pl --delta dev_release_1_2_3:HEAD
I have also done comparisons using dates with the same tool.
I know you have already "solved" your problem, but I had the same problem and here is how I quickly got all of the comments out of cvs from a given revision until the latest:
$ mkdir ~/repo
$ cd ~/repo
$ mkdir cvs
$ cd cvs
$ scp -pr geek#avoid.cvs.org:/cvs/CVSROOT .
$ mkdir -p my/favorite
$ cd my/favorite
$ scp -pr geek#avoid.cvs.org:/cvs/my/favorite/project .
$ cd ~/repo
$ mkdir -p ~/repo/svn/my/favorite/project
$ cvs2svn -s ~/repo/svn/my/favorite/project/src ~/repo/cvs/my/favorite/project/src
$ mkdir ~/work
$ cd ~/work
$ svn checkout file:///home/geek/repo/svn/my/favorite/project/src/trunk ./src
$ cd src
$ # get the comments made from revision 5 until today
$ svn log -r 5:HEAD
$ # get the comments made from 2010-07-03 until today
$ svn log -r {2010-07-03}:HEAD
The basic idea is to just use svn or git instead of cvs :-)
And that can be done by converting the cvs repo to svn or git using cvs2svn or cvs2git, which we should be doing anyway. It got my my answer within about three minutes because I had a small repository.
Hope that helps.
Something like this
cvs -q log -NS -rVERSION_3_0::HEAD
Where you probably want to pipe the output into egrep to filter out the stuff you don't want to see. I've used this:
cvs -q log -NS -rVERSION_3_0::HEAD | egrep -v "RCS file: |revision |date:|Working file:|head:|branch:|locks:|access list:|keyword substitution:|total revisions: |============|-------------"