항상 새로운 팀원이 생길 때마다 흐름을 말로 설명하거나 Notion에 정리해주는 것에 한계를 느꼈다. 블로그 글로 작성 후 링크를 전달해주는 것이 더욱 효율적이고, 누군가는 해당 글을 보고 flow를 익힐 수 있을 것 같아 글로 남기고자 한다.
uptream의 코드 fork 뜨기(fork뜬 저장소는 본인의 origin)
(원본 저장소 관리자는 원본 코드가 origin이지만 직접 origin에 코드를 병합하진 않는다. 해당 사진은 개인 저장소처럼 그려두었지만 보통 organization을 만들어 organization의 소속으로 해두는 편! (팀장도 fork)
git Clone 이후 로컬 환경에서 IDE로 열기
git remote -v
fork한 origin 저장소를 git clone을 할 경우, git은 upstream(원본코드)에 변화가 생길경우 이를 확인할 방법이 없다.
팀원들의 PR 및 merge로 upstream의 코드에 변동사항이 발생할 경우, 이를 동기화해야한다. 따라서 upstream을 remote해준다.
git remote add {이름} {주소}
git remote add upstream {원본 저장소 url}
git checkout -b feature/{작업자 이름}/{작업내용}
git checkout feature/pengooseDev/TicketBoard
JeJe가 코드를 작성해서 코드의 버젼이 v1.1이 되었다고 가정하자.
우리는 두 가지를 생각해야한다. (git이랑 github이랑 다르다. git은 우리 컴퓨터의 저장소로, github은 온라인 저장소로 이해하면 편하다)
1. github의 코드
2. git(로컬. 즉, VSC로 작성한 코드)
부연설명(생성 순서)
- github/origin/v1.0
- JeJe가 github에서 upstream을 fork한 시점에서 생성된다.
- git/origin/v1.0
- JeJe가 git clone을 VSC에서 하는 순간 로컬에 파일로 생긴다.
- git/feature/v1.0
- JeJe가 git으로 git checkout -b feature을 하는 순간 생긴다.
- git/feature/v1.1
- JeJe가 VSC에서 새로운 코드를 작성하는 순간 생긴다.
- github/feature/v1.1
- JeJe가 git에서 작성한 코드를 github의 feature branch로 push하면 생긴다.
- PR
- JeJe의 github에서 feature branch에 존재하는 코드는 v1.1이다(현재 사진 상황)
JeJe는 이 코드를 upstream의 branch중 하나로(develop이라 가정)PR을 날린다 가정하자.
PR이 생성되고 merge되었을 때, upstream의 branch중 merge를 받은 branch(develop)만 v1.1인 상태이고 나머지 branch는 v1.0이다.
사실 엄밀히 말하면 JeJe가 생성한 PR이 merge된 시점에서 upstream의 develop branch에 존재하는 코드는 v1.1이 아니라 v1.1.1이다. PR이 merge되면서 merge되었다는 commit이 추가되기 때문이다.(git이 처음이라면 이해하지 않고 넘어가도 큰 문제가 되지 않는 부분)
그래야 conflict가 최소화된다.
upstream의 코드를 병합하는 과정에서 Conflict가 날 수 있다.
Conflict를 최소화하는 방법과 전략은 여러가지이다.(추후 설명)
일단 행동방식부터 확인하자.
Pengoose(또는 팀원)의 시점으로 바라보자.
다른 팀원(JeJe)이 날린 PR이 upstream에 merge되었다면?
Pengoose는 git(local)
과 github
의 코드를 upstream의 코드로 동기화
를 시켜주어야 한다.
아래의 순서를 따르도록 한다.
(현재 작업하고 있던 branch가 feature branch라고 가정한다)
git stash
git checkout main
필자는 main branch를 동기화를 위해 코드를 받아오는 용도로만 사용한다.
작성한 코드가 main으로 push되는 일은 전혀 없다.
현재 코드의 상황은 아래와 같다.
우선 upstream의 변경된 코드인 v1.1(주황색)을 git main으로 받아오자
git fetch upstream main
의미는 아래와 같다.
git(을 이용해) fetch(데이터를 가져올게요 어디서?) upstream(의) main(branch에서!)
main branch로 서술했지만 gitflow를 사용한다면 아마 develop branch에서 받아오게 될 것이다. PR이 merge된 브랜치에서 받아오면 된다.
git merge upstream/main
의미는 아래와 같다.
git(을 이용해 현재 내가 있는 브랜치에) merge(코드를 병합할게요. 무슨코드를 병합하냐면 fetch해온 코드 중) upstream/main(upstream의 main브랜치에서 받아온 코드를요!)
즉, 3번에서 fetch해온 코드(upstream/main)을 현재 내가 있는 브랜치(main branch)에 병합하는 과정이다.
fetch를 upstream의 develop 브랜치에 했다면 git merge upstream/develop가 맞는 표현!
여기까지 진행했다면 현재 상황은 아래와 같다.
다시 말하지만 git은 로컬이고 github은 인터넷 원격 저장소를 말한다. 둘이 다른거다!
git을 이용해 upstream github에 저장된 코드를 local에 가져오고 병합했다. 나의 github엔 적용이 안되어있으므로 이를 push하여 적용시켜주는 과정이다.
git push origin main
그 결과는 아래와 같다.
새로 업데이트 된 코드를 feature 브랜치에서 병합하여 기존에 작성하던 코드와 합친다고 가정하자.
git checkout feature
origin main에서 코드를 업데이트 해두었기 때문에 origin main에서 코드를 동기화하자. (물론, upstream에서 또 받아와도 되지만 귀찮다. main에 동일한 코드를 백업해두었으니 main에서 가져온다.)
git pull origin
5번과 동일한 논리이다. github feature branch 업데이트 해주자.
git push origin feature
뿌뿌뿌-🥳 코드를 upstream과 동기화하는 여정이 끝났다.
하지만 아직 한 발 남았다.
바로 conflict다.
git stash pop
숨겨두었던 코드를 꺼내자. 여기서 conflict가 날 수 있다.
conflict는 언제 발생하느냐?
현재 코드와 새로운 코드가 동시에 변경사항이 있는 경우
쉽게 말해 코드 변경사항이 겹치면 conflict난다.
Conflict를 최소화하는 것도 전략
이고 실력
이다.
어떻게 관리할 것인가?
위의 코드를 복붙해서 쓰는 것보다, 위의 방식을 이해하고 상황에 따라 적용하는 것이 가장 좋다.
다들 햅삐햅삐 행복코딩!! 🥳
방-긋
해당 브랜치를 가져와 commit을 전부 클론을하고, 현재 브랜치에 추가한다.
git checkout origin pengooseDev
git rebase {가져올 브랜치}
커밋이 쌓였을 때, 특정 커밋만 뽑아서 PR 날리기!
git checkout main
git checkout -b cherry
git cherry-pick {커밋id}
여러개 가져오고 싶은 경우
git cherry-pick {커밋id} {커밋id} {커밋id}
이런거도 가능.
git push origin cherry
新しいチームメンバーが毎回加わるたびに、プロセスを口頭で説明したりNotionにまとめたりすることに限界を感じました。ブログ記事を書いてリンクを共有する方が効率的であり、誰かがその記事を読んでフローを学ぶことができると思い、文章に残すことにしました。
上流のコードをフォークする(フォークしたリポジトリは自分のオリジン)
(原本のリポジトリ管理者は原本のコードがオリジンだが、直接オリジンにコードをマージすることはない。この写真は個人のリポジトリのように描かれているが、通常はorganizationを作成し、organizationの所属として設定する方が一般的だ!(チームリーダーもフォーク)
git Cloneの後、IDEでローカル環境で開く
git remote -v
フォークしたオリジンリポジトリをgit cloneする場合、gitは上流(原本コード)に変更があった場合、これを確認する方法がない。
チームメンバーのPRやmergeにより上流のコードに変更が生じた場合、これを同期させる必要がある。そのため、upstreamをリモートする。
git remote add {名前} {アドレス}
git remote add upstream {原本リポジトリURL}
git checkout -b feature/{作業者名}/{作業内容}
git checkout feature/pengooseDev/TicketBoard
JeJeがコードを書いてバージョンがv1.1になったとしましょう。
私たちは二つのことを考えなければなりません。(gitとgithubは異なる。gitは私たちのコンピュータのリポジトリとして、githubはオンラインリポジトリとして理解すると便利だ)
1. githubのコード
2. git(ローカル。つまり、VSCで書いたコード)
補足説明(作成順序)
- github/origin/v1.0
- JeJeがgithubでupstreamをフォークした時点で作成される。
- git/origin/v1.0
- JeJeがVSCでgit cloneする瞬間にローカルにファイルとして作成される。
- git/feature/v1.0
- JeJeがgitでgit checkout -b featureをする瞬間に作成される。
- git/feature/v1.1
- JeJeがVSCで新しいコードを書く瞬間に作成される。
- github/feature/v1.1
- JeJeがgitで書いたコードをgithubのfeatureブランチにpushすると作成される。
- PR
- JeJeのgithubでfeatureブランチに存在するコードはv1.1である(現在の写真状況)
JeJeはこのコードをupstreamのブランチの一つに(developと仮定)PRを送ると仮定しよう。
PRが作成されmergeされた時、upstreamのブランチの中でmergeを受けたブランチ(develop)のみがv1.1の状態で、他のブランチはv1.0である。
実際には、JeJeが作成したPRがmergeされた時点でupstreamのdevelopブランチに存在するコードはv1.1ではなくv1.1.1である。PRがmergeされるとmergeされたことのcommitが追加されるためだ。(gitが初めてなら理解しなくても大きな問題ではない部分)
そうすることでconflictが最小限になる。
upstreamのコードをマージする過程でConflictが生じる可能性がある。
Conflictを最小限にする方法と戦略は様々だ。(後ほど説明)
まずは行動様式から確認しよう。
Pengoose(またはチームメンバー)の視点で見てみよう。
他のチームメンバー(JeJe)が送ったPRがupstreamにmergeされた場合?
Pengooseはgit(local)
とgithub
のコードをupstreamのコードと同期
させる必要がある。
以下の順序に従う。
(現在作業中のブランチがfeatureブランチだと仮定)
git stash
git checkout main
私はmainブランチを同期のためにコードを受け取る用途でのみ使用する。
書いたコードがmainにpushされることは全くない。
現在のコードの状況は以下の通りです。
まずはupstreamの変更されたコードv1.1(オレンジ色)をgit mainに受け取ろう
git fetch upstream main
意味は以下の通りです。
git(を使って) fetch(データを取ってくるよ、どこから?) upstream(の) main(ブランチから!)
mainブランチと述べましたが、gitflowを使用している場合は、おそらくdevelopブランチから受け取ることになるでしょう。PRがmergeされたブランチから受け取れば良いです。
git merge upstream/main
意味は以下の通りです。
git(を使って現在私がいるブランチに) merge(コードをマージします。どのコードをマージするかというと、fetchしてきたコードの中で) upstream/main(upstreamのmainブランチから受け取ったコードです!)
つまり、3番でfetchしたコード(upstream/main)を現在自分がいるブランチ(mainブランチ)にマージするプロセスです。
fetchをupstreamのdevelopブランチに行った場合、git merge upstream/developが正しい表現になります!
ここまで進めたら、現在の状況は以下のようになります。
再び言いますが、gitはローカルを指し、githubはインターネットのリモートリポジトリを意味します。二つは異なります!
gitを使ってupstream githubに保存されているコードをローカルに持ってきてマージしました。私のgithubには適用されていないので、これをpushして適用させるプロセスです。
git push origin main
その結果は以下の通りです。
新しく更新されたコードをfeatureブランチでマージして、以前に作成していたコードと統合することを想定します。
git checkout feature
origin mainからコードを更新したので、origin mainからコードを同期しましょう。(もちろん、upstreamから再び取得しても構いませんが、面倒です。mainに同じコードをバックアップとして保存しておいたので、mainから取得します。)
git pull origin
5番と同じ論理です。githubのfeatureブランチを更新しましょう。
git push origin feature
ぶぶぶ-🥳コードをupstreamと同期する旅が終わりました。
しかし、まだ一歩が残っています。
それはconflictです。
git stash pop
隠していたコードを取り出しましょう。ここでconflictが発生する可能性があります。
conflictはいつ発生するかというと、
現在のコードと新しいコードが同時に変更された場合
簡単に言えば、コードの変更が重なるとconflictが発生します。
Conflictを最小限に抑えることも「戦略」であり、「技術」です。
どのように管理するか?
上記のコードをコピー&ペーストして使うよりも、上記の方法を理解し、状況に応じて適用することが最も良いです。
みなさん、ハッピーハッピーハッピーコーディング!! 🥳
ばんぐっと
該当ブランチを取得してコミットをすべてクローンし、現在のブランチに追加します。
git checkout origin pengooseDev
git rebase {取得するブランチ}
コミットが積み重なったとき、特定のコミットだけを選んでPRを送ります!
git checkout main
git checkout -b cherry
git cherry-pick {コミットID}
複数取得したい場合
git cherry-pick {コミットID} {コミットID} {コミットID}
このようにもできます。
git push origin cherry