깃 사용법

tomchaccom·2025년 11월 16일

git

목록 보기
4/5

오늘은 깃과 깃허브에 대해서 다뤄볼려고한다. 개발을 함에 있어서 이 둘은 유지보수를 편하게 하며 협업할 수 있는 환경을 제공한다. 깃을 하면서 내가 가장 헷갈리고 자주 부딪히는 상황들과 이에 대한 해결방법, 그리고 기초적인 깃 사용법에 대해서 다뤄 보겠다.

Git != Github

git-is-not-github

먼저 깃과 깃허브는 다르다. 어떤 점에서 차이가 있을까?

깃은 형상관리도구이다. 깃은 파일 기반으로 동작하고 깃에 등록된 파일들은 깃이 변경사항을 추적하게 된다. 개발을 함에 있어서 커밋을 통해 기록을 남기고, 리셋을 통해서 취소하기도 하고 이런 코드의 형상(시점)을 관리할 수 있도록 돕는 도구가 깃이다.깃허브는 웹사이트를 기반으로 깃을 통해 작업한 내용을 저장하고 협업할 수 있는 서비스라고 생각하면 된다.

Git Workflow

git-workflow

기초 개념

Working Directory : 현재 본인이 작업하고 있는 디렉토리이다. 리눅스 환경에서 pwd 명령어를 사용하면 현재 working directory를 확인할 수 있다.
Staging Area : 커밋을 하기전 하나의 커밋을 뭉칠 파일들을 선별해서 올려두는 임시 공간이라고 생각하면 된다. 커밋이 되면 이 파일들은 staging 영역에서 빠져나간다.
Local Repo(로컬 저장소) : 로컬에서 깃의 커밋이력, 즉 히스토리를 저장하는 공간으로 git init 명령어를 터미널에 입력하면 입력한 디렉토리가 local repo가 되고, .git 이란는 커밋 히스토리를 저장해둔 숨김 파일이 생성된다.
Remote Repo(원격 저장소) : 깃허브라고 생각해도 무방하다. 로컬레포의 작업 결과를 git push 명령어를 통해서 작업물을 저장한다.

파일 관리 명령어

  • Git Add : 파일을 스테이징 영역으로 업로드 한다. git add 파일명의 형식으로 사용하고 git add .을 사용하면 변경사항이 존재하는 파일들을 모두 스테이징 영역에 업로드한다.
  • Git Commit : 스테이징 영역에 올라온 파일들을 하나의 커밋으로 묶어서 저장한다.보통 커밋 메시지와 변경된 파일이 커밋을 구성한다. 그리고 이 커밋들은 각각 서로다른 커밋 해시를 가진다
  • Git Push : 생성된 커밋을 remote repo로 push(밀어서) remote repo에 로컬의 커밋을 저장하는 명령어 이다.
  • Git Fetch <원격 저장소>: remote repo의 변경사항을 가져온다. 브랜치의 내용 변화, 새로 생성된 브랜치 정보 등을 local repo로 가져온다.
  • Git Merge <합칠 브랜치> : 현재 브랜치에 다른 브랜치의 변경사항을 합칠 때 사용
  • Git Pull <원격 저장소> <원격 저장소의 브랜치>: remote repo의 변경사항을 가져오면서 현재 내 로컬 브랜치에 합친다. 그래서 fetch의 경우에는 remote repo의 변경사항만을 가져오지만 pull은 이를 합쳐준다. rebase 방식을 사용하면 커밋 히스토리를 깔끔하게 관리할 수 있다.

명령어 사용법

로컬에서 main.java를 수정했다고 가정

$ git status # 깃이 추적하고 있는 파일의 변경사항, 스테이징영역에 올라와있는 파일들을 보여준다

$ git add src/main/main.java # 절대 경로로 입력해주기 

$ git commit -m "feat: getter, setter 추가" # " " 내부는 커밋 메시지 

# git remote add origin <원격 브랜치 주소 > # 원격 브랜치의 주소를 origin이라는 이름으로 등록하기

$ git push 

# git push 가 안된다면 먼저 git push --set-upstream origin <브랜치명>
# 위 명령어를 통해서 업로드 하려는 브랜치 설정 

이 과정을 통해서 변경된 main.java를 원격저장소에 업로드 했다.

fetch VS Pull

깃을 공부하고 직접 사용하면서 언제 사용해야하는지 정확하게 설명하라고 하면 어려운 개념이었어서 좀 더 상세히 알아보겠다.

fetch를 사용하는 경우

깃허브에서 새로운 브랜치를 만들었다! 이제 이 브랜치에서 개발을 진행할 것이다

