본문 바로가기
Book Study/Pro Git

[Pro Git] ch02 - Git의 기초 2 (commit, log, 변경 및 삭제)

by 랩린안 2022. 10. 1.

출처 : Pro Git

- 저자 : Scott Chacon, Ben Straub

 

 

 

변경사항 커밋하기

수정한 것을 커밋하기 위해 Staging Area에 파일을 정리했다. Unstaged 상태의 파일은 커밋되지 않는다는 것을 기억해야 한다. Git은 생성하거나 수정하고 나서 git add 명령으로 추가하지 않은 파일은 커밋하지 않는다. 그 파일은 여 전히 Modified 상태로 남아 있다. 커밋하기 전에 git status 명령으로 모든 것이 Staged 상태인지 확인할 수 있다. 그 후에 git commit을 실행하여 커밋 한다.

 

 git commit

 

메시지를 인라인으로 첨부할 수도 있다. commit 명령을 실행할 때 아래와 같이 -m 옵션을 사용한다.

 

$ git commit -m "Story 182: Fix benchmarks for speed"
[master 463dc4f] Story 182: Fix benchmarks for speed
 2 files changed, 2 insertions(+)
 create mode 100644 README

이렇게 첫번째 커밋을 작성해보았다. commit 명령은 몇 가지 정보를 출력 하는데 위 예제는 (master) 브랜치에 커밋했고 체크섬은 (463dc4f)이라고 알 려준다. 그리고 수정한 파일이 몇 개이고 삭제됐거나 추가된 라인이 몇 라인 인지 알려준다.

 

Git은 Staging Area에 속한 스냅샷을 커밋한다는 것을 기억해야 한다. 수정 은 했지만, 아직 Staging Area에 넣지 않은 것은 다음에 커밋할 수 있다. 커밋할 때마다 프로젝트의 스냅샷을 기록하기 때문에 나중에 스냅샷끼리 비교하거 나 예전 스냅샷으로 되돌릴 수 있다

 

Staging Area 생략하기

Staging Area는 커밋할 파일을 정리한다는 점에서 매우 유용하지만 복잡하 기만 하고 필요하지 않은 때도 있다. 아주 쉽게 Staging Area를 생략할 수 있다. git commit 명령을 실행할 때 -a 옵션을 추가하면 Git은 Tracked 상태의 파일 을 자동으로 Staging Area에 넣는다. 그래서 git add 명령을 실행하는 수고를 덜 수 있다.

 

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: benchmarks.rb
no changes added to commit (use "git add" and/or "git commit -a")
$ git commit -a -m 'added new benchmarks'
[master 83e38c7] added new benchmarks
 1 file changed, 5 insertions(+), 0 deletions(-)

이 예제에서는 커밋하기 전에 git add 명령으로 “benchmarks.rb” 파일을 추가하지 않았다는 점을 눈여겨보자.

 

파일 삭제하기

 

Git에서 파일을 제거하려면 git rm 명령으로 Tracked 상태의 파일을 삭제 한 후에(정확하게는 Staging Area에서 삭제하는 것) 커밋해야 한다. 이 명령은 워킹 디렉토리에 있는 파일도 삭제하기 때문에 실제로 지워진다. 만약 Git없이 그냥 파일을 삭제하고 git status 명령으로 상태를 확인하 면 “Changes not staged for commit”(즉, Unstaged) 에 속한다는 것을 확인할 수 있다.

 

 rm grit.gemspec
$ git status

On branch master
Changes not staged for commit:
 (use "git add/rm <file>..." to update what will be committed)
 (use "git checkout -- <file>..." to discard changes in working directory)
 deleted: grit.gemspec
no changes added to commit (use "git add" and/or "git commit -a")

그리고 git rm 명령을 실행하면 삭제한 파일은 Staged 상태가 된다.

 

$ git rm grit.gemspec
rm 'grit.gemspec'
$ git status
On branch master
Changes to be committed:
 (use "git reset HEAD <file>..." to unstage)
 deleted: grit.gemspec

커밋하면 파일은 삭제되고 Git은 이 파일을 더는 추적하지 않는다. 이미 파 일을 수정했거나 Index에(역주 - Staging Area을 Git Index라고도 부른다) 추가 했다면 -f옵션을 주어 강제로 삭제해야 한다. 이 점은 실수로 데이터를 삭제 하지 못하도록 하는 안전장치다. 한 번도 커밋한적 없는 데이터는 Git으로 복 구할 수 없다

 

