티스토리 뷰

728x90
반응형

2.2 Git Basics - 변경사항을 저장소에 저장하기

변경사항을 저장소에 저장하기

당신은 진짜 Git 저장소를 얻었고, 그 프로젝트를 체크 아웃 받았고, 실제로 사용중인 파일들의 카피본까지 받았다. 당신은 이제 당신이 저장하고 싶은 프로젝트의 상태가 되었을 때마다 그동안의 변경사항의 스냅샵을 당신의 저장소에 커미트를 해야한다.

당신의 working 디렉토리의 각 파일들은 2가지 상태 중 한가지여야 함을 잊지 마라 : 추적됨(tracked) 또는 추적되지않음(untracked). 추적된 파일들은 마지막 스냅샵된 파일들이고, 이것들은 unmodified, modified 또는 staged 가 될수 있다.추적되지 않는 파일들은 working 디렉토리 내에서 마지막 스냅샵에 포함되지 않은 나머지 모든 파일들이며, 그것들은 당신의 staging area에 속해 있지 않다. 당신이 처음 저장소를 clone받았을때, 모든 파일들은 tracked 이고 또 unmodified 상태이다. 왜냐하면 당신은 그저 파일들을 checkout 받았을뿐 수정을 한적이 없기 때문이다.

당신이 파일들을 수정하였다면, Git는 그것들을 modified로 판단할것이다. 왜냐하면 당신이 마지막으로 commit를 한이후로 당신이 변경을 했기 때문이다. 앞으로 당신은 이 변경된 파일들을 stage할것이고, 다시 커밋트 할것이다. 이 사이클은 계속 반복될 것이고 이 라이프사이클은 Figure 2-1에 설명되어 있다.



Figure 2-1. The lifecycle of the status of your files.

Checking the Status of Your Files

당신은 파일들의 상태를 확인하기 위해 주로 git status 명령어를 사용하기 될것이다. 만약 당신이 클론을 받은 이후에 이 명령어를 실행한다면, 다음과 같은 것들을 보게 될것이다:

$ git status
# On branch master
nothing to commit (working directory clean) 
이것은 당신의 working 디렉토리가 아주 깨긋하다는 의미이다.- 다시 말해서, 그곳에는 추적되고 있고 또 수정된 파일이 없다는 것이다. 그리고 Git도 추적되지 않는 그 어떤 파일들을 찾지 못했거나 또는 그것들은 이미 리스트화 되었을 것이다. 마지막으로, 이 명령어는 당신이 어떤 브랜치에 있는지 말해준다. 지금은 언제나 디폴트로 마스터에 있을것이다:당신은 이것에 대해 지금 걱정할 필요없다. 다음 챕터에서는 브랜치와 레퍼런스에 대해 좀더 자세히 알아볼 것이다.

자 새로운 파일("간단한 readme file")을 당신의 프로젝트에 추가했다고 해보자. 만약에 그 파일이 그전에 존재하지 않았었다면, 당신이 git status 실행하면, 추적하고 있는 않은 파일이 다음과 같이 보일것이다:

$ vim README
$ git status
# On branch 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 files"이라는 제목 밑에 그 파일이 포함되어 있기 때문이다. Untracked 라는 것은 기본적으로 Git가 그 이전 스냅샵(커미트)에서 보지 못했던 파일이라는 의미이다.;Git는 당신이 명시적으로 이것에 대해 알려주지 않는한 당신의 커미트 스냅샵에 이 파일을 포함시키고 시작하지 않을 것이다. 이 파일이 자동으로 생성된 파일이나 당신이 의도하지 않게 포함된 파일들이라면 실수로 시작하지 않을 것이다. 자, 당신은 앞으로 readme 파일을 포함해서 추적을 하고 싶으니, 이 파일이 추적되게 만들자.

새 파일을 추적하자

새로운 파일을 추적되게 만드려면, 당신은 git add라는 명령어를 사용해야 한다. readme 파일을 추적하기 위해서는 다음과 같이 실행하라:

$ git add README

이제 status 명령어를 실행시켜보면, 당신은 당신의 readme 파일이 추적되고 있고 또 staged 되었음을 확인할 수 있다.:

