Important

  • 리베이스와 병합의 차이
  • git rebase
  • 리베이스를 쓰지 말아야 할 상황

 

* 리베이스? 병합?

협업 과정에서 각자 피처 브랜치를 이용해 작업하는것을 이제는 안다. 여기서 내 작업이 끝나기전에 누군가의 작업물이 메인 브랜치에 올라간 경우 변경사항을 최신으로 유지하기 위해서 나의 피처 브랜치에 pull을 해야 할 것이다. 이 때 병합 커밋이 필연적으로 발생할텐데 이런 경우가 아주 빈번하게 발생한다면 나의 작업과는 관계없는 병합커밋이 많이 생기게 되어서 이력이 지저분해 질 것이다.

 

병합(위), 리베이스(아래)

병합 대신 리베이스를 사용하면 피처 브랜치의 베이스를 새로 설정해서 일직선으로 만들어준다.

피처 브랜치의 커밋들을 그대로 다시 생성하면서 메인 브랜치의 끝 지점에서부터 배치한다.

 

병합 커밋이 사라지면서 이력이 깔끔해졌다

내가 작업하지 않은 병합 커밋들을 삭제하고 일직선으로 재배치 함으로써 이력이 보기 쉽게 되었다.

참고로 feat 브랜치의 커밋들은 재배치 되면서 커밋 해시가 변경된다.

 

 

* 리베이스를 하지 말아야 하는 경우

리베이스를 실행하는 경우 기존 커밋들이 재배치 되면서 커밋 해시가 변경된다고 했다.

그렇기 때문에 다른 개발자들이 이미 가지고 간 커밋을 리베이스하면 안된다.

피처 브랜치를 메인 브랜치로 푸시하고 병합히 된 후 누군가 메인 브랜치를 가져가서 해당 이력을 가지게 되었고 작업을 한다면 절대로 리베이스를 사용하면 안된다.

 

한줄로 정리할 수 있다. "이미 공유한 커밋을 리베이스 하면 안된다."

 

 

* 리베이스와 충돌

리베이스 도중 충돌이 발생한 경우 취소하고 이전으로 돌아가거나 충돌을 처리하는 방법이 있다.

충돌을 처리하는 방법은 병합과 비슷하다.

 

$ git add/rm <conflicted_files>
$ git rebase --continue

 

충돌이 발생한 파일을 처리한 후 스테이지에 올리고 리베이스를 계속 진행하면 된다.

 

 

리베이스는 유용한 기능이지만 사용하지 말아야 할 때를 정확히 알고 있어야 한다.

Critical

  • 중앙 집중형(단일 브랜치) 작업의 문제
  • 피처 브랜치의 워크플로우
  • 풀 리퀘스트(Pull Requests, PR)
  • 포크(Forks)
  • 포크와 클론의 워크플로우

 

Important

  • 브랜치 보호 규칙

 

중앙 집중형 워크 플로우 에서는 모든 사람이 단일(주로 main) 브랜치에서 작업한다.

원격 및 로컬 저장소에서 단일 브랜치를 사용한다면 협업에 큰 문제가 생길 수 있다.

 

예를 들어서 미완성된 작업물에 대해 도움을 받기 위해 공유를 위해서 푸시를 하게 되면 브랜치가 하나밖에 존재하지 않기 때문에 미완성된 작업물 그대로 브랜치에 커밋이 되어버린다. 이렇게 되면 모두가 미완성된 작업물을 가지게 되므로 아주 큰 문제가 발생한다.

 

브랜치가 단일로 존재하기 때문에 어느 누구도 기본 코드를 건드리지 않고서는 작업할 수가 없다.

게다가 커밋 충돌이 너무 자주 일어나기 때문에 시간의 낭비도 심해진다.

 


 

위와 같은 문제는 피처 브랜치를 사용함으로써 해결할 수 있다. 각자의 작업을 종류나 중요도에 관계 없이 개별 브랜치에서 수행하면 된다.

 

미완성된 작업을 푸시하더라도 브랜치가 다르기때문에 main 브랜치에 영향을 주지 않고 해당 작업을 받아서 협업하는 쪽도 페칭을 하기 때문에 main 브랜치나 자신이 작업중인 브랜치에 전혀 영향을 미치지 않는다.

 

 

일반적으로 main 브랜치는 언제나 배포가능한 상태로 유지한다.

 

 

* 풀 리퀘스트(Pull Requests, PR)

