Как проверить, достижим ли коммит Git из HEAD

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

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

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

Мы рассмотрим два основных метода: использование команды git log --ancestry-path для визуализации иерархии коммитов между двумя точками и применение команды git merge-base --is-ancestor для прямой проверки иерархии. Вы будете практиковать эти методы, создав образцовый репозиторий с несколькими ветками и коммитами, в том числе с некоторыми, которые специально сделаны недостижимыми из HEAD, чтобы вы могли проверить и подтвердить свое понимание.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL git(("Git")) -.-> git/SetupandConfigGroup(["Setup and Config"]) git(("Git")) -.-> git/BasicOperationsGroup(["Basic Operations"]) git(("Git")) -.-> git/BranchManagementGroup(["Branch Management"]) git/SetupandConfigGroup -.-> git/init("Initialize Repo") git/BasicOperationsGroup -.-> git/add("Stage Files") git/BasicOperationsGroup -.-> git/commit("Create Commit") git/BranchManagementGroup -.-> git/branch("Handle Branches") git/BranchManagementGroup -.-> git/checkout("Switch Branches") git/BranchManagementGroup -.-> git/merge("Merge Histories") git/BranchManagementGroup -.-> git/log("Show Commits") git/BranchManagementGroup -.-> git/reflog("Log Ref Changes") git/BranchManagementGroup -.-> git/rebase("Reapply Commits") subgraph Lab Skills git/init -.-> lab-560063{{"Как проверить, достижим ли коммит Git из HEAD"}} git/add -.-> lab-560063{{"Как проверить, достижим ли коммит Git из HEAD"}} git/commit -.-> lab-560063{{"Как проверить, достижим ли коммит Git из HEAD"}} git/branch -.-> lab-560063{{"Как проверить, достижим ли коммит Git из HEAD"}} git/checkout -.-> lab-560063{{"Как проверить, достижим ли коммит Git из HEAD"}} git/merge -.-> lab-560063{{"Как проверить, достижим ли коммит Git из HEAD"}} git/log -.-> lab-560063{{"Как проверить, достижим ли коммит Git из HEAD"}} git/reflog -.-> lab-560063{{"Как проверить, достижим ли коммит Git из HEAD"}} git/rebase -.-> lab-560063{{"Как проверить, достижим ли коммит Git из HEAD"}} end

Использование команды git log --ancestry-path

На этом этапе мы рассмотрим, как использовать команду git log --ancestry-path. Эта команда полезна для просмотра истории коммитов по определенному пути между двумя коммитами. Она помогает понять иерархию изменений.

Сначала создадим простой репозиторий Git и сделаем несколько коммитов, чтобы подготовить сценарий для использования опции --ancestry-path.

Перейдите в директорию проекта:

cd ~/project

Создайте новую директорию для этой лабораторной работы и инициализируйте репозиторий Git:

mkdir ancestry-lab
cd ancestry-lab
git init

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

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

Теперь создадим файл и сделаем первый коммит:

echo "Initial content" > file1.txt
git add file1.txt
git commit -m "Initial commit"

Вы увидите вывод, подтверждающий коммит:

[master (root-commit) <commit-hash>] Initial commit
 1 file changed, 1 insertion(+)
 create mode 100644 file1.txt

Далее сделаем еще один коммит:

echo "Adding more content" >> file1.txt
git add file1.txt
git commit -m "Add more content"

Вы увидите вывод для второго коммита:

[master <commit-hash>] Add more content
 1 file changed, 1 insertion(+)

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

git branch feature
git checkout feature
echo "Feature work" > file2.txt
git add file2.txt
git commit -m "Add feature file"

Вы увидите вывод о создании ветки, переходе на нее и новом коммите:

Switched to a new branch 'feature'
[feature <commit-hash>] Add feature file
 1 file changed, 1 insertion(+)
 create mode 100644 file2.txt

Вернемся в ветку master и сделаем еще один коммит:

git checkout master
echo "More master work" >> file1.txt
git add file1.txt
git commit -m "More master content"

