Git 동작 원리 학습
버전 관리 (형상 관리) 시스템
- 중앙 집중식
- 중앙 서버에서 모든 파일들을 저장 및 유지
- 팀원들이 서로가 무슨 작업을 하는지 알 수 있습니다.
- ex. CVS, Subversion, …
- 분산식
- 각 팀원 client에 Repository를 통째로 복사하여 작업합니다.
- 중앙 서버에 문제가 생기거나, 중앙 서버와의 통신이 끊겨도 작업이 가능합니다.
- Git
GIT 개요
- 위와 같은 그림에서
- working directory, staging area, local repository는 내 컴퓨터 (로컬 머신)
- remote repository는 원격 서버의 구성요소 입니다.
- 아래에서는 내 컴퓨터 안의 add, commit의 동작 원리 위주로 설명합니다.
참고 도식
출처: 깃 내부 동작 원리 및 도식
용어 정리
영역
- Working Directory(= Working Copy, Local)
- 위치: 프로젝트 폴더
- 현재 프로젝트 폴더의 파일들 그 자체를 의미합니다.
- Staging Area (= Cache, Index)
- 위치: 프로젝트 폴더의 .git/index
- add의 결과로 커밋이 이뤄질 준비가 된 파일의 내용들이 위치하는 영역
- 인덱스 파일에는 add된 변경사항 내용에 대하여 그 파일명과 해당 파일 내용을 담고 있는 Blob 파일의 주소(이름)가 기록됩니다.
- Repository
- 위치: 프로젝트 폴더의 .git/objects/
- 깃이 버전 관리를 하기 위해 필요로 하는 데이터들이 저장됩니다.
- 대표적으로 버전 관리 시작 시점부터 현재까지 관리한 여러 버전의 파일 내용들이 Blob 파일로서 이곳에 저장됩니다.
- 이곳에 저장된 파일들을 오브젝트 파일이라 부르며, Blob 파일도 이중 하나입니다.
오브젝트 파일
위의 레포지토리 영역 즉, .git/objects/ 디렉토리에 존재하는 파일을 말합니다. 아래 4개 종류가 있습니다.
- Blob
- 파일의 내용이 저장됩니다.
- 파일의 내용에 SHA1 해싱 기법을 적용해 Blob 파일 이름을 생성하기 때문에 내용이 같은 파일이라면 하나의 Blob 파일로 저장됩니다.
- 여러 버전에 존재하는 파일을 중복 없이 저장할 수 있습니다.
- Tree
- Commit 시점의 파일들 각각에 대해 그 파일명과 해당 파일의 내용을 담고 있는 Blob 파일의 주소(이름)이 기록됩니다.
- 위에서 말한 Staging Area 인덱스 파일 (.git/index)와 성격이 유사합니다.
- Commit
- 하나의 버전 생성은 하나의 Commit 파일을 만드는 것을 의미합니다.
- 이 파일에는 가리키고 있는 Tree 파일의 주소(이름)와 직전 버전에 해당하는 Commit 파일의 주소(이름)이 기록됩니다.
- 히나의 Commit 파일은 하나의 Tree 파일을 가리킵니다.
- Tag
- 버전 태그(ex. v0.1, v1.3)와 연관이 있습니다.
- 여기서는 자세히 다루지 않습니다.
명령어
아래 명령어 앞에는 기본적을 git을 붙입니다.
init- .git 폴더를 만들어 git이 버전 관리를 시작하게 합니다.
add <파일명>- index/staging area와 비교해서 working-directory/local에서 변동된 사항을 staging area에 반영합니다.
status- working directory와 staging area 내용을 비교해서 add의 대상이 될 파일 목록을 표시(빨간색)하고, staging area 내용과 직전 commit의 tree 파일이 가리키는 내용을 비교해 commit의 대상이 되는 파일들을 표시(초록색)합니다.
- 색깔은 다를 수 있습니다!
- 만약 모든 내용이 같다면 Nothing to commit을 출력합니다.
- working directory와 staging area 내용을 비교해서 add의 대상이 될 파일 목록을 표시(빨간색)하고, staging area 내용과 직전 commit의 tree 파일이 가리키는 내용을 비교해 commit의 대상이 되는 파일들을 표시(초록색)합니다.
staging area(index 파일, 비록 역시 Blob 파일 생성해서 참조하지만)와 Tree(를 비롯한 오브젝트 파일들)는 특정 시점에 존재하는 파일의 정보에 대한 스냅샷에 불과하며, 각 명령어는 그러한 스냅샷을 추가하는 것에 불과하다.. 추가로 이해해볼만한 예시는 아래 출처 블로그에 있습니다.
브랜치
HEAD- 위치: ./git/HEAD
- 현재 체크아웃checkout 되어 있는 브랜치의 포인터를 가리키는 포인터입니다. 위의 도식을 보면 참조 순서를 이해할 수 있습니다.
- 간단히 말하면, 현재 브랜치를 의미합니다.
- 브랜치의 포인터가 아닌 일반적인 Commit 파일을 가리키게 할 수도 있으며, 이를 detached HEAD라고 합니다.
- 브랜치 포인터
- 위치: .git/refs/heads/{브랜치명}
- 해당 브랜치의 가장 최신 Commit 파일을 가리키는 포인터입니다.
- 명령어
reset <커밋>: 브랜치 포인터가 지정된 Commit 파일을 가리키도록 바꾼다.- –soft: 브랜치 포인터만 바꾸고, working directory 및 staging area 유지
- –mixed: 브랜치 포인터와 staging area를 바꾸고, working directory 유지
- –hard: 전부 바꿉니다.
checkout <브랜치명>: HEAD(현재 브랜치)를, 지정된 브랜치의 포인터를 가리키도록 바꿉니다.- -b: 해당 브랜치명의 브랜치를 생성하고 checkout합니다.
merge <브랜치명>- 브랜치 내용을 가져와 현재 체크아웃되어 있는 브랜치와 병합하며 새로운 Commit 파일을 생성하고, 현재 브랜치의 포인터를 새로운 Commit 파일을 가리키게 합니다. - - 충돌이 발생할 경우 처리?- Fast-Forward 병합: 현재 브랜치의 Commit 파일과 대상 브랜치의 직전 Commit 파일이 완전히 같은 경우에는 Commit 파일이 새로 생성되지 않고(새로 생성되게 옵션을 줄 수는 있음) 현재 브랜치의 포인터만 옮겨주는 Fast-Forward 병합이 이루어진다.
- 즉, 현재 브랜치의 변경 사항이 없고, 병합할 브랜치의 변경 사항만 있는 경우, 새로운 커밋을 생성하지 않고 단순히 현재 브랜치의 포인터를 병합할 브랜치의 최신 커밋으로 이동합니다.
- Fast-Forward 병합: 현재 브랜치의 Commit 파일과 대상 브랜치의 직전 Commit 파일이 완전히 같은 경우에는 Commit 파일이 새로 생성되지 않고(새로 생성되게 옵션을 줄 수는 있음) 현재 브랜치의 포인터만 옮겨주는 Fast-Forward 병합이 이루어진다.
출처: 깃 내부 동작 원리 관련 영상
NodeJS Crypto
- 파일 내용을 해시 값으로 만들려면?
1
2
3
4
5
import crypto from "crypto";
const createHashedPassword = (password) => {
return crypto.createHash("sha512").update(password).digest("base64");
};
이렇게 단순한 방식으로, 같은 알고리즘과 같은 인코딩 방식을 선택하여 암호화하면 같은 결과를 낳는다. 같은 내용의 중복 파일을 만들지 않아 버전 관리 시스템에 적합하다고 볼 수 있다.
NodeJS Zlib
- 파일 내용을 압축해서 저장하기 위해 zlib를 써야함.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const zlib = require("zlib");
const {
NO_COMPRESSION,
BEST_SPEED,
BEST_COMPRESSION,
DEFAULT_COMPRESSION
} = require("zlib");
// compress data
const deflated = zlib.deflate("hello, world", BEST_SPEED);
console.log(deflated.toString());
// decompress data
const inflated = zlib.inflate(deflated);
console.log(inflated.toString());
This post is licensed under CC BY 4.0 by the author.
