본문 바로가기

Programming/Git

Git Branch 다루기 (생성, merge, conflict, remote repository)

branch란, 하나의 코드 관리 흐름을 말한다.

 

git status를 하면

 

"On branch main" : main branch 위에 있다는 말인데, main 브랜치는 레포지토리를 만들고 커밋을 하면 자동으로 생기는 브랜치이다.

 

 

다른 브랜치를 만들어보자

유료버전 개발을 위한 premium 브랜치를 만들어보자

"git branch premium" 으로 premium 브랜치를 만들어주고 

"git checkout premium" 으로 해당 브랜치로 이동해준다.

이제 작업을 하면 premium 브랜치에만 반영된다. 이제 내 커밋들은 main 브랜치와는 다른 작업이 된다. 

 

License 파일을 바꾸고 커밋해보자

premium 브랜치 위에있을때 License 파일을 출력해보면

PREMIUM이라고 뜬다.

 

main 브랜치로 가서 다시 출력해보자

 

main 브랜치

 

premium브랜치

 

 

 

 

branch 관련 명령어들

git branch : 모든 브랜치 조회

git brach -d (브랜치명) : 브랜치 삭제

git checkout -b (브랜치명) : 브랜치를 만들고 나서 바로 이동하기

 

 

 

 

 

branch merge 하기

무료버전을 main, 유료버전을 premium 브랜치에서 작업할때

무료버전에 메소드를 여러개 추가한 뒤에 반영해주었다.

근데 회사에서 "무료버전에 있는 기능은 유료 버전에도 다 있어야 한다" 라는 지침이 내려왔을 때

 

Premium 브랜치에 가서 확인해보자

 

직접 추가하기보다는 그 브랜치를 직접 가져와서 합치는 merge 하면 된다!!

 

"git merge main " - 현재 위치인 premium 브랜치에 main 브랜치를 합치겠다!

실행해보면 아래와 같은 창이 생긴다.

merge를 하면 merge commit이라는 새로운 commit이 생기기에 commit 메시지 창이 생긴 것이다.

해당 내용도 바뀌어 있다.

 

 

 

 

 

merge 할 때 Conflict가 난다면?

premium 브랜치에서 classification.py 파일을 변경해보자

커밋후에 main 브랜치로 돌아와서

magazine을 magazine_free 함수로 바꿔주고 커밋해보자

 

똑같은 magazine함수를 서로 다른 브랜치에서 서로 다른 이름으로 바꾸었다.

 

이 상태에서 premium branch로 가서 merge 한다면?

경고메시지가 뜬다!!

merge를 하다가 conflict가 생겼다는 뜻! 즉, 충돌이 생겼다.

 

classification.py를 열어보면

가운데 선을 기준으로 각 브랜치에서 어떻게 설정했는지 보여준다. 

git은 둘 중 뭘 반영해야하는지 헷갈려하는 것이다.

 

해결법: 새로 정의하고 닫은 뒤에 

git add .

git commit 하면 커밋 메시지를 쓸 수 있는 창이  뜬다.

conflict를 해결하고 커밋하면 커밋메시지가 자동으로 설정된다.

git history로 확인해보면

Merge 커밋도 잘 해결되어 있다.

즉, Conflict 해결법 :

1. 컨플릭트가 발생한 파일을 연다.

2. 머지의 결과가 되었으면 하는 모습대로 코드를 수정한 뒤, 커밋한다.

     2-1)(혹은 merge자체를 취소해도된다) git merge --abort

 

 

 

 

 

Conflict가 여러 파일에서 나면?

원리는 파일 하나일 때와 같다.

git status 로 확인하면 Unmerged path에 해당 파일들이 속하고, 그 이유가 "both modified"로 남아있는 것을 확인할 수 있다.

 

하나하나 열어서 
똑같이

1. 컨플릭트가 발생한 파일을 연다.

