호댕의 iOS 개발

[Git] Git 총정리 (기본 명령어 등등) 본문

Software Engineering

[Git] Git 총정리 (기본 명령어 등등)

호르댕댕댕 2023. 6. 29. 23:20

멋쟁이 사자처럼 앱스쿨 3기에서 좋은 기회로 Git / Github 관련 특강을 맡게 되어, Git 관련 내용을 총정리해보았다. 

(물론 모든 명령어를 다루진 않지만 기본적으로 많이 사용하는 명령어들과 Git의 등장 배경 등을 정리했다)

 

일단 Git이 무엇이고 왜 등장했는지 살펴보자. 

 

Git은 뭘까?

일단 사전적 의미에 대해 살펴보자. 

 

버전을 편하게 관리할 수 있는 도구

 

따라서 Version Control System 중 하나인 것이다. 

 

그렇다면 굳이 왜 Git이란 툴로 버전을 관리하게 된 것일까?

 

이는 협업을 할 때 조금 더 편리하기 위해서일 것이다. 

 

<협업을 하다보면 발생하는 일들>

  • 동시에 다른 작업들이 병렬적으로 진행
  • 특정 코드에 문제가 생겨 이전 코드로 돌아갈 수 있음
  • 다른 사람들이 코드를 어떻게 작성했는지 히스토리를 확인하거나 코드 리뷰가 필요함

 

만약 그럼 이러한 것들을 파일로 버전을 관리한다고 생각해보자. 

 

 

  • 병렬로 작업이 진행되고 이를 최종적으로 합치려면 변경 사항이 어디서 발생했는지 전부 파악해서 잘 병합해야 함
  • 버전이 많아지는 경우 관리해야 하는 파일들이 어마어마하게 많아짐
  • 어떤 부분에서 변경사항이 발생했는지 한 번에 확인이 거의 불가능

 

파일로 버전을 관리한다고 생각하면 적은 것 이외에도 많은 문제가 있을 것이다... (끔찍... 😱)

 

그래서 개발자들이 거의 다 사용하는 Git이 나오게 된 것이다. 

 

물론 이전에도 Centralized Version Control이라고 서버를 통해 버전을 관리하는 방법이 존재했다. 

하지만 서버에서 버전을 관리하는 만큼 서버에 문제가 생기면 버전 관리가 불가능했고, 네트워크 연결이 어려운 환경에서도 버전 관리가 불가능하다는 단점이 존재했다. 

 

그래서 Distributed Version Control이 등장하게 됐다. 

Git도 Distributed Version Control이다. 

 

이를 사용하면 서버 뿐만 아니라 개발자 각각 로컬에서도 버전에 대한 히스토리를 가지고 있어 특정 버전에 문제가 생기더라도 다른 버전을 들고와서 해결하는 것이 쉬워졌다. 

 

또한 이전 Centrailized Version Control에서 말했던 단점들도 로컬에서도 처리가 가능해지면서 해소되었다. 

 

Git의 기본적인 동작 원리

일단 기본적으로 Git은 크게 3가지 Directory를 가지고 있다. 

 

Working Directory

Xcode에서 수정 사항이 있으면 파란색으로 표시되어 어떤 변경사항이 있었는지를 보여준다.

단순히 요 상태라면 Working Directory에 있다고 이해해도 된다. 

 

Staging Area

이는 git add 명령어를 통해 Working Directory에서 Staging Area로 이동하게 된다. 

이 공간은 버전 히스토리에 저장될 준비가 된 파일들을 옮겨놓는 공간이다. 

 

.git Directory

버전 히스토리를 가지고 있는 곳이다. 

 

각 버전은 고유한 해시 값을 가지고 있으며, 누가 해당 커밋을 했고 언제 해당 커밋을 했는지가 저장되어 있다. 

이는 cmd + shift + . 을 통해 숨김 파일을 보면 직접 확인해볼 수 있다. 

git add

Working Directory에 있는 file의 변경사항을 Staging Area로 옮기는 역할

 

git status를 통해 현재 상태를 확인해보면 Untracked files로 Working Directory의 변경사항이 보이거나

