hiyoko-programingの日記

プログラミングを勉強したてのひよっ子。   エンジニア目指して勉強中。

GitやGitHubを用いる上でのトラブルシューティング

ブランチを作成せず、masterブランチ上でコードを書いてしまった

新たにブランチを作成し、これまで書いたコードを新しいブランチに移す。
コードを書いている途中にブランチを作成する際は、書いているコードを作成したブランチに引き継ぐか、0からコードを書き直すかを選択できる。

 

以下は、masterブランチから新ブランチを作成しようとしている。この時、masterブランチ上でコードを編集しているとする。

https://tech-master.s3.amazonaws.com/uploads/curriculums//21e7d385d075ab5da29cb826da8df4b4.png

ブランチを作成。

https://tech-master.s3.amazonaws.com/uploads/curriculums//51f969b64f2f8cedb1d5c037f61fd189.png

https://tech-master.s3.amazonaws.com/uploads/curriculums//956dcf1304bf3da1e283cd198874acc2.png

すると、以下のような選択肢が出てくる。

https://tech-master.s3.amazonaws.com/uploads/curriculums//121569236cbc302b8e1e3767ef109e0a.png

選択肢について。

Leave my changes on master

元のブランチで書いていたコードを全て捨て、新しいブランチで0からコードを書く選択。

Bring my changes to (新しいブランチ名)

元のブランチで書いていたコードを新しいブランチに引き継ぐ選択です。

もしブランチを作成することを忘れてコードを書き進めてしまった場合、ブランチを作成したらBring my changes to (新しいブランチ名)を選択するようにする。

間違えてLeave my changes on masterを押してしまった場合は、以下の手順で捨てたコードを復旧できる。

まず、一度元のブランチに戻る。

https://tech-master.s3.amazonaws.com/uploads/curriculums//93aa2ee452af21b76332b5342b85dc4b.png

すると、左下にStashed Changesという記述が増えている。ここをクリック。

https://tech-master.s3.amazonaws.com/uploads/curriculums//f449e5bb61dcfbc63aeaa29f815af3cd.png

ここで、上部にあるRestoreというボタンを押すと、コードを復旧することができる。
再度ブランチを作成し直し、選択肢を間違えずにBring my changes to (新しいブランチ名)を選択するようにする。

同じファイルを別の人が既に修正していてマージができない

ブランチをマージしようとしたら、自分がファイルを編集している間に、他の人が同じファイルを編集してmasterブランチに反映していた場合。

変更修正を上書きしてしまうことになり、マージができない。このような事象をコンフリクト(競合)と言う。

コンフリクトは開発を進める上で、常に直面する問題である。

コンフリクトについて

コンフリクトとは、あるファイルにおいてブランチごとに情報が異なり辻褄が合わない状況のことを言う。例えば、トピックブランチをマージしようとする際に、masterブランチとファイル内の情報が合致しない場合である。

以下のような状況を想定してみる。

チーム開発をしているAさんとBさんは、互いに作業内容の確認がとれておらず、二人とも同一のファイルに対して作業をしてしまった。これがコンフリクトである。

https://tech-master.s3.amazonaws.com/uploads/curriculums//6611fb244896f92a1779a8de7ed93549.png

コンフリクトが生じていると、Bさんはマージをしようとしてもできない。コンフリクトを解消した上で、マージをする必要がある。

コンフリクトを解消する

先ほどまで使用していたgit-appを用いる。

Aさんにおける作業を実施

まず、Aさんが作業するブランチを作成する。conflict-Aというブランチとする。

https://tech-master.s3.amazonaws.com/uploads/curriculums//38b95d2a15f1a3a877c1c94e8b9f655b.png

article.rbに以下のようなバリデーションを付与する。

app/models/article.rb
1
2
3
class Article < ApplicationRecord
  validates :title, presence: true
end

以下の様にコミット、プッシュしておく。

https://tech-master.s3.amazonaws.com/uploads/curriculums//f340b23af7c1fc2e7409c9f4bded6586.png

⌘+Rでプルリクエストを作成。

https://tech-master.s3.amazonaws.com/uploads/curriculums//505f28586c2a2bc5d54ae07d60a896d7.png

今回はコンフリクトの練習なので、Why/Whatは記述必要ない。

https://tech-master.s3.amazonaws.com/uploads/curriculums//35744d306085d78f8be5fbcb19aaa13c.png

現時点では下図の作業が完了している。

https://tech-master.s3.amazonaws.com/uploads/curriculums//8083405b5f298b63245f80c535cc8bb8.png

Bさんにおける作業

では次にBさん側の作業をしていく。先程と同様にconflict-Bというブランチをmasterから作成。

https://tech-master.s3.amazonaws.com/uploads/curriculums//e13be245040003c35bead6b60931c895.png

article.rbに以下のようなバリデーションを付与する。バリデーションの意図はAさんと同じですが、記述方法が異なる。

app/models/article.rb
1
2
3
class Article < ApplicationRecord
  validates_presence_of(:title)
end

先程と同様にコミット、プッシュして、プルリクエストを作成する。
現時点では下図の作業が完了している。

https://tech-master.s3.amazonaws.com/uploads/curriculums//d05895c03d5342c2bb1343364f459aa6.png

Aさんのプルリクエストをmasterにマージする

下図で示した部分の作業を実施。

https://tech-master.s3.amazonaws.com/uploads/curriculums//6c54fac91c81f69e3649ccdced7cf7b2.png

通常どおり、プルリクエストをマージ。

https://tech-master.s3.amazonaws.com/uploads/curriculums//f146438119d44b63a1c000c1171e9db9.png

またその後、GitHub Desktopにおけるmasterブランチで、忘れずにプルする。

Bさんのプルリクエストを確認

下図で示した部分の作業をしたい。

https://tech-master.s3.amazonaws.com/uploads/curriculums//8b6af6bde7f0be5ed24965aec5057a81.png

しかし、上記のAさんのプルリクエストをマージした段階で、Bさんのブランチにおけるarticle.rbの変更修正の記述と、masterブランチにおけるAさんが行ったarticle.rbの変更修正記述が被っている。このまま無理やりマージしたとしたら、Bさんの変更修正のみ反映され、Aさんの記述は上書きされて消えてしまう。

しかしGitHubはコンフリクトが生じた際は丁寧に教えてくれる。

Bさんのプルリクエストを確認してみると以下のように、コンフリクトのためにマージできない旨の表示がされている。

https://tech-master.s3.amazonaws.com/uploads/curriculums//53c4573cf3e7752a5fb3c07e5101462a.png

ではこのコンフリクトを解決していく。

コンフリクトを解決

コンフリクトを解決する手順は以下。

  1. コンフリクト箇所をconflict-Bのブランチに反映
  2. コンフリクト箇所を修正
  3. コンフリクト解決をコミット
  4. conflict-Bのブランチをマージ

①. コンフリクト箇所をconflict-Bのブランチに反映
GitHub Desktop conflict-Bのブランチに戻る。
GitHub Desktopで[History]タブの「Select Branch to Compare...」というプレースホルダーが書かれたボックスをクリックすると、現在のブランチとそれ以外のブランチの比較を確認することができる。

https://tech-master.s3.amazonaws.com/uploads/curriculums//142f08d1f663e501a9a9cfc515a0cfea.png

ここで、表示されているmasterブランチをクリックすると、 具体的にどのように異なるのかが確認できる。

https://tech-master.s3.amazonaws.com/uploads/curriculums//ee64af5986d1eeca77b0d7c3914d642c.png

ここのBehindAheadは、Gitにおいて大切な考え方になる。

Behindは、現在のブランチよりも比較するブランチが進んでいる変更履歴を意味し、 Aheadは、現在のブランチが進めた変更履歴を意味する。

現在それぞれのタブでは、 Behindには、conflict-Aの変更と、それをmasterにマージした変更履歴、Aheadには、conflict-Bで編集した変更履歴がそれぞれコミット単位で確認できる。

現在このBehindとAheadに同じファイル編集のコミットが存在しているため、 画面左下に「There will be 1confilicted file when merging master into conflict-B 」という表示がある。これは、もしmasterブランチをconflict-Bブランチにマージした時は、コンフリクトが発生するファイルが1つあります。という意味。

[Merge into conflict-B]というボタンをクリックし、実際にマージして競合を解決していく。

https://tech-master.s3.amazonaws.com/uploads/curriculums//f7da654bc04d52b3f269443bd6279cd2.png

このマージによってBehindで表示されていた分のコミットがconflict-Bに追加され、Aheadにあるarticle.rbと競合が発生する。GitHub Desktopでは以下のような画面が表示されるはず。下図の通りにクリックすると、テキストエディタ自動起動しarticle.rbを開く(画像ではAtomになっているが、実際の環境ではVisual Studio Codeになっているはず)。

https://tech-master.s3.amazonaws.com/uploads/curriculums//2e5506a9f39808550e3e6f841a89eeb0.png

テキストエディタで開かれたarticle.rbは、以下のようになっている。

app/models/article.rb
1
2
3
4
5
6
7
class Article < ApplicationRecord
<<<<<<< HEAD
  validates_presence_of(:title)
=======
  validates :title, presence: true
>>>>>>> master
end

<<<<<<< HEAD>>>>>>> masterという記述が、=======で区切られて追記されている。HEADとは現在のコミットを指していまる。つまり、conflict-Bのブランチで追記した内容。masterというのはリモートリポジトリのmasterブランチを指している。=======で区切られた上と下の記述が競合しているという意味になる。

②コンフリクト箇所を修正
競合を解決するために競合箇所を修正する。競合の解決方法は以下の2つ。

  • 不要な方の記述を削除する
  • 両方とも残す

どちらの方法を選択するかは、状況によって異なる。該当箇所を編集した人に確認するなどして、適切な方法で競合を解決する。今回はBさんの修正を削除したい。なぜならば、Bさんのバリデーションの記述は、古いRailsのバージョンのみに対応したものだからである。

以下のようにarticle.rbを編集し、保存する(残したい箇所以外を消去)。

app/models/article.rb
1
2
3
class Article < ApplicationRecord
  validates :title, presence: true
end

 競合解決の際に、<<<<<<< HEAD>>>>>>> master=======の記述は必ず消す。

③コンフリクト解決をコミット
競合を解決できたので、マージできた結果をコミットする。GitHub Desktopを確認すると先程とは表示が変わり、競合が解決できていることと、[Commit merge]というボタンがクリックできる状態になっていることが分かる。

https://tech-master.s3.amazonaws.com/uploads/curriculums//d7362cd015302fddfb5249347ea51c84.png

そのまま[Commit merge]ボタンをクリックしコミットする。 競合解決の場合は自動的にコミットされる 。忘れずにプッシュしてリモートリポジトリに競合解決をした変更を反映させる。

④conflict-Bのブランチをマージ
プッシュした後に、GitHubでプルリクエストを確認。競合を解決したのでマージできるようになっている。そのまま、conflict-Bをマージする。

https://tech-master.s3.amazonaws.com/uploads/curriculums//4dacb2be34ca3484c665b44b1c0cb6a9.png

マージしたら、ローカルリポジトリのmasterブランチに忘れずにプルしておく。以上でコンフリクトの解決は終了。

作業するブランチを誤ってしまった

「ブランチを切り替え忘れて作業してしまった!」という事態は誰しもが通る道である。今回もgit-appを用いる。

ブランチを作成し忘れて作業してしまった時の対処法

 「edit-readmeブランチで作業すべきだったのに、そのブランチを作成し忘れて、masterで作業してしまった」 というシチュエーションを想定する。ブランチを間違えてしまうと一時は絶望的な気持ちになるかもしれないが、実はトラブルシューティングは非常に簡単。GitHub Desktopを用いた対処法を覚えておく。

変更修正を本来あるべきブランチに移動

まず、masterにいることを確認して、以下のようにREADME.mdを修正。

README.md
1
2
# README
## how to use GitHub Desktop

GitHub Desktopで確認。masterブランチに変更修正が記載されてしまっている。

https://tech-master.s3.amazonaws.com/uploads/curriculums//9b70ca126d09b10654b6a981b8e1bc2f.png

本当はこの変更修正はedit-readmeブランチで実施したかったもの。

そこで以下のように無理やり、新しいブランチの作成を試みる。

https://tech-master.s3.amazonaws.com/uploads/curriculums//5ebd7a696d133ad626ee72ff4db0e818.png

edit-readmeというブランチを作成。

https://tech-master.s3.amazonaws.com/uploads/curriculums//6b1e183e90759709cf9c5dcf3bd5224b.png

すると、2つの選択肢を求められる。

[上側] 今ある変更修正を一度放置して、ブランチを移動する
[下側] 今ある変更修正を携えて、ブランチを移動する

今回は下側を選択。

https://tech-master.s3.amazonaws.com/uploads/curriculums//2436db4658ccdc2a4f241cab571692c5.png

すると、先程の修正変更ごと新しいブランチへ移動できている。簡単!

新しいブランチに移動させない変更修正はコミット

「この変更修正はこのブランチで、一方この変更修正は新しいブランチで」といった場合には、前者はコミットしておく。ブランチを移動してもコミット済みの変更修正は移動しない

コミット済みの変更修正を移動したい場合はundoする

コミット済みの変更修正を別ブランチに移動したい場合は、undo ボタンをクリックする。すると、コミット前のインデックスに登録されただけの段階まで戻すことができる。この状態でブランチを移動すれば、変更修正も一緒に移動できる。

https://tech-master.s3.amazonaws.com/uploads/curriculums//ea50acecca1bbb8f5fe35555e4142ab0.png

誤った情報をプッシュしてしまった

gitにおいて、プッシュした情報をローカルリポジトリに戻すことはできない。したがって、プッシュする前に作業をしているブランチは正しいのかどうか、必ず確認するようにする。

それでも間違ってプッシュしてしまうことがある。ローカルリポジトリにその情報は戻せないものの、リモートリポジトリにある誤ったコミット情報は打ち消す必要がある。なぜならば、十分に内容が精査されていない情報がデプロイされてしまい、本番環境で思わぬバグを引き起こす可能性があるから。

そんな時に有用なのが、git revert。

git revertについて

git revert

コミットを打ち消すコミットを生成する操作が行なえる。プッシュ済みのコミットを消す方法もあるが、git revertでは誤りであるコミットを打ち消したコミットの記録が残る。

git revertを実行

まずはmasterブランチで以下のような修正をする。

README.md
1
2
3
# README
## how to use GitHub Desktop
## how to use git revert

コミットしてプッシュする。するとリモートリポジトリでmasterにコミットが記録されてしまっている。masterのREADME.mdを確認すると先程の追加が反映されてしまっている。

https://tech-master.s3.amazonaws.com/uploads/curriculums//4464bfecf3fdcb72473b331b88369d9c.png
https://tech-master.s3.amazonaws.com/uploads/curriculums//666ad375274b64eef32970db40f5c81d.png

先程のコミットを打ち消すには、コミットIDが必要となるため、git logコマンドでコミットの履歴を確認。

ターミナル
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
$ git log # logの結果は各環境で異なります。

commit 4c695e4565c5718558c260c20d7e6eb57f3ee6c1 (HEAD -> master, origin/master)
Author: user <user@user.com>
Date: Wed Jun 26 10:44:26 2019 +0900
    Update README.md

commit 0cb4947de8ef140a34d1d7bbdcc0b5afc7956acd (stash-test)
Author: user <user@user.com>
Date: Wed Jun 26 10:04:51 2019 +0900
    Update README.md

(以下省略)

最上部のlogが最新のコミットを表す。対象となるコミットは4c695e4565c5718558c260c20d7e6eb57f3ee6c1となる。

以下のようにgit revertコマンドを実行。

ターミナル
1
$ git revert 4c695e4565c5718558c260c20d7e6eb57f3ee6c1

するとターミナル画面が編集できなくなる表示になるが、:qと入力すると脱出できる。
GitHub Desktopを確認。

https://tech-master.s3.amazonaws.com/uploads/curriculums//c2b03aa8ddf0cbf4ae50157adb4f17c0.png

git revertで生成したコミットが確認できる。こちらをpushする。するとリモートリポジトリでは以下のように先程の変更修正が打ち消されていることが確認できる。

https://tech-master.s3.amazonaws.com/uploads/curriculums//391b350cc6a98ef93737d7200ba150df.png

これがrevertコマンドの基本的な使い方である。

git revert後にやること

git revert実行後、revertしたコミットの内容はローカルからも消えている。つまり、同じような記述を再度commit&pushしたい場合は、もう一度最初から当該コミットのコードを書きなおす必要がある。push前の状態に戻されるわけではない。ゆえに、リモートへのpushは慎重に行う。

GitHubトラブルシューティングの際の心得

GitHubには上記で紹介した以外にも、様々なコマンド、様々なオプションが存在する。何か操作に誤りがあった場合に、パニックになって様々なコマンドを試してしまうことがあるが、場合によっては取り返しのつかない状況になる(貴重なコードが消えるなど)こともあるので、十分注意する。一方で、トラブルシューティングを経験することで、GitやGitHubの概念を早く身につけることが可能である。もし何か試してみたいことがあった場合は、git-appのようにミニアプリを用意して試してみるのが良い。