또 Staging Area에서만 제거하고 워킹 디렉토리에 있는 파일은 지우지 않고 남겨둘 수 있다. 다시 말해서 하드디스크에 있는 파일은 그대로 두고 Git만 추 적하지 않게 한다. 이것은 .gitignore 파일에 추가하는 것을 빼먹었거나 대 용량 로그 파일이나 컴파일된 파일인 .a 파일 같은 것을 실수로 추가했을 때 쓴다. --cached 옵션을 사용하여 명령을 실행한다.

 

git rm --cached README

여러 개의 파일이나 디렉토리를 한꺼번에 삭제할 수도 있다. 아래와 같이 git rm 명령에 file-glob 패턴을 사용한다.

 

여러 개의 파일이나 디렉토리를 한꺼번에 삭제할 수도 있다. 아래와 같이 git rm 명령에 file-glob 패턴을 사용한다

 

$ git rm log/\*.log

*앞에 \을 사용한 것을 기억하자. 파일명 확장 기능은 쉘에만 있는 것이 아 니라 Git 자체에도 있기 때문에 필요하다. 이 명령은 log/ 디렉토리에 있 는 .log 파일을 모두 삭제한다. 아래의 예제처럼 할 수도 있다.

 

 

$ git rm \*~

이 명령은 ~로 끝나는 파일을 모두 삭제한다.

 

파일 이름 변경하기

 

Git은 다른 VCS 시스템과는 달리 파일 이름의 변경이나 파일의 이동을 명시 적으로 관리하지 않는다. 다시 말해서 파일 이름이 변경됐다는 별도의 정보를 저장하지 않는다. Git은 똑똑해서 굳이 파일 이름이 변경되었다는 것을 추적 하지 않아도 아는 방법이 있다. 파일의 이름이 변경된 것을 Git이 어떻게 알아 내는지 살펴보자. 이렇게 말하고 Git에 mv 명령이 있는 게 좀 이상하겠지만, 아래와 같이 파일 이름을 변경할 수 있다

 

$ git mv file_from file_to
$ git mv README.md README
$ git status
On branch master
Changes to be committed:
 (use "git reset HEAD <file>..." to unstage)
 renamed: README.md -> README

git mv 명령은 아래 명령어를 수행한 것과 완전 똑같다

$ mv README.md README
$ git rm README.md
$ git add README

git mv는 일종의 단축 명령어이다. 이 명령으로 파일이름을 바꿔도 되고 mv 명령으로 파일이름을 직접 바꿔도 된다. 단지 Git의 mv명령은 편리하게 명 령을 세 번 실행해주는 것뿐이다.

 

중요 한 것은 이름을 변경하고 나서 꼭 rm/add 명령을 실행해야 한다는 것뿐이다.

 

 

커밋 히스토리 조회하기

새로 저장소를 만들어서 몇 번 커밋을 했을 수도 있고, 커밋 히스토리가 있는 저장소를 Clone 했을 수도 있다. 어쨌든 가끔 저장소의 히스토리를 보고 싶을 때가 있다. Git에는 히스토리를 조회하는 명령어인 git log가 있다. 이 예제에서는 “simplegit” 이라는 매우 단순한 프로젝트를 사용한다. 아래 와 같이 이 프로젝트를 Clone 한다.

 

이 프로젝트 디렉토리에서 git log 명령을 실행하면 아래와 같이 출력된 다.

commit ca82a6dff817ec66f44342007202690a93763949 (HEAD -> master, origin/master, origin/HEAD)
Author: Scott Chacon <schacon@gmail.com>
Date:   Mon Mar 17 21:52:11 2008 -0700

    changed the verison number

commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon <schacon@gmail.com>
Date:   Sat Mar 15 16:40:33 2008 -0700

    removed unnecessary test code

commit a11bef06a3f659402fe7563abf99ad00de2209e6
Author: Scott Chacon <schacon@gmail.com>
Date:   Sat Mar 15 10:31:28 2008 -0700

    first commit

 

특별한 아규먼트 없이 git log 명령을 실행하면 저장소의 커밋 히스토리 를 시간순으로 보여준다. 즉, 가장 최근의 커밋이 가장 먼저 나온다. 그리고 이 어서 각 커밋의 SHA-1 체크섬, 저자 이름, 저자 이메일, 커밋한 날짜, 커밋 메시 지를 보여준다. 원하는 히스토리를 검색할 수 있도록 git log 명령은 매우 다양한 옵션을 지원한다.

 

여기에서는 자주 사용하는 옵션을 설명한다.

 

여러 옵션 중 -p는 굉장히 유용한 옵션이다.

-p는 각 커밋의 di€ 결과를 보 여준다.

다른 유용한 옵션으로 -2가 있는데 최근 두 개의 결과만 보여주는 옵 션이다

 

 

 git log -p -2
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date: Mon Mar 17 21:52:11 2008 -0700
 changed the version number
