Перенос SVN-проекта в Git со всеми ветками, тегами и авторами
Инструкция рассчитана на стандартную структуру SVN-проекта:
project/
├── trunk/
├── branches/
│ ├── develop/
│ └── release-1.0/
└── tags/
├── v1.0.0/
└── v1.1.0/
Для импорта необходимо использовать адрес корня проекта:
https://svn.example.com/project
Не следует указывать адрес только основной ветки:
https://svn.example.com/project/trunk
При указании trunk остальные ветки и теги не будут импортированы.
1. Запустите оболочку sh
Если текущей оболочкой является zsh/fish или другие, запустите sh:
sh
После завершения работы выйти из sh можно командой:
exit
2. Задайте параметры миграции
Укажите адрес корня SVN-проекта:
export SVN_URL="https://svn.example.com/project"
Укажите имя пользователя SVN:
export SVN_USER="svn-user"
Укажите рабочий каталог:
export MIGRATION_DIR="$HOME/svn-migration"
Укажите имя локального каталога проекта:
export PROJECT_NAME="project-full-migration"
Укажите путь к файлу сопоставления авторов:
export AUTHORS_FILE="$MIGRATION_DIR/authors.txt"
Укажите адрес целевого Git-репозитория:
export GIT_URL="https://git.example.com/company/project.git"
Проверьте параметры:
printf 'SVN: %s\nGit: %s\nКаталог: %s\nАвторы: %s\n' "$SVN_URL" "$GIT_URL" "$MIGRATION_DIR/$PROJECT_NAME" "$AUTHORS_FILE"
3. Проверьте необходимые инструменты
Проверьте SVN:
svn --version --quiet
Проверьте Git:
git --version
Проверьте git svn:
git svn --version
Проверьте Python:
python3 --version
Python будет использоваться только для формирования файла authors.txt.
4. Проверьте структуру SVN-проекта
Проверьте корень проекта:
svn ls "$SVN_URL" --username "$SVN_USER"
Ожидаемый результат:
branches/
tags/
trunk/
Проверьте ветки:
svn ls "$SVN_URL/branches" --username "$SVN_USER"
Проверьте теги:
svn ls "$SVN_URL/tags" --username "$SVN_USER"
Если в корне проекта отсутствуют каталоги trunk, branches и tags, эта инструкция не подходит для структуры проекта.
5. Создайте рабочий каталог
Создайте каталог миграции:
mkdir -p "$MIGRATION_DIR"
Перейдите в него:
cd "$MIGRATION_DIR"
Проверьте текущий каталог:
pwd
6. Получите журнал SVN
Выгрузите журнал проекта в формате XML:
svn log --xml "$SVN_URL" --username "$SVN_USER" > svn-log.xml
Проверьте, что файл создан:
ls -lh svn-log.xml
Проверьте начало файла:
head -n 10 svn-log.xml
В файле должны присутствовать элементы с именами авторов:
<author>andrey</author>
7. Создайте скрипт формирования authors.txt
Создайте файл:
nano create-authors.sh
Добавьте в него:
#!/bin/sh
set -eu
INPUT_FILE="${1:-svn-log.xml}"
OUTPUT_FILE="${2:-authors.txt}"
if [ ! -f "$INPUT_FILE" ]; then
echo "Не найден XML-файл журнала SVN: $INPUT_FILE" >&2
exit 1
fi
python3 - "$INPUT_FILE" "$OUTPUT_FILE" <<'PY'
import sys
import xml.etree.ElementTree as ET
from pathlib import Path
input_file = Path(sys.argv[1])
output_file = Path(sys.argv[2])
try:
tree = ET.parse(input_file)
except ET.ParseError as error:
print(f"Не удалось разобрать XML-файл: {error}", file=sys.stderr)
raise SystemExit(1)
authors = {
element.text.strip()
for element in tree.findall(".//author")
if element.text and element.text.strip()
}
output_file.parent.mkdir(parents=True, exist_ok=True)
with output_file.open("w", encoding="utf-8", newline="\n") as output:
for author in sorted(authors, key=str.casefold):
output.write(f"{author} = {author} <{author}@svn.local>\n")
print(f"Найдено SVN-пользователей: {len(authors)}")
print(f"Создан файл: {output_file}")
PY
Сделайте скрипт исполняемым:
chmod +x create-authors.sh
Запустите его:
./create-authors.sh svn-log.xml "$AUTHORS_FILE"
8. Отредактируйте authors.txt
Проверьте созданный файл:
cat "$AUTHORS_FILE"
Первоначально он будет выглядеть примерно так:
admin = admin <admin@svn.local>
andrey = andrey <andrey@svn.local>
ivanov = ivanov <ivanov@svn.local>
Откройте файл:
nano "$AUTHORS_FILE"
Укажите настоящие имена и адреса электронной почты:
admin = SVN Administrator <svn-admin@example.com>
andrey = Андрей Иванов <andrey@example.com>
ivanov = Иван Иванов <ivanov@example.com>
Формат каждой строки:
SVN_LOGIN = Имя пользователя <email@example.com>
Левая часть должна точно совпадать с именем автора в SVN. Изменять следует только имя и адрес электронной почты в правой части.
Сохраните резервную копию:
cp "$AUTHORS_FILE" "$AUTHORS_FILE.backup"
9. Импортируйте весь SVN-проект
Убедитесь, что каталог проекта ещё не существует:
test ! -e "$MIGRATION_DIR/$PROJECT_NAME" && echo "Каталог свободен" || echo "Каталог уже существует"
Если каталог остался от предыдущей попытки, переименуйте его:
mv "$MIGRATION_DIR/$PROJECT_NAME" "$MIGRATION_DIR/$PROJECT_NAME-old"
Перейдите в рабочий каталог:
cd "$MIGRATION_DIR"
Запустите импорт:
git svn clone "$SVN_URL" --stdlayout --prefix=svn/ --authors-file="$AUTHORS_FILE" --username="$SVN_USER" --log-window-size=1000 "$PROJECT_NAME"
Команда автоматически создаст локальный Git-репозиторий и импортирует:
trunk;- все каталоги из
branches; - все каталоги из
tags; - историю коммитов;
- даты и сообщения коммитов;
- авторов из
authors.txt.
Инициализировать Git внутри SVN не требуется.
Если импорт остановился из-за неизвестного автора, добавьте его в authors.txt:
printf '%s\n' 'unknown-user = Unknown User <unknown-user@example.com>' >> "$AUTHORS_FILE"
Перейдите в созданный репозиторий:
cd "$MIGRATION_DIR/$PROJECT_NAME"
Продолжите импорт:
git svn fetch
Если импорт прервался из-за сетевой ошибки:
git svn fetch --log-window-size=100
10. Проверьте импортированный репозиторий
Перейдите в каталог проекта:
cd "$MIGRATION_DIR/$PROJECT_NAME"
Проверьте, что это Git-репозиторий:
git rev-parse --is-inside-work-tree
Ожидаемый результат:
true
Проверьте настройки git svn:
git config --get-regexp '^svn-remote\.'
Проверьте импортированные SVN-ссылки:
git branch -r
Результат должен выглядеть примерно так:
svn/trunk
svn/develop
svn/release-1.0
svn/tags/v1.0.0
svn/tags/v1.1.0
11. Создайте основную ветку main
Создайте локальную ветку main из SVN trunk:
git switch -C main refs/remotes/svn/trunk
Проверьте текущую ветку:
git branch --show-current
Ожидаемый результат:
main
Проверьте последние коммиты:
git log --oneline --decorate -10
12. Создайте скрипт переноса веток
Создайте файл:
nano create-branches.sh
Добавьте в него:
#!/bin/sh
set -eu
git for-each-ref --format='%(refname)' refs/remotes/svn/ |
while IFS= read -r ref
do
branch="${ref#refs/remotes/svn/}"
case "$branch" in
trunk|tags/*|*@*)
continue
;;
esac
if ! git check-ref-format "refs/heads/$branch"
then
echo "Недопустимое имя Git-ветки: $branch" >&2
continue
fi
git update-ref "refs/heads/$branch" "$ref"
echo "Создана или обновлена Git-ветка: $branch"
done
Сделайте скрипт исполняемым:
chmod +x create-branches.sh
Запустите его:
./create-branches.sh
Скрипт пропускает:
trunk, поскольку из него уже создана веткаmain;- каталог
tags; - технические ссылки вида
branch@123.
13. Проверьте локальные ветки
Покажите созданные ветки:
git branch --list
Ожидаемый результат:
develop
* main
release-1.0
Покажите только короткие имена веток:
git for-each-ref --format='%(refname:short)' refs/heads/
Сравните их со списком веток в SVN:
svn ls "$SVN_URL/branches" --username "$SVN_USER"
В Git дополнительно должна присутствовать ветка main, созданная из trunk.
14. Создайте скрипт переноса тегов
Создайте файл:
nano create-tags.sh
Добавьте в него:
#!/bin/sh
set -eu
git for-each-ref --format='%(refname)' refs/remotes/svn/tags/ |
while IFS= read -r ref
do
tag="${ref#refs/remotes/svn/tags/}"
case "$tag" in
*@*)
continue
;;
esac
if ! git check-ref-format "refs/tags/$tag"
then
echo "Недопустимое имя Git-тега: $tag" >&2
continue
fi
git tag -f "$tag" "$ref"
echo "Создан или обновлён Git-тег: $tag"
done
Сделайте скрипт исполняемым:
chmod +x create-tags.sh
Запустите его:
./create-tags.sh
15. Проверьте Git-теги
Покажите созданные теги:
git tag --list
Ожидаемый результат:
v1.0.0
v1.1.0
Проверьте конкретный тег:
git show v1.0.0
Сравните со списком тегов в SVN:
svn ls "$SVN_URL/tags" --username "$SVN_USER"
16. Проверьте историю и авторов
Покажите граф всех веток и тегов:
git log --all --graph --decorate --oneline
Проверьте авторов:
git log --all --format='%an <%ae>' | sort -u
Сравните их с файлом:
cat "$AUTHORS_FILE"
Проверьте наличие связи с SVN-ревизиями:
git log --all --grep='git-svn-id:' --oneline
Проверьте целостность репозитория:
git fsck --full
17. Подключите удалённый Git-репозиторий
Целевой Git-репозиторий рекомендуется создавать пустым:
- без
README; - без
.gitignore; - без лицензии;
- без первоначального коммита.
Проверьте текущие подключения:
git remote -v
Если origin отсутствует, добавьте его:
git remote add origin "$GIT_URL"
Если origin уже существует, замените его адрес:
git remote set-url origin "$GIT_URL"
Проверьте результат:
git remote -v
18. Проверьте отправку без публикации
Проверьте основную ветку:
git push --dry-run origin main
Проверьте все локальные ветки:
git push --dry-run origin --all
Проверьте все теги:
git push --dry-run origin --tags
Команда с параметром --dry-run проверяет отправку, но не изменяет удалённый репозиторий.
19. Отправьте все ветки и теги
Отправьте основную ветку:
git push -u origin main
Отправьте все локальные ветки:
git push origin --all
Отправьте все теги:
git push origin --tags
Команда git push origin --all отправляет только ветки. Теги необходимо отправлять отдельной командой.
20. Проверьте удалённый Git-репозиторий
Покажите опубликованные ветки:
git ls-remote --heads origin
Покажите опубликованные теги:
git ls-remote --tags origin
В удалённом Git-проекте должны присутствовать:
main
develop
release-1.0
v1.0.0
v1.1.0
Также необходимо проверить в интерфейсе Git-платформы:
- основную ветку;
- историю коммитов;
- авторов;
- список веток;
- список тегов;
- содержимое файлов в каждой актуальной ветке.
21. Выполните финальную синхронизацию
Если после первоначального импорта в SVN появились новые коммиты, перед окончательным переходом переведите SVN-проект в режим только для чтения.
Перейдите в локальный репозиторий:
cd "$MIGRATION_DIR/$PROJECT_NAME"
Получите последние SVN-ревизии:
git svn fetch
Обновите main до последнего состояния SVN trunk:
git switch main
git reset --hard refs/remotes/svn/trunk
Повторно обновите локальные ветки:
./create-branches.sh
Повторно обновите теги:
./create-tags.sh
Проверьте историю:
git log --all --graph --decorate --oneline
Отправьте обновлённые ветки:
git push origin --all
Отправьте новые теги:
git push origin --tags
Проверьте удалённый репозиторий:
git ls-remote --heads origin
git ls-remote --tags origin
После проверки удалённого Git-репозитория SVN следует оставить в режиме только для чтения на согласованный период. Резервную копию SVN, authors.txt, svn-log.xml и локальный каталог миграции рекомендуется сохранить до окончательного подтверждения успешного перехода.