피처 브랜치에서 작업한 내용은 언젠가는 누군가에 의해 main 브랜치로 병합이 될 것이다.

다만 여기서 누구나 main 브랜치에 푸시 및 병합을 하게 된다면 문제가 발생하기 때문에 작업물 검토를 요청해서 병합을 승인하거나 반려해달라고 할 수 있다.

 

movie 브랜치의 병합을 요청한다

브랜치를 병합할 때, 빨리 감기 또는 충돌에 의한 병합 커밋이 만들어 지는것을 알고있다.

PR 역시 병합이기 때문에 충돌 발생시 처리를 해주어야 한다.

파일이나 수정사항이 몇개 되지 않는다면 깃허브의 웹상에서 처리할 수 있지만 작업량이 많다면 깃허브에서 처리하는것은 불편하므로 로컬에서 처리하는것이 편할것이다.

 

기본적인 처리 과정은 로컬에서 빨리 감기 여부와 관계 없이 병합 커밋을 만들어서 main 브랜치에 푸시하는 것이다.

 

추가로 PR은 깃의 기능이 아니라 깃허브같은 원격 저장소의 기능이다.

 

* 브랜치 보호 규칙 설정

main 브랜치같이 엄격하게 관리되어야 하는 브랜치에 규칙을 설정하여 엄격하게 관리할 수 있다.

 

 

* Fork란?

다른 원격 저장소를 나의 원격 저장소로 복사한다.

클론이 원격 저장소를 나의 로컬 저장소에 복사하는 것이라면 포크는 원격 저장소를 나의 원격 저장소에 복사하는 것이다. 둘의 차이는 클론은 공동 작업자로 등록되지 않는이상 푸시 권한이 없다는 것이지만 포크는 나의 원격 저장소이기 때문에 권한이 있다.

 

마치 로컬-원격의 관계처럼 원격-원격 관계가 성립한다. 정확히는 로컬-원격-원격일 것이다.

그렇기 때문에 포크해온 나의 원격 저장소에 변경된 작업을 푸시하고 해당 브랜치를 원래 프로젝트에 PR할 수도 있다.

 

기본적으로 원격 저장소에 참여해서 푸시할 권한을 가지려면 공동 작업자로 등록하는 절차가 반드시 필요하다.

하지만 규모가 매우 큰 오픈 프로젝트의 경우라면 매번 공동 작업자로 등록하는 절차가 번거로울것이다. 이 때 원격 저장소를 포크해서 변경사항을 PR한다면 기여자로써 등록이 된다.

 

Fork 역시 PR과 마찬가지로 깃의 기능이 아니다.

Critical

  • 공개 저장소와 비공개 저장소
  • 공동 작업자 추가하기

 

Important

  • README.md

 

Nice To Have

  • 마크다운
  • Github Gists, Github Pages

 

 

공개 저장소는 누구나 접근이 가능하다. 단, 저장소의 내용을 변경할 수 있는것은 아니다.

비공개 저장소는 공동 작업자로 추가되어야만 접근할 수 있다.

 

공동 작업자로 추가된 사람은 변경 사항을 저장소에 푸시할 수 있게된다.

 

 

* README.md

프로젝트가 무슨 일을 하는지 설명하기 위한 마크다운 파일이다.

저장소의 대문이라고 볼 수 있다

루트 폴더에 추가해두면 깃허브에서 내용을 렌더링해서 페이지에 보여준다.

 

마크다운이란?

형식화된 텍스트를 만드는 간결하고 편리한 구문이다.

텍스트를 HTML로 변환시켜준다.

 

구문을 매우 간결하고 쉽게 작성할 수 있다는 장점이 있다.

 

https://markdown-it.github.io 

이곳에서 데모를 작성해 볼 수 있다.

 

 

* Github Gists

코드 조각을 간단하게 공유하는 방법이다. 저장소와 마찬가지로 공개, 비공개 여부와 공동 작업자를 설정할 수 있다.

 

 

* Github Pages

클라이언트의 정적 페이지를 호스팅해준다. 때문에 파이썬, Ruby같은 서버 측 언어가 없다. 오직 HTML, CSS 등의 언어만 사용이 가능하다.

유저 사이트와 프로젝트 사이트 두 종류가 있다. 유저 사이트는 깃허브 계정당 한개로 제한되고 프로젝트 사이트는 깃허브 저장소마다 가질 수 있다.

index.html을 추가하면 원하는 정적 페이지를 만들 수 있다.

