[Bash] Git 기초 및 구조

2022. 12. 21. 00:44Developers 공간 [Basic]/Software Basic

728x90
반응형

이번엔 가장 많이 쓰는 Git 주요 명령어들을 한 페이지에 정리하고자 합니다.

항상 Bash 명령어나 모든 언어는 완벽하게 이해하고 출발하기는 힘드니, 자주 쓰는 명령어 먼저 익히고 하나씩 확장하는 식으로 배우시는 것을 추천합니다.

 

<구성>
1. Git의 구조
2. 자주 쓰는 필수 명령어
   a. git 명령어 기초
   b. local repository's branch 내에서 commit을 확인하고 옮겨 다니기
   c. local repository, remote repository 각각의 branch를 확인, 변경
   d. local repository와 remote repository 사이에서 정보 관리하기

글효과 분류1 : 코드

글효과 분류2 : 폴더/파일

글효과 분류3 : 용어설명

글효과 분류4 : 글 내 참조


1. Git의 구조

Git 명령어를 정리하기에 앞서 Git 구조 및 용어를 설명해 보면... 아래 처럼 정리할 수 있습니다.

[Local과 Remote의 Git 구조]

 

  • Local 은 Working Directory, Staging Area, Repository(Local)로 이루어져 있습니다.
    • Working Directory : 말 그대로 내 PC에 저장되어 있는 작업 메모리이며 sandbox 입니다.
    • Staging Area : Repository로 올릴 준비를 하기 위한 단계이며 Index라고 부르기도 합니다. 아직 repository에 가기직전으로, "git add" 명령어와 연관이 있습니다.
    • Repository(Local) : 사실 이 곳에 올라가 있으면 Remote Repository에 갈 준비가 다 되었다고 보는 것입니다. "git commit"과 연관이 있습니다. 
      • branch는 현재 작업하고 있는 repository의 작업장 이름을 말하며, commit은 현재 branch의 버전이라고 생각하시면 됩니다. 
      • HEAD : '현재 작업하려고 하는 branch'의 '가장 최신 commit'을 가리키고 있습니다. "git log --oneline" 혹은 "git reflog"로 확인이 가능하며, 뒤에서 자세히 다뤄보도록 합니다.
  • Remote는 Remote Repository로 이루어져 있습니다.
    • Repository(Remote) : 위의 그림은 Remote Repository의 저장소를 origin, origin2, origin3란 이름으로 만든 것이고, 보통은 origin을 사용합니다. 
      • git remote rename origin A : origin 에서 A라는 이름으로 원격저장소의 origin의 이름을 바꿔줄 수 있습니다. 

 


2. 자주 쓰는 필수 명령어

a. git 명령어 기초

 

Git Repository를 처음 만들었을 때 아래 명령어를 많이 보셨을 겁니다. 가장 기본적으로 Git이 생성되고, remote로 보내는 과정을 확인해봅시다.

git init
git add 파일
git commit -m "메시지"
git branch -M master
git remote add origin https://github.com/XXX/XXX.git
git push -u origin master
  • git init : ".git"폴더를 만들어 줌과 동시에 준비를 마칩니다. Working Directory에 ".git" 폴더가 없으면 git과 상관 없는 폴더라고 생각하시면 됩니다. 
    • 내가 만들기 시작한 Working Directory가 아닌 다른 사람의 repository를 끌어와 다른 repository를 시작하고 싶다면 "git clone https://github.com/YYY/YYY.git"으로 소스를 받아 온뒤 ".git"폴더를 지우고 다시 "git init"을 해주면됩니다.
  • git add 파일 : 앞서 말씀드린 Staging Area로 올리기 위한 방법입니다
    • 올리고 싶은 파일과, 그렇지 못한 파일들은 staging area로 올릴 때 대부분 해결하곤 합니다.
      • 혹시 올리기 싫은 파일이나 폴더가 있다면 ".gitignore"이라는 파일을 직접 만들어 내용을 추가해두면 git add단계에서 해당 파일들을 무시하게 됩니다. 아래 예시를 참조하세요.
      • (주의) "git add * " 는 gitignore에 있는 파일도 포함시킬 수 있어 "git add ." 혹은 "git add --all"이 안전합니다
    • git add --all : 현재 working directory에 있는 모든 내용들을 staging area로 올리기 위한 명령어입니다.
    • 현재 added 된 파일들이 어떤 것들인지 확인하려면 "git status"라는 명령어를 사용합니다.
    • <.gitignore 예시>
