[git]GitHub 환경에서의 실전 Git 레시피
GIT 기본 사용법
=====================================================================
저희 팀은 Git으로 소스를 관리하고, GitHub로 코드 리뷰를 진행하고 있습니다.
또한, 저희 팀 상황에 맞춰 변형한 Git Flow 전략으로 브랜치 관리를 하고 있죠.
하지만 Git을 사용하며 개발을 하다 보면 종종 뭔가 잘못되거나, 예상과는 다르게 동작할 때가 많습니다.
또한, 저희 팀 상황에 맞춰 변형한 Git Flow 전략으로 브랜치 관리를 하고 있죠.
하지만 Git을 사용하며 개발을 하다 보면 종종 뭔가 잘못되거나, 예상과는 다르게 동작할 때가 많습니다.
이 글에선 저희가 현재 사용하고 있는 Git 환경을 간략히 설명한 후,
자주 마주치는 상황에서 사용할 수 있는 Git 명령어를 알아보려고 합니다.
레시피 성격인 만큼 심도 있게 설명하거나 Git의 모든 옵션을 언급하진 않겠습니다.
자주 마주치는 상황에서 사용할 수 있는 Git 명령어를 알아보려고 합니다.
레시피 성격인 만큼 심도 있게 설명하거나 Git의 모든 옵션을 언급하진 않겠습니다.
본 글에서 예제로 사용하는 repository 링크는 임의로 지어낸 것입니다.
그대로 사용할 수는 없으니, 참고하셔서 본인의 환경에 맞게 바꾸어 사용하시기 바랍니다.
로컬 환경 구축

