포스트

Git 스터디 노트 (2) 기본 명령어: 사이클, 브랜치, 원격, 되돌리기

Git의 기본 명령어를 세 영역 모델 위에 얹어 정리합니다. status/add/commit 기본 사이클, .gitignore, 브랜치와 충돌 해결, 원격 push/fetch/pull, 그리고 상황별 되돌리기(restore, reset, revert, reflog)를 다룹니다.

Git 스터디 노트 (2) 기본 명령어: 사이클, 브랜치, 원격, 되돌리기

1편에서 잡은 스냅샷과 세 영역 모델 위에 실제 명령어를 얹는 글입니다. 명령어가 헷갈릴 때마다 “어느 영역에서 어느 영역으로 무엇을 옮기는가”로 돌아오면 됩니다.

기본 사이클: 네 개 명령이 전부

1편의 세 영역 모델에서 기본 작업은 네 개 명령이 전부입니다.

1
2
3
4
5
git status                  # 지금 세 영역의 상태. 막히면 무조건 이것부터
git add src/train.py        # working에서 staging으로 (파일 지정)
git add -p                  # 파일 안에서도 덩어리(hunk) 단위로 골라 담기
git commit -m "학습 루프에 조기 종료 추가"
git log --oneline --graph   # 이력을 그래프로 확인

git status는 아무것도 바꾸지 않는 안전한 명령이고, 지금 상태뿐 아니라 “다음에 뭘 하면 되는지”까지 안내문으로 알려줍니다. 뭔가 꼬였다 싶으면 반사적으로 git status. 이것이 Git에서 가장 중요한 습관입니다.

git add -p1편에서 말한 “커밋 하나 = 의미 하나”를 실현하는 도구입니다. 한 파일 안에서 버그 수정 부분만 골라 담고 리팩토링 부분은 다음 커밋으로 미룰 수 있습니다.

변경 내용 확인은 diff인데, 세 영역 모델을 알면 옵션이 자연스럽습니다.

1
2
git diff             # working vs staging, 아직 add 안 한 변경
git diff --staged    # staging vs 마지막 커밋, 커밋될 내용 미리보기

.gitignore: 추적하지 않을 것을 선언한다

데이터 파일, 가상환경, 자격 증명은 저장소에 들어가면 안 됩니다. 프로젝트 루트의 .gitignore에 패턴을 선언합니다.

.venv/
data/
*.parquet
.env
__pycache__/

데이터를 다루는 프로젝트에서 특히 중요합니다. 수백 MB짜리 데이터나 모델 가중치를 실수로 커밋하면 저장소가 영구히 무거워지고(스냅샷 모델이라 이력에서 빼기 어렵습니다), .env의 API 키를 커밋하면 보안 사고입니다. 저장소에는 코드와 설정만, 데이터는 경로와 받는 방법만 남기는 것이 원칙입니다.

주의할 점 하나. 이미 추적 중인 파일은 .gitignore에 추가해도 무시되지 않습니다. git rm --cached 파일로 추적을 먼저 끊어야 합니다.

브랜치: 만들고, 옮기고, 합치기

1편에서 브랜치는 커밋을 가리키는 이름표라고 했습니다. 그래서 명령도 가볍습니다.

1
2
3
4
5
git branch                      # 브랜치 목록
git switch -c feat/early-stop   # 만들면서 이동 (checkout -b의 현대식 표기)
git switch main                 # 다른 브랜치로 이동
git merge feat/early-stop       # 현재 브랜치로 대상 브랜치를 합침
git branch -d feat/early-stop   # 합친 브랜치 정리

switch는 비교적 최근에 정리된 명령입니다. 예전에는 checkout이 브랜치 이동과 파일 복원을 겸해서 헷갈렸는데, 지금은 브랜치 이동은 switch, 파일 복원은 restore로 나뉘었습니다. 옛 자료에 나오는 git checkout -bgit switch -c와 같습니다.

충돌 해결 절차

merge에서 두 브랜치가 같은 파일의 같은 부분을 다르게 고쳤을 때 충돌이 납니다. 파일을 열면 두 버전이 표시되어 있습니다.

1
2
3
4
5
<<<<<<< HEAD
lr = 0.001          # 내 브랜치의 내용
=======
lr = 0.0005         # 합치려는 브랜치의 내용
>>>>>>> feat/early-stop

절차는 세 단계가 전부입니다.

  1. 원하는 쪽을 남기고 표식(<<<<<<<, =======, >>>>>>>)을 지웁니다. 둘을 섞어야 하면 직접 편집합니다.
  2. git add 파일로 “해결했다”고 표시합니다.
  3. git commit으로 마무리합니다. 병합 커밋 메시지는 대개 자동으로 채워집니다.

처음 만나면 당황하지만 절차는 이게 전부입니다. 도중에 그만두고 싶으면 git merge --abort로 병합 전으로 되돌릴 수 있습니다.

원격: push, fetch, pull