더보기

--------------------------------------------------------------------------

# : 코멘트 방법
*.a : .a files
!lib.a : lib.a라는 파일은 그래도 업로드해라!
/todo : todo file  (현재 폴더에 있는것! FOLDER/todo말고)
FOLDER/ : FOLDER folder
FOLDER/*.a : FOLDER안에 *.a파일들  ( FOLDER/FOLDER2/*.a는 아님)

--------------------------------------------------------------------------

  • git commit -m "메시지" : local repository의 현재 해당하는 branch 로 올리기 위한 방법입니다.
    • -m "메시지" : 어떤 내용으로 commit을 했는지 개인적으로 메시지를 남기는 부분입니다.
    • 현재 어떤 branch에서 작업하고 있는지는 "git branch -v"로 확인할 수 있습니다.
    • git config --global user.email "you@example.com"을 원하는 경우가 있는데,
      이는 처음에 github에 접근할 계정정보를 입력하기 위함 입니다.
      • --global : 플랫폼 전체에서 동일한 계정을 활용할 때 사용하는 것이므로, 하나의 시스템을 여러사람이 공유하고 있다면 이 명령어를 빼주거나 --local로 대체합니다.
      • 혹시나 --global로 이미 추가해 버렸다면 하기의 명령어로 "git config --list" 로 확인 한 후 "git config --global --unset user.email" 혹은 "git config --local --unset user.name"명령어를 활용해 지워줄 수 있습니다.
    • git commit을 통해 local repository로 올라간 feature를 'commit'이라고 부르며, 앞서 말씀드린 바와 같이 가장 최신 commit을 가리키고 있는 것을 'HEAD'라고 합니다.
  • git branch -M 브랜치명 : 현재 local repository의 branch 이름을 '브랜치명'으로 변경하겠다는 뜻입니다.
    • -M 이름 : '현재'의 local repository이름을 '브랜치명' 이름의 branch로 '복제'하지 않고 '변경'하기 위한 명령어 입니다.
    • 이는 "git checkout -b master" 와 비슷하지만, checkout -b 명령어는 기존 repository를 유지하면서 새로운 repository로 '복제'하며 이동합니다.
  • git remote add origin ...git : 'https:/github.com/XXX/XXX.git'의 Remote Repository를 내 컴퓨터에 origin 이라는 이름으로 remote repository로 인식하도록 저장하겠다는 뜻입니다.
    • 추가 후 현재 어떤 remote repository가 있는지를 확인하려면 "git remote -v"로 확인할 수 있습니다.
    • 다시 삭제하고 싶은 경우 "git remote rm origin"명령어로 현재 등록된 remote repository를 삭제해줄 수 있습니다.
  • git push -u origin master : 현재  local repository의 HEAD가 가리키고 있는 commit을, 'origin으로 인식하고 있는 Remote Repository'의 master라는 branch로 보내겠다는 뜻입니다.
    • -u 혹은 --set-upstream : 다음에 또 push & pull 하는 경우 "git pull" 혹은 "git push"만 적어주더라도 origin 이라는 remote repository의 master라는 branch와 연동해서 작업하도록 할 수 있는 옵션입니다. remote repository에 원하는 branch가 아직 없는 경우 최초 한번 해주어야 하는 경우도 있습니다.

b. local repository's branch 내에서 commit을 확인하고 옮겨 다니기

 

작업을 완료하고 "git add" 혹은 "git commit" 혹은 "git push"를 통해 내 작업물을 local repository 내에서 관리할텐데, 이때 다양한 실수가 날 수 있습니다. 이런 상황에 필요한 명령어들을 확인해 봅시다. 

# commit 상태 확인
git reflog

# Working Repository --> Staging Area
git add --all
git reset 파일이름
git reset

# Staging Area --> Local Repository
git commit -m "revised"
git reset HEAD^

# Local Repository --> Remote Repository
git push origin branch

git revert -f HEAD@{1}
git add --all && git commit -m "revised2"

git push origin branch -f

가장 기본적인 commit을 관리하기 위한 용어를 먼저 확인하기 위해 git reflog 명령어를 먼저 살펴보기로 합니다. 

  • git reflog : git에서 커밋, 체크아웃, 머지 등의 이력을 모두 기록
    ** git reflog : Local에 대한 전체 이력(commit, checkout, reset, merge...)을 확인 가능합니다.
    ** git log : Repository(Local+Remote)에 대한 현재 branch만의 log(commit)를 확인 가능합니다.
    • HEAD : HEAD란 해당 브랜치의 마지막 commit 을 뜻
    • HEAD@{번호} : reflog 결과로 볼 수 있는 commit의 숫자
    • master^ : 'master의 부모'와 같은 의미
    • master^^ : 'master의 조부모(부모의 부모)'와 같은 의미
    • HEAD~4 : (~) 틸드 연산자는 올라가고 싶은 부모의 갯수가 뒤에 숫자가 온다
    • (예시)
      • git reflog 
        1c6743127 (HEAD -> dev) HEAD@{0}: reset: moving to head~1
        (reset을 활용해 head@{1}로 이동했으며 현재 이동한 commit의 ID는 1c6743127 혹은 HEAD@{0}입니다. 또한 해당 이것이 현재의 HEAD입니다)
        f48441c33 (origin/dev) HEAD@{1}: commit: update older code3
        1c6743127 (HEAD -> dev) HEAD@{2}: reset: moving to head~1
        f48441c33 (origin/dev) HEAD@{3}: reset: moving to head~1
        e535d54a8 HEAD@{4}: commit: update older code2
        f48441c33 (origin/dev) HEAD@{5}: commit: update older code
      • git reset --hard HEAD@{1} :  HEAD@{}를 활용해 해당 commit으로 되돌릴 수 있다
        git reset --hard 커밋id : 해당 위치로 돌아갈 수 있습니다. 
        git reset f48441c33 : 직접 hash를 적어줄 수도 있습니다.
        이 명령어들은 아래에서 자세히 다뤄볼 예정입니다.
  • Working Repository --> Staging Area : add 실수 시
    • 현재 아직 commit하지 않았으므로 HEAD는 이전의 commit입니다. 아래에서 설명드리겠지만 reset의 default는 --mixed이기 때문에, Stage의 Index를 HEAD상태로 돌려놓되, working directory는 그대로 두도록 만들면 git add를 취소할 수 있습니다.
    • git reset HEAD 파일 혹은 git reset 파일 : 파일을 HEAD 상태로 되돌리되, 변경된 내용은 남겨 놓습니다.
      git reset HEAD 혹은 git reset : 모든 파일을 HEAD로 모두 돌리되, 변경된 내용은 남겨 놓습니다.
      이유는 아래 git reset의 여러가지 옵션을 설명을 들으면 자세히 아실 수 있습니다.
    • git checkout HEAD 파일 : git HEAD 상태로 파일을 복구한다. 변경된 내용은 삭제됩니다.
      (주의) 작업하고 있는 파일이 삭제될 수 있습니다.
    • checkout & reset & rm 의 차이
      • checkout vs reset : git checkout은 detached branch가 발생할 수 있습니다. 해당 내용은 하기 링크에 자세히 나와 있으니 참조하면 좋습니다.
        https://blog.naver.com/codeitofficial/222011693376
        • git checkout 브랜치 : checkout은 HEAD가 branch를 변경할때 주로 사용합니다.
          git checkout HEAD@{3} : checkout은 HEAD가 다른 commit을 가리키도록 할때도 사용합니다. 이때 detached branch가 발생할 수 있습니다.
          git reset HEAD@{3} : HEAD와 branch가 모두 다른 commit을 가리키도록 합니다.
      • reset vs rm : reset은 update를 취소하지만 rm 는 파일을 삭제했다는 정보를 보냅니다. 따라서 reset을 활용하면 remote의 파일이 지워지지 않지만, rm을 활용하면 '파일이 지워졌다'는 메시지를 보내게 됩니다.
        • git rm 파일 : rm 명령 이후 "git add" 를 실행해 'delete'신호를 보낸 것과 같습니다. local repository에서 삭제하므로 stage에만 올라가 있을 때 "git rm"를 하면 error가 발생합니다.
          (주의) 작업하고 있는 파일이 삭제될 수 있습니다.
          git rm -r cached 파일  : 파일을 local repository에서 삭제하되 현재 작업중인 것은 남겨둡니다. 
  • Staging Area --> Local Repository : commit 실수 시
    • 현재 HEAD가 실수로 변경되었으므로 HEAD^로 변경해주면 됩니다. 이때 원래 의도대로 commit만 취소하고 싶다면 "git reset --soft HEAD^"가 원래 의도라고 볼 수 있습니다.
    • git reset --hard HEAD^ : HEAD의 부모 commit으로 변경하고, 이후 변경 내용을 모두 삭제합니다 (주의)
      git reset --mixed HEAD^ (default) : HEAD의 부모 commit으로 변경하고, 이후 변경 내용은 그대로입니다.
      git reset --soft HEAD^ : HEAD의 부모 commit으로 변경하고, 이후 변경 내용은 그대로입니다. 단 stage에 올라간 상태로 유지 됩니다.
    • git revert f48441c33: commit을 취소하는 commit을 하는 것입니다. 따라서 git reset과 다르게 다시 이전으로 돌릴 수 있습니다.
      (주의) git reset --hard와 같으므로 working directory 파일 삭제가 될 수 있습니다. 다만, 이전에 작업하던 파일이 reflog에 기록되므로 다시 돌릴 수는 있습니다.
  • Local Repository --> Remote Repository : push 실수 시
    • git push origin branch -f : "git revert" 명령어를 사용하거나 변경된 commit을 만든 이후에 강제로 push 합니다.
    • 필자는 이 명령어를 사용하기 보다는 git revert는 github 페이지에서 손수 revert 해 준 다음, git pull을 통해 로컬 repository를 업데이트합니다.

c. local repository, remote repository 각각의 branch를 확인, 변경

 

 현재 HEAD가 가리키고 있는 local repository와, origin으로 저장해둔 remote repository의 브랜치가 있다고 할 때, 이들을 관리하기 위한 방법들을 살펴봅시다.

# Local 관리하기
git branch -v
git checkout BRANCH
git checkout -b BRANCH2

# 상황1. A,B,C,D,E라는 파일이 있을때 branch1에서 branch2로 checkout 하면?
# (각각 수정 & commit 후 checkout))
#EDIT!!!
git add --all
git commit -m "revised"
git checkout branch2

#상황2. A,B,C,D,E라는 파일이 있을때 branch1에서 branch2로 checkout 하면?
#(각각 commit & 수정 후 checkout)
git add --all
git commit -m "revised"
#EDIT!!!
git checkout branch2

# Remote 관리하기
git branch -r
git remote -v

# 모두 보기
git branch -a
git show-ref

Local 에서 branch를 확인하고 변경하는 방법은 아래와 같습니다.

  • Local 관리하기
    • git branch -v 혹은 git branch (확인): 현재 local branch 종류 확인하기 위한 명령어 
    • git branch 브랜치명 (변경) : 만들고 옮기지 않기
    • git branch -m 브랜치명 (변경) : 만들고 기존 것을 삭제하고 move
    • git checkout 브랜치명 (변경) : 이미 있는 곳에 옮기기
      • "git checkout 브랜치명 ."혹은 "git checkout . " : 해당 브랜치로 모든 파일을 checkout
      • "git checkout 브랜치명 파일명"혹은 "git checkout 파일명 " : 해당 브랜치로  파일을 checkout
    • git checkout -b 브랜치명 (변경): 만들고 기존 것은 두고 move
    • git branch -d 브랜치명 (삭제) : 현재가 아닌 branch를 삭제할 수 있다.
      • 해당 branch에 commit이 새로 되어있는 경우 삭제가 힘들 수 있습니다.

Local 에서 checkout으로 변경할 때의 주의사항을 알기위해 아래 두가지 상황을 살펴보도록 합니다.

첫번째 상황은 checkout하기 전 모든 수정을 마치고 commit을 한 상황이고,

두번째 상황은 commit을 하지 않고 checkout을 하는 경우입니다.

  • Local 관리하기 - 상황1. A,B,C,D,E라는 파일이 있을때 branch1에서 branch2로 checkout 하면?
    (각각 수정 & commit 후 checkout)
    • 결론 : commit을 하고 나면 checkout은 원하던 그대로 옮겨 갈 수 있습니다.
Branch1 A B(revised) C(revisedA) D X
Branch2 A(revised) B C(revisedB) X E
Branch3 Checkout A(revised) B C(revisedB) X E

 


  • Local 관리하기 - 상황2. A,B,C,D,E라는 파일이 있을때 branch1에서 branch2로 checkout 하면?
    (각각 commit & 수정 후 checkout)
    • ERROR : error : Your local changes to the following files would be overwritten by checkout :
      • A를 지우고 branch 2로 checkout 한 후에 branch 1로 다시 돌아오면 변경사항은 모두 사라져 있다.
      • 위 경우에 A를 남기고 branch 2로 변경하고 싶다면 commit을 한 뒤에 옮기면 된다.
    • 결론 : 두 branch상의 같은 파일을 commit하지 않고 checkout 하면 error가 발생합니다.
Branch1 A(revsied) B(added) C(added) D(deleted) D(rename2E)
Branch2 A' B' X X D'
Branch3 Checkout ERROR ERROR C X E, D'

  • Remote 관리하기
    • git branch -r 혹은 git branch --remote (확인) : remote repository(remote repository 이름, branch)의 상태를 확인하기 위한 명령어 
    • git remote -v (확인) : remote repository(주소 및 remote repository 이름)의 상태를 확인하기 위한 명령어 
    • git push origin --delete 원격브랜치명 (삭제) : 원격 branch를 삭제할 수 있다.

  • Local과 Remote 모두 관리하기
    • git branch -a : local(브랜치이름)과 remote repository(remotes/origin/브랜치이름)를 보여줍니다.
    • git show-ref : local(refs/heads/브랜치이름)과 remote repository(refs/remotes/origin/브랜치이름)을 보여줍니다.

d. local repository와 remote repository 사이에서 정보 관리하기

 

현재 origin으로 등록된 A라는 branch에서 git pull을 하는 경우, 내가 현재 locla repository에서 작업하고 있는 파일 중 어떤 것이 업데이트 될지 확인하는 것도 중요합니다.

상황1 : A,B,C,D,E라는 파일이 있을때 pull을 하면?
git checkout -b tk
git pull origin tk

#상황2 : A,B,C,D,E라는 파일이 있을때 push을 하면?
git push origin HEAD:tk

push와 pull의 error 케이스를 분석하기 위해 아래 두가지 상황을 살펴보도록 합니다.

첫번째는 pull을 하는 여러가지 경우이고

두번째는 push를 하는 여러가지 경우입니다.

  • 상황1 : A,B,C,D,E,F,G,H,I라는 파일이 있을때 pull을 하면? 
    • git pull origin tk : 앞서 push 할 때 -u 명령어를 사용하지 않았으므로 origin이라는 remote repository의 tk 라는 branch를 pull 하도록 직접 명시합니다.
    • ERROR : Your local changes to the following files would be overwritten by merge 혹은
      Automatic merge failed; fix conflicts and then commit the result (파일에 표식 생김)
      • C(revB)의 내용을 C(revA)와 똑같이 만들면 B케이스와 같이 진행 가능
      • C를 삭제하면 D와 같이 
    • 결론 : remote에 변화가 발생하면, local에 같이 변화가 생기지 않는 한 remote에 따르고, 
    • 결론 : local에 변화가 발생하면, remote에 같이 변화가 생기지 않는 한 local은 그대로다.
    • 결론 : remote와 local에 동시에 변화가 발생하면, remote의 변화가 우선이거나 error가 난다.
Remote Repository A B(rev) C(revA) D X(del) F(add) X H(add) X(del)
Working Directory A(rev) B C(revB) X(del) E X G(add) X(del) I(add)
Working Directory Pull A(rev) B(rev) ERROR X X F G H X

  • 상황2 : A,B,C,D,E,F,G라는 파일이 있을때 push를 하면?
    • git push origin HEAD:tk : origin 이라는 remote repository의 tk라는 branch로 현재 local repository에서 HEAD가 가리키고 있는 commit을 push 한다
      • "git push origin tk" 를 한 경우 "error : src refspec tk does not match any"라고 뜬다면 현재 remote repository에 tk라는 브랜치에 HEAD commit을 push 하는 것이 아닌, remote repository에 존재하는 tk라는 branch를 push 하게 된다.
    • ERROR : ![rejected] HEAD -> tk (fetch first) : remote repository에 D를 가지고 있으므로 
      • B,C는 pull 하고 다시 실행하면 가능 (단,C는 상황1의 문제 해결 필요)
      • D,E파일은 같은 파일을 다시 만들어도 안된다!
    • 결론 : remote 에 변화가 발생한 경우 "git pull"을 하지 않고 push를 하면 에러가 발생한다.
Remote Repository A B(rev) C(revA) D X(del) F(add) X
Working Directory A(rev) B C(revB) X(del) E X G(add)
Working Directory Push A(rev) ERROR ERROR X ERROR ERROR G

 

결론적으로 local과 remote의 차이가 발생하는 경우, pull을하고 push를 해서 해결되는 경우도 있지만, 절대해결되지 않는 경우도 있습니다.

따라서 가장 좋은방법은 협업에 있어 동료들과 같은 파일을 작업하지 않는 것이 좋습니다.


728x90
반응형