쉽게 정리하는 git 사용법

새 프로젝트 저장소에 올리고 내리고 할 때 아직도 한 번씩 버벅거려서 정리를 해 보기로 함.

이 프로젝트를 git 에서 사용하고 싶다

A라는 프로젝트를 새로 시작했다! 그럼 자바 프로젝트든, 요구사항 정의서든 어떤 내용물이 폴더에 담기게 된다. 이 폴더를 git에 올려서 버전 관리를 하기로 마음먹었다면, 가장 먼저 폴더 안에서 git init을 입력한다. 그러면 이제 git이 이 폴더 안에 있는 파일과 코드들을 추적하기 시작한다.

→ git init을 한 번 하고 나면 웹에 있는 리포지토리와 연결하지 않은 상태로도 commit이 가능하다. 이미 폴더의 .git 폴더(숨겨져 있음)안에 리포지토리가 존재하기 때문임.

작업 폴더에서 git add 한 파일들은 staging area에 스테이징 되고, 추가하지 않은 파일들은 아직 작업 폴더에 머물러 있다. 이 상태에서 git commit을 하면 staging area에 있던 파일들만 repository에 옮겨지는 것.


이 로컬저장소를 원격저장소(gitHub등)에 올리고 싶다

원격저장소는 미리 만들어둬야 한다. 그런 다음 작업 폴더에서 터미널을 켜서 아래를 입력한다.

1
2
git push -u 원격저장소 주소 main //로컬저장소의 main 브랜치를 원격저장소에 올리라는 뜻이다(다른 브랜치도 올릴 수 있음) 
// -u 옵션은 방금 입력한 주소를 기억해두라는 뜻이다. 다음부터는 git push만 입력해도 사용이 가능해진다. 

원격저장소 주소 기억해두기

1
git remote add 변수명 저장소주소 // git remote add origin https://~~.git 이런 식으로 사용

위와 같이 입력하면 리포지토리 주소가 필요할 때마다 origin이라는 변수명을 쓸 수 있다. 저 위의 명령어를 git push -u origin main 으로 써도 된다는 뜻이다. 변수 목록을 살펴보고 싶으면 git remote -v 를 입력하면 볼 수 있다.


원격저장소에 있던 거 그대로 내려받기

다른 환경에서 소스가 필요해지는 경우도 많다. 그럴 때는 원격 저장소에 올려둔 코드를 그대로 내려받아서 시작하면 편리하다.

1
git clone 원격저장소 주소 

이 코드가 전에 commit한 거랑 어떻게 다른지 알고 싶다

git diff를 입력하면 바로 전 커밋과 현재 코드의 차이점을 비교해서 보여줌. 하지만 코드가 길어지면 이걸로 전체를 비교해서 보는 건 힘들다. 이 기능의 가장 유용한 사용법은 과거의 특정 commit과 현재 파일을 비교해야 할 때. git diff 커밋id 를 입력하면 최근 커밋과 해당 커밋을 비교해준다. git diff 커밋id1 커밋id2 하면 과거의 특정 commit 두 개 간의 차이점 비교도 가능하다.

1
git log —oneline //참고 : 커밋 id 확인 

git diff는 알아보기 힘들다

git difftool을 입력하면 화면상에서 반씩 나눠서 차이점을 보여준다. diff와 마찬가지로 git difftool 커밋id를 하면 특정 commit과의 비교도 가능함. vim 에디터로 여는 거라서 이걸로 열면 :qa 입력해서 나가야 함.


브랜치란 쉽게 말해 프로젝트 복사다

프로젝트를 진행하면서 기존 소스를 많이 수정해야 하는 경우가 종종 있다. 무작정 소스를 수정했다가는 멀쩡히 잘 돌아가던 프로젝트가 이상한 곳에서 문제가 발생할지도 모른다. 아무래도 프로젝트의 복사본을 만들어서 거기에서 먼저 기능 추가를 해 보는 게 안전할 것 같다. 그럴 때 사용하는 게 브랜치다. 처음 브랜치 개념을 배울 때 가지가 뻗어 나가서 다른 버전이 생기고 어쩌고 하고 배웠더니 오히려 복잡하고 쉽게 사용하기 어려운 기능처럼 느껴졌는데, 프로젝트 복사하기 라고 생각하는 게 훨씬 편하다.

