1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
//! A domain-specific-language to select commits in a git repo. Similar to //! [Mercurial's revset](https://www.mercurial-scm.org/repo/hg/help/revsets). //! //! ## Language Specification //! //! Specifying a commit: //! - Reference names like `master`, `release-foo`, or `origin/master`. //! - Hex commit hashes or hash prefixes. //! - A dot `.`, or the at sign `@` refers to `HEAD`. //! //! Operators: //! - `x + y`, `x | y`, `x or y`, `union(x, y)`: Union of `x` and `y` (1). //! - `x & y`, `x and y`, `intersection(x, y)`: Intersection of `x` and `y`. //! - `x - y`, `difference(x, y)`: Commits in `x` but not `y`. //! - `!x`, `not x`, `negate(x)`: Commits not in `x`. //! - `::x`, `ancestors(x)`: Ancestors of `x`, including `x`. //! - `x::`, `descendants(x)`: Descendants of `x`, including `x`. //! - `x^`, `parents(x)`: Parents of `x` (2). //! - `x % y`, `only(x, y)`: Reachable from `x`, not `y`, or `::x - ::y`. //! - `x:y`, `range(x, y)`: A DAG range, descendants of `x` and ancestors of //! `y`, or `x:: & ::y` (3). //! //! Functions: //! - `children(x)`: Commits with at least one parent in the `x` set. //! - `heads(x)`: Heads of a set, `x - parents(x)`. //! - `roots(x)`: Roots of a set, `x - children(x)`. //! - `gca(x, y, ...)`, `ancestor(x, y, ...)`: Heads of common ancestors (4). //! - `first(x, ...)`: First item in `x`, or `first(...)` if `x` is empty. //! - `last(x)`: Last item in `x`, or empty. //! - `head()`: Visible heads (references). //! - `all()`: Visible commits, aka. `::head()`. //! - `publichead()`: Heads referred by remotes, `ref("remotes/**")`. //! - `drafthead()`: Heads not referred by remotes, `head() - publichead()`. //! - `public()`: Commits reachable from `publichead()`, `::publichead()`. //! - `draft()`: Commits only reachable from draft heads, `all() - public()`. //! - `author(name)`: Filter by author name or email. //! - `committer(name)`: Filter by committer name or email. //! - `date(date)`: Filter by author date. //! - `committerdate(date)`: Filter by committer date. //! - `desc(text)`: Filter by commit message. //! - `modifies(path)`: Filter by modified path. //! - `predecessors(x)`: Previous versions of `x`, including `x`. //! - `successors(x)`: Newer versions of `x`, including `x`. //! - `obsolete()`: Commits with at least one newer versions. //! - `id(hexhash)`: Resolve a commit explicitly by a hex hash string. //! - `ref()`: All references. //! - `ref(name)`: Resolve commits by a reference name or glob. //! - `tag()`: All tags. //! - `tag(name)`: Resolve commits by a tag name or glob. //! - `none()`: Empty set. //! - `present(set)`: Empty set on "unresolved name" error. Otherwise just `set`. //! - `apply(expr, $1, $2, ...)`: Replace `$1`, `$2` in `expr` with evaluated //! sets. Then evaluate `expr`. Useful to avoid evaluate same sets multiple //! times. //! //! Differences from Mercurial: //! 1. `x + y` does not make sure `y` comes after `x`. For example, //! `first(x + y)` might be `first(x)` or `first(y)`. In Mercurial, //! `first(x + y)` would be `first(x)`. //! 2. `x^` selects all parents, not just first parents. //! 3. `x:y` selects DAG range `x` to `y`, not revision number range. //! `x::y` is invalid syntax, for easier parsing. //! 4. `ancestor(x, y)` can return multiple commits for criss-cross merges. //! //! ## Quick Start //! //! First, construct a [`gitrevset::Repo`](struct.Repo.html): //! //! ``` //! # fn main() -> gitrevset::Result<()> { //! # #[cfg(feature = "testutil")] //! # { //! # use gitrevset::git2; //! # let repo = gitrevset::TestRepo::new(); //! # repo.set_env(); //! # let path = repo.git_repo().path(); //! // Open from the current directory. //! let repo = gitrevset::Repo::open_from_env()?; //! //! // Open from a libgit2 repository. //! let git_repo = git2::Repository::open(path)?; //! let repo = gitrevset::Repo::open_from_repo(Box::new(git_repo))?; //! # } //! # Ok(()) //! # } //! ``` //! //! Then, use [`revs`](struct.Repo.html#method.revs) or //! [`anyrevs`](struct.Repo.html#method.anyrevs) to perform queries. //! //! ``` //! # fn main() -> gitrevset::Result<()> { //! # #[cfg(feature = "testutil")] //! # { //! # let mut repo = gitrevset::TestRepo::new(); //! # repo.drawdag("A"); //! # repo.add_ref("refs/heads/master", repo.query_single_oid("A")); //! # repo.git_repo().config().unwrap().set_str("revsetalias.foo", "parents($1)").unwrap(); //! let set = repo.revs("(draft() & ::.)^ + .")?; //! //! // With user-defined aliases considered. //! // ex. git config revsetalias.foo "parents($1)" //! let set = repo.anyrevs("foo(.)")?; //! # } //! # Ok(()) //! # } //! ``` //! //! Finally, use [`to_oids`](trait.SetExt.html#tymethod.to_oids) to extract Git //! object IDs from the resulting set: //! //! ``` //! # fn main() -> gitrevset::Result<()> { //! # #[cfg(feature = "testutil")] //! # { //! # let repo = gitrevset::TestRepo::new(); //! # let set = repo.revs("all()")?; //! use gitrevset::SetExt; //! for oid in set.to_oids()? { //! dbg!(oid?); //! } //! # } //! # Ok(()) //! # } //! ``` //! //! To parse the revset expression at compile time, to avoid issues about //! string escaping or injection, use the [`ast!`](macro.ast.html) macro. //! //! ## Note on Commit Graph Index //! //! `gitrevset` takes advantage of the commit graph index from the //! [EdenSCM](https://github.com/facebookexperimental/eden) project that is //! designed to support very large repositories. The index is built on demand //! under the `.git/dag` directory during the construction of the //! [`Repo`](struct.Repo.html) struct. //! //! The index is highly optimized for ancestors-related queries. For example, //! `gca(x, y)`, `x & ::y`, `x % y` usually complete under 1 millisecond //! regardless of the distance between `x` and `y`. //! //! The index is not optimized for many visible heads. Having too many //! references might have a visible performance penalty on //! [`Repo`](struct.Repo.html) construction. //! //! The index can be accessed by [`repo.dag()`](struct.Repo.html#method.dag) //! and the re-exported `dag` crate. #![allow(dead_code)] #![deny(missing_docs)] /// Extended methods on types defined in other crates. pub mod ext; mod ast; mod error; mod eval; mod mutation; mod parser; mod repo; #[cfg(any(test, feature = "testutil"))] mod testrepo; #[cfg(feature = "testutil")] pub use testrepo::TestRepo; #[cfg(test)] mod tests; pub use error::Error; pub use gitdag::dag; pub use gitdag::git2; /// `Result` type used by `gitrevset`. pub type Result<T> = std::result::Result<T, Error>; pub use ast::Expr; pub use eval::Context as EvalContext; pub use ext::SetExt; pub use repo::Repo;