얼레벌레

[git] git 사용법 본문

취업/KT AIVLE SCHOOL

[git] git 사용법

__Lucie__ 2022. 8. 3. 21:39

7/26에 오프닝데이를 맞이하고

7/27에 2일차로 git에 대해 배웠다.

엄청 대단한 강사분이 오셨다. 개발자라면 누구나 알 만한..?

 

버전 만들기

git을 사용하려면 초기 셋팅을 해주어야 한다. 

(처음 사용 할 때 커맨드 창에서 "git"이라고 입력하면 모든 명령어를 볼 수 있다.)

  1. git init 
    • git에게 현재 디렉토리를 버전 관리하라고 명령하는 명령어이다.
    • git init을 실행하면 .git 파일이 생기는데, 이 파일은 git의 저장소이자, git으로 추적 및 관리하는 대상이다.
    • 즉, .git 파일은 우리가 github에서 프로젝트를 로컬로 클론해오고, git add, git commit 등등 작업을 하면서 변경사항을 만드는데, 바로 이런 변경 사항을 .git 폴더에 저장해서, 우리가 commit을 하기 전으로 돌아갈 수도 있고.. 등등 관리를 해주는 폴더이다.
  2. git add (파일이름)
    • git add 명령어는 파일을 staging area에 등록한다.
    • staging area란, 직후의 커밋에 어떤 것들이 포함될 지 알려주는 정보를 가지고 있는 파일을 말한다.
  3. git commit -m "커밋메시지"
    • 버전을 제출하는 것이다. (제출이 영어로 commit)
    • 그런데 커밋할 때마다 사용자 설정을 해야 한다. 
      하지만 버전을 만들 때마다(커밋할 때마다) 사용자 설정하기는 힘들다..
      그래서 딱 한번 설정해주면 된다.
    • commit 의 옵션들
      • -a
        auto-adding 옵션, 커밋할 때마다 add하는거 귀찮으니깐 한번에
      • -m 
        커밋메시지 옵션
    • 주의: untracked 파일은 auto adding이 안된다.
      이유는, git에서 절대로 commit 하면 안되는 파일들이 있다. (예: config.txt와 같은 id, pw 정보 담긴 파일 )
      그래서 git은 add를 통해 tracked 상태로 바꿔준 파일에 대해서만 -a 옵션이 작동한다.
git config user.name 'lucie' #이 프로젝트에서만 유효

# 최초로 버전 만들 때 다음과 같이 설정하면 다음 commit 시에 사용자 설정을 안해도 됨
git config --global user.name 'lucie' #전역적으로 유효
git config --global user.email 'lucie@a.com'

+ 버전 확인하는 명령어: git log

 

명령어 정리

git status 

  • 현재 상태를 보여주는 명령어이다. (커밋하지 않은 파일이 있는지 확인)

위 사진에서 Changes not staged for commit에 있는 파일은 한번 이상 커밋을 이전에 한 파일이고,

Untracked files에 있는 파일은 한 번도 add한 적이 없는 파일이다.

 

git log

  • 버전을 확인하는 명령어이다.

이전 버전과 현재 어떤 버전에 있는지를 알 수 있다.

git log --oneline 으로 --oneline 옵션을 걸어주면 한 줄로 볼 수 있다.

 

git의 원리?

강사님께서 git은 원리를 알면 쉬워진다고 하셨다.

  • 우리가 프로젝트에서 git init 을 하면 프로젝트 안에 .git 파일이 생긴다.
  • 바로 이 .git 파일이 repository(레포지토리, 저장소)이다.
  • 그리고 우리가 작업하는 폴더는 working directory(폴더) 이다.
  • repository는 machine의 영역, working directory는 우리가 작업하는 human의 영역이라서,
    .git 파일을 숨겨 놓은 것이다. (사람들이 실수하지 않게끔)

이걸 그림으로 그려보면 다음과 같다.

