Git Basics (2)

4 minute read

Tracking status, changes and commit history

깃은 파일에 대한 변경 히스토리를 추적할 때 매우 유용하게 쓰일 수 있다.

“Software Development”를 hello.txt 내용에 붙이고 git status 커맨드를 실행하여 마지막 커밋으로부터 무엇이 변경되었는지 확인해보자.

echo Software Development >> hello.txt
git status

다음과 같은 아웃풋이 나올 것이다.

On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)

    modified:  hello.txt

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

hello.txt를 스테이징하기 전에 가장 최근의 커밋과 얼마나 다른지 확인할 수 있다. 리눅스의 diff 커맨드와 비슷하게 git diff는 두 개의 깃 데이터 소스 (i.e. commits, branches, files, etc)들 간의 차이점을 보여준다.

git diff

를 실행하면 다음과 같은 아웃풋이 나온다.

diff --git a/hello.txt b/hello.txt
index dec4cb0..f4a4437 100644
--- a/hello.txt
+++ b/hello.txt
@@ -1 +1,2 @@
Hello World
+Software Development

자, 이제 파일을 staging area에 추가한다.

git add hello.txt
git status

다음과 같은 아웃풋이 나올 것이다.

On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)


modified:  hello.txt

이 때, 만약 스테이징 된 파일이나 이전 커밋의 스테이징된 파일의 변경 사항을 보려면 git diff --staged를 하면 된다. 이제 커밋을 하기 위해 다음과 같이 실행한다.

git commit -m "add course name to hello.txt"

이제 다시

find .git/objects -type f

를 실행해보면 깃 레포지토리에 6개의 object가 있는 것을 확인할 수 있다. 2개는 commit object이고, 각각 자신의 tree object를 가지고 있다. 또 2개의 blob object가 있다. 이제 hello.txt와 hello.bak이 서로 다른 내용을 담고 있기 때문에 2개의 blob object가 존재하는 것이다. 구별하기 위해서 git cat-file -p 커맨드를 사용하여 확인할 수 있다. 그러나 모든 레포지토리는 많은 커밋들을 가지고 있고 디폴트로 깃은 가장 최근 것만 보여준다. 가장 최근의 커밋을 찾기 위해 cat 명령어를 사용하여 확인할 수 있다.

cat .git/refs/heads/master

아웃풋은 가장 최근 커밋을 포함하는 SHA1 해시 파일 이름이다. 그 파일을 다음과 같이 확인해보자.

git cat-file -p <file name outputted from the last command>

다음과 같은 아웃풋이 나올 것이다.

tree 78b63e73cb6a5dabdad47d1168dbbc0b7a405db4 
parent c104d14008cdcef613ec0ef06bdd8af708db0d8b 
author Basem <basem.suleiman@sydney.edu.au> 1532582657 +1000 
committer Basem <basem.suleiman@sydney.edu.au> 1532582657 +1000 

add course name to hello.txt  

첫 커밋과 다르게 이번에는 parent commit object에 대한 정보도 있다. 깃이 내용의 변경에 대한 히스토리를 추적하는 방법이 이것이다. 모든 커밋들은 그것의 부모 포인터로 연결되어 있다. 링크드 리스트 구조를 갖으며 이 리스트의 헤드는 외부 파일이 포인팅하고 있다. 즉, 우리의 레포지토리 커밋 히스토리는 head -> c2 -> c1인 것이다.

때때로는 파일들을 staging area에 먼저 추가하지 않고 커밋하고 싶을 때도 있을 것이다. 이 때는 git commit-a flag을 달아주면 git add를 건너뛰고 커밋할 수 있다.

Checking commit logs

모든 변경 사항들을 커밋하였으면, 이제 어떤 일들이 일어났는지를 다시 확인하고 싶을 수도 있다. 커밋 히스토리는 다음과 같이 확인할 수 있다.

git log

다음과 같은 아웃풋을 볼 수 있을 것이다.

Author: Sui <sui.man@google.com>
Date: Sun May 12 20:23:21 2021 +1000


  add course name to course.txt

