Git 커밋이 HEAD 에서 도달 가능한지 확인하는 방법

GitBeginner
지금 연습하기

소개

이 랩에서는 특정 Git 커밋이 현재 HEAD 에서 도달 가능한지 확인하는 방법을 배우게 됩니다. 이는 리포지토리의 히스토리를 이해하고 활성 개발 라인의 일부인 커밋을 식별하는 데 필수적인 기술입니다.

두 가지 주요 방법을 살펴볼 것입니다. git log --ancestry-path 명령을 사용하여 두 지점 사이의 커밋 계보를 시각화하고, git merge-base --is-ancestor 명령을 사용하여 조상 관계를 직접 확인합니다. 여러 브랜치와 커밋이 있는 샘플 리포지토리를 설정하여 이러한 기술을 연습할 것입니다. 여기에는 HEAD 에서 의도적으로 도달할 수 없도록 만들어진 일부 커밋이 포함되어 있어 이해도를 테스트하고 확인할 수 있습니다.

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에 대해 배우겠습니다. 이 명령은 한 커밋이 다른 커밋의 조상인지 확인하는 데 사용됩니다. 커밋 정보를 출력하는 대신 상태 코드 (true 는 0, false 는 1) 를 반환하는 간단한 확인입니다. 이는 스크립팅이나 빠른 확인에 특히 유용합니다.

이전 단계에서 생성한 ancestry-lab 리포지토리를 계속 사용하겠습니다. 올바른 디렉토리에 있는지 확인하십시오.

cd ~/project/ancestry-lab

이전 단계의 커밋 히스토리를 기억하십시오. masterfeature 브랜치 모두에 커밋이 있습니다.

"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 $?

다시, "Initial commit"이 두 브랜치의 시작점이므로 echo $?의 출력은 0이어야 합니다.

마지막으로, 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 이 히스토리를 추적하는 방법과 병합 및 리베이스와 같은 작업이 작동하는 방식을 이해하는 데 기본입니다. --is-ancestor 플래그는 이 관계를 확인하는 간단한 방법을 제공합니다.

도달 불가능한 커밋 테스트

이 단계에서는 Git 에서 "도달할 수 없는 (unreachable)" 커밋의 개념을 살펴보겠습니다. 도달할 수 없는 커밋은 브랜치, 태그 또는 기타 참조에서 도달할 수 없는 커밋입니다. 이러한 커밋은 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라는 특수 참조가 있습니다. --walk-reflogs 옵션과 함께 git log를 사용하거나 단순히 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"에 대한 항목을 확인하십시오. reflog 에서 master@{1}을 통해 도달할 수 있습니다. 그러나 현재 HEAD 또는 브랜치 팁에서는 도달할 수 없습니다.

도달할 수 없는 커밋은 결국 Git 의 가비지 수집 (git gc) 에 의해 정리되지만, 기본 기간 (일반적으로 30 일 또는 90 일) 동안 reflog 를 통해 계속 액세스할 수 있습니다. 이는 실수로 커밋을 재설정하거나 삭제한 경우 생명의 은인이 될 수 있습니다.

도달할 수 없는 커밋을 이해하면 Git 의 내부 객체 데이터베이스와 해당 데이터베이스 내의 커밋을 가리키는 참조 (브랜치, 태그, HEAD) 간의 차이점을 이해하는 데 도움이 됩니다.

요약

이 랩에서는 두 가지 주요 방법을 사용하여 Git 커밋이 HEAD 에서 도달 가능한지 확인하는 방법을 배웠습니다. 먼저, 두 개의 지정된 커밋 사이의 경로를 따라 커밋 히스토리를 시각화할 수 있는 git log --ancestry-path 명령을 살펴보았습니다. 여러 브랜치와 커밋이 있는 간단한 리포지토리를 설정하여 이 명령이 계보를 이해하고 한 커밋이 다른 커밋의 조상인지 식별하는 데 어떻게 도움이 되는지 시연했습니다.

둘째, 커밋이 다른 커밋의 조상인지 확인하는 보다 직접적이고 프로그래밍 방식인 git merge-base --is-ancestor 명령을 사용하는 방법을 배웁니다. 마지막으로, 도달할 수 없는 커밋으로 이러한 방법을 테스트하여 Git 리포지토리 내에서 도달 가능성을 확인하는 방법에 대한 이해를 굳힐 것입니다.