Gitプルフォース–Gitでローカルの変更を上書きする方法

コーディングを学ぶと、遅かれ早かれバージョン管理システムについても学びます。この分野には多くの競合ツールがありますが、そのうちの1つは、業界のほぼすべての人が使用している事実上の業界標準です。非常に人気があるため、ブランドにその名前を使用している企業があります。もちろん、Gitについて話しています。

Gitは強力なツールですが、その力は十分に隠されています。Gitに本当に習熟するために理解する必要のあるいくつかの重要な概念があります。良いニュースは、一度それらを学んだら、逃げられないトラブルに遭遇することはほとんどないということです。

典型的なワークフロー

典型的なGitワークフローでは、ローカルリポジトリ、リモートリポジトリ、および1つ以上のブランチを使用します。リポジトリには、プロジェクト全体の履歴やすべてのブランチなど、プロジェクトに関するすべての情報が格納されます。ブランチは基本的に、空のプロジェクトから現在の状態に至るまでの変更のコレクションです。

リポジトリのクローンを作成した後、ローカルコピーで作業し、新しい変更を導入します。ローカルの変更をリモートリポジトリにプッシュするまで、すべての作業は自分のマシンでのみ利用できます。

タスクが完了したら、リモートリポジトリと同期します。プロジェクトの進行状況に対応するためにリモートの変更をプルし、ローカルの変更をプッシュして他のユーザーと作業を共有したいとします。

ローカルの変更

あなたとあなたのチームの他のメンバーが完全に別々のファイルで作業しているときは、すべてうまくいきます。何が起こっても、お互いの足を踏むことはありません。

ただし、あなたとあなたのチームメートが同じ場所で同時に変更を導入する場合があります。そして、それは通常問題が始まるところです。

あなたgit pullは恐ろしいものを見るためだけに処刑したことがありerror: Your local changes to the following files would be overwritten by merge:ますか?遅かれ早かれ、誰もがその問題に遭遇します。

ここでさらに混乱するのは、何もマージしたくないということです。プルするだけですよね?実際、プルはあなたが思っていたよりも少し複雑です。

Git Pullはどの程度正確に機能しますか?

プルは単一の操作ではありません。これは、リモートサーバーからデータをフェッチし、変更をローカルリポジトリにマージすることで構成されます。これらの2つの操作は、必要に応じて手動で実行できます。

git fetch git merge origin/$CURRENT_BRANCH

origin/$CURRENT_BRANCHパーツ手段もの:

  • Gitは、origin(クローン元の)という名前のリモートリポジトリからの変更をマージします
  • に追加された $CURRENT_BRANCH
  • ローカルのチェックアウトされたブランチにまだ存在していない

Gitはコミットされていない変更がない場合にのみマージを実行するためgit pull、コミットされていない変更を実行するたびに問題が発生する可能性があります。幸いなことに、一枚でトラブルから抜け出す方法があります!

私たちは家族です

さまざまなアプローチ

コミットされていないローカル変更があり、それでもリモートサーバーから新しいバージョンをプルしたい場合、ユースケースは通常、次のいずれかのシナリオに分類されます。どちらか:

  • ローカルの変更を気にせず、それらを上書きしたい場合は、
  • あなたは変更を非常に気にかけていて、リモート変更の後にそれらを適用したいと思っています、
  • リモート変更をダウンロードしたいが、まだ適用していない

それぞれのアプローチには、異なるソリューションが必要です。

あなたはローカルな変更を気にしません

この場合、コミットされていないローカルの変更をすべて削除するだけです。実験のためにファイルを変更した可能性がありますが、変更する必要はありません。気になるのは、アップストリームを最新の状態にすることだけです。

これは、リモート変更のフェッチとそれらのマージの間にもう1つのステップを追加することを意味します。この手順により、ブランチが変更されていない状態にリセットされ、git merge動作できるようになります。

