테마 선택

📚 지식 노트

AI·하드웨어·주변기기·게임·컴퓨터 시스템·이론 컴퓨터 과학·수학·개발과 운영·보안·응용 분야에 대해 알아 두면 좋은 내용을 정리한 노트다. 위에서 분야를 고르고 아래에서 노트를 고른다.

버전 관리(Git)

Git이 파일을 내용의 해시로 저장하는 방법부터 브랜치가 사실은 이름표라는 것, 병합 기준을 찾아 3-way로 합치는 과정, 리베이스와 충돌, 원격 저장소와 브랜치 전략, 큰 저장소를 다루는 법, 좋은 커밋 습관까지를 실제 해시를 계산해 가며 정리한다.

목차 (11)
  1. 객체 모델: 내용이 곧 주소다
  2. 참조: 브랜치는 이름표다
  3. 병합: 병합 기준을 찾아 3-way로 합친다
  4. 리베이스와 병합
  5. 충돌: 양쪽이 같은 곳을 다르게 고쳤을 때
  6. 원격 저장소: fetch, pull, push
  7. 브랜치 전략
  8. 큰 저장소: LFS, partial clone, 모노레포
  9. 커밋 위생과 bisect
  10. 되돌리기: restore, reset, revert, cherry-pick
  11. 핵심 자료

버전 관리 시스템은 파일의 변경 이력을 남기고, 여러 사람이 동시에 고친 내용을 합치게 해 준다. 지금은 사실상 Git이 표준이다. 명령어를 외우는 것보다 Git이 안에서 무엇을 저장하는지를 알면 낯선 상황(엉킨 병합, 사라진 것 같은 커밋)에서도 길을 찾을 수 있다. 이 노트는 그 안쪽 구조에서 출발한다. 커밋이 배포로 이어지는 과정은 CI/CD 노트에서 이어진다.

객체 모델: 내용이 곧 주소다

Git 저장소의 .git/objects에는 세 종류의 객체가 쌓인다. 파일 내용 하나는 blob, 폴더 하나(이름과 그 아래 blob·tree의 목록)는 tree, 프로젝트 전체의 한 순간은 commit이다. 커밋은 최상위 트리 하나, 부모 커밋들, 작성자와 시각, 메시지를 담는다. 즉 커밋은 ‘바뀐 부분’이 아니라 그 순간의 스냅숏 전체를 가리킨다. 바뀌지 않은 파일은 같은 blob을 다시 가리키므로 공간을 거의 쓰지 않는다.

Git 객체 모델: 커밋 객체가 트리 객체 하나를 가리키고, 트리는 파일 이름과 함께 blob 두 개를 가리킨다커밋은 트리를, 트리는 blob과 하위 트리를 ID로 가리킨다commit 456b170tree 2a067d0author Dev"첫 커밋"tree 2a067d0README.md 5adde32main.py b80e322blob 5adde32# 메모blob b80e322print("hi")ID = SHA-1("종류 크기\0" + 내용) · 파일 이름은 blob이 아니라 트리에 있다
파일 두 개로 만든 첫 커밋. 그림의 ID는 같은 내용·작성자·시각으로 실제 git commit을 했을 때와 똑같다. 파일 이름은 blob이 아니라 트리에 적히므로, 이름만 바꾼 파일은 blob이 그대로다.

