Git 알아보기
Git
git을 add, rebase, commit, push, merge, pull
만 아는 상태라 더 깊은 지식이 필요하다는 생각이 들었다.
git을 왜 쓸까?
Git은 버전 관리 시스템이다. 코드나 문서의 변경 이력을 추적하고(형상관리라고 하는), 여러 개발자들이 동시에 작업해도 충돌을 방지하게 해준다.
누가 언제 무엇을 바꿨는지
+ 특정 기점으로 되돌릴 수 있음
이 Git을 사용하는 목적이라고 생각한다.
좀 더 효율을 높이고자 나온 git 브랜치 전략들이 git flow, github flow와 같은 것인데, git flow 정도는 사용해봤다.
git 브랜치 전략?
브랜치 전략은 git 구조화 룰인데, 이건 팀마다 다를 수 있다고 한다. 이번엔 학습의 의미가 강하니 가장 많이 사용되는 3가지 전략 git flow, github flow, trunk-based
를 살펴 보자.
git flow
graph TD
subgraph main
M1["● main: 초기"]
M2["● main: v1.0"]
M3["● main: hotfix 병합"]
end
subgraph develop
D1["● develop: 시작"]
D2["● develop: 로그인 병합"]
end
subgraph feature
F1["● feature/login"]
end
subgraph release
R1["● release/1.0"]
end
subgraph hotfix
H1["● hotfix/urgent"]
end
M1 --> D1
D1 --> F1
F1 --> D2
D2 --> R1
R1 --> M2
M2 --> H1
H1 --> M3
git flow는 feture별 branch와, 개발용, 배포용 브랜치로 나누어져있다. main은 항상 배포되게, develop은 개발 통합 브랜치로, 나머지는 feature/*
release/*
hotfix/*
로 관리한다.
단계별 브랜치가 명확하고, 기능단위로 브랜치가 나뉘어져 있어서 규모가 큰 프로젝트에 사용하기 적합한 전략이다. QA도 따로 가능한 구조이기 때문에 안정성도 높다고 볼 수 있다.
github flow
graph TD
subgraph main
M1["● main: 초기"]
M2["● main: 로그인 병합"]
M3["● main: 회원가입 병합"]
end
subgraph feature_login
F1["● feature/login"]
end
subgraph feature_signup
F2["● feature/signup"]
end
M1 --> F1
F1 --> M2
M2 --> F2
F2 --> M3
github flow는 git flow보다 더 빠르고 유연한 형태다. 항상 배포가 가능한 상태로 있는 main 브랜치를 기준으로, 기능 개발이 필요할 때 main에서 따오고, 완성되면 main으로 병합시킨다. 다만 git flow가 브랜치를 단계별로 나눠놔서 안정성을 확보했던거랑 비교하면 많이 간소화 되어있기 때문에, 배포프로세스에 대한 신중함이 필요하다. main으로 합쳤을 때 배포가 깨질 수 있기 때문이다.
그래서 github flow는 git flow보다 팀 규모가 작고, 빠른 개발이 필요한 환경에서 채택한다고 한다.
Trunk-based Development
graph TD
subgraph main
M1["● main: 초기"]
M2["● main: 버튼 수정 병합"]
M3["● main: API 최적화 병합"]
end
subgraph task_button
T1["● task/button-fix"]
end
subgraph task_api
T2["● task/api-optimize"]
end
M1 --> T1
T1 --> M2
M2 --> T2
T2 --> M3
어떻게 보면 github flow와 비슷한 것 같다. main
이 유일한 기준 브랜치이며 작은 작업을 따로 짧게 브랜치로 만들고 빠르게 병합하는 걸 목표로한다.
브랜치를 따지 않고 거의 바로 main에 병합하고, 기능이 완성되지 않아도 병합하며 개발 속도, CI 중심 전략이라 github flow보다 안정성은 떨어지지만 테스트 커버리지가 매우 높을 경우
+ CI가 매우 안정적
으로 되어있을 경우 채택하면 좋은 전략같다.
이제 git 명령어를 잘 써보자
1
2
3
git checkout main
git pull origin main
git checkout -b feature/login
브랜치를 생성하는 명령어다. -b
옵션을 추가하면, 브랜치 생성과 동시에 전환이 된다. 브랜치를 생성할 때 주의할 점은 항상 최신화된 대상 브랜치에서 checkout해야된다는 점이다.
그래서 main으로 브랜치를 전환해 최신화하고, 브랜치를 생성하는 것이다.
1
2
3
git add .
git commit -m "feat: 로그인 페이지 UI 추가"
git push origin feature/login
질리도록 사용한 명령어다. 변경이 발생한 파일들을 스테이지에 올리고, commit으로 기록한다. 커밋메시지에 사용될 커밋 컨벤션을 꼭 잘 지키도록 하자.
커밋 뒤에 push를 하면 되는데, 원격저장소에는 로컬에서 생성한 브랜치가 올라가있지 않다. 그래서 origin feature/login
으로 원격저장소에 브랜치를 올려주는 것이다.
병합과정은 더 단순하다. 근데 방향이 중요하다.
1
2
3
4
5
git checkout main
git pull origin main
git merge feature/login
git push origin main
feature/login 브랜치를 main에 합치는 것이다. 나는 합쳐질 브랜치에 위치해야한다. 그래서 main으로 이동해 최신화 해주고, merge로 브랜치를 병합하는 것이다.
충돌나면 잘 알려주니까 그냥 해결하면 된다.
그럼 이제 제일 중요한 git 실수 했을 때 대처방법을 알아보자. 물론 커밋을 굉장히 신중히하는 것이 첫번째이겠지만, 그래도 사람은 실수를 하게 되어있다.
실수 시나리오는 많다.
커밋을 잘못날렸을 경우, 너무 자잘하게 해서 지저분한 경우, 실수로 다른 브랜치에서 딴 경우, 커밋을 그냥 없던일로 하고 싶은 경우… 생각만해도 아찔하지만 침착하게 처리하는 게 더 중요하다.
1
2
3
4
git commit --amend
git add LoginUseCase.kt
git commit --amend
--amend
로 방금 날린 commit 메시지를 수정할 수 있다. –amend를 사용하면 vi 화면
같은 게 나오고, 거기서 수정하면 된다. vi가 익숙하지 않다면 vscode를 사용하는 방법도 있는 것 같은데 그건 잘 모르겠다.
빠진 파일을 추가하고 커밋메시지를 수정하는 것도 가능하다. 그럼 이미 푸시해버린 경우는 어떻게 해야될까?
1
2
git commit --amend
git push --force
git push --force
로 덮어 씌워야한다. 문제는 협업일 때 상의 없이 이러면 문제가 생길 수 있으니 force를 사용할 때 꼭 알리도록 하자.
커밋을 신중히 해야하지만, 어쩔 수 없이 중간 저장과 같은 걸 하려면 자잘한 커밋이 늘어날 가능성이 있다. 그럴경우 커밋내역을 단순화 하고 싶거나, 의미없게 넣은 커밋 메시지를 수정하고 싶을 것이다. 이럴 때 squash
를 써야한다.
시작은 rebase
다. 이전 커밋을 관리하는 영역이기 때문에 rebase로 커밋을 수정해야한다.
1
git rebase -i HEAD~3
이 명령어는 최근 3개의 커밋을 내가 원하는 대로 수정하거나 합치기 위해 인터랙티브 리베이스를 시작하자는 의미다. 인터렉티브 리베이스는 amend때와 마찬가지로 vi와 같은 에디터를 띄워준다.
중요한 것은 스테이징 된 커밋메시지를 수정하는 것이기 때문에 커밋이 없다면 오류가 발생할 것이다.
도움말에도 있지만, pick, squash, drop를 알면 된다. 커밋삭제는 drop, 커밋 합치기가 squash다. 지금 커밋을 하나만 해두고, HEAD~1이라 설명과는 상이하지만 맨위 커밋만 pick으로 두고, 나머지를 다 squash나 s로 바꿔주면 된다.
1
2
3
pick a123456 feat: 로그인 폼 UI 구현
reword 02be4f8 잘못된 메시지 수정
drop 9c8d1c3 필요 없는 커밋
저장하고 닫으면 이제 커밋이 예쁘게 바뀌어있는 걸 볼 수 있다. 이 경우에도 이미 원격에 push됐다면 --force
옵션을 켜줘야한다.
이 과정을 수행하다가 꼬였다면 git rebase --abort
로 rebase 명령 실행 전으로 돌리면 된다. 실수하면 복구해버리자.
커밋합치기를 원할 수도 있고, 커밋 자체를 되돌리고 싶을 수 있다. 이때는 rebase가 아닌 reset도 알아야 한다.
1
2
3
git reset --soft HEAD~1
git reset --mixed HEAD~1
git reset --hard HEAD~1
git reset은 커밋을 없애거나 이전 상태로 되돌리는 명령어로 HEAD 포인터 자체를 움직이는 명령어라고 생각하면된다.
옵션 | HEAD 이동여부 | 스테이징 영역 | 파일상태 |
---|---|---|---|
–soft | O | 유지됨 | 유지 |
–mixed(이게 디폴트) | O | 초기화 돼서 git add된 내역도 다 날아감 | 유지 |
–hard | O | mixed와 동일 | 수정사항까지 삭제됨 |
soft는 스테이징한 상태까지는 유지된거고 커밋만 없앤다. 즉 git add 된 상태는 유지되는 것이다. 그래서 메시지 수정이나 커밋을 다시하고 싶을 때 쓰면 된다.
mixed는 스테이징 올린 것도 풀어버리는 것이다. 그래도 파일은 수정된 상태로 남아있다.
hard는 매우 조심해야한다. 커밋도, 스테이징도, 파일상태까지 다 날려버린다. 즉 커밋 전 상태로 완전히 돌려버리는 것이다.
HEAD로 몇 번째 전 커밋을 선택할지 고를 수도 있지만, 특정 커밋으로 바로 되돌리는 방법도 있다.
1
git log --oneline
이거로 커밋로그를 보고
1
git reset --soft abc1234
커밋아이디로 바로 되돌릴 수 있다.