$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   README
# 
당신은 이제 이 파일이 staged 되었다고 말할 수 있다. 왜냐하면 이것들이 “Changes to be committed"라는 제목 밑에 있기 때문이다. 당신이 이 시점에서 커밋트를 하면, 당신이 git add를 했을때 붙은 파일의 버젼이 역사적인 스냅샵의 버젼으로 사용될 것이다. 당신은 아마 git init를 이전에 실행했던것을 기억할것이고, 그 이후에 git add (files)을 함으로써 당신의 디렉토리안에 있는 파일들이 추적당하기 시작했다. Git add라는 명령어는 파일이나 디렉토리 둘다에게 사용할 수 있다; 만약에 디렉토리라면, 그 디렉토리 밑에 들어 있는 전체 파일들이 재귀적으로 추가될것이다..

수정된 파일들을 Staging 하기

자, 이제 추적되고 있는 파일들을 수정해보자, 그전에 추적되던 benchmarks.rb 파일을 변경하고, status 명령어를 실행시켜보면 다음과 같은 것을 보게 될것이다:

$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   README
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#   modified:   benchmarks.rb
#

bechmark.rb 파일은 Changed but not updated 섹션 밑에 나타난다. - 이것은 이 파일이 추적되고 있는 상태에서 수정이 되었지만 아직 stage 상태가 아니라는 의미이다. Stage를 하기 위해서는 git add명령어를 사용해야 한다. (이 명령어는 여러가지 의미가 있다. 새로운 파일을 추적되게 만들거나 파일들을 stage되게 하거나 또는 merge-conflict 되었을 때 해결되었다고 마킹할때도 사용한다.) 자, git add를 한 후, git status다시 실행해 보자:

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

두 파일다 이제 staged상태이고 이제 커미트를 할시간이다.이때, 만약에 당신이 커미트하기 전에 bechmark.rb 파일을 약간 수정할것이 생각났다면, 그저 그 파일을 열고 수정하면 된다. 그러나 이후에 git status를 실행해보면 다음과 같은 결과를 볼수있다:

$ vim benchmarks.rb
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   README
#   modified:   benchmarks.rb
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#   modified:   benchmarks.rb
# 
이것은 무엇인가? becnchmark.rb 가 staged와 unstaged 두군데 다 있다. 어떻게 이런게 가능한가?

이것으로 우리는 Git 가 당신이 git add를 실행시키는 순간 정확하게 파일을 stage시키다는 것을 알게 되었다.만약 지금 commit를 하게 되면 당신이 마지막으로 git add를 했을때의 버젼의 benchmark.rb가 커밋되고, working 디렉토리 안에 있는 benchmar.rb는 커밋되지 않는다. 그래서 git add 이후에 파일을 수정했다면, 반드시 git add를 실행시킴으로써 그 파일의 가장 최신 버젼을 stage상태로 만들어야 한다.:

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

파일들을 무시하기

가끔 당신은 Git가 자동으로 add시키거나 추적되고 있다는 것조차 보여주기 싫은 파일이 있을것이다.(보통 자동으로 생성되는 log 파일들이나, 빌드 시스템에 의해 생성되는 파일들이 있다.) 이런 경우에는, 당신은 파일 리스팅 패턴을 만들어 .gitignore라는 파일에 저장하면 된다. 여기에 .gitignore샘플이 있다:

$ cat .gitignore
*.[oa]
*~ 
첫번째 라인은 .o 또는 .a(object, achive)로 끝나는 모든 파일을 무시하라는 의미이고, 두번째 라인은 ~표시로 또는 모든 파일을 무시하라는 의미이다.(보통 Emacs같은 텍스트 에디터들이 생성하는 템프파일이다.) 이외에도 log,tmp, pid 디렉토리같은 경우가 있다. 그래서 다른 것보다 우선적으로 .gitignore 파일을 미리 세팅을 함으로써 실수로 당신이 커밋하고 싶은 않은 파일들을 Git 저장소에 저장하는 일이 없도록 해야한다.

다음은 당신이 .gitignore 파일에 적용할 수 있는 룰이다:

  • Blank lines or lines starting with # are ignored.
  • Standard glob patterns work.
  • You can end patterns with a forward slash (/) to specify a directory.
  • You can negate a pattern by starting it with an exclamation point (!).
Glob patterns 은 마치 셀에서 사용하는 정규화 표현을 간편화한 것과 비슷하다.*(아스타)는 0 또는 더 많은 문자와 매칭된다.[abc]는 []안의 문자들중 하나를 의미한다.?는 단일문자를 의미하고, 괄호안의 -(하이픈)은 범위를 의미한다[0-9].

