Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

repo.references() fails to deduplicate packed/loose refs #1928

Closed
yuja opened this issue Apr 5, 2025 · 12 comments · Fixed by #1931
Closed

repo.references() fails to deduplicate packed/loose refs #1928

yuja opened this issue Apr 5, 2025 · 12 comments · Fixed by #1931
Assignees
Labels
acknowledged an issue is accepted as shortcoming to be fixed

Comments

@yuja
Copy link
Contributor

yuja commented Apr 5, 2025

Current behavior 😯

With the following setup, repo.references() emits both loose and packed refs.

git init -q

mkdir -p .git/refs/heads/a
echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904 > .git/refs/heads/a-
echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904 > .git/refs/heads/a/b
echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904 > .git/refs/heads/a0
git pack-refs --all

This generates the following packed-refs:

# pack-refs with: peeled fully-peeled sorted
4b825dc642cb6eb9a060e54bf8d69288fbee4904 refs/heads/a-
4b825dc642cb6eb9a060e54bf8d69288fbee4904 refs/heads/a/b
4b825dc642cb6eb9a060e54bf8d69288fbee4904 refs/heads/a0
EOF

Create loose refs:

mkdir -p .git/refs/heads/a
echo 0000000000000000000000000000000000000000 > .git/refs/heads/a-
echo 0000000000000000000000000000000000000000 > .git/refs/heads/a/b
echo 0000000000000000000000000000000000000000 > .git/refs/heads/a0

List all refs:

fn main() {
    let path = env::args().nth(1).unwrap();
    let repo = gix::open(path).unwrap();
    for git_ref in repo.references().unwrap().all().unwrap() {
        let git_ref = git_ref.unwrap();
        println!("{} {:?}", git_ref.name().as_bstr(), git_ref.target());
    }
}

refs/heads/a/b in packed-refs should be omitted:

refs/heads/a- Object(Sha1(0000000000000000000000000000000000000000))
refs/heads/a/b Object(Sha1(4b825dc642cb6eb9a060e54bf8d69288fbee4904))
refs/heads/a0 Object(Sha1(0000000000000000000000000000000000000000))
refs/heads/a/b Object(Sha1(0000000000000000000000000000000000000000))

I think this is regression caused by the fix for #1850. Maybe we'll need to sort file-system entries in the same way as tree::EntryRef?

Expected behavior 🤔

No response

Git behavior

No response

Steps to reproduce 🕹

No response

@Byron Byron self-assigned this Apr 5, 2025
@Byron Byron added the acknowledged an issue is accepted as shortcoming to be fixed label Apr 5, 2025
@Byron
Copy link
Member

Byron commented Apr 5, 2025

Thanks a lot for reporting and for figuring out a reproduction.

It's going to go into the test-suite, from where I'd hope to be able to develop a patch. I perfectly agree that it's related to sorting.

Since you went so far, I wouldn't want to be in your way of submitting a PR with a reproduction test-case, maybe you want to take a shot at figuring out the sorting as well?
It will probably take me a couple of days.

@yuja
Copy link
Contributor Author

yuja commented Apr 5, 2025

Sure, I'll take a look. Is it okay to add gix-features -> gix-path dependency? We'll probably need to convert OsStr to BStr.

@Byron
Copy link
Member

Byron commented Apr 5, 2025

Thank you! And absolutely, do what you must.

yuja added a commit to yuja/gitoxide that referenced this issue Apr 5, 2025
…GitoxideLabs#1928)

This follows up 7b1b5bf. Since packed-refs
appears to be sorted by full ref name, loose-refs should also be emitted in
that order.

The comparison function is copied from gix::diff::object::tree::EntryRef.
Non-utf8 file names are simply mapped to "" on Windows. We could add some
fallback, but callers can't handle such file names anyway.
yuja added a commit to yuja/gitoxide that referenced this issue Apr 5, 2025
…GitoxideLabs#1928)

This follows up 7b1b5bf. Since packed-refs
appears to be sorted by full ref name, loose-refs should also be emitted in
that order.

