Git Pull Force - как перезаписать локальные изменения с помощью Git

Когда вы научитесь программировать, рано или поздно вы также узнаете о системах контроля версий. И хотя в этой сфере существует множество конкурирующих инструментов, один из них является стандартом де-факто, используемым почти всеми в отрасли. Он настолько популярен, что есть компании, которые используют его имя в своем бренде. Мы, конечно, говорим о Git.

Git - мощный инструмент, но его мощь хорошо скрыта. Есть несколько важных концепций, которые вам нужно понять, чтобы стать действительно опытным пользователем Git. Хорошая новость заключается в том, что, изучив их, вы вряд ли когда-нибудь столкнетесь с проблемами, от которых не сможете убежать.

Типичный рабочий процесс

В типичном рабочем процессе Git вы будете использовать локальный репозиторий, удаленный репозиторий и одну или несколько веток. В репозиториях хранится вся информация о проекте, включая всю его историю и все ветки. Ветка - это в основном набор изменений, ведущих от пустого проекта к текущему состоянию.

После клонирования репозитория вы работаете над своей локальной копией и вносите новые изменения. Пока вы не отправите локальные изменения в удаленный репозиторий, вся ваша работа будет доступна только на вашем компьютере.

Когда вы закончите задачу, самое время выполнить синхронизацию с удаленным репозиторием. Вы хотите получить удаленные изменения, чтобы идти в ногу с прогрессом проекта, и хотите применить локальные изменения, чтобы поделиться своей работой с другими.

Местные изменения

Все хорошо, когда вы и остальная часть вашей команды работаете над совершенно разными файлами. Что бы ни случилось, вы не станете наступать друг другу на ноги.

Однако бывают случаи, когда вы и ваши товарищи по команде одновременно вносите изменения в одном месте. И обычно здесь начинаются проблемы.

Вы когда-нибудь казнили git pullтолько для того, чтобы увидеть ужасное error: Your local changes to the following files would be overwritten by merge:? Рано или поздно каждый сталкивается с этой проблемой.

Что больше сбивает с толку, так это то, что вы не хотите ничего объединять, просто тяните, верно? На самом деле тянуть немного сложнее, чем вы могли подумать.

Насколько точно работает Git Pull?

Вытягивание - это не разовая операция. Он состоит из выборки данных с удаленного сервера и последующего объединения изменений с локальным репозиторием. Эти две операции можно выполнить вручную, если вы хотите:

git fetch git merge origin/$CURRENT_BRANCH

В origin/$CURRENT_BRANCHчасти означает , что:

  • Git объединит изменения из удаленного репозитория с именем origin(того, из которого вы клонировали)
  • которые были добавлены в $CURRENT_BRANCH
  • которые еще не присутствуют в вашей местной проверенной ветке

Поскольку Git выполняет слияние только тогда, когда нет незафиксированных изменений, каждый раз, когда вы запускаете git pullс незафиксированными изменениями, у вас могут возникнуть проблемы. К счастью, есть способы избавиться от неприятностей в целости и сохранности!

Мы семья

Разные подходы

Если у вас есть незафиксированные локальные изменения, но вы по-прежнему хотите получить новую версию с удаленного сервера, ваш вариант использования обычно попадает в один из следующих сценариев. Либо:

  • вы не заботитесь о локальных изменениях и хотите их перезаписать,
  • вы очень заботитесь об изменениях и хотели бы применить их после удаленных изменений,
  • вы хотите скачать удаленные модификации, но еще не применять их

Каждый из подходов требует своего решения.

Тебе плевать на локальные изменения

В этом случае вы просто хотите отбросить все незафиксированные локальные изменения. Возможно, вы изменили файл для экспериментов, но вам больше не нужны изменения. Все, что вас волнует, - это быть в курсе последних событий.

Это означает, что вы добавляете еще один шаг между получением удаленных изменений и их объединением. Этот шаг вернет ветку в исходное состояние, что позволит git mergeработать.

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

Если вы не хотите вводить имя ветви каждый раз при запуске этой команды Git имеет хороший ярлык , указывающий на входную ветвь: @{u}. Восходящая ветвь - это ветка в удаленном репозитории, в которую вы отправляете и извлекаете из нее.

Вот как будут выглядеть приведенные выше команды с помощью ярлыка:

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

Мы цитируем ярлык в примере, чтобы оболочка не интерпретировала его.

Вы очень заботитесь о местных изменениях

Когда незафиксированные изменения важны для вас, есть два варианта. Вы можете зафиксировать их, а затем выполнить git pull, или можете спрятать их.

Спрятать означает отложить изменения на мгновение, чтобы вернуть их позже. Чтобы быть более точным, git stashсоздает фиксацию, которая не отображается в вашей текущей ветке, но все еще доступна Git.

Чтобы вернуть изменения, сохраненные в последнем тайнике, вы используете git stash popкоманду. После успешного применения сохраненных изменений эта команда также удаляет фиксацию тайника, поскольку она больше не нужна.

Тогда рабочий процесс мог бы выглядеть так:

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 огромен. В этой статье описывается только один из аспектов обслуживания репозитория: включение удаленных изменений в локальный репозиторий. Даже этот повседневный сценарий потребовал от нас более глубокого изучения внутренних механизмов этого инструмента контроля версий.

Изучение реальных вариантов использования поможет вам лучше понять, как Git работает изнутри. Это, в свою очередь, даст вам почувствовать силу всякий раз, когда вы попадете в неприятности. Все мы время от времени делаем это.