🍴

GitHub の Public リポジトリを Private に Fork する

2023/06/10に公開

他者が公開で管理している Git リポジトリを個人的に(or 社内だけのものとして) Fork したい。
ググるといろんな手段があって違いがよくわからないのでやってみた。

先に簡単に結論

branch も含めてコピーしたかったら --mirror を付けて clone してきて --mirror をつけて自分の private リポジトリに push すればいい。

git clone --mirror https://github.com/${user-or-org}/${repo-fork-from}.git
git push  --mirror https://github.com/${user-or-org}/${repo-fork-to}.git

それだけ。
本題じゃないけど Fork 先のリポジトリの GitHub Actions は リポジトリの Settings から無効にしておこう。 Fork もとにワークフローファイルがあると知らずに起動されててびっくりしたりするので。

Fork 元となるリポジトリの作成

まずは Fork 元となるリポジトリを作成 → https://github.com/hatappo/fork-test

  • リポジトリを作成して Push
  • ブランチ feature/fuga を作って Push して GitHub 上で main にマージ

この状態から以降スタート。

Fork してみる

4パターン。

  • a) GitHub の公式の Fork
  • b) シンプルに clone して remote を追加して push する。
  • c) clone --bare して push する。
  • d) clone --mirror して push --mirror する。

a) GitHub の公式の Fork

GitHub の画面から 「Fork」 → 「Create a new fork」。

ここで「自分がオーナーのリポジトリを自分のところに Fork できない」ということを知る。そりゃそうだ。諦める。

そもそもこのやり方では目的の「 Private に」というのが実現できないので OK。

b) clone して origin を追加して push する。

# fork-test-simple という名前で Checkout
$ git clone https://github.com/hatappo/fork-test.git fork-test-simple
Cloning into 'fork-test-simple'...
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 7 (delta 1), reused 5 (delta 0), pack-reused 0
Receiving objects: 100% (7/7), done.
Resolving deltas: 100% (1/1), done.

$ cd ./fork-test-simple

# ここで GitHub 上で fork-test-simple というコピー先のリポジトリを作っておく。

# remote repository を追加して push
$ git remote add my https://github.com/hatappo/fork-test-simple.git
$ git push -u my main
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (6/6), 856 bytes | 856.00 KiB/s, done.
Total 6 (delta 0), reused 6 (delta 0), pack-reused 0
To https://github.com/hatappo/fork-test-simple.git
 * [new branch]      main -> main
branch 'main' set up to track 'my/main'.

出来上がったリポジトリ → https://github.com/hatappo/fork-test-simple

  • Branch: デフォルトブランチである main のみがコピーされた。

c) clone --bare して push する。

# fork-test-bare という名前で Checkout
$ git clone --bare https://github.com/hatappo/fork-test.git fork-test-bare
Cloning into bare repository 'fork-test-bare'...
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 7 (delta 1), reused 5 (delta 0), pack-reused 0
Receiving objects: 100% (7/7), done.
Resolving deltas: 100% (1/1), done.

$ cd ./fork-test-bare

# ここで GitHub 上で fork-test-bare というコピー先のリポジトリを作っておく。

# remote repository を追加して push
$ git remote add my https://github.com/hatappo/fork-test-bare.git
$ git push -u my main
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (6/6), 856 bytes | 856.00 KiB/s, done.
Total 6 (delta 0), reused 6 (delta 0), pack-reused 0
To https://github.com/hatappo/fork-test-bare.git
 * [new branch]      main -> main
branch 'main' set up to track 'my/main'.

出来上がったリポジトリ → https://github.com/hatappo/fork-test-bare

  • Branch: デフォルトブランチである main のみがコピーされた。

d) clone --mirror して push --mirror する。

# fork-test-mirror という名前で Checkout
$ git clone --mirror https://github.com/hatappo/fork-test.git fork-test-mirror
Cloning into bare repository 'fork-test-mirror'...
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 7 (delta 1), reused 5 (delta 0), pack-reused 0
Receiving objects: 100% (7/7), done.
Resolving deltas: 100% (1/1), done.

$ cd ./fork-test-mirror

# --mirror で push
$ git push --mirror https://github.com/hatappo/fork-test-mirror.git
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (7/7), 922 bytes | 922.00 KiB/s, done.
Total 7 (delta 1), reused 7 (delta 1), pack-reused 0
remote: Resolving deltas: 100% (1/1), done.
To https://github.com/hatappo/fork-test-mirror.git
 * [new branch]      feature/fuga -> feature/fuga
 * [new branch]      main -> main
 ! [remote rejected] refs/pull/1/head -> refs/pull/1/head (deny updating a hidden ref)
error: failed to push some refs to 'https://github.com/hatappo/fork-test-mirror.git'

最後にエラーが出ている。。

出来上がったリポジトリ → https://github.com/hatappo/fork-test-mirror

  • Branch: 全てのブランチがコピーされた。

Fork したリポジトリに、Fork 元リポジトリのその後の更新をマージ

さっき作業したローカルリポジトリは一度消した前提で、改めて更新を取り込みたいときはどうするんだろ、というシナリオ

b) 無印のパターン

# Fork 先を clone
$ git clone https://github.com/hatappo/fork-test-simple
Cloning into 'fork-test-simple'...
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 6 (delta 0), reused 6 (delta 0), pack-reused 0
Receiving objects: 100% (6/6), done.