❗️ 그런데 local repo는 깃허브(원격)에서 새로운 브랜치를 만들었다는 사실을 알까? 그리고 원격에 만든 브랜치가 로컬에도 똑같이 존재할까?

현재 local repo는 원격의 변경사항을 알지 못한다. 그러므로 이를 알려주고 새로운 브랜치를 만들어 줘야 한다!

$ git fetch origin 
$ git checkout -b <원격에 생성한 브랜치  = 로컬에 만들고자하는 브랜치 >

이처럼 fetch는 로컬 저장소에 원격저장소의 변경사항을 가져올 때 사용한다.

pull을 사용하는 경우

같이 작업하는 사람과 A 라는 브랜치를 base로 B, C 브랜치를 만들었고 나는 C 브랜치에서 작업을 시작했다. B브랜치에서의 개발이 먼저 종료되어 A 브랜치에 merge 하게 되었고, 이제 나도 C 브랜치의 작업이 종료되어 push 하려고 한다.

❗️ 그런데? 원격에서는 이미 B 브랜치의 변경 사항이 존재한다 그렇지만 B브랜치의 변경사항이 없는 상태의 A브랜치를 base로 생성한 C브랜치를 push 하면 B브랜치의 변경점에서 충돌이 발생할 것이다!

이때 pull 을 사용한다.

pull 명령어는 fetch + merge라고 생각하면 된다. 먼저 변경사항을 가져오고 현재 브랜치에 원격 브랜치를 병합한다. 근데 그냥 git pull 을 사용하면 커밋 히스토리가 지저분해진다. 왜냐면 merge를 진행하는 과정에서 merge 커밋을 하나더 생성하기 때문이다. 그래서 커밋 히스토리를 깔끔하게 유지하고 싶다면 rebase를 사용해 주면 된다


$ git pull <원격저장소명> <원격저장소의 브랜치명>
$ git pull --rebase <원격저장소명> <원격저장소의 브랜치명>

최종적으로 정리하면, 원격에 변경사항은 가져오고 싶은데 로컬의 변경사항에는 영향을 주지 않게 하려면 fetch, 원격의 변경사항을 바로 반영하려면 pull을 사용하면 되겠다!

Branch, HEAD


브랜치는 깃에서 기본적인 작업 단위이다. 보통 브랜치 단위로 기능 개발을 한다. 브랜치를 사용하는 이유는 각 브랜치마다 독립적인 환경을 가지고 개발을 가능하게 하며, main 브랜치를 기준으로 분기시켜서 원본 브랜치에는 변화 또는 영향을 주지 않기 때문이다
위 초록선이 main이라고 생각하고, 나머지 색의 선이 분기된 브랜치라고 생각하면 된다


HEAD란 무엇인가? HEAD는 브랜치의 가장 최근에 반영한 커밋 == 마지막 변경사항을 가리키는 포인터라고 생각하면 된다. 위 사진을 보면 현재 877C212 커밋 해시를 가진 커밋을 로컬과 원격 브랜치가 모두 가리키고 있다.

브랜치 조작 명령어

git checkout [브랜치명]: 이름은 checkout이지만 실제로는 다른 브랜치로 이동할 때 사용한다.
git switch -c [새로운 브랜치명]: 새로운 브랜치를 생성하면서 브랜치 이동
git branch [새로운 브랜치명]: 새로운 브랜치를 생성
git branch -d [브랜치명]: 로컬 브랜치를 삭제
git push origin --delete [브랜치명]: 원격의 브랜치를 삭제

clone, init, origin

git init : 현재 디렉토리를 로컬 레포로 설정하는 명령어
git clone : 원하는 디렉토리에 clone을 통해서 깃허브에 저장되어 있는 원격 저장소 작업물을 복사해서 가져올 수 있다.

reset

reset ]

reset은 로컬에서 커밋 후 커밋 메시지를 잘못 작성했거나. 잘못된 파일이 커밋에 포함된 경우 이전 시점으로 돌아가는 경우에 사용합니다.

git reset --soft [커밋ID] : 가장 최근 커밋만 취소, 변경사항과 스테이징영역에는 커밋이 취소되면서 그 취소한 커밋에 들어있던 파일들이 다시 스테이징 영역에서 확인 가능
git reset --mixed [커밋ID] : 지정한 커밋으로 이동하고 스테이징영역도 지정된 커밋의 상태로 돌아감
git reset --hard [커밋ID]: 가장 최근 커밋이 가지고 있는 변경사항을 모두 취소 이전 커밋으로 돌아간다고 생각하기

→ 셋다 커밋을 취소하지만, 각각의 상황에 따라 어떤 reset을 사용할지 고민

Pull Request, Merge

pull-request-merge

  • Pull Request :
  • Merge :
    1) Fast-Forward Merge : 대상 브랜치가 병합하려는 브랜치의 최신 커밋을 그대로 따라갈 수 있을 때 발생합니다. 별도의 병합 커밋 없이 브랜치의 HEAD가 이동합니다.
    2) 3-Way Merge : 두 브랜치가 서로 다른 변경 사항을 가질 때 발생하며, 공통 조상(ancestor)을 기준으로 병합합니다. 새로운 병합 커밋이 생성됩니다.
  • Pull Request : 브랜치에서 기능 개발 후 메인 브랜치에 합치기 위해서 이 코드를 리뷰하고 확인하는 과정을 요청하는 것을 의미합니다
  • Merge : 브랜치 단위로 개발한 기능을 메인 브랜치에 합칠 때 사용합니다
  1. Fast-Forward Merge : 메인 브랜치와 분기된 작업 브랜치를 비교했을 때, 작업 브랜치가 메인 브랜치의 커밋을 포함하고 추가 분기가 없으면 Fast-Forward 상태이며, 이 상태에서는 작업 브랜치의 커밋을 따라 메인 브랜치 포인터를 단순히 앞으로 이동시키는 방식으로 Merge가 가능하다. HEAD 만 작업 브랜치로 옮기기 때문에 별도의 merge commit은 생성되지 않는다
  2. 3-Way Merge : 병합하려는 메인 브랜치에서도 커밋이 존재하고, 작업 브랜치에도 메인 브랜치에 존재하지 않는 커밋이 존재할 때 공통 조상(base) 뒤에 들어온 커밋들을 합쳐서 하나의 커밋 단위로 만들어서 커밋 히스토리를 선형적으로 유지
  • git merge 시 base를 기준으로 3-way를 비교 후 새로운 merge commit 생성
  • 3-way Merge 텍스트 다이어 그램
    초기 상태:
    main: A --- B --- E
    feature: A --- B --- C --- D

    Merge(3-Way):
    main: A --- B --- E --- M
                     \ 
    feature:          C --- D

    M = Merge Commit (C+D+E를 합친 새로운 커밋)

rebase

rebase

rebase : 커밋 히스토리를 유지하면서 내 커밋을 현재 최신 원격 브랜치에 뒤에 이어 붙인다. merge와 다르게 새로운 merge 커밋을 만들지 않고 한줄로 이어 붙임 원격을 변경사항을 내 브랜치에 바로 적용하고 싶을 때 사용한다

stash

stash

git stash : 현재 깃이 추적하고 있는 파일들의 변경사항을 임시저장
git stash list: 저장된 stash 목록을 확인
git stash apply stash@{0} : stash@{0} 에 저장한 변경사항을 다시 가져온다.단, stash는 삭제되지 않고 남아있음
git stash pop: 가장 최신 stash를 현재 브랜치에 적용하고 stash에 저장한 내용까지 삭제
git stash drop stash@{0}: 특정 stash만 삭제
git stash clear: stash 전체 삭제

$ git status
# 수정한 파일(main.java)이 있으나 아직 commit 하지 않은 상태

$ git stash
# 현재 작업 중인 변경사항을 임시 저장(stash)하고 작업 디렉토리를 깨끗한 상태로 되돌린다
# 스테이징된 파일도 함께 stash됨

$ git stash list
# 저장된 stash 목록 확인
# 예) stash@{0}: WIP on feature/login: 123abcd message...

$ git checkout <다른브랜치명>
# 변경사항이 사라졌으므로 안전하게 다른 브랜치로 이동 가능

$ git stash apply
# 가장 최근 stash를 작업 디렉토리에 다시 적용한다
# 단, stash 목록에서 삭제되지는 않음

# 또는 지정해서 꺼내기
$ git stash apply stash@{0}
# stash@{0} 항목을 현재 브랜치에 적용

$ git stash drop stash@{0}
# 특정 stash 항목 삭제

$ git stash pop
# stash 적용 + stash 목록에서 삭제까지 동시에 수행
# 일반적으로 가장 많이 쓰이는 방식

글을 마치며

개념 위주로 많이 설명을 적어둬서 글을 보면서 실제로 깃을 사용하기시는 어려울 수도 있지만, 제가 이해한 개념을 잘 이해한다는 관점에서 글을 읽어주시면 감사하겠습니다! 다음 글에는 충돌이나 실제 깃을 처음 접하는 사람들이 해결하기 어려운 그런 문제들에 대해서도 다뤄보겠습니다.

profile
백엔드 개발과 친해지기!

0개의 댓글