The comparison function is copied from gix::diff::object::tree::EntryRef.
Non-utf8 file names are simply mapped to "" on Windows. We could add some
fallback, but callers can't handle such file names anyway.
@ilyagr
Copy link

ilyagr commented Apr 6, 2025

Meanwhile, with gix 0.71, I'm getting a similar problem to #1850 , but for remote references instead of local references this time. How do I check whether this is the same as the issue that Yuya found?

$ bat src/main.rs
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
File: src/main.rs
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
use std::path::Path;

use gix::bstr::{B, BString, ByteSlice};

fn main() -> anyhow::Result<()> {
    let repo = gix::discover(Path::new(
        "/Users/ilyagr/dev/jj",
    ))?;
    for b in repo.references()?.remote_branches()? {
        let b = b.unwrap();
        if b.name().as_bstr().find(b"squash-preserve").is_some() {
            dbg!(b);
        }
    }
    Ok(())
}
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
$ cargo run
[src/main.rs:12:13] b = Reference {
    name: FullName(
        "refs/remotes/upstream/ig/squash-preserve-snapshot",
    ),
    target: Object(
        Sha1(cbf363db4b7036e736660218edb099ce8dfda99c),
    ),
    peeled: None,
}
[src/main.rs:12:13] b = Reference {
    name: FullName(
        "refs/remotes/upstream/ig/squash-preserve-snapshot",
    ),
    target: Object(
        Sha1(389858b237528e259d9d81c1353fe5fd4dd5baa8),
    ),
    peeled: None,
}

The program that I copied above is almost identical to before:

diff --git a/src/main.rs b/src/main.rs
index 6a4aba9403..b67638212e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,17 +1,14 @@
 use std::path::Path;