1
git branch 브랜치명

위 명령어를 입력하면 입력한 브랜치명으로 프로젝트 사본이 하나 생기는 거다. 이 브랜치로 이동하고 싶다면 아래처럼 사용한다.

1
2
3
git switch 브랜치명 //요즘은 이렇게 씀 
git checkout 브랜치명 //전에는 이렇게 썼는데 아직 쓸 수 있음 
git status // 내가 어느 브랜치에 있는지 모르겠다면 이걸로 확인 가능

메인 브랜치로 돌아가고 싶다면 위와 마찬가지로 git switch main(또는 설정에 따라 git switch master)를 해주면 된다.

이 브랜치 내용을 원본에도 적용 할래요

브랜치에서 커밋을 하든 푸시를 하든 리포지토리에는 저장되지만 메인 브랜치나 다른 브랜치에는 전혀 영향을 주지 않는다. 그럼 이 브랜치의 작업 내용을 메인 브랜치에 합치려면 어떻게 해야 할까? merge 기능을 이용하면 된다.

1
2
git switch main //main(또는 master) 브랜치로 이동해서
git merge 브랜치명 // 합칠 브랜치명을 입력하면 합쳐진다. 

메인 브랜치로 이동한 후, 거기에서 merge 해야 메인 브랜치에 합쳐진다는 점을 기억하자. 물론 브랜치01을 브랜치 02와 합칠 수도 있다. 이 경우에도 합칠 주체가 되는 브랜치에 있는 상태로 다른 브랜치를 merge 해야한다.

여기서 주의 사항이 있다! master브랜치와 합치려는 브랜치가 같은 파일의 같은 줄을 수정했을 경우 충돌conflict이 발생한다. 둘 중에 어느 걸 채택해야 할 지 누군가 결정해주어야 한다. 이 경우 해결 방법은 두 가지다.

  1. 일단 머지를 취소하고 코드를 살펴본다.

    1
     git merge --abort //abort : 버리다, 취소하다. 즉 머지 이전 상태로 돌려놓는다. 
    
  2. conflict 상태의 파일을 수정하고 다시 commit한다 .

    1
    2
    3
    4
    5
    6
     <<<<<<≤ HEAD 
     이 안의 내용이 HEAD가 가리키던(or MASTER 브랜치가 가리키던) 최신 커밋에서의 파일 내용 
     =======
     이 안의 내용이 
     특정 브랜치가 가리키던 최신 커밋에서의 파일 내용이다.  
     >>>>>>> 브랜치명 
    

    충돌 지점에서 어느 코드를 선택할 지 고르고 나머지를 다 지운 후 새로 commit하면 된다(혹은 전체를 다 지우고 다른 코드를 입력해도 된다).

merge 후에도 남아있는 브랜치가 거슬린다

merger 후에도 브랜치가 삭제되지는 않는다. 그래서 필요 없어진 브랜치를 보기 싫다면 수동으로 삭제해주어야 한다.

1
2
3
4
5
6
7
git branch -d 브랜치명 
git brancd -D 브랜치명 
// 둘 중에 아무거나 써서 브랜치 삭제가 가능함. 병합이 완료된 브랜치 삭제는 -d만 해도 가능하고, 
// 병합하지 않은 브랜치 삭제는 -D로만 가능

git push origin --delete 브랜치명 
// 로컬 말고, 원격 저장소에 생성된 브랜치도 로컬에서 지우려면 위와 같이 입력한다. 

브랜치의 시작점을 다른 커밋으로 옮기고 싶으면 rebase

새로운 브랜치의 시작점을 main 브랜치의 최근 커밋으로 옮겨서 바로 합치는 게 rebase다. git log가 너무 더러워지는 게 싫을 때 사용할 수 있다. 단, 브랜치 끼리 차이점이 너무 많은 경우 충돌이 여기저기서 발생할 수 있다(역시 일일이 수정해줘야 함). 이것도 역시 main 말고 다른 브랜치 끼리도 가능하다.

