GitHubでFork/cloneしたリポジトリを本家リポジトリに追従する


- 2019/12/11
    - 分かりやすいサイトへのリンクを追加しました
    - hub コマンドの hub fork について追加しました
- 2013/04/11
    - 興味深い手法があれば随時追加していきます

---

ネットを検索すると、色々な手法が出てきますが、自分としては[「WEB+DB PRESS plus 開発ツール徹底攻略」](http://www.amazon.co.jp/dp/4774156167?tag=tetsujijp-22)p.71 に載っていた以下の手法がシンプルで良く理解できました。

# 本家リモート upstream を追加する方法

本家リポジトリの例として、実際にGitHubに存在する練習用リポジトリ `git@github.com:DQNEO/Renshu.git` を使います

あなた (*youraccount*) が既にForkしているRenshuリポジトリをcloneします。

```shell-session
$ git clone git@github.com:youraccount/Renshu.git
Cloning into 'Renshu'...
remote: Counting objects: 109, done.
remote: Compressing objects: 100% (70/70), done.
remote: Total 109 (delta 15), reused 109 (delta 15)
Receiving objects: 100% (109/109), 12.34 KiB, done.
Resolving deltas: 100% (15/15), done.    
$ cd Renshu
```

ブランチは以下のような状態になっているはずです (他にもあるかもしれません)。

```shell-session
$ git branch -a 
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
```

リモートリポジトリとして、オリジナルのリポジトリを **upstream** という名前で設定します。

```shell-session
$ git remote add upstream git://github.com/DQNEO/Renshu.git
```

以後、このリポジトリは **upstream** という名前で本家リポジトリを参照します。

ブランチを確認すると、以下のように remotes/upstream/master が加わったことが分かります。出てこない場合には `git fetch upstream` を試した後に再度 `git branch -a` で確認してみてください。

```shell-session
$ git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
  remotes/upstream/master
```

本家リポジトリの変更を取り出すためには **fetch** と **merge** の手順を取ります。

まずfetch

```shell-session
$ git fetch upstream
remote: Counting objects: 1, done.
remote: Total 1 (delta 0), reused 1 (delta 0)
Unpacking objects: 100% (1/1), done.
From git://github.com/DQNEO/Renshu
 * [new branch]      develop    -> upstream/develop
 * [new branch]      master     -> upstream/master
```


カレントブランチを master にして、upstream/master を merge。

```shell-session
$ git checkout master
Already on 'master'
Your branch is up to date with 'origin/master'.

$ git merge upstream/master
Updating fe579f8..a1e70ae
Fast-forward
```

変更が無い場合には "Already up-to-date." と表示されて終わりです。今回は差分があったので、コミットIDがfe579f8からa1e70aeまでの差分がmergeされました。

詳細は[当該書籍](http://www.amazon.co.jp/dp/4774156167?tag=tetsujijp-22)を参照ください。

# hub コマンドの hub fork

GitHub 独自の操作を行うことができる [hub コマンド](https://github.com/github/hub) があります。Qiita にも hub コマンドについて紹介した記事が多くありますので [hub タグのページ](https://qiita.com/tags/hub)などから探してみましょう。

hub コマンドは git のサブコマンドを指定された場合は git と同じ動作をする（git に処理を依頼する）ので、git の alias として hub を指定することで、あたかも git コマンドが GitHub 独自操作を行うサブコマンドを搭載したかのようになります。しかし、この記事では hub コマンド独自の操作は hub コマンドを明示的に書くことにします。また、hub コマンドで GitHub へのログインを求められた場合には、画面に表示された指示通りにログイン処理を行って下さい。

興味あるリポジトリを clone します。普段通り git clone してもいいですが、せっかく hub コマンドがあるので、拡張された hub clone サブコマンドで簡単に GitHub リポジトリを指定して clone してみましょう。

```shell-session
$ hub clone plack/Plack
Cloning into 'Plack'...
remote: Enumerating objects: 15, done.
remote: Counting objects: 100% (15/15), done.
remote: Compressing objects: 100% (15/15), done.
remote: Total 14715 (delta 6), reused 7 (delta 0), pack-reused 14700
Receiving objects: 100% (14715/14715), 5.17 MiB | 566.00 KiB/s, done.
Resolving deltas: 100% (8210/8210), done.
```

作成されたワーキングディレクトリに入ります。合わせて git remote -v で、origin が登録されていることを確認してみます。

```
$ cd Plack/

$ git remote -v
origin  git://github.com/plack/Plack.git (fetch)
origin  git://github.com/plack/Plack.git (push)
```

このディレクトリ内で、hub fork を打つことで GitHub 上で fork が行われます。

```shell-session
$ hub fork
Updating xtetsuji
From git://github.com/plack/Plack
 * [new branch]      builder-interface            -> xtetsuji/builder-interface
 * [new branch]      code-badges                  -> xtetsuji/code-badges
 * [new branch]      conditionalget-fix           -> xtetsuji/conditionalget-fix
(----------- 中略 -----------)
 * [new branch]      travis-devel                 -> xtetsuji/travis-devel
 * [new branch]      un-http-body                 -> xtetsuji/un-http-body
 * [new branch]      wide-strings-check           -> xtetsuji/wide-strings-check
new remote: xtetsuji
```

fork を行うだけでなく、このリポジトリに自分のユーザ名（以下の例では xtetsuji）の新たなリモートが登録されます。

```
$ git remote -v
origin  git://github.com/plack/Plack.git (fetch)
origin  git://github.com/plack/Plack.git (push)
xtetsuji        git@github.com:xtetsuji/Plack.git (fetch)
xtetsuji        git@github.com:xtetsuji/Plack.git (push)
```

今回はリモート origin が第三者のリポジトリとなったので、いつも通り `git fetch` と master がカレントブランチの状態と `git pull` をすれば良いことになります。

前出の upstream を追加する方法との違いをまとめてみましょう。

|  | upstream 手動追加 | hub fork |
|----|----|----|
| 必要なコマンド | git | git と hub |
| clone するもの | GitHub Web上で自分のアカウントに fork しておいたもの | 興味ある第三者のリポジトリ |
| fork | GitHub ウェブ画面などで事前に行っておく | hub fork で行う |
| リモート origin | fork した自分のリポジトリ | 第三者のリポジトリ (readonly) |
| リモート upstream | 手動で追加した第三者のリポジトリ (readonly) | - |
| リモート *youraccount* | - | hub fork が追加する |
| ブランチの push 先 | origin | *youraccount* | 
| 第三者のリポジトリに追従する merge の方向 | upstream/master → master | 普段の fetch と pull で OK|

# 分かりやすいサイト

「自分は今までの説明を読んで分かるけれど、初学者や後輩にどう教えれば…」という場合、このことを書いた分かりやすいサイトは他にもあるので探してみると良いでしょう。

特にマンガで解説した以下のサイトは初学者でも分かりやすそうです。

- [マンガでわかるGit 12話「本家リポジトリに追従する方法」 | リクナビNEXTジャーナル](https://next.rikunabi.com/journal/20180322_t12_iq/)