{"id":51500619,"url":"https://github.com/paladini/backup-github-to-gitlab","last_synced_at":"2026-07-07T18:30:38.678Z","repository":{"id":364273625,"uuid":"1267195412","full_name":"paladini/backup-github-to-gitlab","owner":"paladini","description":"Mirror all your GitHub repositories to GitLab automatically — private stays private, public stays public.","archived":false,"fork":false,"pushed_at":"2026-06-19T20:20:06.000Z","size":52,"stargazers_count":3,"open_issues_count":1,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-19T21:25:25.822Z","etag":null,"topics":["backup","backup-script","executable","github","github-to-gitlab","gitlab","migration","package","python","repo-sync","repository-migration","sync","user-backup","user-repo"],"latest_commit_sha":null,"homepage":"https://paladini.github.io/backup-github-to-gitlab/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/paladini.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-12T10:04:54.000Z","updated_at":"2026-06-19T20:20:10.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/paladini/backup-github-to-gitlab","commit_stats":null,"previous_names":["paladini/backup-github-to-gitlab"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/paladini/backup-github-to-gitlab","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paladini%2Fbackup-github-to-gitlab","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paladini%2Fbackup-github-to-gitlab/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paladini%2Fbackup-github-to-gitlab/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paladini%2Fbackup-github-to-gitlab/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/paladini","download_url":"https://codeload.github.com/paladini/backup-github-to-gitlab/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paladini%2Fbackup-github-to-gitlab/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35239467,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-07T02:00:07.222Z","response_time":90,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backup","backup-script","executable","github","github-to-gitlab","gitlab","migration","package","python","repo-sync","repository-migration","sync","user-backup","user-repo"],"created_at":"2026-07-07T18:30:37.382Z","updated_at":"2026-07-07T18:30:38.667Z","avatar_url":"https://github.com/paladini.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# backup-github-to-gitlab\n\n\u003e Mirror all your GitHub repositories to GitLab automatically — private stays private, public stays public.\n\n[![Python](https://img.shields.io/badge/python-3.11+-3776AB.svg?logo=python\u0026logoColor=white)](https://python.org)\n[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n\n---\n\n## Why\n\nHaving all your code on a single platform is a risk. This tool clones every repository from your GitHub account and creates matching repositories on GitLab, preserving visibility exactly. Run it once for a full backup, run it again anytime to sync incrementally.\n\n## Features\n\n- **Visibility-safe** — private repos are *never* created as public; defaults to private when in doubt\n- **Idempotent** — safe to re-run; existing GitLab repos receive incremental pushes, not duplicates\n- **Full mirror** — clones all branches and tags via `git clone --mirror`\n- **Dry-run mode** — preview every action before executing a single write\n- **Selective backup** — filter by glob pattern (`--filter \"myproject-*\"`)\n- **Works on Windows** — handles read-only `.git` files; tested on Windows 10 / PowerShell\n- **Rich terminal output** — progress bar and per-repo status table\n\n## Prerequisites\n\n- Python 3.11+\n- `git` in your PATH\n- SSH key registered on both GitHub and GitLab\n- [GitHub token](#github-token) with `repo` scope\n- [GitLab token](#gitlab-token) with `api` scope\n\n## Quick Start\n\n```bash\ngit clone git@github.com:paladini/backup-github-to-gitlab.git\ncd backup-github-to-gitlab\npip install -r requirements.txt\n\ncp config.example.yaml config.yaml   # then edit: set github.username and gitlab.username\ncp .env.example .env                  # then edit: set GITHUB_TOKEN and GITLAB_TOKEN\n\npython backup.py --dry-run            # preview — no changes made\npython backup.py                      # run the backup\n```\n\n## Token Setup\n\n### GitHub token\n\n1. Go to **Settings → Developer settings → Personal access tokens → Tokens (classic)**\n2. Click **Generate new token (classic)**\n3. Select scope: `repo` (required to list private repositories)\n4. Paste the token into `.env` as `GITHUB_TOKEN`\n\n### GitLab token\n\n1. Go to **User Settings → Access Tokens → Add new token**\n2. Select scope: `api` (required to create projects)\n3. Paste the token into `.env` as `GITLAB_TOKEN`\n\n## Configuration\n\n**`config.yaml`** — copy from `config.example.yaml`:\n\n```yaml\ngithub:\n  username: your-github-username\n\ngitlab:\n  username: your-gitlab-username    # can differ from GitHub\n  url: https://gitlab.com           # change only for self-hosted GitLab\n\nbackup:\n  include_forks: false      # include forked repositories? (default: false)\n  include_archived: true    # include archived repositories? (default: true)\n  temp_dir: ./tmp           # temporary dir for clones — auto-cleaned after each repo\n```\n\n**`.env`** — copy from `.env.example`:\n\n```\nGITHUB_TOKEN=ghp_...\nGITLAB_TOKEN=glpat-...\n```\n\nTokens are loaded at runtime and never logged. `.env` is in `.gitignore`.\n\n## Usage\n\n```bash\n# Back up all personal repositories\npython backup.py\n\n# Preview what would happen — no writes\npython backup.py --dry-run\n\n# Back up only repos matching a pattern\npython backup.py --filter \"myproject-*\"\n\n# Include forks (skipped by default)\npython backup.py --include-forks\n\n# Verbose: show raw git output (useful for debugging SSH issues)\npython backup.py --verbose\n\n# Use a different config file\npython backup.py --config /path/to/config.yaml\n```\n\n### Example output\n\n```\nDRY RUN MODE — no changes will be made\n\n my-private-repo ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3/10\n\nRepository                     Status          Details\nmy-private-repo                ~ dry run       would create as private\nmy-public-site                 ~ dry run       would create as public\narchived-experiment            ~ dry run       would create as private\n\n10 dry run\n\n[DRY RUN] Nenhuma operação foi executada.\n```\n\n## Security\n\nPrivate repositories on GitHub are **always** created as private on GitLab. Visibility is derived exclusively from the GitHub API response — there is no code path that can promote a private repository to public. If visibility cannot be determined, the tool defaults to private and logs a warning.\n\nSSH keys are used for all git operations. API tokens are read from environment variables (`.env`), never from the config file, and never written to logs.\n\n## Known Limitations\n\n| Limitation | Details |\n|---|---|\n| Git LFS | LFS objects are not transferred (`git clone --mirror` skips them) |\n| Organization repos | Not included in v1; planned for v2 with `--include-orgs` |\n| GitLab pull mirroring | Requires GitLab Premium for private repos; a GitHub Actions alternative is planned for v2 |\n\n## Roadmap\n\n- [x] **v1** — Full backup: clone + push via SSH, idempotent, dry-run, selective filter\n- [ ] **v2** — Auto-sync: GitLab pull mirror (Premium) or GitHub Actions push mirror\n- [ ] **v2** — Organization repos with explicit opt-in\n- [ ] **v3** — Wiki mirroring\n\n## Contributing\n\nIssues and pull requests are welcome. Please open an issue first to discuss what you'd like to change.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaladini%2Fbackup-github-to-gitlab","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpaladini%2Fbackup-github-to-gitlab","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaladini%2Fbackup-github-to-gitlab/lists"}