1
2
3
4
git switch 새로운 브랜치 
git rebase main // 이렇게 하면 main브랜치 밑에 새로운 브랜치가 생긴다. 
git switch main 
git merge 새로운 브랜치 // main으로 돌아가서 새 브랜치를 병합. 

새 브랜치의 코드 변경사항을 main으로 옮겨주는 squash

1
2
3
git switch main //메인 브랜치로 와서
git merge --squash 브랜치명 //브랜치에 squash 옵션을 추가해주고 
git commit -m "메세지" // 커밋하면 main으로 변경 내역만 옮겨줌 

rebase랑 squash는 왜 하는 걸까

  • 여러 사람과 협업을 하다보면 git log가 복잡해지게 되고, 그러다보면 히스토리를 파악하기 어려워지기도 한다. 협업을 위해 git log를 깔끔하게 보이도록 관리해주는 것. rebase는 merge commit이 남지 않아서 깔끔하고, squash는 revert 하기 쉽다는 장점이 있다.

커밋한 파일 하나를 이전 시점으로 되돌리려면

커밋 하면서 수정된 파일이 여러 개인데 특정 파일 하나만 이전 시점으로 돌리고 싶을 때에는 restore를 사용하면 된다.

1
2
3
git restore 파일명 //최근 커밋된 상태로 현재 파일의 수정내역을 돌린다. 
git restore --source 커밋아이디 파일명 //이 파일이 지정한 커밋아이디 시점으로 복구된다.
git restore --staged 파일명 // 이러면 특정 파일을 staging 취소 시킬 수 있다.

커밋 내역을 이전 시점으로 되돌리려면

과거의 커밋 하나가 문제가 생겼다는 걸 알게 됐다. 이 때 커밋했던 작업 전체가 다 문제가 되어서 커밋 자체를 취소하고 싶을 수 있다. 그럴 때는 revert를 사용하면 된다. 엄밀히 말하면 커밋 자체를 취소하는 건 아니고(없던 일이 되지는 않는다는 뜻), 취소하는 commit 하나를 새로 생성해준다.

1
git revert 커밋아이디

이렇게 하면 그 커밋아이디에서 일어난 일들만 취소해준다. 에디터로 커밋 메시지를 새로 입력하라는 창이 뜰 텐데, 대충 원하는 내용을 입력하고 끄면 된다. 그 커밋 이후에 변경된 파일이나 커밋들은 영향 없이 유지된다. merge명령으로 만들어진 커밋도 revert가 가능하다. 그러면 merge가 취소된다.


그냥 다 시간을 돌리고 싶어

이미 너무 많은 게 잘못돼서 그냥 특정 지점까지 리셋하고 거기서부터 새로 하는 게 더 나을 것 같은 순간이 (안 오면 좋겠지만) 생길 수 있다. 그럴때는 reset을 사용한다.

1
2
3
git reset --hard 커밋아이디 //커밋아이디 이후의 미래 내역이 모두 날아간다.
git reset --soft 커밋아이디 //커밋아이디 이후의 내역들이 staging area로 돌아간다. 이 내용을 살펴보고 커밋할 수 있음. 
git reset --mixed 커밋아이디 //커밋아이디 이후의 내역들이 unstaging 상태로 돌아간다. 이 내용을 살펴보고 git add할 수 있음.

습관적으로 쓰는 git pull, 구체적으로 뭘 해주는 걸까

회사에서 git 쓸 때 기계적으로 commit → pull → push 하면서도 pull의 원리를 생각해 보지 않은 것 같다. 그냥 최신 소스를 내려받는 건가보다 정도의 생각이었다. git pull은 git fetch + git merge 를 해 주는 명령어라고 한다. git fetch는 원격 저장소에 있는 commit 중에 로컬에 없는 신규 commit을 가져오라는 뜻이고, git merge는 그걸 합치라는 뜻이다.

그래서 git pull 할 때 여러 명이 같은 파일의 같은 라인을 건드리는 중이라면 충돌이 날 수 있다. 소스 까서 어떤 걸 남길 지 확인하고 침착하게 다시 커밋 하면 된다.