아래 처럼 아직 Staging Area로 올라오지 않은 커밋을 위한 변경사항을 볼 수 있다. 

 

  • git add {파일 명} : 특정 파일만 Staging Area로 이동
  • git add . : 변경 사항이 있는 모든 파일을 Staging Area로 이동

만약 Staging Area로 이동하면 안되는 파일을 이동시킨 경우 git restore --staged {파일 명} 명령어를 사용하면 다시 Working Directory로 이동시킬 수 있다. 

 

git commit

Staging Area에 있는 파일들을 변경 히스토리를 기록하기 위해 .git Directory로 옮기는 역할

 

아까와는 다르게 not staged가 아닌 것을 확인할 수 있다. (Staging Area로 올라왔다는 의미)

 

이때 git commit 명령어를 사용하게 되면 .git Directory로 변경사항을 옮기며 변경 히스토리를 기록할 수 있게 된다. 

만약 가장 최근에 한 커밋을 수정해야 한다면 git commit --amend 명령어를 사용하면 수정이 가능하다. 

 

버전 히스토리를 남기는 것은 종국적으로 협업을 잘 하기 위함이다!! 

 

따라서 일관된 커밋 메세지를 남기는 것과 커밋을 잘 쪼개는 것은 매우 중요하다. 

협업을 하게 되면 커밋을 통해 작업 내용의 히스토리를 확인하기 때문이다.

(물론 PR의 Description도 잘 작성하고 확인해야 한다)

커밋 템플릿의 경우 위 방법으로 생성이 가능하다. 

 

이때 적용할 만한 커밋 메세지 규칙은 Karma 방식이 있다. 

 

Karma - Git Commit Msg

In the repository we use and enforce the commit message conventions. The conventions are verified using commitlint with Angular config. The reasons for these conventions: # automatic generating of the changelog simple navigation through git history (e.g. i

karma-runner.github.io

 

커밋을 쪼개는 단위도 적당히 쪼개야 한다. 

  • 너무 커지면 너무 많은 기능이 단일 커밋에 들어가서 커밋 히스토리를 확인하거나 이를 되돌려야 하는 경우 어렵고...
  • 너무 작으면 무의미한 커밋들이 너무 많이 생기게 되고...

 

따라서 나중에 되돌려도 작업에 큰 영향이 없도록 기능 단위로 최대한 하나의 기능만 하나의 커밋으로 들어가도록 하는 것이 좋은 것 같다. 

 

.gitignore

Git 버전 관리에서 제외할 파일 목록을 지정하는 파일

 

부수적인 내용이거나 외부에 노출되면 안되는 중요한 내용인 경우 .gitingnore 파일을 통해 관리할 수 있다. 

이는 vi .gitignore 명령어를 통해 해당 파일을 생성하고 바로 수정도 할 수 있다. 

 

여기에 들어갈 컨텐츠는 아래 사이트에서 키워드를 통해 찾을 수 있다. 

 

gitignore.io

Create useful .gitignore files for your project

www.toptal.com

 

그렇다면 보안 관련 중요한 파일들은 어떻게 관리하는 것이 좋을까?

다른 방법도 많이 있을 수 있지만 크게 2가지 방법을 소개해보고자 한다. 

 

1. Configuration Settings File 활용

cmd + n을 눌러 새로운 파일을 추가할 때 위 이미지와 동일한 형태의 파일을 찾을 수 있을 것이다. 

먼저 이를 생성해주자

 

그리고 여기서 관리해야 할 키 값을 정의할 수 있다. 

이때 Swift에서 String을 적을 때처럼 큰 따옴표 안에 적지 않는다. 

 

그 후 info.plist에 해당 값을 등록해준다. 

 

사용할 때에는 아래 이미지처럼 사용하면 된다. 

마지막으로 config 관련 파일을 .gitignore에 올려서 해당 파일은 따로 버전 히스토리로 남기지 않도록 하면 외부에 key / value 값을 노출시키지 않아도 된다. 

 

2. Property List 활용

이 방법도 이전 방법과 거의 유사하나 Property List를 사용한다는 차이만 존재한다. 

 

이를 통해 Key / Value 값을 관리하고 해당 파일은 .gitignore에 올려주면 된다. 

 

 

하지만 이 방법을 사용하면 대부분 이 문제에 직면하게 될 것이다. 

