티스토리 뷰

1. Upstream과 Downstream

이때까지 모호하게만 알고 있던 개념을 확실하게 짚고 가고 싶었다. upstream, origin에 대한 정확한 것이 무엇일까?

아래 그림을 참고하자. upstream이 있으면 downstream이 있다. 영어로 상류, 하류라는 뜻이다.

<Upstream & Downstream> 출처 : https://thechartroom.co/2019/05/20/are-we-travelling-upstream-or-downstream/

git에서 이러한 용어들을 이용하는 이유는 관계를 잘 표현해 주기 위한 것이다.

이것은 흐름을 나타내기 위한 상대적인 개념이다. 

git에서도 remote upstream, remote origin, local 로 점점 내려온다. fork한 저장소에서 나의 remote 저장소에서 나의 local 저장소인 컴퓨터로 내려온다. 이 관계를 표현해 주기 위해서 강의 상류와 하류를 표현하는 upstream과 downstream의 개념을 도입한 것이다.

그럼 대충 이것들이 상대적인 개념이란 것은 알겠는데 origin은 도대체 무엇일까?

 

(1) GitHub의 remote repository(origin)과 local의 관계

`git clone` 했을 때에는 자동으로 origin이 등록되어 있고, `git init`을 했을 때에는 `git remote add origin <url>`로 origin을 직접 등록한다. 여기서 말하는 origin이 github에 존재하는 remote repository, 즉 원격 저장소를 뜻한다.

remote에 origin이라는 이름을 붙인 것 뿐이다.

이 origin과 local을 기준으로 upstream과 downstream 개념을 도입해 본다면, origin인 원격저장소는 upstream, local은 downstream이 된다. 그 이유는 push와 pull을 기준으로 생각했을 때, origin으로 부터 local로 흐르는 관계가 형성되기 때문이다.

git bash를 통해 `git push -u origin main`이라는 명령어를 입력 시, -u 가 --set-upstream 옵션의 줄임으로 upstream을 설정한다는 말이다. upstream을 한 번 설정하고 나면 다음부터는 git push, git pull만 입력해도 자동으로 origin의 main 브랜치로 부터 pull과 push를 진행하는 이유가 upstream 옵션을 통해 해당 브랜치에서 upstream, downstream 관계가 설정되는 것이다.

 

(2) Fork를 통한 upstream

다른 사람의 repository를 내 repository로 복사하는 일인 fork를 할때, 원래 소유자의 remote repository와 내가 fork한 remote repository에도 upstream과 downstream의 관계가 형성된다. 그래서 소통 원래 소유자의 remote를 말할 때 upstream, 내가 fork한 remote를 말할 때 origin 이라는 용어를 사용하곤 한다.

앞의 local과 origin의 관계에서는 local이 downstream, origin이 upstream이었는데, fork한 repository를 기준으로 본다면 origin이 downstream, 원본 remote repository가 upstream의 관계가 된다. 

따라서 현재 우아한테크코스를 통해 매 미션을 fork할 때, 다음과 같은 프로세스를 거치게 된다.

 

1. 원본 remote repository, 우아한 테크코스 repsitory (upstream)를 GitHub을 통해 fork

2. fork한 나의 remote repository (origin)을 내 local 저장소로 clone

3. 우아한 테크코스 미션 기능 구현

  •   clone한 local에 commit, local에서 origin으로 push

4. upstream인 우아한 테크코스 저장소 upstream에 반영하기

  • PR을 등록하기 전 upstream에 바뀐 내용이 없는 경우 : origin 에서 upstream으로 PR
  • PR을 등록하기 전 upstream에 바뀐 내용이 있는 경우 : upstream에서 local로 pull, local에서 origin으로 push, origin에서 upstream으로 PR

 

정리하자면, 우아한테크코스 저장소를 upstream이라고 했을 때, 상류에서 하류로 여러 물줄기가 뻗듯 각 remote repository에서 fork를 해 origin으로 삼고, local로 clone을 하는 과정이 마치 물줄기가 여러 줄기로 뻗어 나가는 개념을 비유적으로 upstream와 downstream으로 표현하는 것이다.

 

2. Git의 객체

Git의 핵심은 단순한 key-value의 데이터 저장소라는 것이다. 어떤 형식의 데이터라도 집어 넣고, 해당 key값으로 언제든 데이터를 다시 가져올 수 있다.

Git은 init 명령으로 저장소를 초기화할 때, objects 디렉토리를 만들고 그 밑에 pack과 info 디렉토리를 만든다. 

객체가 담긴 파일의 이름은 git이 오브젝트 컨텐츠의 내용을 참고해 생성하는 40자리 문자열이다.

git에 hello.txt라는 파일을 하나 추가하면, hello.txt라는 이름의 객체를 생성하는 것이 아니라 hello.txt의 내용 전부를 해시 테이블에 넣어 40자리의 해시값을 뽑아 객체 파일 이름으로 사용한다.

 

그러면 hello.txt라는 이름은 어디 저장되는 것일까?

hello.txt를 위한 객체인 blob에는 파일 이름인 hello.txt라는 문자열이 저장되지 않는다.

