[PR #317] [MERGED] fix(mirror): reuse existing same-source mirrors instead of creating suffixed duplicates #6287

Closed
opened 2026-07-12 09:52:03 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/RayLabsHQ/gitea-mirror/pull/317
Author: @arunavo4
Created: 6/12/2026
Status: Merged
Merged: 6/13/2026
Merged by: @arunavo4

Base: mainHead: fix/315-source-aware-reuse


📝 Commits (1)

  • 5d08467 fix(mirror): reuse existing same-source mirrors instead of creating suffixed duplicates (#315)

📊 Changes

6 files changed (+901 additions, -400 deletions)

View changed files

📝 src/lib/gitea-enhanced.ts (+6 -1)
📝 src/lib/gitea.ts (+241 -96)
📝 src/lib/scheduler-service.ts (+21 -0)
src/lib/starred-repos-handler.ts (+0 -303)
src/lib/utils/mirror-source-match.test.ts (+420 -0)
src/lib/utils/mirror-source-match.ts (+213 -0)

📄 Description

Fixes #315. Part of #309 (strategy-change duplicates and phantom forks).

Problem

Starred repos got re-mirrored into a brand-new Gitea repo on every sync with incrementing names (starred/immich, immich-immich-app, -1, -2, …), with mirroredLocation repointed to the newest copy and older ones orphaned. The same root cause produced duplicates when changing mirror strategy/naming (#309) and forks being marked "mirrored" while pointing at another repo's mirror (#309).

Root cause

  • generateUniqueRepoName only asked "does a repo with this name exist?" — never "is the existing repo already a mirror of this same source" — so a repo's own prior mirror counted as a collision and earned a new suffix every run.
  • repository.mirroredLocation was written but never read on the create path.
  • The in-function "already mirrored → reuse" branch checked the freshly suffixed name, making it structurally unreachable for the self-collision case.
  • When migrate succeeded but metadata failed, the repo stayed failed (with its live mirror preserved), so the scheduler re-entered the create path every cycle.

Historical note: a correct base-name reuse pre-check existed in starred-repos-handler.ts but that module was never imported by anything; #281 routed scheduled starred auto-mirror through the unprotected direct path.

Fix

  • New src/lib/utils/mirror-source-match.ts: clone-URL normalization (credentials/.git/case), source-identity matching via Gitea's original_url, and findExistingMirror (prefers recorded mirroredLocation, then the base name).
  • Both create paths (mirrorGithubRepoToGitea, mirrorGitHubRepoToGiteaOrg) now reuse an existing same-source mirror before any name generation.
  • generateUniqueRepoName is source-aware: an occupied name that mirrors the SAME source is reused; suffixing only happens on a genuine different-source collision (preserves the #95/#236 behavior). Per-user DB claims still block cross-user reuse.
  • Phantom-fork guard: an existing mirror at the computed name is only treated as "ours" after a source-URL match; otherwise a unique name is generated and a separate mirror created.
  • Scheduler: a failed repo whose recorded location still resolves to a live same-source mirror is synced, not re-created.
  • Removed the dead starred-repos-handler.ts.

When Gitea doesn't expose original_url, matching fails toward "collision" (legacy suffix behavior) rather than risking reuse of an unrelated repo.

Testing

  • 30 new unit tests (reuse vs. collision matrix, URL normalization, strategy-change reuse via mirroredLocation, stale-location fallback, per-user separation). Full suite green.
  • Live verification on a Gitea 1.24 test bed seeded with three real duplicates from v3.17.1: re-mirroring logged Reusing existing same-source mirror, pointed at the canonical repo, created no fourth copy, and was idempotent across repeated runs. original_url confirmed populated on real Gitea mirrors.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/RayLabsHQ/gitea-mirror/pull/317 **Author:** [@arunavo4](https://github.com/arunavo4) **Created:** 6/12/2026 **Status:** ✅ Merged **Merged:** 6/13/2026 **Merged by:** [@arunavo4](https://github.com/arunavo4) **Base:** `main` ← **Head:** `fix/315-source-aware-reuse` --- ### 📝 Commits (1) - [`5d08467`](https://github.com/RayLabsHQ/gitea-mirror/commit/5d0846780566d0c638c21035484c4a5fe504c8d2) fix(mirror): reuse existing same-source mirrors instead of creating suffixed duplicates (#315) ### 📊 Changes **6 files changed** (+901 additions, -400 deletions) <details> <summary>View changed files</summary> 📝 `src/lib/gitea-enhanced.ts` (+6 -1) 📝 `src/lib/gitea.ts` (+241 -96) 📝 `src/lib/scheduler-service.ts` (+21 -0) ➖ `src/lib/starred-repos-handler.ts` (+0 -303) ➕ `src/lib/utils/mirror-source-match.test.ts` (+420 -0) ➕ `src/lib/utils/mirror-source-match.ts` (+213 -0) </details> ### 📄 Description Fixes #315. Part of #309 (strategy-change duplicates and phantom forks). ## Problem Starred repos got re-mirrored into a brand-new Gitea repo on every sync with incrementing names (`starred/immich`, `immich-immich-app`, `-1`, `-2`, …), with `mirroredLocation` repointed to the newest copy and older ones orphaned. The same root cause produced duplicates when changing mirror strategy/naming (#309) and forks being marked "mirrored" while pointing at another repo's mirror (#309). ## Root cause - `generateUniqueRepoName` only asked "does a repo with this name exist?" — never "is the existing repo already a mirror of this same source" — so a repo's own prior mirror counted as a collision and earned a new suffix every run. - `repository.mirroredLocation` was written but never read on the create path. - The in-function "already mirrored → reuse" branch checked the freshly suffixed name, making it structurally unreachable for the self-collision case. - When migrate succeeded but metadata failed, the repo stayed `failed` (with its live mirror preserved), so the scheduler re-entered the create path every cycle. Historical note: a correct base-name reuse pre-check existed in `starred-repos-handler.ts` but that module was never imported by anything; #281 routed scheduled starred auto-mirror through the unprotected direct path. ## Fix - New `src/lib/utils/mirror-source-match.ts`: clone-URL normalization (credentials/`.git`/case), source-identity matching via Gitea's `original_url`, and `findExistingMirror` (prefers recorded `mirroredLocation`, then the base name). - Both create paths (`mirrorGithubRepoToGitea`, `mirrorGitHubRepoToGiteaOrg`) now reuse an existing same-source mirror **before** any name generation. - `generateUniqueRepoName` is source-aware: an occupied name that mirrors the SAME source is reused; suffixing only happens on a genuine different-source collision (preserves the #95/#236 behavior). Per-user DB claims still block cross-user reuse. - Phantom-fork guard: an existing mirror at the computed name is only treated as "ours" after a source-URL match; otherwise a unique name is generated and a separate mirror created. - Scheduler: a `failed` repo whose recorded location still resolves to a live same-source mirror is synced, not re-created. - Removed the dead `starred-repos-handler.ts`. When Gitea doesn't expose `original_url`, matching fails toward "collision" (legacy suffix behavior) rather than risking reuse of an unrelated repo. ## Testing - 30 new unit tests (reuse vs. collision matrix, URL normalization, strategy-change reuse via mirroredLocation, stale-location fallback, per-user separation). Full suite green. - Live verification on a Gitea 1.24 test bed seeded with three real duplicates from v3.17.1: re-mirroring logged `Reusing existing same-source mirror`, pointed at the canonical repo, created no fourth copy, and was idempotent across repeated runs. `original_url` confirmed populated on real Gitea mirrors. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-07-12 09:52:03 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/gitea-mirror#6287