What is the git equivalent of Mercurial revsets?
Asked Answered
H

3

20

Mercurial has a domain-specific language called revsets that allows users to specify sets of revisions.

For example, you might want to list patches that haven't yet been merged into the branch default:

hg log -r "all() - ancestors('default')"

As a more complex example, the link above gives the example of listing changesets between the revision tagged 1.3 and the revision tagged 1.5 which mention "bug" and affect a file in the directory hgext:

hg log -r "1.3::1.5 and keyword(bug) and file('hgext/*')"

The revset language is quite rich, allowing selection of changesets based on dates, username, commit message, whether the commit exists at a particular remote location, and so on.

Does git have an equivalent mechanism for querying changesets, either in the core program or available as an extension?

Hectocotylus answered 20/3, 2014 at 0:0 Comment(15)
The languages are quite different but the general command in git for generating revision-lists is git rev-list. Most git commands use it (either directly via compiled-in access, or by invoking it as a command because they are shell scripts themselves). In cases where git rev-list alone is not sufficient, you can write shell scripts that read its output, feed it input, and so on.Threat
@torek: The point of using revsets is because I got tired of writing shell scripts :). git rev-list looks like it provides at least some of the functionality of revsets. Would you consider putting you comment as an answer, perhaps with a quick comparison of the two?Hectocotylus
I'm afraid the comparison will get really long and I'm much less familiar with the behaviors of hg revsets (I keep being surprised by boundary condition differences between hg and git for instance), so I'm a bit hesitant :-)Threat
Is there something specific you want help cooking up a script for?Rambunctious
@jthill: No. I am moving over to git, and rely heavily on Mercurial revsets for day-to-day work. I have so far only encountered basic revision ranges, and was trying to establish if git was lacking something similar, or if I was just not looking in the right place.Hectocotylus
Please see the gitrevisions(7) manual page.Gustin
@kostix: The gitrevisions(7) man page only talks about basic methods of specifying revisions. For example, how would I specify all commits older than two weeks with "bug" in their commit message not a descendant from the current checkout? In revsets, it would be not date(-14) & desc("bug") & not ancestors(.).Hectocotylus
@davidg, Git implements a different approach: revision ranges in it operate only on commit names (SHA-1 or symbolic) and log limiting in git log and friends is done using command-line options such as --grep. Note that selecting commits by date is still done when specifying revisions -- see the manual page I referred to.Gustin
@kostix: gitrevisions and git rev-list appear to a be figurative grep, when what I really want (and what Mercurial revsets provide) is a figurative SQL. It is looking increasingly like I will need to find either a git extension, or roll up my sleeves and write something myself.Hectocotylus
@davidg, while I wish you good luck implementing this, I'd first consdider that you appear to be the first person on Earth wanting this sort of thing. I mean, there's a bunch of productive hackers around Git and no one wanted to implement such a feature yet. To me, this means it might be better to just adopt the extsting approaches to the problem, even if they currently feel to be inferior to what you've learned with Mecrurial.Gustin
@kostix: Replace "git" with "subversion" and "revsets" with "distributed version control", and your sentence would have rung true in 2003 ;). The Mercurial community has found the feature useful. git rev-list seems to solves a subset of the problems, which suggests that some of the problems revsets attempts to solve have been hit by git developers and tackled by git developers in an ad-hoc manner. Anyway, I am going very off-topic from the original question, which was not about the benefits of revsets, but whether there exists an equivalent (either core or third-party) for git.Hectocotylus
@davidg, a minor correction: git rev-list is a plumbing level command used by git log and friends, it's not intended to be used by users but rather by scripts so I'd not say it tries to solve any problems beyond making git log to its job ;-)Gustin
Try git log with commit ranges such as .., ..., and --not. Then add on top the git log with grep, and this probably provides the equivalent functionality. Time permitting, I will type up an answer with examples that match your question.Zygodactyl
re git/revset::svn/dvcs analogy, (a) rev-list and/or log already do all the basics but svn can't dvcs at all, and (b) sort/join/sed can do arbitrary set operations as oneliners or nearly so. So what, specifically, are you having trouble discovering about your histories, please?Rambunctious
This is a great question and confining revsets to just a log-search may be misleading individuals who have not used complex revsets. The full power of a reveset is simply not realized in such a context - for example, take hg diff -r 'my-feature + tip~3':tip which has no git equivalent, much less anything that can be written in a generalized manner. Now, is it "required"? Well, no as the lack-of-caring-of-git-users shows .. but there are a few things hg does do better, and this is one.Shaftesbury
H
4

The git-branchless suite of tools now includes a revset-like language under the command git branchless query.

Once installed and initialised on the current repository, the examples in the previous question (from eight years ago!) could be queried as follows:

# List patches not yet been merged into the branch default:
git branchless query "all() - ancestors('default')"
# List patches between the revision tagged 1.3 and the revision tagged
# 1.5 which which mention "bug" and affect a file in the directory hgext
git branchless query "1.3::1.5 and message('bug') and paths.changed('glob:hgext/*')"

These are remarkably similar to their HG equivalents.

Hectocotylus answered 22/12, 2022 at 3:36 Comment(0)
S
2

To list all commits except from the default master branch, like I assume the first example does:

git log --all --not master

To get a result somewhat equal to the second example:

git log 1.3...1.5 --grep="bug" -- hgext
Semiskilled answered 15/1, 2015 at 16:27 Comment(2)
Thanks for the thought. Your answer indeed solves the particular examples I gave, but was not the general solution I was hoping for. I was really hoping for a plugin/extension that provides a DSL for specifying revisions similar to that of Mercurial's revsets DSL. Figuratively, you have solved my examples using grep, while what I really want is SQL. Such a DSL doesn't seem to exist for git at this time, but I will leave this question open in case that changes in the future. (I should give more interesting examples; when or and and predicates are nested, grep starts to break down).Hectocotylus
I understand the issue, However, it is possible to add multiple grep arguments to git log, each grep argument is like an or clause let's say you want to match a string "Create" or the string "Add" you can run git log --grep "Create" --grep "Add" For affected files or folders, just add the files / folders you want to include after the -- argument.Semiskilled
T
2

Does git have an equivalent mechanism for querying changesets, either in the core program or available as an extension?

The closest equivalent in git is gitrevisions(7), but it's completely ad-hoc, and significantly less regular, and less composable. And not all commands use it (famously git diff has identical constructs which behave completely differently, because gitrevisions works on ranges but git diff works on pairs of commits).

Thaumatrope answered 3/9, 2021 at 8:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.