diff --git a/Rakefile b/Rakefile
index a874b73..8f94139 100644
--- a/Rakefile
+++ b/Rakefile
@@ -5,7 +5,7 @@ require 'rake/gempackagetask'
 spec = Gem::Specification.new do |s|
 s.platform = Gem::Platform::RUBY
 s.name = "simplegit"
- s.version = "0.1.0"
+ s.version = "0.1.1"
 s.author = "Scott Chacon"
 s.email = "schacon@gee-mail.com"
 s.summary = "A simple gem for using Git in Ruby code."
commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon <schacon@gee-mail.com>
Date: Sat Mar 15 16:40:33 2008 -0700
 removed unnecessary test
diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index a0a60ae..47c6340 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -18,8 +18,3 @@ class SimpleGit
 end
 end
-
-if $0 == __FILE__
- git = SimpleGit.new
- puts git.show
-end
\ No newline at end of file

이 옵션은 직접 di€를 실행한 것과 같은 결과를 출력하기 때문에 동료가 무 엇을 커밋했는지 리뷰하고 빨리 조회하는데 유용하다. 또 git log 명령에는 히스토리의 통계를 보여주는 옵션도 있다.

--stat 옵션으로 각 커밋의 통계 정보를 조회할 수 있다.

 

 

 

이 결과에서 --stat 옵션은 어떤 파일이 수정됐는지, 얼마나 많은 파일이 변경됐는지, 또 얼마나 많은 라인을 추가하거나 삭제했는지 보여준다. 요약정 보는 가장 뒤쪽에 보여준다. 

 

다른 또 유용한 옵션은 --pretty 옵션이다. 이 옵션을 통해 히스토리 내용 을 보여줄 때 기본 형식 이외에 여러 가지 중에 하나를 선택할 수 있다. 몇개 선 택할 수 있는 옵션의 값이 있다. oneline 옵션은 각 커밋을 한 라인으로 보여 준다. 이 옵션은 많은 커밋을 한 번에 조회할 때 유용하다. 추가로 short, full, fuller 옵션도 있는데 이것은 정보를 조금씩 가감해서 보여준다.

 

$ git log --pretty=oneline
ca82a6dff817ec66f44342007202690a93763949 changed the version number
085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 removed unnecessary test
a11bef06a3f659402fe7563abf99ad00de2209e6 first commit

 

가장 재밌는 옵션은 format 옵션이다. 나만의 포맷으로 결과를 출력하고 싶을 때 사용한다. 특히 결과를 다른 프로그램으로 파싱하고자 할 때 유용하 다. 이 옵션을 사용하면 포맷을 정확하게 일치시킬 수 있기 때문에 Git을 새 버 전으로 바꿔도 결과 포맷이 바뀌지 않는다.

 

 

$ git log --pretty=format:"%h - %an, %ar : %s"
ca82a6d - Scott Chacon, 6 years ago : changed the version number
085bb3b - Scott Chacon, 6 years ago : removed unnecessary test
a11bef0 - Scott Chacon, 6 years ago : first commit

저자(Author) 와 커미터(Committer) 를 구분하는 것이 조금 이상해 보일 수 있다. 저자는 원래 작업을 수행한 원작자이고 커밋터는 마지막으로 이 작업을

 

적용한(저장소에 포함시킨) 사람이다. 만약 당신이 어떤 프로젝트에 패치를 보냈고 그 프로젝트의 담당자가 패치를 적용했다면 두 명의 정보를 모두 알 필요가 있다. 그래서 이 경우 당신이 저자고 그 담당자가 커미터다.

 

oneline과 format 옵션은 --graph 옵션과 함께 사용할 때 더 빛난다. 이 명령은 브랜치와 머지 히스토리를 보여주는 아스키 그래프를 출력한다.

 

$ git log --pretty=format:"%h %s" --graph
* 2d3acf9 ignore errors from SIGCHLD on trap
* 5e3ee11 Merge branch 'master' of git://github.com/dustin/grit
|\
| * 420eac9 Added a method for getting the current branch.
* | 30e367c timeout code and tests
* | 5a09431 add timeout protection to grit
* | e1193f8 support for heads with slashes in them
|/
* d6016bc require time for xmlschema
* 11d191e Merge branch 'defunkt' into local

다음 장에서 살펴볼 브랜치나 Merge 결과를 히스토리를 이런 식으로 살펴 보면 훨씬 흥미롭게 볼 수 있다. git log 명령의 기본적인 옵션과 출력물의 형식에 관련된 옵션을 살펴보 았다. git log 명령은 앞서 살펴본 것보다 더 많은 옵션을 지원한다

 

 