샘플예제이다:
# a comment - this is ignored
*.a       # no .a files
!lib.a    # but do track lib.a, even though you're ignoring .a files above
/TODO     # only ignore the root TODO file, not subdir/TODO
build/    # ignore all files in the build/ directory
doc/*.txt # ignore doc/notes.txt, but not doc/server/arch.txt

Staged와 Unstaged 변경사항 확인하기

만약에 당신에게 git status라는 명령어가 막연하게 느껴진다면, 다시말해서 파일이 변경되었다, 안되었다 보다 정확하게 무엇이 변경되었는지에 대해서 알고 싶다면, 당신은 git diff라는 명령어를 사용할 수 있다. 우리 나중에 git diff에 대해좀 더 자세히 알아 볼텐데, 아마도 당신은 다음 2가지 질문에 대한 대답을 하기 위해 이 명령어를 아주 자주 사용하게 될것이다. : 변경은 했지만 staged 상태가 아닌 것들은 무엇인가? 이제 커미트를 하기 위해 staged 상태로 만든 것들은 무엇인가? 물론 git status 도 이 두가지 질문에 대해 아주 일반적으로 대답할 수 있지만 git diff는 라인별로 무엇이 추가되었고 무엇이 삭제되었는지 보여준다. 말하자면 패치 같은 것을 알수있다.

자 readme file을 수정하고 stage상태로 만든다음 benchmark.rb 파일을 수정하고 stage상태로 만들지 말아보자. 그리고 status 명령어로 보면, 당신은 다음과 비슷한 것을 다시한번 보게 될것이다:

$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   README
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#   modified:   benchmarks.rb
#

stage상태가 아닌 것들의 변경사항에 대해서 보고 싶다면, git diff라고만 쳐보자:

$ git diff
diff --git a/benchmarks.rb b/benchmarks.rb
index 3cb747f..da65585 100644
--- a/benchmarks.rb
+++ b/benchmarks.rb
@@ -36,6 +36,10 @@ def main
           @commit.parents[0].parents[0].parents[0]
         end

+        run_code(x, 'commits 1') do
+          git.commits.size
+        end
+
         run_code(x, 'commits 2') do
           log = git.commits('master', 15)
           log.size

이 명령어는 stage영역의 것과 working 디렉토리의 것을 비교하고, stage영역에 있지 않은 파일의 변경사항에 대해 말해준다..

만약 당신이 다음에 커미트를 할 stage파일들을 알고 싶다면, git diff --cached를 이용해보자.(Git version 1.6.1버젼 이후부터는 git diff --staged를 사용할 수 있다.) 이 명령어는 당신이 마지막으로 커미트한 것과 stage영역에 있는 파일을 비교해준다:

$ git diff --cached
diff --git a/README b/README
new file mode 100644
index 0000000..03902a1
--- /dev/null
+++ b/README2
@@ -0,0 +1,5 @@
+grit
+ by Tom Preston-Werner, Chris Wanstrath
+ http://github.com/mojombo/grit
+
+Grit is a Ruby library for extracting information from a Git repository

git diff 는 당신이 마지막으로 커미트한 이후에 모든 변경사항에 대해 보여주지 않는 다는 것을 명심하자. 이것은 오직 unstaged 상태인 파일들의 변경사항만 보여준다. 따라서 당신이 모든 파일을 stage 상태로 만든다면, 이 명령어는 아무런 출력값을 보여주지 않을것이다.

다른 예로, 만약 당신이 benchmarks.rb 파일을 stage상태로 만들고 다시 수정을 하고, 다시 git diff을 실행한다면 unstaged 파일과 staged 파일을 비교해서 변경사항을 보여줄것이다:

$ git add benchmarks.rb
$ echo '# test line' >> benchmarks.rb
$ git status
# On branch master
#
# Changes to be committed:
#
#   modified:   benchmarks.rb
#
# Changed but not updated:
#
#   modified:   benchmarks.rb
#

이제 당신은 git diff를 이용해서 아직도 unstaged인것을 볼 수 있다.
$ git diff
diff --git a/benchmarks.rb b/benchmarks.rb
index e445e28..86b2f7c 100644
--- a/benchmarks.rb
+++ b/benchmarks.rb
@@ -127,3 +127,4 @@ end
 main()

 ##pp Grit::GitRuby.cache_client.stats
+# test line

그리고 git diff - cached로 지금까지 stage영역으로 보냈던 것들을 볼 수 있다:

$ git diff --cached
diff --git a/benchmarks.rb b/benchmarks.rb
index 3cb747f..e445e28 100644
--- a/benchmarks.rb
+++ b/benchmarks.rb
@@ -36,6 +36,10 @@ def main
          @commit.parents[0].parents[0].parents[0]
        end

+        run_code(x, 'commits 1') do
+          git.commits.size
+        end
+
        run_code(x, 'commits 2') do
          log = git.commits('master', 15)
          log.size

변경사항들을 커미트하기

이제 당신의 stage 영역에 당신이 원하는대로 준비가 되었고, 변경사항들을 커밋트할 수 있다. 그리고 만약에 아직 staged 인 것들 - 새로 만든 파일이 있거나, 수정이 후 git add를 하지 않은 것들-이 있다면 이것들은 commit 될 수 없다는 것을 알아두자. 그것들은 그냥 변경된 파일들로 당신의 디스크에 남아있을 것이다. 마지막으로 당신이 git status를 했을때 모든 파일이 stage 상태라면, 당신은 당신의 변경사항을 commit를 할 준비가 된것이다. 커밋트를 하는 가장 쉬운 방법은 git commit를 실행하는것이다.:

$ git commit 

이렇게 하면 선택한 편집기가 실행됩니다.(이것은 당신의 셀의 $EDITOR 환경변수로 지정할 수 있으나 보통 vim 또는 emacs이다. 그리고 당신은 1장에서 배운대로 git config --global core.editor를 통해 당신이 원하는 그 어떤 편집기로 변경할 수 있다..

편집기는 아래와 같은 Text를 보여준다. (이 예는 Vim 화면이다):

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   README
#       modified:   benchmarks.rb
~
~
~
".git/COMMIT_EDITMSG" 10L, 283C

당신은 git status 명령어 커멘트와 상단의 빈줄을 포함한 디폴트 커밋트 메세지를 볼 수 있다. 당신은 이 커멘트를 삭제하고 당신의 원하는 커밋트 메세지를 넣거나 또는 당신이 무엇을 변경했는지 도움을 주는 이 커멘트를 나둘수 있다.. (당신이 좀더 정확하게 무엇이 변경되었는지 기억하고 싶다면 git commit 뒤에 -v옵션을 넣어라. 이렇게 하면 diff 명령어를 했을때의 자세한 내용이 당신의 에디터에 보일것이다.) 당신이 에디터로 빠져 나올때, Git는 커밋트 메세지와 함께 commit를 생성할것이다.(with the comments and diff stripped out).

다른 방안으로, 당신은 commit 명령어 -m 옵션을 줌으로써,  한줄짜리 commit 메세지를 함께 쓸수 있다. :

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

이제, 당신은 첫번째 커밋트를 생성하였다. 당신은 Commit가 당신에게 주는 출력화면을 보게 될것이다. 어떤 브랜치에 커밋(master)를 했는지, SHA-1 합계값(463dc4f)이 몇이었는지, 얼마나 많은 파일이 변경되었고, 라인 추가와 삭제에 대한 통계값을 알려준다.

Commit는 당신의 staging 영역에 대한 snapshot을 저장한다는 것을 명심하자. 당신이 stage상태로 보내지 않는 변경된 것들은 그대로 그자리에 있을것이다. 당신이 commit를 할때마다, 프로젝트의 snapshot을 저장하고 차후에 이것을 불러올수도 있고 이것과 비교할 수도 있다.

Staging 영역 띄어 넘기

아무리 staging 영역이 놀랍게 유용하더라도 가끔 이곳이 더 복잡할 경우가 있다. 따라서 staging을 그냥 띄어 넘는 방법을 Git는 제공한다. git commit 명령어에 -a 옵션을 붙이므로 Git는 자동으로 모든 파일들을 커미트 하기 전에 stage영역으로 보내게 한다:

$ git status
# On branch master
#
# Changed but not updated:
#
#   modified:   benchmarks.rb
#
$ git commit -a -m 'added new benchmarks'
[master 83e38c7] added new benchmarks
 1 files changed, 5 insertions(+), 0 deletions(-)
당신이 커미트를 하기전에 benchmarks.rb파일을 git add를 하지 않아도 된다는 것을 명심하자.

파일을 삭제하기

Git에서 파일을 제거하기 위해, 당신은 추적을 하고 있는 파일들을 삭제한 후(더 정확하게는 당신의 staging 영역에서 삭제해야한다.) 커밋트를 해야한다. git rm 명령어는 당신의 working 디렉토리에서 그 파일들을 삭제하기 때문에 당신이 다음에 추적되지 않는 파일로도  이 파일들을 볼 수 없다.

만약에 당신이 간단하게 그 파일을 지워버리면, git status 를 실행시켰을때 “Changed but not updated”으로 이 파일들이 출력된다.( 다시말해서, 아직 추적되고 있는 파일이므로 다시 stage영역으로 보내고 또 commit를 해야 한다. ):
$ rm grit.gemspec
$ git status
# On branch master
#
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#
#       deleted:    grit.gemspec
#

후에 git rm을 실행한다면, 그 파일의 삭제 상태를 stage 시킨다l:

$ 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
#

다음 commit를 할때, 그 파일은 사라지게 될것이고, 더이상 추적하지도 않을 것이다. 만약 당신이 이 파일을 수정하고 이미 index(stage)에 추가하였다면, 당신은 강제로 -f 옵션을 추가함으로써 삭제해야한다. 이것은 snapshot에 아직 저장하지 않아서 Git로 되돌릴수 없는 데이터를 실수로 삭제하는것을 예방하기 위한 안전한 기능이다..

당신이 혹시 원하는 또다른 유용한 기능은 아마 그 파일을 stage 영역에서 삭제를 하더라도 working tree에 유지하는 것일것이다. 다시 말해서, 이 파일을 당신의 하드 디스크에 저장은 하되 더이상 Git에 의해 추적되지 않았으면 하는 것이다. 이것은 당신이 .gitignore파일을 만들기 전에 파일들을 add를 했을 때 사용하면 매우 유용한다.  --cached 옵션을 통해서 이것을 할수있다:
$ git rm --cached readme.txt

당신은 git rm 명령어 다음에 파일, 디렉토리, file-glob 패턴을 다음과 넘겨줄수 있다

$ git rm log/\*.log

*앞에 \(백슬래쉬)라는 것은 명심하자. 이것은 Git가 자신의 파일이름을 확장자를 당신의 셀의 파일이름에 추가하기 위해서 꼭 필요하다. 이 명령어는 log/디렉토리 안의 .log 확장자가 붙은 모든 파일들을 삭제한다. 또는 다음과 같이 할수 있다.:

$ git rm \*~

이 명령어는 ~으로 끝나는 모든 파일들을 삭제한다.

파일 이동하기

많은 다른 VCS 시스템과 다르게, Git는 파일의 이동을 추적하지 않습니다. 만약 당신의 Git안의 파일의 이름을 변경한다해도 이 파일의 이름이 변경되었다는 정보를 Git의 어떠한 metadata에 저장하지 않습니다. 그렇지만 Git가 꽤 똑똑해서, 그 사실을 알아차릴 수 있다.( 차후에 우리는 파일 이동에 대한 Git Defect에 대해 알아 볼 것이다.)

그래서 Git가 mv 라는 명령어를 가지고 있다는 것 자체가 혼동을 준다. 만약 당신이 Git안의 파일이름을 수정하고 싶으면, 당신은 다음과 실행할것이다.

$ git mv file_from file_to

이것은 잘 작동할 것이다. 사실, 이렇게 실행한 후에 status를 보면, 당신은 Git가 변경된 파일에 대해 알고 있다는 것을 알수 있다.

$ git mv README.txt README
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 1 commit.
#
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       renamed:    README.txt -> README
#

그러나, 이것은 다음과 똑같다.:

$ mv README.txt README
$ git rm README.txt
$ git add README

당신이 mv 명령어를 이용하여 rename을 하든, 다른 방법을 사용하던지 상관없이 Git는 내부적으로 rename 했다는 것을 알수 있다. 단지 다른 유일한 것은 git mv명령어는 3가지 명령어를 한번에 처리를 해준다는 것이다.더 중요하게, 당신은 그 어떠한 툴을 이용하여 이름을 변경해도 상관없지만, 다만 Commit하기 전에 add/rm 명령을 반드시 실행해야한다.

반응형
댓글
250x250
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함