대신 디렉토리 구조를 나타내는 tree 객체에서 hello.txt라는 문자열을 찾을 수 있다.

 

이러한 객체들은 하나의 파일로 .git/objects에 쌓이게 되는데, 한 디렉토리에 너무 많은 파일이 있으면 성능이 저하될 수 있기에 객체 파일이름 중 앞 두 글자는 디렉토리 이름으로 사용하고 나머지 38글자를 파일 이름으로 사용하게 된다.

 

Git 객체 저장소에는 blob, tree, commit, tag 객체가 존재하며 이 객체들이 Git의 상위 레벨 데이터 구조의 기초가 된다.

branch는 객체가 아닌 commit 객체 들에 대한 참조이다.

이러한 네 가지 객체들은 .git/objects에 개별적인 파일들로 존재한다. 그리고 하나의 commit, tree, blob, tag는 각각 하나의 파일이다. 

 Git과 linux를 만든 리누스 토발즈는 git 또한 그 구조를 보면 linux와 유사하다.

모든 것을 tree와 blob 개체로 저장하는데, Tree는 유닉스의 디렉토리, Blob는 일반 파일에 대응 된다.

 

단순화한 Git 데이터 모델. 출처 : https://git-scm.com/book/ko/v2/Git%EC%9D%98-%EB%82%B4%EB%B6%80-Git-%EA%B0%9C%EC%B2%B4

git add를 통해 staging area에 올릴때, git은 staging area의 상태대로 tree 개체를 만들고 기록한다.

그래서 tree 개체를 만들기 위해서는 우선 staging area에 파일을 추가해서 index를 만들어야 한다. 

 

하지만 각기 다른 스냅샷을 나타내는 Tree 개체를 만들어도 이 스냅샷을 불러오려면 hsah 값을 기억해야 한다는 점이 남는다. 이 스냅샷을 누가, 언제, 왜 저장했는지에 대한 정보가 아예 없는데, 이런 정보를 Commit 개체에 저장된다.

Commit 개체는 commit-tree 명령으로 만드는데, 이 명령에 commit 개체에 대한 설명과 Tree 개체의 hash값 하나를 넘긴다. 내부 포인터를 따라가면 아래와 같은 그림이 그려진다.

출처 : https://git-scm.com/book/ko/v2/Git%EC%9D%98-%EB%82%B4%EB%B6%80-Git-%EA%B0%9C%EC%B2%B4

Commit 객체는 save 포인트로 저장하는 목적은 잘 돌아가기 위한 정보들이 포함되어야 한다.

add나 commit으로 명령어를 나눈 이유는 위와 같이 add를 하면 tree와 blob개체간의 관계가 형성이 되고, commit을 하면 snapshot을 찍는 것과 같이 commit 개체가 생성된다.

위 그림에서 볼 수 있듯, commit은 변경 사항만 저장하는 것이 아니라 통째로 저장한다. 변경 사항을 저장하면 용량에 유리하긴하지만 수천개의 커밋이 쌓여있다 할때, 이전 커밋으로 많이 돌아가려면 모든 파일 상태를 추적하고, 비교해야 하는 연산비용이 많이 들기 때문에 속도 적인 측면에서 파일을 통째로 저장하는 것이 유리하기 때문이다.

하지만 이렇게 각각의 파일들로 하나의 객체로 관리하는데 branch도 만들고, 매번 쌓으면 용량적으로 부담 되기에

참조하고 관계 나타 낼 수 있는 tree객체를 만들고, 그 tree객체를 commit들이 관리를 하는 것이다.

따라서 파일 전체를 관리 하기에 속도적에서 git이 강점이며 git은 안전한 도구이다. 

 

Git의 개체 네 가지는 다음과 같이 정리를 할 수 있다.

  • blob(binary large object): 파일들이 blob 객체로 관리. 파일의 내용을 저장한다. 다양한 형식의 파일이 저장될 수 있다. 파일 이름이나 파일 형식은 blob에 저장되지 않는다.
  • commit : 저장 단위, snapshot을 찍는 것. tree + blob + 메타 정보. 중요한 객체다. 변경 사항에 대한 메타데이터를 가지고 있으며 각 커밋은 tree객체를 가리킨다.
  • tree : 한 레벨의 디렉토리 정보를 나타내며 Blob를 묶어서 관리한다. (디렉토리 구조와 유사) 
  • tag : 커밋에 대한 참조이지만 설명이 추가되는 객체로서 객체종류와 태그이름, 등을 가지고 있다.

 

Reference: 

우아한테크코스 1주차 피드백 영상

https://git-scm.com/book/ko/v2/Git%EC%9D%98-%EB%82%B4%EB%B6%80-Git-%EA%B0%9C%EC%B2%B4

 

Git - Git 개체

여러분이 사용하는 쉘이 어떤 것인가에 따라 master^{tree} 표현식이 오류를 일으킬 수도 있다. Windows 에서 CMD는 ^ 문자는 이스케이프 기호로 사용한다. ^ 문자를 제대로 사용하려면 git cat-file -p master

git-scm.com

https://storycompiler.tistory.com/7

최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday