Skip to content

Commit

Permalink
shallow: Support shallow git repositories
Browse files Browse the repository at this point in the history
  • Loading branch information
Veykril committed Sep 12, 2024
1 parent 8d4445d commit 9a7f715
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 24 deletions.
13 changes: 2 additions & 11 deletions cli/src/command_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -438,20 +438,11 @@ impl From<git2::Error> for CommandError {
impl From<GitImportError> for CommandError {
fn from(err: GitImportError) -> Self {
let hint = match &err {
GitImportError::MissingHeadTarget { .. }
| GitImportError::MissingRefAncestor { .. } => Some(
"\
Is this Git repository a shallow or partial clone (cloned with the --depth or --filter \
argument)?
jj currently does not support shallow/partial clones. To use jj with this \
repository, try
unshallowing the repository (https://stackoverflow.com/q/6802145) or re-cloning with the full
repository contents."
.to_string(),
),
GitImportError::RemoteReservedForLocalGitRepo => {
Some("Run `jj git remote rename` to give different name.".to_string())
}
GitImportError::MissingHeadTarget { .. } => None,
GitImportError::MissingRefAncestor { .. } => None,
GitImportError::InternalBackend(_) => None,
GitImportError::InternalGitError(_) => None,
GitImportError::UnexpectedBackend => None,
Expand Down
7 changes: 3 additions & 4 deletions docs/git-compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ a comparison with Git, including how workflows are different, see the
* **.gitignore: Yes.** Ignores in `.gitignore` files are supported. So are
ignores in `.git/info/exclude` or configured via Git's `core.excludesfile`
config. The `.gitignore` support uses a native implementation, so please
report a bug if you notice any difference compared to `git`.
report a bug if you notice any difference compared to `git`.
* **.gitattributes: No.** There's [#53](https://github.com/martinvonz/jj/issues/53)
about adding support for at least the `eol` attribute.
* **Hooks: No.** There's [#405](https://github.com/martinvonz/jj/issues/405)
Expand All @@ -45,7 +45,7 @@ a comparison with Git, including how workflows are different, see the
* **Staging area: Kind of.** The staging area will be ignored. For example,
`jj diff` will show a diff from the Git HEAD to the working copy. There are
[ways of fulfilling your use cases without a staging
area](https://github.com/martinvonz/jj/blob/main/docs/git-comparison.md#the-index).
area](https://github.com/martinvonz/jj/blob/main/docs/git-comparison.md#the-index).
* **Garbage collection: Yes.** It should be safe to run `git gc` in the Git
repo, but it's not tested, so it's probably a good idea to make a backup of
the whole workspace first. There's [no garbage collection and repacking of
Expand All @@ -57,8 +57,7 @@ a comparison with Git, including how workflows are different, see the
not be lost either.
* **Partial clones: No.** We use the [libgit2](https://libgit2.org/) library,
which [doesn't have support for partial clones](https://github.com/libgit2/libgit2/issues/5564).
* **Shallow clones: No.** We use the [libgit2](https://libgit2.org/) library,
which [doesn't have support for shallow clones](https://github.com/libgit2/libgit2/issues/3058).
* **Shallow clones: Yes.** Shallow commits all have the virtual root commit as their parent.
* **git-worktree: No.** However, there's native support for multiple working
copies backed by a single repo. See the `jj workspace` family of commands.
* **Sparse checkouts: No.** However, there's native support for sparse
Expand Down
39 changes: 30 additions & 9 deletions lib/src/git_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,7 @@ fn commit_from_git_without_root_parent(
id: &CommitId,
git_object: &gix::Object,
uses_tree_conflict_format: bool,
is_shallow: bool,
) -> BackendResult<Commit> {
let commit = git_object
.try_to_commit_ref()
Expand All @@ -537,10 +538,16 @@ fn commit_from_git_without_root_parent(
.map(|b| b.reverse_bits())
.collect(),
);
let parents = commit
.parents()
.map(|oid| CommitId::from_bytes(oid.as_bytes()))
.collect_vec();
// shallow commits don't have parents their parents actually fetched, so we
// discard them here
let parents = if is_shallow {
vec![]
} else {
commit
.parents()
.map(|oid| CommitId::from_bytes(oid.as_bytes()))
.collect_vec()
};
let tree_id = TreeId::from_bytes(commit.tree().as_bytes());
// If this commit is a conflict, we'll update the root tree later, when we read
// the extra metadata.
Expand Down Expand Up @@ -859,6 +866,8 @@ fn import_extra_metadata_entries_from_heads(
head_ids: &HashSet<&CommitId>,
uses_tree_conflict_format: bool,
) -> BackendResult<()> {
let shallow_commits = git_repo.shallow_commits().ok().flatten();

let mut work_ids = head_ids
.iter()
.filter(|&id| mut_table.get_value(id.as_bytes()).is_none())
Expand All @@ -868,11 +877,18 @@ fn import_extra_metadata_entries_from_heads(
let git_object = git_repo
.find_object(validate_git_object_id(&id)?)
.map_err(|err| map_not_found_err(err, &id))?;
let is_shallow = shallow_commits
.as_ref()
.is_some_and(|shallow| shallow.contains(&git_object.id));
// TODO(#1624): Should we read the root tree here and check if it has a
// `.jjconflict-...` entries? That could happen if the user used `git` to e.g.
// change the description of a commit with tree-level conflicts.
let commit =
commit_from_git_without_root_parent(&id, &git_object, uses_tree_conflict_format)?;
let commit = commit_from_git_without_root_parent(
&id,
&git_object,
uses_tree_conflict_format,
is_shallow,
)?;
mut_table.add_entry(id.to_bytes(), serialize_extras(&commit));
work_ids.extend(
commit
Expand Down Expand Up @@ -1142,7 +1158,12 @@ impl Backend for GitBackend {
let git_object = locked_repo
.find_object(git_commit_id)
.map_err(|err| map_not_found_err(err, id))?;
commit_from_git_without_root_parent(id, &git_object, false)?
let is_shallow = locked_repo
.shallow_commits()
.ok()
.flatten()
.is_some_and(|shallow| shallow.contains(&git_object.id));
commit_from_git_without_root_parent(id, &git_object, false, is_shallow)?
};
if commit.parents.is_empty() {
commit.parents.push(self.root_commit_id.clone());
Expand Down Expand Up @@ -2151,8 +2172,8 @@ mod tests {
author Someone <[email protected]> 0 +0000
committer Someone <[email protected]> 0 +0000
gpgsig test sig
hash=9ad9526c3b2103c41a229f2f3c82d107a0ecd902f476a855f0e1dd5f7bef1430663de12749b73e293a877113895a8a2a0f29da4bbc5a5f9a19c3523fb0e53518
initial
Expand Down

0 comments on commit 9a7f715

Please sign in to comment.