The revsets help mentions
"x::y" A DAG range, meaning all changesets that are descendants of x
and ancestors of y, including x and y themselves. If the first
endpoint is left out, this is equivalent to "ancestors(y)", if the
second is left out it is equivalent to "descendants(x)". An
alternative syntax is "x..y".
"x:y" All changesets with revision numbers between x and y, both
inclusive. Either endpoint can be left out, they default to 0 and tip.
"x % y" Changesets that are ancestors of x but not ancestors of y
(i.e. ::x - ::y). This is shorthand notation for "only(x, y)" (see
below). The second argument is optional and, if left out, is
equivalent to "only(x)".
It is not clear what the results are differentiated. In general, "x % y" returns what I want to choose, but I want to understand others.
the difference between the statements may be subtle indeed, there is a "between" and a "range"
take for instance ordered an linear revisions 1 to 5 (in a new dummy repo)
then notice the difference between 1:5 5:1 1::5 5::1
5:1 gives the same as 1:5, everything between the two, no matter which one goes first
OTHO 5::1 gives NO revisions at all, since their order is inverted, so the ancestors/descendants are none
while 1::5 in this example would give the same as 1:5
$ hg init tmptrial
$ cd tmptrial
$ echo 0 > file.txt
$ hg add file.txt
$ hg commit -m 'r0'
$ for i in {1..5} ; do echo $i > file.txt ; hg commit -m r$i ; done
$ hg log -r 1::5 --template "{desc}\n"
$ for range in 1::5 5::1 1:5 5:1 ; do echo -- $range -- ; hg log -r $range --template "{desc}\n" ; done
run the above example and notice the empty output of 5::1
Related
I know from this answer that I can do
hg status --rev x:y
to list files that have been changed between revisions x and y. Is there a way to get not the indiviual files but only the directories in which they are contained?
For example if the above command would yield
A Foo\Bar\SomeFile.txt
A Foo\Bar\AnotherFile.cs
M Baz\AnotherFile.txt
I want to get
Foo\Bar
Baz
instead.
Not a ready-to use solution (see end-notes), just hints
You can use templates for changing output format of hg st, as it can done for any "log-like" hg commands (see -T option)
in template you can filter output (filenames) with filter "dirname", which'll strip filename part of file
but I tried it and discovered (on my repos) some "oddities" (and expected troubles)
End-notes:
While default output of hg st eliminates duplicate filenames (if they appear) in result, your template will not (again, "by default")
I saw wrong (totally wrong) result of templated output for the same range of status
good
>hg st --rev 1170:tip
M hggit\__init__.py
M hggit\compat.py
M hggit\git_handler.py
M hggit\gitdirstate.py
M hggit\hg2git.py
M tests\test-illegal-contents.t
my
>hg st --rev 1170:tip --template "{files % '{file}\n'}"
hggit/__init__.py
hggit/__init__.py
hggit/__init__.py
hggit/__init__.py
hggit/__init__.py
hggit/__init__.py
(six times files from tip only instead of range)
Even with hg log -T instead of st (which give all files) you'll have problem from p.1: "more than one file-entry in output for the same file"
In my repository, I have tags of the form version-1.2.3. I would like to make a revset alias new() that is called like this:
hg log -r 'new(1.2.3, 1.2.4)'
...and expands to this:
hg log -r '::version-1.2.4 - ::version-1.2.3' # What's new in 1.2.4?
When I tried to do this:
[revsetalias]
new($1, $2) = ::version-$2 - ::version-$1
...Mercurial interpreted it as subtracting the revision $2 (e.g. 1.2.3) from the revision version, which was not my intent.
I also tried this, using the ## concatenation operator:
new($1, $2) = ::"version-" ## $2 - ::"version-" ## $1
But then hg log -r 'new(1.2.3, 1.2.4)' gives me this error:
hg: parse error at 13: syntax error
I also tried using ancestors() instead of the :: syntax, but still got the syntax error. Is this possible to do?
I tested the following that works:
new($1, $2) = (::"version-" ## $2) - (::"version-" ## $1)
For reference $1::$2 won't give you the same thing, it means the revision between $1 and $2
An equivalent revset that I would prefer is:
new($1, $2) = only("version-" ## $2, "version-" ## $1)
According to the doc it is strictly equivalent to what you want:
"only(set, [set])"
Changesets that are ancestors of the first set that are not ancestors of
any other head in the repo. If a second set is specified, the result is
ancestors of the first set that are not ancestors of the second set
(i.e. ::<set1> - ::<set2>).
Side note: $1::$2 will be more readable and give you the same part of DAGOnly only() provides correct result in all cases, DAG may fail according to discussion in #lc2817 answer)
I was almost successful in getting answer, but have some troubles (and know no ways to debug) at last step: aggregating all in [revsetalias]
Preface
Because parameters are tags and tag() predicate allow to use regexps in parameter - I'll use they
Revset tag("re:version\-") show all tags, started with "version-"
Revset with hardcoded number as string show single changeset
hg log -r "tag('re:version\-1.7$')
changeset: 2912:454a12f024f3
(trailing $ is mandatory, otherwise it will be all 1.7* tags)
My best attempt in revsetalias was tag('re:version\-\$1$') - with no errors and no output: I can't get fully expanded command in order to see all processings and substitutions and detect my mistakes with parametrized revsetalias
HTH
This this possible? Currently, I first do an hg parents to find the parents, then an hg diff using two revisions. But it would be easier if hg diff had the option to take a single revision and find the parents automatically.
You're looking for the 'change' switch on diff:
-c --change REV change made by revision
you use it like this (for revision 298):
hg diff --change 298
If you use it on a merge changeset (which has two parents), only the first parent is compared. In that case you can use the p2 revset to get the second parent:
hg diff -r 298:p2(298)
hg diff -r 'tip~1'
because
"x~n"
The nth first ancestor of x; "x~0" is x; "x~3" is "x^^^". For n < 0, the nth unambiguous descendent of x.
Is the most reliable method to go one-by-one, using the backout command for each of many changesets, or is there a way to create one big reversal changeset to cover a whole bunch of [edit: non-contiguous] changesets.
If one-by-one, does order matter? (Should one go last-to-first?)
Does the best method differ if there are merges among different sub-projects along the way?
Does this tend to go smoothly in your experience? :-)
If you have no merges along the way, you can either back out every individual change (in reverse order), or, if there are many of them, do it with one big inverse patch.
If you have good changesets atop the ones you need to back out, better commit the inverse patch on top of the most recent bad changeset, then rebasing them onto the tip of the branch.
1 -- 2 -- A -- B -- C -- 3 -- 4
\
C'B'A'
$ hg up C
$ hg diff -r C:2 > backout.diff
$ hg import --no-commit backout.diff
$ hg ci -m "Backout A, B, C"
$ hg up 4
$ hg rebase -s C'B'A -d .
There will be problems if you want to back out merge changesets, see this wiki page for more information.
In such a case, if possible, consider re-doing the branch and stripping the old lineage. Otherwise, you might have to abandon the branch altogether, salvaging the good changesets via graft or transplant.
There is --collapse option for rebase.
Helgi's answer can be upgraded into:
1 -- A -- 2 -- B -- 3 -- C -- 4 -- 5
\
C' -- B' -- A'
$ hg update --clean C
$ hg backout --rev C --message "Backed out changeset: C"
$ hg backout --rev B
$ hg commit --message "Backed out changeset: B"
$ hg backout --rev A
$ hg commit --message "Backed out changeset: A"
$ hg rebase --collapse --source C' --dest 5
$ hg commit --message "Backed out C, B, A"
which will result in the following
1 -- A -- 2 -- B -- 3 -- C -- 4 -- 5 -- C'B'A'
However, backing out in separate branch may result in [logical] conflict in the subsequent merge.
1 -- A -- 2 -- B -- 3 -- X -- 4
\ \
B' -- A' -- M
if X depends on A or B, then M will have conflict (at least logical conflict).
What I came up with is inelegant, but got the job done, despite that the changes I needed to back out were interspersed with other work and had some internal branching. Here's what I did. (Comments and improvements are welcome.)
Got a list of all of the changesets (which I then used to generate the commands below):
hg log -r 'keyword(xyz)' --template '{rev}\n'
Generated a patch for each changeset:
hg diff -p -U 8 --reverse -c 15094 > 15094.rev.patch
hg diff -p -U 8 --reverse -c 15095 > 15095.rev.patch
...
Then, applied each reverse patch. Here the order matters, last-to-first:
hg import -m "reversing changeset 15302" 15302.rev.patch
hg import -m "reversing changeset 15292" 15292.rev.patch
...
This process was interrupted several times for merges that didn't go through automatically, where I had to manually apply changes to a file from its .rej file and then manually commit, before picking up the imports where it had left off.
Finally (in another clone... did I mention I did this all in a clone?) I compressed the whole set of reverse changesets into one changeset using hg histedit -o and its fold command.
Now I've got a single changeset that I should be able to reverse and apply if I decide to put the work back in at a later date (Although if I cross that bridge, I might apply the "forward" patches piecemeal again in order to get better blame/annotate information)
This is how you can do it with TortoiseHg.
Of course you can do the same with the command line.
Given this history, where you wan't to get rid of changeset A, B and C:
1 -- 2 -- A -- B -- C -- 3 -- 4
First update to revision 2.
Then rebase the first of any later revisions you wan't to keep - in this case revision 3.
Your history now looks like this:
1 -- 2 -- A -- B -- C
\
3 -- 4
Now update to revison 4.
And finally use "Merge with local" to merge revision C onto revision 4.
At this point it is crucial that you select the option "Discard all changes from merge target (other) revision".
The description may not be the most logical, but it means that you merge the old tip C back to the default branch - but without the changesets A, B and C.
The result is:
1 -- 2 -- A -- B -- C --
\ /
3 -- 4
Commit and you're done.
If you don't want the "backout" changesets in your history, you could also do something else:
Make a clone of your repository, but only up to the last changeset that you don't want to get rid of.
See Mercurial: Fix a borked history for an example how to do this.
If your repository was a local one, that's all you have to do.
But if the bad changesets were already pushed to a central repository, you'd need server access to delete the repository there and replace it by your clone.
Plus, if someone else already pulled from the repo with the bad changesets, they need to delete and re-clone (otherwise the bad changesets are in the central repo again as soon as one of the other people pushes again).
So it depends on the circumstances whether this solution is a good one for you...
Is there a way to obtain the number of changed lines of code over a certain time period in a mercurial repository? Something along the lines of what statsvn does would be great, but anything counting the number of changed lines of code within 6 months will do (including a clever combination of arguments to hg log).
The hg churn extension is what you want.
You can get visual results with hg activity or hg chart.
Edit: hg diff and hg log both support a --stat option that can do this for you, only better and quicker.
I made an alias called lines to count changed lines (not necessarily lines of code) for me. Try putting this alias in your .hgrc file:
[alias]
lines = !echo `hg log -pr $# | grep "^+" | wc -l` Additions; echo `hg log -pr $# | grep "^-" | wc -l` Deletions;
Then pass it the revision first, followed by any optional arguments:
hg lines tip or hg lines 123:456 -u brian
Sometimes you want to know the number of lines changed excluding whitespace-only changes. This requires using diff -w underneath instead of log -p. I set up a linesw alias for this:
#ignore whitespace
linesw = ![[ $1 =~ : ]] && r=$1 || r="$1~1:$1"; echo `hg diff -wr $r | grep "^+\([^+]\|$\)" | wc -l` Additions; echo `hg diff -wr $r | grep "^-\([^-]\|$\)" | wc -l` Deletions;
hg linesw tip or hg lines 123:456
Note they behave slightly differently because diff and log behave differently -- for example, log will take a --user parameter while diff will not, and when passing a range, log will show changes commited in the first revision given in the range, while diff will not.
This has only been tested using bash.
I needed to do this, and spent quite a bit of time with the hg churn extension and similar solutions.
In the end, I found that what worked best for me was CLOC (Count Lines of Code): http://cloc.sourceforge.net/
You can give it two folders containing two versions of a project, and it will count all of the lines that are the same, modified, added, removed. It recognises multiple languages and itemises code, comments and blank lines.
To use it, I pulled out the two versions of my code from Hg into two parallel folders, and then used cloc --diff --ignore-whitespace