GitHub에 있는 저장소는 origin이라는 이름의 원격(remote)으로 등록됩니다. 1편에서 말한 대로 원격과의 통신은 세 계열입니다.

1
2
3
git push                 # 내 커밋을 원격으로 올림
git fetch                # 원격의 새 커밋을 받아만 옴 (내 브랜치는 그대로)
git pull                 # fetch + merge, 받아와서 내 브랜치에 합침

pull = fetch + merge라는 등식을 기억해 두면, “pull 했더니 충돌이 났다”가 이해됩니다. 받아오기는 문제없었고 합치기에서 충돌한 것입니다. 원격의 상태를 부담 없이 확인만 하고 싶을 때는 fetchgit log origin/main으로 구경하면 됩니다.

혼자 작업할 때의 협업 리듬은 단순합니다. 작업 시작 전에 pull, 의미 단위마다 commit, 하루가 끝나기 전에 push. 팀으로 넘어가면 브랜치를 push한 뒤 Pull Request로 리뷰를 받아 합치는 흐름으로 확장되는데, 이건 3편에서 다룹니다.

되돌리기: 상황별로 도구가 다르다

Git이 어렵다는 인상의 대부분은 되돌리기 명령이 여러 개라서입니다. 하지만 “무엇을, 어디까지 되돌리는가”로 나누면 각자 자리가 명확합니다.

상황명령파괴적인가
파일 수정을 마지막 커밋 상태로git restore 파일예 (수정 내용이 사라짐)
add를 취소 (staging에서 내리기)git restore --staged 파일아니오
방금 커밋의 메시지나 내용 수정git commit --amend이력 수정
커밋 자체를 없던 일로 (로컬)git reset --soft HEAD~1이력 수정, 변경은 보존
커밋을 “취소하는 커밋”을 추가git revert 커밋아니오 (이력에 추가만)

reset의 세 모드는 세 영역 모델로 읽으면 됩니다. --soft는 repository만 되감고(커밋만 취소, 파일은 staged 상태로), --mixed(기본값)는 staging까지, --hard는 working directory까지 되감습니다. 즉 --hard만이 작업 내용을 실제로 삭제합니다.

어떤 것을 쓸지 가르는 기준은 하나입니다.

push해서 공유된 이력은 reset이 아니라 revert로 되돌린다. reset은 이력을 다시 쓰는 것이라, 이미 그 이력을 받아간 동료의 저장소와 어긋나게 됩니다. revert는 “취소했다는 사실”까지 이력에 남기므로 공유 상태에서 안전합니다. 로컬에만 있는 커밋은 reset으로 자유롭게 정리해도 됩니다.

그리고 최후의 안전망으로 git reflog가 있습니다. HEAD가 움직인 모든 기록이 남아 있어서, reset --hard로 날린 커밋조차 대부분 git reset --hard HEAD@{1}로 복구할 수 있습니다. “커밋까지 갔던 것은 웬만해선 살릴 수 있다”는 뜻이고, 그래서 1편에서 말한 대로 자주 커밋하는 습관이 곧 백업 습관입니다.

자주 쓰는 조합 몇 가지

1
2
3
4
5
6
git stash                    # 작업 중인 변경을 임시 보관하고 working을 깨끗하게
git stash pop                # 보관한 변경을 다시 꺼냄. 급히 브랜치를 옮길 때
git log --oneline -10        # 최근 10개 커밋 요약
git diff main..feat/x        # 두 브랜치 차이
git blame src/train.py       # 각 줄을 누가 어느 커밋에서 수정했는지
git checkout 커밋해시 -- 파일   # 특정 시점의 파일 하나만 가져오기

stash는 “커밋하기엔 어중간한데 브랜치는 옮겨야 할 때”의 표준 해법입니다. 다만 stash가 쌓이기 시작하면 그냥 임시 커밋을 하는 편이 안전합니다. 커밋은 reflog에 남지만 날린 stash는 찾기 어렵습니다.

정리

명령하는 일
git status세 영역의 현재 상태. 막히면 무조건 이것부터
git add / git add -pworking에서 staging으로. -p는 덩어리 단위로
git commitstaging의 내용을 스냅샷으로 확정
git switch / git merge브랜치 이동과 병합. 충돌은 add하고 commit으로 마무리
git push / git fetch / git pull원격 통신. pull = fetch + merge
git restore / reset / revert되돌리기. 공유된 이력은 revert
git reflog최후의 안전망. 날린 커밋도 대부분 복구

명령어를 다 외울 필요는 없습니다. 1편의 세 영역 그림과 git status, git reflog만 손에 익으면 나머지는 그때그때 유추하거나 찾아 쓰면 됩니다.

다음 글

여기까지가 혼자 쓰는 Git입니다. 3편에서는 이 명령어들이 팀 협업으로 확장되는 지점, 브랜치를 push하고 Pull Request로 리뷰받아 합치는 흐름을 다룹니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.