Git rebase와 Git merge를 언제 사용하는 것이 좋습니까?
성공적으로 리베이스 한 후에도 병합해야합니까?
Git rebase와 Git merge를 언제 사용하는 것이 좋습니까?
성공적으로 리베이스 한 후에도 병합해야합니까?
답변:
그래서 언제 사용합니까?
init
새 저장소, add
파일 및commit
. 새 기능 분기 체크 아웃 ( checkout -b feature
.) 기능 분기 에 두 개의 새로운 커밋이 있도록 텍스트 파일을 변경하고 커밋하고 반복하십시오. 다음 checkout master
과 merge feature
. 에서가 log
, 내 초기이 기능에서 통합 된이 다음에, 마스터에 커밋을 참조하십시오. 의 경우 merge --squash feature
기능이 마스터로 병합되었지만 커밋되지 않았으므로 마스터에 대한 새로운 커밋은 내가 만든 것입니다.
간단 해. 리베이스를 사용하면 다른 브랜치를 작업의 새로운 기반 으로 사용한다고 말합니다 .
예를 들어 master
분기가있는 경우 새 기능을 구현하기 위해 분기를 작성하고 이름을 지정하십시오 cool-feature
. 물론 마스터 분기는 새 기능의 기본입니다.
이제 특정 지점에서 master
지점 에서 구현 한 새 기능을 추가하려고합니다 . 지점으로 전환하여 master
병합 할 수 있습니다 cool-feature
.
$ git checkout master
$ git merge cool-feature
그러나이 방법으로 새로운 더미 커밋이 추가됩니다. 스파게티 역사를 피하려면 다음과 같이 리베이스하십시오 .
$ git checkout cool-feature
$ git rebase master
그런 다음에 병합하십시오 master
.
$ git checkout master
$ git merge cool-feature
이번에는 토픽 브랜치가 동일한 마스터 커밋과 새로운 기능을 가진 커밋을 가지고 있기 때문에 병합은 빨리 진행됩니다.
but this way a new dummy commit is added, if you want to avoid spaghetti-history
-어떻게 나빠?
Sean Schofield
가 코멘트에 다음과 같이 말합니다 : "당신이 일단 UR 자료를 마스터로 다시 병합하면 (이미 설명 된 것처럼 사소한 일이지만) ur 커밋 기록의"맨 위 "에 앉아 있기 때문에 Rebase도 좋습니다. 기능이 작성되었지만 몇 주 후에 병합 된 프로젝트의 경우, 역사에서 마스터 방식으로 "채워 지므로"마스터로 병합하고 싶지 않습니다. 개인적으로 git log를 수행하고 볼 수있는 것이 좋습니다. 커밋 날짜는 그대로 유지됩니다. rebase는 해당 정보를 변경하지 않습니다. "
merge
, rebase
, fast-forward
, 등) 방향성 비순환 그래프의 특정 조작을 참조한다. 그들은 그 정신 모델을 염두에두고 추론하기가 더 쉬워집니다.
보완하기 위해 내 자신의 대답은 언급 TSamper에 의해 ,
리베이스는 병합하기 전에 수행하는 것이 좋습니다. 아이디어는 지점 Y
에서 B
병합 할 지점 의 작업을 지점 에 통합하기 때문입니다.
그러나 다시, 병합하기 전에, 당신은 어떤 분쟁 해결 하여 ( "의 지점에서 최근 시점부터 내 지점에서 내 작품을 재생으로,"REBASE "즉 지점 B
).
에 지점에서 제대로 후속 병합을 수행하는 경우 분기 B
가 빨리 진행될 수 있습니다.
병합은 대상 분기에 직접 영향을 B
미치므로 병합이 더 낫다는 것을 의미합니다. 그렇지 않으면 분기 B
가 안정 상태로 돌아 오기까지 시간이 오래 걸릴 수 있습니다 (모든 충돌을 해결할 시간)
리베이스 후 합병 점?
내가 설명하는 경우, 나는 B
지점에 기반을 B
두면서 최근 지점에서 작업을 다시 할 수있는 기회를 가지려고하지만 지점에 머무르는 동안.
이 경우에도 "재생 된"작업을 수행하려면 병합이 여전히 필요합니다.B
.
다른 시나리오 ( 예 : Git Ready에서 설명 )는 작업을 B
리베이스 를 통해 직접 가져 오는 것입니다 (모든 커밋을 보존하거나 대화식 리베이스를 통해 재주문 할 수있는 기회를 제공함).
이 경우 (B 브랜치에있는 동안 리베이스하는 경우) 맞습니다. 더 이상 병합 할 필요가 없습니다.
병합 또는 리베이스하지 않은 경우 기본적으로 Git 트리
우리는 rebasing에 의해 얻을 :
두 번째 시나리오는 모든 것입니다. 어떻게 새로운 기능을 마스터로 되돌릴 수 있습니까?
첫 번째 rebase 시나리오를 설명함으로써 나의 요점은 모든 사람들이 rebase를 예비 단계로 사용할 수 있음을 상기시키는 것입니다 ( "새로운 기능을 다시 마스터로 가져 오기").
rebase를 사용하여 먼저 새 기능 분기에 "마스터"를 마스터로 가져올 수 있습니다. rebase는에서 새 기능 커밋을 재생 HEAD master
하지만 여전히 새 기능 분기에서 분기 시작점을 이전 마스터 커밋에서 효과적으로 이동시킵니다 HEAD-master
.
이를 통해 지점의 모든 충돌을 해결할 수 있습니다 (충돌 해결 단계가 너무 오래 걸리면 마스터가 계속 병렬로 진행할 수 있음).
그런 다음 마스터 및 병합 new-feature
(또는 리베이스 전환 할 수 있습니다new-feature
에 master
당신이 할 커밋을 유지하려는 경우new-feature
분기).
그래서:
master
.의심스러운 경우 merge를 사용하십시오.
리베이스와 병합의 유일한 차이점은 다음과 같습니다.
따라서 짧은 대답은 당신의 역사가 어떻게 보이길 원하는지를 기반으로 리베이스 또는 병합 을 선택하는 것 입니다.
사용할 작업을 선택할 때 고려해야 할 몇 가지 요소가 있습니다.
그렇다면 리베이스하지 마십시오. Rebase는 지사를 파괴하고 개발자가 사용하지 않는 한 해당 저장소가 손상되었거나 일관되지 않습니다 git pull --rebase
. 이것은 다른 개발자들을 빠르게 화나게하는 좋은 방법입니다.
Rebase는 파괴적인 작업입니다. 즉, 제대로 적용하지 않으면 커밋 된 작업이 손실되거나 다른 개발자 리포지토리의 일관성이 손상 될 수 있습니다.
저는 회사가 지점과 합병에 대한 전담 직원을 감당할 수있는 시점에서 개발자가 모두 온 팀에서 일했습니다. 그 개발자들은 Git에 대해 잘 모르고 많이 알고 싶지 않습니다. 이 팀에서 나는 어떤 이유로 든 rebasing을 추천 할 위험이 없습니다.
일부 팀은 각 지점이 기능 (또는 버그 수정 또는 하위 기능 등)을 나타내는 기능별 지점 모델을 사용합니다.이 모델에서 지점은 관련 커밋 세트를 식별하는 데 도움이됩니다. 예를 들어, 해당 분기의 병합을 되돌려 서 피쳐를 신속하게 되돌릴 수 있습니다 (공평하게도 드문 작업입니다). 또는 두 가지를 비교하여 기능을 비교하십시오 (더 일반적). Rebase는 지점을 파괴하고 이것은 간단하지 않습니다.
또한 개발자 당 지점 모델을 사용하는 팀에서 일했습니다 (모두가 있었음). 이 경우 지점 자체는 추가 정보를 전달하지 않습니다 (커밋에는 이미 작성자가 있음). 리베이스에 아무런 해가 없습니다.
취소 취소와 같이 리베이스를 되 돌리는 것은 병합을 되 돌리는 것과 비교하여 상당히 어렵거나 불가능합니다 (리베이스에 충돌이있는 경우). 되돌릴 가능성이 있다고 생각되면 되돌리기를 사용하고 병합을 사용하십시오.
Rebase 작업은 해당으로 가져와야합니다 git pull --rebase
. 혼자서 작업하는 경우 적절한 시간에 사용해야하는 것을 기억할 수 있습니다. 팀에서 작업하는 경우 조정하기가 매우 어렵습니다. 이것이 대부분의 rebase 워크 플로우가 모든 병합 (및 git pull --rebase
모든 풀)에 rebase를 사용하도록 권장하는 이유 입니다.
다음과 같은 병합이 있다고 가정합니다.
B -- C
/ \
A--------D
어떤 사람들은 병합이 커밋 히스토리를 "파괴"한다고 말하는데, 이는 마스터 브랜치 (A-D)의 로그 만 살펴보면 B와 C에 포함 된 중요한 커밋 메시지를 놓치기 때문입니다.
이것이 사실이라면 우리는 이와 같은 질문 이 없을 것 입니다. 기본적으로 B와 C를 보지 않으면 명시 적으로 보지 않는 한 (--first-parent 사용) B가 표시됩니다. 이것은 스스로 시도하기가 매우 쉽습니다.
두 가지 접근 방식은 서로 다르게 병합되지만 하나가 항상 다른 것보다 낫다는 것은 확실하지 않으며 개발자 워크 플로에 따라 달라질 수 있습니다. 예를 들어, 개발자가 정기적으로 커밋하는 경향이있는 경우 (예 : 직장에서 집으로 전환 할 때 하루에 두 번 커밋) 지정된 브랜치에 대해 커밋이 많이있을 수 있습니다. 이러한 커밋의 대부분은 최종 제품과 같이 보이지 않을 수도 있습니다 (기능마다 한두 번 내 접근 방식을 리팩터링하는 경향이 있습니다). 다른 사람이 관련 코드 영역에서 작업하고 있고 내 변경 사항을 리베이스하려고하면 상당히 지루한 작업 일 수 있습니다.
"시간 절약" 으로 별칭 rm
을 지정 rm -rf
하려면 rebase가 적합합니다.
저는 언젠가 Git rebase가 문제를 해결하는 멋진 도구 인 시나리오를 보게 될 것이라고 생각합니다. Git reflog가 내 문제를 해결하는 멋진 도구 인 시나리오를 보게 될 것이라고 생각합니다. 나는 지금 5 년 이상 Git과 함께 일했다. 일어나지 않았습니다.
지저분한 역사는 실제로 나에게 문제가되지 않았습니다. 나는 신나는 소설처럼 내 커밋 역사를 읽지 않습니다. 어쨌든 Git 비난이나 Git bisect를 사용할 역사가 필요합니다. 이 경우 병합 커밋을 갖는 것이 실제로 유용합니다. 병합에서 문제가 발생하면 의미있는 정보가되기 때문입니다.
내 일반적인 조언은 여전히 유효하지만 rebase 사용을 개인적으로 부드럽게했다고 언급 할 의무가 있습니다. 최근에 Angular 2 Material 프로젝트 와 많은 상호 작용을 해왔습니다 . 그들은 rebase를 사용하여 매우 깨끗한 커밋 기록을 유지했습니다. 이를 통해 커밋이 특정 결함을 수정 한 내용과 해당 커밋이 릴리스에 포함되었는지 여부를 매우 쉽게 확인할 수있었습니다. 리베이스를 올바르게 사용하는 좋은 예입니다.
여기에 많은 답변이 병합하면 모든 커밋이 하나로 바뀌므로 커밋을 유지하기 위해 rebase를 사용하는 것이 좋습니다. 이것은 올바르지 않습니다. 그리고 당신이 이미 커밋을 푸시했다면 나쁜 생각 입니다.
병합은 커밋을 없애지 않습니다 . 병합은 역사를 보존합니다! Rebase는 기록을 다시 씁니다. 기록을 푸시 한 후 나쁜 일 입니다.
병합을 사용하십시오- 이미 푸시 할 때마다 리베이스하지 마십시오 .
여기 Linus '(Git의 저자)가 사용합니다 (현재 Wayback Machine에서 복구 한대로 내 블로그에서 호스팅 ). 정말 잘 읽었습니다.
또는 아래에서 동일한 아이디어의 내 버전을 읽을 수 있습니다.
마스터에서 브랜치를 리베이스 :
반대로 주제 분기를 마스터로 병합 :
TLDR : 그것은 가장 중요한 것에 달려있다-깔끔한 역사 또는 개발 순서의 진정한 표현
깔끔한 이력이 가장 중요한 경우 먼저 리베이스 한 다음 변경 사항을 병합하여 새 코드가 무엇인지 명확하게 알 수 있습니다. 이미 지사를 밀었다면 그 결과를 다룰 수 없다면 기지를 세우지 마십시오.
시퀀스의 진정한 표현이 가장 중요한 경우에는 리베이스하지 않고 병합합니다.
병합 의미 : 변경 사항을 대상으로 병합하는 새 커밋 하나를 만듭니다. 참고 : 이 새로운 커밋에는 두 개의 부모가 있습니다-커밋 문자열에서 최신 커밋과 병합중인 다른 지점의 최신 커밋.
Rebase 의미 : 현재 커밋 세트를 힌트로 사용하여 완전히 새로운 일련의 커밋을 만듭니다. 다시 말해, 내가 근거를 둔 지점에서 변경을 시작한 경우 내 변경 내용이 어떻게 보일지 계산하십시오. 리베이스 후, 변경 사항을 다시 테스트해야 할 수도 있고, 리베이스 중에 약간의 충돌이있을 수 있습니다.
이것이 주어진 이유는 무엇입니까? 개발 이력을 명확하게 유지하기 위해. 기능 X에서 작업 중이고 작업이 끝나면 변경 사항을 병합한다고 가정 해 봅시다. 대상에 "Added feature X"줄에 무언가를 나타내는 단일 커밋이 생겼습니다. 이제 병합하는 대신 리베이스 한 다음 병합하면 대상 개발 기록에 모든 개별 커밋이 단일 논리적 진행으로 포함됩니다. 이렇게하면 나중에 변경 사항을 훨씬 쉽게 검토 할 수 있습니다. 50 명의 개발자가 항상 다양한 기능을 통합하고 있다면 개발 이력을 검토하기가 얼마나 힘든지 상상해보십시오.
즉, 이미 업스트림에서 작업중인 지점을 푸시 한 경우 리베이스하지 말고 병합해야합니다. 업스트림으로 푸시되지 않은 브랜치의 경우 리베이스, 테스트 및 병합합니다.
리베이스하려는 또 다른 시간은 업스트림을 추진하기 전에 지점에서 커밋을 제거하려는 경우입니다. 예를 들어, 초기에 디버깅 코드를 소개하는 커밋과 해당 코드를 정리하는 다른 커밋이 있습니다. 이를 수행하는 유일한 방법은 대화식 리베이스를 수행하는 것입니다.git rebase -i <branch/commit/tag>
업데이트 : 또한 Git을 사용하여 비선형 히스토리를 지원하지 않는 버전 제어 시스템 ( 예 : Subversion) 에 인터페이스 할 때 rebase를 사용하려고합니다 . git-svn 브리지를 사용할 때 Subversion으로 병합하는 변경 사항은 트렁크의 가장 최근 변경 사항에 대한 순차적 인 변경 사항 목록이어야합니다. 이를 수행하는 방법은 두 가지뿐입니다. (1) 변경 사항을 수동으로 다시 작성하고 (2) rebase 명령을 사용하면 훨씬 빠릅니다.
업데이트 2 : rebase를 생각하는 또 다른 방법은 개발 스타일에서 커밋하는 리포지토리에 허용되는 스타일로 일종의 매핑을 가능하게한다는 것입니다. 작고 작은 덩어리로 커밋한다고 가정 해 봅시다. 오타를 고치려는 커밋과 사용되지 않은 코드 등을 제거하는 커밋이 있습니다. 해야 할 일을 마칠 때까지 일련의 커밋이 있습니다. 이제 커밋하는 리포지토리가 큰 커밋을 장려한다고 가정 해 봅시다. 따라서 수행중인 작업에 대해 하나 또는 두 개의 커밋이 필요합니다. 커밋 문자열을 어떻게 받아 예상대로 압축합니까? 대화식 리베이스를 사용하고 작은 커밋을 더 큰 덩어리로 스쿼시합니다. 스타일이 약간 큰 커밋 인 경우 반대가 필요한 경우에도 마찬가지입니다. 그러나 저장소에는 작은 커밋이 긴 문자열이 필요했습니다. 리베이스를 사용하여 그렇게 할 수도 있습니다. 대신 병합 한 경우 커밋 스타일을 기본 리포지토리에 이식했습니다. 개발자가 많은 경우 일정 시간이 지난 후 여러 가지 커밋 스타일로 역사를 따르는 것이 얼마나 어려운지 상상할 수 있습니다.
업데이트 3 : Does one still need to merge after a successful rebase?
그렇습니다. 그 이유는 리베이스가 본질적으로 커밋의 "이동"을 수반하기 때문입니다. 위에서 말했듯이, 이러한 커밋은 계산되지만 분기 지점에서 14 개의 커밋이 있고 rebase에 아무런 문제가 없다고 가정하면 14 번 커밋이 진행됩니다 (당신이 기초를 두는 지점). 리베이스가 완료되었습니다. 리베이스 전에 지점이있었습니다. 이후에 같은 길이의 가지가 생깁니다. 변경 사항을 게시하기 전에 병합해야합니다. 다시 말해, 원하는만큼 여러 번 리베이스하십시오 (다시, 변경 사항을 업스트림으로 푸시하지 않은 경우에만). 리베이스 한 후에 만 병합하십시오.
git merge
는 --no-ff
강제로 병합 커밋을 수행 하는 옵션을 지원합니다 .
병합은 변경 사항을 통합하는 가장 쉽고 가장 일반적인 방법이지만 유일한 방법은 아닙니다. Rebase 는 대체 통합 방법입니다.
조금 더 잘 병합 이해
Git은 병합을 수행 할 때 세 가지 커밋을 찾습니다.
빨리 감기 또는 병합 커밋
아주 간단한 경우, 분기가 발생한 이후 두 분기 중 하나에 새로운 커밋이 없습니다. 최신 커밋은 여전히 공통 조상입니다.
이 경우 통합을 수행하는 것은 매우 간단합니다. Git은 공통 상위 커밋 위에 다른 분기의 모든 커밋을 추가 할 수 있습니다. Git에서이 가장 간단한 통합 형태를 "빨리 감기"병합이라고합니다. 두 가지 모두 정확히 같은 역사를 공유합니다.
그러나 많은 경우에 두 지점이 개별적으로 전진했습니다.
통합을하려면 Git은 차이점을 포함하는 새로운 커밋을 만들어야합니다 (병합 커밋).
인간 커밋 및 병합 커밋
일반적으로 커밋은 인간에 의해 신중하게 만들어집니다. 관련 변경 사항 만 래핑하고 주석으로 주석을 달 수있는 의미있는 단위입니다.
병합 커밋은 약간 다릅니다. 개발자가 만드는 대신 Git에서 자동으로 만듭니다. 관련 변경 사항을 래핑하는 대신 매듭처럼 두 가지를 연결하는 것이 목적입니다. 나중에 병합 조작을 이해하려면 분기 및 해당 커밋 그래프의 히스토리를 살펴 봐야합니다.
Rebase와 통합
어떤 사람들은 그러한 자동 병합 커밋없이 가기를 선호합니다. 대신, 그들은 프로젝트의 역사가 마치 하나의 직선으로 진화 한 것처럼 보이기를 원합니다. 어떤 지점에서 여러 지점으로 분할되었다는 표시는 없습니다.
리베이스 작업을 단계별로 살펴 보겠습니다. 시나리오는 이전 예제와 동일합니다. 우리는 브랜치 B에서 브랜치 A로 변경 사항을 통합하려고하지만 이제 rebase를 사용합니다.
우리는 세 단계로 이것을 할 것입니다
git rebase branch-A // Synchronises the history with branch-A
git checkout branch-A // Change the current branch to branch-A
git merge branch-B // Merge/take the changes from branch-B to branch-A
먼저, Git은 분기가 시작된 후 (공통 상위 커밋 이후) 발생한 A 분기의 모든 커밋을 "실행 취소"합니다. 그러나 물론, 그것들은 버리지 않을 것입니다. 대신에 당신은 그 커밋들을 "일시적으로 저장되었다"고 생각할 수 있습니다.
다음으로, 통합하려는 분기 B의 커밋을 적용합니다. 이 시점에서 두 가지 모두 똑같이 보입니다.
마지막 단계에서 브랜치 A의 새 커밋이 이제 다시 적용되지만 브랜치 B의 통합 커밋 위에 새 위치에 있습니다 (다시 기반으로 함).
결과는 개발이 직선으로 일어난 것처럼 보입니다. 모든 결합 된 변경 사항을 포함하는 병합 커밋 대신 원래 커밋 구조가 유지되었습니다.
마지막으로 원치 않는 자동 생성 커밋이없는 깨끗한 브랜치 브랜치 A 를 얻습니다 .
병합 / 리베이스하기 전에 :
A <- B <- C [master]
^
\
D <- E [branch]
후 git merge master
:
A <- B <- C
^ ^
\ \
D <- E <- F
후 git rebase master
:
A <- B <- C <- D' <- E'
(A, B, C, D, E 및 F는 커밋입니다)
Git에 대한이 예제와 훨씬 더 잘 설명 된 정보는 Git The Basics Tutorial 에서 찾을 수 있습니다. .
이 문장은 그것을 얻는다 :
일반적으로 두 세계를 최대한 활용하는 방법은 자신이 만든 로컬 변경 사항을 리베이스 (rebase)하는 것입니다. 그러나 스토리를 정리하기 위해 푸시하기 전에 아직 공유하지는 않았지만 어딘가에 푸시 한 것을 리베이스하지 마십시오. .
이 답변은 Git Flow를 중심으로 광범위하게 진행 됩니다. 테이블은 nice ASCII Table Generator 로 생성 되었으며이 멋진 명령을 사용하여 히스토리 트리 ( 별명git lg
)는 다음 과 같습니다.
git log --graph --abbrev-commit --decorate --date=format:'%Y-%m-%d %H:%M:%S' --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%ad%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n'' %C(white)%s%C(reset) %C(dim white)- %an%C(reset)'
테이블은 히스토리 트리와보다 일관되게 역순으로 정렬됩니다. 첫 번째 git merge
와 git merge --no-ff
첫 번째 의 차이점도 참조하십시오 (일반적으로 git merge --no-ff
역사가 현실에 더 가깝게 보이 도록 사용하려고합니다 ).
git merge
명령 :
Time Branch "develop" Branch "features/foo"
------- ------------------------------ -------------------------------
15:04 git merge features/foo
15:03 git commit -m "Third commit"
15:02 git commit -m "Second commit"
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
결과:
* 142a74a - YYYY-MM-DD 15:03:00 (XX minutes ago) (HEAD -> develop, features/foo)
| Third commit - Christophe
* 00d848c - YYYY-MM-DD 15:02:00 (XX minutes ago)
| Second commit - Christophe
* 298e9c5 - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git merge --no-ff
명령 :
Time Branch "develop" Branch "features/foo"
------- -------------------------------- -------------------------------
15:04 git merge --no-ff features/foo
15:03 git commit -m "Third commit"
15:02 git commit -m "Second commit"
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
결과:
* 1140d8c - YYYY-MM-DD 15:04:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/foo' - Christophe
| * 69f4a7a - YYYY-MM-DD 15:03:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * 2973183 - YYYY-MM-DD 15:02:00 (XX minutes ago)
|/ Second commit - Christophe
* c173472 - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git merge
vs git rebase
첫 번째 요점 : 항상 기능을 개발에 병합하고 기능에서 개발을 리베이스하지 마십시오 . 이것은 Rebasing의 황금률의 결과입니다 .
황금률
git rebase
은 공공 지점 에서 절대 사용하지 않는 것 입니다.
즉 :
당신이 어딘가에 밀었던 것을 리베이스하지 마십시오.
저는 개인적으로 다음과 같이 덧붙 입니다. 기능 지점이 아니고 귀하와 귀하의 팀이 결과를 인식하지 않는 한 .
따라서 git merge
vs 의 문제 git rebase
는 거의 피처 브랜치에만 적용됩니다 (다음 예제에서는 --no-ff
병합 할 때 항상 사용되었습니다). 더 나은 솔루션이 있는지 확실하지 않기 때문에 ( 토론이 존재 함 ) 두 명령의 작동 방식 만 제공합니다. 내 경우 git rebase
에는 더 멋진 기록 트리를 생성하므로 사용 하는 것이 좋습니다. :)
git merge
명령 :
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- --------------------------------
15:10 git merge --no-ff features/bar
15:09 git merge --no-ff features/foo
15:08 git commit -m "Sixth commit"
15:07 git merge --no-ff features/foo
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
결과:
* c0a3b89 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 37e933e - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * eb5e657 - YYYY-MM-DD 15:07:00 (XX minutes ago)
| |\ Merge branch 'features/foo' into features/bar - Christophe
| * | 2e4086f - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | | Fifth commit - Christophe
| * | 31e3a60 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | | Fourth commit - Christophe
* | | 98b439f - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ \ Merge branch 'features/foo' - Christophe
| |/ /
|/| /
| |/
| * 6579c9c - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * 3f41d96 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ Second commit - Christophe
* 14edc68 - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git rebase
명령 :
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10 git merge --no-ff features/bar
15:09 git merge --no-ff features/foo
15:08 git commit -m "Sixth commit"
15:07 git rebase features/foo
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
결과:
* 7a99663 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 708347a - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * 949ae73 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | Fifth commit - Christophe
| * 108b4c7 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | Fourth commit - Christophe
* | 189de99 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ Merge branch 'features/foo' - Christophe
| |/
| * 26835a0 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * a61dd08 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ Second commit - Christophe
* ae6f5fc - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
develop
기능 지점에git merge
명령 :
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10 git merge --no-ff features/bar
15:09 git commit -m "Sixth commit"
15:08 git merge --no-ff develop
15:07 git merge --no-ff features/foo
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
결과:
* 9e6311a - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 3ce9128 - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * d0cd244 - YYYY-MM-DD 15:08:00 (XX minutes ago)
| |\ Merge branch 'develop' into features/bar - Christophe
| |/
|/|
* | 5bd5f70 - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\ \ Merge branch 'features/foo' - Christophe
| * | 4ef3853 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | | Third commit - Christophe
| * | 3227253 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ / Second commit - Christophe
| * b5543a2 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | Fifth commit - Christophe
| * 5e84b79 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/ Fourth commit - Christophe
* 2da6d8d - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git rebase
명령 :
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10 git merge --no-ff features/bar
15:09 git commit -m "Sixth commit"
15:08 git rebase develop
15:07 git merge --no-ff features/foo
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
결과:
* b0f6752 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 621ad5b - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * 9cb1a16 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | Fifth commit - Christophe
| * b8ddd19 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/ Fourth commit - Christophe
* 856433e - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\ Merge branch 'features/foo' - Christophe
| * 694ac81 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * 5fd94d3 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ Second commit - Christophe
* d01d589 - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git cherry-pick
하나의 특정 커밋이 필요할 때 git cherry-pick
좋은 해결책입니다 (이 -x
옵션 은 원래 커밋 메시지 본문에 " (체리에서 선택되었습니다 ...) " 행을 추가 하므로 일반적으로 사용하는 것이 좋습니다.git log <commit_sha1>
보고 그것):
명령 :
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- -----------------------------------------
15:10 git merge --no-ff features/bar
15:09 git merge --no-ff features/foo
15:08 git commit -m "Sixth commit"
15:07 git cherry-pick -x <second_commit_sha1>
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
결과:
* 50839cd - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 0cda99f - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * f7d6c47 - YYYY-MM-DD 15:03:00 (XX minutes ago)
| | Second commit - Christophe
| * dd7d05a - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | Fifth commit - Christophe
| * d0d759b - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | Fourth commit - Christophe
* | 1a397c5 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ Merge branch 'features/foo' - Christophe
| |/
|/|
| * 0600a72 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * f4c127a - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ Second commit - Christophe
* 0cf894c - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git pull --rebase
나는 Derek Gourlay 보다 더 잘 설명 할 수 있는지 잘 모르겠습니다 ... 기본적으로 git pull --rebase
대신 git pull
:을 사용 하십시오.) 기사에서 누락 된 것은 기본적으로 활성화 할 수 있다는 것입니다 .
git config --global pull.rebase true
git rerere
다시 여기에 잘 설명되어 있습니다 . 그러나 간단히 말하면, 활성화하면 더 이상 동일한 충돌을 여러 번 해결할 필요가 없습니다.
프로 힘내 책은에 정말 좋은 설명이 리베이스 페이지를 .
기본적으로 병합은 두 개의 커밋을 수행하고 결합합니다.
리베이스는 두 사람의 공통 조상으로 가서 서로 위에 변경 사항을 점차적으로 적용합니다. 이것은 더 깨끗하고 선형적인 역사를 만듭니다.
그러나 리베이스하면 이전 커밋을 포기하고 새 커밋을 만듭니다. 따라서 공용 저장소를 리베이스하지 마십시오. 저장소에서 일하는 다른 사람들이 당신을 미워할 것입니다.
그런 이유로 나는 거의 독점적으로 합병합니다. 내 지점의 99 %가 그다지 다르지 않으므로 충돌이있는 경우 한두 곳에서만 발생합니다.
Git rebase는 히스토리 정리 및 저장소 구조의 분기 경로를 선형으로 만드는 데 사용됩니다.
또한 변경 사항을 서버에 적용하고 푸시 한 후 서버에 변경 사항을 적용한 후 분기를 삭제하면 작업 한 분기에 대한 증거가 없으므로 사용자가 만든 분기를 비공개로 유지하는 데 사용됩니다. 따라서 지사는 이제 지역의 관심사입니다.
리베이스를 수행 한 후 정상적인 병합을 수행하는지 확인하는 추가 커밋도 제거합니다.
예, rebase 명령은 rebase 중에 언급 한 브랜치 위에 작업을 배치하고 master와 같이 브랜치의 첫 번째 커밋을 마스터 브랜치의 직계 자손으로 만들기 때문에 여전히 성공적인 rebase 후에 병합을 수행해야합니다. . 이는 이제이 분기에서 마스터 분기로 변경 사항을 가져 오기 위해 빨리 감기 병합을 수행 할 수 있음을 의미합니다.
Gerrit가 대규모 개발과 다소 관련이있는 실제 사례 가 검토 및 전달 통합에 사용되는 :
기능 분기를 새로운 원격 마스터로 향상시킬 때 병합합니다. 이것은 최소한의 향상 작업을 제공하고 예를 들어 gitk 와 같은 기능 개발의 역사를 쉽게 따라갈 수 있습니다.
git fetch
git checkout origin/my_feature
git merge origin/master
git commit
git push origin HEAD:refs/for/my_feature
배달 커밋을 준비 할 때 병합합니다.
git fetch
git checkout origin/master
git merge --squash origin/my_feature
git commit
git push origin HEAD:refs/for/master
배달 커밋이 어떤 이유로 든 통합에 실패하면 리베이스하고 새로운 원격 마스터로 업데이트해야합니다.
git fetch
git fetch <gerrit link>
git checkout FETCH_HEAD
git rebase origin/master
git push origin HEAD:refs/for/master
rebase와 merge가 무엇인지 여러 번 설명되었지만 언제 무엇을 사용해야합니까?
언제 rebase를 사용해야합니까?
Git 리베이스가 역사를 바꾼다. 따라서 다른 사람이 같은 지점에서 작업 할 때 / 밀어 넣은 경우에는 사용하지 마십시오. 그러나 로컬 지점이있는 경우 분기를 다시 마스터로 병합하기 전에 병합 리베이스 마스터를 수행하여 더 깨끗한 기록을 유지할 수 있습니다. 이렇게하면 마스터 브랜치에 병합 한 후에는 마스터 브랜치에서 브랜치를 사용한 것을 볼 수 없습니다. "머지 .."를 자동 생성하지는 않았지만 기록은 "깨끗합니다". "merged .."커밋을 자동 생성하지 않고 마스터 브랜치의 전체 기록.
그러나 git merge feature-branch --ff-only
기능을 기본으로 병합 할 때 단일 커밋을 작성하는 데 충돌이 없는지 확인 하는 데 사용하십시오. 기능 분기의 히스토리를 가져올 때 작업하는 모든 태스크에 대해 기능 분기를 사용하지만 "병합 된 .."커미트가 아닌 경우 이는 흥미 롭습니다.
두 번째 시나리오는 지점에서 분기하고 기본 지점에서 변경된 사항을 알고 자하는 경우입니다. Rebase는 모든 단일 커밋을 포함하므로 정보를 제공합니다.
언제 병합을 사용해야합니까?
마스터 브랜치에 피처 브랜치의 모든 기록이 필요하지 않거나 원하는 경우 또는 다른 브랜치가 동일한 브랜치에서 작업중인 경우 / 추천했습니다. 여전히 히스토리를 유지하려면 기능 분기를 마스터로 병합하기 전에 마스터를 기능 분기로 병합하십시오. 그러면 마스터에 기능 분기 히스토리 (마스터를 병합하여 기능 분기에 있던 병합 커미트 포함)가있는 곳에서 빨리 감기 병합이 수행됩니다.
언제 사용 git rebase
합니까? 역사를 다시 쓰기 때문에 거의 절대로. git merge
프로젝트에서 실제로 일어난 일을 존중하기 때문에 거의 항상 선호하는 선택입니다.