git fetch git reset --hard HEAD git merge origin/$CURRENT_BRANCH

このコマンドを実行するたびにブランチ名を入力したくない場合は、Gitにアップストリームブランチを指す便利なショートカットがあります@{u}。アップストリームブランチは、プッシュおよびフェッチするリモートリポジトリ内のブランチです。

This is how the above commands would look like with the shortcut:

git fetch git reset --hard HEAD git merge '@{u}'

We are quoting the shortcut in the example to prevent the shell from interpreting it.

You Very Much Care About the Local Changes

When your uncommitted changes are significant to you, there are two options. You can commit them and then perform git pull, or you can stash them.

Stashing means putting the changes away for a moment to bring them back later. To be more precise, git stash creates a commit that is not visible on your current branch, but is still accessible by Git.

To bring back the changes saved in the last stash, you use the git stash pop command. After successfully applying the stashed changes, this command also removes the stash commit as it is no longer needed.

The workflow could then look like this:

git fetch git stash git merge '@{u}' git stash pop

By default, the changes from the stash will become staged. If you want to unstage them, use the command git restore --staged (if using Git newer than 2.25.0).

You Just Want to Download the Remote Changes

The last scenario is a little different from the previous ones. Let's say that you are in the middle of a very messy refactoring. Neither losing the changes nor stashing them is an option. Yet, you still want to have the remote changes available to run git diff against them.

As you have probably figured out, downloading the remote changes does not require git pull at all! git fetch is just enough.

One thing to note is that by default, git fetch will only bring you changes from the current branch. To get all the changes from all the branches, use git fetch --all. And if you'd like to clean up some of the branches that no longer exist in the remote repository, git fetch --all --prune will do the cleaning up!

Some Automation

Have you heard of Git Config? It's a file where Git stores all of the user-configured settings. It resides in your home directory: either as ~/.gitconfig or ~/.config/git/config. You can edit it to add some custom aliases that will be understood as Git commands.

For example, to have a shortcut equivalent to git diff --cached (that shows the difference between the current branch and the staged files), you'd add the following section:

[alias] dc = diff --cached

After that, you can run git dc whenever you wish to review the changes. Going this way, we can set up a few aliases related to the previous use cases.

[alias] pull_force = !"git fetch --all; git reset --hard HEAD; git merge @{u}" pf = pull_force pull_stash = !"git fetch --all; git stash; git merge @{u}; git stash pop"

This way, running git pull_force will overwrite the local changes, while git pull_stash will preserve them.

The Other Git Pull Force

Curious minds may have already discovered that there is such a thing as git pull --force. However, this is a very different beast to what's presented in this article.

It may sound like something that would help us overwrite local changes. Instead, it lets us fetch the changes from one remote branch to a different local branch. git pull --force only modifies the behavior of the fetching part. It is therefore equivalent to git fetch --force.

Like git push, git fetch allows us to specify which local and remote branch do we want to operate on. git fetch origin/feature-1:my-feature will mean that the changes in the feature-1 branch from the remote repository will end up visible on the local branch my-feature. When such an operation modifies the existing history, it is not permitted by Git without an explicit --force parameter.

Just like git push --force allows overwriting remote branches, git fetch --force (or git pull --force) allows overwriting local branches. It is always used with source and destination branches mentioned as parameters. An alternative approach to overwriting local changes using git --pull force could be git pull --force "@{u}:HEAD".

Conclusion

Gitの世界は広大です。この記事では、リポジトリ保守の1つの側面、つまりリモート変更をローカルリポジトリに組み込むことだけを取り上げました。この日常的なシナリオでさえ、このバージョン管理ツールの内部メカニズムをもう少し詳しく調べる必要がありました。

実際のユースケースを学ぶことは、Gitが内部でどのように機能するかをよりよく理解するのに役立ちます。これにより、トラブルに巻き込まれたときはいつでも、力を与えられたと感じることができます。私たちは皆、時々それをします。