- git-merge : Join two or more development histories together.
- git-rebase : Forward-port local commits to the updated upstream head.
Git 매뉴얼을 보면 위와같이 정의가 되어 있고 Git, 분산 버전 관리 시스템을 보면 git merge는 바로 합치기로 git rebase는 브랜치 이력 재정렬하기로 되어 있는데 결과적으로는 둘다 합쳐지는 건데 명확한 차이점이 잘 구분되지 않았습니다. 이런 저런 문서나 설명을 봐도 명확히 잘 이해가 되지 않아서 간단히 테스트를 해보았습니다.
일단 파일을 2개 준비했습니다. one.txt와 two.txt 2개의 파일을 만들고 각 라인마다 한글자씩 one.txt는 1~10까지 쓰고 two.txt에는 a~j까지 작성했습니다. master 브랜치에서 여기까지 커밋을 하고 slave 브랜치를 만들어서 두 파일에서 각각 2번에 걸쳐서 한라인씩 삭제를 하고 다시 master브랜치에서 파일마다 한라인씩 삭제했습니다.
그 결과는 위와 같습니다.(시간대가 정확히 안나오는데 master의 위의 2개의 커밋은 시간순으로는 slave 커밋 이후에 처리된 커밋입니다.) Author에 Insider로 된 커밋들은 slave에서 커밋된 내용들이고 일부러 커미터를 다르게 하였습니다.
이제 master 브랜치에서 slave브랜치를 머지합니다. 변경내역이 합쳐진 것을 볼 수 있습니다.
커밋히스토리를 master에서 보면 위와 같습니다. 시간대상으로는 master상의 최근 커밋 2개가 slave 쪽 커밋들보다 늦은 커밋이지만 트리로그는 새로 머지한 것들이 최근 커밋들로 합쳐지고 마지막에 merge brance 커밋이 하나더 추가되었고 트리그래프가 브랜치되고 머지된 내역을 표시해 주고 있습니다.
이번에는 rebase입니다. 마찬가지로 master 브랜치에서 slave 브랜치를 rebase했습니다. merge때와는 로그가 다르게 나타납니다.
위와같이 시간순서대로 정렬된 커밋내역이 나타나고 트리그래프도 브랜치에 상관없이 일렬로 나오는 것을 볼 수 있습니다.
사실 이 결과는 제가 기대했던 rebase의 결과와는 약간 다릅니다. rebase라는 명령어는 사실상 브랜치의 기준이 되는 base를 다시 잡아주는 것인데 저는 master를 최신커밋 위에 slave 커밋들이 재정렬되기를 기대했는데 그렇게 되지 않았습니다. 이는 사실 rebase의 대상이 반대로 되었기 때문입니다. master에서 빠져나온 slave 브랜치를 다시 master의 HEAD기준으로 rebase하려면 slave 브랜치에서 master를 rebase해주어야 하는데 저는 반대로 했기 때문에 위와 같은 결과가 나온 것입니다.
그래서 다시 테스트를 했습니다. 앞의 테스트와 동일한 환경에서 master에 3개의 커밋을 더 추가했습니다.(just now로 표시된) slave의 커밋한 내용과 동일한 수정내용을 가지는 2개의 커밋을 더 하고 새로운 수정내용을 하나더 커밋했습니다.
이번에는 slave에서 master를 머지했습니다.
slave기준으로 보면 slave의 커밋내역이후에 master의 커밋내역이 머지가 되고 머지커밋도 추가로 생기는 것을 볼 수 있습니다.
이번엔 slave에서 master를 rebase했습니다. 로그를 보면 2개의 커밋에 대해서는 No changes -- Patch already applied라고 나오는 것을 볼 수 있습니다.
이번엔 원하는대로 rebase가 되었습니다. master의 HEAD를 기준으로 slave쪽에 있던 4개의 커밋이 재적용되었고 동일한 변경내역인 2개의 커밋은 이미 master에 존재하기 때문에 사라져버려서 결과적으로 히스토리에는 2개의 slave커밋만 적용되었습니다.
위의 명령에 대한 실행결과를 보면 rebase의 동작방식이 꽤 명확한 하게 이해할 수 있는데 master에서 중간에 브랜치되어서 4건의 커밋을 갖게된 slave를 현재의 master를 기준으로 rebase하니 master의 HEAD를 기준으로 4건의 커밋을 재적용하였고 master의 커밋과 겹치는 커밋들은 적용안되는 것을 볼 수 있습니다.
Git 매뉴얼의 rebase설명부분에 설명이 잘 되어 있습니다.(내부 동작에 대한 설명이 많은데 트리 그림있는 부분만 보셔도 될듯 합니다.) 그럼 merge랑 rebase를 어떻게 구별해서 사용해야 하는가라는 의문이 남습니다. A Rebase Workflow for Git를 보면 히스토리 관리를 별로 신경쓰지 않고 혼자서만 커밋하거나 아니면 믿을수 있는 소수만 커밋하지 않는다면 merge대신 rebase를 사용하라고 권장하고 있습니다.
예를 들어 A라는 공개 프로젝트를 clone받아서 제가 수정을 하게 된다면 master에서 직접 수정하지 않고 브랜치를 분리해서 작업을 하고나서 커밋하기 전에 rebase를 하고 푸시를 하는 방식을 취할 수 있습니다. 이렇게 할 경우 같은 내역을 다른 커미터가 먼저 커밋했을때 해당 커밋을 유지시키고 자신이 수정한 커밋은 생략되어서 히스토리를 깔끔하게 유지할 수 있고 필요가 없는 merge 커밋같은 것이 남지 않습니다.
그렇군요!
나도 이번에 처음 알았어... ㅎ
아하! 그렇군요!
hg를 더 많이 쓰시지 않나요? ㅋ
어이쿠 어렵군요! 브랜치는 딸 일이 없기만을 바랄 뿐.. ^^
저도 브랜치안쓰다가 DVCS에서는 브랜치위주로 작업을 보통하는것 같아서 연습해보고 있습니다. ㅎㅎㅎ
엇, 정리했군! 아쉽다... 압박할 수 있는 다른 소재를 찾아야겠네.
역시 못보고 얘기하신거였군요 ㅎㅎ
엇! 안녕하세요!
마지막예제에서 slave 내용을 다시 master 에 적용시키려면 master 에서 slave 를 rebase 하면 트리 그래프가 예쁘게 나올것 같지 않은데요. merge 보다는 rebase 를 권장한다기에 궁금증이 생겨서 고민해봤는데요.
slave 브랜치에 master 내용을 적용하고 싶을때는 rebase 를 사용하고 master 에 slave 브랜치 내용을 적용하고자 할때는 merge 를 사용하는게 좋을것 같은데 어떤가요? git 을 저도 개인프로젝트로만 사용중이라 github 의 jquery-mobile, rails 프로젝트를 봤더니 master 에 브랜치를 적용시킬때 merge 를 사용하기에 궁금증이 더해졌습니다.
그부분은 추가로 확인해보지는 않았습니다만 리베이싱을 해도 그래프는 이쁘게 나올것 같습니다. 다만 slave에서 리베이싱을 하고 master에서 다시 리베이싱을 해야겠지요. 그래프는 이미 master의 커밋들은 slave측에 모두 적용되어 있기 때문에 master측에서 리베이싱을 하면 아마 slave의 트리그래프와 동일한 모양으로 master쪽에도 생길꺼라고 생각합니다.
사실 무조건이라는건 없을꺼라 생각합니다. 그렇다면 rebase만 있고 merge라는 것은 필요가 없었을 것 같은데요. 위에서 말한 흐름상이나 일반적인 경우에는 rebase가 더 적절하지 않나 생각하게 된거구요 좀더 디테일한 부분은 실제로 사용해보면서 필요한 경우가 어떤경우가 있는지 느껴봐야 할것 같습니다. 그냥 단순히 slave를 master로 적용하기 위해서라면 (2번해야하지만)리베이싱이 더 낫지 않나 생각중입니다.
다른 경우가 있으시면 말씀해 주시면 감사~
안녕하세요 궁금증이 있어 올립니다.
merge를 하면 merge commit이 생겨 혹시나 잘못 merge를 했을 경우 merge commit 만 reset 하면 적용된 다른 commit 도 제거되던데
rebase를 하면 다시 복원 하려면 해당 commit을 모두 reset 해줘야 하는것 같은데 이점에 있어서는 merge가 더 안정적이지 않을까요?
저는 롤백까지는 생각해 보지 않았는데요.. 테스트 해보진 않았지만 안정적의 문제보다는 명령어를 몇번입력해서 복원하느냐의 문제가 아닌가 싶습니다. 안정성과는 관계가 없지 않을까요? 해당행위를 머지로 생각치 않고 각각의 커밋이라고 생각하면 다른 커밋과 똑같이 대해지는것 같습니다.
그리고 일단 푸쉬가 된 다음에 커밋을 롤백하는건 이슈가 크기 때문에 다른 문제로 보입니다. 로컬이라면 혼자의 문제니까 큰 상관이 없구요.
안녕하세요 좋은 정보 감사합니다.. ㅎㅎ
그런데 위에 commit tree 랑 commit message 나오는 프로그램은 어떤 프로그램 인가요??
예 안녕하세요.
git tower라는 맥용 git 클라이언트입니다.
아... 감사합니다... 혹시 linux 용이나 windows 용도 있나요??
사실 git은 윈도우지원은 좀 형편없는 편이고요. linux정도는 있을것 같기도 한데 저는 command위주로 써서 클라이언트는 잘 모르겠네요.
리눅스는 gitg가 비슷할 겁니다.
rebase 사용시에는 큰 문제점이 있습니다.
rebase시에는 기존에 생성된 commit의 commit-id가 다시 생성된다는 것입니다.
base가 될 branch에 포함되지 않은 새로운 commit들을 base branch의 head에 다시 diff id에 의해서 집어 넣기 때문에 commit이 새로 생성됩니다.
따라서... 이미 git을 공유하고 있는 다른 개발자가 해당 commit object를 fetch해 갔을 경우는 rebase를 쓰시면 문제가 발생할 수 있습니다.
rebase는 자신의 local에 생성한 topic 브랜치의 작업 commit을 push하기 전에 master branch에 rebase하여 graph를 linear하게 만들어 준 뒤에 push하는데 많이 사용합니다.
그러면 개발자들간 공유 시에는 예쁜 graph를 유지 할 수 있습니다.
좋은 지적 감사합니다.
이미 공유된 커밋에 대해서는 그런 문제가 있을 수 있겠군요. 기본적인 시나리오상은 마지막에 말씀하신 것처럼 커밋하기 전에 리베이스를 통해서 커밋을 정렬하고 푸쉬를 하는 시나리오가 적당한듯 합니다.
안녕하세요..
git client 를 사용하시면서 repo 도 설치해서 사용하시나요? windows client인
git extensions 을 사용 하려고 하는데 repo 설치를 어떻게 해야 하는지를 잘 몰라서요..
repo를 설치한다는게 리모트 저장소를 말씀하시는건가요?
저같은 경우는 리모트가 필요한 경우에는 github를 이용하고 있습니다. 딱히 private로 해야하는건 없어서요. git는 리모트는 그냥 서버에 그냥 설치하고 저장소만 있으면 되는 것으로 알고 있는데 해본적은 없습니다.
좋은 내용 감사합니다.
오타도 약간보이고, 번역서를 읽는 기분이 드는 문장이 좀 보이는데, 리비전을 올려가며 수정해주시면 좋겠네요. ^^ 감사합니다.
블로그라서 리비전 관리기능은 없네요.
신경써서 쓰기는 하는데 온라인이다보니 인쇄물보다는 문장검토에 신경쓰지는 않곤 합니다. 거기까지 신경쓰면 시간이 너무 투자되서요 ㅠㅠ
어떤 부분이 잘못되었는지지적해 주시면 참고해서 적용은 하곤 합니다.
좋은 개념 정리와 좋은 예제 감사합니다.^_^
네 ^^
"아니면 믿을수 있는 소수만 커밋하지 않는다면" 이 부분이 조금 햇갈리는데요. "http://git-scm.com/book/en/v2/Git-Branching-Rebasing"(원본 과 한글번역본) 를 참고하면서 느낀 것과는 달라서요. 저는 믿을 수 있는 소수(즉, 혼자인 경우도 포함)만 로컬에 커밋하고 원격에 푸시하는 상황이면 리베이스가 더 권장 된다는 생각입니다. 위 댓글에도 누군가 설명해주셨는데 rebase 는 자신의 커밋 히스토리를 말 그대로 "이쁘게" 만들기 위해 쓰는게 좋다는게 제 결론입니다. 레퍼런스에서 인용하자면 "Do not rebase commits that exist outside your repository." 라고 즉, 원격에 있는 남들의 커밋 히스토리까지 "이쁘게" 만들려고 리베이스를 했다가는 같은 내용의 다른 id 만 가지고 있는 중복되는 커밋들 때문에 다른 개발자들이 고통받을 수 있다고 하는 것 같네요.
말을 좀 애매하게 쓰긴 했네요. 아래 문장을 보고 저렇게 적은 것이네요.
The merge workflow will do you no damage at all if you only have one committer (or a very small number of committers, and you trust them all)
저 당시에는 merge, rebase를 처음 공부하던 상황인데 제가 지금 이해하기로는 여기서는 단순 merge, rebase보다 워크플로우 관점으로 얘기하고 있는 것 같네요. 혼자서 한다면 상관없지만 여럿이 하는 프로젝트에서 merge 워크플로우만 사용한다면 히스토리를 이해하기 어려울 정도로 꼬이기 때문에 저렇게 설명했다고 봅니다.
사실 rebase냐 merge냐는 정답은 없긴 하지만 지금은 둘다 비슷한 빈도로 사용하는 편이고 관점은 no-ff냐 ff냐로 구분하는 편입니다. 그래서 no-ff로 머지하는 경우에도 rebase를 한 후에 merge를 진행하고 있습니다.(그렇지 않으면 히스토리를 보기가 아주 어렵습니다.) 프로젝트마다 다르긴 하지만 ff로 머지하는 경우에도 rebase를 진행한 후에야 merge를 할 수 있죠.
말씀하신 것처럼 푸시한 커밋을 rebase하는 것은 git에서 거의 하지 않도록 하고 있고 히스토리를 이쁘게 만드려면 rebase가 반드시 필요합니다. 이 과정에서 푸시한 커밋을 rebase하는 것은 어떤 flow를 사용하던 가능한한 하지 않아야 한다고 봅니다.
git 공부하다가 rebase/merge 설명이 헷갈려서 검색해보다가 이 글 읽었습니다.
설명 감사합니다. 잘 읽고 갑니다.
git에서 중요한 개념인데 헷갈리는 부분이기도 하죠. ^^
깃 공부하다가 헷갈렸는데 감사합니다.
네 방문해 주셔서 감사합니다.