Git 자유자재로 활용하기 2

타키탸키·2021년 5월 14일
0

지난 시간에는 Git을 자유자재로 활용할 수 있도록 돕는 reflog, rebase, stash 등의 커맨드와 다른 꿀팁들에 대해 알아봤습니다.

이번 시간에는 지난 시간에 이어 실무에서 유용하게 사용할 수 있는 여러 커맨드들과 팁들을 소개해 드리겠습니다.

🧙 잘못된 브랜치에서 작업했다면?

앞서 git stash는 작업하던 도중에 다른 브랜치로 이동해야할 때, 작업하던 내용을 잠시 저장하기 위해 사용하는 커맨드라고 배웠습니다. 그런데 이 커맨드는 또 다른 상황에서도 유용하게 사용될 수 있는데요. 바로 잘못된 브랜치에서 작업했을 때입니다.

master 브랜치로 이동해서 퍼센티지를 구하는 함수 get_Percent를 추가하겠습니다.

git checkout master

그런데 만들고 보니 이 함수는 무료 버전이 아니라 유료 버전에 만들어야 했습니다. 다시 말해, master 브랜치가 아니라 premium 브랜치에서 작업했어야 한다는 것이죠. 바로 이런 상황에서 git stash를 사용하여 쉽게 문제를 해결할 수 있습니다.

우선, git stash를 하여 stack에 작업 내용을 저장합니다.

git stash

그리고 premium 브랜치로 이동해주세요.

git checkout premium

stack에 저장한 내용은 꼭 같은 브랜치에서만 불러올 수 있는 것은 아닙니다. 따라서, premium 브랜치에서 stack 내부를 보더라도 master 브랜치에서 작업하던 내용을 볼 수 있습니다.

git stash list


브랜치는 premium인데 stash@{0}에 우리가 master 브랜치에서 작업했던 내용이 저장되어 있다는 것을 확인할 수 있습니다. stash@{1}에는 저번 시간에 작업했던 내용도 저장되어 있네요. 이 상태에서 apply를 하게 되면 어떻게 될까요?

앞서 stack은 먼저 들어온 데이터가 가장 마지막에 나가는 구조라고 했습니다. 따라서, 제일 최근에 들어온 데이터인 stash@{0} 즉, 방금 저장한 작업 내용이 먼저 적용됩니다. 그렇기 때문에 이 상황에서는 그대로 apply를 해줘도 됩니다. 하지만 어떤 작업을 적용할 건지 정확하게 지정해 주는 것이 더 안전한 방법이니 작업 내용의 아이디를 apply 뒤에 적어주겠습니다.

git stash apply stash@{0}


실행해보니, CONFLICT가 발생하네요. 기존에 존재하던 premium 브랜치 함수와 동일한 위치에 새로운 함수를 넣으려 하니 충돌이 발생한 겁니다. 따라서, Sublime Text로 이동해서 원하는 모습대로 수정해보겠습니다.

파일을 수정했으니 git add를 하고 커밋만 하면 되겠죠?

git add .
git commit -m "Add get_Percent function"

이와 같이 브랜치 수가 많을 때는 잘못된 브랜치에서 작업하는 실수를 하곤 합니다. 이럴 때는 당황하지 말고 git stash로 stack에 작업 내용을 저장하고 올바른 브랜치로 가서 다시 git stash apply를 하면 됩니다.

그런데 git stash를 자주 사용하다 보면 stack에 작업 내용이 계속 쌓여서 보기가 힘듭니다. 따라서, 이미 적용한 작업 내용은 지워주는 게 좋습니다.

만약 첫번째 작업 내용을 삭제하려면 git stash drop을 하면 됩니다. drop은 '떨어뜨리다'라는 뜻이 일반적이지만 컴퓨터 용어로는 '삭제한다'라는 의미를 가지고 있습니다. 첫번째 외 다른 작업 내용을 삭제하려면 drop 뒤에 삭제하고 싶은 작업 아이디를 적어주면 됩니다.

git stash drop stash@{0}

그리고 다시 한 번, stack 리스트를 보겠습니다.

git stash list


삭제가 잘 되어 기존의 stash@{1}에 있던 작업 내용이 stash@{0}에 저장되어 있는 것을 확인할 수 있습니다. 나머지 stack도 비워보겠습니다.

git stash drop stash@{0}
git stash list


stack이 모두 비워졌네요.

🧙 적용한 작업 내용은 스택에서 없애기

stash 커맨드에 대한 내용을 정리해보겠습니다.

1. 작업 내용 저장

git stash

2. 작업 내용 조회(스택 내용 살펴 보기)

git stash list

3. 작업 내용 적용

git stash apply [작업 내용 아이디] // 생략 시, 가장 최근 작업 내용 적용

4. 작업 내용 제거

git stash drop [작업 내용 아이디] // 생략 시, 가장 최근 작업 내용 제거

앞서 적용(apply)한 작업 내용은 stack에서 제거(drop)해주는 것이 좋다고 했는데요. 그래서 git stash apply 이후에 git stash drop을 했었죠? 그런데 이렇게 번거롭게 할 필요 없이 작업 내용을 적용하면서 동시에 stack에서 제거도 해주는 커맨드가 있습니다.

git stash pop [작업 내용 아이디]

위 커맨드를 입력하면 특정 작업 내용을 적용함과 동시에 stack에서 제거합니다. 실습을 통해 확인해보겠습니다.

❗ git stash 하기

README.md 파일로 작업을 진행하겠습니다. 기존 내용에 테스트용 문장인 git stash pop test라는 문장을 추가해주세요.

저장 후, 작업 내용을 stack에 저장하겠습니다.

git stash

커맨드 입력 후, 다시 Sublime Text로 돌아와보면 작업했던 내용이 사라집니다. 작업 내용을 stack에 임시로 저장하고 최신 커밋의 상태로 돌아갔기 때문이죠.

git stash list


리스트를 확인해보니 stack에 잘 저장되어 있네요.

❗ git stash pop 하기

이 상태에서 pop을 하게 되면,

git stash pop


README.md 파일이 변경되었다는 문구가 출력됩니다. 직접 확인해보겠습니다.

돌아온 게 맞네요. 여기까지는 git stash apply와 똑같죠? 그런데 다시 stack 내부를 들여다 보면,

git stash list


stack이 비워져 있습니다. 적용과 동시에 stack에서 제거가 진행되었네요!

참고로, git stash pop 커맨드는 작업 내용 아이디를 인자로 주면 특정 작업 내용을 적용하면서 stack에서 제거합니다. 반면에 아이디를 생략하면, 가장 최근에 작업한 내용을 적용하면서 stack에서 제거합니다.

앞으로 stack에 저장된 작업 내용을 working directory에 적용할 때, 그 작업 내용을 나중에 또 쓸 필요가 있다면 apply를, 나중에 쓸 필요가 없다면 pop을 쓰면 됩니다. 일반적으로는 후자의 경우가 더 많습니다.

🧙 필요한 커밋만 가져오기

유료 버전의 퀄리티를 높이기 위해, premium 브랜치에 보다 더 난이도가 있는 함수를 추가해주려 합니다. 바로 1부터 n까지 합을 구해주는 get_Sum 함수인데요. 그런데 1부터 n까지 합을 구해주는 방법은 다양하기 때문에 가장 좋은 방법이 무엇인지 알기 위해 테스트가 필요하다고 하네요.

우리는 앞서 테스트를 위한 test 브랜치를 만든 적이 있습니다. 이 test 브랜치에서 작업을 진행해보겠습니다.

git checkout test

그런 다음, Sublime Text로 이동해서 첫번째 코드를 적어보겠습니다.

저장 후, 터미널에서 커밋까지 진행해주세요.

git add .
git commit -m "Add get_Sum_ver1 function"

