[2장]Git의 기초 - 수정하고 저장소에 저장하기

수정하고 저장소에 저장하기

워킹 디렉토리의 모든 파일은 아래 두 가지로 나뉜다.

  • Tracked(관리대상)

    이미 스냅샷에 포함돼 있던 파일이다. Tracked는 아래 세 가지 상태 중 하나이다.

    • Unmodified(수정하지 않음)
    • Modified(수정함)
    • Staged(커밋으로 저장소에 기록할)
  • Untracked(관리대상 아님)

    Tracked 가 아닌 모든 파일

    워킹 디렉토리에 있는 파일 중 스냡샷에도 Staging Area에도 포함되지 않은 파일을 말한다.

처음 저장소를 Clone 하면 모든 파일은 Tracked 이면서 Unmodified 상태이다. 파일을 Checkout 한 후 아무것도 수정하지 않았기 때문이다.

마지막 커밋 이후 아직 아무것도 수정하지 않은 상태에서 어떤 파일을 수정하면 Git은 그 파일을 Modified 상태로 인식한다. 실제로 커밋을 하기 위해서는 이 수정한 파일을 Staged 상태로 만들고, Staged 상태의 파일을 커밋한다. 이런 라이프사이클을 계속 반복 하는 것이다.

 

파일의 라이프사이클

파일의 라이프사이클

파일 상태 확인하기(git status)

최초 Clone 후 git status 를 실행하면 아래와 같은 메시지를 볼 수 있다.

$ cd /d/Projects/libgit2
$ git status
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

이 내용은 파일을 아직 하나도 수정하지 않았다는 것을 말해준다. Tracked파일이 하나도 수정되지 않았다는 의미다.

Untracked파일은 아직 없어서 목록에 나타나지 않는다. 그리고 현재 작업 중인 브랜치를 알려주며 서버의 같은 브랜치로부터 진행된 작업이 없는 것을 나타낸다. 기본 브랜치가 master 이기 때문에 현재 브랜치 이름이 master 로 나온다.

프로젝트에 README 파일을 만들어보자. READMD 파일은 새로 만든 파일이기 때문에 git status를 실행하면 Untracked files 에 들어 있다.

$ echo 'My Project' > README
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        README

nothing added to commit but untracked files present (use "git add" to track)

README 파일은 Untracked file 부분에 속해 있는데 이것은 README 파일이 Untracked 상태라는 것을 말한다. Git은 Untracked 파일을 아직 스냅샷(커밋)에 넣어지지 않은 파일이라고 본다. 파일이 Tracked 상태가 되기 전까지는 Git은 절대 그 파일을 커밋하지 않는다. 그래서 일하면서 생성하는 바이너리 파일 같은 것을 커밋하는 실수는 하지 않게 된다. README 파일을 추가해서 직접 Tracked 상태로 만들어 보자.

파일을 새로 추적하기(git add)

git add 명령을 실행하면 git은 README 파일을 추적하기 시작한다.

$ git add README
warning: LF will be replaced by CRLF in README.
The file will have its original line endings in your working directory

git status 명령어로 상태를 확인

$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   README

이제 README 파일이 Tracked 상태이면서 커밋에 추가될 Staged 상태라는 것을 확인할 수 있다.

Changes to be committed 에 들어 있는 파일은 Staged 상태라는 것을 의미한다. 커밋하면 git add 를 실행한 시점의 파일이 커밋되어 저장소 히스토리에 남는다.

앞에서 git init 명령을 실행 후, git add (files) 명령을 실행했던 걸 기억할 것이다. 이 명령을 통해 디렉토리에 있는 파일을 추적하고 관리하도록 한다. git add 명령은 파일 또는 디렉토리의 경로를 아규먼트로 받는다. 만약 디렉토리라면 디렉토리 하위에 있는 모든 파일들까지 재귀적으로 추가한다.

Modified 상태의 파일을 Stage 하기

README.md 라는 파일을 수정하고 나서 git status 명령을 다시 실행

$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   README

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   README.md