객체의 이름(ID)은 내용의 해시다. Git은 "blob 6\0"처럼 종류와 바이트 수를 머리말로 붙인 뒤 SHA-1을 계산한다. hello와 줄바꿈 한 줄짜리 파일의 blob ID는 어느 컴퓨터에서든 ce01362…이고, 느낌표 하나를 더하면 4effa19…로 완전히 달라진다. 이것을 콘텐츠 주소(content addressing)라 한다. 같은 내용은 저장소가 달라도 같은 ID를 가지니 중복이 저절로 사라지고, 커밋 ID 하나가 트리와 부모를 거쳐 이력 전체의 해시를 품으므로 과거의 어느 바이트를 몰래 바꿔도 ID가 어긋나 드러난다.

  • SHA-1의 약점: 2017년 SHAttered가 내용이 다른 두 PDF의 SHA-1을 같게 만들어 보였다. Git은 그 뒤로 충돌 공격의 흔적을 알아채는 SHA-1 변형(sha1collisiondetection)을 쓴다. 공격을 막는 응급 처치일 뿐 해시를 바꾼 것은 아니다.
  • SHA-256 전환: git init --object-format=sha256로 SHA-256 저장소를 만들 수 있다. 같은 파일의 ID는 64자리 2cf8d83…가 된다. 다만 SHA-1 저장소와 서로 주고받는 변환 계층은 설계 문서 단계에 있고 호스팅 서비스의 지원도 고르지 않다. Git은 BreakingChanges 문서에서 3.0부터 새 저장소의 기본값을 SHA-256으로 바꾸겠다고 예고했다. 2026년 10월 현재 최신 판은 2.56(2026년 9월)이고 3.0은 아직 나오지 않았다.
  • 팩 파일: 객체를 하나씩 파일로 두면 낭비라서, Git은 때때로 비슷한 객체끼리 델타 압축해 팩 파일(packfile)로 묶는다. 스냅숏 모델이 저장 공간을 많이 먹지 않는 이유다.

참조: 브랜치는 이름표다

커밋 ID는 외우기 어려우니 사람이 붙이는 이름이 참조(ref)다. 브랜치는 커밋 ID 하나를 담은 작은 파일(.git/refs/heads/main)일 뿐이고, 새 커밋을 만들면 지금 브랜치의 파일이 새 ID로 바뀐다. 브랜치를 만드는 비용이 거의 0인 이유다. 태그는 움직이지 않는 이름표다. 그리고 HEAD는 ‘지금 어느 브랜치에 있는지’를 가리키는 참조의 참조다.

브랜치와 HEAD: main은 C, feature는 E, 태그 v1.0은 B를 가리키고 HEAD는 main을 가리킨다. 오른쪽은 각 이름표 파일의 내용이다ABCDEHEADmainv1.0feature이름표 파일의 내용.git/HEADref: refs/heads/main.git/refs/heads/main759b922… (C).git/refs/heads/featuree71bab7… (E).git/refs/tags/v1.03108520… (B)화살표는 부모 쪽을 가리킨다. 브랜치는 커밋 ID 하나를 담은 파일이다.
커밋 그래프와 이름표. 화살표는 자식에서 부모로 향한다. 오른쪽은 각 이름표 파일에 실제로 적힌 내용이다(큰 저장소에서는 packed-refs 파일에 모여 있기도 하다).
  • detached HEAD: 브랜치가 아니라 커밋을 직접 체크아웃하면 HEAD가 커밋 ID를 바로 담는다. 이 상태에서 만든 커밋은 어떤 브랜치도 가리키지 않아, 다른 곳으로 옮기면 찾기 어려워진다. 남기려면 git switch -c 새-브랜치로 이름을 붙인다.
  • reflog: HEAD와 브랜치가 어디를 거쳐 왔는지는 git reflog에 남는다(기본 90일). 잘못된 reset이나 리베이스로 ‘사라진’ 커밋도 대개 여기서 ID를 찾아 되살릴 수 있다. 아무도 가리키지 않는 객체는 나중에 git gc가 지운다.

병합: 병합 기준을 찾아 3-way로 합친다

git merge feature는 지금 브랜치와 feature의 변경을 합친다. 먼저 두 끝에서 부모를 따라 올라가 공통 조상 중 가장 가까운 커밋, 병합 기준(merge base)을 찾는다. 그다음 기준에 견줘 ‘우리 쪽이 바꾼 것’과 ‘그쪽이 바꾼 것’을 모두 적용한다. 세 판(기준·우리·그쪽)을 보므로 3-way 병합이라 한다.

병합 단계 그림: main(A-B-C)과 feature(B에서 갈라진 D-E)의 조상을 칠해 병합 기준 B를 찾고, 부모가 C와 E인 병합 커밋 M을 만든다ABCDEmain (HEAD)ABCDEmain (HEAD)ABCDEmain (HEAD)ABCDE병합 기준main (HEAD)ABCDE병합 기준main (HEAD)ABCDEMmain (HEAD)featuremain에서 닿음feature에서 닿음
1 / 6