-use gix::{
-    bstr::{B, BString, ByteSlice},
-    date::time::format,
-    revision::walk::Sorting,
-};
+use gix::bstr::{B, BString, ByteSlice};

 fn main() -> anyhow::Result<()> {
-    let repo = gix::discover(Path::new("/Users/ilyagr/dev/jj"))?;
-    for b in repo.references()?.local_branches()? {
+    let repo = gix::discover(Path::new(
+        "/Users/ilyagr/dev/jj",
+    ))?;
+    for b in repo.references()?.remote_branches()? {
         let b = b.unwrap();
-        let pat: Vec<u8> = B("pr4021").into();
-        if b.name().as_bstr().find(pat.as_slice()).is_some() {
+        if b.name().as_bstr().find(b"squash-preserve").is_some() {
             dbg!(b);
         }
     }

@yuja
Copy link
Contributor Author

yuja commented Apr 6, 2025

Meanwhile, with gix 0.71, I'm getting a similar problem to #1850 , but for remote references instead of local references this time. How do I check whether this is the same as the issue that Yuya found?

Can you test with #1931 applied? The current git::import_refs() in jj is pretty much broken.

@ilyagr
Copy link

ilyagr commented Apr 6, 2025

#1931 fixes it! Awesome! 🎉 🎉

BTW, unsurprisingly, running git gc or jj util gc --expire now is still a working workaround.

To be more precise, I did

diff --git a/Cargo.toml b/Cargo.toml
index 38f0489907..1f7c18f9cc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,3 +7,5 @@
 anyhow = "1.0.96"
 gix = "0.71.0"

+[patch.crates-io]
+gix = { git = "https://github.com/yuja/gitoxide", branch = "push-klrqpplwxrkx" }

I guess the same would work for jj (for people who are willing to compile it from source).

Byron pushed a commit to yuja/gitoxide that referenced this issue Apr 6, 2025
…itoxideLabs#1928)

This follows up 7b1b5bf. Since packed-refs
appears to be sorted by full ref name, loose-refs should also be emitted in
that order.

The comparison function is copied from gix::diff::object::tree::EntryRef.
Non-utf8 file names are simply mapped to "" on Windows. We could add some
fallback, but callers can't handle such file names anyway.
Byron added a commit that referenced this issue Apr 6, 2025
make `fs::walkdir_sorted_new()` sort entries by paths literally (#1928)
@moroten
Copy link

moroten commented Apr 6, 2025

I have tested with gix=0.68.0 and overriding with gix-features=0.41.1 and I still get refs reported that are not part of repo.references()?.prefixed(...) scope. The refs that are reported are loose refs which are not present in .git/packed-refs.

@Byron
Copy link
Member

Byron commented Apr 6, 2025

I don't know if that really works, gix should be at 0.71 to actually pick up the gix-features override.
However, if the issue still persists, please open a new issue with a reproduction.
Thank you.

@moroten
Copy link

moroten commented Apr 6, 2025

Still the same problem with gix=0.71.0 overridden with gix-features=0.41.1 (1612c73).

yuja added a commit to yuja/jj that referenced this issue Apr 6, 2025
This should fix git::import_refs() issue with gix 0.71.0. Old commits could be
repopulated by importing stale refs stored in packed-refs.

GitoxideLabs/gitoxide#1928

The Zlib license is added to the allow list because foldhash appears in the
dependency chain.
github-merge-queue bot pushed a commit to jj-vcs/jj that referenced this issue Apr 6, 2025
This should fix git::import_refs() issue with gix 0.71.0. Old commits could be
repopulated by importing stale refs stored in packed-refs.

GitoxideLabs/gitoxide#1928

The Zlib license is added to the allow list because foldhash appears in the
dependency chain.
pierrechevalier83 pushed a commit to pierrechevalier83/gitoxide that referenced this issue Apr 6, 2025
pierrechevalier83 pushed a commit to pierrechevalier83/gitoxide that referenced this issue Apr 6, 2025
…itoxideLabs#1928)

This follows up 7b1b5bf. Since packed-refs
appears to be sorted by full ref name, loose-refs should also be emitted in
that order.

The comparison function is copied from gix::diff::object::tree::EntryRef.
Non-utf8 file names are simply mapped to "" on Windows. We could add some
fallback, but callers can't handle such file names anyway.
@ilyagr
Copy link

ilyagr commented Apr 6, 2025

It might help if @Byron could release gix 0.71.1 that depends on the new patch version.

I'm not sure this is a real issue, nor whether there's an easier solution, but we're thinking of making a patch release of jj for this bug. I'm worried that if jj only list the new gix-features release in its Cargo.lock (since it's an indirect dependency), it might not propagate to other people packaging jj or even cargo install jj-cli.

@Byron
Copy link
Member

Byron commented Apr 7, 2025

Cargo will auto-upgrade to the latest available gix-features upon installation and thus should pick up all released fixes. Feature flags aren't involved, and I don't think this fix can be suppressed or somehow not come through.
Maybe I am missing something though. I tested this, and it picks up gix-features@0.41.1 as one would expect.

I think if jj really wants to make sure, they could add gix-features as dummy-dependency in the latest version, and then create a patch release.
That dummy could be removed immediately after the release is done.

yuja added a commit to yuja/jj that referenced this issue Apr 7, 2025
This should fix git::import_refs() issue with gix 0.71.0. Old commits could be
repopulated by importing stale refs stored in packed-refs.

GitoxideLabs/gitoxide#1928

The Zlib license is added to the allow list because foldhash appears in the
dependency chain.
martinvonz pushed a commit to jj-vcs/jj that referenced this issue Apr 7, 2025
This should fix git::import_refs() issue with gix 0.71.0. Old commits could be
repopulated by importing stale refs stored in packed-refs.

GitoxideLabs/gitoxide#1928

The Zlib license is added to the allow list because foldhash appears in the
dependency chain.
@ilyagr
Copy link

ilyagr commented Apr 7, 2025

I think if jj really wants to make sure, they could add gix-features as dummy-dependency in the latest version, and then create a patch release.
That dummy could be removed immediately after the release is done.

That's good advice, thanks!

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
acknowledged an issue is accepted as shortcoming to be fixed
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants