Gitでmasterブランチを作らずに作業をしてしまった時の対処

作業用ブランチの最も親にダミーのcommitを追加して、そこからmasterブランチを生やす力技で対処したよ!
2020.05.07

こんにちは、CX事業本部の若槻です。

今回は、Git(GitHub)を利用した開発で、リポジトリにmasterブランチを作らずに作業をしてしまい、Pull Requestが作れなくなってしまったので対処をした事例についてご紹介します。

前提(正規の開発フロー)

まず、今回の事例が発生した開発作業で"本来なぞるべきだった"開発フローについて前置きしておきます。

プロジェクトでGitHub上の既存リポジトリに対して作業を行う際は、以下のようなフローで開発作業を行っています。

  1. masterブランチから作業用ブランチをcheckoutする。
  2. 作業用ブランチで作業を行い、作業内容をcommitしてpushする。
  3. 作業用ブランチからmasterブランチへMergeするためのPull Requestを作成する。
  4. Pull Requestを他の開発メンバーがレビューする。指摘があれば修正を行う。
  5. Pull Requestが承認されたら、masterブランチへMergeする。

また、リポジトリを新規作成して作業を行う場合は、事前に以下の対応も必要となります。

  1. GitHub上にリポジトリを新規作成する。
  2. リポジトリにmasterブランチを作成する。

今回の事例は、リポジトリの新規作成から作業を行った際に発生した事例となります。

やってしまっとこと

GitHubで https://github.com/new から新規リポジトリ(hoge)を作ります。 image.png

リポジトリが作成できました。このリポジトリのURLを控えます。 image.png

控えたURLを使ってhogeリポジトリをローカルにcloneします。

$ git clone https://github.com/username/hoge.git
Cloning into 'hoge'...
warning: You appear to have cloned an empty repository.

cloneできたのでローカルリポジトリのディレクトリに入って、いつものように作業用ブランチ(feature-branch)を作りました。

$ cd ./hoge
$ git checkout -b feature-branch
Switched to a new branch 'feature-branch'

作業をしてコミットをいくつか作成したので、作業用ブランチをリモートにpushします。

$ git log --oneline
c7913b7 (HEAD -> feature-branch) なんか変更3
94c4c79 なんか変更2
fc8f596 なんか変更1

$ git push origin feature-branch
Counting objects: 8, done.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (8/8), 738 bytes | 369.00 KiB/s, done.
Total 8 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), done.
To https://github.com/username/hoge.git
 * [new branch]      feature-branch -> feature-branch

作業内容をすべてpushできたのでmasterブランチにmergeするためのPull Requestを作ろうとGitHubでリポジトリの画面を開いたら何か違和感を感じました。 image.png

hogeリポジトリのブランチ一覧を見るとmasterブランチがなく、作業用ブランチfeature-branchしか作成されていません。 image.png

「masterブランチをpushし忘れたからかな?」と思い、ローカルでブランチ一覧を見てみると、そもそもmasterブランチがローカル含めどこにも存在していません。

$ git branch -a
* feature-branch
  remotes/origin/feature-branch

ここでようやく「正規の開発フローを逸脱してmasterブランチを作り忘れていた」ことに気が付きました。

対処してみた

対処方針

さて、すっかり忘れていたmasterブランチをどうにかして作成しないといけないのですが、作る際の条件として、すでに作成されている作業用ブランチ上のいずれかのcommitを親として作る必要があります。なぜなら、同じcommitを親として持たないブランチの間でmergeを行うPull Requestは作成できないからです。

そこでまず考えられるのは、作業用ブランチの最も親のcommitfc8f596 なんか変更1(以下強調部分)をHEADとしてmasterブランチを作成することです。

$ git log --oneline
c7913b7 (HEAD -> feature-branch, origin/feature-branch) なんか変更3
94c4c79 なんか変更2
fc8f596 なんか変更1

しかしその場合の問題点として、fc8f596 なんか変更1での作業内容がすでにmasterブランチに存在することとなり、Pull Requestでのレビュー対象に含まれなくなります。理想としては、作業用ブランチでのすべての作業内容をレビュー対象としたいです。

よって今回は、作業用ブランチの最初のcommitの前にさらに親commitを1つ追加し、そのcommitをmasterブランチのHEADとする対処方針としました。

対処手順

ここで紹介する手順はリモートにpush済みの作業用ブランチの変更履歴をローカル側で変更する手順となります。チーム開発で同様の対処を行う際はデグレを引き起こす可能性があるため、他のメンバーが同じリポジトリに対して作業を行っていないことを十分に確認してから行いましょう。

git push --delete origin <ブランチ名>コマンドを実行し、GitHubにpush済みの作業用ブランチをリモートから削除します。

$ git push --delete origin feature-branch
To github.com:Account/hoge.git
 - [deleted]         feature-branch

作業用ブランチで適当なファイル(今回はREADME.md)を追加したダミーのcommitを作成します。

$ touch README.md
$ git add README.md 
$ git commit -m "add README.md"
[feature-branch bb9ace1] add README.md
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 README.md

コマンドgit rebase -i --rootを実行します。

$ git rebase -i --root

すると以下のように、rebase可能なcommitが記載されたファイルがエディターで開きます。 image.png

commitは親から順に並んでおり、ダミーとして作成したcommitは上から4番目にあります。

pick 6e3cb51 なんか変更1
pick 43a6a9b なんか変更2
pick f53eea3 なんか変更3
pick bb9ace1 add README.md

〜省略〜

これを編集して、以下のようにダミーのcommitが上から1番目になるように並び替えたら、ファイルを保存してエディターを閉じます。

pick bb9ace1 add README.md
pick 6e3cb51 なんか変更1
pick 43a6a9b なんか変更2
pick f53eea3 なんか変更3

〜省略〜

commitログを確認するとダミーのb5a5ae1 add README.mdのcommitが作業用ブランチの最も親のcommitとなっていることが確認できます。

$ git log --oneline
2ced002 (HEAD -> feature-branch) なんか変更3
6b6ef78 なんか変更2
a23bb92 なんか変更1
b5a5ae1 add README.md

rebaseにより履歴を編集した作業用ブランチをリモートにpushします。

$ git push origin feature-branch

git checkout <commit>コマンドを実行し、並び替えにより最も親のcommitとしたb5a5ae1をcheckoutします。

$ git checkout b5a5ae1
Note: checking out 'b5a5ae1'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

HEAD is now at b5a5ae1... add README.md

git checkout -b masterコマンドを実行し、commitb5a5ae1からmasterブランチをcheckoutにより作成します。

$ git checkout -b master
Switched to a new branch 'master'

両ブランチのコミットグラフは以下のようになりました。作業用ブランチで作成したダミーのcommitがmasterブランチのHEADとなっています。

$ git log --graph master feature-branch --oneline                                                                                                                                       
* 2ced002 (feature-branch) なんか変更3
* 6b6ef78 なんか変更2
* a23bb92 なんか変更1
* b5a5ae1 (HEAD -> master) add README.md

masterブランチをリモートにpushします。

$ git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 239 bytes | 239.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: 
remote: Create a pull request for 'master' on GitHub by visiting:
remote:      https://github.com/username/hoge/pull/new/master
remote: 
To https://github.com/username/hoge.git
 * [new branch]      master -> master

GitHubのコンソールで[Settings] - [Branches] - [Default branch]より、デフォルトブランチをmasterブランチに変更します。 image.png

作業用ブランチのPull Requestの作成を開始します。 image.png

作業用ブランチでの作業内容となる3つのcommitすべてをレビュー対象とするPull Requestをmasterブランチに対して作成できるようになりました。 image.png

補足(ダミーのcommitについて)

今回、masterブランチのHEADとするダミーのcommitとして、空のcommitではなく、適当なファイルを追加する変更内容のcommitを作成しましたが、その理由は「rebaseをすると空のcommitが消えてしまう動作となる」からです。

例えば、git commit --allow-emptyコマンドで空のcommitを作成します。commit logを見るとこの時点では空のcommitがちゃんと作成されていることが確認できます。

$ git commit --allow-empty -m "initial commit"
[feature-branch 72e085e] initial commit
$ git log --oneline
72e085e (HEAD -> feature-branch) initial commit
b8b5bd9 なんか変更3
ebeea18 なんか変更2
91c974a なんか変更1

しかし、rebaseしてcommit順を並び替えた後に再度commit logを確認すると、空のcommitが消えてしまっています。(空のcommitをブランチの最初の親とする意味がないからなのでしょうか?)

$ git rebase -i --root
Successfully rebased and updated refs/heads/feature-branch.
$ git log --oneline
f53eea3 (HEAD -> feature-branch) なんか変更3
43a6a9b なんか変更2
6e3cb51 なんか変更1

よって今回ダミーとして空のcommitではなく、適当なファイルを追加する変更内容のcommitをわざわざ作成する手順となりました。

おわりに

以上、Gitでmasterブランチを作らずに作業をしてしまうというやらかしと、その対処についてのご紹介でした。

今までの開発作業は既にmasterブランチが存在している作成済みのリポジトリに対する作業がほとんどでそれに慣れていたため、masterブランチの作成がすっかり抜け落ちていました。。。今後気を付けます。

ですがgitのrebaseがとても強力で便利なコマンドであることが知れたのは良かったです。

それでは。