main은 C, feature는 E를 가리킨다. HEAD는 main이다. git merge feature를 실행한다.

병합의 순서. Git은 두 브랜치 끝에서 부모를 따라 올라가 공통 조상 중 가장 가까운 커밋(병합 기준)을 찾고, 기준에 견준 양쪽 변경을 합친다. 한쪽이 다른 쪽의 조상이면 커밋을 만들 필요 없이 이름표만 옮긴다(fast-forward).
  • fast-forward: main이 아직 B에 있었다면 main은 E의 조상이다. 합칠 변경이 한쪽에만 있으니 새 커밋 없이 main 이름표만 E로 옮긴다. 같은 그래프로 계산하면 결과는 fast-forward다. 이력을 일부러 남기고 싶으면 --no-ff로 병합 커밋을 강제한다.
  • 왜 두 판이 아니라 세 판인가: 두 판만 보면 어느 줄이 ‘한쪽이 추가한 것’인지 ‘다른 쪽이 지운 것’인지 알 수 없다. 기준이 있어야 누가 무엇을 바꿨는지 판정된다.
  • 병합 기준이 여럿일 때: 서로 엇갈려 병합한(criss-cross) 이력에서는 가장 가까운 공통 조상이 둘 이상일 수 있다. Git 2.34부터 기본인 ort 전략은 그 기준들을 먼저 병합해 가상의 기준 하나를 만든다.

리베이스와 병합

리베이스(git rebase main)는 feature의 커밋들을 하나씩 떼어 main 끝 위에 다시 적용한다. 결과는 갈래 없는 한 줄 이력이다. 그런데 커밋 ID는 부모 ID까지 넣어 해시하므로, 변경 내용이 같아도 부모가 바뀐 커밋은 새 커밋이다. 옛 D·E는 그대로 남아 있다가 아무도 가리키지 않으면 지워진다.

병합과 리베이스 비교: 왼쪽 병합은 D-E 갈래와 병합 커밋 M이 남고, 오른쪽 리베이스는 C 뒤에 새 커밋 D′·E′를 만들어 한 줄이 되며 옛 D·E는 아무도 가리키지 않게 된다merge: 갈래가 남는다rebase: 한 줄이 된다ABCMDEABCD′E′DE70cb175e71bab72c259a746dc61970cb175e71bab7D′·E′는 D·E와 같은 변경이지만 부모가 C라서 ID가 다르다(커밋 ID는 부모 ID까지 해시한다).
같은 출발점에서 병합한 결과와 리베이스한 결과. 아래 짧은 ID는 같은 메시지·같은 트리로 부모만 바꿔 계산한 값이다.
  • 병합은 있었던 일을 그대로 남긴다. 언제 갈라져 언제 합쳤는지 보이지만, 큰 팀에서는 그래프가 얽힌다.
  • 리베이스는 읽기 좋은 한 줄을 만든다. 대가로 이력을 고쳐 쓴다. 각 커밋이 새 기준 위에서 처음 시험되는 셈이라 중간 커밋이 빌드되지 않을 수도 있다.
  • 규칙 하나: 남과 공유한 커밋은 리베이스하지 않는다. 다른 사람의 저장소에는 옛 D·E가 남아 있어, 다시 합칠 때 같은 변경이 두 번 들어온다. 내 브랜치를 고쳐 쓴 뒤 올려야 한다면 git push --force-with-lease를 쓴다. 원격이 내가 마지막으로 본 위치일 때만 덮어써서 남의 커밋을 날리는 사고를 막는다.
  • squash 병합: PR의 커밋을 하나로 뭉쳐 main에 올리는 방식이다. main은 PR 하나에 커밋 하나로 깔끔하지만 PR 안의 작은 단계는 사라진다.

충돌: 양쪽이 같은 곳을 다르게 고쳤을 때

3-way 병합의 규칙은 단순하다. 한쪽만 바꾼 곳은 바꾼 쪽을 고르고, 양쪽이 똑같이 바꿨으면 그것을 고르고, 양쪽이 다르게 바꾼 곳만 충돌이다. 아래 설정 파일에서 네 줄 중 2줄은 자동으로 정해지고 1줄만 사람이 고른다. 실제 Git은 줄 단위 diff로 바뀐 덩어리(hunk)를 맞춘 뒤 같은 규칙을 덩어리마다 적용한다.

