Как отменить операцию git cherry-pick

GitBeginner
Практиковаться сейчас

Введение

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

Настройка тестового репозитория

На этом шаге мы создадим тестовый Git-репозиторий для практики операций cherry-pick. Это обеспечит безопасную среду для экспериментов с различными командами Git.

Создание нового Git-репозитория

Начнем с создания нового каталога для нашего тестового репозитория и инициализации его как Git-репозитория:

mkdir -p ~/project/cherry-pick-lab
cd ~/project/cherry-pick-lab
git init

Вы должны увидеть вывод, аналогичный следующему:

Initialized empty Git repository in /home/labex/project/cherry-pick-lab/.git/

Настройка конфигурации пользователя Git

Прежде чем мы сможем делать коммиты, нам нужно настроить имя пользователя и адрес электронной почты для Git:

git config --local user.name "LabEx User"
git config --local user.email "labex@example.com"

Создание начальных коммитов в основной ветке

Давайте создадим несколько начальных коммитов в основной ветке:

## Create and commit the first file
echo "## Cherry Pick Lab" > README.md
git add README.md
git commit -m "Initial commit with README"

## Create and commit a second file
echo "console.log('Hello, world!');" > app.js
git add app.js
git commit -m "Add main application file"

Просмотр истории коммитов

Давайте проверим историю коммитов, чтобы убедиться, что все настроено правильно:

git log --oneline

Вы должны увидеть вывод, аналогичный следующему:

abcd123 (HEAD -> main) Add main application file
efgh456 Initial commit with README

Фактические хеши коммитов будут отличаться в вашей системе. Запишите хеш коммита для "Add main application file", так как мы будем использовать его позже.

Создание ветки разработки (Feature Branch)

Теперь давайте создадим ветку разработки, в которой мы внесем некоторые дополнительные изменения:

git checkout -b feature-branch

Вы должны увидеть вывод, аналогичный следующему:

Switched to a new branch 'feature-branch'

Теперь давайте добавим несколько коммитов в эту ветку разработки:

## Create and commit a new feature file
echo "function newFeature() { return 'awesome'; }" > feature.js
git add feature.js
git commit -m "Add new feature function"

## Modify the README file
echo -e "## Cherry Pick Lab\n\nThis repo demonstrates git cherry-pick operations." > README.md
git add README.md
git commit -m "Update README with project description"

Теперь у нас есть основная ветка с двумя коммитами и ветка разработки с двумя дополнительными коммитами. На следующем шаге мы будем использовать cherry-pick, чтобы применить один из коммитов ветки разработки к основной ветке.

Выполнение операции Cherry-pick

На этом шаге мы узнаем, как использовать команду cherry-pick для применения определенного коммита из одной ветки в другую. Это полезно, когда вы хотите выборочно включить изменения из одной ветки в другую.

Понимание Cherry-pick

Cherry-picking в Git позволяет вам выбрать определенный коммит из одной ветки и применить его к другой ветке. В отличие от слияния (merging) или перебазирования (rebasing), которые обычно применяют несколько коммитов, cherry-picking применяет только один коммит за раз.

Переключение обратно в основную ветку

Сначала давайте переключимся обратно в основную ветку, в которую мы хотим применить коммит из ветки разработки:

git checkout main

Вы должны увидеть вывод, подтверждающий переключение:

Switched to branch 'main'

Просмотр коммитов ветки разработки

Перед cherry-picking давайте рассмотрим коммиты в ветке разработки, которые мы, возможно, захотим применить к основной ветке:

git log feature-branch --oneline

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

1234abc Update README with project description
5678def Add new feature function
abcd123 Add main application file
efgh456 Initial commit with README

Запишите хеш коммита для "Add new feature function" (в этом примере это 5678def). Мы будем использовать этот хеш на следующем шаге.

Cherry-picking коммита

Теперь давайте выполним cherry-pick коммита "Add new feature function" из ветки разработки в нашу основную ветку:

git cherry-pick [COMMIT_HASH]

Замените [COMMIT_HASH] фактическим хешем, который вы записали ранее. Например:

git cherry-pick 5678def

Если cherry-pick успешен, вы увидите вывод, аналогичный следующему:

[main 98765ab] Add new feature function
 1 file changed, 1 insertion(+)
 create mode 100644 feature.js

Проверка Cherry-pick

Давайте убедимся, что cherry-pick сработал, как ожидалось:

git log --oneline

Теперь вы должны увидеть коммит, выбранный с помощью cherry-pick, в истории вашей основной ветки:

98765ab (HEAD -> main) Add new feature function
abcd123 Add main application file
efgh456 Initial commit with README

Вы также можете проверить, что файл существует:

ls -la

Вы должны увидеть feature.js в списке вывода:

total 16
drwxr-xr-x 3 labex labex 4096 Jan 1 00:00 .
drwxr-xr-x 3 labex labex 4096 Jan 1 00:00 ..
drwxr-xr-x 8 labex labex 4096 Jan 1 00:00 .git
-rw-r--r-- 1 labex labex   29 Jan 1 00:00 app.js
-rw-r--r-- 1 labex labex   42 Jan 1 00:00 feature.js
-rw-r--r-- 1 labex labex   16 Jan 1 00:00 README.md

Операция cherry-pick успешно применила коммит из ветки разработки в основную ветку. На следующем шаге мы узнаем, как отменить этот cherry-pick, если это необходимо.

Отмена Cherry-pick с помощью Git Reset

Теперь, когда мы успешно выполнили cherry-pick коммита, давайте узнаем, как отменить эту операцию. На этом шаге мы будем использовать команду git reset, которая является наиболее простым способом отмены недавнего cherry-pick.

Понимание Git Reset

Команда git reset перемещает указатель текущей ветки к указанному коммиту, эффективно "отменяя" любые коммиты, которые были после этой точки. Существует три основных режима git reset:

  • --soft: Перемещает указатель ветки, но оставляет изменения в подготовленном состоянии (staged)
  • --mixed (по умолчанию): Перемещает указатель ветки и отменяет подготовку изменений (unstages)
  • --hard: Перемещает указатель ветки и отбрасывает все изменения

Для отмены cherry-pick мы будем использовать опцию --hard, чтобы полностью удалить коммит, выбранный с помощью cherry-pick, и его изменения.

Проверка текущего статуса

Сначала давайте проверим наш текущий статус, чтобы убедиться, что мы находимся в основной ветке с коммитом, выбранным с помощью cherry-pick:

git log --oneline -n 3

Вы должны увидеть вывод, аналогичный следующему:

98765ab (HEAD -> main) Add new feature function
abcd123 Add main application file
efgh456 Initial commit with README

Отмена Cherry-pick с помощью Git Reset

Чтобы отменить операцию cherry-pick, мы будем использовать git reset с опцией --hard, чтобы переместить указатель ветки на один коммит назад:

git reset --hard HEAD~1

Эта команда указывает Git сбросить ветку к коммиту, предшествующему текущему HEAD (часть ~1 означает "один коммит назад").

Вы должны увидеть вывод, аналогичный следующему:

HEAD is now at abcd123 Add main application file

Проверка сброса

Давайте проверим, что cherry-pick был отменен:

git log --oneline

Вы должны увидеть, что коммит, выбранный с помощью cherry-pick, больше не находится в истории:

abcd123 (HEAD -> main) Add main application file
efgh456 Initial commit with README

Давайте также проверим, был ли удален файл feature.js:

ls -la

Вывод не должен включать feature.js:

total 12
drwxr-xr-x 3 labex labex 4096 Jan 1 00:00 .
drwxr-xr-x 3 labex labex 4096 Jan 1 00:00 ..
drwxr-xr-x 8 labex labex 4096 Jan 1 00:00 .git
-rw-r--r-- 1 labex labex   29 Jan 1 00:00 app.js
-rw-r--r-- 1 labex labex   16 Jan 1 00:00 README.md

Поздравляем! Вы успешно отменили операцию cherry-pick с помощью git reset. Этот метод чистый и простой, но он перезаписывает историю, поэтому его следует использовать только для локальных изменений, которые не были отправлены в общий репозиторий.

Отмена Cherry-pick с помощью Git Revert

На предыдущем шаге мы использовали git reset для отмены cherry-pick. Однако git reset перезаписывает историю, что может быть проблематично, если вы уже отправили свои изменения в общий репозиторий. На этом шаге мы узнаем, как использовать git revert для безопасной отмены cherry-pick без перезаписи истории.

Понимание Git Revert

Команда git revert создает новый коммит, который отменяет изменения, внесенные предыдущим коммитом. В отличие от git reset, который удаляет коммиты из истории, git revert добавляет новый коммит, который противодействует изменениям, сохраняя историю коммитов.

Повторное выполнение Cherry-pick

Сначала давайте снова выполним cherry-pick коммита, чтобы у нас было что отменять:

## Get the commit hash from the feature branch
FEATURE_HASH=$(git log feature-branch --oneline | grep "new feature" | cut -d ' ' -f 1)

## Cherry-pick the commit
git cherry-pick $FEATURE_HASH

Вы должны увидеть вывод, аналогичный следующему:

[main 98765ab] Add new feature function
 1 file changed, 1 insertion(+)
 create mode 100644 feature.js

Проверка текущего статуса

Давайте убедимся, что cherry-pick был успешен:

git log --oneline -n 3
ls -la

Вы должны увидеть коммит, выбранный с помощью cherry-pick, в истории и файл feature.js в списке каталогов.

Отмена Cherry-pick

Теперь давайте используем git revert, чтобы отменить cherry-pick, сохраняя историю:

git revert HEAD --no-edit

Флаг --no-edit указывает Git использовать сообщение коммита по умолчанию, не открывая редактор.

Вы должны увидеть вывод, аналогичный следующему:

[main abc9876] Revert "Add new feature function"
 1 file changed, 1 deletion(-)
 delete mode 100644 feature.js

Проверка Revert

Давайте проверим историю коммитов:

git log --oneline -n 4

Вы должны увидеть как коммит, выбранный с помощью cherry-pick, так и коммит отмены:

abc9876 (HEAD -> main) Revert "Add new feature function"
98765ab Add new feature function
abcd123 Add main application file
efgh456 Initial commit with README

Теперь давайте проверим, был ли удален файл feature.js:

ls -la

Вывод не должен включать feature.js:

total 12
drwxr-xr-x 3 labex labex 4096 Jan 1 00:00 .
drwxr-xr-x 3 labex labex 4096 Jan 1 00:00 ..
drwxr-xr-x 8 labex labex 4096 Jan 1 00:00 .git
-rw-r--r-- 1 labex labex   29 Jan 1 00:00 app.js
-rw-r--r-- 1 labex labex   16 Jan 1 00:00 README.md

Хотя и git reset, и git revert достигают одного и того же конечного результата (удаление изменений, внесенных с помощью cherry-pick), они делают это разными способами:

  • git reset удаляет коммит из истории, что может вызвать проблемы, если коммит был передан другим пользователям.
  • git revert добавляет новый коммит, который отменяет изменения, сохраняя историю коммитов, что безопаснее для общих репозиториев.

Выбор между этими методами зависит от вашей конкретной ситуации:

  • Используйте git reset для локальных изменений, которые не были переданы
  • Используйте git revert для изменений, которые были отправлены в общий репозиторий

Обработка конфликтов Cherry-pick

Иногда, когда вы выполняете cherry-pick коммита, Git может столкнуться с конфликтами, если изменения в коммите конфликтуют с изменениями в вашей текущей ветке. На этом шаге мы узнаем, как обрабатывать конфликты cherry-pick и как прервать операцию cherry-pick.

Создание сценария с потенциальными конфликтами

Сначала давайте создадим сценарий, который приведет к конфликту cherry-pick:

## Switch to main branch and modify README.md
git checkout main
echo -e "## Cherry Pick Lab\n\nThis is the main branch README." > README.md
git commit -am "Update README in main branch"

## Switch to feature branch and make a conflicting change to README.md
git checkout feature-branch
echo -e "## Cherry Pick Lab\n\nThis README has been updated in the feature branch." > README.md
git commit -am "Update README in feature branch"

Попытка Cherry-pick с конфликтами

Теперь давайте вернемся в основную ветку и попробуем выполнить cherry-pick коммита из ветки feature:

git checkout main
CONFLICT_HASH=$(git log feature-branch --oneline | grep "Update README in feature" | cut -d ' ' -f 1)
git cherry-pick $CONFLICT_HASH

Поскольку обе ветки изменили одни и те же строки в README.md, вы должны увидеть конфликт:

error: could not apply a1b2c3d... Update README in feature branch
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'

Просмотр конфликта

Давайте рассмотрим конфликт:

git status

Вы должны увидеть вывод, указывающий на конфликт в README.md:

On branch main
You are currently cherry-picking commit a1b2c3d.
  (fix conflicts and run "git cherry-pick --continue")
  (use "git cherry-pick --abort" to cancel the cherry-pick operation)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
  both modified:   README.md

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

Давайте посмотрим на содержимое конфликтного файла:

cat README.md

Вы должны увидеть что-то вроде:

## Cherry Pick Lab

<<<<<<< HEAD
This is the main branch README.
=======
This README has been updated in the feature branch.
>>>>>>> a1b2c3d... Update README in feature branch

Разрешение конфликта

Чтобы разрешить конфликт, нам нужно отредактировать файл и решить, какие изменения сохранить. Давайте изменим README.md, чтобы включить оба изменения:

echo -e "## Cherry Pick Lab\n\nThis is the main branch README.\n\nThis README has also been updated with content from the feature branch." > README.md

Теперь давайте отметим конфликт как разрешенный и продолжим cherry-pick:

git add README.md
git cherry-pick --continue

Git откроет редактор с сообщением коммита по умолчанию. Сохраните и закройте редактор, чтобы завершить cherry-pick.

Прерывание Cherry-pick

Иногда вы можете решить, что не хотите разрешать конфликты и предпочитаете отменить операцию cherry-pick. Давайте создадим еще один конфликт, а затем прервем cherry-pick:

## Create another conflicting commit in feature branch
git checkout feature-branch
echo "// This will conflict with app.js in main" > app.js
git commit -am "Modify app.js in feature branch"

## Try to cherry-pick this commit to main
git checkout main
ANOTHER_CONFLICT=$(git log feature-branch --oneline | grep "Modify app.js" | cut -d ' ' -f 1)
git cherry-pick $ANOTHER_CONFLICT

Вы должны увидеть еще один конфликт. На этот раз давайте прервем cherry-pick:

git cherry-pick --abort

Вы должны увидеть, что операция cherry-pick была отменена, и ваш рабочий каталог был восстановлен в предыдущее состояние:

git status

Output:

On branch main
nothing to commit, working tree clean

Обработка конфликтов во время операций cherry-pick является важным навыком для пользователей Git. У вас есть три варианта, когда вы сталкиваетесь с конфликтом:

  1. Разрешить конфликт вручную, затем использовать git add и git cherry-pick --continue
  2. Пропустить конфликтующий коммит с помощью git cherry-pick --skip
  3. Прервать всю операцию cherry-pick с помощью git cherry-pick --abort

Лучший подход зависит от конкретной ситуации и требований вашего проекта.

Резюме

В этой лабораторной работе вы получили практический опыт работы с функцией cherry-pick Git и изучили несколько способов отмены операций cherry-pick:

  1. Вы создали тестовый репозиторий с несколькими ветками и коммитами для практики.
  2. Вы выполнили операцию cherry-pick, чтобы применить определенный коммит из одной ветки в другую.
  3. Вы узнали, как отменить cherry-pick с помощью git reset, что подходит для локальных изменений.
  4. Вы изучили, как безопасно отменить cherry-pick с помощью git revert, который сохраняет историю.
  5. Вы попрактиковались в обработке конфликтов cherry-pick и узнали, как прервать операцию cherry-pick.

Эти навыки бесценны при работе с Git в реальных проектах. Cherry-picking позволяет вам выборочно применять изменения между ветками, а знание того, как отменить cherry-picks, помогает вам восстанавливаться после ошибок и поддерживать чистую историю Git.

Помните, что разные методы отмены служат разным целям:

  • Используйте git reset для локальных изменений, которые не были переданы.
  • Используйте git revert для изменений, которые были отправлены в общий репозиторий.
  • Используйте git cherry-pick --abort для отмены выполняющейся операции cherry-pick.

Понимая эти варианты, вы можете выбрать наиболее подходящий метод для вашей конкретной ситуации.