Вы увидите вывод о переходе между ветками и новом коммите:

Switched to branch 'master'
[master <commit-hash>] More master content
 1 file changed, 1 insertion(+)

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

git log --all --decorate --oneline

Вы увидите журнал, похожий на этот (хэши коммитов и их порядок могут отличаться):

<commit-hash> (HEAD -> master) More master content
<commit-hash> Add more content
<commit-hash> (feature) Add feature file
<commit-hash> Initial commit

Теперь используем команду git log --ancestry-path. Эта команда требует двух ссылок на коммиты. Она покажет коммиты, являющиеся предками второго коммита и потомками первого коммита.

Найдем хэш коммита для "Initial commit" и "More master content". Вы можете получить эти хэши из вывода команды git log --all --decorate --oneline. Замените <initial-commit-hash> и <master-commit-hash> на фактические хэши из вашего вывода.

git log --ancestry-path <initial-commit-hash> <master-commit-hash> --oneline

Эта команда покажет коммиты на пути от начального коммита до последнего коммита в ветке master. Вы должны увидеть коммиты "Initial commit", "Add more content" и "More master content".

Опция --ancestry-path полезна для понимания прямой линии разработки между двумя точками в истории, игнорируя коммиты из других веток, которые могут быть объединены позже.

Использование команды git merge-base --is-ancestor

На этом этапе мы узнаем о команде git merge-base --is-ancestor. Эта команда используется для проверки, является ли один коммит предком другого коммита. Это простая проверка, которая возвращает код статуса (0 - если утверждение истинно, 1 - если ложно), а не выводит информацию о коммитах. Это особенно полезно при написании скриптов или для быстрых проверок.

Мы продолжим использовать репозиторий ancestry-lab, который мы создали на предыдущем этапе. Убедитесь, что вы находитесь в правильной директории:

cd ~/project/ancestry-lab

Вспомним историю коммитов с предыдущего этапа. У нас есть коммиты как в ветке master, так и в ветке feature.

Найдем хэши коммитов для "Initial commit" и последнего коммита в ветке master ("More master content"). Вы можете использовать команду git log --oneline, чтобы увидеть недавние коммиты.

git log --oneline

Вывод будет похож на следующий:

<master-commit-hash> (HEAD -> master) More master content
<commit-hash> Add more content
<initial-commit-hash> Initial commit

Теперь используем команду git merge-base --is-ancestor, чтобы проверить, является ли "Initial commit" предком последнего коммита в ветке master. Замените <initial-commit-hash> и <master-commit-hash> на фактические хэши.

git merge-base --is-ancestor <initial-commit-hash> <master-commit-hash>
echo $?

Команда echo $? выводит код завершения предыдущей команды. Если первый коммит является предком второго, код завершения будет 0. В противном случае он будет 1.

Поскольку "Initial commit" действительно является предком последнего коммита в ветке master, вывод команды echo $? должен быть 0.

Теперь проверим, является ли "Initial commit" предком последнего коммита в ветке feature. Сначала найдем хэш коммита для "Add feature file".

git log --all --decorate --oneline

Вывод будет похож на следующий:

<master-commit-hash> (HEAD -> master) More master content
<commit-hash> Add more content
<feature-commit-hash> (feature) Add feature file
<initial-commit-hash> Initial commit

Теперь используем команду git merge-base --is-ancestor, чтобы проверить, является ли "Initial commit" предком коммита "Add feature file". Замените <initial-commit-hash> и <feature-commit-hash> на фактические хэши.

git merge-base --is-ancestor <initial-commit-hash> <feature-commit-hash>
echo $?

Снова, вывод команды echo $? должен быть 0, потому что "Initial commit" является стартовой точкой для обеих веток.

Наконец, проверим, является ли последний коммит в ветке feature предком последнего коммита в ветке master. Замените <feature-commit-hash> и <master-commit-hash> на фактические хэши.

git merge-base --is-ancestor <feature-commit-hash> <master-commit-hash>
echo $?