commit af55a4046cfc58804565fae37683fc902fb008ec
Author: Sui <sui.man@google.com>
Date: Sun May 12 20:01:11 2021 +1000


  My First Commit

git log는 디폴트로 모든 커밋을 시간 역순으로 나열한다. 다음과 같은 flag를 사용하여 제한 사항을 둘 수도 있다.

예를 들어,

git log -n

은 최근 n개의 커밋만 표시하고,

git log -n -p

는 최근 n개의 커밋, 그리고 각 커밋에 대하여 그것의 전 커밋과의 차이점을 보여준다.

git log tool의 몇 가지 데코레이션 기능도 있다. 예를 들어 그래프를 커맨드 라인에 그릴 수 있다.

git log --graph

다음과 같은 아웃풋이 나올 것이다.

* commit e7432094641cfbf534295c95d4114df887f732df (HEAD -> master) 
| Author: Sui <sui.man@google.com>
| Author: Sui <sui.man@google.com>
| Date:   Sun Jul 21 20:18:24 2019 +1000
| 
|     add course name to hello.txt 
|
* commit af55a4046cfc58804565fae37683fc902fb008ec 
  Author: Sui <sui.man@google.com>
  Date:   Sun Jul 21 20:01:49 2019 +1000 

      My First Commit 

위와 같은 경우는 매우 단순한 예시이고, 더 많은 커밋을 가진 레포지토리들을 더 복잡하고 지저분한 아웃풋이 나올 수 있다. 커밋의 아웃풋을 좀 더 간략화하기 위해, --oneline이나 --decorate flag를 사용할 수 있다.

NOTE: 현재 2.14.4 버전 이후의 git에서는 디폴트로 –decorate으로 로그가 나오기 때문에, git loggit log --decorate 아웃풋이 같은 것을 확인할 수 있다.

git log --graph --oneline --decorate

를 실행하면 다음과 같이 나온다.

* e743209 (HEAD -> master) add course name to hello.txt
* af55a40 My First Commit 

좀 더 큰 프로젝트로 해보면 다음과 같이 나온다.

* 0e25143 (HEAD, master) Merge branch 'feature' 
|\  
| * 16b36c6 Fix a bug in the new feature  
| * 23ad9ad Start a new feature
* | ad8621a Fix a critical security issue
|/
* 400e4b7 Fix typos in the documentation
* 160e224 Add the initial code base

깃은 reference logs, 혹은 reflogs라는 메커니즘을 사용하여 브랜치에 대한 업데이트 또한 추적한다. 각 git ref에 대해, 레포지토리에서의 업데이트를 보여주는 reflog가 있다. 다음과 같이 실행한다.

git reflog

HEAD ref가 모든 커밋들이 생기는 과정 동안 어떻게 업데이트 되었는지 보려면,

git reflog show HEAD

를 하면 된다.

git reflog를 실행하면,

3557078 (HEAD -> master) HEAD@{0}: commit: Committed unit.txt and unit.bak
9643a7c HEAD@{1}: commit (initial): My First Commit

다음과 같이 커밋들이 어떻게 가르키고 있는지 포인터 정보가 나오고, 특정 ref에 대해, 예를 들어 HEAD ref에 대해 보려면 git reflog show HEAD를 한다. 그러면 위와 동일하게 나오고, 만약 제일 최근 커밋의 ref를 보려면 git reflog show HEAD@{0}를 해본다. 또 위와 동일하게 나온다. 만약, 첫 커밋을 기준으로 보려면 git reflog show HEAD@{1}을 한다.

그럴 경우,

9643a7c HEAD@{1}: commit (initial): My First Commit

다음과 같이 나오고, 만약 git reflog show HEAD@{2}를 해보면, 이는 존재하지 않기 떄문에 다음과 같은 오류가 뜬다.

fatal: log for 'HEAD' only has 2 entries

히스토리에 두 개의 커밋밖에 없기 때문에 당연한 결과이다.

Categories:

Updated:

Leave a comment