Git에서 데이터를 전송할 때 Dumb 프로토콜과 스마트 프로토콜 두 가지를 사용한다.
읽기전용으로만 사용하는 HTTP 저장소를 Clone하거나 Fetch할 때 사용
http-fetch 과정 알아보기
git clone <저장소 주소>
=> GET info/refs
ca82a6dff817ec66f44342007202690a93763949 refs/heads/master
=> GET HEAD
ref: refs/heads/master
⇒ 1번 과정을 통해 master 브랜치가 가리키는 커밋 개체 해시값을 알 수 있고 HEAD를 통해 기본 브랜치가 무엇인지 알 수 있다.
ca82a6 커밋에서 시작해보자 => GET objects/ca/82a6dff817ec66f44342007202690a93763949
(179 bytes of binary data)
$ git cat-file -p ca82a6dff817ec66f44342007202690a93763949
tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
parent 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
author Scott Chacon <schacon@gmail.com> 1205815931 -0700
committer Scott Chacon <schacon@gmail.com> 1240030591 -0700
changed the version number
=> GET objects/08/5bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
(179 bytes of data)
=> GET objects/cf/da3bf379e4f8dba8717dee55aab78aef7f4daf
(404 - Not Found)
부모 커밋 개체는 내려받았지만 트리개체는 가져오지 못 했다. 해당 트리 개체가 서버에 느슨한 압축으로 저장되어 있지 않아 다른 저장소에 있더나 저상소의 Packfile 속에 있을 수 있다. (여기서 느슨한 압축이란 git 개체가 저장될 때 해싱한 값을 압축해서 저장하는 것 맞다.)
다른 저장소에 없는 개체는 확실이 Packfile속에 있다. 이는 objects/info/packs 파일에 들어있으며, update-server-info 명령이 생성한다.
=> GET objects/info/packs
P pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack
=> GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.idx
(4k of binary data)
=> GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack
(13k of binary data)
서버에는 하나의 Packfile이 있다. Packfile이 포함하는 파일의 목록을 Packfile의 Index라고 하는데 여기서 찾고자 하는 개체를 찾는다.
Packfile의 Index를 가져와서 개체가 있는지 확인하고 해당 개체의 SHA-1값과 오프셋을 확인했으면 해당 Packfile을 내려받는다.
이렇게 5번에서 못 찾았던 Tree 개체를 얻어오고 나서 커밋 데이터를 가졍온다. 커밋 데이터는 Packfile 속에 모두 들어있을 것이다. 그리고 이렇게 서버 작업은 끝나고 최종적으로 Git은 HEAD가 가리키는 master 브랜치의 소스코드를 복원한다.
스마트 프로토콜에서 리모트 서버는 클라이어니트가 어떤 데이터를 갖고 있고 어떤 데이터가 필요한지 분석하여 실제로 전송할 데이터를 추려낸다.
서로 연결되는 클라이언트에서 실행되는 send-pack 과정과 서버의 receive-pack 과정이 있다.
git push origin main 명령을 실행하면 Git은 send-pack 을 시작한다.

git-receive-pack 명령은 Refs 정보를 한 라인에 하나씩 보여준다.
ssh 방식과 유사하지만 처음 핸드세이킹 과정이 약간 다르다. 아래의 클라이언트의 요청과 서버의 응답으로 시작한다.
=> GET http://server/simplegit-progit.git/info/refs?service=git-receive-pack
001f# service=git-receive-pack
00ab6c5f0e45abd7832bf23074a333f739977c9e8188 refs/heads/master□report-status \
delete-refs side-band-64k quiet ofs-delta \
agent=git/2:2.1.1~vmg-bitmaps-bugaloo-608-g116744e
0000
그리고 클라이언트는 이어서 POST 메서드를 사용해 send-pack 명령이 제공하는 데이터를 서버로 전송한다. 이렇게 전송한 데이터에 대해 서버에서 처리된 결과가 HTTP 응답으로 돌아온다.
=> POST http://server/simplegit-progit.git/git-receive-pack
클라이언트가 fetch-pack 을 시작하면 서버의 upload-pack 에 연결되고 서로 어떤 데이터를 내려받을지 결정한다.
upload-pack 이 아래 형식의 데이터를 전송한다.

데이터를 업로드할 때 수행되는 receive-pack 의 응답과 유사하지만 Capability 부분이 다르다. HEAD Refs를 알려주기 때문에 저장소를 clone하면 어디로 checkout 해야하는지 알 수 있다.
fetch-pack 은 upload-pack 으로부터 받은 데이터에서 이미 가지고 있는 개체는 “have”를 내려받아야 하는 개체에는 “want”를 붙인 정보를 만들고 마지막에 “done”이라고 적어서 보낸다.
003cwant ca82a6dff817ec66f44342007202690a93763949 ofs-delta
0032have 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
0009done
0000
그러면 서버의 upload-pack 이 해당 데이터를 Packfile로 만들어 전송한다.
http fetch 과정은 두 개의 http 요청 과정을 가지는데, 첫 요청은 ssh에서 얻은 결과와 같다.
=> GET $GIT_URL/info/refs?service=git-upload-pack
001e# service=git-upload-pack
00e7ca82a6dff817ec66f44342007202690a93763949 HEAD□multi_ack thin-pack \
side-band side-band-64k ofs-delta shallow no-progress include-tag \
multi_ack_detailed no-done symref=HEAD:refs/heads/master \
agent=git/2:2.1.1+github-607-gfba4028
003fca82a6dff817ec66f44342007202690a93763949 refs/heads/master
0000
두 번째 요청이 조금 다르다. 근데 방식은 정말 유사한 것 같다.
=> POST $GIT_URL/git-upload-pack HTTP/1.0
0032want 0a53e9ddeaddad63ad106860237bbf53411d11a7
0032have 441b40d833fdfa93eb2908e52742248faf0ee993
0000
Git이 내부에서 보내는 http 요청을 보고 싶었다. 지피티가 여러 방법을 알려주었는데 그 중에서 그냥 바로 CLI를 통해서 사용할 수 있는 방법을 사용해보았다. 위에서 학습 내용에서 보았던 요청들을 볼 수 있어서 흥미로웠다.
다음 명령어를 사용하면 git 명령어를 수행할 때 네트워크 내부에서 일어나는 과정을 볼 수 있다. tcp 수립 과정도 볼 수 있다. 나는 예시로 git clone을 해보았다.
GIT_TRACE=1 GIT_CURL_VERBOSE=1 git clone <저장소 주소>
레퍼런스