Skip to content

Repository::is_dirty() can report false negative #2587

@tisonkun

Description

@tisonkun

Background - scopedb/scopeql#30 (comment)

Minimal Reproduce:

// case: gix_is_dirty
fn main() {
    let repo = gix::discover(".").unwrap();
    println!("gix_is_dirty={}", repo.is_dirty().unwrap());
}

// case: custom_is_dirty
fn main() {
    let repo = gix::discover(".").unwrap();
    println!("custom_is_dirty={}", is_dirty(&repo).unwrap());
}

fn is_dirty(repo: &gix::Repository) -> Result<bool, gix::Error> {
    let status_platform = repo
        .status(gix::progress::Discard)
        .map_err(gix::Error::from_error)?;
    let status_iter = status_platform
        .untracked_files(gix::status::UntrackedFiles::Collapsed)
        .into_iter(None)
        .map_err(gix::Error::from_error)?;
    for item in status_iter {
        let item = item.map_err(gix::Error::from_error)?;
        let dirty = match item {
            gix::status::Item::IndexWorktree(item) => match item {
                gix::status::index_worktree::Item::Modification { .. } => true,
                gix::status::index_worktree::Item::DirectoryContents { entry, .. } => {
                    matches!(entry.status, gix::dir::entry::Status::Untracked)
                }
                gix::status::index_worktree::Item::Rewrite { .. } => true,
            },
            gix::status::Item::TreeIndex { .. } => true,
        };
        if dirty {
            return Ok(true);
        }
    }

    Ok(false)
}

Case 1 - Unborn Repo

gix_is_dirty:

called `Result::unwrap()` on an `Err` value: HeadTreeId(HeadCommit(PeelToCommit(PeelToObject(Unborn { name: FullName("refs/heads/main") }))))

custom_is_dirty:

custom_is_dirty=true

git status:

On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	.gitignore
	Cargo.lock
	Cargo.toml
	src/

nothing added to commit but untracked files present (use "git add" to track)

Case 2 - Untracked File

A clean Git repo with commits, and then touch dirty_file in repo root.

gix_is_dirty:

gix_is_dirty=false

custom_is_dirty:

custom_is_dirty=true

git status:

On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)
	dirty

nothing added to commit but untracked files present (use "git add" to track)

Others

Other cases seem work the same.

Dependency

gix = { version = "0.83.0", default-features = false, features = ["sha1", "status"] }

Additional

Repository::is_dirty's doc says:

Note that untracked files do not affect this flag.

So this seems to be expected? But anyway I need a function to check the same as:

[ -z "$(git status --porcelain)" ]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions