Git 주요 명령어 정리(2)

조성철 (JoSworkS)·2020년 8월 9일
1

TIL(Today I Learned)

목록 보기
71/73
post-thumbnail

아래 글은 'いまさらだけどGitを基本から分かりやすくまとめてみた'을 번역하여 정리한 글입니다.
출처: https://qiita.com/gold-kou/items/7f6a3b46e2781b0dd4a0

git reset

  • HEAD의 위치를 변경
  • 3종류의 옵션(--soft, --mixed, --hard) 으로 HEAD의 위치를 변경했을 때, 돌아오는 방법을 선택

--soft

  • commit만 취소
  • Staging Area에 있는 파일은 그 대로 유지
  • ^ 의 수로 취소하는 위치를 지정

--mixed

  • commit과 add를 취소
  • Staging Area에 둔 파일은 working directory로 돌아가게 하고, 삭제 x

--hard

  • commit과 add를 취소한 후에 working directory 상의 파일도 삭제

옵션별 명령어 정리

# [방법 1] commit을 취소하고 해당 파일들은 staged 상태로 워킹 디렉터리에 보존
$ git reset --soft HEAD^

# [방법 2] commit을 취소하고 해당 파일들은 unstaged 상태로 워킹 디렉터리에 보존
$ git reset --mixed HEAD^ # 기본 옵션
$ git reset HEAD^ # 위와 동일
$ git reset HEAD~2 # 마지막 2개의 commit을 취소

# [방법 3] commit을 취소하고 해당 파일들은 unstaged 상태로 워킹 디렉터리에서 삭제
$ git reset --hard HEAD^

# 출처: https://gmlwjd9405.github.io/2018/05/25/git-add-cancle.html

reset 취소

  • reset 자체를 reset으로 취소 가능
  • HEAD를 움직이기 때문에 reflog에 잔존

# a.txt를 취소
$ ls
README.md

# reflog 확인。HEAD@{0}로 reset한 이력이 남아 있음
$ git reflog
63678f0 HEAD@{0}: reset: moving to HEAD@{1}
08f9902 HEAD@{1}: commit: Add a.txt
63678f0 HEAD@{2}: clone: from http://XXXXX

# HEAD@{0} reset보다 전으로 돌아가는 것으로 reset을 취소
$ git reset --hard HEAD@{1}
HEAD is now at 08f9902 Add a.txt

# a.txt가 돌아간 것을 확인
$ ls
README.md  a.txt

# reflog 확인. 새로운 reset 이력이 있음
$ git reflog
08f9902 HEAD@{0}: reset: moving to HEAD@{1} # 새 이력
63678f0 HEAD@{1}: reset: moving to HEAD@{1}
08f9902 HEAD@{2}: commit: Add a.txt
63678f0 HEAD@{3}: clone: from http://XXXXX

git fetch

  • 리모트 레포의 최신 상태를 로컬 레포의 리모트 추적 브랜치에 다운로드
  • 리모트 추적 브랜치에 다운로드만 하므로 워킹 디렉토리에 영향 X

git merge

  • 지정한 브랜치를 현재의 브랜치에 통합
  • fast-forward와 non-fast-forward 두 종류의 머지가 존재

fast-forward

  • fast-forward는 부모 브랜치의 히스토리가 파생된 브랜치의 생성 시와 비교해서 진행되어 있지 않은 상태에서 머지할 경우의 처리방법
  • fast-forward 가능한 상태에서 머지하는 경우에는 커밋이 그대로 통합하고자 하는 브랜치에 통합되고 새롭게 머지할 필요 X
  • 하지만, non-fast-forward 옵션을 붙이면 fast-forward라도 반드시 머지 커밋을 붙이는게 가능

fast-forward의 이미지 그림(merge 전)

A--B ←master
    \
     C ←sample

fast-forward의 이미지 그림(merge 후)

A--B---C ←master
    \ /
     C ←sample

fast-forward 예시

# 1. 새로운 브랜치 생성
$ git checkout -b sample

# 2. 현재 브랜치 조회
$ git branch
  master
* sample

# 3. 현재 디렉토리 내 파일 조회
$ ls
README.md

# 4. sample 브랜치에 새로운 파일 생성 후 커밋
$ touch a.txt

$ git add a.txt

$ git commit -m "Add a.txt"
[sample5 7525ea8] Add a.txt
 1 file changed, 1 insertion(+)
 create mode 100644 a.txt

# 5. master 브랜치로 이동
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.

# 6. sample 브랜치의 커밋을 master 브랜치에 머지
$ git merge sample
Updating cfec4a3..7525ea8
Fast-forward
 a.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 a.txt

# 7. 현재 디렉토리 내 파일 재조회
$ ls
README.md  a.txt