3-way 병합 표: 네 줄 중 port는 그쪽, timeout은 우리 쪽 값을 자동으로 고르고, retries는 같으며, log는 양쪽이 다르게 바꿔 충돌한다. 아래는 파일에 남는 충돌 표시다기준(B)port = 8080timeout = 30retries = 3log = infomain(우리)port = 8080timeout = 60retries = 3log = warnfeature(그쪽)port = 9090timeout = 30retries = 3log = debug병합 결과port = 9090그쪽timeout = 60우리retries = 3같음충돌: 직접 고른다<<<<<<< HEADlog = warn=======log = debug>>>>>>> feature충돌 1곳: 자동으로 고를 수 없는 줄만파일 안에 양쪽 내용을 표시해 둔다.하나를 고르거나 새로 고친 뒤git add → git commit
줄마다 기준과 비교해 고른 결과. 회색 글자는 기준과 같은 줄, 결과 칸의 색은 자동으로 고른 줄이다. 충돌한 줄은 파일 안에 양쪽 내용을 표시해 둔다.
  • 기준도 함께 보기: git config merge.conflictStyle zdiff3로 두면 충돌 표시에 기준 판(||||||| 아래)도 나온다. ‘원래 info였는데 한쪽은 warn, 한쪽은 debug로 바꿨다’가 보여 판단이 훨씬 쉽다.
  • 문법상 충돌만 잡는다: 한쪽이 함수 이름을 바꾸고 다른 쪽이 옛 이름으로 새 호출을 추가하면, 줄이 겹치지 않아 깔끔하게 병합되지만 빌드가 깨진다. 병합한 결과도 CI에서 다시 시험해야 하는 이유다.
  • 충돌을 줄이는 법: 브랜치를 짧게 유지하고 main을 자주 받아 들인다. 오래 떨어져 있을수록 같은 곳을 고칠 확률이 커진다. 포매터를 팀 전체가 똑같이 쓰는 것도 의미 없는 공백 충돌을 없앤다.

원격 저장소: fetch, pull, push

Git은 분산 시스템이다. 내 저장소와 원격(origin)은 서로 다른 커밋 그래프를 갖고, 명령으로 객체와 참조를 주고받는다. 내 저장소에는 원격 브랜치의 위치를 기억해 두는 원격 추적 브랜치(origin/main)가 있다. 이것은 마지막으로 통신했을 때의 모습이라, 그 사이 동료가 올린 커밋은 fetch하기 전까지 보이지 않는다.

원격 저장소와 명령: 원격 origin의 main이 F일 때, fetch는 내 저장소의 origin/main을 F로 갱신하고, merge(pull)는 그것을 내 main에 합치며, push는 내 main을 원격 main으로 보낸다내 저장소main → Eorigin/main → F마지막 fetch 때 본 원격 위치merge원격 originmain → F동료가 push한 커밋 Fpushfetchpull = fetch + merge(설정에 따라 rebase)push는 원격 main이 내 main의 조상일 때만(fast-forward) 받아들여진다.
세 명령의 방향. fetch는 내 브랜치를 건드리지 않고 원격 추적 브랜치만 갱신한다. pull은 fetch 뒤에 병합(또는 리베이스)까지 한다. push는 원격 브랜치를 내 커밋으로 옮긴다.
  • fetch를 먼저: git fetch 후 git log main..origin/main으로 무엇이 들어올지 보고 합치면 놀랄 일이 줄어든다.
  • push가 거부될 때: 원격 main이 내 main의 조상이 아니면(그 사이 누가 올렸으면) push는 non-fast-forward로 거부된다. 받아서 합친 뒤 다시 올린다. 강제 push로 덮어쓰면 남의 커밋이 원격에서 사라진다.
  • pull의 기본 동작: 받은 쪽과 내 쪽이 둘 다 앞서 있으면 병합 커밋이 생긴다. pull.rebase 설정으로 리베이스하게 하거나, pull.ff only로 fast-forward만 허용하고 나머지는 직접 고르게 할 수 있다.

