{"id":26502285,"url":"https://github.com/akuli/ghbackup","last_synced_at":"2026-04-17T12:31:30.615Z","repository":{"id":277040715,"uuid":"931129247","full_name":"Akuli/ghbackup","owner":"Akuli","description":"Local backups of GitHub issue/PR comments","archived":false,"fork":false,"pushed_at":"2025-02-12T13:52:33.000Z","size":37,"stargazers_count":2,"open_issues_count":4,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-20T17:55:11.987Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/Akuli.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}},"created_at":"2025-02-11T19:04:54.000Z","updated_at":"2025-02-12T20:58:49.000Z","dependencies_parsed_at":"2025-02-11T20:41:41.052Z","dependency_job_id":null,"html_url":"https://github.com/Akuli/ghbackup","commit_stats":null,"previous_names":["akuli/ghbackup"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Akuli/ghbackup","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Akuli%2Fghbackup","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Akuli%2Fghbackup/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Akuli%2Fghbackup/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Akuli%2Fghbackup/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Akuli","download_url":"https://codeload.github.com/Akuli/ghbackup/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Akuli%2Fghbackup/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31929503,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-17T10:35:34.458Z","status":"ssl_error","status_checked_at":"2026-04-17T10:35:09.472Z","response_time":62,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":[],"created_at":"2025-03-20T17:53:22.803Z","updated_at":"2026-04-17T12:31:30.584Z","avatar_url":"https://github.com/Akuli.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GitHub Backups\n\nThis is a Python script that downloads comments from GitHub.\nThis way you can look at GitHub issues and pull requests of your projects\neven if GitHub is down or your internet doesn't work.\n\n**This script does not download the content of the repository itself, only issue and PR comments.**\nBy design, Git repositories are always somewhat backed up,\nbecause every developer has a local clone of the whole repository.\n\n\n## Usage\n\nCreate a new directory, and place a file named `info.txt` in it with the following content:\n\n```\nGitHub URL: https://github.com/Akuli/ghbackup\n```\n\n(or whatever repository you want to back up). Then run:\n\n```\n$ pip install requests\n$ python3 ghbackup.py path/to/folder_that_contains_info_dot_txt\n```\n\nThis will copy all issue and pull request comments from GitHub to your local folder.\n\nIf you get rate limit errors, you can simply run the same command again later\n(will continue where it left off, not start from scratch).\nAlternatively, you can specify a GitHub API token.\nRequests with an API token have higher rate limits than requests without.\n\n```\n$ python3 ghbackup.py path/to/folder_that_contains_info_dot_txt --token $(cat my_secret_token.txt)\n```\n\nThis assumes you have saved your GitHub token to file `my_secret_token.txt`.\nI don't recommend copy/pasting the token directly to command line,\nbecause the token may get saved in your bash history or similar.\n\n\n## File Structure\n\nGitHub returns everything as JSON.\nThis script converts GitHub's JSONs into a file structure\nthat is human-readable and easy to parse.\nFor example, you can commit the results to Git and get reasonable `git diff` output.\n\nOnce the script has ran, the backup folder will look something like this:\n\n```\n$ ls\ninfo.txt     issue_00024  issue_00051  issue_00084  pr_00014  pr_00035  pr_00059  pr_00079\nissue_00002  issue_00026  issue_00052  issue_00085  pr_00016  pr_00036  pr_00061  pr_00080\nissue_00003  issue_00029  issue_00056  issue_00086  pr_00017  pr_00037  pr_00062  pr_00089\nissue_00004  issue_00031  issue_00060  issue_00087  pr_00019  pr_00038  pr_00063  pr_00090\nissue_00005  issue_00032  issue_00065  issue_00088  pr_00020  pr_00039  pr_00064  pr_00092\nissue_00006  issue_00040  issue_00070  issue_00091  pr_00021  pr_00043  pr_00066  pr_00094\nissue_00008  issue_00041  issue_00071  issue_00093  pr_00022  pr_00046  pr_00067  pr_00095\nissue_00011  issue_00042  issue_00074  issue_00097  pr_00025  pr_00047  pr_00068  pr_00096\nissue_00012  issue_00044  issue_00077  issue_00098  pr_00027  pr_00053  pr_00069  pr_00099\nissue_00013  issue_00045  issue_00078  pr_00001     pr_00028  pr_00054  pr_00072\nissue_00015  issue_00048  issue_00081  pr_00007     pr_00030  pr_00055  pr_00073\nissue_00018  issue_00049  issue_00082  pr_00009     pr_00033  pr_00057  pr_00075\nissue_00023  issue_00050  issue_00083  pr_00010     pr_00034  pr_00058  pr_00076\n```\n\nThe content of `info.txt` is:\n\n```\nGitHub URL: https://github.com/Akuli/ghbackup\nUpdated: 2025-02-10 22:19:28.145222+00:00\n```\n\nEach `issue_xxxxx` and `pr_xxxxx` is a subfolder.\n\n```\n$ cd issue_00077/\n$ ls\n0001_Akuli.txt  0002_Moosems.txt  0003_Akuli.txt  0004_Akuli.txt  0005_Akuli.txt  info.txt\n```\n\nThe content of `issue_xxxxx/info.txt` or `pr_xxxxx/info.txt` looks like this:\n\n```\n$ cat info.txt\nTitle: Jou package manager?\nUpdated: 2025-01-26T18:25:08+00:00\n```\n\nOther files in the subfolders represent comments on the issue or PR, and they look e.g. like this:\n\n```\n$ cat 0002_Moosems.txt\nGitHub ID: 1868421964\nAuthor: Moosems\nCreated: 2023-12-24T03:13:38Z\n\nAny thoughts on the package manager?\n```\n\n\n## Automating\n\nI added the following to a shell script that runs every time I log in:\n\n```\n# github issues/PR comments backup\n(\n    [ -d gh-backups ] || git init gh-backups\n    cd gh-backups\n    [ -f ghbackup.py ] || wget https://raw.githubusercontent.com/Akuli/ghbackup/refs/heads/main/ghbackup.py\n    function add_repo {\n        local url=$1\n        local dir=$(basename $url)\n        [ -f $dir/info.txt ] || (mkdir -vp $dir \u0026\u0026 echo \"GitHub URL: $url\" | tee $dir/info.txt)\n    }\n    add_repo https://github.com/Akuli/3d-game-experiment\n    add_repo https://github.com/Akuli/3dthingy\n    add_repo https://github.com/Akuli/asda\n    add_repo https://github.com/Akuli/catris\n    ...more repos...\n    while sleep 1h; do\n        python3 -u ghbackup.py */ || true  # may fail due to GitHub API rate limit\n        git add .\n        git commit -m \"Automatic github backup on $(date)\" || true  # fails if no changes\n    done\n) \u0026\u003eghbackup-log.txt \u0026\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fakuli%2Fghbackup","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fakuli%2Fghbackup","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fakuli%2Fghbackup/lists"}