2. 머지의 결과가 되었으면 하는 모습대로 코드를 수정한 뒤, 커밋한다.

     2-1)(혹은 merge자체를 취소해도된다) git merge --abort

과정을 반복하면 된다. 

 

 

 

 

 

Remote Repository에 대하여

Local Repository의 내용을 Remote Repository에 보낼 때

git remote add origin https://github.com/Changho0514/book.git
git push -u origin master 

 

할때의 코드를 뜯어보자

 

1. origin이란?

git remote add origin https://github.com/Changho0514/book.git 에서 

이 커맨드에서 remote는 리모트 레포지토리에 관한 작업을 할 때 쓰는 커맨드이다. 

그리고 그 뒤의 add는 "새로운 리모트 레포지토리를 등록하겠다"는 뜻

그 다음에는 origin  https://github.com/Changho0514/book.git이라고 써있는 것은

https://github.com/Changho0514/book.git 리모트 레포지토리를 origin이라는 이름(별칭)으로 등록하겠다는 뜻이다.

그러니까 이 커맨드를 실행하고 나면 https://github.com/Changho0514/book.git origin으로 간단하게 나타낼 수 있게 된다.

 

왜 하필 origin이라고 하는 걸까

origin이 아닌 여러분이 원하는 다른 단어를 입력해도 큰 상관은 없다.

하지만 Git에서는 리모트 레포지토리를 최초로 추가할 때 origin이라는 이름으로 가리키는 것이 관례화되어 있다. 

origin은 ‘근원’, ‘기원’이라는 뜻이니만큼

아마도 다른 사람의 리모트 레포지토리를 자신의 컴퓨터로 가져와서 작업을 하는 사람의 입장에서는

리모트 레포지토리가 프로젝트의 근원이 되는 존재이기 때문에 그런 관습이 생긴 것으로 추측된다.

 

사실

 git remote add RYERIOWEROI https://github.com/Changho0514/book.git

처럼 origin 대신 우리가 원하는 임의의 단어(RYERIOWEROI)를 써도 상관은 없지만,

되도록 관례에 따라 origin을 쓰는 것이 좋겠다.

 

2. Remote Repositoy에 있는 브랜치

git push -u origin main

이 커맨드의 뜻은 

  • 현재 로컬 레포지토리에 있는 master 브랜치의 내용(=main 브랜치와 관계된 모든 커밋들)을
  • origin이라는 리모트 레포지토리로 보낸다는 뜻이다.

이때 같은 이름의 브랜치로 전송하게 되는데 만약 origin이라는 리모트 레포지토리에 main 브랜치가 없으면 main 브랜치를 새로 생성하고 푸시한다.

옵션 -u는?

-u--set-upstream이라는 옵션의 약자이다.

이렇게 --set-upstream(-u) 옵션을 주면 

  • 로컬 레포지토리에 있는 main 브랜치가 
  • origin에 있는 main 브랜치를 tracking(추적)하는 걸로 설정된다. 

tracking이라는 건 로컬 레포지토리의 한 브랜치가 리모트 레포지토리의 한 브랜치와 연결되어 그것을 계속 바라보는 상태가 되는 것과 같다. 이렇게 맺어진 연결 상태를 tracking connection이라고 한다.

만약

  • 로컬 레포지토리에 A라는 브랜치가 있고,
  • 리모트 레포지토리에 B라는 브랜치가 있을 때 
  • 이런 tracking connection이 서로 맺어진 경우,
  • B 브랜치를 A 브랜치의 upstream branch라고 한다.
  • 지금은 구별하기 위해서 A와 B라고 표현했지만 보통은 같은 이름인 경우가 대부분이다.

이렇게 tracking connection이 한번 설정되고 나면,

사용자가 현재 main 브랜치에 위치해있을 때,

git push

라고만 써도 자동으로 리모트 레포지토리의 main 브랜치를 대상으로 git push가 동작하고,

git pull 

라고만 써도 리모트 레포지토리의 main 브랜치를 대상으로 git pull이 동작한다. 