되돌리기

 

Git을 사용하면 우리가 한 실수를 복구하지 못할 것은 거의 없지만 되돌린 것은 복구할 수 없다. 종종 완료한 커밋을 수정해야 할 때가 있다. 너무 일찍 커밋했거나 어떤 파 일을 빼먹었을 때 그리고 커밋 메시지를 잘못 적었을 때 한다. 다시 커밋하고 싶으면 --amend 옵션을 사용한다.

 

$ git commit --amend

이 명령은 Staging Area를 사용하여 커밋한다. 만약 마지막으로 커밋하고 나서 수정한 것이 없다면(커밋하자마자 바로 이 명령을 실행하는 경우) 조금 전에 한 커밋과 모든 것이 같다. 이때는 커밋 메시지만 수정한다. 편집기가 실행되면 이전 커밋 메시지가 자동으로 포함된다. 메시지를 수정 하지 않고 그대로 커밋해도 기존의 커밋을 덮어쓴다. 커밋을 했는데 Stage 하는 것을 깜빡하고 빠트린 파일이 있으면 아래와 같 이 고칠 수 있다.

 

$ git commit -m 'initial commit' 
$ git add forgotten_file 
$ git commit --amend

여기서 실행한 명령어 3개는 모두 하나의 커밋으로 기록된다. 두 번째 커밋 은 첫 번째 커밋을 덮어쓴다.

 

 

파일 상태를 Unstage로 변경하기

다음은 Staging Area와 워킹 디렉토리 사이를 넘나드는 방법을 설명한다.

두 영역의 상태를 확인할 때마다 변경된 상태를 되돌리는 방법을 알려주기 때문 에 매우 편리하다.

예를 들어 파일을 두 개 수정하고서 따로따로 커밋하려고 했지만, 실수로 git add * 라고 실행해 버렸다. 두 파일 모두 Staging Area에 들어 있다. 이제 둘 중 하나를 어떻게 꺼낼까? 우선 git status 명령으로 확 인해보자

 

$ git add .
$ git status
On branch master
Changes to be committed:
 (use "git reset HEAD <file>..." to unstage)
 renamed: README.md -> README
 modified: benchmarks.rb

Changes to be commited 밑에 git reset HEAD ... 메시지를 볼 수 있다. 이 명령으로 Unstated 상태로 변경할 수 있다. benchmarks.rb 파일 을 Unstated 상태로 변경해보자

 

$ git reset HEAD benchmarks.rb
Unstaged changes after reset:
M benchmarks.rb
$ git status
On branch master
Changes to be committed:
 (use "git reset HEAD <file>..." to unstage)
 renamed: README.md -> README
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: benchmarks.rb

. benchmarks.rb 파일은 Unstated 상태가 됐다.

 

it reset 명령을 --hard 옵션과 함께 사용하면 워킹 디렉토리의 파일이 수정돼서 을 조심해야 한다. --hard 옵션만 사용하지 않는다면 git reset 명령은 Staging Area의 파일만 조작하기 때문에 위험하지 않다.

지금까지 살펴본 내용이 git reset 명령에 대해 알아야 할 대부분의 내용 이다. reset 명령이 정확히는 어떻게 동작하는지, 어떻게 전문적으로 활용하 는지는 “Reset 명확히 알고 가기” 부분에서 자세히 살펴보기로 한다.

 

 

# Modified 파일 되돌리기

 

어떻게 해야 benchmarks.rb 파일을 수정하고 나서 다시 되돌릴 수 있을까? 그 러니까 최근 커밋된 버전으로(아니면 처음 Clone 했을 때처럼 워킹 디렉토리 에 처음 Checkout 한 그 내용으로) 되돌리는 방법이 무얼까? git status 명령 이 친절하게 알려준다. 바로 위에 있는 예제에서 Unstaged 부분을 보자.

 

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: benchmarks.rb

위의 메시지는 수정한 파일을 되돌리는 방법을 꽤 정확하게 알려준다.

 

git checkout -- benchmarks.rb
$ git status
On branch master
Changes to be committed:
 (use "git reset HEAD <file>..." to unstage)
 renamed: README.md -> README

정상적으로 복원된 것을 알 수 있다.

 

git checkout -- [file] 명령은 꽤 위험한 명령이라는 것을 알아야 한다. 원래 파 일로 덮어썼기 때문에 수정한 내용은 전부 사라진다. 수정한 내용이 진짜 마음에 들 지 않을 때에만 사용하자.

변경한 내용을 쉽게 버릴수는 없고 하지만 당장은 되돌려야만 하는 상황이 라면 Stash와 Branch를 사용하자