브랜치 전략

팀이 브랜치를 언제 만들고 언제 합칠지 정한 약속이 브랜치 전략이다. 핵심 변수는 브랜치가 얼마나 오래 사는가다. 오래 살수록 병합이 커지고 충돌이 늘며, 통합된 상태로 시험받는 시간이 줄어든다.

브랜치 전략 비교: 트렁크 기반은 하루 안에 합치는 짧은 갈래, GitHub Flow는 기능 브랜치와 PR로 main에 합치고, Git Flow는 main·develop·feature 갈래와 release 병합을 오래 유지한다트렁크 기반작은 변경을 하루 안에 main에 합친다. 덜 된 기능은 플래그로 숨긴다GitHub FlowPR 병합PR 병합브랜치 → PR·리뷰 → main 병합 → 곧바로 배포. main은 늘 배포할 수 있다Git Flowmaindevelopfeaturereleasev1.0갈래 여럿을 오래 유지하고 판 번호 단위로 출시한다. 정해진 출시 주기가 있는 제품에 맞는다
세 전략의 모양. 굵은 선이 배포 기준이 되는 브랜치다. 아래로 갈수록 갈래가 길고 많아진다.
  • 트렁크 기반 개발: 모두가 main(트렁크)에 직접 또는 하루 안에 사라지는 짧은 브랜치로 합친다. 덜 만든 기능은 기능 플래그로 숨긴다. 지속적 통합(CI)의 원래 뜻에 가장 가깝고, 강한 자동 테스트가 전제다.
  • GitHub Flow: main은 늘 배포할 수 있게 두고, 기능마다 브랜치를 만들어 PR로 리뷰받아 합친 뒤 배포한다. 웹 서비스에서 가장 흔한 방식이고, 브랜치를 짧게 유지하면 트렁크 기반과 거의 같아진다.
  • Git Flow: main(출시본)과 develop(다음 판)을 따로 두고, feature·release·hotfix 브랜치를 규칙대로 오간다. 여러 판을 동시에 지원하거나 출시 주기가 정해진 제품(설치형 소프트웨어, 앱 스토어 심사)에 맞는다. 원작자도 지속적으로 배포하는 웹 앱이라면 더 단순한 흐름을 권한다.

큰 저장소: LFS, partial clone, 모노레포

Git은 clone할 때 모든 판의 모든 객체를 받는 것이 기본이다. 소스 코드에는 문제가 없지만, 판마다 통째로 바뀌는 큰 바이너리(텍스처, 영상, 모델 가중치)가 섞이면 저장소가 금세 무거워진다. 40MB 파일을 25번 고쳤다면 그 파일만으로 1000MB를 받아야 한다.

Git LFS의 효과: 40MB 파일 25판을 그대로 담으면 clone이 1000MB를 받지만, LFS는 지금 판 40MB와 포인터 파일만 받는다40MB 바이너리를 25번 고친 저장소를 clone할 때 받는 양Git에 그대로1000MBGit LFS40MB (약 25분의 1)version https://git-lfs.github.com/spec/v1oid sha256:c0e6623abfbed73c146…size 40000000저장소에는 이 133바이트만남고, 실제 파일은 LFS서버에서 체크아웃 때 받는다압축된 바이너리는 판끼리 델타 압축이 거의 안 돼 판마다 통째로 쌓인다고 보고 계산했다.
큰 바이너리 하나의 이력을 받는 양. LFS는 저장소 안의 파일을 짧은 포인터로 바꾸고, 진짜 내용은 별도 서버에 둔다.
  • Git LFS: 지정한 패턴(*.psd 등)의 파일을 포인터 파일(133바이트)로 바꿔 커밋하고, 실제 내용은 체크아웃할 때 필요한 판만 받는다. 위 예에서 받는 양은 약 25분의 1이 된다. 서버 저장 용량과 전송량이 따로 과금되는 경우가 많고, 바이너리는 병합할 수 없으니 잠금(git lfs lock)을 함께 쓴다.
  • partial clone: git clone --filter=blob:none은 커밋과 트리는 다 받고 blob은 필요할 때 받는다. LFS 없이도 큰 이력을 가볍게 받을 수 있지만 서버가 지원해야 한다. --depth 1(얕은 clone)은 이력 자체를 자르므로 CI처럼 지금 판만 필요할 때 쓴다.
  • sparse-checkout: 저장소는 그대로 두고 작업 폴더에 일부 디렉터리만 꺼낸다. partial clone과 함께 쓰면 거대한 모노레포에서도 내 팀 폴더만 받아 일할 수 있다. 이런 설정을 한 번에 해 주는 도구가 Git에 들어 있는 Scalar다.
  • 모노레포와 폴리레포: 여러 프로젝트를 한 저장소에 두면 서로 걸친 변경을 커밋 하나로 원자적으로 하고 의존성 판을 하나로 맞추기 쉽다. 대신 빌드·테스트를 바뀐 부분만 돌리는 도구(Bazel, Nx, Turborepo 등)와 위의 큰 저장소 기법이 필요해진다. 저장소를 나누면 각자 가볍지만, 여러 저장소에 걸친 변경은 판 맞추기가 일이 된다.

