Fetching a Repository with special branch names breaks on Windows
Current behavior 😯
Calling fetch_only on Windows for a repository with a branch named foo<1.0 fails due to:
Fetch(UpdateRefs(EditReferences(FileTransactionCommit(DeleteReference { full_name: "refs/remotes/origin/foo<1.0", err: Os { code: 123, kind: InvalidFilename, message: "The filename, directory name, or volume label syntax is incorrect." } }))))
Expected behavior 🤔
I would expect this to work on Windows, given that foo<1.0 is a well-formed name according to git's ref naming rules.
Steps to reproduce 🕹
- Use Windows
- Try to
PrepareFetchandfetch_onlyforhttps://github.com/calyptobai/gix-fetch-win-fail.git
Thanks for reporting!
git seems to be able to do it by writing packed-refs right away.
# pack-refs with: peeled fully-peeled sorted
2f3c410f1900acbf8bb3d29f008d1f9b57103053 refs/remotes/origin/foo<1.0
2f3c410f1900acbf8bb3d29f008d1f9b57103053 refs/remotes/origin/main
gix also does that which works around issues around case-insensitivity for example, but that doesn't seem to kick in here. Instead it tries to create a file with such a name and fails.
The goal here would to figure out why it's not writing straight to packed refs.
A quick investigation shows that…
gix(CLI)` already asks to write packed refs only - this doesn't seem to fully kick-in- By default, this setting won't write packed refs by default which on windows and/or for performance should probably rather be 'always'.
gix-refwill still try to obtain a lock on each of the refs individually even when moving them into a packed-ref, apparently, even though it shouldn't have to do that.
So in this case, it's probably these the last two points to adjust and investigate.
I didn't test this prior to the changes in #1374, but currently the failure, if tested with gix, is that it attempts to write a reflog for that remote branch to a file of the same name:
C:\Users\ek\src> gix clone https://github.com/calyptobai/gix-fetch-win-fail.git
14:39:23 indexing done 3.0 objects in 0.00s (2.1k objects/s)
14:39:23 decompressing done 729B in 0.00s (456.3KB/s)
14:39:23 Resolving done 3.0 objects in 0.05s (58.0 objects/s)
14:39:23 Decoding done 729B in 0.05s (14.2KB/s)
14:39:23 writing index file done 1.2KB in 0.00s (5.3MB/s)
14:39:23 create index file done 3.0 objects in 0.06s (49.0 objects/s)
14:39:23 read pack done 626B in 0.06s (9.9KB/s)
Error: Failed to update references to their new position to match their remote locations
Caused by:
0: The reflog could not be created or updated
1: Could not open reflog file at "gix-fetch-win-fail\\.git\\logs\\refs\\remotes\\origin\\foo<1.0" for appending
2: The filename, directory name, or volume label syntax is incorrect. (os error 123)
It's amazing that Git can do it - or does it just ignore the reflog maybe?
If so, and it's my guess that it does, gix could do the same.
It's amazing that Git can do it - or does it just ignore the reflog maybe?
Yeah, with git I don't see any file for it there.
(Attempting to switch to the branch with git does fail, as I would expect, due to an attempt to create a lockfile that has the branch name as its basename.)
I should acknowledge that this may not be all that must change. We also want to be able to fetch again.
C:\Users\ek\src\gix-fetch-win-fail [main ≡]> gix fetch
Error: The ref file ".\\.git\\refs\\remotes\\origin\\foo<1.0" could not be read in full
Caused by:
The filename, directory name, or volume label syntax is incorrect. (os error 123)
There, the problem is not related to a log. This may be considered more strongly to resemble the original problem description. git fetch succeeds with no output. (git diff can also compare to origin/foo<1.0, and it is shown in the output of git log, which also accepts it as an argument.)
Great! This is quite actionable, even though it's not trivial to implement. After all, I wonder if gitoxide should generally treat the reflog as optional. Maybe Git only treats the creation as optional though, or maybe it's a Windows-only fix?
With more research to understand Git as baseline, one can probably make a good decision there.
But with the comment above, it's once again clear that this is more work to get right, probably in many different places then. And with that, I once more wonder if Git has a clever solution for this to also 'do the right thing' instead of just ignoring errors.