TL;DR
git fetch origin master의origin master는 원격 저장소와 가져올 브랜치를 가리키며,git switch -c my-feature origin/master의origin/master는 로컬에 저장된 원격 브랜치의 상태를 가리킴.- Git은 불변 콘텐츠 주소 지정 데이터베이스뿐 아니라, 문자열 키가 콘텐츠 주소 지정 객체를 가리키는 변경 가능한 키-값 저장소도 갖춤.
- 브랜치와 태그, Git 노트는 모두
refs이며,refs/heads/나refs/remotes/같은 이름 공간으로 구분됨. - 원격 추적 브랜치는 마지막 동기화 시점에 파악한 원격 상태의 로컬 기록이며, 로컬 브랜치는 로컬 커밋에 따라 별도로 움직임.
fetch는 원격 브랜치 정보를 로컬 원격 추적 ref에 저장하고,switch -c는 그 ref를 시작점으로 새 로컬 브랜치를 만듦.
Git의 커밋과 refs
- Git의 커밋은 콘텐츠 해시로 식별되며, 주로 특정 시점 코드베이스의 메모리 없는 스냅샷과 부모 커밋 해시 목록으로 구성됨.
- 이 모델만으로도
git log출력을 이해하고, 저장소를 다시 복제하지 않고도 잘못된 리베이스에서 복구할 수 있음. 경력의 대략 절반 동안 저장소를 다시 복제했으며, Git 학습은 유용하지만 일을 시작할 때 배워야 할 최우선 과제는 아니라는 관점임. - Git은 추가만 가능한 불변 콘텐츠 주소 지정 데이터베이스와 함께, 변경 가능한 키-값 저장소도 제공함.
- 이 저장소는 문자열 키와 콘텐츠 주소 지정 객체를 연결하는 변경 가능한 맵이며, 키는 관례적으로 파일 시스템 경로 형태임. 맵의 상태는 대체로
.git/refs를 나열해 확인할 수 있음. - 예를 들어
.git/refs/heads/master파일에는 해당 브랜치가 가리키는 커밋 해시가 저장되며,git show-ref refs/heads/master로 같은 ref와 해시를 확인할 수 있음.
refs가 혼란스러운 이유
- refs는 사용자에게 보이는 여러 Git 기능을 작동시키지만, 그 자체는 구현 세부 사항임.
- 일상적인 사용에서는 refs가 거의 보이지 않아 추상화가 일부 새어 나옴.
- Git 명령줄 인터페이스(CLI)는 refs의 축약 표기와 기본 인수를 사용하므로, 특정 인수가 ref라는 사실이 명확하지 않을 때가 있음.
- Git은 하나의 대상에 여러 이름을 붙이고, 같은 이름을 서로 다른 대상에 재사용하기도 함.
- 브랜치와 태그, Git 노트는 모두 refs임.
`git fetch origin master`의 의미
- 단축 표기를 모두 풀면
git fetch origin master는 원격 저장소 URLhttps://github.com/matklad/matklad.github.io와 소스·대상 ref 쌍refs/heads/master:refs/remotes/origin/master를 지정하는 명령임. fetch의 첫 번째 인수는 원격 저장소 위치이며, Git은 해당 주소에 접속해 데이터를 네트워크를 통해 로컬로 전송함.- 두 번째 인수는 문자열 키(ref)의 소스·대상 쌍임. 소스는 원격 저장소의 키이고 대상은 로컬 키의 이름이며,
fetch는 원격 값을 읽어 로컬의 다른 이름으로 저장함. - 저장소 URL을 매번 입력하지 않도록 Git은 URL에 기호 이름을 부여하며,
origin은 주 원격 저장소에 흔히 쓰이는 이름임. 따라서git fetch origin refs/heads/master:refs/remotes/origin/master처럼 쓸 수 있음. refs/heads/master는 원격 저장소의 브랜치를 완전히 표기한 이름임.my-feature브랜치도 실제로는refs/heads/my-feature라는 ref이며,refs/branch/my-feature일 수도 있었지만 그렇게 정해져 있지는 않음.- 구체적인 축약 규칙은 알지 못하지만, 일반적으로 Git은 ref의 접미부만 입력하는 표기를 허용함. 따라서 소스는
master로 줄여 쓸 수 있음. refs/remotes/origin/master는 가져온 결과를 저장할 로컬 ref임. 원격과 같은 이름을 로컬에서도 그대로 쓰면 원격 저장소가 하나일 때만 문제가 없으며, 포크한 저장소와 원본 저장소처럼 업스트림이 둘이면 ref 이름이 충돌함.- 원격 저장소
foo의 refs를refs/remotes/foo아래에 두는 이유는 원격별 이름 공간을 만들기 위함이며, 단순한 설정에서origin은 관례적인 원격 이름임. - 로컬에서 원격 ref 구조를 그대로 복제하는 방식이라면
refs/heads/my-branch가refs/remotes/origin/heads/my-branch로 이어지겠지만, Git은 중복되는heads구성 요소를 제거함. heads에서remotes/$remote로 이어지는 매핑이 내장되어 있어 명령을git fetch origin master로 줄일 수 있음.
원격 브랜치와 로컬 브랜치
- Git은 오프라인 우선 모델을 따르며, 동기화 시점을 명시적으로 지정한다고 가정함. 연결이 있을 때 원격 저장소와 백그라운드 동기화를 하는 대신, 데이터를 네트워크로 전송하려면 명시적으로
fetch와push를 실행해야 함. - 이 방식에서는 마지막으로 원격과 통신한 순간의 원격 상태를 모델링하는 것이 유용함. 이는 상대방의 상태를 추정해 기억하는 것과 같음.
- 예를 들어
main브랜치는origin원격에refs/heads/main으로 존재함. 로컬 저장소를origin과 동기화하면refs/remotes/origin/main이 생기며, 이는origin의main상태에 대한 현재 로컬 지식임. - 로컬
refs/heads/main은 보통 처음에는refs/remotes/origin/main과 같은 커밋을 가리킴. 하지만 커밋을 만들면refs/heads/main은 앞으로 이동하고refs/remotes/origin/main은 그대로임. - 로컬 커밋을
origin에 푸시하려 할 때 그사이 원격main이 앞으로 이동했다면 충돌이 발생함. 이때 Git은 원격 상태의 로컬 미러인refs/remotes/origin/main을 자동으로 갱신하지만,refs/heads/main을 갱신하고 다시 푸시하는 일은 직접 해야 함.
전체 명령 예시
git fetch origin master는.git/config에서origin원격의 URL을 찾고 해당 기기에 네트워크 요청을 보냄. 그 결과 로컬refs/remotes/origin/master가 원격의refs/heads/master와 같은 커밋을 가리키도록 갱신되며, 해당 커밋과 조상 커밋도 로컬로 전송됨.git switch -c my-feature origin/master는refs/remotes/origin/master를 시작점으로 삼아 새 브랜치인refs/heads/my-feature를 만듦. 두 번째 인수는 축약된 ref이므로refs/remotes/origin/master로 완전히 적을 수 있음.- 첫 번째 인수인
my-feature는 ref를 만들 이름이지만, 그 자체가 ref는 아님. 이를refs/heads/my-feature로 완전히 적으면 Git은refs/heads/refs/heads/my-feature를 만들게 됨. - 이 설명이 특별히 유용하지 않을 수도 있지만 흥미로운 내용일 수 있음.
댓글 (0)
로그인하면 이 기사에 내 생각을 남길 수 있어요