В этом случае последний коммит в ветке feature не является предком последнего коммита в ветке master (они находятся на разных ветках после начального разделения). Поэтому вывод команды echo $? должен быть 1.

Понимание отношения предков между коммитами является фундаментом для понимания того, как Git отслеживает историю и как работают операции слияния (merging) и перебазирования (rebasing). Флаг --is-ancestor предоставляет простой способ проверить это отношение.

Тестирование недостижимых коммитов

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

Мы продолжим использовать репозиторий ancestry-lab. Убедитесь, что вы находитесь в правильной директории:

cd ~/project/ancestry-lab

В настоящее время все наши коммиты достижимы либо из ветки master, либо из ветки feature. Создадим сценарий, при котором коммит станет недостижимым.

Сначала сделаем новый коммит в ветке master:

echo "Temporary commit" >> file1.txt
git add file1.txt
git commit -m "Temporary commit"

Вы увидите вывод для этого нового коммита:

[master <commit-hash>] Temporary commit
 1 file changed, 1 insertion(+)

Теперь сбросим ветку master на предыдущий коммит. Это сделает коммит "Temporary commit" недостижимым из ветки master. Мы будем использовать команду git reset --hard HEAD~1. HEAD~1 ссылается на коммит, непосредственно предшествующий текущему HEAD.

Будьте осторожны при использовании git reset --hard, так как эта команда уничтожает изменения! В данном случае мы намеренно удаляем коммит "Temporary commit" из истории ветки master.

git reset --hard HEAD~1

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

HEAD is now at <previous-commit-hash> More master content

Теперь посмотрим на стандартный вывод git log:

git log --oneline

Вы увидите, что коммит "Temporary commit" больше не отображается в журнале коммитов ветки master.

<previous-commit-hash> (HEAD -> master) More master content
<commit-hash> Add more content
<initial-commit-hash> Initial commit

Коммит "Temporary commit" по-прежнему существует в базе данных Git, но на него не ссылается ни одна ветка или тег. Теперь он является "недостижимым" коммитом.

Как можно увидеть недостижимые коммиты? Git имеет специальную ссылку под названием reflog, которая записывает обновления вершин веток и других ссылок. Мы можем использовать команду git log с опцией --walk-reflogs или просто git reflog, чтобы увидеть эти коммиты.

Давайте используем git reflog:

git reflog

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

<master-commit-hash> (HEAD -> master) master@{0}: reset: moving to HEAD~1
<temporary-commit-hash> master@{1}: commit: Temporary commit
<previous-commit-hash> master@{2}: commit: More master content
<commit-hash> master@{3}: commit: Add more content
<initial-commit-hash> master@{4}: commit (initial): Initial commit

Обратите внимание на запись о коммите "Temporary commit". Он доступен через master@{1} в reflog. Однако он недоступен из текущего HEAD или вершины любой ветки.

Недостижимые коммиты в конечном итоге удаляются при выполнении сборки мусора Git (git gc), но они остаются доступными через reflog в течение стандартного периода (обычно 30 или 90 дней). Это может стать спасением, если вы случайно сбросили или удалили коммиты.

Понимание недостижимых коммитов помогает вам понять разницу между внутренней базой объектов Git и ссылками (ветками, тегами, HEAD), которые указывают на коммиты в этой базе.

Резюме

В этом практическом занятии (лабораторной работе) мы узнали, как проверить, достижим ли коммит Git из HEAD, используя два основных метода. Во - первых, мы изучили команду git log --ancestry-path, которая позволяет визуализировать историю коммитов по пути между двумя заданными коммитами. Мы создали простой репозиторий с несколькими ветками и коммитами, чтобы продемонстрировать, как эта команда помогает понять родословную и определить, является ли один коммит предком другого.

Во - вторых, мы научимся использовать команду git merge - base --is - ancestor, которая предоставляет более прямой и программный способ определить, является ли коммит предком другого. Наконец, мы протестируем эти методы с недостижимыми коммитами, чтобы закрепить наше понимание того, как проверять достижимость коммитов в репозитории Git.