사실 --set-upstream(-u) 옵션을 주지 않아도 그 후에 git push와 git pull을 할 수 있기는 하다.

하지만 맨 처음에 이 옵션을 주지 않으면 tracking connection이 없기 때문에 나중에 git push를 하고 싶을 때

git push origin main:main

이런 식으로 적어줘야 한다. 여기서

  • origin리모트 레포지토리를 나타내고,
  • main:main에서 더 먼저 나오는 main는 로컬 레포지토리의 main 브랜치(~에서)/더 뒤에 나오는 main는 리모트 레포지토리의 main 브랜치(~으로)를 나타낸다.

그러니까 tracking connection이 없으면 매번 이런 식으로 git push를 해줘야 한다. git pull도 마찬가지.

그러니까 그냥 처음부터 tracking connection을 설정하고 그 이후부터는 git push, git pull이라고만 써서 편하게 푸시와 풀을 하는 게 좋다. 이게 바로 맨 처음에 로컬 레포지토리의 내용을 리모트 레포지토리로 보낼 때 -u라는 옵션을 썼던 이유이다.

3. origin/main의 의미

  • 로컬 레포지토리의 main 브랜치 
  • 리모트 레포지토리의  main 브랜치 

이렇게 같은 이름이지만, 서로 다른 2개의 브랜치가 있다. 

그럼 리모트 레포지토리에 있는 main 브랜치는 어떻게 볼 수 있을까? GitHub 페이지에서 보면 되지만,

내 컴퓨터에서도 확인할 수 있는 방법이 있다.

커밋 히스토리를 살펴보면

 

위 그림에서

  • main이 로컬 레포지토리의 main 브랜치를 나타내고
  • origin/main이 리모트 레포지토리의 main 브랜치를 나타낸다. 

이때까지 로컬 레포지토리의 main 브랜치에서 여러 커밋을 했지만 그러고나서 git push를 해준 적은 없었다.

그래서 위 그림처럼 origin/main이 main보다 이전의 커밋을 가리키고 있다.

 

 

 

main 브랜치, Premium 브랜치 둘다 push 하기

main 브랜치로가서 git push를 하면

main 브랜치에서 한 작업들이 리모트 레포지토리의 main 브랜치로 올라간다.

 

git history를 확인해보면

두 브랜치가 같은 커밋에 놓여있다 -> 리모트 레포지토리가 최신화되었다

 

premium 브랜치로 가서 git push를 또 해보자

에러가 발생한다. 내용인 즉슨, "premium 브랜치가 upstream 브랜치를 갖고있지 않다"는 것

아래에 나온

git push --set-upstream origin premium

코드를 실행시켜보자

Github 홈페이지에서 확인해보면

branch가 2개가 되어있다.

 

 

 

HEAD와 브랜치의 관계

브랜치도 어떤 커밋을 가리키는 존재(포인터) 이다.

 

HEAD는 브랜치를 가리키고, 브랜치가 커밋을 가리킨다.

즉, HEAD -> branch -> commit 의 구조를 가진다.

HEAD -> main -> (commit)

의 구조에서 "git checkout premium" 이라고 하면

HEAD -> premium ->(commit) 이 되는 것이다. 

 

 

결국에는 HEAD 위치에 따라 working directory가 바뀐다.

git reset 또한, 

  1. 과거의 커밋으로 git reset을 한다고 그 이후의 커밋들이 삭제되는 게 절대 아니라 계속 남아있다. 
  2. git reset은 과거의 커밋뿐만 아니라 현재 HEAD가 가리키는 커밋 이후의 커밋으로도 할 수 있다. 

 

 

 

git reset VS git checkout의 차이점

git reset

 

Git checkout

이렇게 HEAD가 특정 커밋을 직접 가리키게 하는 이유는 여러가지가 있을 수 있는데

그 중에서 주된 이유 한 가지는 바로 과거의 특정 커밋에서 새로운 브랜치를 만들고 싶을 때이다.