Critical

  • 원격 추적 브랜치
  • git fetch/pull

 

협업을 하지 않고 로컬 저장소만 사용할 거라면 fetch, pull은 필요없다. 하지만 협업할 계획이라면 매우 중요하다.

 

 

* 원격 추적 브랜치

로컬에서의 브랜치는 앞서 다뤄봤으므로 익숙할 것이다. 하지만 clone이나 remote를 통해 원격 저장소와 동기화를 이루고 나면 "origin/main"과 같은 새로운 브랜치가 생성된다. 이 브랜치가 원격 추적 브랜치이다.

마지막으로 원격 저장소와 통신한 시점을 기억하는 포인터라고 생각하면 된다.

 

# 원격 브랜치 목록 확인
$ git branch -r
> origin/HEAD -> origin/main
> origin/main

 

추가로 커밋 후 git status 실행 시, 로컬에서만 작업했을 때와는 다른 메시지가 출력된다.

 

$ git status
> On branch main
> Your branch is ahead of 'origin/main' by 1 commit.

 

"origin/main" 브랜치에 비해서 커밋 1개가 앞서있다는 얘기이다.

앞서 말했듯이 원격 저장소와 로컬 저장소는 자동으로 동기화되지 않기 때문에 한쪽에서 커밋하는 순간 어긋나기 때문에 그것을 알려주는 것이다.

 

로컬에는 없지만 원격에는 존재하는 브랜치에 접근하려면 어떻게 해야할까?

매우 간단하다. switch 명령어를 사용하면 된다.

로컬에는 없지만 원격에 존재하는 경우
로컬과 원격 모두 없는 경우

기본적으로 로컬 브랜치와 원격 브랜치의 이름이 같으면 서로 묶인다.

 

# 브랜치 추적 설정
$ git branch -u <remote-branch>
# 브랜치 추적 해제
$ git branch --unset-upstream <remote-branch>

 

만약 자동으로 브랜치를 추적하지 않는다면 수동으로 설정해주면 된다. 추적할 필요가 없다면 해제 할 수도 있다.

두 명령어 모두 대상 브랜치로 이동해서 실행해야 한다.

 

로컬과 원격의 워크 플로우

 

* Fetching

내가 로컬에서 작업하는 동안 다른 인원들이 작업한 것들이 원격 저장소에 올라간 경우가 있을 수 있다. 협업 과정에서는 매우 흔한 일일 것이다.

이 때, 내 작업 영역을 망치지 않으면서 변경 사항에 접근할 수 있도록 하는 명령어가 fetch이다.

 

origin/master가 업데이트 됐다. master와 별개의 브랜치이다

원격 저장소에서 변경 사항을 가져오지만 작업 파일에 통합하지는 않는다.

 

origin/food가 변경되었을 뿐 로컬에는 영향이 없다

 

 

* Pulling

Fetching과 다르게 HEAD 브랜치를 업데이트 한다. 작업 영역이 업데이트 된다는 얘기이다.

간단하게 정리하자면 fetch+merge 이다.

매우 중요한 점이 있는데, 현재 HEAD가 위치한 브랜치에 풀링이 적용 된다.

 

$ git pull origin movies

 

원격 저장소의 movies 브랜치를 풀링한다는 의미이다. 의도하는 것은 로컬 저장소의 movies 브랜치에 풀링하는 것인데 만약 HEAD가 main이라면 main 브랜치에 풀링이 되어버리므로 조심해야한다.

 

fetch와 다르게 origin/movies와 movies브랜치가 병합되었다. 위와 같은 경우는 충돌이 발생하지 않았으므로 빨리 감기 병합이 이뤄졌다.

 

로컬 브랜치와 원격 브랜치가 추적되도록 설정되었다면 명령어를 간소화 시킬 수 있다.

 

$ git pull

 

main 브랜치에서 실행했다면 원격의 main 브랜치를 풀링한다.

 

추가로 원격 저장소에 푸시하기 전에 풀다운 하여 변경 사항을 확인하는 습관을 들이는것이 좋다.

Critical

  • 깃허브가 하는 일
  • git clone
  • 깃허브 등록 및 SSH키 설정
  • 깃허브 저장소 만들기
  • 원격 작업
  • git push

 

 

* Github란?

깃 저장소를 위한 호스팅 플랫폼이다.

깃 허브에 등록한 저장소가 포트폴리오가 될 수 있다.

최신 정보를 얻기에도 매우 유용하다.

 

 

git clone <url>