README.md 파일은 Changes not staged for commit 에 있다. 이것은 수정한 파일이 Tracked 상태이지만 아직 Staged 상태는 아니라는 것이다. 이것을 Staged 상태로 만들려면 git add 명령을 실행해야 한다. git add 명령은 파일을 새로 추적할 때도 사용하고, 수정한 파일을 Staged 상태로 만들 때도 사용한다. Merge 할 때 충돌난 상태의 파일을 Resolve 상태로 만들때도 사용한다. **add 의 의미는 프로젝트에 파일을 추가한다기 보다는 다음 커밋에 추가한다고 받아들이는게 좋다.**

git add 명령을 실행해 README.md 파일을 Staged 상태로 만들고 git status 로 결과를 확인해 보자.

$ git add README.md
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   README
        modified:   README.md

두 파일 모두 Staged 상태이므로 다음 커밋에 포함된다. 하지만 아직 더 수정해야 한다는 것을 알게 되어 바로 커밋하지 못하는 상황이 되었다고 생각해 보자. 이 상황에서 README.md 파일을 다시 열고 수정한다. 이제 커밋할 준비가 다 됐다고 생각할 테지만 Git은 그렇지 않다. 다시 git status 명령으로 파일의 상태를 보자.

$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   README
        modified:   README.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   README.md

README.mdStaged 상태이면서 동시에 Unstaged 상태로 나온다.😱 어떻게 이런 일이 가능할까?

git add 명령을 실행하면 Git은 파일을 바로 Staged 상태로 만든다. 지금 이 시점에서 커밋을 하면 git commit 명령을 실행하는 시점의 버전이 커밋되는 것이 아니라 마지막으로 git add 명령을 실행했을 때의 버전이 커밋된다. 그러니까 git add 명령을 실행한 후 또 파일을 수정했다면 다시 git add 명령을 실행해 최신 버전을 Staged 상태로 만들어야 한다.

$ git add README.md
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   README
        modified:   README.md

파일 상태를 짤막하게 확인하기

git status 보다 간단하게 변경된 내용을 확인 할 수 있는 옵션. -s 또는 --short

임시로 LICENSE.txt 파일을 생성 후 옵션을 붙여 변경된 내용을 확인해 보자.

$ echo '라이센스 추가' > LICENSE.txt
$ git status -s
A  README
M  README.md
?? LICENSE.txt

짧게 보기의 결과는 상태정보 컬럼에서 두 가지 정보를 보여준다.

           A                           README

Staging Arae 에서의 상태        Working Tree에서의 상태

왼쪽의 기호가 의미하는 것

?? : 아직 추가(git add)하지 않은 파일

A : 새로 생성된 파일이면서 Staged 된 상태

M : 수정한 파일

▶ 예제

$ git status -s
 M README
MM Rakefile
A lib/git.rb
M lib/simplegit.rb
?? LICENSE.txt

README 파일 같은 경우 내용을 변경했지만 아직 Staged 상태로 추가하지는 않았다. lib/simplegit.rb 파일은 내용을 변경하고 Staged 상태로 추가까지 한 상태이다. 위 결과에서 차이점을 비교해보자. Rakefile 은 변경하고 Staged 상태로 추가한 후 또 내용을 변경해서 Staged 이면서 Unstaged 상태인 파일이다.

파일 무시하기

로그파일, 빌드 시스템, 중요 시스템 파일 등은 Git이 관리할 필요가 없다. 그런 파일을 무시하려면 .gitignore 파일을 만들고 그 안에 무시할 파일 패턴을 적는다.

패턴 규칙

  • 아무것도 없는 라인이나, #(주석)로 시작하는 라인은 무시한다.
  • 표준 Glob 패턴(정규표현식을 단순하게 만든 것)을 사용한다.
  • 슬래시(/)로 시작하면 하위 디렉토리에 적용되지(Recursivity) 않는다.
  • 디렉토리는 슬래시(/)를 끝에 사용하는 것으로 표현한다.
  • 느낌표(!)로 시작하는 패턴의 파일은 무시하지 않는다.

▶ 예제

# 확장자가 .a인 파일 무시
*.a

# 윗 라인에서 확장자가 .a인 파일은 무시하게 했지만 lib.a는 무시하지 않음
!lib.a

# 현재 디렉토리에 있는 TODO파일은 무시하고 subdir/TODO처럼 하위디렉토리에 있는 파일은 무시하지 않음
/TODO