non-fast-forward

  • non-fast-forward는 부모 브랜치의 히스토리가 파생된 브랜치의 생성 시와 비교해서 진행되어 있을 때의 처리방법
  • non-fast-forward 상태에서 머지하는 경우는 머지하는 곳에서 새롭게 머지-커밋 필요

non-fast-forward의 이미지 그림(merge 전)

A--B--D ←master
    \
     C ←sample

non-fast-forward의 이미지 그림(merge 후)

A--B--D--E ←master
     \  /
      C ←sample

non-fast-forward 상태에서의 흐름

  • master 브랜치에서 커밋이 한 단계 늦은 sample 브랜치 상에서 실행한 커밋을 master 브랜치에 머지하는 예시
# 1. sample 브랜치 작성
$ git branch sample

# 2. 브랜치 조회
$ git branch
* master
  sample

# 3. 현재 디렉토리의 파일 조회
$ ls
README.md

# 4. master 브랜치에서 a.text를 생성하고 커밋. sample 브랜치에서 보면 master 브랜치가 커밋이 한 단계 진행되어 있는 상태
$ touch a.txt

$ git add a.txt

$ git commit -m "Add a.txt"
[master 66ea8cb] Add a.txt
 1 file changed, 1 insertion(+)
 create mode 100644 a.txt

# 5. sample 브랜치로 이동
$ git checkout sample

# 6. sample 브랜치에서 b.txt를 생성하고 커밋
$ touch b.txt

$ git add b.txt

$ git commit -m "Add b.txt"
[sample 6c92a36] Add b.txt
1 file changed, 1 insertion(+)
create mode 100644 b.txt

# 7. master 브랜치로 이동
$ git checkout master
Switched to branch 'master'

# 8. sample 브랜치의 커밋을 부모 브랜치(master)에 머지. non-fast-forward를 위해 머지-커밋 실시
$ git merge sample
--------------------------------------------
머지-커밋을 하기 위해 아래와 같은 안내문이 표시
Merge branch 'sample'

# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
--------------------------------------------
Merge made by the 'recursive' strategy.
 b.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 b.txt

# 9. 파일 조회
$ ls
README.md a.txt b.txt

git rebase

  • 히스토리를 변경
  • 여러 옵션이 있지만, 주로 '무(無) 옵션', 'i 옵션', 'continue 옵션', 'abort 옵션' 을 사용

무(無) 옵션

  • 파생 브랜치의 베이스를 최신으로 변경
  • 아래 그림을 보면, 파생 브랜치 A에서 작업을 하고 있는 사이에 다른 파생 브랜치인 B가 master 브랜치에 머지된 것에 의해 master 브랜치가 진행된 경우


1. 충돌이 없는 경우

  • 충돌이 없는 경우에는 간단
  • 아래 예시는 sample1 브랜치의 커밋이 master 브랜치에 머지된 것으로 뒤쳐진 sample2 브랜치에서 rebase 실시
  • sample1 브랜치와 sample2 브랜치에서는 수정 대상 파일이 다르기 때문에 충돌이 발생 X
# 1. 파생 브랜치 생성
$ git branch
* master

$ git branch sample1

$ git branch sample2

# 2. sample1 브랜치로 이동
$ git checkout sample1
Switched to branch 'sample1'

# 3. a.txt를 생성하고 add -> commit -> push 실시
$ touch a.txt # aaa 입력 후 저장

$ cat a.txt
aaa

$ git add a.txt

$ git commit -m "Add a.txt"

$ git push origin sample1
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 271 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: Updating references: 100% (1/1)
To http://xxxxx
 * [new branch]      sample1 -> sample1

# push한 내용을 리모트 레포에서 머지, sample2 브랜치의 기준에선 늦어져 있는 상태가 됨

# 4. sample2 브랜치로 이동
$ git checkout sample2
Switched to branch 'sample2'

# 5. b.txt를 생성하고 add -> commit -> push 실시
$ touch b.txt

$ git add b.txt

$ git commit -m "Add b.txt"
[sample2 b08565c] Add b.txt
 1 file changed, 1 insertion(+)
 create mode 100644 b.txt

$ git push origin sample2
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 273 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: Updating references: 100% (1/1)
To http://XXXXX
 * [new branch]      sample2 -> sample2

  • 여기서, github 브라우저 화면에서 Pull Request를 하면, 아래와 같이 경고창 표시


해결 방법

# 현재 origin 리모트 레포의 master 브랜치에 rebase 실시
$ git rebase origin/master
First, rewinding head to replay your work on top of it...
Applying: Add b.txt

# sample1 브랜치에서 생성한 a.txt가 들어있는 것을 확인
$ ls
README.md  a.txt b.txt

# 재push, rebase한 후의 push는 f(force) 옵션이 필요
$ git push origin sample2 -f
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 304 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: Updating references: 100% (1/1)
To http://xxxxx
 + b08565c...1910d84 sample2 -> sample2 (forced update)

  • 머지가 정상적으로 실행


