git에서 커밋을 스쿼시한다는 것은 무엇을 의미합니까?


117

Squashing commits in git은 무엇을 의미합니까? Github에서 커밋을 어떻게 스쿼시합니까?

저는 Git을 처음 접했고 coala-analyzer의 새로운 버그에 할당되도록 요청했습니다. 버그를 수정했고 이제 커밋을 스쿼시하라는 요청을 받았습니다. 어떻게하나요?


안녕하세요 @Lakshman은 가장 높은 표를 얻은 답변을 자유롭게 수락하십시오. 귀하의 질문에 제대로 답변합니다.
Mostafiz Rahman

답변:


180

Git은 작업 디렉토리 (들)의 스냅 샷에 대한 고급 데이터베이스라고 생각할 수 있습니다.

Git의 아주 좋은 기능 중 하나는 커밋 기록을 다시 작성할 수 있다는 것입니다.
이렇게하는 주된 이유는 그러한 기록의 상당수가 그것을 생성 한 개발자에게만 관련이 있기 때문에 공유 저장소에 제출하기 전에 단순화하거나 더 멋지게 만들어야하기 때문입니다.

커밋을 스 쿼싱 한다는 것은 관용적 관점에서 해당 커밋에 도입 된 변경 사항을 부모로 이동하여 두 개 (또는 그 이상) 대신 하나의 커밋으로 끝나는 것을 의미합니다.
이 프로세스를 여러 번 반복하면 n 커밋을 단일 커밋으로 줄일 수 있습니다 .

당신이 커밋 태그에서 작업을 시작하면 시각적으로 시작 , 당신이 원하는

Git은 스 쿼싱을 커밋합니다.

새 커밋의 파란색 음영이 약간 더 어둡다는 것을 알 수 있습니다. 이것은 의도적 인 것입니다.

Git에서 스 쿼싱은 Interactive Rebase 라는 특수한 형태 의 Rebase를 사용하여 수행됩니다 . 커밋 세트를 브랜치 B 로 리베이스 할 때 단순화하면 원래 조상이 아닌 B 에서 시작하여 수행 된 커밋에 의해 도입 된 모든 변경 사항을 적용합니다 .

시각적 단서

여기에 이미지 설명 입력

파란색의 다른 음영을 다시 확인하십시오.

대화 형 리베이스를 사용하면 커밋을 리베이스하는 방법을 선택할 수 있습니다. 이 명령을 실행하는 경우 :

 git rebase -i branch

리베이스 할 커밋을 나열하는 파일이 생성됩니다.

 pick ae3...
 pick ef6...
 pick 1e0...
 pick 341...

나는 커밋 이름을하지 못했지만,이 네 사람은에서 커밋되는 것이다 시작머리

이 목록의 좋은 점 은 편집 가능 하다는 것입니다 .
커밋을 생략하거나 스쿼시 할 수 있습니다 .
당신이해야 할 일은 첫 번째 단어를 squash 로 변경하는 것 입니다.

 pick ae3...
 squash ef6...
 squash 1e0...
 squash 341...

편집기를 닫고 병합 충돌이 발견되지 않으면 다음 기록으로 끝납니다.

여기에 이미지 설명 입력

귀하의 경우에는 다른 브랜치로 리베이스하는 것이 아니라 이전 커밋으로 리베이스하고 싶지 않습니다.
첫 번째 예에서와 같이 히스토리를 변환하려면 다음과 같이 실행해야합니다.

git rebase -i HEAD~4

첫 번째 커밋을 제외한 모든 커밋 을 스쿼시 하도록 "명령"을 변경 한 다음 편집기를 닫습니다.


역사 변경에 대한 참고 사항

Git에서는 커밋이 편집되지 않습니다. 잘라낼 수 있고, 연결할 수 없게 만들거나, 복제 할 수는 있지만 변경할 수는 없습니다.
리베이스하면 실제로 새 커밋을 생성하는 것입니다.
이전 항목은 더 이상 심판이 도달 할 수 없으므로 기록에 표시되지 않지만 여전히 존재합니다!

이것은 실제로 rebase를 위해 얻는 것입니다.

여기에 이미지 설명 입력

이미 어딘가에 밀어 넣었다면 역사를 다시 작성하면 실제로 분기가 만들어집니다!


좋은-원래 커밋 주석은 어떻게됩니까-하나의 큰 커밋 주석으로 결합됩니까 아니면 손실됩니까?
Paul R

From man git rebase: 접힌 커밋에 대해 제안 된 커밋 메시지는 첫 번째 커밋과 "squash"명령을 사용하는 커밋 메시지의 연결입니다
Margaret Bloom

1
이것은 내가 리베이스에 대해 본 최고의 설명입니다. 감사합니다.
Kerry Jones

첫 번째 이미지의 스 쿼싱에 대해 : 스 쿼싱 이전 (연한 파란색)과 이후 (진한 파란색) HEAD에 다른 작업 디렉토리가있는 예를 만들 수 있습니까? 모든 예제에서 스 쿼싱을 시도한 것은 START와 HEAD 사이의 세 커밋을 삭제 한 것처럼 보였습니다.
actual_panda

@actual_panda 내가 따르는 지 잘 모르겠습니다. HEAD는 커밋에 대한 참조입니다. 매장 델타를 커밋합니다. 작업 디렉토리는 저장소 전체의 속성이며 체크 아웃 한 커밋에 따라 다릅니다. 일반적으로 두 개의 ref (예 : HEAD 및 START)는 체크 아웃 할 때 항상 두 개의 다른 작업 디렉토리를 제공합니다. 동일한 브랜치에서 스쿼시를 리베이스하는 경우 중간 커밋을 "느슨하게"하는 효과가 있지만 실제로는 git이 모든 델타로 새 커밋을 만들었습니다. git diff무슨 일이 있었는지 보여줄 수 있습니다.
Margaret Bloom

