소스코드를 관리하는 시스템은 크게 3종류로 나눌 수 있다.
버전관리 시스템 종류를 학습하고, 차이점을 비교해본다.
저장소란 실제 소스코드가 담겨있으면서 commit 내역 등의 모든 작업 이력이 담겨 있는 공간을 의미한다.
프로젝트의 메타데이터를 포함한 각종 데이터는 .git 폴더에 담기게 되는데 실제 폴더를 열어보면 각종 데이터와 해시 값 등이 담겨있다.
어떠한 파일을 commit하게 되면 각 작업들을 분류하기 위해 내부적으로 해당 작업에 대한 hash 값을 이용하는데 일반적으로 해시 값은 충돌이 발생하지 않기 때문에 정확히 커밋 내역들을 관리할 수 있다.
Remote Repository : 원격 저장소. 원격 서버를 말한다.
Local Repository : 내 로컬 저장소
init : 해당 레퍼지토리를 버전관리 하겠다. .git폴더를 만들어 해당 레퍼지토리를 Git 레퍼지토리로 초기화.
clone : 원격 저장소를 복제해 새 디렉토리로 가져온다.
add : 파일내용을 인덱스에 추가. =해당 파일을 버전관리 하겠다.
commit : 현재의 상태를 버전으로 만드는 작업
git repository : .git 폴더를 의미하며 .git폴더에는 버전 정보들이 저장돼있다.
staging area : 준비 영역. add 명령어를 통해 올라가고 commit시 staging area의 내용이 확정된다. 일종의 버퍼 역할
working directory : 실제 작업할 파일들이 있는 디렉토리
git ignore : 버전관리를 할 필요가 없는 파일들은 따로 .gitignore 폴더에 모아서 git이 무시하게 한다.
working directory에서 add를 통해 Index(staging area)에 추가되고 commit을 통해 최종확정본이 HEAD에 반영된다.
현재 변경 내용은 로컬 저장소의 HEAD에 머물고 있다.
git push origin master
을 통해 원격 서버로 로컬의 내용을 올릴 수 있다.
branch는 안전하게 격리된 상태에서 무언가를 만들 때 사용한다.
저장소를 처음 만들면 기본으로 master branch가 생성된다.
다른 branch를 만들어 개발을 진행하고, 개발이 완료되면 master 가지로 돌아와 병합(merge)시킨다.
git checkout -b [branch_name]
branch를 만들고 이동한다.
git checkout master
master branch로 돌아간다.
git branch -d [branch_name]
해당 branch를 삭제한다.
git push origin [branch_name]
생성한 branch를 원격 저장소에 올린다.
git pull
= fetch + merge. 해당 명령어를 통해 로컬 저장소를 원격 저장소에 맞춰 갱신한다.
git merge [branch_name]
현재 branch에 해당 branch의 내용을 병합한다.
이 때, 충돌(conflict)이 발생할 수 있는데 해당 부분은 직접 수정하여야 한다.
git checkout -- [file_name]
로컬의 변경 내용을 변경 전 상태(HEAD)로 되돌린다. 이미 인덱스에 추가된 변경 내용과 새로 생성한 파일은 그대로 남는다.
참조 : http://rogerdudler.github.io
Untracked : 버전관리를 하지 않는 파일들의 상태. git이 버전관리를 해주지 않는다. 한 번이라도 버전을 만들었던 파일은 tracked 상태가 된다.
Unmodified : 파일이 수정되지 않은 상태. 파일 수정 시 modified 상태가 된다. git commit 직후 모든 파일은 unmodified 상태이다.
Modified : 파일이 수정된 상태.
Staged : 커밋으로 저장소에 기록할 상태. add하면 staged 상태가 된다.
참조 : https://bite-sized-learning.tistory.com
Git은 Content-addressable 파일 시스템이다.
쉽게 말해 Git의 핵심은 단순한 Key-Value 데이터 저장소라는 것이다.
어떤 형식의 데이터라도 집어넣을 수 있고, Key를 이용해 언제든 데이터를 다시 가져올 수 있다.
Git은 init
명령으로 저장소를 초기화할 때, objects 디렉토리를 만들고, 그 밑에 pack과 info 디렉토리도 만든다.
Git은 데이터를 저장할 때 헤더정보와 데이터 모두에 대한 SHA-1 체크섬으로 파일 이름을 짓는다. 해시의 처음 두 글자를 따서 디렉토리 이름에 사용하고 나머지 38글자를 파일 이름에 사용한다.
Git이 파일 버전을 관리하는 방식을 이해할 수 있도록 가상의 상황을 만들어 살펴본다.
우선 새 파일을 하나 만들고 Git 저장소에 저장한다.
$ echo 'v. 01' > test.txt
$ git hash-object -w test.txt /* git hash-object 명령은 이 데이터에 접근하기 위한 key를 반환한다. -w 옵션을 주면 해당 데이터를 저장한다. */
83baae61804e65cc73a7201a7252750c76066a30 // 반환된 key 값
파일을 수정하고 다시 저장한다.
$ echo 'v. 02' > test.text
$ git hash-object -w test.txt
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
이제 데이터베이스에는 데이터가 두 가지 버전으로 저장돼있다.
이 때, 스택처럼 동작하여 가장 최근의 버전이 데이터 상단에 저장된다.
$ find .git/objects -type f
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30
이와 같이 되면 이제 test.txt 파일을 변경하더라도 저장된 버전으로 되돌릴 수 있다.
$ git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30 > test.txt
$ cat test.txt
v. 01
cat-file
명령어를 통해 첫 번째 버전으로 되돌렸다.
이처럼 파일의 SHA-1 키를 외워서 사용하는 것은 너무 어렵다.
게다가 원래 파일의 이름은 저장하지도 않았다. 단지 파일의 내용만 저장했을 뿐이다. 이런 종류의 개체를 Blob 개체라고 부른다.
다음은 Tree 개체에 대해 알아보자.
이 Tree 개체에 파일 이름을 저장한다. 파일 여러개를 한꺼번에 저장할 수도 있다.
Git은 유닉스 파일 시스템과 비슷한 방법으로 저장하지만 좀 더 단순하다.
모든 것을 Tree와 Blob 개체로 저장한다. Tree는 유닉스의 디렉토리에 대응되고 Blob는 Inode나 일반 파일에 대응된다.
Tree 개체 하나는 항목을 여러개 가질 수 있는데 Blob 개체나 하위 Tree 개체를 가리키는 SHA 포인터, 파일 모드, 개체 타입, 파일 이름이 들어있다.
simplegit 프로젝트의 마지막 Tree 개체를 살펴보자
$ git cat-file -p master^{tree}
100644 blob a906cb2a4a904a152e80877d4088654daad0c859 README
100644 blob 8f94139338f9404f26296befa88755fc2598c289 Rakefile
040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0 lib
master^{tree}
구문은 master 브랜치가 가리키는 Tree 개체를 말한다.
lib 항목은 디렉토리인데 Blob 개체가 아니고 다른 Tree 개체이다.
git이 저장하는 데이터는 대강 아래 그림과 같다.
스냅샷을 나타내는 Tree 개체를 만들었다.
아직 남은 어려운 점은 이 스냅샷을 불러오려면 SHA 값을 기억하고 있어야 한다는 점이다. 스냅샷을 누가, 언제, 왜 저장했는지에 대한 정보가 아예 없다.
이러한 정보는 commit object에 저장된다.
커밋 개체는 commit-tree
명령으로 만든다.
이 명령에 커밋 개체에 대한 설명과 Tree 개체의 SHA 값 한 개를 넘긴다.
새로운 커밋 개체를 만들 때에는 각 커밋 개체가 이전 개체를 가리키도록 한다.
커밋 개체의 형식은 간단하다. 해당 스냅샷에서 최상단 Tree를 하나 가리킨다.
그리고 user.name과 user.email 설정에서 가져온 Author/Committer 정보, 시간 정보, 그리고 한 라인 띄운 다음 커밋 메세지가 들어간다.
각 커밋 개체는 각각 해당 스냅샷을 나타내는 Tree 개체를 하나씩 가리키고 있다.
마지막 커밋 개체의 SHA 값을 주고 git log 명령을 실행하면 log를 볼 수 있다.
여기까지 한 일이 git add
와 git commit
명령을 실행했을 때 Git 내부에서 일어나는 일이다.
Git은 변경된 파일을 Blob 개체로 저장하고 현 Index에 따라서 Tree 개체를 만든다. 그리고 이전 커밋 개체와 최상위 Tree 개체를 참고해서 커밋 개체를 만든다.
즉 Blob, Tree, Commit Object가 Git의 주요 개체이고, 이 개체들은 전부 .git/objects 디렉토리에 저장된다.