Commit Selectors
Commit selectors describe which commits to load from a workspace. The
Workspace::checkout
method accepts any type implementing the
CommitSelector
trait and returns a TribleSet
containing data from those
commits. It currently supports individual commit handles, lists of handles and a
handful of higher level selectors.
Most selectors operate on ranges inspired by Git's two‑dot syntax. a..b
selects all commits reachable from b
that are not reachable from a
. In set
terms it computes ancestors(b) - ancestors(a)
. Omitting the start defaults a
to the empty set so ..b
yields ancestors(b)
. Omitting the end defaults b
to the current HEAD
, making a..
resolve to ancestors(HEAD) - ancestors(a)
while ..
expands to ancestors(HEAD)
.
#![allow(unused)] fn main() { // Check out the entire history of the current branch let history = ws.checkout(ancestors(ws.head()))?; }
While convenient, the range-based design makes it difficult to compose complex queries over the commit graph.
Implemented selectors
CommitSelector
is implemented for:
CommitHandle
– a single commit.Vec<CommitHandle>
and&[CommitHandle]
– explicit lists of commits.ancestors(commit)
– a commit and all of its ancestors.nth_ancestor(commit, n)
– follows the first-parent chainn
steps.parents(commit)
– direct parents of a commit.symmetric_diff(a, b)
– commits reachable from eithera
orb
but not both.- Standard ranges:
a..b
,a..
,..b
and..
following the two‑dot semantics described above. filter(selector, predicate)
– retains commits for whichpredicate
returnstrue
.history_of(entity)
– commits touching a specific entity (built onfilter
).time_range(start, end)
– commits whose timestamps intersect the inclusive range.
A future redesign could mirror Git's revision selection semantics.
Instead of passing ranges, callers would construct commit sets derived from
reachability. Primitive functions like ancestors(<commit>)
and
descendants(<commit>)
would produce sets. Higher level combinators such as
union
, intersection
and difference
would then let users express queries
like "A minus B" or "ancestors of A intersect B". Each selector would return
a CommitSet
patch of commit handles for checkout
to load.
This approach aligns with Git's mental model and keeps selection logic separate from workspace mutation. It also opens the door for additional operations on commit sets without complicating the core API.
Filtering commits
The filter
selector wraps another selector and keeps only the commits for
which a user provided closure returns true
. The closure receives the commit
metadata and its payload, allowing inspection of authors, timestamps or the
data itself. Selectors compose, so you can further narrow a range:
#![allow(unused)] fn main() { use hifitime::Epoch; use tribles::repo::{filter, time_range}; let since = Epoch::from_unix_seconds(1_609_459_200.0); // 2020-12-01 let now = Epoch::now().unwrap(); let recent = ws.checkout(filter(time_range(since, now), |_, payload| { payload.iter().any(|t| t.e() == &my_entity) }))?; }
Higher level helpers can build on this primitive. For example history_of(entity)
filters
ancestors(HEAD)
to commits touching a specific entity:
#![allow(unused)] fn main() { let changes = ws.checkout(history_of(my_entity))?; }
Git Comparison
The table below summarizes Git's revision grammar. Each row links back to the official documentation. Forms that rely on reflogs or reference objects other than commits are listed for completeness but are unlikely to be implemented.
Git Syntax | Planned Equivalent | Reference | Status |
---|---|---|---|
A | commit(A) | gitrevisions | Implemented |
A^ /A^N | nth_parent(A, N) | gitrevisions | Not planned |
A~N | nth_ancestor(A, N) | gitrevisions | Implemented |
A^@ | parents(A) | gitrevisions | Implemented |
A^! | A minus parents(A) | gitrevisions | Unimplemented |
A^-N | A minus nth_parent(A, N) | gitrevisions | Not planned |
A^0 | commit(A) | gitrevisions | Implemented |
A^{} | deref_tag(A) | gitrevisions | Unimplemented |
A^{type} | object_of_type(A, type) | gitrevisions | Not planned: non-commit object |
A^{/text} | search_from(A, text) | gitrevisions | Not planned: requires commit message search |
:/text | search_repo(text) | gitrevisions | Not planned: requires repository search |
A:path | blob_at(A, path) | gitrevisions | Not planned: selects a blob not a commit |
:[N:]path | index_blob(path, N) | gitrevisions | Not planned: selects from the index |
A..B | range(A, B) | gitrevisions | Implemented |
A...B | symmetric_diff(A, B) | gitrevisions | Implemented |
^A | exclude(reachable(A)) | gitrevisions | Unimplemented |
A@{upstream} | upstream_of(A) | gitrevisions | Not planned: depends on remote config |
A@{push} | push_target_of(A) | gitrevisions | Not planned: depends on remote config |
A@{N} | reflog(A, N) | gitrevisions | Not planned: relies on reflog history |
A@{<date>} | reflog_at(A, date) | gitrevisions | Not planned: relies on reflog history |
@{N} | reflog(HEAD, N) | gitrevisions | Not planned: relies on reflog history |
@{-N} | previous_checkout(N) | gitrevisions | Not planned: relies on reflog history |
Only a subset of Git's revision grammar will likely be supported. Selectors relying on reflog history, remote configuration, or searching commits and blobs add complexity with little benefit for workspace checkout. They are listed above for completeness but remain unplanned for now.
TimeRange
Commits record when they were made via a timestamp
attribute of type
NsTAIInterval
. When creating a commit this
interval defaults to (now, now)
but other tools could provide a wider range
if the clock precision is uncertain. The TimeRange
selector uses this interval
to gather commits whose timestamps fall between two Epoch
values:
#![allow(unused)] fn main() { use hifitime::Epoch; use tribles::repo::time_range; let since = Epoch::from_unix_seconds(1_609_459_200.0); // 2020-12-01 let now = Epoch::now().unwrap(); let tribles = ws.checkout(time_range(since, now))?; }
This walks the history from HEAD
and returns only those commits whose
timestamp interval intersects the inclusive range.
Internally it uses filter(ancestors(HEAD), ..)
to check each commit's
timestamp range.