로컬에 없는 저장소를 가져온다.

깃허브의 공개 저장소에 있는 것들은 모두 클론이 가능하다. 단, 푸시는 불가능하다.

그리고 거의 대부분의 경우에 깃허브가 쓰일 뿐이지 깃허브만 가능한것이 아니다. git clone 명령어도 github와 관계가 없다.

 

* SSH키 설정 및 깃허브에 등록하기 (Windows 기준)

깃허브 가입이 완료된다면 SSH키를 설정해야한다. 그래야 매번 로그인을 하지 않고도 인증할 수 있기 때문이다.

SSH키가 있으면 그걸 사용하면 되고 없다면 터미널 명령어로 생성시켜 주어야 한다.

 

$ ssh-keygen -t ed25519 -C <email>
> Generating public/private ALGORITHM key pair.

 

 

암호 설정까지 끝내고 생성을 완료했다면 SSH 에이전트를 실행시켜서 키를 추가시켜 준다.

 

$ eval "$(ssh-agent -s)"
> Agent pid ****

$ ssh-add ~/.ssh/id_ed25519
> Identity added: /c/User/../.ssh/id_ed25519 (email)

 

 

깃허브에서 SSH키를 등록하기 위해 공개키를 복사 한다.

 

$ clip < ~/.ssh/id_ed25519.pub

 

 

깃허브의 Setting - [Access] 의 SSH and GPG keys 페이지로 이동해서 "New SSH key" 를 클릭해서 복사한 키를 입력하고 제목을 정해주면 완료된다.

 

과정이 번거롭지만 디바이스별로 딱 한번만 등록해두면 다시는 할 필요가 없다.

 

* 로컬에 기존 저장소가 있는 경우

  1. 깃허브에서 새 저장소를 만든다.
  2. 로컬 저장소에서 원격을 만든다.
  3. 푸시한다.

 

* 아직 작업을 시작하지 않은 경우

  1. 깃허브에서 새 저장소를 만든다.
  2. 클론한다.
  3. 푸시한다.

 

git remote

현재 저장소에 있는 원격 목록을 보여준다

git remote -v : 저장소의 주소까지 출력해준다.

 

git remote add <remote> <url>

새로운 원격을 만들어준다. url은 깃허브의 저장소 주소이다.

 

일반적으로 원격 이름은 origin으로 만든다. 원격은 여러개 존재할 수 있다.

 

아래와 같은 명령어도 사용된다.

git remote rename <old> <new> : 원격 이름을 수정한다.

git remote remove <name> : 원격을 삭제한다.

 

remote는 깃에게 알려줄 url에 이름을 붙인것이다.

 

git push <remote> <branch>

깃에게 대상 저장소 및 푸시할 브랜치를 알려준다.

git push origin main 은 정말 흔하게 사용하는 명령어이다.

 

 

저장소를 만든 직후 아무것도 없는 상태

원격 유무를 포함한 정보를 확인한 후에 push를 실행하면 아무것도 없던 저장소에 파일들이 추가된다.

로컬에서 작업하던것을 깃허브에 올린것이기 때문에 로컬에서 작업했던 브랜치의 이력들이 모두 올라간다.

현재는 main 브랜치만 푸시했기 때문에 main 브랜치만 존재한다.

 

 

git push <remote> <local-branch>:<remote-branch>

기본적인 푸시는 로컬 브랜치가 원격 저장소에 존재하지 않으면 새로 생성하고 그 이후에는 같은 브랜치에 푸시되는 방식이다. 즉, 로컬 A브랜치를 원격 저장소의 A브랜치로 푸시하는 것이다.

여기서 로컬 브랜치와 원격 저장소의 브랜치를 다르게 설정할 수 있다. 로컬 A브랜치를 원격 저장소의 B브랜치로 푸시할 수 있다는 것이다.

일반적으로 자주 사용되는 명령어는 아니다.

 

 

git push -u <remote> <branch>

upstream의 약어이다. 로컬 브랜치를 원격 브랜치에 연결시킨다.

 

# local의 main을 origin의 main에 연결한다
$ git push -u origin main

 

연결시키고 난 뒤에는 git push 명령어 만으로도 푸시가 가능해진다.

보통은 main 브랜치를 처음 푸시할 때 사용한다.

 

로컬 브랜치와 깃허브의 브랜치는 다르다는 것을 인지해야한다.

 

 

깃허브는 로컬 저장소와 자동으로 동기화되지 않는다.

+ Recent posts