본문 바로가기
IT 기본지식

Git 명령어 기본부터 심화까지

by 내기록 2023. 4. 4.
반응형
 

목차 LIST

     

     

     

    Git은 소스코드 버전 관리 시스템이며, Git으로 관리하는 프로젝트를 올려둘 수 있는 Git 호스팅 사이트 중 하나가 GitHub입니다. GitHub 외에도 GitLab, BitBucket 등 다양하게 있습니다.

     

    Git 시작하기

    용어 정리

    용어 설명
    워킹 트리
    (Working tree)
    워킹트리, 워킹 디렉토리, 작업 디렉토리, 작업 폴더 모두 같은 의미이다.
    커밋을 체크아웃하면 워킹 트리가 해당 커밋 상태로 업데이트 되는데,
    정확하게는 작업 폴더에서 [.git]폴더인 로컬저장소를 뺀 나머지 부분이다.
    로컬저장소
    (Local repository)
    git init으로 생성되는 [.git] 폴더로 커밋, 스테이지가 이 폴더에 저장됨
    원격저장소
    (remote repository)
    로컬 저장소를 업로드 하는 곳이며, GitHub은 원격 저장소를 호스팅하는 플랫폼이다.
    Git 저장소 Git 명령어로 관리할 수 있는 폴더 전체를 Git 프로젝트 또는 Git 저장소라고 부른다.
    엄밀하게는 [.git] 폴더를 의미하지만, 넓게는 워킹 트리 전체를 의미하기도 한다.

     

    git init

    git init 명령어를 수행하면, [.git]이라는 폴더가 자동으로 생성됩니다. 이 폴더에는 Git으로 생성된 버전 정보와 설정 파일, 원격저장소 주소 등의 정보가 포함되어 있으며, [.git] 폴더를 '로컬 저장소'라고 부릅니다.

    로컬 저장소가 있는 현재 폴더, 일반적인 작업 폴더를 '워킹트리' 라고 합니다.

    $ git init
    Initialized empty Git repository in /Users/iin/PycharmProjects/pythonProject/.git/

     

    계정 등록

    지역 옵션으로 설정하고 싶으면 아래 명령어에서 --global을 삭제합니다. 지역 옵션은 현재 Git 저장소에만 사용되는 옵션입니다.

    git config --global user.email "이메일"
    git config --global user.name "이름"

     

    add : 변경사항 선택 /  commit : 변경사항을 하나로 묶어 버전으로 생성

    git commit -a 는 add 명령을 생략하고 바로 커밋할 때 사용합니다. 다만, untracked 상태의 파일은 커밋되지 않습니다.

    파일 상태에 대한 설명은 아래에 있습니다.

    $ git add main.py
    $ git commit -m "init"
    [master (root-commit) 8aa7e99] init
     1 file changed, 24 insertions(+)
     create mode 100644 main.py
    알아두면 좋은 커밋 메시지 7규칙
    1. 제목과 본문은 빈줄로 분리
    2. 제목은 50자 이내
    3. 제목을 영어로 쓸 경우, 첫 글자는 대문자
    4. 제목에는 마침표 쓰지않기
    5. 제목을 영어로 쓸 경우 현재형으로 쓰기
    6. 본문을 72자 단위로 줄바꿈
    7. 어떻게보다 무엇과 왜를 설명

     

    git pull : git fetch + git merge

    원격저장소의 변경사항을 로컬 저장소로 가져온 후, 워킹 트리에 반영합니다.

    즉, git fetch 와 git merge를 함께 실행하는 명령어입니다.

     

    git status : Git 저장소의 상태를 알고싶을 때

    Git 워킹트리의 상태를 보여주는 명령으로 Git 저장소가 아닌 폴더에서 실행하면 오류가 발생합니다.

    $ git status
    On branch main
    Untracked files:
      (use "git add <file>..." to include in what will be committed)
    	.idea/
    	test_0327.py
    
    nothing added to commit but untracked files present (use "git add" to track)

     

    git status -s 는 파일의 상태를 짧게 요약해서 상태를 보여주는 명령입니다. 변경된 파일이 많을 때 유용합니다.

    $ git status -s
    ?? .idea/
    ?? test_0327.py

     

     

    git log : 커밋 로그 확인

    commit 3d94a73e2257735ed9ccd12e940ede9fc7ca3d45 (HEAD -> master)
    Author: sunrise <@gmail.com>
    Date:   Tue Apr 4 22:27:52 2023 +0900
    
        add asynciotest
    
    commit 8aa7e992312b8e2337c33e353d169d96e0f176d3
    Author: sunrise <@gmail.com>
    Date:   Tue Apr 4 22:27:14 2023 +0900
    
        init

    더 깔끔하게 보고싶다면 아래 명령어를 사용합니다.

    git log --oneline --graph --decorate --all

    $ git log --oneline --graph --decorate --all
    * 3d94a73 (HEAD -> main, origin/master, origin/main, master) add asynciotest
    * 8aa7e99 init

    --oneline : 커밋 메시지를 한 줄로 요약해서 보여줍니다.

    --graph : 커밋 간의 관계와 브랜치 흐름을 그래프로 보여줍니다. (GUI와 유사한 모습)

    --decorate : 각 커밋에 대한 브랜치와 태그 정보를 간결하게 표시합니다.

    --all : 모든 브랜치의 커밋 로그를 표시합니다. 이 옵션이 없으면 현재 체크아웃된 브랜치만 표시됩니다.

     

    원격저장소(Github)에 코드 올리기

    Repository를 생성하고 아래 작업을 진행합니다.

     

    git remote add 명령어로 로컬 저장소에 원격 저장소 주소를 연결하여, 이후 커밋을 원격 저장소로 Push 할 수 있도록 합니다.
    git remote add origin https://github.com/_/git_test.git

     

    PUSH : 원격 저장소로 커밋 올리기

    git push origin master

     

    * 참고) 2020년 10월부터 Github에서 New 버튼으로 프로젝트 저장소를 생성하면 기본 브랜치가 main이 됩니다.

    push 했을 때 "main and master are entirely different commit histories." 에러가 발생할 수 있습니다.

    이 에러는 main, master 브랜치가 서로 다른 커밋 기록을 가지고 있어 병합할 수 없기 때문에 발생합니다. 즉, 두 브랜치에서 독립적으로 작업을 해서 공통된 커밋 기록이 없는 상태고 이를 병합하려고 할 때 Git은 브랜치에서 발생한 변경사항을 어떻게 병합하면 좋을지 알 수 없어 발생합니다. 따라서 git rebase나 git cherry-pick과 같은 명령어를 사용해서 한 브랜치에서 다른 브랜치로 변경 사항을 넘기거나, 한 브랜치에 다른 브랜치에서 발생한 변경사항을 반영해야 합니다.

     

    아래 명령어를 수행하면 master 브랜치의 내용으로 main 브랜치를 덮어씁니다. 만약, main 브랜치에는 readme.md 파일이 있고 master 브랜치에는 다른 여러 파일이 있는 상황에 아래 명령어를 실행하면 main 브랜치는 master 브랜치와 동일해지며 readme.md 파일은 유실됩니다.

    # master 브랜치로 옮겨가서
    $ git checkout master
    
    # main 브랜치를 master 브랜치가 현재 가리키는 커밋으로 설정
    $ git branch main master -f
    
    # main 브랜치로 옮겨가서
    $ git checkout main
    
    # push
    $ git push origin main -f

     

    다음 명령어를 자세히 살펴보겠습니다.

    git branch main master -f

    > "main" 브랜치를 새로 만들거나 이동시켜 "master" 브랜치가 현재 가리키는 커밋으로 설정
    > 이 명령어는 강제적으로 브랜치의 커밋을 덮어쓰므로, 실행 전에 중요한 변경 사항을 백업하는 등의 주의가 필요합니다.
    • git branch는 브랜치 생성, 리스트 조회, 삭제에 사용하는 명령어
    • main은 새로 만들거나 이동시킬 브랜치 이름
    • master는 main 브랜치의 새로운 시작점으로 사용할 브랜치 이름
    • -f, --force는 커밋 기록이 삭제되더라도 작업을 수행하도록 강제하는 선택적 플래그
      (브랜치가 이미 존재하거나 다른 커밋을 가리키고 있을 때 덮어쓰거나 새로운 커밋으로 이동)

     

    Push - upstream

    git push 명령어를 실행했을 때 `fatal : The current branch master has no upstream branch.` 가 발생하는 경우가 있습니다.

    upstream은 로컬저장소와 연결된 원격저장소를 말합니다. 이 에러는 로컬 저장소의 브랜치와 연결된 원격 저장소의 브랜치가 없어서 발생하는 오류로 --set-upstream을 사용하거나 이 명령의 단축 명령인 -u 옵션을 사용합니다.

    git push -u origin master

    이렇게 하면 origin 저장소의 [feature/a]가 로컬 저장소 [feature/a]의 업스트림으로 지정되어 git push 명령어만으로 에러없이 push가 가능합니다.

     

     

    커밋은 Delta(차이점)가 아니라 Snapshot(스냅샷)

    Git은 변경이 일어났을 때 수정 사항(Delta)만 저장하는 것이 아니라, 전체 파일의 스냅샷을 저장합니다. 차이점만 저장하는 것이 용량도 적고 빠를 것이라고 생각할 수 있지만, 이 방식은 이전 버전을 보여줄 때 모든 변경 사항을 추적하며 계산해야 하는 단점이 있습니다. 반면에, Git은 파일의 스냅샷(전체)을 저장하기 때문에 이러한 복잡한 계산이 필요 없습니다.

     

    Git에서 사용하는 파일의 상태 4가지

    untracked untracked 추적 안됨 한 번도 커밋되지 않은 신규 파일
    Git에서 아직 추적하지 않는 파일
    tracked modified 수정함 Git에서 추적 중인 파일이 수정된 상태
    변경이 일어난 파일
    staged 스테이지됨 add 명령어를 통해 변경 사항을 로컬저장소 스테이지에 올린 파일
    이 파일은 다음 커밋에 포함될 준비가 된 상태
    unmodified 수정 없음 커밋 후 변경되지 않은 파일로 현재 스냅샷과 일치한 상태
    commit 명령어를 통해 스냅샷으로 저장되며, 이때는 로컬 저장소에만 적용
    이후 push 명령어로 커밋을 원격 저장소로 업로드할 수 있음

     

    https://www.git-tower.com/learn/git/ebook/en/command-line/remote-repositories/introduction

    branch(브랜치)

    브랜치는 Git에서 특정 커밋을 가리키는 포인터입니다. 커밋을 할 때마다 해당 브랜치는 자동으로 최신 커밋을 가리키게 됩니다. 브랜치가 '가리킨다'라는 표현을 쓰는 이유는 브랜치는 단순 "포인터"이기 때문입니다.

    아래 이미지에서는 f30ab 커밋을 master, testing 브랜치가 동시에 가리키고 있습니다.

     

    HEAD는 현재 작업 중인 브랜치 또는 커밋을 가리키는 특수한 포인터입니다. HEAD를 통해 현재 어떤 브랜치에서 작업 중인지 알 수 있으며, 이를 이용해 브랜치를 자유롭게 넘나들 수 있습니다. 예를 들어, 지금 HEAD가 master브랜치를 가리키고 있지만, 다른 브랜치(예:testing)로 이동할 수도 있습니다.

    브랜치의 최신 커밋이 아닌 과거 커밋으로도 HEAD를 이동시킬 수 있습니다. (아래 이미지에서 98ca9 또는 34ac2)

    다만, 이 경우에는 master 브랜치의 포인터와 HEAD가 떨어지기 때문에 분리된 HEAD(Detached HEAD) 상태가 됩니다.

    이는 브랜치가 아닌 특정 커밋에서 작업하는 상황을 의미합니다.

    https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell

    규칙

    원격저장소는 여러 사람이 함께 작업하기 때문에 브랜치 규칙을 정하는 것이 일반적입니다. 간단한 규칙 예시입니다.

    1. [master] 브랜치에는 직접 커밋하지 않는다. (여러 사람이 동시에 작업하면 충돌이 생길 수 있음)
    2. 새로운 기능 개발 시 [master] 브랜치에서 새로운 브랜치를 생성한다.
    3. 새로운 브랜치의 이름은 [feature/기능명] 으로 하고, 한 명만 커밋을 올린다.
    4. [feature/기능명] 브랜치에서 기능 개발이 완료되면 [master] 브랜치에 병합한다.

    [feature/detail-page] 브랜치에서 작업 후 commit/push 하게되면 [origin/feature/detail-page] 브랜치가 생성됩니다. 여기서 origin/은 원격 저장소에 올라갔다는 것을 의미합니다.

     

    브랜치 생성 명령어

    git branch 로컬 저장소의 브랜치 목록을 조회합니다.
    표시된 브랜치 중 *가 붙어 있으면 HEAD가 해당 브랜치를 가리키고 있다는 뜻입니다.
    git branch [-f] <브랜치명> [커밋체크섬] 새로운 브랜치를 생성합니다.
    커밋 체크섬을 제공하지 않으면 HEAD로부터 브랜치 생성
    -f 옵션을 사용하면 기존 브랜치를 다른 커밋으로 강제 이동시킬 수 있음
    git branch -r 원격 저장소의 브랜치 목록을 조회합니다.
    git checkout <브랜치명> 특정 브랜치로 이동(체크아웃)
    git checkout -b <브랜치명> <커밋체크섬> 특정 커밋에서 브랜치를 새로 생성하고, 그 브랜치로 체크아웃 진행

    master로부터 hotfix 브랜치 생성하고 체크아웃
    $git checkout -b hotfix master
    git merge <대상 브랜치> 현재 브랜치와 대상 브랜치를 병합
    병합 커밋이 새로 생기는 경우가 많음
    git rebase <대상 브랜치> 현재 브랜치의 커밋들을 대상 브랜치 위로 재배치합니다.
    git branch -d <브랜치명> 특정 브랜치를 삭제할 때 사용합니다.
    단, HEAD가 가리키고 있는 브랜치나 병합되지 않은 브랜치는 삭제 불가

    (참고: rebase 추가 설명)

     

    HEAD

    HEAD는 현재 작업중인 브랜치나 커밋을 가리킵니다. 브랜치는 특정 커밋을 가리키기 때문에, HEAD는 현재 작업 중인 브랜치의 최신 커밋을 가리키는 역할을 합니다.

    $ git log --oneline --graph --decorate
    * 3d94a73 (HEAD -> main, origin/master, origin/main, master) add asynciotest

     

    여기서 HEAD가 가리키는 main은 로컬의 [main] 브랜치이며, 원격 저장소의 [origin/main] 브랜치도 같은 커밋을 가리키고 있습니다.

     

    checkout : 브랜치 이동

    브랜치가 가리키고 있는 커밋의 내용을 워킹트리에 반영할 때 사용합니다. checkout 명령어는 브랜치를 전환하거나 특정 커밋으로 코드를 되돌릴 때 주로 사용합니다.

     

    브랜치 생성 시 : 새로운 브랜치는 보통 master 또는 main 브랜치로 체크아웃한 이후에 생성합니다.

    [master] 브랜치를 기준으로 feature 브랜치를 생성하고, 해당 브랜치에서 작업한 수정 사항을 다시 큰 줄기인 master로 병합합니다.

    $ git branch
    * main
      master
      
    $ git checkout master
    Switched to branch 'master'
    
    $ git branch
      main
    * master

     

    특정 커밋 되돌리기 : checkout 명령어에 커밋 아이디 앞 7자리를 사용해서 해당 커밋의 상태로 코드를 되돌릴 수 있습니다.

    아래는 첫 번째 커밋 아이디를 사용해서 init 으로 되돌리는 예제입니다.

    $ git checkout 8aa7e99
    Note: switching to '8aa7e99'.
    
    ...
    HEAD is now at 8aa7e99 init

     

    이전 커밋으로 돌아가기 : 마지막 커밋인 두 번째 커밋으로 돌아가려면 동일하게 커밋 아이디 7자리를 사용해도 되지만, `checkout -` 을 사용해 이전 커밋으로 복귀할 수 있습니다.

    $ git checkout -
    Previous HEAD position was 8aa7e99 init
    Switched to branch 'master'

     

    참고)

    checkout 명령어는 크게 두 가지 역할을 합니다.

    1) 브랜치 전환

    2) 작업 디렉토리의 파일 내용 복구

     

    최근에 checkout 명령어의 역할이 git switch와 git restore 명령으로 나뉘었습니다. 앞으로 switch, restore가 기본 명령이 될 가능성이 있으니 이것도 기억하면 좋습니다. 브랜치 전환을 위해서는 아래와 같이 사용할 수 있습니다.

    $ git switch <브랜치명>

     

     

    merge : 브랜치 병합

    A 브랜치와 B 브랜치를 병합할 때 다음과 같은 상황이 발생할 수 있습니다.

     

    1. Merge commit(병합 커밋) : A 브랜치의 변경점과 B 브랜치의 변경점이 합쳐져 새로운 커밋을 생성합니다.
    2. Fast-forward(빨리 감기) : A와 B중 하나로 상태를 변경합니다.
    3. Conflict : 충돌 발생

     

    아래 그림을 보겠습니다. 파란색 [master] 브랜치는 커밋2를 가리키고 있습니다. 이때, 커밋4를 가리키는 브랜치로 [master]브랜치가 머지된다면 이것은 Fast-forware mege입니다. 병합을 위한 새로운 커밋을 생성하지 않고 [master] 브랜치의 포인터만 이동하기 때문입니다. 

    이 다음 커밋4에 있는 분홍색 [master] 브랜치를 커밋5에 있는 브랜치와 merge할 때는 초록색 merge commit을 생성해서 서로의 변경사항을 적용해야합니다. 이걸 merge commit이 발생했다고 합니다.

     

    PR(Pull Request) : 브랜치를 합치는 예의바른 방법

    A 브랜치로 B 브랜치를 병합해도 되겠니? 수정 사항은 다음과 같아.

    마스터 브랜치에 코드를 병합하기 전에 협력자가 확인할 수 있는 과정을 거칩니다. 예를 들면 나의 코드를 병합하기 전에 사수의 허락을 거치는 것입니다.

     

     

    Release : 완성된 프로젝트 출시

    tag(태그) : 특정 커밋에 붙이는 태그

    브랜치는 특정 커밋을 가리키는 포인터라고 앞에서 이야기했는데, 태그도 동일합니다.

    태그를 새로 만들었을 때는 브랜치와 동일하게 푸시를 해줘야 원격 저장소에서도 확인이 가능합니다.

    -a로 주석있는 태그 생성하며 메시지와 태그 이름은 필수입니다.
    브랜치 이름을 생략하면 HEAD에 태그를 생성합니다.
    $ git tag -a -m <간단 메시지> <태그명> [브랜치 또는 체크섬]
    
    원격 저장소에 태그 업로드
    $ git push <원격저장소 별명> <태그명>

     

    example)

    # 로그 확인
    $ git log --oneline
    3d94a73 (HEAD -> master, origin/master, origin/main, main) add asynciotest
    8aa7e99 init
    
    # 태그 생성
    $ git tag -a -m "첫 번째 태그" v0.1
    
    # 생성 확인
    $ git log --oneline
    3d94a73 (HEAD -> master, tag: v0.1, origin/master, origin/main, main) add asynciotest
    8aa7e99 init
    
    # 태그 푸시
    $ git push origin v0.1
    Enumerating objects: 1, done.
    Counting objects: 100% (1/1), done.
    Writing objects: 100% (1/1), 180 bytes | 180.00 KiB/s, done.
    Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
    To https://github.com/sunrise/git_test.git
     * [new tag]         v0.1 -> v0.1

     

     

    Fetch : 원격 저장소의 이력 동기화

    fetch는 원격저장소의 브랜치와 커밋들을 로컬저장소에 동기화하는 명령어입니다.

     

    원격 저장소에서 최신 커밋 히스토리를 가져와서 로컬 저장소에 반영하는 일종의 "새로고침" 기능입니다. 

    예를 들어, 동료 개발자가 master 브랜치에 새로운 커밋을 올렸지만 내 소스트리에는 아직 그 이력이 보이지 않을 때 fetch 명령어를 사용해 원격 저장소에서 커밋 이력을 가져올 수 있습니다. 이때, 코드 자체는 변경되지 않고 커밋 기록만 업데이트 됩니다. (최신 코드를 반영하는 pull과 다름)

     

     

    심화

    amend : 마지막 커밋 수정

    amend는 방금 만든 커밋을 수정할 때 사용하는 명령어입니다. 커밋 후에 추가적인 변경 사항이 생겼거나, 커밋 메시지를 수정하고 싶을 때 유용합니다. 예를 들면 방금 만든 커밋에 파일을 추가하고 싶을 때 사용 가능합니다. 이를 통해 새로운 커밋을 만들지 않고, 기존 커밋을 덮어쓸 수 있습니다.

     

    소스트리에는 [Amend last commit : 마지막 커밋 정정] 버튼이 있습니다. 마지막 커밋을 amend(수정)하는 것입니다.

    이걸 사용하면 방금 수정해서 스테이지에 올린 변경사항을 기존 커밋에 덮어쓸 수 있습니다.

     

    주의 사항

    * 로컬 저장소의 master 브랜치에는 변경사항이 반영되지만 원격 저장소인 origin/master 브랜치에는 반영이 되지 않습니다. 즉, 원격 저장소에 이미 커밋을 푸시한 후에 amend로 커밋을 수정한 경우에는 이때는 강제 푸시(git push --force)가 필요합니다.

    * 강제 푸시는 혼자 사용하는 브랜치에서만 사용해야 합니다. 공용으로 사용하는 브랜치에서 하게되면 다른 사람의 커밋 히스토리가 꼬일 수 있으므로 매우 주의해야 합니다.

     

     

    Cherry-pick : 커밋 하나만 가져와서 지금 브랜치에 붙이고 싶을 때

    Cherry-pick은 특정 커밋 하나만 선택해서 현재 브랜치에 적용할 때 사용하는 명령어입니다. 주로 여러 커밋 중 일부 커밋만 가져와야 할 경우에 유용합니다. 해당 명령어를 사용하여 특정 수정 사항만 선별적으로 반영할 수 있습니다.

     

    브랜치 전략은 조직마다 다르겠지만, 한가지 예를 들어 보겠습니다.

    개발자는 [master] 브랜치에서 각자 [feature/기능이름]으로 브랜치를 따서 개발하고, 개발이 완료되면 [master] 브랜치로 병합합니다. [master] 브랜치 코드는 자동으로 테스트서버로 배포되어 항상 최신 개발 버전을 볼 수 있습니다. 그리고 [master]에서 굵직한 개발이 끝나면 [release] 브랜치에 병합시키고 이를 실제 서버에 배포합니다.

     

    브랜치명 특징
    feature/기능이름 각 개발자가 개발 중인 브랜치로 직접 커밋을 올림
    master [feature/기능이름] 브랜치에서 합쳐지는 브랜치. 출시 전인 베타 버전.
    직접 커밋을 올리지 않으며 병합을 통해서만 업데이트
    release 실제 출시할 코드를 올리는 브랜치
    [master]에서 굵직한 개발이 끄나면 출시 시점에 [release] 브랜치로 코드 병합

     

    이런 상황에서, 어제 출시한 release 브랜치에 당장 고쳐야 하는 버그가 있다는 사실을 뒤늦게 알았습니다. [master] 브랜치에서 [fix/test-bug] 브랜치를 따서 수정하고 이를 [master] 브랜치로 병합했습니다. 이제 [release] 브랜치에 반영해야 하는데, [master] 브랜치에는 다른 변경사항들이 있어서 딱 버그를 고친 커밋만 반영하고 싶습니다.

    이때, 체리픽(cherry-pick, 선별하다) 기능을 사용합니다. 따길 원하는 체리(커밋)을 선택하고 [체리픽] 을 수행합니다.

    https://www.geeksforgeeks.org/git-cherry-pick/

     

     

    주의 사항

    * cherry-pick으로 복사한 커밋은 새로운 커밋으로 기록되므로, 커밋 ID는 달라집니다. 같은 내용을 가진 커밋이라도 Git은 이를 다른 커밋으로 인식합니다.

    * cherry-pick은 유용하지만, 커밋 이력이 복잡해질 수 있으므로 주의해서 사용해야 합니다.

     

    Reset : 옛날 커밋으로 브랜치를 되돌리고 싶을 때

    git reset은 이전 커밋으로 브랜치를 되돌릴 때 사용합니다. 또한, 스테이징된 파일을 스테이징 해제(Unstaging) 할 때도 사용할 수 있습니다. 기본적으로 git reset 명령어는 mixed 모드로 동작합니다.

    $ git reset [파일명]

     

     

    Soft/Mixed reset : 변경사항을 남기면서 브랜치 되돌리기

    소스트리 기준, 원하는 커밋 우클릭하고 이 커밋까지 현재 브랜치를 초기화 선택.

    원하는 커밋으로 이력을 돌리며 변경 사항을 유지해준다.

     

    • Sort : 브랜치를 특정 커밋으로 이동하지만, 변경 사항을 스테이징 영역에 남겨둔다. 즉, 변경 사항은 바로 커밋할 수 있는 상태로 유지된다.
    • Mixed(Default) : 브랜치를 특정 커밋으로 이동하며, 변경 사항을 작업 디렉토리(워킹 디렉토리)에 남겨두지만 스테이징 영역에서 내린다. unstaging된 상태이기 때문에 다시 git add로 스테이징해야 커밋할 수 있다.

    Soft와 Mixed는 기능적으로 큰 차이가 없습니다. Mixed로 하고 파일을 Add 해서 스테이지로 올리면 Soft 상태가 되는거고, Soft로 하고 파일을 스테이지에서 내리면 Mixed 상태와 동일하기 때문입니다. 결국 스테이징 상태만 다를 뿐 변경 사항은 모두 유지됩니다.

     

    Hard reset : 변경사항을 지우면서 브랜치 되돌리기

    hard reset은 특정 커밋으로 브랜치를 이동할 때, 작업 중인 모든 변경 사항을 삭제합니다. 스테이징 영역과 워킹 디렉토리 모두 지정한 커밋 상태로 돌아갑니다. 이 명령어는 모든 작업을 지워야 할 때 사용합니다.

    git reset --hard <이동할 커밋 체크섬>
    현재 브랜치를 지정한 커밋으로 옮기며 작업 폴더도 함께 변경됩니다.

     

    이 명령어는 작업 폴더와 스테이징 영역을 지정한 커밋 상태로 완전히 초기화하며, 커밋되지 않은 변경 사항은 모두 삭제되므로 주의해서 사용해야 합니다.

     

     

    커밋 체크섬은 git log를 통해 확인할 수 있지만 CLI에서 체크섬을 다시 타이핑하는 것은 꽤 번거롭습니다. 이럴 때는 HEAD~ 또는 HEAD^ 로 시작하는 약칭을 사용할 수 있습니다.

    HEAD~<숫자>
    
    HEAD~ 는 헤드의 부모 커밋
    
    HEAD~2 는 헤드의 할아버지 커밋
    
    HEAD~n 은 n번째 위쪽 조상
    
    
    
    HEAD^<숫자>
    
    HEAD^ 헤드의 부모 커밋
    
    HEAD^2 는 두 번째 부모로 병합 커밋처럼 부모가 둘 이상인 커밋에서만 의미가 있음

     

    따라서 현재 브랜치를 두 단계 이전으로 돌릴 때는 아래 명령어를 사용합니다.

    $ git reset --hard HEAD~2
    
    
    위 명령어는 아래 세 명령어를 한 번에 수행합니다.
    $ git checkout HEAD~2
    $ git branch -f master
    $ git checkout master

     

     

    Revert : 커밋을 되돌리지만 이력을 남기고 싶을 때

    revert는 특정 커밋의 변경 사항을 되돌리면서, 이력을 유지하고 새로운 커밋을 만드는 명령어입니다.

    reset은 커밋을 없던 것처럼 되돌리지만, 함께 사용하는 브랜치에서 이력관리가 중요한 경우에는 없던 일처럼 만드는 것보다 변경사항을 되돌리는 새로운 커밋을 만드는 것이 더 좋습니다.

    즉, 'timeout 설정 추가' 라는 커밋의 변경사항을 없애고 싶을 때 'timeout 설정 되돌리기' 라는 새로운 커밋을 추가하는 것입니다.

     

    실제로는 'timeout 설정 추가' 라는 커밋을 되돌리면, Revert 'timeout 설정 추가' 라는 커밋이 만들어집니다.

    잘못된 커밋이 있다면 얼마든지 되돌릴 수 있습니다.

     

    example)

    C1 <- F1 <- C2 <- F2 <- C3 (master) 인 상태에서 긴급하게 기능 하나를 롤백해야 합니다. 기능은 F1, F2 커밋이지만 사이사이 다른 커밋들이 포함되어 있습니다.

    git revert F2
    git revert F1

    revert는 최신 커밋부터 취소를 하는 것이 좋습니다. 위 명령어로 취소를 하게되면 F2, F1을 취소하는 커밋 (RF2, RF2)을 만듭니다.

    C1 <- F1 <- C2 <- F2 <- C3 <- RF2 <- RF1 (master)

     

    이렇게 하면 이전의 히스토리를 변경하지 않고도 히스토리 중간의 여러 커밋을 작업 이전 상태로 돌릴 수 있습니다.

     

     

    Stash : 변경사항을 잠시 저장하고 싶을 때

    stash는 아직 커밋하지 않은 변경 사항을 임시로 저장하고 싶을 때 사용하는 명령어입니다.

     

     

    예를 들어, A 브랜치에서 개발 중이었는데, 갑자기 다른 브랜치로 이동해 긴급한 버그를 수정해야 하는 상황이라면, 아직 완성되지 않은 변경 사항을 커밋하지 않고 잠시 저장해둘 수 있습니다.

    stash는 작업 내용을 저장한 뒤, 언제든지 다시 꺼내서 작업할 수 있습니다. 

     

    참고로, stash에는 tracked 상태(추적중, 한 번이라도 Git에 올렸던 상태)의 파일만 들어갈 수 있습니다. 새로 생성한 파일은 untracked 상태이기 때문에 포함되지 않습니다.

     

     

     

    [Git] 3-way merge와 rebase를 이용한 fast-forward merge

    목차 LIST 3-way 병합하기 3-way merge는 쉽게 말해서 내 브랜치 커밋, 다른 브랜치 커밋을 병합해서 새로운 커밋을 생성하는 방법입니다. 어떤 상황에서 사용되는지 아래 예시로 살펴보겠습니다. 예

    sunrise-min.tistory.com

     

     

    References

    팀 개발을 위한 Git, GitHub 시작하기

    반응형

    댓글