여기까지 진행했는데 누군가가 새로운 방법으로 get_Sum 함수를 구현하여 그 함수를 적용해보기로 했습니다. 바로 Sublime Text로 이동해서 ver1을 지우고 ver2로 교체하겠습니다.

마찬가지로 커밋까지 진행해주겠습니다.

git add .
git commit -m "Add get_Sum_ver2 function" 

다음으로 해야 할 일은 두 번째 버전의 함수를 premium 브랜치에 적용하는 것입니다.

git checkout premium

이제 get_Sum 함수를 premium 브랜치로 가져오기 위해 merge를 진행하면 되는데요. 갑자기 기존에 만들었던 첫 번째 버전의 함수가 더 빠르고 정확하니 ver1을 추가하라는 요청이 왔습니다.

일단, 커밋 히스토리를 보겠습니다.

git history --all --graph

이 중, ver1 함수를 만들었던 ad0e 커밋만을 가져오면 됩니다. 이럴 때 필요한 커맨드가 있습니다. 바로 git cherry-pick입니다.

cherry-pick은 '좋은 것만 골라 먹는다'는 뜻을 가지고 있는데요. 그래서 현실에서 자기에게 유리한 선택만 하려는 사람을 cherry picker라고 부르기도 합니다. Git에서는 자신이 원하는 작업이 들어있는 커밋들만 가져와서 현재 브랜치에 추가하는 기능을 수행하는 커맨드를 말하죠.

특정 커밋을 가져오기 때문에 커맨드 뒤에는 커밋 아이디를 적어야겠죠?

git cherry-pick ad0e


실행하니 CONFLICT가 발생하네요. 이제는 익숙하죠? Sublime Text에서 원하는 모습대로 수정해봅시다.

위 모습대로 커밋을 진행하면 됩니다.

git add .
git commit -m "Add get_Sum function from test branch"

이처럼 git cherry-pick 커맨드는 원하는 작업이 있는 커밋의 내용만을 가져옵니다. 더 좋은 계산기 프로그램을 만들기 위해 앞으로 test 브랜치에서 여러 작업을 하게 될 텐데요. 물론 새로운 함수의 테스트가 끝날 때마다 매번 merge를 하고 필요 없는 함수를 지워줘도 됩니다. 하지만 cherry-pick 커맨드를 사용하면 번거로운 과정 없이 필요한 함수만 가져오면 됩니다.

🧙 여러 커밋을 하나의 커밋으로 만들기(심화)

이번에는 premium 브랜치에서 팩토리얼 연산을 하는 함수를 추가해보겠습니다. 팩토리얼은 어떤 수가 주어졌을 때, 1부터 그 수 사이에 있는 모든 수들을 다 곱하는 연산입니다. 예를 들어, 4 팩토리얼은 1부터 4까지 곱한1x2x3x4=24입니다. Sublime Text로 이동해서 이 함수를 추가하겠습니다.

저장 후, 터미널로 이동해서 커밋까지 진행해주세요.

git add .
git commit -m "Add factorial function"

그런데 커밋까지 완료한 후에 더 효율적으로 팩토리얼을 계산하는 코드가 작성되었다고 합니다. 그래서 기존에 작성했던 내용을 수정해야 된다네요.

위와 같이 수정을 하고 다시 커밋을 진행합니다.

git add .
git commit -m "Add factorial function2"

커밋 히스토리도 한번 보겠습니다.

git history


잘 진행되었네요.

그런데 사실 앞서 작업했던 팩토리얼 함수에 대한 커밋은 이제 필요 없습니다. 더 효율적인 팩토리얼 함수를 포함한 커밋이 추가되었으니까요. 그래서 앞서 했던 커밋을 없었던 것처럼 하고 싶은데 어떻게 해야할까요? git reset을 하면 됩니다.

