お題
特定のリポジトリをforkしたり、Issueドリブンで開発をしたことがなかったので、練習してみる。
本編
登場人物の整理
①[リモート]fork元リポジトリ(sky14var)
②[リモート]自分のアカウントのリポジトリ(sky0621)
③[ローカル]実作業場所
上記それぞれに、ブランチという概念が入ってくるし、ローカルではステージングという概念も入ってくる。
■自分のアカウントでGitHubにログイン中
■fork元のリポジトリ
画面右上の「Fork」を押下すると。。。
■forkして自分のアカウントのリポジトリへ
■コンソールで自分のアカウントにforkしたリポジトリをクローン
$ git clone https://github.com/sky0621/study-001.git
Cloning into 'study-001'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
$
$ cd study-001/
$ ls -la
合計 16
drwxr-xr-x 3 koge koge 4096 8月 16 00:43 .
drwxr-xr-x 34 koge koge 4096 8月 16 00:43 ..
drwxr-xr-x 8 koge koge 4096 8月 16 00:43 .git
-rw-r--r-- 1 koge koge 11 8月 16 00:43 README.md
まだ何も修正していない、かつ、自分のアカウントのリポジトリ上も、fork元のリポジトリ上も修正が加わっていないので、すべて、同じコミット履歴のまま。
$ git log
commit 7d91ef51aa6eb11e7dee25bf31f719819bd4b367 (HEAD -> master, origin/master, origin/HEAD)
Author: sky14var <42414565+sky14var@users.noreply.github.com>
Date: Thu Aug 16 00:31:51 2018 +0900
Initial commit
■Fork元のリポジトリを自分のローカルに紐付ける
まず現状。自分のアカウントのリポジトリを git clone
したので紐付けは↓だけ。
デフォルトで git clone
元のリポジトリには origin
という名が付く。
$ git remote -v
origin https://github.com/sky0621/study-001.git (fetch)
origin https://github.com/sky0621/study-001.git (push)
トピックブランチをローカルに作って、最終的にはfork元のリポジトリにプルリク出したい。
ローカルでの作業の過程でfork元のリポジトリに別途加わった変更を取り込むため、fork元とも紐付ける。
(名前は(慣例?で) upstream
とする)
$ git remote add upstream https://github.com/sky14var/study-001.git
$ git remote -v
origin https://github.com/sky0621/study-001.git (fetch)
origin https://github.com/sky0621/study-001.git (push)
upstream https://github.com/sky14var/study-001.git (fetch)
upstream https://github.com/sky14var/study-001.git (push)
■Issueドリブンな開発を想定
Issue対応用のトピックブランチをローカルに作成
今は master
ブランチだけしかいない。
$ git branch
* master
対応するIssue番号を含む名前でブランチを新設
※今回は最初にfork元リポジトリをforkしてからトピックブランチ作成に至るまで間がなかったこともあり、
origin
の master
ブランチをベースにトピックブランチを作ったが、
本来であればプルリクした結果のマージ先であるfork元( upstream
と名付けた)リポジトリの master
ブランチをベースにすべきかも。
(どのみち、定期的にローカル、自分のアカウントのリポジトリ、fork元のリポジトリとの間で最新化をはかることになるので、結果的には同じと思うけど)
$ git checkout -b feature/issue#1 origin/master
Branch feature/issue#1 set up to track remote branch master from origin.
Switched to a new branch 'feature/issue#1'
$
$ git branch
* feature/issue#1
master
■Issueにならって、A, B, C, Dの4つのコミットを作る
まず1つ目。
$ echo A > a.txt
$
$ git status
ブランチ feature/issue#1
Your branch is up-to-date with 'origin/master'.
追跡されていないファイル:
(use "git add <file>..." to include in what will be committed)
a.txt
nothing added to commit but untracked files present (use "git add" to track)
$
$ git add a.txt
$
$ echo A >> README.md
$ cat README.md
# study-001A
$
$ git add README.md
$
$ git status
ブランチ feature/issue#1
Your branch is up-to-date with 'origin/master'.
コミット予定の変更点:
(use "git reset HEAD <file>..." to unstage)
modified: README.md
new file: a.txt
$
$ git commit -m "A"
[feature/issue#1 969aa79] A
2 files changed, 2 insertions(+), 1 deletion(-)
create mode 100644 a.txt
Aの分のコミットログが追加された。
$ git log
commit 969aa79a5b364481a5f729fc607c24d1f9dc910f (HEAD -> feature/issue#1)
Author: sky0621 <dummy@example.com>
Date: Thu Aug 16 02:09:09 2018 +0900
A
commit 7d91ef51aa6eb11e7dee25bf31f719819bd4b367 (origin/master, origin/HEAD, master)
Author: sky14var <42414565+sky14var@users.noreply.github.com>
Date: Thu Aug 16 00:31:51 2018 +0900
Initial commit
以降、B, C, Dについてそれぞれコミット。最終的にコミットログは、こうなる。
$ git log
commit 5b133ae98be634f62d45aa645fbb4e7fe78b73c4 (HEAD -> feature/issue#1)
Author: sky0621 <dummy@example.com>
Date: Thu Aug 16 02:14:15 2018 +0900
D
commit 92ae4384c135ada111e3bd70e26c428ca033012c
Author: sky0621 <dummy@example.com>
Date: Thu Aug 16 02:13:51 2018 +0900
C
commit 7eb5e72d67f3734c1d7dae8bbcaf4be85d0a896b
Author: sky0621 <dummy@example.com>
Date: Thu Aug 16 02:13:27 2018 +0900
B
commit 969aa79a5b364481a5f729fc607c24d1f9dc910f
Author: sky0621 <dummy@example.com>
Date: Thu Aug 16 02:09:09 2018 +0900
A
commit 7d91ef51aa6eb11e7dee25bf31f719819bd4b367 (origin/master, origin/HEAD, master)
Author: sky14var <42414565+sky14var@users.noreply.github.com>
Date: Thu Aug 16 00:31:51 2018 +0900
Initial commit
■ここまでトピックブランチにコミットした分(これはローカルにしか存在しない)を自分のアカウントのリポジトリに反映
$ git push -u origin feature/issue#1
Counting objects: 16, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (8/8), done.
Writing objects: 100% (16/16), 1.19 KiB | 610.00 KiB/s, done.
Total 16 (delta 0), reused 0 (delta 0)
To https://github.com/sky0621/study-001.git
* [new branch] feature/issue#1 -> feature/issue#1
Branch feature/issue#1 set up to track remote branch feature/issue#1 from origin.
$
$ git branch -a
* feature/issue#1
master
remotes/origin/HEAD -> origin/master
remotes/origin/feature/issue#1
remotes/origin/master
fork元へのプルリク前に、別筋からfork元にコミットが入ったケースを想定
これにより、fork元のコミットログは、以下の通り。
「Update README.md」
↑
「Initial Commit」
対して、fork先(=自分のアカウントのリポジトリ)のコミットログは、以下の通り。
「D」
↑
「C」
↑
「B」
↑
「A」
↑
「Initial Commit」
■fork元のコミットログの続きとしてトピックブランチのログが乗っかるようにログを改変
プルリクを出す上でのお作法というのかな。。。
まずはローカル上に存在するfork元のコミット履歴を記載した台帳を最新化
$ git fetch upstream master
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From https://github.com/sky14var/study-001
* branch master -> FETCH_HEAD
* [new branch] master -> upstream/master
これで、ローカル上でfork元のコミット履歴を参照すると、「Update README.md」のログが現れる。
$ git log upstream/master
commit 44ad95133671b42d364bf85f56755ec2c1cf29e5 (upstream/master)
Author: sky14var <42414565+sky14var@users.noreply.github.com>
Date: Thu Aug 16 02:36:32 2018 +0900
Update README.md
E
commit 7d91ef51aa6eb11e7dee25bf31f719819bd4b367 (origin/master, origin/HEAD, master)
Author: sky14var <42414565+sky14var@users.noreply.github.com>
Date: Thu Aug 16 00:31:51 2018 +0900
Initial commit
さて、いよいよトピックブランチのログを改変。
$ git branch
* feature/issue#1
master
$
$ git rebase upstream/master
First, rewinding head to replay your work on top of it...
Applying: A
Using index info to reconstruct a base tree...
M README.md
Falling back to patching base and 3-way merge...
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
error: Failed to merge in the changes.
Patch failed at 0001 A
The copy of the patch that failed is found in: .git/rebase-apply/patch
When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
はい。 README.md が競合しました。それはそうです。トピックブランチではABCDを書き込んで、fork元リポジトリではEを書き込んでいるので。
では、競合を解消します。
$ cat README.md
<<<<<<< HEAD
# study-001
E
=======
# study-001A
>>>>>>> A
fork元の「E」を活かしつつ、トピックブランチの「A」を入れることにします。
$ cat README.md
# study-001A
E
そして、競合発生時のコメントに書かれていた通り git rebase --continue
を実行。
$ git add README.md
$
$ git rebase --continue
Applying: A
Applying: B
Using index info to reconstruct a base tree...
M README.md
Falling back to patching base and 3-way merge...
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
error: Failed to merge in the changes.
Patch failed at 0002 B
The copy of the patch that failed is found in: .git/rebase-apply/patch
When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
はい、また競合。 これ、場合によってはトピックブランチのコミット数の分だけ発生するのでは???
複数人で、ある程度の期間の開発を並行していた場合、けっこうキツいのでは・・・。
こまめな git rebase
が大事ということかな。
$ cat README.md
# study-001A
E
B
$
$ git add README.md
$ git rebase --continue
Applying: B
Applying: C
Using index info to reconstruct a base tree...
M README.md
Falling back to patching base and 3-way merge...
Auto-merging README.md
Applying: D
Using index info to reconstruct a base tree...
M README.md
Falling back to patching base and 3-way merge...
Auto-merging README.md
おっ、残りのコミット分は競合せず、すんなりいけた様子。
$ git status
ブランチ feature/issue#1
Your branch and 'origin/feature/issue#1' have diverged,
and have 5 and 4 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
nothing to commit, working tree clean
$
$ git log
commit df6453f5eeb29f8f27475e4862e9b87b15196652 (HEAD -> feature/issue#1)
Author: sky0621 <dummy@example.com>
Date: Thu Aug 16 02:14:15 2018 +0900
D
commit 69c4b87ed13623c8376a35ed1b3f1ac52b846453
Author: sky0621 <dummy@example.com>
Date: Thu Aug 16 02:13:51 2018 +0900
C
commit cd2a9b20cedfa2c7e8388b5284a7abc755f6280c
Author: sky0621 <dummy@example.com>
Date: Thu Aug 16 02:13:27 2018 +0900
B
commit f2f5f851c3ed4329f5f74e6f420f6e59266f6bf4
Author: sky0621 <dummy@example.com>
Date: Thu Aug 16 02:09:09 2018 +0900
A
commit 44ad95133671b42d364bf85f56755ec2c1cf29e5 (upstream/master)
Author: sky14var <42414565+sky14var@users.noreply.github.com>
Date: Thu Aug 16 02:36:32 2018 +0900
Update README.md
E
commit 7d91ef51aa6eb11e7dee25bf31f719819bd4b367 (origin/master, origin/HEAD, master)
Author: sky14var <42414565+sky14var@users.noreply.github.com>
Date: Thu Aug 16 00:31:51 2018 +0900
Initial commit
■改変したログを自分のアカウントのリポジトリに反映
$ git push -u origin
To https://github.com/sky0621/study-001.git
! [rejected] feature/issue#1 -> feature/issue#1 (non-fast-forward)
error: failed to push some refs to 'https://github.com/sky0621/study-001.git'
ヒント: Updates were rejected because the tip of your current branch is behind
ヒント: its remote counterpart. Integrate the remote changes (e.g.
ヒント: 'git pull ...') before pushing again.
ヒント: See the 'Note about fast-forwards' in 'git push --help' for details.
はい、怒られた。 git rebase
でfork元のコミット履歴「E」を取り込んだ結果、自分のアカウントのリポジトリ上の履歴と食い違いが。
とはいえ、それを前提でログ改変したので、この場合は強制的にプッシュしてしまうらしい。
$ git push -f -u origin
Counting objects: 19, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (9/9), done.
Writing objects: 100% (19/19), 1.83 KiB | 936.00 KiB/s, done.
Total 19 (delta 0), reused 0 (delta 0)
To https://github.com/sky0621/study-001.git
+ 5b133ae...df6453f feature/issue#1 -> feature/issue#1 (forced update)
Branch feature/issue#1 set up to track remote branch feature/issue#1 from origin.
はい、今度はOK。 GitHub上でもコミット履歴を確認してみると、「Update README.md」のログが間に挟まっている。
fork元が↓なので、続きとしてトピックブランチのログが乗っかるようにプルリクが出せる。
■トピックブランチのログをまとめる
さて、もう1つ、お作法的なものが。 トピックブランチでは、複数回に分けてコミットログを作っていたけど、
fork元からすると、一連のコミットログは1つのIssueへの対応分であるので、まとめて1つのコミットログとした方が後から見てわかりやすい。
はい、なので、コミットログをまとめます。
git rebase
により、fork元の最新コミットに続けてトピックブランチのコミット(「A」の書き込み分から)が乗っかっているので、「A」のログ以降をまとめてみる。
$ git log --oneline
df6453f (HEAD -> feature/issue#1, origin/feature/issue#1) D
69c4b87 C
cd2a9b2 B
f2f5f85 A
44ad951 (upstream/master) Update README.md
7d91ef5 (origin/master, origin/HEAD, master) Initial commit
$ git rebase -i f2f5f851c3ed4329f5f74e6f420f6e59266f6bf4
エディタが起動する。
pick cd2a9b2 B
pick 69c4b87 C
pick df6453f D
# Rebase f2f5f85..df6453f onto f2f5f85 (3 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
コミット「B」「C」「D」をひとまとめにしてコミットメッセージも書き換えるので、それぞれ「pick」→「squash」に変更して保存。
squash cd2a9b2 B
squash 69c4b87 C
squash df6453f D
# Rebase f2f5f85..df6453f onto f2f5f85 (3 commands)
#
<省略>
コミットメッセージ書き換えのためにエディタが再起動するので、コミットメッセージを以下のように書き換える。
# This is a combination of 4 commits.
# This is the 1st commit message:
issue#1 A, B, C, D
# This is the commit message #2:
# This is the commit message #3:
# This is the commit message #4:
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date: Thu Aug 16 02:09:09 2018 +0900
#
# interactive rebase in progress; onto f2f5f85
# Last commands done (3 commands done):
# squash 69c4b87 C
# squash df6453f D
# No commands remaining.
# You are currently rebasing branch 'feature/issue#1' on 'f2f5f85'.
#
# コミット予定の変更点:
# modified: README.md
# new file: a.txt
# new file: b.txt
# new file: c.txt
# new file: d.txt
#
上の内容で保存すると、以下のように「Successfully rebased and updated」と出力され。
[detached HEAD bcc0fcc] issue#1 A, B, C, D
Date: Thu Aug 16 02:09:09 2018 +0900
5 files changed, 8 insertions(+), 1 deletion(-)
create mode 100644 a.txt
create mode 100644 b.txt
create mode 100644 c.txt
create mode 100644 d.txt
Successfully rebased and updated refs/heads/feature/issue#1.
git log
するとログがひとまとめになっていることがわかる。
$ git log
commit bcc0fcc2231904c2d8434c1e4b8799b5c878dc45 (HEAD -> feature/issue#1)
Author: sky0621 <dummy@example.com>
Date: Thu Aug 16 02:09:09 2018 +0900
issue#1 A, B, C, D
commit 44ad95133671b42d364bf85f56755ec2c1cf29e5 (upstream/master)
Author: sky14var <42414565+sky14var@users.noreply.github.com>
Date: Thu Aug 16 02:36:32 2018 +0900
Update README.md
E
commit 7d91ef51aa6eb11e7dee25bf31f719819bd4b367 (origin/master, origin/HEAD, master)
Author: sky14var <42414565+sky14var@users.noreply.github.com>
Date: Thu Aug 16 00:31:51 2018 +0900
Initial commit
■改変したコミット履歴を自分のアカウントのリポジトリに反映
$ git push -u origin
To https://github.com/sky0621/study-001.git
! [rejected] feature/issue#1 -> feature/issue#1 (non-fast-forward)
error: failed to push some refs to 'https://github.com/sky0621/study-001.git'
ヒント: Updates were rejected because the tip of your current branch is behind
ヒント: its remote counterpart. Integrate the remote changes (e.g.
ヒント: 'git pull ...') before pushing again.
ヒント: See the 'Note about fast-forwards' in 'git push --help' for details.
はい、怒られた。(2回目)
forceが必要です。
$ git push -f -u origin
Counting objects: 7, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (7/7), 430 bytes | 430.00 KiB/s, done.
Total 7 (delta 0), reused 0 (delta 0)
To https://github.com/sky0621/study-001.git
+ df6453f...bcc0fcc feature/issue#1 -> feature/issue#1 (forced update)
Branch feature/issue#1 set up to track remote branch feature/issue#1 from origin.
自分のアカウントのリポジトリにも反映されました。
$ git log origin/feature/issue#1
commit bcc0fcc2231904c2d8434c1e4b8799b5c878dc45 (HEAD -> feature/issue#1, origin/feature/issue#1)
Author: sky0621 <dummy@example.com>
Date: Thu Aug 16 02:09:09 2018 +0900
issue#1 A, B, C, D
commit 44ad95133671b42d364bf85f56755ec2c1cf29e5 (upstream/master)
Author: sky14var <42414565+sky14var@users.noreply.github.com>
Date: Thu Aug 16 02:36:32 2018 +0900
Update README.md
E
commit 7d91ef51aa6eb11e7dee25bf31f719819bd4b367 (origin/master, origin/HEAD, master)
Author: sky14var <42414565+sky14var@users.noreply.github.com>
Date: Thu Aug 16 00:31:51 2018 +0900
Initial commit
■プルリクエストを出す
はい、上記の通り、行儀よくプルリク出せるようになったので、いざ、プルリク作成。
「Create pull request」ボタン押下すると、
■プルリクをマージ
マージされた。
■マージ後のログを確認
あれ、マージしたログが出てしまうのは、しょうがないのか?
まとめ
ひとまず、表題にあげたことはできた。
はじめてやったからか、なんだか神経使った・・・。
マージログは、もう1回 rebase(-iでない方)が必要なのかな。