# build/ 디렉토리에 있는 모든 파일은 무시
build/

# doc/notes.txt 파일은 무시하고 doc/server/arch.txt 파일은 무시하지 않음
doc/*.txt

# doc 디렉토리 아래의 모든 .pdf 파일을 무시
doc/**/*.pdf

GitHub은 다양한 프로젝트에서 자주 사용하는 .gitignore 예제를 관리하고 있다. 아래 두 사이트를 활용하라.

Staged와 Unstaged 상태의 변경 내용을 보기

단순히 파일이 변경됐다는 사실이 아니라 어떤 내용이 변경됐는지 살펴보려면 git diff 명령을 사용해야 한다. 어떤 라인이 추가됐고, 삭제되었는지 궁금할 때 사용하는 것이다.

$ vi README.md

# README.md 파일에 아래 내용 추가
이것을 추가했어요.

$ git diff
diff --git a/README.md b/README.md
index a4ea79c3b..bb83eea72 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,7 @@
 ==================================

 ## Test message
+이것을 추가했어요.

 | Build Status | |
 | ------------ | - |

이 명령은 워킹 디렉토리에 있는 것과 Staging Area에 있는 것을 비교한다. 그래서 수정하고 아직 Stage 하지 않은 것(Unstaged)을 보여준다.

만약 커밋하려고 Staging Area에 넣을 파일의 변경 부분을 보고 싶으면 git diff --staged 옵션을 사용한다.

$ git diff --staged
diff --git a/README b/README
new file mode 100644
index 000000000..56266d360
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+My Project
...이하 생략

git diff명령은 마지막으로 커밋한 후에 수정한 것들 전부를 보여주지 않는다. git diffUnstaged상태인 것들만 보여준다. 이 부분이 조금 헷갈릴 수 잇다. 수정한 파일을 모두 Staging Area에 넣었다면 git diff명령은 아무것도 출력하지 않는다.

README.md 파일을 Stage 한 후에 다시 수정해도 git diff 명령을 사용할 수 있다. 이 때는 Staged 상태인 것과 Unstaged 상태인 것을 비교한다.

$ git add README.md
$ echo '# test line' >> README.md
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   README
        modified:   README.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   README.md

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        LICENSE.txt

$ git diff
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory
diff --git a/README.md b/README.md
index bb83eea72..f0598531e 100644
--- a/README.md
+++ b/README.md
@@ -392,3 +392,4 @@ gratis.  However, if you modify libgit2 itself, you must distribute the
 source to your modified version of libgit2.

 See the [COPYING file](COPYING) for the full license text.
+# test line

Staged 상태인 파일은 git diff --cached 옵션으로 확인한다. --staged--cached 는 같은 옵션이다.

$ git diff --cached
diff --git a/README b/README
new file mode 100644
index 000000000..56266d360
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+My Project
diff --git a/README.md b/README.md
index ef85fd9f6..bb83eea72 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,9 @@
-libgit2 - the Git linkable library
+# libgit2 - the Git linkable library
 ==================================