git reset에는 세 가지 옵션이 있었죠? 그 중, working directory의 내용을 건드리지 않는 옵션mixedsoft였습니다. 우리는 working directory의 모습은 새로운 함수가 포함된 현재의 모습으로 남기고 reset을 하고 싶습니다. 따라서, 위 두 옵션 중 하나를 사용하면 됩니다.

soft 옵션을 사용해서 진행해보겠습니다. 돌아가고자 하는 시점은 처음 팩토리얼 함수를 추가하기 전 커밋입니다.

git reset --soft 880f

이렇게 하면 과거의 커밋으로 돌아가면서도 working directory의 내용은 현재 모습으로 보존됩니다. 이 상태로 git add를 하고 커밋만 해주면 됩니다.

git add .
git commit -m "Add factorial function"

원하는대로 잘 진행되었는지 커밋 히스토리를 확인해봅시다.

git history


기존에 있던 두 개의 팩토리얼 함수 관련 커밋이 사라지고 새로 추가한 커밋이 최신 커밋이 되었습니다.

Sublime Text로 돌아가 효율적인 팩토리얼 함수만 남았는지도 확인해보겠습니다.

Sublime Text에도 원하는대로 새로 바꾼 팩토리얼 함수가 보이네요.

정리하자면, 없애고 싶은 자잘한 커밋 이전으로 git reset을 할 때는 soft 옵션과 mixed 옵션을 활용하면 됩니다. 그럼 HEAD는 이전 커밋을 가리키게 되지만 working directory는 최신 상태로 유지되기 때문에 그 상태로 커밋만 새로 해주면 되는 것이죠.

사람은 누구나 실수를 하고 한번에 완벽한 코드를 짤 수는 없기 때문에 Git을 활용하는 동안 자잘한 커밋이 생기는 것은 당연한 일입니다. 그럴 때마다 아주 유용하게 사용할 수 있는 꿀팁이므로 꼭 알아두시길 바랍니다.

🧙 git이 무시하는 파일들

우리는 working directory에 있는 파일들을 git add와 git commit 커맨드를 통해 버전 관리를 합니다. 그런데 working directory의 내부에는 존재 자체가 무시되는 파일들이 있는데요. 그 파일들에 대해 알아봅시다.

❗ .git ignore 파일

GitHub에서 새로운 레포지토리를 생성할 때, 다음과 같은 옵션이 있습니다.

해당 란에 체크를 하지 않으면 .gitignore 파일을 만들지 않겠다는 뜻입니다. 그런데 이 .gitignore 파일은 무엇일까요?

이 파일은 working directory 내에 존재하는 파일들 중에서 마치 존재하지 않는 것처럼 Git이 인식해야 할 파일들의 목록이 적힌 파일입니다. 말 그대로 Git이 ignore(무시)하는 파일들의 이름이 적혀있는 파일인데요. 해당 란에 체크를 하게 되면,

이와 같이, 여러 단어들이 등장합니다. 이 단어들은 사실 프로그램이 실행되는 플랫폼이나 프로그래밍 언어들입니다. 이들 중 하나를 선택하면, 그 플랫폼에서 실행될 프로그램을 만들거나 해당 프로그래밍 언어로 코드를 작성할 때, (보통 자동으로) 생성되는 파일들 중에서 굳이 Git에 의해 버전 관리가 될 필요 없는 파일들의 이름이 정리된 .gitignore 파일을 자동으로 생성해줍니다.

우리는 지금껏 Python으로 작업을 해왔는데요. Python을 선택해봅시다. 그런 다음에 레포지토리를 생성하면,

위와 같이 .gitignore 파일이 생성됩니다. 클릭해서 내용을 보겠습니다.

그럼 여러 파일 이름 혹은 디렉토리 이름이 보입니다. 이들 중, *py[cod], .$py.class, *.so는 무슨 뜻일까요? *길이 0개 이상의 아무 단어라고 생각하면 되고 대괄호([])는 그 안의 알파벳 중 하나라고 생각하면 됩니다.

