# Repositories & Synchronization > How URGithub discovers your repositories, keeps them in sync, and what happens when a repository **cannot** be pushed. ## Discovery URGithub discovers Git repositories within its configured repository area (`repos in github\`) and reconciles the local state against GitHub using `gh repo list`. A repository generally needs to: - exist as a valid directory - contain a Git working tree - have valid Git metadata - have an appropriate remote - be accessible by the authenticated GitHub account On the **first run** it clones your entire GitHub account — private repos and forks included (unless `skip_forks` is enabled). ```mermaid flowchart TD A[repos in github folder] --> B{Valid git repo?} B -->|yes| C[Has remote?] B -->|no| D[ignored / flagged missing] C -->|yes| E[reconcile with gh repo list] C -->|no| F[flagged: no remote configured] E --> G{Exists on GitHub?} G -->|yes| H[tracked] G -->|no| I[deleted-repo policy / quarantine] E --> J{Exists locally but
not on GitHub?} J -->|yes| K[new repo — tracked] J -->|no| L[clone_missing_repos
clones it] ``` Inspect what was found with: ```bash python urgithub.py --repos python urgithub.py --verify ``` `--verify` checks registry entries, folders, Git state, and remote information. ## Registry Every managed repository has a registry entry in `database\registry.json` (status, SHAs, quarantine state). - `--forget NAME` — remove one repository from the registry (folder left on disk). - `--prune` — remove all stale registry entries (missing / quarantined / deleted). - `--yes` — skip confirmation prompts for `--forget` / `--prune`. ## Keeping names in sync with GitHub GitHub is the **source of truth** for names. - **Renamed on GitHub only** → URGithub detects it via `gh api` returning the new name and renames the local folder + registry entry automatically. - **Renamed locally only** → URGithub **adopts** it: on the next run it reads the folder's `origin`, matches it to the existing registry entry (whose path is gone), and reuses that entry — updating the path, no duplicate clone, no quarantine. Then just `gh repo rename NewName --repo owner/OldName` on GitHub to match. - Leftover ghost entries (from manual moves) can be dropped with `--forget NAME` or bulk-cleaned with `--prune`. ## Deleted repositories — quarantine, never deletion Repositories confirmed deleted on GitHub are **not** silently deleted. They are moved to `deleted repos\` (a quarantined archive) and only with confirmation: ```mermaid flowchart TD A[GitHub returns 404] --> B{3 consecutive scans?} B -->|no| C[wait / keep scanning] B -->|yes| D{7 days elapsed?} D -->|no| E[wait] D -->|yes| F{Remote confirms deletion?} F -->|no| G[do nothing] F -->|yes| H{User confirms?} H -->|no| G H -->|yes| I["Move to deleted repos folder"] I --> J[reported in report.html] ``` Configurable via `deleted_repo_policy.*` (defaults: 3 scans, 7 days, both confirmations required). ## The sync engine Synchronization is deliberately conservative — **never** `reset`, `--force`, `rebase`, or `clean`. ```mermaid gitGraph commit id: "base" branch local checkout local commit id: "local change" checkout main commit id: "remote change" checkout local merge main commit id: "pushed" ``` The safe pull/commit/push sequence: ```text fetch → fast-forward-only pull → commit (per policy) → push ``` ## When a repository is **not** pushed | Situation | Result | |---|---| | Secret files found (`block_on_secrets`) | `blocked: secrets` — by filename pattern **and** file content | | File larger than `limits.max_file_mb` | `blocked: oversize files` | | Local ahead **and** remote ahead | `blocked: divergence` | | Remote unreachable | `blocked: remote unreachable` | | No push permission on the repo | `blocked: no push permission` | | No `origin` remote | `blocked: no remote configured` | | Folder missing / not a git repo / quarantined | `blocked: missing` | | Dirty tree + `auto_commit` off | `skipped` (never silently commits) | | Fetch / `git add` / commit / push fails | `failed` (journaled with the reason) | Every case is journaled and visible in `report.html`. Repos that pass are still pushed — **one bad repo never blocks the others.** ## Divergence protection If the local and remote histories diverged (both ahead of the common base), the repo is **blocked** instead of clobbered. ```mermaid flowchart LR A{Local ahead?} --> C{Remote ahead?} A -->|no| B[clean / push] C -->|yes| D[BLOCKED: divergence
you review, you decide] C -->|no| E[fast-forward safe] ``` You review, you decide. URGithub never force-pushes and never merges divergent history automatically. ## The lock A global PID-based lock (`locks\run.lock`) serializes runs: - A second run while another is active reports `skipped — lock held`. - A stale lock expires after **15 seconds**. ```mermaid sequenceDiagram participant R1 as Run 1 participant R2 as Run 2 participant L as Lock R1->>L: acquire R2->>L: try acquire → held L-->>R2: skipped — lock held R1->>L: release ``` See [Security](security.md) for what stops a sync, and [Report](report.md) for what each run records.