22

rebase 명령에는 --interactive(또는 -i) 모드 에서 사용할 수있는 몇 가지 멋진 옵션 이 있으며 가장 널리 사용되는 옵션 중 하나는 커밋을 스쿼시하는 기능입니다. 이것이하는 일은 작은 커밋을 더 큰 커밋으로 결합하는 것입니다. 이는 하루의 작업을 마무리하거나 변경 사항을 다르게 패키징하려는 경우 유용 할 수 있습니다. 이 작업을 쉽게 수행 할 수있는 방법을 살펴 보겠습니다.

주의 사항 : 외부 저장소로 푸시되지 않은 커밋에만이 작업을 수행하십시오. 다른 사람이 삭제할 커밋을 기반으로 작업을 수행하면 많은 충돌이 발생할 수 있습니다. 다른 사람과 공유 한 기록을 다시 쓰지 마십시오.

이제 몇 개의 작은 커밋을 수행했고 그중 하나를 더 큰 커밋으로 만들고 싶다고 가정 해 보겠습니다. 우리 저장소의 역사는 현재 다음과 같습니다 :

여기에 이미지 설명 입력

마지막 4 개의 커밋은 함께 묶어두면 훨씬 더 행복 할 것이므로 대화식 리베이스를 통해 그렇게합시다.

$ git rebase -i HEAD~4

pick 01d1124 Adding license
pick 6340aaa Moving license into its own file
pick ebfd367 Jekyll has become self-aware.
pick 30e0ccb Changed the tagline in the binary, too.

# Rebase 60709da..30e0ccb onto 60709da
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

그래서 여기서 몇 가지 일이 일어났습니다. 먼저 HEAD가 HEAD ~ 4 인 마지막 4 개의 커밋을 사용하여 리베이스하고 싶다고 Git에게 말했습니다. Git은 이제 위의 텍스트와 함께 수행 할 수있는 작업에 대한 약간의 설명이있는 편집기에 나를 넣었습니다. 이 화면에서 많은 옵션을 사용할 수 있지만 지금은 모든 것을 하나의 커밋으로 스쿼시 할 것입니다. 따라서 파일의 처음 네 줄을 이렇게 변경하면 트릭이됩니다.

pick 01d1124 Adding license
squash 6340aaa Moving license into its own file
squash ebfd367 Jekyll has become self-aware.
squash 30e0ccb Changed the tagline in the binary, too.

기본적으로 이것은 Git에게 네 개의 커밋을 모두 목록의 첫 번째 커밋으로 결합하도록 지시합니다. 이 작업이 완료되고 저장되면 다음과 같은 다른 편집기가 나타납니다.

# This is a combination of 4 commits.
# The first commit's message is:
Adding license

# This is the 2nd commit message:

Moving license into its own file

# This is the 3rd commit message:

Jekyll has become self-aware.

# This is the 4th commit message:

Changed the tagline in the binary, too.

    # Please enter the commit message for your changes. Lines starting
    # with '#' will be ignored, and an empty message aborts the commit.
    # Explicit paths specified without -i nor -o; assuming --only paths...
    # Not currently on any branch.
    # Changes to be committed:
    #   (use "git reset HEAD <file>..." to unstage)
    #
    #   new file:   LICENSE
    #   modified:   README.textile
    #   modified:   Rakefile
    #   modified:   bin/jekyll
    #

너무 많은 커밋을 결합하고 있기 때문에 Git을 사용하면 프로세스에 포함 된 나머지 커밋을 기반으로 새 커밋의 메시지를 수정할 수 있습니다. 원하는대로 메시지를 편집 한 다음 저장하고 종료하십시오. 완료되면 커밋이 성공적으로 처리되었습니다!

Created commit 0fc4eea: Creating license file, and making jekyll self-aware.
 4 files changed, 27 insertions(+), 30 deletions(-)
  create mode 100644 LICENSE
    Successfully rebased and updated refs/heads/master.

그리고 우리가 다시 역사를 보면 ... 여기에 이미지 설명 입력

그래서 이것은 지금까지 비교적 고통스럽지 않았습니다. 리베이스 중에 충돌이 발생하면 일반적으로 해결하기가 매우 쉽고 Git이 가능한 한 많이 안내합니다. 이것의 기본은 문제의 충돌, git add파일을 수정 한 다음 git rebase --continue프로세스를 재개하는 것입니다. 물론 git rebase --abort원하는 경우 a 를 수행하면 이전 상태로 돌아갈 수 있습니다. 어떤 이유로 리베이스에서 커밋을 잃어버린 경우 reflog를 사용하여 되돌릴 수 있습니다.

자세한 내용은이 링크 에서 찾을 수 있습니다 .


3
감사합니다! 누구든 나처럼 VIM을 불편하게 여기는 경우 ... 편집 할 작업을 입력하는 지점에 도달하면 'i'를 눌러 편집 모드로 들어갑니다. 변경을 마쳤 으면 이스케이프를 누른 다음 ": wq"를 입력하면 앞으로 이동합니다. (Mac)
Farasi78

이 작업을 수행해야하는 경우의주의 및 컨텍스트에 +1. 내 개인적인 경험상, 기능 브랜치에서 혼자 작업하고 "업데이트 된 readme", "아무도 신경 쓰지 않는 일"과 같은 사소한 커밋이 많고 브랜치를 병합 할 준비가 된 경우 좋습니다. 이 모든 것을 하나의 커밋으로 스쿼시하십시오. 아무도 당신의 "아무도 신경 쓰지 않는 일을했다"로 되돌리고 싶지 않을 것이며 커밋 기록을 오염시킵니다
Adam Hughes

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.