SubversionからGitHubへgit-svnへ移行する方法

- 環境
    - Windows 10 64bit バージョン1909
    - git version 2.25.0.windows.1

# やりたいこと
SVNで管理されてきたものをGitHubに移行します。
SVNにはドキュメントもソースも一緒に入っているのでドキュメントのディレクトリだけでリポジトリを作って移行します。
移行対象となるドキュメントのディレクトリ構成は、SVNの基本的な構成になっています。

```text
svn-repos
├ document ┬ branches --↓移行対象--
｜         ├ tags
｜         └ trunk    --↑移行対象--
└ ソース   ┬ branches
           ├ tags
           └ trunk
```

# リポジトリを初期化する
参考 : [Git-svnでSubversionからGitに移行方法 | 焼き芋ストロング](https://www.yoshikinoue.net/2018-09-21-subversion-git-gitsvn)

```bash
# 作業用ディレクトリを作成する
$ mkdir iko
$ cd iko/

# リポジトリ初期化する
$ git svn init -s --prefix=svn/ --no-minimize-url https://svn.server.com/svn-repos/document
Initialized empty Git repository in C:/git/iko/.git/
Authentication realm: <https://svn.server.com:443> Authorization Realm # 認証情報をダイアログで入力

# .gitディレクトリが作成される
$ ls -la
total 4
drwxr-xr-x 1 ponsuke 1049089 0  4月  9 10:50 ./
drwxr-xr-x 1 ponsuke 1049089 0  4月  9 08:25 ../
drwxr-xr-x 1 ponsuke 1049089 0  4月  9 10:51 .git/

# configの[svn-remote "svn"]を確認する
$ cat .git/config
[core]
        repositoryformatversion = 0
        filemode = false
        bare = false
        logallrefupdates = true
        symlinks = false
        ignorecase = true
[svn-remote "svn"]
        url = https://svn.server.com/svn-repos/document
        fetch = trunk:refs/remotes/svn/trunk
        branches = branches/*:refs/remotes/svn/*
        tags = tags/*:refs/remotes/svn/tags/*
```

## 失敗したこと
### Using higher level of URL
```bash
$ git svn init -s --prefix=svn/ https://svn.server.com/svn-repos/document
Initialized empty Git repository in C:/svn/iko/.git/
Using higher level of URL: https://svn.server.com/svn-repos/document => https://svn.server.com/svn-repos
```

- 原因 : オプションをつけずに元のSVNの一部だけ使おうとしたから
- 対応 : `--no-minimize-url`オプションをつける
- 参考
    - [Why is git-svn is randomly changing my root directory to the parent? - Stack Overflow](https://stackoverflow.com/questions/3756169/why-is-git-svn-is-randomly-changing-my-root-directory-to-the-parent/27556887)
    - [git-svnの使い方 | Tech控え帳](https://www.chihayafuru.jp/tech/index.php/archives/285)
    - [Git - git-svn Documentation](https://git-scm.com/docs/git-svn)

### svn-remote.svn.url already set
```bash
$ git svn init -s --prefix=svn/ --no-minimize-url https://svn.server.com/svn-repos/document
svn-remote.svn.url already set: https://svn.server.com/svn-repos
wanted to set to: https://svn.server.com/svn-repos/document
```
- 原因 : 1回目に`git svn init`に失敗した時に`.git/conf`が作成されて`[svn-remote "svn"]`の`url`が既に登録されているから
- 対応1 : `.git/conf`を削除してから`git svn init`する
- 対応2 : `.git/conf`の`url`を手動で書き直してから`git svn init`する

### cygwin_exception::open_stackdumpfile: Dumping stack trace to perl.exe.stackdump
事象と対応 : [cygwin_exception::open_stackdumpfile: Dumping stack trace to perl.exe.stackdump - Qiita](https://qiita.com/ponsuke0531/items/5e97a0c3ead318c447fa)


# SVNをフェッチする

```bash
# SVNをフェッチする(履歴が多いと数時間がかかる)
$ git svn fetch
W: Ignoring error from SVN, path probably does not exist: (160013): Filesystem has no item: '/svn-repos/!svn/bc/100/document' path not found
W: Do not be alarmed at the above message git-svn is just searching aggressively for old history.
This may take a while on large repositories
...省略...
r4500 = 398d507a54e980f048e03ec712482f52d4883e97 (refs/remotes/svn/trunk)
Can`t create session: Unable to connect to a repository at URL 'https://svn.server.com/svn-repos/document' at C:/apps/Git/mingw64/share/perl5/Git/SVN.pm line 184.

# 途中で止まったら、止まってしまったリビジョンから開始する
$ git svn fetch -r 4500:HEAD
...省略...
r5517 = d2a1e6a1a28571b3625e32be11f4e9b922c79380 (refs/remotes/svn/trunk)
Auto packing the repository in background for optimum performance.
See "git help gc" for manual housekeeping.
Enumerating objects: 24405, done.
Counting objects: 100% (24405/24405), done.
Delta compression using up to 4 threads
Compressing objects: 100% (23549/23549), done.
Writing objects: 100% (24405/24405), done.
Total 24405 (delta 12186), reused 0 (delta 0)
Removing duplicate objects: 100% (256/256), done.

# できたリモートブランチを確認する
$ git branch -r
  svn/dev1
  svn/dev2
  svn/dev3
  svn/tags/dev1
  svn/tags/hoge-dev2
  svn/tags/dev3
  svn/trunk
```


## 失敗したこと
### Ignoring error from SVN, path probably does not exist
```bash
$ git svn fetch
W: Ignoring error from SVN, path probably does not exist: (160013): Filesystem has no item: '/svn-repos/!svn/bc/100/document' path not found
(訳)SVNからのエラーを無視します。パスが存在しない可能性があります:(160013)...
W: Do not be alarmed at the above message git-svn is just searching aggressively for old history.
(訳)上記のメッセージに驚かないでください。git-svnは古い履歴を積極的に検索しています。
This may take a while on large repositories
```
- 原因 : 多分古い履歴にあるファイルがtruenkにはない？
- 対応 : 「Ignoring error」「Do not be alarmed」と言っているか気にしない・・・でいいといいな
- 参考 : [GitへSVNから移行時にエラー-Ignoring error from SVN | 森の人ブログ](https://morinohito.site/it/git/git-svn-error)

# GitHubに空のリポジトリを作成する
参考 : [リポジトリを作成する - GitHub ヘルプ](https://help.github.com/ja/github/getting-started-with-github/create-a-repo)
空のリポジトリにしたかったので[Initialize this repository with a README]にはチェックを入れずにリポジトリを作成した。

# ブランチを作成する
参考 : [git-svnでSVN→Gitへの移行をやってみたログ - Qiita](https://qiita.com/hidekuro/items/4727715fbda8f10b6b11)
SVNで`branches`にあったものを同じ名前でブランチにして`trunk`にあったものを`master`ブランチにする。
フェッチが終わった時点で、`svn/trunk`が`master`ブランチになっている場合もある(今回はそうならなかった)。


```bash
# svn/tags と svn/trunk 以外を「svn/」を除いたブランチ名で順次チェックアウトする
$ for BRANCH_NAME in $(git branch -r | grep -ve 'svn/tags\|svn/trunk\|.*@\d*' | sed -e 's:svn/::'); do
>     git checkout -b "$BRANCH_NAME" "svn/$BRANCH_NAME"
> done;
Switched to a new branch 'dev1'
Updating files: 100% (247/247), done.
Switched to a new branch 'dev2'
Switched to a new branch 'dev3'

# svn/trunk を masterブランチとしてチェックアウトする
$ git checkout -b master svn/trunk
Updating files: 100% (11617/11617), done.
Switched to a new branch 'master'

# ローカルのブランチを確認する
$ git branch
  dev1
  dev2
  dev3
* master

# あらかじめGitHubに作っておいた空リポジトリに追加します
$ git remote add origin https://github.com/username/new-repository.git

# プッシュします
$ git push -u origin --all
Enumerating objects: 24385, done.
Counting objects: 100% (24385/24385), done.
Delta compression using up to 4 threads
Compressing objects: 100% (11358/11358), done.
Writing objects: 100% (24385/24385), 1.43 GiB | 14.58 MiB/s, done.
Total 24385 (delta 12177), reused 24379 (delta 12172)
remote: Resolving deltas: 100% (12177/12177), done.
To https://github.com/username/new-repository.git
 * [new branch]        dev1 -> dev1
 * [new branch]        dev2 -> dev2
 * [new branch]        dev3 -> dev3
 * [new branch]        master -> master
Branch 'dev1' set up to track remote branch 'dev1' from 'origin'.
Branch 'dev2' set up to track remote branch 'dev2' from 'origin'.
Branch 'dev3' set up to track remote branch 'dev3' from 'origin'.
Branch 'master' set up to track remote branch 'master' from 'origin'.
```

# タグを作成する
SVNで`tags`にあったものを同じ名前でタグする。

```bash
# svn/tags を「svn/」を除いてタグにします
$ for TAG_NAME in $(git branch -r | grep -e 'svn/tags' | grep -ve '.*@\d*' | sed -e 's:svn/tags/::'); do
>    git tag "$TAG_NAME" "svn/tags/$TAG_NAME"
> done;

# できたタグを確認します
$ git tag
dev1
hoge-dev2
dev3

# タグをプッシュします
$ git push origin --tags
Enumerating objects: 688, done.
Counting objects: 100% (688/688), done.
Delta compression using up to 4 threads
Compressing objects: 100% (490/490), done.
Writing objects: 100% (688/688), 260.72 MiB | 10.43 MiB/s, done.
Total 688 (delta 295), reused 484 (delta 182)
remote: Resolving deltas: 100% (295/295), done.
To https://github.com/username/new-repository.git
 * [new tag]           dev1 -> dev1
 * [new tag]           hoge-dev2 -> hoge-dev2
 * [new tag]           dev3 -> dev3
```

# 最後の確認
1. ブラウザでGitHubに作ったリポジトリのブランチやタグを確認する
1. GitHubに移行したリポジトリをクローンしてSourceTreeなどのツールで履歴を確認する