+## Test message
+이것을 추가했어요.
+
 | Build Status | |
 | ------------ | - |
 | **master** branch CI builds | [![Azure Pipelines Build Status](https://dev.azure.com/libgit2/libgit2/_apis/build/status/libgit2?branchName=master)](https://dev.azure.com/libgit2/libgit2/_build/latest?definitionId=7&branchName=master)   |

변경사항 커밋하기

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

그 후에 git commit 을 실행하여 커밋한다.

$ git commit

Git 설정에서 지정된 편집기가 실행되고, 아래와 같은 텍스트가 자동으로 포함된다. (아래 예제는 Vim 편집기 화면이다.) 편집기는 앞에서 설정한 것 처럼 git config --global core.editor 명령으로 어떤 편집기를 사용할지 설정할 수 있다

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
# Your branch is up to date with 'origin/master'.
#
# Changes to be committed:
#       new file:   README
#       modified:   README.md
#
# Changes not staged for commit:
#       modified:   README.md
#
# Untracked files:
#       LICENSE.txt
#
~
~
~
D:/Projects/libgit2/.git/COMMIT_EDITMSG [unix] (21:30 07/03/2020)

자동으로 생성되는 커밋 메시지의 첫 라인은 비어 있고 둘째 라인부터 git status 명령의 결과가 채워진다. 커밋한 내용을 쉽게 기억할 수 있도록 이 메시지를 포함할 수도 있고 메시지를 전부 지우고 새로 작성할 수 있다 (정확히 뭘 수정했는지도 보여줄 수 있는데, git commit-v 옵션을 추가하면 편집기에 diff 메시지도 추가된다). 내용을 저장하고 편집기를 종료하면 Git은 입력된 내용(#로 시작하는 내용을 제외한)으로 새 커밋을 하나 완성한다.

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

$ git commit -m "Story 182: Fix benchmarks for speeed"
[master da8a65ee0] Story 182: Fix benchmarks for speeed
 2 files changed, 5 insertions(+), 1 deletion(-)
 create mode 100644 README

commit 명령은 몇 가지 정보를 출력하는데 위 예제는 (master) 브랜치에 커밋했고 체크섬은 (da8a65ee0)이라고 알려준다. 그리고 수정한 파일이 몇 개이고 삭제됐거나 추가된 라인이 몇 라인인지 알려준다.
Git은 Staging Area에 속한 스냅샷을 커밋한다. 수정은 했지만, 아직 Staging Area에 넣지 않은 것은 다음에 커밋할 수 있다. 커밋할 때마다 프로젝트의 스냅샷을 기록하기 때문에 나중에 스냅샷끼리 비교하거나 예전 스냅샷으로 되돌릴 수 있다

$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   README.md

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        LICENSE.txt

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

Staging Area 생략하기

git add 를 생략하고 바로 커밋하기. -a 옵션 사용

$ git commit -a -m "Add new benchmarks"
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory
[master 14c69c4e2] Add new benchmarks
 1 file changed, 1 insertion(+)

이 예제에서는 커밋하기 전에 git add 명령으로 README.md 파일을 추가하지 않았다는 점을 눈여겨보자. -a 옵션을 사용하면 모든 파일이 자동으로 추가된다. 💥편리한 옵션이긴 하지만 주의 깊게 사용해야 한다. 생각 없이 이 옵션을 사용하다 보면 추가하지 말아야 할 변경사항도 추가될 수 있기 때문이다.

상태 확인

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        LICENSE.txt

nothing added to commit but untracked files present (use "git add" to track)

파일 삭제하기

Git에서 파일을 제거하려면 git rm 명령으로 Tracked 상태의 파일을 삭제한 후에(정확하게는 Staging Area 에서 삭제하는 것) 커밋해야 한다. 🚩이 명령은 워킹 디렉토리에 있는 파일도 삭제하기 때문에 실제로 파일도 지워진다.

Git 명령을 사용하지 않고 단순히 워킹 디렉터리에서 파일을 삭제하고 git status 명령으로 상태를 확인하면 Git은 현재 Changes not staged for commit(즉, Unstaged 상태)라고 표시해준다.

$ rm LICENSE.txt
$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   LICENSE.txt

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        deleted:    LICENSE.txt

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

$ git rm LICENSE.txt
rm 'LICENSE.txt'

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

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

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

$ git rm --cached README
rm 'README'

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        deleted:    README

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        README

실제 파일은 유지되고 Git VCS에서만 삭제되었다.

여러 개의 파일이나 디렉토리를 한꺼번에 삭제할 수도 있다. 아래와 같이 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의 상태를 확인해보면 Git은 이름이 바뀐 사실을 알고 있다.

$ git mv README.md README
$ git status
On branch master
Your branch is up-to-date with 'origin/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 명령을 실행해야 한다는 것 뿐이다.

'TIL > GIT' 카테고리의 다른 글

[2장]되돌리기  (0) 2020.03.10
[2장]커밋 히스토리 조회하기  (0) 2020.03.09
좋은 git 커밋 메시지를 작성하기 위한 8가지 약속  (0) 2020.03.08
[2장]Git 기초 - 저장소 만들기  (0) 2020.03.07
[1장]GIT 시작하기  (0) 2020.03.06

+ Recent posts