# Fork 元である origin から Pull
cd ./fork-test-simple
$ git pull origin main
From https://github.com/hatappo/fork-test
 * branch            main       -> FETCH_HEAD
Updating be26448..2a3622c
Fast-forward
 README.md | 4 ++++
 1 file changed, 4 insertions(+)

# Fork 先である my に Push
$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 665 bytes | 665.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/hatappo/fork-test-simple
   be26448..2a3622c  main -> main

Fork 先を clone してきて、 Fork 元を Pull してきて、Fork 先に Push 。単純といえば単純。

c) --bare のパターン

# Fork 元のほうを clone
$ git clone --bare https://github.com/hatappo/fork-test fork-test-bare
Cloning into bare repository 'fork-test.git'...
remote: Enumerating objects: 11, done.
remote: Counting objects: 100% (11/11), done.
remote: Compressing objects: 100% (5/5), done.
remote: Total 11 (delta 2), reused 7 (delta 0), pack-reused 0
Receiving objects: 100% (11/11), done.
Resolving deltas: 100% (2/2), done.

$ cd ./fork-test-bare

# Fork 先を remote に追加して Push
$ git remote add my https://github.com/hatappo/fork-test-bare.git
$ git push -u my main
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 665 bytes | 665.00 KiB/s, done.
Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
To https://github.com/hatappo/fork-test-bare.git
   be26448..2a3622c  main -> main
branch 'main' set up to track 'my/main'.

Fork 先を clone してくると、どうやって Fork 元を Pull すればいいか分からなかった。なので Fork 元を clone してきて最初と同じ手順をやったかたち。

d) --mirror のパターン

# Fork 元のほうを clone
$ git clone --mirror https://github.com/hatappo/fork-test fork-test-mirror
Cloning into bare repository 'fork-test-mirror'...
remote: Enumerating objects: 11, done.
remote: Counting objects: 100% (11/11), done.
remote: Compressing objects: 100% (5/5), done.
remote: Total 11 (delta 2), reused 7 (delta 0), pack-reused 0
Receiving objects: 100% (11/11), done.
Resolving deltas: 100% (2/2), done.

$ cd ./fork-test-mirror

# Fork 先を remote に追加して Push
$ git remote add my https://github.com/hatappo/fork-test-mirror.git
$ git push --mirror my
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 278 bytes | 278.00 KiB/s, done.
Total 3 (delta 0), reused 2 (delta 0), pack-reused 0
To https://github.com/hatappo/fork-test-mirror.git
 * [new branch]      feature/piyo -> feature/piyo
 * [new reference]   my/main -> my/main
 ! [remote rejected] refs/pull/1/head -> refs/pull/1/head (deny updating a hidden ref)
 ! [remote rejected] refs/pull/2/head -> refs/pull/2/head (deny updating a hidden ref)
error: failed to push some refs to 'https://github.com/hatappo/fork-test-mirror.git'

こちらもやはり Fork 元を clone してきて最初と同じ手順をやった。 Branch までコピーされるのがやはり違い。

最後に出てるエラーの解消方法はこちら → https://tsuyoshin.hatenablog.com/entry/20200930/1601424899

結論

  • --bare でやるのとそうでないのとで違いがよく分からなかった。
  • branch (と試さなかったけどたぶん tag )までコピーしたかったら --mirror を付けて clone と push すればいい。

おまけ - Fork 元のリポジトリの作成

$ mkdir fork-test

$ cd fork-test/

$ git init
Initialized empty Git repository in /*****/fork-test/.git/

# ファイル作成
$ git add --all && git commit --message "1st commit"
[main (root-commit) 05e5fa4] 1st commit
 1 file changed, 2 insertions(+)
 create mode 100644 README.md

# gh コマンドで GitHub にリポジトリを作成して Push
$ gh repo create
? What would you like to do? Push an existing local repository to GitHub
? Path to local repository .
? Repository name fork-test
? Repository owner hatappo
? Description
? Visibility Public
✓ Created repository hatappo/fork-test on GitHub
? Add a remote? Yes
? What should the new remote be called? origin
✓ Added remote https://github.com/hatappo/fork-test.git
? Would you like to push commits from the current branch to "origin"? Yes
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 231 bytes | 231.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/hatappo/fork-test.git
 * [new branch]      HEAD -> main
branch 'main' set up to track 'origin/main'.
✓ Pushed commits to https://github.com/hatappo/fork-test.git

# ブランチを作成
$ git checkout -B feature/fuga
Switched to a new branch 'feature/fuga'

# ファイルに追記
$ echo "\n\nfuga\nfuga" >> README.md

# コミット
$ git add --all && git commit --message "2nd commit"
[feature/fuga 75ddefa] 2nd commit
 1 file changed, 4 insertions(+)

# ブランチを GitHub に Push
$ git push --set-upstream origin feature/fuga
...
remote:
remote: Create a pull request for 'feature/fuga' on GitHub by visiting:
remote:      https://github.com/hatappo/fork-test/pull/new/feature/fuga
remote:
To https://github.com/hatappo/fork-test.git
 * [new branch]      feature/fuga -> feature/fuga
branch 'feature/fuga' set up to track 'origin/feature/fuga'.

そして GitHub 上で PR を作って、 feature/fuga を main にマージ。
出来上がった GitHub リポジトリ → https://github.com/hatappo/fork-test

Discussion