[그림 1] GitHub의 repository 상호 관계도
[저작자] GitHub
[이미지출처] https://github.com/blog/2042-git-2-5-including-multiple-worktrees-and-triangular-workflows
[저작자] GitHub
[이미지출처] https://github.com/blog/2042-git-2-5-including-multiple-worktrees-and-triangular-workflows
[그림 1]은 GitHub 사용자가 repository를 사용하는 시나리오를 간략히 나타낸 관계도입니다.
upstream은 메인 repository입니다.
origin은 메인 repository를 자기 계정으로 fork한 repository이죠.
fork는 GitHub의 fork 버튼을 사용해 간단히 실행할 수 있습니다.
upstream은 메인 repository입니다.
origin은 메인 repository를 자기 계정으로 fork한 repository이죠.
fork는 GitHub의 fork 버튼을 사용해 간단히 실행할 수 있습니다.
이제 fork된 repository를 로컬로 clone해봅시다.
이하 명령어에 사용한 링크와 같은 repository 링크는 개인 GitHub 페이지에서 복사하여 가져올 수 있습니다.
이하 명령어에 사용한 링크와 같은 repository 링크는 개인 GitHub 페이지에서 복사하여 가져올 수 있습니다.
$ git clone https://github.com/junsik-shim/some-project.git
some-project라는 폴더가 생성되고 프로젝트가 잘 받아졌을 겁니다.
해당 폴더로 들어가서 git remote를 실행해보면 자신의 fork repository가 origin으로 이미 설정되어 있음을 볼 수 있습니다.
해당 폴더로 들어가서 git remote를 실행해보면 자신의 fork repository가 origin으로 이미 설정되어 있음을 볼 수 있습니다.
이제 메인 repository를 upstream이란 이름으로 설정해야 합니다.
메인 repository url 역시 GitHub 프로젝트 페이지에서 복사할 수 있습니다.
메인 repository url 역시 GitHub 프로젝트 페이지에서 복사할 수 있습니다.
$ git remote add upstream https://github.com/project-space/some-project.git
다시 한번 git remote를 실행해보면, 아래와 같이 설정되었음을 알 수 있습니다.
$ git remote
origin
upstream
이제 환경 구축은 끝났습니다!
기본 루틴
이슈가 발생했을 때, 이것을 처리하기 위해 pull 받고, 소스를 수정하고, 커밋하고, push 해서, pr(pull request)을 날리는 데까지 필요한 과정을 보겠습니다.
upstream에서 최신 소스 받기
merge conflict를 피하기 위해서라도 로컬의 소스를 늘 최신으로 갖춰야겠죠.
저희의 경우 최신 개발 내용은 브랜치 전략에 따라 develop 브랜치에 두고 있습니다.
따라서 아래와 같이 develop 브랜치에 접근하여 최신 개발 내용을 가져옵니다.
이 때 보통 git pull 보단 git pull --rebase를 사용하는 게 커밋 히스토리 관리에 좋습니다.
저희의 경우 최신 개발 내용은 브랜치 전략에 따라 develop 브랜치에 두고 있습니다.
따라서 아래와 같이 develop 브랜치에 접근하여 최신 개발 내용을 가져옵니다.
이 때 보통 git pull 보단 git pull --rebase를 사용하는 게 커밋 히스토리 관리에 좋습니다.
$ git pull --rebase upstream develop
새로운 브랜치 생성하기
저희는 Git Flow 전략에 따라, 기존 develop 브랜치로부터 새로운 개발 이슈에 해당하는 feature 브랜치를 만들어 작업하고 있습니다.
이렇게 함으로써 기존에 잘 작동하는 개발 코드와 새로 변경될 개발 코드를 분리하고 각각 보존할 수 있습니다.
feature 브랜치는 Git Flow 전략에서 지칭하는 단위 개발 브랜치를 보편적으로 가리키는 용어입니다.
브랜치의 이름을 지을 때 반드시 feature라는 이름을 사용해야만 하는 것은 아닙니다.
이렇게 함으로써 기존에 잘 작동하는 개발 코드와 새로 변경될 개발 코드를 분리하고 각각 보존할 수 있습니다.
feature 브랜치는 Git Flow 전략에서 지칭하는 단위 개발 브랜치를 보편적으로 가리키는 용어입니다.
브랜치의 이름을 지을 때 반드시 feature라는 이름을 사용해야만 하는 것은 아닙니다.
이제 새로운 개발을 위해 새 feature 브랜치를 생성합니다.
이슈 번호가 'qa/123'일 경우, 아래와 같이 실행합니다.
이슈 번호가 'qa/123'일 경우, 아래와 같이 실행합니다.
$ git checkout -b qa/123
Switched to a new branch 'qa/123'
수정한 파일 추가하고 커밋하기
쉘에서 명령어를 통해 수정한 파일을 추가하고 커밋하는 것은 번거로운 편입니다.
이런 작업은 IDE나 Git Client에서 하는 것을 권해드립니다.
이런 작업은 IDE나 Git Client에서 하는 것을 권해드립니다.
커밋을 fork repository로 올리기
$ git push origin qa/123
To https://github.com/junsik-shim/some-project.git
* [new branch] qa/123 -> qa/123
메인 repository로 pull request 날리기
본인의 fork repository에 가서 compare & pull request 버튼을 누릅니다.
이렇게 하면 메인 repository로 코드 리뷰를 위한 pr을 날릴 수 있습니다.
이렇게 하면 메인 repository로 코드 리뷰를 위한 pr을 날릴 수 있습니다.
발생할 수 있는 다양한 상황들
머리를 쥐어뜯게 만드는 상황들에 대한 해결법입니다. (물론 유일한 해결법은 아닙니다.)
소스 수정을 한참 하다 보니 새로운 브랜치가 아닌 develop에서 작업을 하고 있었다!
아직 커밋을 하지 않은 상태이므로, 그냥 새로운 브랜치를 만들면 수정하고 있는 사항들도 같이 옮겨갑니다.
$ git checkout -b new-branch
소스 수정을 하고 커밋도 했는데, 새로운 브랜치가 아닌 develop에서 작업을 하고 있었다!
이런 경우에는, 현재 상태로 새로운 브랜치를 만들고, develop을 커밋하기 전의 상태로 되돌리면 됩니다.
$ git checkout -b new-branch
$ git checkout develop
$ git reset --hard HEAD~1
만약 커밋이 여러 개였다면 HEAD 브랜치 뒤에 1이 아니라 커밋 개수만큼 숫자를 넣어주면 되겠죠.
새로운 브랜치를 만들어야 하는데, develop에 아직 코드 리뷰가 끝나지 않은 커밋이 포함되어 있다!
기존 작업을 별도의 브랜치에서 하지 않고, 작업 내용을 develop에 바로 커밋하여 pr을 날렸을 경우 발생할 수 있는 상황입니다.
이 경우 기존 작업의 코드 리뷰가 끝나지 않았는데, 새로운 이슈를 작업하기 위해 develop에서 새로운 브랜치를 만들면, 코드 리뷰를 받지 않은 기존 작업의 커밋이 따라붙게 됩니다.
이 경우 기존 작업의 코드 리뷰가 끝나지 않았는데, 새로운 이슈를 작업하기 위해 develop에서 새로운 브랜치를 만들면, 코드 리뷰를 받지 않은 기존 작업의 커밋이 따라붙게 됩니다.
이런 경우에는, 해당 커밋이 생성되기 전의 상태로 새로운 브랜치를 만들면 됩니다.
$ git checkout -b new-branch HEAD~1
이 명령어에서도 마찬가지로 HEAD 브랜치 뒤에 1 대신 돌아가야 하는 커밋 숫자를 넣어주시면 됩니다.
뭔가 실행했더니 이상해져 버렸다!
pull을 잘못 받거나 revert를 잘못 했거나... 아니면 뭘 했는지도 모르겠는데 뭔가 이상해졌을 때에는 reset을 해주면 됩니다.
우선 HEAD 브랜치의 변경 내역을 확인하기 위해 reflog 명령어를 사용합니다.
우선 HEAD 브랜치의 변경 내역을 확인하기 위해 reflog 명령어를 사용합니다.
$ git reflog
544ebb9 HEAD@{0}: pull upstream monkey3: Fast-forward
e94fc27 HEAD@{1}: checkout: moving from monkey3 to test
e94fc27 HEAD@{2}: checkout: moving from analyticsqa/246 to monkey3
f19873 HEAD@{3}: commit: 차트 로딩 추가 (analyticsqa/246)
e94fc27 HEAD@{4}: checkout: moving from monkey3 to analyticsqa/246
HEAD 브랜치의 내역을 통해 어떤 작업을 했는지, 그 중 어떤 것이 잘못되었는지 확인할 수 있습니다.
이제 첫 번째에 있는 pull을 취소하고 싶다면, 그 하단에 있는 HEAD@{1}로 이동하면 됩니다.
이제 첫 번째에 있는 pull을 취소하고 싶다면, 그 하단에 있는 HEAD@{1}로 이동하면 됩니다.
$ git reset --hard HEAD@{1}
이것 또한 1 대신 돌아가기를 원하는 위치를 넣으면 됩니다.
메인 repository에 push를 하고 나니 뭔가 이상해진 걸 깨달았다!
이미 public으로 나간 커밋은 reset을 하면 안 됩니다.
이런 경우에는 해당 커밋을 revert하고 다시 push를 해야 합니다.
이런 경우에는 해당 커밋을 revert하고 다시 push를 해야 합니다.
먼저 git log를 하여 revert하고 싶은 커밋의 해시(예: 028c6298eb025ff8e5ccfbba399501a7e8e50af8)를 복사한 후, 다음과 같이 실행합니다.
$ git revert 028c6298eb025ff8e5ccfbba399501a7e8e50af8
그러면 커밋 메시지를 수정할 수 있는 창이 뜨고, 종료하면 revert가 수행됩니다.
merge 커밋을 revert하고 싶다!
merge 커밋을 revert하기 위해선 명령어에 -m 플래그를 추가해줘야 합니다.
-m 뒤에는 원하는 부모의 번호를 넣어줘야 하는데, 보통 부모의 시작 번호인 1을 넣어주면 됩니다.
이 부모 번호는 브랜치들의 merge를 실행할 때 merge를 실행한 베이스 브랜치가 어느 쪽이었느냐에 따라 달라질 수 있습니다.
-m 뒤에는 원하는 부모의 번호를 넣어줘야 하는데, 보통 부모의 시작 번호인 1을 넣어주면 됩니다.
이 부모 번호는 브랜치들의 merge를 실행할 때 merge를 실행한 베이스 브랜치가 어느 쪽이었느냐에 따라 달라질 수 있습니다.
$ git revert -m 1 028c6298eb025ff8e5ccfbba399501a7e8e50af8
꼬였던 로컬 브랜치를 다 정리했고, origin에 있는 같은 브랜치를 이것으로 덮어씌우고 싶다!
강제로 덮어씌우고 싶을 땐, -f를 붙여주면 됩니다.
메인 repository에는 절대로 하면 안 됩니다! 보통은 이미 못하게 막혀있겠지만요...
메인 repository에는 절대로 하면 안 됩니다! 보통은 이미 못하게 막혀있겠지만요...
$ git push -f origin some-branch
로컬에만 있어야 하는 파일이 이미 push까지 되어버렸다!
IDE 설정 파일과 같이 로컬에만 존재해야 하는 파일이 메인 repository까지 실수로 들어갔을 때가 있습니다.
이럴 때는 Git에서는 지우면서 로컬에 있는 파일은 지우지 않아야 합니다.
이럴 때는 Git에서는 지우면서 로컬에 있는 파일은 지우지 않아야 합니다.
$ git rm --cached some-filename
이미 커밋을 했는데 커밋 메시지를 수정하고 싶다!
커밋 메시지를 수정하고 싶을 때는 commit 명령어에 인자로 amend를 사용하면 됩니다.
$ git commit --amend
이 명령어는 엄밀히 말해 기존의 커밋을 수정하는 것이 아니고 새로운 커밋을 만들어냅니다.
만약 이미 해당 커밋이 push가 되어 있는 상황이라면 다른 사람들의 히스토리를 꼬이게 만들 수 있으므로 되도록 지양해야 합니다.
만약 이미 해당 커밋이 push가 되어 있는 상황이라면 다른 사람들의 히스토리를 꼬이게 만들 수 있으므로 되도록 지양해야 합니다.
브랜치 이름을 바꾸고 싶다!
브랜치 이름을 바꾸고 싶을 땐, 아래와 같이 하면 됩니다.
$ git branch -m old-name new-name
비슷한 여러 커밋을 정리하고 싶다!
같은 주제로 작업한 커밋이 여러 개라면 깔끔한 히스토리 관리를 위해 커밋들을 squash하는 게 바람직합니다.
squash는 실제로 존재하는 명령어가 아니고, git rebase 기능 중 interactive rebase를 할 때 사용하는 옵션입니다.
git log 명령어를 이용해 다음과 같은 작업 내용을 확인했습니다.
$ git log --oneline
971e62d a 수정
fc8812c a 수정
a1b2ae7 a 수정
16cfc94 Merge pull request #249 from changuk-lee/renewal_monkey3
3d19554 Merge remote-tracking branch 'origin/renewal_monkey3' into renewal_monkey3
...
위에서 3개의 'a 수정' 커밋들을 하나로 묶어봅시다. 이것을 interactive rebase라고 합니다.
$ git rebase -i HEAD~3
이제 텍스트 에디터가 실행되고, 각 커밋에 대해 원하는 작업을 할 수 있습니다.
pick 971e62d a 수정
pick fc8812c a 수정
pick a1b2ae7 a 수정
...
두번째와 세번째 라인의 pick을 s(squash)로 바꿔봅시다.
pick 971e62d a 수정
s fc8812c a 수정
s a1b2ae7 a 수정
...
이제 저장하고 종료하고 나면 커밋 메시지를 수정하는 화면이 나옵니다.
# This is a combination of 3 commits.
# The first commit's message is:
a 수정
# This is the 2nd commit message:
a 수정
# This is the 3rd commit message:
a 수정
원하는 커밋 메시지를 넣은 후 저장/종료 후, git log를 해보시면 3개의 커밋이 하나의 커밋으로 합쳐진 것을 볼 수 있습니다.
Squash하고 싶은데 순서가 얽혀있다!
커밋 순서가 아래와 같은 경우에, 두 개의 'a 수정' 커밋을 묶고 싶다면 어떻게 해야 할까요?
$ git log --oneline
ae5ba65 a 수정
3f30dc1 b 수정
b341e53 a 수정
...
일반 squash할 때와 똑같지만, 커밋의 순서를 바꿔주면 됩니다.
pick b341e53 a 수정
pick 3f30dc1 b 수정
pick ae5ba65 a 수정
에서,
pick b341e53 a 수정
s ae5ba65 a 수정
pick 3f30dc1 b 수정
이렇게 바꿔주면 됩니다.
마치며
제가 GitHub 환경에서 Git을 통해 브랜치 관리와 코드 리뷰를 해오면서 겪었던 문제들과 해결책들을 간략히 적어보았습니다.
분명히 더 많은 문제가 있었던 것 같은데... 잘 기억이 나지 않는 관계로...
빠진 게 많다면 추후에 2편을 작성하는 걸 고려해봐야겠습니다...
분명히 더 많은 문제가 있었던 것 같은데... 잘 기억이 나지 않는 관계로...
빠진 게 많다면 추후에 2편을 작성하는 걸 고려해봐야겠습니다...
감사합니다.
댓글
댓글 쓰기