개발자 간 협업의 중요성과, git을 사용하는 이유를 간단히 ChatGPT에게 물어보았다.
개발자 간 협업은 효율적인 소프트웨어 개발을 위해 중요하며, 버전 관리 시스템인 git은 여러 개발자가 동시에 작업할 때 코드 충돌을 방지하고 작업 내역을 체계적으로 관리하는 데 도움을 줍니다.
Git은 분산 버전 관리, 가볍고 빠른 브랜치 관리, 그리고 다양한 협업 플랫폼과의 통합으로 인해 현대 소프트웨어 개발에서 널리 사용되고 있습니다.
ChatGPT가 잘 요약해 주었듯이, git은 개발 과정에서의 편의성과 협업을 위해 가장 많이 사용되는 소프트웨어이다.
그런데 나는 과연 지금까지 git이 어떻게 작동하는지 이해하고 사용하고 있는지? 에 대한 의문이 들어, 우리가 git add
와 git commit
, git push
할 때 어떤 일들이 발생하는지 간략히 이해해보는 시간을 가졌다.
https://git-scm.com/book/en/v2/Git-Internals-Git-Objects
원 매뉴얼이 정말 잘 작성되어 있다! 보다 정확한 이해를 위해서는 직접 읽고 따라 해보는 것을 강력하게 추천한다.
git은 컨텐츠 주소 지정이 가능한 파일 시스템이다. Git의 핵심은 단순한 key - value 데이터 저장소이다.
git hash-object
명령어는 다음의 역할을 수행한다.
.git/objects
디렉토리 내부에 저장된 데이터를 들여다 본다.해당 명령어를 사용해서 git 시스템 상에 새로운 key - value 데이터를 생성할 수도 있다.
echo 'test!' | git hash-object -w --stdin
임의의 git repository (git init
을 마친) 에서 수행했을 때에는 다음이 리턴되었다.
989f51ae9a59308340a3827f0df9de24033f97b6
해당 값은 git 시스템이 git 데이터베이스에 저장될지도 모르는 유니크한 키 값이다. 즉
cat
명령어는 파일 이름을 받아서 그 내용을 콘솔에 쫙 뿌려주는 명령어이다.
비슷하게, git cat-file
커맨드는 위에서 생성한 객체 데이터의 키 값을 인자로 받아 해당 객체를 볼 수 있는 여러 유용한 도구를 제공한다. (매뉴얼에서는 스위스제 군용 나이프에 비유한 것이 인상 깊다)
git cat-file -p 989f51ae9a59308340a3827f0df9de24033f97b6
test!
echo 'version 1' > test.txt
git hash-object -w test.txt
83baae61804e65cc73a7201a7252750c76066a30
echo 'version 2' > test.txt
it hash-object -w test.txt
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
cat test.txt
version 2
git cat-file -t 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
blob
git hash-object
커맨드를 통해서 git 시스템 상에 추가된 데이터 객체들은 ./git/objects
폴더 내에 저장된다.git cat-file
에 -t
옵션을 주게 되면 (git cat-file -t <object key>
) 해당 키 값에 상응하는 데이터 객체의 타입을 조회할 수 있다. blob
타입으로 추가된 것을 확인할 수 있었다.
blob
타입 이외에도 git 시스템에서 지원하는 다른 객체 타입을 알아보자.
직감적으로 느꼈을 수도 있겠지만, 위에서 살펴보았던 blob
타입으로만으로는 파일 이름과 파일들을 함께 아우르는 폴더를 저장하지 못한다.
이런 문제를 해결해주는 것이 tree
객체 타입이다.
tree
와 blob
객체로써 저장된다. tree
blob
tree
객체는 하나 이상의 진입점을 포함한다.README
, Rakefile
은 파일로써, blob
타입 객체로 저장되며 tree
객체에 의해 참조된다.lib
폴더 내부에 저장된 simplegit.rb
파일은 blob
타입 객체로 저장된다. lib
폴더는 tree
객체로써, simplegit.rb
파일을 참조한다.lib
폴더에 상응하는 tree
객체는 다시 최상위 tree
객체에 의해 참조된다.
tree
객체는 버전 컨트롤의 대상이 되는 프로젝트의 서로 다른 스냅샷을 표현할 수 있다.
git 시스템은 스테이징 영역에 있는 상태로부터 tree
객체를 생성하던가, 인덱스를 가져다가 tree
객체를 생성할 수 있다.
git update-index
명령어를 사용하면 처음 추가된 text.txt
파일을 새로이 스테이징 영역으로 올릴 수 있다.--add
옵션을 더한다. --cacheinfo
옵션을 추가한다.git update-index --add --cacheinfo 100644 <추가하는 object hash key> <파일 이름>
git blob
타입은 아래 3개의 모드를 가진다.
100644
는 일반적인 파일에 대한 모드를 의미한다.100755
는 실행 가능한 파일에 대한 모드를 의미한다. 120000
는 심볼릭 링크에 대한 모드를 의미한다.위와 같이 update-index
명령어를 실행한 이후에는 git write-tree
명령어를 실행해서 스테이징 영역을 tree
객체로 만들 수 있다.
위에서 알아봤던 것처럼, tree
객체를 사용함으로써 서로 다른 버전의 파일들과 복잡한 파일 구조를 가지는 프로젝트들을 하나의 tree
객체로 만들 수 있음을 알아보았다.
하지만 위 방식은 아래와 같은 단점이 존재한다.
commit
객체는 이렇게 tree
객체만으로는 알 수 없는 사실들을 저장하기 위해서 생겨났다.
commit
객체를 임의로 생성하기 위해 git commit-tree
명령어를 사용할 수 있다.
echo 'First commit' | git commit-tree d8329f
fdf4fc3344e67ab068f836878b6c4951e3b15f3d
commit
객체에 저장되는 포맷은 간단하다.
tree
객체 정보user.name
과 user.email
정보이렇게 생성된 commit
객체는 우리가 많이 쓰는 git log
명령어를 통해서 조회할 수도 있다.
이렇게
git add
명령어와git commit
명령어를 전혀 쓰지 않고도 파일을 커밋할 수 있음을 알아보았다!
blob
객체를 저장하고 (git hash-object
)blob
객체에 대한 인덱스를 업데이트 한 이후 (git update-index
)tree
를 작성하고 (git write-tree
)commit
객체를 생성했다. (git commit-tree
)이렇게 저장된 객체들은 전부 ./git/objects
디렉터리 하위에 저장된다.
아마 커밋을 생성한 시점 이후의 객체 그래프를 그려보면 아래와 같이 보일 것이다.
앞서 살펴봤던 git이 객체를 저장할 때 일종의 헤더를 사용한다고 했었다. blob
객체를 저장할 때 git 시스템은 아래 방법을 사용해서 객체를 저장한다.
(본문에는 좀 더 상세하게 언급되었지만 본 글에서는 간단하게만 다룬다)
blob
)header
에 컨텐츠의 byte 크기를 명세한다. git은 철저히 프로젝트의 버전 컨트롤을 위한 시스템이다.
github는 그렇게 버전 관리되는 프로젝트를 웹 상으로 공유하여 발전시키기 위한 웹 서비스이다.
git과 GitHub의 차이는 Java와 Javascript 만큼의 차이라고 할 수 있다.
git 상에서 버전 컨트롤되는 파일들은 삭제 또한 변경으로 본다.
blob
객체를 만들고, commit
객체로 tree
객체를 참조하게 되는 git 시스템의 특성blob
객체를 버전 컨트롤하기https://stackoverflow.com/questions/65632477/how-can-i-save-my-files-without-committing-with-git
위에서 살펴보았던 것처럼 commit
객체를 추가하지 않아도 로컬 git 시스템 상에 파일을 논리적으로 등록하고 저장할 수는 있다. 그렇다면 이렇게 저장된 파일을 다른 사람이 pull 해서 볼 수 있을까?
결론은 그렇지 않다. 커밋되지 않은 파일은 git gc의 대상이 된다.
git push
시점에 로컬 git 시스템은 저장된 다양한 객체들을 remote git 시스템에 업로드한다.git gc
를 수행한다.즉, 커밋되지 않고 로컬에 추가된 git object들은 remote 저장소에 영구적으로 저장될 수 없다.
github 이슈 또는 커멘트에 파일을 copy and paste 또는 첨부하여 편리하게 본문에 파일을 모두가 볼 수 있게 하는 기능은 anonymized url 방식을 사용한다.
관련하여 확인한 be 로직은 아래와 같았다. (github enterprise 기준, 상세 동작은 상이할 수 있습니다)
1. 이슈 또는 커멘트 본문에 파일 첨부시 POST /upload/policies/asset
를 한번 호출하여 해당 유저가 업로드할 권한이 있는지 체크하고
2. POST /user/{userNo}/files
을 호출하여 해당 유저의 private한 namespace에 업로드한 파일에 접근할 수 있는 url을 응답 값으로 주었다.
3. 해당 응답 값으로 오게 된 url은 바로 preview 될 수 있는 형태인 <img>
태그 안에 래핑되어 paste 된다.
github에 복사 + 붙여넣기되는 첨부 파일들은 첨부와 동시에 github 로그인된 계정의 리소스로 업로드 된다.
이후 업로드된 파일에 접근할 수 있는 github url을 자동으로 삽입함으로써 우리는 편리하게 이슈 및 커멘트에 첨부파일을 추가할 수 있었다.
(방금 이 글을 쓰면서 확인한건데, velog에서 첨부파일을 글 본문에 추가할 때에도 어느정도 유사한 방식을 사용한다 😉)
.git/objects
) 에 저장함으로써 프로젝트의 버전을 관리할 수 있다.git add
와 git commit
은 복잡한 객체 데이터 생성을 편리하게 해주는 일종의 명령어 집합이다.
제목 센스에 무릎을 탁 치고 갑니다