commit 할 때 git 내부에서 벌어지는 일

  1. git은 커밋할 때 이 버전의 부모가 누군지를 찾는다.
    그 때 마지막 커밋을 의미하는 HEAD를 참조해서 지금 만든 버전의 부모가 해당 커밋 id라는 것을 알아낸다.
  2. 그러고 나서 파일 이름, 컨텐츠, 등등을 합쳐서 조합해가지고 해시 값을 만든 고유한 id가 지금 버전의 커밋 id가 된다.
  3. 마지막으로, 이 새로 만든 커밋 id는 마스터의 새로운 값이 된다.

HEAD와 master branch (헷갈려서 내가 찾아본 내용)

모든 브랜치에는 HEAD 값이 존재하는데, HEAD란 해당 브랜치의 마지막 커밋을 뜻한다.

우리가 저장소를 처음 만들면, git은 바로 'master'라는 이름의 브랜치를 만든다.

이 새로운 저장소에 새로운 파일을 추가하거나, 내용을 변경해 저장(커밋)하는 것은 모두 'master'라는 이름의 브랜치를 통해 처리할 수 있는 일이 된다.

즉, checkout을 통해 새로운 브랜치를 만들지 않는이상, 이 때의 모든 작업은 master 브랜치에서 이루어 진다.

 

백업 - 시간여행

백업을 하는 이유가 뭘까?

'복원을 하기 위해서' 이다.

복원은 진짜 중요한 작업이다. (왜냐면 그 순간이 이 파일을 좌지우지 하기 때문에..)

git에 대한 이해가 잘 없는 상태로 시간여행을 시도하면 파일이 날라가는 경우가 생겨서 보통 무서워한다.

 

우리의 working directory를 돌려보자.

HEAD는 current commit (working dir에서의 커밋), master는 브랜치니까 last commit 을 가리킨다.

위와 같은 상황에서, working dir을 'C'로 되돌리고 싶다면,

  • git checkout (C의 커밋아이디)
    • checkout 은 HEAD를 옮긴다.

C로 체크아웃을 하면 다음과 같이 된다. (detached head state)

확인 할 때 git log 명령어를 사용했는데,

"git log --oneline" 이라고 하면 각 버전을 한 줄로 출력할 수 있고 (헤드까지만 출력됨),

"git log --oneline --all" 이라고 하면 헤드까지만 출력하는게 아니라, 마스터까지도 출력이 된다.

 

다시 D로 돌아가려면 어떻게 해야 할까?

  1. git checkout (D의 커밋아이디)
  2. git checkout master

둘 중 하나를 사용하면 된다.

하지만 1.의 방법을 사용한다면 HEAD가 master에 붙어서 D를 가리키는 형태가 아닌, 다이렉트로 D를 가리키게 된다.

 

checkout과 reset

위와 같은 상태에서 만약 B를 삭제하고 싶다면 어떻게 해야할까?

master-B 가 연결된 것을 끊고 A에 연결하면 되지 않을까?

결론부터 말하면, " git reset --hard (A의 커밋id) " 를 치면 된다.

  • checkout은 HEAD를 바꾼다.
  • reset 은
    • 만약 HEAD 가 attached 상태라면 master를 바꾼다.
    • dettached 상태라면 HEAD를 바꾼다.

따라서, git reset 명령어를 쓰면 된다.

하지만 git reset 명령어를 쓰더라도, git은 B를 물리적으로 지우지는 않는다.

단지, 아무도 B를 가리키지 않아서 삭제된 것 처럼 보일 뿐이다. - git의 불변성 (삭제인데도 이름이 reset인 이유)

B를 다시 살리고 싶다면? " git reset --hard (B의 커밋 id) " 를 치면 된다.

그래서, 위험한 작업을 할 때 (과거로 돌아가거나 등등..) 삭제하는 커밋 ID를 적어두자.

 

!! 하지만 다른사람에게 업로드한 버전은 절대로 reset 하지말자 !!

내 컴퓨터에 있는 것만 리셋 가능하도록 하자.