다시 말해, 이 세 가지 단어의 뜻은 각각 .pyc 또는 .pyo, pyd로 끝나는 파일명, $py.class로 끝나는 파일명, .so끝나는 파일명입니다. 어렵지 않죠? 이에 해당하는 파일들은 모두 Git이 무시해버립니다.

그리고 이름 맨 뒤에 슬래시(/)가 붙은 것은 디렉토리를 의미하는데요. 예를 들어, build/라면 build 디렉토리에 있는 모든 파일을 Git이 무시한다는 뜻입니다.

이처럼 Python의 .gitignore 파일에는 Python으로 작업하다보면 생겨나는 여러가지 전형적인 부산물들의 이름이 적혀있습니다. 이들은 딱히 버전 관리를 할 정도의 가치가 없고 오히려 버전 관리를 하면 용량만 더 차지할 뿐만 아니라 나중에 각 버전을 살펴볼 때 가독성을 떨어뜨리기만 하기 때문에 이렇게 Git이 무시하도록 설정한 것입니다.

❗ Git이 무시한다

그런데 Git이 무시한다는 것이 정확히 어떤 말일까요? 실습을 통해 알아봅시다.

앞서 생성한 리모트 레포지토리를 GitHub에서 생성하고 git clone을 통해 로컬 레포지토리로 가져오겠습니다.

git clone https://github.com/tataki26/MathBox.git

그런 다음, MathBox 디렉토리로 이동해서 디렉토리 내부를 살펴보겠습니다.

cd MathBox/
ls -al


.gitignore 파일이 보이시나요?

다음으로 이 working directory 안에서 calculator.py라는 파일과 library.so라는 파일을 생성하겠습니다.

touch calculator.py
touch library.so

그 후, git status를 확인해보겠습니다.

git status

그럼 아직 calculator.py 파일을 git add하지 않았다는 결과가 출력됩니다. 그런데 이상하지 않나요? 분명 library.so라는 파일도 같이 만들었는데 왜 calculator.py 파일만 뜬 걸까요?

우리는 앞서 .gitignore 파일 목록에서 .so라는 파일 이름을 봤습니다. 즉, library.so 파일은 so라는 확장자를 가졌기 때문에 Git이 무시한 것이죠.

이 상태에서 .gitignore 파일을 삭제하고 git status를 다시 보겠습니다.

rm .gitignore
git status


그럼 위와 같이 library.so 파일에도 git add를 하지 않았다는 경고가 뜹니다. so 확장자를 담고 있는 .gitignore 파일이 삭제되었기 때문에 이제 Git은 모든 파일을 인식합니다.

Git이 특정 파일을 무시한다는 말이 무슨 뜻인지 아시겠죠? 만약 여러분이 working directory에서 버전 관리를 할 필요가 없는 항목들이 있다면 이렇게 .gitignore 파일에 그 이름을 추가하고 버전 관리를 하면 됩니다. 그럼 좀 더 깔끔하게 버전 관리를 할 수 있습니다. 만약 어떤 파일들을 무시해야할 지 잘 모르겠다면 방금 했던 것과 마찬가지로 GitHub에서 기본으로 제공되는 옵션을 선택해보시길 바랍니다.


이번 시간에는 Git을 자유자재로 활용할 수 있는 더 많은 팁들을 배웠습니다. git stash apply, drop, pop, 그리고 git cherry-pick과 같은 커맨드, working directory의 내용을 유지하며 reset하는 방법과 Git이 무시하는 .gitignore 파일까지, 대부분은 실무에서 유용히 사용되기 때문에 잘 알아두면 좋습니다.

다음 시간에는 마지막으로 지금까지 배운 Git 커맨드들과 팁에 대해 정리하도록 하겠습니다.

* 이 자료는 CODEIT의 'Git으로 배우는 버전 관리' 강의를 기반으로 작성되었습니다.
profile
There's Only One Thing To Do: Learn All We Can

0개의 댓글