커밋 위생과 bisect

좋은 커밋은 한 가지 일만 하고, 그 자체로 빌드되며, 왜 바꿨는지 적혀 있다. 이 습관의 값어치는 버그를 추적할 때 드러난다. git bisect는 좋은 커밋과 나쁜 커밋 사이를 이분 탐색해 처음 나쁜 커밋을 찾는다. 사이에 커밋이 1,000개면 최악 10번, 100만 개라도 20번만 시험하면 된다. git bisect run ./test.sh처럼 스크립트를 주면 자동으로 끝까지 간다.

git bisect: 0번은 좋고 15번은 나쁜 커밋 16개에서 가운데를 시험해 범위를 반씩 줄여, 네 번 만에 11번이 처음 나쁜 커밋임을 찾는다01234567891011121314151회7번 좋음2회11번 나쁨3회9번 좋음4회10번 좋음후보가 매번 반으로 줄어 11번이 처음 나쁜 커밋으로 남는다.
커밋 16개에서 처음 나쁜 커밋 찾기. 흐린 칸은 이미 후보에서 빠진 커밋이다. 찾아낸 커밋이 ‘리팩터링 + 기능 + 포맷’을 한꺼번에 담고 있으면 다시 그 안을 뒤져야 한다.
  • 메시지: 첫 줄은 50자 안팎의 요약을 명령형으로, 한 줄 띄우고 본문에는 ‘무엇을’보다 ‘왜’를 적는다(Chris Beams의 일곱 규칙). Conventional Commits의 feat:·fix: 머리말을 쓰면 변경 기록과 판 번호를 자동으로 만들 수 있다.
  • 태그와 판 번호: 출시한 커밋에는 git tag -a v1.4.0처럼 주석 태그를 단다. 번호는 흔히 유의적 버전(SemVer)의 MAJOR.MINOR.PATCH를 따른다. 호환되지 않는 API 변경이면 MAJOR, 호환되는 기능 추가면 MINOR, 호환되는 버그 수정이면 PATCH를 올린다. Conventional Commits의 fix:는 PATCH, feat:는 MINOR, BREAKING CHANGE는 MAJOR에 대응한다. 0.y.z는 초기 개발 단계라 무엇이든 바뀔 수 있다는 뜻이고, 한 번 내보낸 판의 내용은 고치지 않고 새 판으로 낸다.
  • 올리기 전에 정리: 작업 중 커밋(‘wip’, ‘오타’)은 공유하기 전에 git rebase -i나 git commit --fixup + --autosquash로 뜻 있는 단위로 묶는다. git add -p로 한 파일 안에서도 관련된 조각만 골라 담을 수 있다.
  • 넣지 말 것: 비밀값(API 키, 비밀번호)은 한 번 커밋되면 이력에 남아, 다음 커밋에서 지워도 clone한 누구나 꺼낼 수 있다. 커밋 전 검사(pre-commit 훅, 비밀값 스캐너)로 막고, 새어 나갔다면 이력 정리보다 키 교체가 먼저다. 빌드 산출물과 의존성 폴더는 .gitignore로 뺀다.

되돌리기: restore, reset, revert, cherry-pick