(협업의 맥락에서는 reset은 신중해야 한다. 가급적 사용 권장하지 않음)

 

=> 활용 : 실험적인 작업을 할 때 유용하다.

  • git checkout (현재버전)
  • 새로 작업한 파일(실험 내용)들을 git commit
  • git checkout master

이렇게 하면 실험했던 것을 버리고 기존으로 돌아갈 수 있다.

 

ignore

위에서 commit 이야기 할 때 config 파일에 대해 잠깐 언급했다.

config 파일은 id, pw 정보 등이 담겨있는 파일로, 커밋하면 안되는 파일이다.

하지만 그런데도 불구하고 config 파일을 실수로 올려버린다면?? 

그리고 git status 찍을때마다 config 파일이 나오면 불안하다. 내가 커밋해버릴까봐..

=> 그래서 쟤를 git이 무시하게 만들자! 는게 ignore이다.

 

  • 디렉토리 안에 .gitignore 파일을 만든다.
  • 그리고 .gitignore 파일 안에 무시하고 싶은 파일 명을 적는다.

이렇게 하면 git status를 찍었을 때 git이 .gitignore 파일을 먼저 탐색한다.

그리고 나서 걔네를 무시한다.

 

Branch

'커밋에다가 이름 붙여주기'

 

Q. 병렬 작업을 하려면 어떻게 해야 할까?

" git checkout (커밋 id) " 를 통해 이동 이동 이동 해야 한다.

근데 이렇게 하려면 매번 커밋 id를 적어야 하니깐 귀찮다.

 

그러면 이름을 붙여주자!

그래서 " git checkout (커밋id) " 대신 " git checkout (내가 정한 이름) " 으로 이동할 수 있게끔.

 

  • git branch exp1
    exp1이라는 새로운 브랜치를 생성한다.
  • git checkout exp1
    HEAD가 exp1을 가리키게 만든다.
  • (working dir에서 작업을 하고나서 커밋을 한다)
    => master와 exp1의 버전이 달라짐
  • git checkout master
    HEAD를 master로 옮긴다.
  • git branch exp2
    master가 있는 위치에서 exp2를 시작한다.
  • git checkout exp2
    HEAD가 exp2를 가리키게 한다.

위의 모든 과정이 끝난 사진이다. (git graph 확장 프로그램 이용)

 

Merge

merge 하기

병합할 때는 HEAD가 master에 있어야 하고, 병합하고 싶은 브랜치를 찾아서

" git merge (브랜치이름) " 을 통해 병합한다.

 

*마스터가 exp를 병합하면 HEAD가 마스터에,

exp가 마스터를 병합하면 HEAD가 exp에 있어야 한다.

 

만약 master 브랜치에서 exp를 병합하면 부모가 2개가 된다.

예시: 병합하니까 새로운 버전의 부모가 두 개이고, 헤드가 새로운 마스터 버전을 가리키는 것을 알 수 있다.

merge 취소

  • git reset --hard (이전 master 커밋 id)

git graph 에서 merge 및 취소 방법

  • HEAD가 master에 있는 것을 확인한다.
  • 병합하고 싶은 브랜치를 우클릭
    'merge into current branch'
  • 취소하려면 돌아가고 싶은 곳을 찾아서 reset 누르기, 옵션은 hard로

 

충돌 (극혐)

git은 충돌을 다룰 때 공통의 조상을 찾는다.

공통의 조상은 base라고 한다.

3 way merge를 쓰니까 충돌이 3/4 -> 1/4로 줄어든다.

 

실습:

exp2 브랜치 생성하고, 마스터에서 common.txt 수정 (2->m2)
exp2 브랜치로 이동해 exp2에서 common.txt 수정( (3->e3)

 그 후 exp2에서 4->e4, master에서 4->m4 로 변경하고 커밋한 후

master에서 " git merge exp2 "

자동으로 1, m2, e3로 만들었다. m4와 e4는 충돌이라서 이렇게 나온다. 저 위에 있는 옵션들 중 하나를 선택하면 된다.

 

Github

대망의 github 파트이다.

원격 저장소를 빌려주는 곳이 github이다.

시작

  1. github에 가서 remote repository 생성
  2. git remote add origin https://github.com/ShineYeon/kt-ai-2.git
    원격 저장소에다가 올리려면 지역저장소가 원격저장소를 알아야 한다.
    그래서 github에 연결할 때 위의 명령어를 사용한다.
    뜻: git아 remote를 추가할게. origin이라는 이름으로. 주소는 뒤에 있는 주소란다.
  3. git push 
    원격 저장소로 push 한다.
    하지만 최초 push할 때는 '페어링'이 필요하다. 
    "git push --set-upstream origin master" => 원격의 origin과 로컬의 master를 페어링
    위의 명령어를 친 이후에는 그냥 git push만 하면 된다.

 

clone

지금까지 git 저장소를 만들 때는 로컬에서 git init 을 했는데, 

github에 있는 걸 복제해올 때는

" git clone https://github.com/ShineYeon/kt-ai-2.git "

 

reject

상황

  • 같은 레포지토리에서 A는 B1을 커밋하고, B는 B2를 커밋했다고 하자. (다른 파일 수정)
  • 그 상황에서 A가 git push 를 했다.
  • 이 때 B가 git push 하면 실패한다.
    다른 파일이기 때문에 conflict가 아니라 reject이다. (B한테 B1이 없기 때문)

=> 해결 방법

  • B가 git pull 로 가져온다.
    그러면 merge commit 이 된다. (git pull 시 자동으로 원격과 로컬이 merge 됨)
  • 그러고 나서 git push 

* reject는 충돌때문에 발생하는 것이 아니다.

충돌

그럼 만약 A와 B가 같은 파일을 수정했다면?

  • A가 common.txt 파일을 수정, B도 common.txt 파일을 수정
  • A가 먼저 git push
  • B가 git push -> rejected (conflict가 아님)
  • 그래서 B가 git pull
    이 때 충돌이 발생하는 것이다.
    그러면 옵션 선택하고 나서 git add common.txt 명령어를 입력,
    git commit 후 뜨는 새로운 창을 닫아준다
    => pull 완료
  • B가 git push 하면 끝

 

꿀팁들과 여러 노하우

  1. git graph 확장 패키지 설치하기
  2. 만약 과거 버전에서 버그가 났다면 checkout을 통해 버그가 난 시점으로 돌아가서 해당 파일만 커밋하면 고칠 수 있나?
    ㄴㄴ. 그 버전에서 버그를 확인하고 현재로 돌아와서 수정해야 최신 작업들을 잃어버리지 않는다.
  3. add 한거에서 빼려면
    git rm --cached (해당 파일 이름)
  4. HEAD와 master 라는 status(상태)가 있고, checkout과 reset이라는 행위가 있다.
    조합하면 여러 작업을 할 수 있는데, 행위에 집중하지말고 상태에 집중하자.
  5. git log를 통해 로그를 보고 맨 처음에 뭘 찾아야 할까?
    헤드.
    헤드를 찾아서 내 working dir이 거기라고 인지 => 내가 어떤 작업을 하고 있구나. 등등 이해할 수 있음.
  6. 단축 지정
    git log --oneline --all --graph => 너무 길어
    git config --global alias.l "log --oneline --all --graph" => l로 사용할 수 있다.
    " git l " 명령어 치면, " git log --oneline --all --graph " 친 결과를 볼 수 있다.
  7. 단축 삭제
    home directory에서 .gitconfig 파일을 열면 우리의 전역적인 설정이 들어가 있다.
    (이메일, 이름, alias 등등) 여기서 지우면 되는데, 방법은 찾아봐야함 
  8. add 3대 의미
    • working directory -> staged
    • untracked -> tracked
    • 충돌을 해결했다는 것을 알려주는 것