QA@IT

Gitでbranchを変えるときに、ファイルの変更はどう扱えばいいのか?

15507 PV

現在、Gitを使って開発しているのですが、git checkoutするときに変更した物をどうすれば(どう扱えばいいのか?)わからなくなったので、質問させていただきました。

具体的な状況を説明します。

最初はmasterブランチのみで作業(以後A作業)をしていました。
しかし、平行してやりたい作業(以後B作業)が出てきました。そのB作業は今やっているA作業とは関連性は薄い作業でした。
また、B作業は完成まである程度時間がかかる作業でしたので、サーバに反映されるmasterブランチに一緒にしたくはありません。
そこでB作業の為に、ブランチをきる事にしました(以後Bブランチ)。

そしてgit checkout Bとして、Bブランチで作業していたのですが、A作業の方を進めたくなりました。
そこでmasterブランチの方に戻ろうとしたのですが、このBブランチで今までやっていた作業は、どうすればいいかわからなくなりました。

何がわからないかというと、
今まではブランチを切り替える度にgit commit -m "ブランチを切り替える為にコミット"というような感じで、コミットしてからブランチを変えていました。
しかしそもそも、ブランチを切り替える毎にコミットするのに違和感を覚えたからです。

また試しに、コミットせずにgit checkout masterとすると、Bブランチでやったファイルの変更がmasterブランチに戻っても、変更されたままでした。(私見ですが、私はこの動作は少し以外でした。ブランチを切れば、ブランチ同士はmargeしない限り、干渉しないと思っていたからです。)

ここで改めて質問なんですが、このような「今までやっていた作業」はどうすればよいのでしょうか?
ご回答お待ちしております。

回答

今回の問題を解決するために、私がよく用いる方法を書きます。
大きく2種類あります。

  1. とりあえず commit する。
  2. stash で取っておく。

私のオススメは 1. です。

1. とりあえず commit する。

とりあえずBブランチに commit しましょう。
そして master の作業が終わったらBブランチに戻ってきて作業しましょう。
ここで「ブランチを切り替える毎にコミットするのに違和感を覚えた」と仰っている理由は、開発に関係ないログは残したくない、ということではないでしょうか。
ログを見た時に「ブランチを切り替える為にコミット」とかイヤですよね。

もちろん git は、こういう場合の解決策を持っています。
これも大きく3種類あります。

  • A. git commit --amend
  • B. git reset; git commit
  • C. git rebase -i

今回は A の方だけ説明します。B、C は調べてみて下さい。
(特に、rebase -i は git らしい変態的なコマンドです。合わせて git の内部構造も勉強してみると面白いです)

master からBブランチに戻ってきて、作業した結果を commit する時、
$ git commit
ではなく
$ git commit --amend
を実行してください。

これは、commit をやり直すコマンドです。
直前の commit と今回の commit を1つにまとめることができます。

今回のようなケース以外でも、「commit 後に軽微なミスを発見したので修正したい」ときや、「commit 時のコメントを修正したい」ときにも使えます。

2. stash で取っておく。

現在の状態を commit せずに一旦取っておく事ができます。

$ git stash save (save は省略可能)
$ git stash pop

例として、modified_file.txt が更新された状態とします。

$ git status
# On branch b
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   modified_file.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

ここで git stash save をすると、変更ファイルを作業領域に保存して、HEAD の状態に戻します。
git status すると変更がないことを確認できます。

$ git stash save
Saved working directory and index state WIP on b: 09d117b Previous commit log
HEAD is now at 09d117b Previous commit log

$ git status
# On branch b
nothing to commit, working directory clean

保存されている情報は git stash list で確認できます。

$ git stash list
stash@{0}: WIP on b: 09d117b Previous commit log

master で作業してBブランチに戻ったら、git stash pop で保存した状態を復元できます。

注意点

ここで、中止しないといけないのは、 git stash pop はBブランチ以外でも可能 なことです。

間違って master ブランチで pop すると、Bブランチの作業中の状態が復元されます。
master とBブランチの内容が乖離していた場合、pop するときに CONFLICT が発生するので気づくかもしれませんが、そうでない場合、pop が成功します。
(というか、まさにそういうことがしたいときに使うコマンドです。誤って別のブランチで作業していたときに、本来のブランチに移す目的で使われます)

それに気づかず作業すると、Bブランチで行うはずの作業を master で行うことになります。
まぁ、その場合はまた git stash save してBブランチで git stash pop すれば良いのですが。
今回の用途には少し複雑な機能かもしれません。


git はとにかく機能が多く、コマンド名から機能が分かりにくいものもあるので、批判の対象になったりしますが、それらの機能の多くは開発者のミスを取り消してくれるものです。
思想・仕組みを理解するとこれほど手に馴染む道具はないと思うので、少しづつ覚えていってください。

編集 履歴 (1)
  • amendを使う場合は、引数なしpushなどでamend予定のブランチをリモートに送らない様に注意が必要ですね。 -
  • > flied_onion さん
    フォローありがとです。

    たしかに、git にはログを書き換えるコマンドが多数ありますが(1.で紹介したA~Cもそれに該当します)、それを使うのはローカルブランチのみに限定したほうが良いです。

    リモートに push 済みのログを書き換えて、更にそれを push してしまうと、それはもう訳分からなくなります。
    -
  • お返事ありがとうございます。
    うーん、なるほど。とりあえずコミットしてから、修正するか。一時的にstashで保存しておくかという2つがあるんですね。
    stashが手軽そうにも思えましたが、masterでstash、Bでもstashとなるとめんどそうに思えたので、コミットする方向でいこうと思います。ありがとうございました。
    -

普段Mercurialしか使ってないのでgitはあまり詳しくないですが、

とりあえずコミットコメントは変更内容を記述しましょう。なぜコミットしたかは後で見ても役に立たない情報ですので残しておく意味がありません(ホントはもっとちゃんと残している場合はごめんなさい)。

git checkout ブランチ名
の時は問題がなければコミット先ブランチを切り替えるだけですね。
単純に切り替えられない場合(作業ツリーの変更が切り替え先と競合する場合)はエラーで「ブランチをスイッチできない」と言われた気がします。

コミットはしたくないけれど今やっている作業はとっておきたいときは、Mercurialの場合だとShelveを使うんですが、gitだと stash がそれにあたるんですかね。
棚のようなところに置いておく感じになります。
今やっている修正を保存しておいて、作業中のファイルは変更前に戻します。他の作業が一通り終わったら戻ってきて保存しておいた修正を適用します。

ただまぁ、キリのいいところなら修正内容を覚えているうちにコミットしてしまうのも手です。

あとはやや力技なやり方になりますが他のディレクトリにクローンしてそっちでmasterの編集をしてクローン元へpushするという手もありますね。

編集 履歴 (1)
  • お返事ありがとうございます。stashも便利かと思いましたが、複数のブランチで複数のstashがあるのも大変そうだと感じたので、コミットして修正する形にしようと思います。ありがとうございました。 -
ウォッチ

この質問への回答やコメントをメールでお知らせします。