‘되돌린다’는 말이 가리키는 대상이 셋이다. 작업 폴더의 파일, 다음 커밋에 담을 스테이지(index), 그리고 브랜치가 가리키는 커밋이다. 명령마다 건드리는 층이 달라서, 무엇을 지우는지 모르고 쓰면 작업을 잃는다. 흔히 헷갈리는 다섯 가지를 표로 정리했다.

명령하는 일이력을 고쳐 쓰나
git restore 파일작업 폴더의 파일을 스테이지(또는 --source로 준 커밋) 내용으로 되돌린다. --staged는 스테이지에서만 내린다.아니다. 다만 저장 안 한 수정은 사라진다.
git reset --soft 커밋브랜치 이름표만 그 커밋으로 옮긴다. 스테이지와 파일은 그대로라 커밋 몇 개를 하나로 다시 묶을 때 쓴다.그렇다
git reset 커밋(--mixed, 기본)이름표와 스테이지를 옮기고 파일은 둔다. 변경이 ‘커밋 안 한 수정’으로 돌아온다.그렇다
git reset --hard 커밋이름표·스테이지·작업 폴더를 모두 그 커밋으로 맞춘다. 커밋하지 않은 수정은 reflog에도 남지 않아 되살릴 수 없다.그렇다
git revert 커밋그 커밋의 변경을 거꾸로 적용한 새 커밋을 만든다. 옛 커밋은 그대로 남는다.아니다
  • 공유한 뒤에는 revert: 리베이스와 같은 이유로, 이미 push한 커밋을 reset으로 지우고 강제 push하면 동료의 저장소와 어긋난다. 남이 받았을 수 있는 커밋은 revert로 되돌리고, reset은 아직 내 저장소에만 있는 커밋에 쓴다. 병합 커밋을 revert할 때는 어느 부모를 본줄기로 볼지 -m 1처럼 정해 줘야 한다.
  • cherry-pick: 다른 브랜치의 커밋 하나가 들인 변경만 지금 브랜치에 새 커밋으로 옮긴다. main의 버그 수정을 출시 브랜치에 가져올 때 흔히 쓴다. 부모가 다르니 ID도 새로 생기고, 나중에 두 브랜치를 병합하면 같은 변경이 두 커밋으로 남는다. -x를 주면 메시지에 원래 커밋 ID를 적어 출처를 따라갈 수 있다.
  • stash: 커밋하기 이른 수정을 잠시 치워 두고 깨끗한 상태로 브랜치를 옮길 때 쓴다. 기본으로는 Git이 추적하는 파일만 치우므로 새로 만든 파일까지 넣으려면 -u를 준다. 오래 쌓아 두면 무엇이었는지 잊으니, 길어질 일이면 임시 브랜치에 커밋하는 편이 안전하다.

핵심 자료

자료한 줄 요지
Pro Git (Scott Chacon, Ben Straub)Git 공식 사이트에 무료로 공개된 책. 기초부터 브랜치, 서버, 내부 구조(10장)까지 한 권에 담았다.
Pro Git 10.2 Git Objectsblob·tree·commit을 hash-object, cat-file로 직접 만들어 보며 내용 주소 저장소를 설명한다.
Git 레퍼런스 문서명령마다의 공식 설명서. 옵션의 정확한 뜻은 결국 여기서 확인한다.
Hash Function TransitionSHA-1에서 SHA-256으로 옮겨 가는 설계 문서. 두 형식을 함께 쓰는 변환 계획까지 적혀 있다.
Git BreakingChangesGit 3.0에서 바뀔 기본값 목록. 새 저장소의 기본 해시를 SHA-256으로 바꾸는 계획이 있다.
Trunk Based Development짧은 갈래와 잦은 통합을 중심으로 한 브랜치 전략을 사례와 함께 정리한 사이트.
A successful Git branching model (Vincent Driessen)Git Flow의 원문(2010). 2020년에 덧붙인 글에서 지속적으로 배포하는 웹 앱에는 더 단순한 흐름을 권했다.
Conventional Commits 1.0.0feat·fix 같은 머리말로 커밋 메시지를 쓰는 약속. 변경 기록과 판 번호를 자동으로 만들 수 있다.