안녕하세요, 이번 포스팅에서는 깃허브의 Fork 와 upstream, origin 을 동기화는 작업의 전체적인 흐름에 대해 자세히 알아보겠습니다.
Fork 는 내가 원하는 프로젝트에 대해 내 깃허브 레포지토리로 복제하는 것으로, 본인이 진행하는 프로젝트외에도 제 3자의 프로젝트 또한 수정을 하고 PR 을 올려서 기여를 할 수 있는 유용한 기능입니다.
타인의 프로젝트를 복제해오는 Forking의 방법은 간단합니다. 원하는 레포지토리에 접속해서, 아래와 같은 Fork 버튼을 눌러서 가져오시면 됩니다.
몰론 Forking 이 여기서가 끝이 아니겠죠? 프로젝트를 카피 해왔다면 본인의 목적에 따라서 복제한 레포지토리에서 마음대로 소스코드를 수정할 수 있습니다. 이를 어떻게 하는지 계속 자세히 알아보도록 하겠습니다.
저희는 Fork 에 관한 이번 내용에서 두 종류의 remote (원격) 레포지토리를 알아야합니다. 바로 origin 과 upstream 입니다.
- origin : 포킹한 내 소유의 remote 레포지토리
- upstream : 원본 레포지토리. 즉, 포킹을 당한 레포지토리
이때 origin 은 clone 을 받으면 자동으로 origin 으로 등록이되어서, 별도로 등록을 해줄 필요가 없습니다.
즉, upstream 레포지토리에 대해서만 새롭게 URL 을 등록해주면 됩니다.
명렁어를 우선 정리해보자면 아래와 같습니다.
Forking 하고 -> 로컬에서 작업후 origin, upstream 레포지토리에 PR 요청을 하기 까지의 과정을 요약하면 아래와 같습니다.
과정
- git clone
- git remote add upstream "포킹한 레포지토리(origin) 의 url"
- 로컬에서 작업하기
- git push origin 브랜치명
- 활성화된 PR 버튼을 누르면, origin 뿐만 아니라 upstream 에도 PR 요청이 가게된다.
위 과정을 조금 더 자세히 살펴보자면 아래와 같습니다.
1. git clone // 포킹한 나의 레포지토리로부터 clone을 받는다.
2. git remote add upstream "포킹한 레포지토리의 url" // 제 3자(원작자) 의 레포지토리를 upstream 으로 등록
3. git checkout -b branch1 // 브랜치 branch1 을 생성 및 이동해서
4. 브랜치 branch1 에서 원하는 작업진행하고
5. git push origin branch1 // 포킹한 내 원격 레포지토리에 작업한 내용을 push
6. origin 레포지토리(포킹한 레포지토리) 에서 활성화된 Compare & pull request 버튼을 클릭해서 PR 을 진행
7. 버튼을 눌러서 PR 을 진행하면,upstream 레포지토리(원본 레포지토리)에 PR 생성 및 Merge 를 원작자에게 PR 요청이 넘어감
8. 원작자는 요청이 들어온 PR 을 수락하고 Merge 할지말지를 결정
아래 과정을 따라하면 포킹한 origin 레포지토리뿐 아니라, 로컬 레포지토리에도 upstream 의 변경사항이 반영됩니다.
origin 레포지토리에 반영하기 전에,로컬 레포지토리에 upstream 의 변경사항을 fetch 로 내려받고 origin 에 push 하는 것이라서 그런 것입니다.
- git fetch upstream
- git checkout main ( 이때 로컬에서 현재 작업중인 브랜치가 이미 main 이면 checkout 할 필요가 없다. )
- git merge upstream/main
- git push
1. git fetch upstream // 원본 레포지토리(upstream) 으로부터 fetch 하기
2. git checkout main // 로컬 레포지토리의 main 브랜치로 이동해서
3. git merge upstream/main // fetch 했던 내용을 main 브랜치에 Merge 하기
4. git push // 포킹한 origin 레포지토리에 upstream 변경사항을 반영
위 내용들을 아래에서 더 자세히 어떻게 이루는 것인지 상세하게 설명드리겠습니다.
예를들어 위와 같은 레포지토리가 있고, 이 레포지토리를 Fork 해봅시다.
그리고 내 레포지토리 origin_repo 를 만들고, 포킹한 결과입니다.
이제 포킹한 origin 레포지토리를 로컬로 내려받고 작업을 진행해봅시다.
- git clone : 내가 포킹한 레포지토리(origin) 을 클론받습니다.
- git remote add upstream "upstream 레포지토리 URL"
이때 remote 레포지토리를 하나 더 추가해줍니다. 바로 upstream 레포지토리를 추가하는 것입니다.
결과는 아래와 같이 upstream 이 정상적으로 연결된 모습을 확인할 수 있습니다.
그 뒤로 브랜치 branch1 를 생성하고 아래처럼 로컬에서 작업을 진행해주고, push 를 진행해줍시다.
- git checkout -b branch1
- 작업수행
- git push origin branch1
그러면 아래와 같이 PR 활성화 버튼이 생성되었습니다.
PR 활성화 버튼을 눌러서 PR 을 본격적으로 요청해줍시다.
그러면 PR 요청이 origin 레포지토리에 전달이 될텐데, 이떄 가장 중요한 것이 하나있습니다. 그것은 바로 PR 요청이 origin 뿐만 아니라 upstream 에도 모두 동시에 전달된다는 것입니다.
PR 요청을 하면 origin 뿐만 아니라 upstream 레포지토리에도 동시에 요청이 전달됩니다.
아래처럼 upstream 에도 동시에 PR 요청이 들어가서 PR 활성화 버튼이 생긴것을 확인할 수 있습니다.
그러면 PR은 upstream 의 PR 내역에 추가가됩니다.
이 내역은 이제 원본 Repository에 수정 권한을 가진 사람들에 의해 검토될 것이며 원본 소스와 병합(merge)되는 것이 타당한 것으로 판단되는 경우 원본 소스에 병합될 것입니다.
여기까지 하면 협업 프로세스가 끝이 납니다.
깃허브를 통해 협업할때, 해당 레포지토리에 대해 협업자들 외에도 Collaborators 들이 있습니다. Collaborator 로 설정된 사람들은 merge 권한이 있어서 각자 자신이 작업한 내용을 바로 merge 할 수가 있습니다.
이는 장점이자 단점이 됩니다. 장점으로써는 신속한 Merge 가 된다는 것이지만, 반대로 단점은 master 에 merge 할 수 있는 권한은 매우 중요한 것이기 때문에 여러명이 Merge 권한을 갖고 있는것은 나중에 에러와 보안 문제를 야기시키는 위험한 방법이 될 수 있습니다.
Fork 를 사용한다면 프로젝트의 소유자만이 merge 권한을 갖고, 다른 팀원들은 해당 브런치에 PR 을 날리기만 할 수 있습니다.
또한 PR 을 날릴 수 있는 사람의 수에 대한 제한도 없습니다.
정리
fork 는 내 레포지토리로 가져와서 작업하고 PR 를 날리기만 할 수 있는것입니다.
(이때 내가 날린 PR이 merge 되면 내가 contributor 가 된것임. 몰론 contribtor 가 되더라도 Merge 할 권한은 그대로 없다.)
지금까지 살펴본 내용은 포킹한 레포지토리에서 내용을 수정하고 PR 을 올리는 과정이었습니다.
이제는 원본 레포지토리(upstream) 에서 변경사항이 발생했을 때, 포킹한 레포지토리(origin) 에 어떻게 변경사항을 동일하게 반영해주는지(내려받을 수 있는지)를 알아보겠습니다.
- git fetch upstream
우선 upstream 레포지토리로 부터 로컬 레포지토리에 fetch 합니다.
이때 pull 이 아닌 fetch 이므로, upstream 의 변경사항이 바로 반영되지 않고 임시 브랜치(upstream/main)에 저장됩니다.
- git checkout main
- git merge upstream/main
그러면 임시 브랜치 upstream/main 에 있는 내역들을 로컬 레포지토리의 main 브랜치에 반영을 해줘야합니다. 따라서 main 으로 이동하고(checkout), 임시 브랜치를 Merge 해줌으로써 upstream 의 내용이 로컬의 main 브랜치에 반영될 수 있습니다.
- git push
임시 브랜치를 병합까지 다 했다면, 로컬 레포지토리 내용과 origin 레포지토리의 내용이 다를겁니다.
방금 따끈따끈하게 최신화된 로컬 레포지토리의 내용을 origin 에다 push 해주도록 합시다.
이렇게 함으로써 upstream 에서 변경되었던 내용을 origin repository 로 반영할 수 있게 되는 것이입니다.
사실 위 방법은 Merge 의 과정이 들어가있기 떄문에 커밋 히스토리가 다소 햇갈리고, 잘못하다가 충돌이 발생할 수도 있습니다. 따라서 제 개인적으로는 rebase 사용하시는 것을 권장드립니다.
- git fetch upstream main
- git rebase upstream/main
- fetch 에서 얻어낸 upstream 의 main 의 내용을 기준으로 재정렬
- 로컬 레포지토리와 upstream 이 동기화 된 것이다!
- git push origin main -f
- origin 의 main 에다 push 해서, origin 도 동기화 시켜줍시다.
우선 Fork 의 이점은 앞서 설명드렸습니다. 이 또한 당연히 Fork 를 사용하는 이유가 되겠죠?
또 다른 이유도 있습니다. 이 물음에 대한 대답을 하기 위해선, 우선 협업의 범위에 대해 다시 되짚어볼 필요가 있습니다.
여기서 오해하지 말아야할 것이, 협업을 할때 쓰인다는게, 자기 팀끼리의 협업을 말하는게 아닙니다.
Fork를 진행할 해당 레포지토리는 현재 본인이 참석하고 있는 프로젝트뿐만이 아닌, 제 3자가 진행한 프로젝트의 레포지토리를 Forking 할 수도 있는 것입니다.
만일 제 3자가 진행했던 프로젝트를 포킹하고 자신의 원격 레포지토리(fork한 레포지토리)에서 내용을 수정한다음 원작자에게 PR 을 올릴 수 있습니다. 즉, 만약 뜯어 고친 코드가 원작 코드보다 좋다고 생각해서, 원작자에게 PR 요청을 보낼 수 있는것이죠.
원작자가 내가 올린 PR 요청을 수락해준다면, 나 또한 해당 프로젝트의 Contributor 가 되는 것입니다.
그러나 수락을 안할시에는 원작코드는 바뀌지 않겠죠? 원작자가 내가 요청한 수정내용이 마음에 들지 않아서 거절할 수도 있는 것입니다.
즉, 타인의 프로젝트 레포지토리를 Forking(복제)해서 나만의 원하는 새로운 기능들을 추가 및 수정한 새로운 프로젝트를 생성하고, 상황에 따라 원작자에게 PR 을 요청해서 더 나은 프로젝트를 만들도록 하는 것이 Forking 을 하는 이유라고 볼 수 있겠습니다.😎
https://eunhee-programming.tistory.com/159
https://deepinsight.tistory.com/167
https://chanhuiseok.github.io/posts/git-2/
https://salix97.tistory.com/222