Skip to content

SSH mirrors fail on gitcache -u because URL normalization drops the username #159

Closed

Description

@waipeng

Affected versions: v1.0.20 onwards (since commit 54147ff "feature: Normalize URLs to maximize cache usage.")

Description

Mirrors created from SSH URLs (both ssh://user@host/... and scp-style git@host:path) fail to
update via gitcache -u or once the update interval triggers a fetch.

Root cause

  1. GitMirror._clone() stores self.normalized_url in the database, and GitMirror.normalize_url()
    discards the user@ part of the URL.
  2. git_update_all_mirrors() constructs each GitMirror from its path only, so self.url is read
    back from the database and no longer contains the username.
  3. _add_credentials_to_remote() then compares self.url with strip_credentials(self.url); they
    are equal, so the credentials are never restored on the origin remote (the "Temporarily
    restoring credentials..." message never appears). The fetch runs against host:path without a
    username and SSH authentication fails (it uses the local login name instead of e.g. git).

Mirrors created before v1.0.20 still have the full git@... URL in the database, so they
restore credentials and update fine. This makes the failure look inconsistent: mirrors of the same
host can pass or fail depending on when they were first cloned.

The bug is an interaction of two features: normalize_url() dropping user@ from the database URL
(commit 54147ff, v1.0.20) and the credential handling from #131 / #134 assuming the database URL is
a usable source of truth for restoring credentials. For SSH URLs it is not, because the username was
already lost during normalization.

A note on intent (please correct me if I read this wrongly)

I am guessing at the original intention here, so please treat the following as an inference rather
than a statement of fact:

I am aware the SSH LFS tests added later (test_15_lfs.py, PR #139) assert the database URL without
git@, so if stripping the SSH username is intended behaviour, I would be happy to hear the
reasoning and what the recommended setup is (e.g. relying on ~/.ssh/config to supply the user).

Steps to reproduce

  1. gitcache git clone git@<some-host>:<group>/<repo>.git (with gitcache >= 1.0.20)
  2. Note that ~/.gitcache/db stores "url": "<some-host>:<group>/<repo>" without git@.
  3. gitcache -u
  4. The update fails with an SSH authentication/permission error; the log shows no "Temporarily
    restoring credentials on the mirror remote URLs." line.

Suggested fix

The username of an SSH URL is not a credential secret; it is required to authenticate. Suggested
changes:

  • normalize_url(): keep user@ for ssh:// and scp-style URLs (dropping only a password), so the
    database retains a usable upstream URL. The mirror path can continue to ignore the username so
    cache sharing still works.
  • helpers.strip_credentials(): for SSH-style URLs, keep the username and strip/mask only the
    password; keep current behaviour for http(s)/ftp(s).

Note: existing databases with already-stripped URLs cannot be repaired automatically since the
username is lost; affected users need to re-add the username to ~/.gitcache/db or re-create the
mirror.

I have a patch with unit tests ready and can open a PR if this approach is acceptable.


This issue was investigated and drafted with the assistance of Claude Code; the analysis of the
repository history and the proposed fix were reviewed by a human before posting.

Activity

  1. self-assigned this
    on Jul 18, 2026
  2. seeraven commented on Jul 18, 2026

    @seeraven
    Owner

    Hi, thanks to bring that up. 👍 It looks indeed like a bug. I have to look a little bit deeper into the issue tomorrow or next week, as I'm currently busy. If you like, you could already start the PR.

  3. added 2 commits that reference this issue on Jul 21, 2026
    7027fad
    50b2aac
  4. waipeng commented on Jul 21, 2026

    @waipeng
    ContributorAuthor

    Thanks for replying. I am fortunate enough to have access to Claude, and have used it to get something decent. I have 2 commits with different approaches, hope you can let me know what you prefer.

    • waipeng/gitcache@50b2aac - Use a function to strip password
    • waipeng/gitcache@7027fad - Use a regex to strip password - this is more complicated. Ideally this should be done by a library (e.g. urlparse), but that will have side effects as it may normalise hostnames (to be tested).

    I understand some maintainers may not like AI generated code, so feel free to take parts of this or disregard, but I just wanted to contribute as this has helped me in my work.

    Thanks for this wonderful software.

  5. github-actions commented on Aug 21, 2026

    @github-actions

    This issue is stale because it has been open for 30 days with no activity.

  6. github-actions commented on Sep 4, 2026

    @github-actions

    This issue was closed because it has been inactive for 14 days since being marked as stale.

  7. seeraven commented on Sep 6, 2026

    @seeraven
    Owner

    Sorry for closing the issue. We have school holidays at the moment and it's hard to find time to work on this. ;-)
    In general, I have no problem with AI generated code, but it usually requires a really thorough review. AI tends to create stuff that really looks good but might have subtle bugs hidden somewhere.

  8. seeraven commented on Sep 6, 2026

    @seeraven
    Owner
    • waipeng/gitcache@50b2aac - Use a function to strip password
    • waipeng/gitcache@7027fad - Use a regex to strip password - this is more complicated. Ideally this should be done by a library (e.g. urlparse), but that will have side effects as it may normalise hostnames (to be tested).

    I like the first one better as it is easier to read. Can you open a pull request for it?

  9. Bilb commented on Sep 25, 2026

    @Bilb
    Contributor

    We hit the same root cause through a different path: the same repository reached through an HTTPS URL, here an HTTPS clone in a CI step on a machine whose own checkouts use ssh. The mirror update then has nothing to restore the user from and connects as <local user>@github.com.

    I opened #163 with @waipeng's commit 50b2aac kept as is, author preserved, plus a repair for mirrors already stripped by v1.0.31 to v1.0.34, the test adjustments and a changelog entry. @waipeng, if you would rather open your own PR, say so and I will close mine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions