git pull実行時にローカルで未コミットの変更を保持する方法

git pullを実行する際に、ローカルで未コミットの変更に対して、リモートの状態で上書きせず、ローカルでの変更を保持したい場合や、やっぱりリモートの変更で上書きしたくなった場合の対処方法についてのご紹介です。
2020.01.14

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

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

git pullを実行する際に、ローカルで未コミットの変更をリモートに反映させずローカルでだけ保持したい場合があります。

例えば、以下のようにhoge.txtの変更がローカルで行われていますが、この変更はリモートには反映したくないため、未コミットとしています。

$ cat hoge.txt
ローカルでの変更
$ git fetch
$ git diff remotes/origin/master
diff --git a/hoge.txt b/hoge.txt
index 48ee78d..e3130ae 100644
--- a/hoge.txt
+++ b/hoge.txt
@@ -1 +1 @@
-リモートでの変更
+ローカルでの変更
$ git status
On branch master
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)

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:   hoge.txt

no changes added to commit (use "git add" and/or "git commit -a")

しかし、この状態でgit pullをすると以下のようにエラーとなり、プル操作が行えません。

$ git pull origin master
From https://github.com/account/test
 * branch            master     -> FETCH_HEAD
Updating 017abaf..c79d59e
error: Your local changes to the following files would be overwritten by merge:
        hoge.txt
Please commit your changes or stash them before you merge.
Aborting

対処方法

以下の2通りの対処方法をご紹介します。

  • リモートの変更で上書きせず、ローカルでの変更を保持したい場合
  • やっぱりリモートの変更で上書きしたくなった場合

リモートの変更で上書きせず、ローカルでの変更を保持したい場合

事前に、ローカルでだけ保持したい変更以外の変更をすべてコミットしておきます。

ローカルでだけ保持したい変更をgit stashで退避させ、ローカルをクリーンな状態にします。

$ git stash save
Saved working directory and index state WIP on master: 017abaf Create hoge.txt
$ git status
On branch master
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)

nothing to commit, working tree clean

この状態でgit pullをします。ローカルのhoge.txtがリモートでの変更で上書きされます。

$ git pull origin master
From https://github.com/r-wakatsuki/test
 * branch            master     -> FETCH_HEAD
Updating 017abaf..c79d59e
Fast-forward
 hoge.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
$ cat hoge.txt
リモートでの変更

退避させた変更をstashから戻します。このときhoge.txtは競合状態となります。

$ git stash apply stash@{0}
Auto-merging hoge.txt
CONFLICT (content): Merge conflict in hoge.txt
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Unmerged paths:
  (use "git reset HEAD <file>..." to unstage)
  (use "git add <file>..." to mark resolution)

        both modified:   hoge.txt

no changes added to commit (use "git add" and/or "git commit -a")
$ cat hoge.txt
<<<<<<< Updated upstream
リモートでの変更
=======
ローカルでの変更
>>>>>>> Stashed changes

競合した変更のうちstashから戻した方の変更を適用します。状態としてはhoge.txtはまだ競合状態にあります。

$ git checkout --theirs .
$ cat hoge.txt
ローカルでの変更
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Unmerged paths:
  (use "git reset HEAD <file>..." to unstage)
  (use "git add <file>..." to mark resolution)

        both modified:   hoge.txt

no changes added to commit (use "git add" and/or "git commit -a")

hoge.txtを一旦ステージングして競合状態を解消します。

$ git add .
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   hoge.txt

hoge.txtのステージングを取り消します。これでローカルで未コミットの変更を保持したままgit pullを行うことができました。

$ git reset HEAD
Unstaged changes after reset:
M       hoge.txt
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

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:   hoge.txt

no changes added to commit (use "git add" and/or "git commit -a")

リモートの変更で上書きして良い場合

ローカルで未コミットのまま作業してきた変更を、git pull時に「やっぱりリモートの変更で上書きしてもいいや」となった場合の対処方法です。

変更を未コミットとしているhoge.txtに対してチェックアウトを行い、ローカルでの最後のコミット内容を適用してローカルをクリーンにします。

$ git checkout hoge.txt
$ git status
On branch master
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)

nothing to commit, working tree clean

git pullを実行してリモートの変更で上書きします。

$ git pull origin master
From https://github.com/account/test
 * branch            master     -> FETCH_HEAD
Updating c79d59e..37b80c5
Fast-forward
 hoge.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
$ cat hoge.txt
リモートでの変更

おわりに

git pullを実行する際に、ローカルで未コミットの変更に対して

  • リモートの変更で上書きせず、ローカルでの変更を保持したい場合
  • やっぱりリモートの変更で上書きしたくなった場合

の対処方法についてご紹介しました。

リポジトリに含まれている構成ファイルなどを作業のためにローカルでだけ変更することはよくあるので、覚えておくと役に立ちそうです。どなたかの参考になれば幸いです。

以上