실제로 우리가 개발하는 SW는 단순히 개발 --> 사용의 단계를 거치지 않고 디버깅, 개선 작업, 배포 등 매우 다양한 과정을 거친다. 이런 단계들은 순서대로 발생하지 않으며 병렬로 진행되므로 서로에게 영향을 주어서는 안 된다.
이런 경우 branch를 사용하면 리소스들은 각각의 branch에서 관리되기 때문에 서로 간의 충돌 없이 작업할 수 있게 된다.
또한 여러 팀원과의 협업에서도 팀원 간의 독립적인 작업 공간을 확보하기 위해 branch는 필요하다
branch 생성과 사용
branch 생성
기존의 master branch는 코드를 통합해서 관리하는 역할을 하고 두 명의 개발자를 의미하는 dev1, dev2 branch를 생성해보자.
[Team] -> [Switch To] -> [New Branch]로 dev1, dev2 branch를 생성할 수 있다.
branch name에 dev1을 입력하고 [Finish]를 클릭하면 branch가 생성된다. 특정 branch를 사용하기 위해서는 checkout 명령을 사용하는데 하단의 [Check out new branch]를 선택하면 branch 생성과 동시에 해당 branch를 사용하게 된다.
이때 프로젝트를 보면 master에서 dev1으로 branch 정보가 바뀐 것을 알 수 있다. 동일한 방법으로 dev2 branch도 생성해 보자.
현재 상태는 dev1, dev2, master가 모두 동일한 버전을 참조하고 있고 현재 dev2를 사용하고 있음(굵은 글씨)을 알 수 있다.
전체 branch 가 다 보이지 않을 때는 Ref Filters 메뉴를 이용하여 refs/heads/** 가 필터에 추가되도록 하자.(클릭하면 토글 됨)
dev1과 dev2가 있을 때 dev1이 dev2의 최신 커밋을 포함하고 있는 상태에서 Fast Forward 병합이 가능하다. Fast Forward 병합 시 단지 dev2를 dev1의 branch pointer까지 전진시켜 같은 commit을 참조하게 할 수 있고(옵션 1 - 기본) 새로운 merge commit을 만들 수도 있다.(옵션 2) 만약 command line에서 처리한다면 --no-ff 옵션을 추가하면 된다.
이제 Test.java를 살펴보면 dev1에서 작업했던 내용이 잘 반영되어 있음을 알 수 있다. 신기하게도 master의 Test.java에 dev1 개발자가 작업한 String branch="dev1"이라는 부분이 통합되어 있다.
package gittest;
public class Test {
String first = "first";
String second = "second";
String third = "third";
String branch = "dev1";
}
그런데 여기서 잠깐 고민해 볼 내용이 있다. master에서 dev1을 merge 할 때 dev1에 있던 String branch = "dev1"이라는 부분이 master의 Test.java에 통합되는 것을 확인했다. 그런데 dev2의 Test.java에는 동일한 위치에 String branch="dev2"라고 작성되어 있다.
즉 동일한 위치에서 다른 내용이 있는 것이다. 과연 git은 이 코드를 어떻게 통합할까?
dev2와의 merge를 진행해 보면 소위 충돌(conflict)이 발생한다.
conflict 상황은 코드를 병합하는 과정에서 어떤 코드가 사용돼야 하는지 git을 결정하지 못하기 때문에 사용자에게 결정을 요청하는 상황이다.
Test.java를 살펴보면 어디에서 문제가 발생해서 통합에 실패했는지 잘 표현해 준다.
package gittest;
public class Test {
String first = "first";
String second = "second";
String third = "third";
<<<<<<< HEAD : 현재 branch의 head가 가지는 내용
String branch = "dev1";
======= : 통합될 코드가 가지는 내용
String branch = "dev2";
>>>>>>> refs/heads/dev2
}
즉 현재 head가 가지고 있는 내용과 dev2의 head가 가지고 있는 내용이 충돌하는데 어떤 코드를 사용할 것인지 묻는 것이다. 이런 경우는 dev1 개발자와 dev2 개발자가 상의하여 올바른 코드를 결정하고 그 코드로 반영해 주면 된다.
아래와 같이 코드를 변경하고 다시 commit 해주자.
package gittest;
public class Test {
String first = "first";
String second = "second";
String third = "third";
String branch = "dev1 & dev2";
}
commit 과정에서는 어떤 branch를 통합하다가 어떤 리소스에서 충돌이 발생했는지가 자동으로 commit message에 적용된다.
이제 git history를 살펴보면 master가 가장 윗자리를 차지하고 있다.
비로소 두 개발자의 작업이 master로 잘 통합되었다.
checkout
이제 통합이 완료되었고 다시 dev1과 dev2 branch로 이동해 보자.
branch를 변경할 때 [Team] -> [Switch To] 메뉴 외에 history view에서 대상 branch를 선택 후 context menu에서 [checkout]을 선택해도 동일한 결과를 얻는다.
다시 각각의 작업 진행
이제 master의 Test가 변경되었고 다시 dev1과 dev2는 master에 적용된 최종 버전을 이용해서 각자의 작업을 이어나가면 된다.
가장 일반적인 방법은 각각의 sub branch로 checkout 한 후 거꾸로 master branch를 merge 하는 일이다.
또는 dev1으로 checkout 한 후 master의 commit으로 reset 처리해 주자.