Starred repos get duplicated on every sync #134

Closed
opened 2026-10-05 01:43:40 +02:00 by adam · 2 comments
Owner

Starred repos get re-duplicated on every sync (repo, repo-owner, repo-owner-1, ...)

What happens

A starred repo gets mirrored into a brand new Gitea repo on every scheduled sync, with an incrementing name. One starred source ended up as five separate repos, all mirroring the same upstream:

starred/immich
starred/immich-immich-app
starred/immich-immich-app-1
starred/immich-immich-app-2

The app keeps a single DB row and just repoints mirrored_location at the newest copy, so the older ones become orphans. It happened to more than one starred repo, so it isn't repo-specific.

Version / setup

  • v3.17.0
  • Starred repos, dedicated-org mode (org starred), default suffix duplicate strategy
  • AUTO_MIRROR_STARRED=true with scheduled sync, metadata mirroring enabled

Why

The scheduled sync calls mirrorGithubRepoToGitea directly (scheduler-service.ts:269). For a starred repo it picks the destination name via generateUniqueRepoName (gitea.ts:1185) before any reuse check. That function's isNameTaken only asks "does a repo with this name exist", never "is it already a mirror of this same source". So it treats the repo's own previous mirror as a collision and moves on to the next free suffix, creating a new repo each run. The "already mirrored" reuse check right after (gitea.ts:606) runs against the freshly generated unique name, so it never matches.

It compounds with the scheduler's selection (scheduler-service.ts:214): only repos in status imported, pending, or failed get re-mirrored. If a sync never finishes (e.g. metadata mirroring of a huge repo times out), the repo never reaches mirrored, so every cycle re-runs the create path and adds another duplicate.

Expected

Reuse the existing mirror instead of creating a new one. If repository.mirroredLocation is set, or a repo at the candidate name is already a mirror of the same source, sync that one. Only allocate a new name for a genuine collision with a different source.

## Starred repos get re-duplicated on every sync (`repo`, `repo-owner`, `repo-owner-1`, ...) ### What happens A starred repo gets mirrored into a brand new Gitea repo on every scheduled sync, with an incrementing name. One starred source ended up as five separate repos, all mirroring the same upstream: ``` starred/immich starred/immich-immich-app starred/immich-immich-app-1 starred/immich-immich-app-2 ``` The app keeps a single DB row and just repoints `mirrored_location` at the newest copy, so the older ones become orphans. It happened to more than one starred repo, so it isn't repo-specific. ### Version / setup - v3.17.0 - Starred repos, `dedicated-org` mode (org `starred`), default `suffix` duplicate strategy - `AUTO_MIRROR_STARRED=true` with scheduled sync, metadata mirroring enabled ### Why The scheduled sync calls `mirrorGithubRepoToGitea` directly (`scheduler-service.ts:269`). For a starred repo it picks the destination name via `generateUniqueRepoName` (`gitea.ts:1185`) before any reuse check. That function's `isNameTaken` only asks "does a repo with this name exist", never "is it already a mirror of this same source". So it treats the repo's own previous mirror as a collision and moves on to the next free suffix, creating a new repo each run. The "already mirrored" reuse check right after (`gitea.ts:606`) runs against the freshly generated unique name, so it never matches. It compounds with the scheduler's selection (`scheduler-service.ts:214`): only repos in status `imported`, `pending`, or `failed` get re-mirrored. If a sync never finishes (e.g. metadata mirroring of a huge repo times out), the repo never reaches `mirrored`, so every cycle re-runs the create path and adds another duplicate. ### Expected Reuse the existing mirror instead of creating a new one. If `repository.mirroredLocation` is set, or a repo at the candidate name is already a mirror of the same source, sync that one. Only allocate a new name for a genuine collision with a different source. <!-- github-sync-id:4608196833 -->
adam closed this issue 2026-10-05 01:43:40 +02:00
Author
Owner

@Quadrubo try the latest version

@Quadrubo try the latest version <!-- github-comment-id:4697264141 -->
Author
Owner

Fixed in v3.18.0 (#317). The name generator only checked whether a repo name existed, not whether it was already a mirror of the same source, so every sync treated the previous mirror as a conflict and added another suffix. It now reuses the existing same-source mirror (matched on original_url) and only suffixes on a genuine different-source collision. This won't remove the duplicate repos already created in Gitea, so you'll need to delete those manually.

Fixed in v3.18.0 (#317). The name generator only checked whether a repo name existed, not whether it was already a mirror of the same source, so every sync treated the previous mirror as a conflict and added another suffix. It now reuses the existing same-source mirror (matched on `original_url`) and only suffixes on a genuine different-source collision. This won't remove the duplicate repos already created in Gitea, so you'll need to delete those manually. <!-- github-comment-id:4700702439 -->
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: adam/gitea-mirror#134