분명 .gitignore에 등록을 했는데도 불구하고 계속 변경사항이 추적되는 것이다. 

 

이때는 git rm --cached {추적되지 않아야 하는 파일 명} 명령어를 사용하면 된다. 

이 명령어는 Working Directory에선 파일을 유지한 채 Git이 추적하고 있는 파일만 쏙 제거하는 명령어이다. 

 


이렇게 하는 경우 기본적으로 버전 히스토리에 기록하는 것은 할 수 있을 것이다. 

하지만 우리는 이렇게 개발을 하다보면 반드시 실수를 하게 된다. 

 

따라서 실수를 했을 때 이를 해결하는 방법을 아는 것도 중요하다. 

 

git restore

커밋하지 않은 변경사항을 취소하고 싶을 때

 

이전에는 git checkout 명령어를 통해 했으나, checkout 명령어가 가지는 기능이 다양해지면서 restore로 변경되었다. 

 

기존 git checkout 명령어가 가지던 기능

  • 브랜치 변경 → git switch 
  • 특정 커밋으로 이동
  • 커밋하지 않은 변경사항을 취소하고 싶을 때 → git restore
    • git restore --staged : Staging Area에 올라간 내용을 취소할 때 
    • git restore : Staging Area에 올라가지 않은 내용을 취소할 때 

 

git commit --amend

가장 최근 커밋을 수정하고 싶을 때

 

이때 git log를 통해 로그를 확인해보면 커밋의 해시값이 변경된 것을 볼 수 있다. 

따라서 이미 Remote에 버전 히스토리를 올려버린 경우, 강제 푸시를 통해 해당 커밋을 덮어쓰기 해줘야 한다. 

 

 

하지만 강제 푸시를 하면 협업을 할 때에는 굉장히 위험하다. 

아예 이전 커밋 기록이 덮어씌워지면서 기존 다른 코드를 기반으로 작업했던 개발자들에게 혼선을 줄 수 있고 이로 인해 Conflict이 발생할 가능성이 크기 때문이다. 

 

반드시 필요한 경우에는 동료 개발자들에게 동의를 구하고 해야겠지만, 협업 환경에서의 강제 푸시(git push -f)는 지양하는 것이 바람직하다.

물론 혼자서 작업하는 브랜치라면 강제푸시를 해도 크게 문제가 되지 않는다. 

 

git reset

아예 이전 커밋으로 되돌아가고 싶다면 

 

일단 git reset만 하게 되면 기본적으로 mixed로 동작하게 된다. 

 

  • git reset (--mixed) : 이전 커밋으로 돌아가면서 변경사항은 Working Directory로 옮기게 됨
  • git reset --soft : 이전 커밋으로 돌아가면서 변경사항은 Staging Area로 옮기게 됨 (git add를 할 필요 X)
  • git reset --hard : 이전 커밋으로 돌아가면서 변경사항은 아예 폐기

 

그렇다면 reset을 한 경우 다시 reset을 하기 전으로 돌아갈 순 없을까?

물론 이것도 가능하다. 

 

git reflog 명령어를 사용하면 지금까지 사용했던 Git 명령어들의 히스토리를 확인할 수 있고 이 또한 각각 해시 값이 부여되어 있다. 

따라서 git reset --hard {해시값} 명령어를 사용하여 특정 명령어 히스토리의 해시값을 적게 되면 reset을 하기 전으로도 돌아갈 수 있다. 

 

git revert

로그 기록을 유지한 채로 이전으로 돌아가고 싶다면 

 

git reset의 경우 아예 이전 커밋으로 돌아가는 것이다. 하지만 만약 Log는 유지한 채로 이전으로 돌아가고 싶다면 git revert를 활용하면 된다. 

 

사용하는 방법은 git reset과 거의 동일하나 로그는 유지할 수 있다. 

기본적으로는 Revert를 했다는 커밋이 남게 되며 git revert --no-commit 명령어를 사용하면 커밋을 남기지 않고 이전으로 되돌아갈 수도 있다. 

 

git merge

이렇게 실수도 수정하고 작업이 완료되었다면 master 브랜치로 병합을 해야 한다. 

 

git merge를 사용하면 fast forward merge를 사용하게 되며 master의 Head 포인터를 병합하려는 브랜치의 마지막 커밋으로 둬서 머지를 하게 된다. 

 