2. 충돌이 있는 경우

  • 충돌이 있는 경우에는 난이도가 상승
  • 아까와 마찬가지로 sample1 브랜치의 커밋이 master 브랜치에 머지된 경우이며 뒤쳐저 있는 sample2에서 rebase 실시
  • 단, sample1과 sample2에서 같은 파일을 수정하여 충돌이 발생시키는 예시

# 1. 파생 브랜치를 생성
$ git branch
* master

$ git branch sample1

$ git branch sample2

# 2. sample1 브랜치로 이동
$ git checkout sample1
Switched to branch 'sample1'

# 3. a.txt를 수정하고 add -> commit -> push 실시
$ touch a.txt # aaa ccc 입력 후 저장

$ cat a.txt
aaa
ccc

$ git add a.txt

$ git commit -m "Modify a.txt ccc"
[sample1 417b2d8] Modify a.txt ccc
 1 file changed, 1 insertion(+)

# 4. sample1을 remote repo에 push
$ git push origin sample1
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 584 bytes | 0 bytes/s, done.
Total 6 (delta 0), reused 0 (delta 0)
remote: Updating references: 100% (1/1)
To http://XXXXX
 * [new branch]      sample1 -> sample1

# 브라우저(깃헙) 상의 리모트 레포에서 머지. sample2는 master와 비교해서 늦어진 상황

# 5. sample2로 이동
$ git checkout sample2

# 6. a.txt를 수정하고 add -> commit -> push 실시
 $ touch a.txt # aaa ddd 입력 후 저장

 $ cat a.txt
 aaa
 ddd

$ git add a.txt

$ git commit -m "Modify a.txt ddd"
[sample2 58af6b0] Modify a.txt ddd
1 file changed, 1 insertion(+)

$ git push origin sample2
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 311 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: Updating references: 100% (1/1)
To http://XXXXX
  * [new branch]      sample2 -> sample2

  • 이 상태에서 Pull Request를 하면 아래와 같이 충돌이 발생
  • 왜냐하면, 이미 리모트 레포의 master 브랜치에 머지된 sample1의 a.txt를 수정해서 그 변경을 반영하지 않은 채 sample2의 같은 파일인 a.txt를 다른 내용으로 수정했기 때문


해결 방법

# 1. rebase 실행하면 충돌때문에 실패
$ git rebase origin/master
First, rewinding head to replay your work on top of it...
Applying: Modify a.txt ddd
Using index info to reconstruct a base tree...
M       a.txt
Falling back to patching base and 3-way merge...
Auto-merging a.txt
CONFLICT (content): Merge conflict in a.txt
error: Failed to merge in the changes.
Patch failed at 0001 Modify a.txt ddd
The copy of the patch that failed is found in: .git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

# 2. 브랜치를 확인. rebase중인 것을 확인
$ git branch
* (no branch, rebasing sample2)
  master
  sample1
  sample2

# 3. 해당 파일의 충돌을 해결
$ open a.txt # 파일을 열고 충돌되는 부분 수정

# vscode나 vi 등으로 확인해 보면 아래와 같이 diff가 표시됨

-------------------------------------------------------
  aaa
  <<<<<<< 3639ad3cc4a0208ba8201ad074accdb31d9348d8
  ccc
  =======
  ddd
  >>>>>>> Modify a.txt ddd
-------------------------------------------------------

# 4. 충돌된 부분을 해결(sample1과 sample2의 변경사항을 둘 다 반영)
-------------------------------------------------------
aaa
ccc
ddd
-------------------------------------------------------

# 5. 수정한 파일 add
$ git add a.txt

# 6. rebase 완료
$ git rebase --continue
Applying: Modify a.txt ddd

# 7. 브랜치 확인. rebase 상태가 아닌 것을 확인
$ git branch
  master
  sample1
* sample2

# 8. 재push。rebase한 후에 push는 f(force) 옵션 필요
$ git push origin sample2 -f
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 318 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: Updating references: 100% (1/1)
To http://XXXXX
 + 58af6b0...0731fcc sample2 -> sample2 (forced update)

  • 아래와 같이 머지가 가능한 상태로 전환

  • 머지 완료 후, 잊지 않고 로컬 레포의 master로 이동하여 최신화
$ git checkout master

$ git pull

rebase의 continue 옵션

  • 현재의 rebase를 완료
  • 충돌을 해결한 후에 쓰는 경우 多

추가적인 학습을 위한 참고자료

  • 아래 링크들 참고
  1. https://git-scm.com/docs
  2. https://git-scm.com/book/ko/v2
  3. https://opentutorials.org/module/3963/24442
  4. https://www.holaxprogramming.com/2018/11/01/git-commands/

0개의 댓글