# Перенос проекта с GitHub на GitLab с сохранением истории и дат Инструкция для локального репозитория, когда GitHub недоступен (аккаунт заблокирован) и переносить приходится из своей копии. Так был перенесён `lpra`: 195 коммитов, 8 тегов, 3 релиза - хэши и даты совпали. ## Что сохраняется, а что нет | Что | Сохраняется? | |---|---| | Даты коммитов (автора и коммита) | **Да, точно.** Они хранятся внутри коммита; push той же истории не меняет ни одного хэша | | Ветки и теги | Да, если они есть локально (см. шаг 2) | | Дата создания проекта | **Нет.** На gitlab.com она всегда - день создания проекта, задним числом не ставится. Начало проекта видно по первому коммиту | | Релизы | Создаются заново, **с исходной датой** (`--released-at`); файлы релизов - заново собрать или взять свои копии | | Issues, PR, звёзды, Actions | Нет: при заблокированном GitHub их не выгрузить | **Нельзя:** `rebase`, `squash`, `filter-branch`, `filter-repo`, `commit --amend` по старым коммитам, «Import from GitHub» с чужой копии - всё это переписывает коммиты и их даты. ## 0. Один раз: вход в GitLab ```sh brew install glab glab auth login --hostname gitlab.com --web --git-protocol https ``` Вход через браузер даёт права и на push, и на релизы. Git берёт пароль у glab - для каждого репозитория это настраивается в шаге 4 (или один раз для всех: `git config --global credential.https://gitlab.com.helper '!glab auth git-credential'`). SSH-ключ тоже подойдёт, но в GitLab у ключа должен быть **Usage type: Authentication & Signing** (с типом «Signing» по SSH не пускает). ## 1. Пустой проект в GitLab https://gitlab.com/projects/new → **Create blank project** → имя, владелец `itzredtea`. **Снять галочку «Initialize repository with a README»** - иначе в проекте появится свой первый коммит, и историю придётся склеивать. Или из терминала: ```sh glab repo create itzredtea/ИМЯ --public # или --private; README не создаётся ``` ## 2. Проверить, что всё есть локально ```sh cd ~/CodeRepos/ИМЯ git status # незакоммиченное закоммитить или убрать git branch -a # ветки: свои и remotes/origin/* git tag -l # теги ``` - Ветки, которых нет локально, но есть `remotes/origin/X`, тоже переносятся (шаг 3). - **Теги, созданные на GitHub при публикации релиза, могут не быть локально** - если их не делали `git fetch --tags`. Такой тег создаётся заново на нужном коммите (шаг 5). Коммит ищется по дате публикации релиза: последний коммит перед ней (`git log --format='%h %cd %s' --date=iso --until='ДАТА'`). Снимок до переноса - для сверки: ```sh git log --branches --remotes=origin --tags --format='%H %aI %cI' | sort > /tmp/before.txt git show-ref --tags | sort > /tmp/tags-before.txt ``` ## 3. Отправить историю ```sh git remote rename origin github # GitHub остаётся под своим именем git remote add origin https://gitlab.com/itzredtea/ИМЯ.git H='!glab auth git-credential' git -c credential.helper= -c "credential.helper=$H" push origin --all # все локальные ветки git -c credential.helper= -c "credential.helper=$H" push origin 'refs/remotes/github/*:refs/heads/*' # ветки, что были только на GitHub git -c credential.helper= -c "credential.helper=$H" push origin --tags ``` Вторая команда push может сказать про `HEAD` - это не ветка, сообщение можно не смотреть. ## 4. Сверить и переключиться Сверка - по свежей копии из GitLab: ```sh rm -rf /tmp/check && git clone -q https://gitlab.com/itzredtea/ИМЯ.git /tmp/check cd /tmp/check && git log --remotes=origin --tags --format='%H %aI %cI' | sort > /tmp/after.txt git show-ref --tags | sort > /tmp/tags-after.txt diff /tmp/before.txt /tmp/after.txt && echo "коммиты и даты совпали" diff /tmp/tags-before.txt /tmp/tags-after.txt && echo "теги совпали" ``` Пустой `diff` - история перенесена один в один. Дальше работать с GitLab: ```sh cd ~/CodeRepos/ИМЯ git config --local credential.https://gitlab.com.helper '!glab auth git-credential' git fetch origin && git branch -u origin/main main ``` ## 5. Релизы с исходными датами Дата - время публикации релиза на GitHub (UTC). Файлы - свои копии или сборка из коммита релиза во временной папке: ```sh git worktree add --detach /tmp/rel-1.2.0 1.2.0 # тег или хэш коммита релиза cd /tmp/rel-1.2.0 && ./build.sh # как проект собирается glab release create 1.2.0 bin/файл1 bin/файл2 \ -R itzredtea/ИМЯ --name "Release 1.2.0" \ --released-at "2026-10-15T16:34:53Z" \ --notes "Перенесено с GitHub, дата публикации исходная." # тега нет (его делал GitHub) - добавить: --ref <полный хэш коммита> git worktree remove /tmp/rel-1.2.0 ``` Создавать по порядку, от старых к новым. Последним GitLab считает релиз с самой поздней датой. Ссылки на файлы: - конкретный релиз: `https://gitlab.com/itzredtea/ИМЯ/-/releases/ТЕГ/downloads/ФАЙЛ` - последний: `https://gitlab.com/itzredtea/ИМЯ/-/releases/permalink/latest/downloads/ФАЙЛ` ## 6. Ссылки в самом проекте Найти и заменить всё, что смотрит на GitHub: ```sh grep -rn "github.com\|githubusercontent" --exclude-dir=.git . ``` | GitHub | GitLab | |---|---| | `https://raw.githubusercontent.com/USER/ИМЯ/main/ПУТЬ` | `https://gitlab.com/itzredtea/ИМЯ/-/raw/main/ПУТЬ` | | `https://github.com/USER/ИМЯ/releases/latest/download/ФАЙЛ` | `https://gitlab.com/itzredtea/ИМЯ/-/releases/permalink/latest/downloads/ФАЙЛ` | | `https://api.github.com/repos/USER/ИМЯ/releases/latest` (`tag_name`) | `https://gitlab.com/api/v4/projects/itzredtea%2FИМЯ/releases/permalink/latest` (`tag_name`) | | `https://api.github.com/repos/USER/ИМЯ/contents/ПАПКА?ref=main` | `https://gitlab.com/api/v4/projects/itzredtea%2FИМЯ/repository/tree?path=ПАПКА&ref=main` (поле `path`) | | картинки `github.com/user-attachments/...` | скачать, пока открываются, в `docs/` и ссылаться на файлы | Путь Go-модуля (`module github.com/...` в `go.mod`) можно не трогать: это только имя. ## 7. После переноса - Описание проекта в GitLab: «Перенесён с GitHub, начат ДД.ММ.ГГГГ» - раз дата создания новая. - Токены, которые светились в чатах и логах, - отозвать и выпустить новые.