이때 머지를 했다는 커밋은 따로 남지 않는다. 

 

하지만 작업을 github을 통해 하게 되고, Merge를 하기 전에 Pull Request를 올리고 코드 리뷰를 받은 후 GitHub에서 바로 머지를 하는 일이 잦다보니 명령어로 브랜치들을 병합하는 것은 거의 사용하진 않았다. 

 

GitHub에서의 3가지 Merge 방법

  • Create a merge commit : 문자 그대로 지금까지의 모든 커밋이 base branch에 추가되고 마지막에 Merge Commit이 남는 형태이다. 어디부터 어디까지 특정 브랜치에서 왔는지 확인하는 것이 쉽다. 

  • Squash and merge : 지금까지의 모든 커밋을 하나의 커밋으로 정리해서 머지를 한다. 로그가 깔끔해지는 장점이 있다. 
  • Rebase and merge : 지금까지 모든 커밋을 그대로 base branch에 붙이게 된다. fast forward merge와 유사하다.

 

git rebase

git add / git commit 등 간단한 명령어들을 제외하면 협업을 할 때 가장 중요하고 많이 사용하는 명령어인 것 같다. 

 

이는 https://ho8487.tistory.com/90 여기서 이미 정리를 열심히 해놔서 링크로 대체한다. 

 

[Git] rebase를 자세히 알아보자 (개념, 사용법, 강제 푸쉬 등에 관하여)

이전 글에서 간단하게 rebase를 사용하여 이미 push한 commit의 내용을 바꾸는 방법을 포스팅한 적이 있다. [Git] 이미 Push를 해버린 Commit을 어떻게 고치지... 이미 커밋을 하고 한~참 시간이 지났다가

ho8487.tistory.com

 

git cherry-pick

특정 커밋만 빼오고 싶다면 

 

케이크에서 맛있는 체리만 쏙 빼먹는 것처럼 특정 커밋만 쏙 빼오고 싶을 때 사용하는 명령어이다. 

 

git cherry-pick {해시 값} 명령어를 통해 간단하게 사용이 가능하다. 

 

git fetch / git pull

서버에 있는 버전 히스토리를 로컬로 가져오고 싶을 때 

 

  • git fetch : 로컬의 Head는 그대로 두고 서버에 있는 히스토리를 가져오기 때문에 로컬의 작업에는 영향 X
  • git pull : git fetch를 수행한 후 로컬 브랜치도 이에 맞게 업데이트하는 작업을 수행하여 로컬의 커밋 히스토리와 Head가 자동으로 업데이트 됨

 

Branch 전략

GitFlow

일단 GitFlow는 크게 5개의 브랜치로 구성이 되며 필요하면 추가나 생략이 가능하다. 

  • main (master) : 배포와 흐름을 동일하게 가져가는 브랜치 
  • develop : 실제 개발을 진행하는 브랜치
  • feature : 특정 기능 개발을 위해 develop 브랜치에서 파는 브랜치
  • release : 배포 전 QA / 테스트가 진행되는 브랜치
  • hotfix : 배포 후 빠르게 수정해야 하는 문제를 수정하는 브랜치

 

<장점>

  1. 테스트와 리뷰를 할 수 있는 상황이 많아 코드 품질이 보장되며, 버그를 조기에 찾아낼 수 있음
    (feature > develop, release 브랜치 등)
  2. 리뷰가 많기 때문에 코드 스타일을 일정하게 유지하기 쉬움

<단점>

  1. 프로세스가 비교적 많아서 개발 속도가 느려질 수 있음

 

가장 큰 장점은 테스트와 리뷰가 많다는 점이기 때문에 프로덕트가 어느 정도 궤도에 올랐고, 주니어 개발자가 많은 경우 유용하다. 

 

Trunk-based

<장점>

  1. 빠른 개발 속도
  2. 포괄적인 자동화 테스트 도입이 용이

<단점>

  1. feature 브랜치에서 많은 테스트가 필요하며, 여기서 검증이 안되면 코드 품질 저하로 이어질 수 있음

 

따라서 숙련된 시니어 개발자가 많은 환경이나 초기 프로덕트로 빠른 개발이 필요한 경우 유용하다. 

Comments