-
-
Notifications
You must be signed in to change notification settings - Fork 0
Source control command list: git, hg, jj
The most common use of this crate is with version control systems. The examples below cover three such systems: Git, Mercurial, and Jujutsu.
This section covers some of the considerations you should keep in mind when working with paths in a version control system.
Paths as produced by tools like git diff --name-only are typically separated by newline characters (\n). However, on Unix platforms filenames can themselves have newlines embedded in them, so source control systems often end up quoting newlines and other "unusual" characters.
A robust, lossless way to retrieve a list of paths is by separating them with null characters (i.e. NUL or \0: a byte with a value of zero). Both Unix and Windows guarantee that a path can never have embedded null characters. This crate is geared towards supporting these kinds of outputs. This means that this doesn't handle any kind of escaping or quoting, instead assuming that the paths are already correctly formatted.
Both Git and Mercurial support null-separated paths. Jujutsu as of version 0.25.0 does not, but will hopefully support them in the future. (With current versions, commands like jj file list will return paths separated by newlines and will not perform any kind of quoting or escaping on them, so this crate is compatible with most real-world paths.)
Unix platforms always use forward slashes (/) as the path separator. Windows platforms use \ as the main separator, but also supports / as an alternative in most situations.
On Windows, Git always returns file paths with forward slashes. (This is related to Git's history as a Unix-first tool.) Mercurial and Jujutsu, both being native Windows utilities, always return paths with backslashes.
Independently of the system in use, based on your requirements, you might prefer:
- Paths with forward slashes (Unix-style or Git-style) on all platforms.
- Paths using the native separator on each platform.
This crate provides a way to convert any of these representations to one of your choosing.
In all of the recipes below, paths are returned with respect to the current directory. In most cases, you will want to run the below commands from the root of the repository.
Here's a set of recipes for how to make such paths in Git, Mercurial, and Jujutsu.
First, you need to decide:
-
What query do you want to perform? In general, you may either want to list all files in the working directory or in a revision, or to perform a diff of some kind.
-
If you're performing a diff, what are you diffing from and to? There are three possible answers here:
- Between two revisions.
- From the merge base or nearest/greatest/lowest common ancestor of two revisions, to a particular revision.
- Between a revision and the working directory.
In general, if you're concerned with what changed in a branch, you'll want to use the merge base. If you're concerned with what changed in a commit, you'll want to diff a commit against its parent.
-
If the working directory is part of what you're doing, do you only want to consider tracked files, or do you want to include untracked files as well? (In general, most users will exclude ignored files.)
Jujutsu does not have a notion of untracked files.
-
Do you need to filter by directory? Most commands here let you filter to a directory by passing in
-- path/to/directoryto the command.However, doing so might cause paths to be returned relative to the directory rather than relative to the root of the repo. You're encouraged to test commands out against your own repositories to see how this works.
(This is all a bit complicated! Feel free to take a break at this point if this is too much.)
Here's how this breaks down:
List all files in the working directory, excluding untracked files:
-
Git:
git ls-files -zproduces null-separated paths with forward slashes. -
Mercurial:
hg files --print0produces null-separated paths with the native separator. -
Jujutsu:
jj file listproduces newline-separated paths with the native separator.
List all files in the working directory, including untracked files:
-
Git:
git ls-files -z --cached --others --exclude-standardproduces null-separated paths with forward slashes. -
Mercurial:
hg status --print0 -mardu --no-statusproduces null-separated paths with the native separator. - Jujutsu: Jujutsu does not have a notion of untracked files.
List all files present in revision $REV:
-
Git:
git ls-tree -z -r --name-only $REVproduces null-separated paths with forward slashes. -
Mercurial:
hg status --print0 --no-status --rev null --rev $REVproduces null-separated paths with the native separator. -
Jujutsu:
jj file list --revision $REVproduces newline-separated paths with the native separator.
List changed files from a revision $REV to the working directory, excluding untracked files:
-
Git:
git diff-index -m -z --name-only $REVproduces null-separated paths with forward slashes. -
Mercurial:
hg status --no-status --print0 --rev $REVproduces null-separated paths with the native separator. -
Jujutsu:
jj diff --from $REV --name-onlyproduces newline-separated paths with the native separator.
List changed files from a revision $REV to the working directory, including untracked files:
-
Git: There isn't a single command to do this. Instead, first obtain the diff excluding untracked files, then combine with the list of untracked files obtained from
git ls-files -z --others --exclude-standard. -
Mercurial:
hg status --no-status --print0 -mardu --rev $REVproduces null-separated paths with the native separator. - Jujutsu: Jujutsu does not have a notion of untracked files.
List changed files from the merge base of a revision $REV to the working directory, excluding untracked files:
-
Git:
git diff-index -m --merge-base -z --name-only $REVproduces null-separated paths with forward slashes. -
Mercurial:
hg status --no-status --print0 --rev 'ancestor($REV,.)'produces null-separated paths with the native separator. -
Jujutsu:
jj diff --from 'fork_point(@ | $REV)' --name-onlyproduces newline-separated paths with the native separator.
List changed files from the merge base of a revision $REV to the working directory, including untracked files:
-
Git: There isn't a single command to do this. Instead, first obtain the diff excluding untracked files, then combine with the list of untracked files obtained from
git ls-files -z --others --exclude-standard. -
Mercurial:
hg status --no-status --print0 -mardu --rev 'ancestor($REV,.)'produces null-separated paths with the native separator. - Jujutsu: Jujutsu does not have a notion of untracked files.
List changed files in a revision compared to its parent(s):
-
Git:
git diff-tree -z --name-only $REVproduces null-separated paths with forward slashes. -
Mercurial:
hg status --no-status --print0 --change $REVproduces null-separated paths with the native separator. -
Jujutsu:
jj diff --revision $REV --name-onlyproduces newline-separated paths with the native separator.
List changed files from $OLD_REV to $NEW_REV:
-
Git:
git diff-tree -z --name-only $OLD_REV $NEW_REVproduces null-separated paths with forward slashes. -
Mercurial:
hg status --no-status --print0 --rev $OLD_REV --rev $NEW_REVproduces null-separated paths with the native separator. -
Jujutsu:
jj diff --from $OLD_REV --to $NEW_REV --name-onlyproduces newline-separated paths with the native separator.
List changed files from the merge base of $OLD_REV and $NEW_REV, to $NEW_REV:
-
Git:
git diff-tree -z --name-only --merge-base $OLD_REV $NEW_REVproduces null-separated paths with forward slashes. -
Mercurial:
hg status --no-status --print0 --rev 'ancestor($OLD_REV,$NEW_REV)' --rev $NEW_REVproduces null-separated paths with the native separator. -
Jujutsu:
jj diff --from 'fork_point($OLD_REV | $NEW_REV)' --to $NEW_REV --name-onlyproduces newline-separated paths with the native separator.