Git을 사용하여 마지막 X 커밋을 스쿼시하십시오.


3586

마지막 X 커밋을 Git을 사용하여 하나의 커밋으로 어떻게 스쿼시 할 수 있습니까?




2
@matt TortoiseGit이 당신의 도구입니다. 백그라운드에서 모든 단계를 자동으로 호출하는 단일 기능 "Commine to one commit"을 제공합니다. 불행히도 Windows에서만 사용할 수 있습니다. 아래 답변을 참조하십시오.
Matthias M

1
- 개까지 부수 대해 먼저 참조 커밋 stackoverflow.com/questions/1657017/...
goelakash

1
스쿼시 포스트 강제 밀어 넣기 stackoverflow.com/questions/10298291/…
vikramvi

답변:


2092

매뉴얼에git rebase -i <after-this-commit> 설명 된대로 두 번째 및 후속 커밋에서 "pick"을 사용 하고 "squash"또는 "fixup"으로 바꿉니다 .

이 예에서, <after-this-commit>rebase 명령에 대한 커밋이 분석되는 현재 분기의 HEAD로부터의 SHA1 해시 또는 상대 위치입니다. 예를 들어, 사용자가 과거에 현재 HEAD에서 5 개의 커밋을 보려면 명령은 git rebase -i HEAD~5입니다.


260
이것은 생각에,이 질문에 조금 더 나은 답변 stackoverflow.com/a/5201642/295797
Roy Truelove

92
무엇을 의미 <after-this-commit>합니까?
2540625

43
<after-this-commit>커밋 X + 1, 즉 스쿼시하려는 가장 오래된 커밋의 부모입니다.
joozek

339
이 답변이 너무 간결하여 이해하기 어려웠습니다. 예가 도움이되었을 것입니다.
Ian Ollmann 2016 년

53
이 차이 rebase -i접근하고 reset --soft,되는 rebase -i동안, 나를이 저자를 저지 유지할 수 있습니다 reset --soft나를 재회 할 수 있습니다. 때로는 저자 정보를 유지하면서 풀 요청 커밋을 스쿼시해야합니다. 때로는 내 커밋에서 소프트를 재설정해야합니다. 어쨌든 두 가지 큰 답변 모두에 찬성합니다.
zionyx

3826

git rebase또는 없이이 작업을 상당히 쉽게 수행 할 수 있습니다 git merge --squash. 이 예에서는 마지막 3 개의 커밋을 스쿼시합니다.

새로운 커밋 메시지를 처음부터 작성하려면 다음과 같이 충분합니다.

git reset --soft HEAD~3 &&
git commit

기존 커밋 메시지를 연결하여 새 커밋 메시지 편집을 시작하려면 (예 : pick / squash / squash /… / squash git rebase -i명령어 목록 과 유사 ) 해당 메시지를 추출하여 전달해야합니다. 그들에게 git commit:

git reset --soft HEAD~3 && 
git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"

이 두 가지 방법 모두 마지막 3 개의 커밋을 같은 방식으로 하나의 새로운 커밋으로 뭉칩니다. 소프트 리셋은 스쿼시하지 않으려는 마지막 커밋으로 HEAD를 다시 가리 킵니다. 소프트 재설정으로 인덱스와 작업 트리를 건드리지 않고 새 커밋에 대해 원하는 상태로 인덱스를 유지합니다 (즉, 이미 "버려 버리려는 커밋의 모든 변경 사항이 이미 있습니다").


163
하아! 나는이 방법을 좋아한다. 문제의 정신에 가까운 사람입니다. 너무 많은 부두가 필요한 것은 유감입니다. 이와 같은 것이 기본 명령 중 하나에 추가되어야합니다. 아마도 git rebase --squash-recent, 또는 심지어 git commit --amend-many.
Adrian Ratnapala

13
@ABB는 : 지사는 "상류"세트가있는 경우, 당신은 사용할 수 있습니다 branch@{upstream}(또는 @{upstream}현재의 지점에 대해, 두 경우 모두, 마지막 부분에 생략 할 수있다 @{u}; 참조 gitrevisions을 ). 이 다를 수 있습니다 귀하의 "커밋 밀어 마지막으로"(예를 들어 다른 사람이 가장 최근의 푸시 꼭대기에 건설 한 다음 그것을 가져온 것을 무언가를 밀어 경우),하지만 가까이 당신이 원하는에있을 것 같다.
Chris Johnsen

104
이 일종의 소르 타는 저를 요구 push -f했지만 그렇지 않으면 사랑 스럽습니다. 감사합니다.
2rs2ts

39
@ 2rs2ts git push -f 소리가 위험합니다. 스쿼시 지역 커밋에만주의하십시오. 푸시 된 커밋을 건드리지 마십시오!
Matthias M

20
또한 git push --force커밋을 수행 할 수 있도록 나중에 사용해야 합니다.
Zach Saucier

738

git merge --squash이보다 약간 더 우아하게 사용할 수 있습니다 git rebase -i. 당신이 마스터에 있고 마지막 12 개의 커밋을 하나로 묶고 싶다고 가정 해보십시오.

경고 : 먼저 작업을 커밋해야합니다. 단계적이거나 단계적이지 않은 변경 사항을 버릴 git status것이므로 깨끗한 지 확인하십시오.git reset --hard

그때:

# Reset the current branch to the commit just before the last 12:
git reset --hard HEAD~12

# HEAD@{1} is where the branch was just before the previous command.
# This command sets the state of the index to be as it would just
# after a merge from that commit:
git merge --squash HEAD@{1}

# Commit those squashed changes.  The commit message will be helpfully
# prepopulated with the commit messages of all the squashed commits:
git commit

에 대한 문서는git merge 설명 --squash자세히 옵션을 선택합니다.


업데이트 :git reset --soft HEAD~12 && git commit Chris Johnsen이 그의 대답 에서 제안한 것보다 더 간단한이 방법의 유일한 장점 은 스쿼시하는 모든 커밋 메시지로 커밋 메시지가 미리 채워져 있다는 것입니다.


16
당신은 이것이보다 '우아한'것이라고 말하지만 git rebase -i이유를 제시하지는 않습니다. 실제로 반대의 경우가 사실이며 이것이 해킹 인 것 같습니다. 특별히 설계된 git merge것 중 하나를 수행 하기 위해 필요한 것보다 많은 명령을 수행하지 git rebase않습니까?
Mark Amery

76
@ Mark Amery : 이것이 더 우아하다고 여러 가지 이유가 있습니다. 예를 들어, 불필요하게 편집기를 생성 한 다음 "할 일"파일에서 문자열을 검색하고 교체하지 않아도됩니다. git merge --squash스크립트에서도 사용하기 가 더 쉽습니다. 본질적으로, 추론은 당신이 전혀 "상호 작용"을 필요로하지 않기 때문 git rebase -i입니다.
Mark Longair

12
또 다른 장점은 git merge --squash특히 로컬 지점에서 병합하는 경우 rebasing에 비해 이동 / 삭제 / 이름 변경시 병합 충돌이 발생할 가능성이 적다는 것입니다. (면책 조항 : 하나의 경험 만 바탕으로, 일반적인 경우에 해당되지 않는 경우 수정하십시오!)
Cheezmeister

2
하드 리셋에 대해서는 항상 매우 꺼려합니다. HEAD@{1}예를 들어 워크 플로가 정전 등으로 1 시간 동안 중단 된 경우와 같이 안전한면이 아닌 임시 태그를 사용합니다 .
Tobias Kienzler

8
@BT : 커밋을 파괴 했습니까? :( 당신이 무슨 뜻인지 잘 모르겠습니다. 커밋 한 모든 것은 git의 reflog에서 쉽게 돌아올 수 있습니다. 커밋되지 않은 작업이 있었지만 파일이 준비 되었더라도 여전히 얻을 수 있어야합니다. 더 많은 작업을하게 되더라도 내용을 되돌릴 수 있습니다 .하지만 아직 작업이 완료되지 않은 경우 수행 할 수있는 작업이 거의 없을 것입니다. (git reset 이후
하드는

218

git reset가능하면 특히 Git-novices를 피하는 것이 좋습니다 . 많은 커밋을 기반으로 프로세스를 자동화 해야하는 경우가 아니라면 덜 이국적인 방법이 있습니다 ...

  1. 작업중인 분기에 스쿼시 된 커밋을 넣습니다 (아직없는 경우)-gitk를 사용하십시오.
  2. 대상 지점을 확인하십시오 (예 : '마스터')
  3. git merge --squash (working branch name)
  4. git commit

커밋 메시지는 스쿼시를 기반으로 미리 채워집니다.


4
이것은 가장 안전한 방법입니다. 재설정 소프트 / 하드 (!!) 또는 reflog 사용 없음!
TeChn4K

14
(1)에서 확장하면 좋을 것입니다.
Adam

2
@Adam : 기본적으로 이것은 GUI 인터페이스를 사용하여 gitk스쿼시중인 코드 줄에 레이블을 지정하고 스쿼시 할 기준에 레이블을 지정합니다. 일반적인 경우 두 레이블이 모두 존재하므로 단계 (1)을 건너 뛸 수 있습니다.
nobar

3
이 방법은 작업 분기가 완전히 병합 된 것으로 표시되지 않으므로 제거하면 강제로 삭제해야합니다. :(
Kyrstellaine

2
(1)의 git branch your-feature && git reset --hard HEAD~N경우 가장 편리한 방법을 찾았 습니다. 그러나 git reset을 다시 포함 하므로이 답변은 피하려고했습니다.
eis

132

을 바탕으로 크리스 욘센의 대답은 ,

bash에서 전역 "스쿼시"별칭 추가 : (또는 Windows의 Git Bash)

git config --global alias.squash '!f(){ git reset --soft HEAD~${1} && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; };f'

... 또는 Windows 명령 프롬프트 사용 :

git config --global alias.squash "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"


당신은 ~/.gitconfig지금이 별칭을 포함해야한다 :

[alias]
    squash = "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"


용법:

git squash N

... 마지막 N커밋을 자동으로 묶습니다 .

참고 : 결과 커밋 메시지는 모든 스와치 된 커밋을 순서대로 조합 한 것입니다. 마음에 들지 않으면 언제든지 git commit --amend수동으로 수정할 수 있습니다. 또는 취향에 맞게 별칭을 편집하십시오.


6
흥미롭지 만, 저의 커밋 메시지를 자동으로 입력하는 것보다 여러 커밋에 대한 설명 요약으로 직접 입력하고 싶습니다. 따라서 푸싱되지 않은 커밋의 수를 자동으로 지정 git squash -m "New summary."하고 N결정했습니다.
Acumenus

1
@ABB, 이것은 별도의 질문처럼 들립니다. (나는 그것이 OP가 요구 한 것과 정확히 같지 않다; 나는 git squash workflow에서 그것을 필요로 느낀 적이 없다.)
EthanB

3
꽤 달콤합니다. 개인적으로 첫 번째 커밋 된 커밋에서 커밋 메시지를 사용하는 버전을 원합니다. 공백 조정과 같은 것들에 좋습니다.
funroll

@funroll 동의합니다. 마지막 커밋 메시지를 삭제하는 것이 가장 일반적인 요구입니다. 우리는 그것을 고안 할 수 있어야합니다 ...
Steve Clay

2
@ABB를 사용 git commit --amend하면 메시지를 추가로 변경할 수 있지만이 별칭을 사용하면 커밋 메시지에 포함되어야 할 내용을 잘 시작할 수 있습니다.
dashesy

131

이 편리한 블로그 게시물 덕분 에이 명령을 사용하여 마지막 3 개의 커밋을 스쿼시 할 수 있음을 발견했습니다.

git rebase -i HEAD~3

추적 정보 / 원격 리포지토리가없는 로컬 지점에 있어도 편리합니다.

이 명령은 대화식 rebase 편집기를 열고 평소대로 재정렬, 스쿼시, 리워드 등을 수행 할 수 있습니다.


대화식 rebase 편집기 사용 :

대화식 rebase 편집기는 마지막 3 개의 커밋을 보여줍니다. 이 제한 조건은 HEAD~3명령을 실행할 때 결정됩니다 git rebase -i HEAD~3.

가장 최근의 커밋 HEAD이 첫 번째 줄에 먼저 표시됩니다.로 시작하는 줄 #은 주석 / 문서입니다.

표시되는 문서는 매우 명확합니다. 주어진 행 pick에서 명령을 원하는 명령으로 변경할 수 있습니다 .

fixup이 명령 은 커밋 변경 사항을 위의 줄에 커밋으로 "제곱"하고 커밋 메시지를 삭제 하기 때문에 명령을 사용하는 것을 선호 합니다.

1 행의 커밋 HEAD이 대부분이므로 대부분의 경우이를 그대로 둡니다 pick. 당신은 사용할 수 없습니다 squash또는 fixup이 있기 때문에 다른이 (가)에 커밋 스쿼시 것을 약속합니다.

커밋 순서를 변경할 수도 있습니다. 이를 통해 연대순으로 인접하지 않은 커밋을 스쿼시하거나 수정할 수 있습니다.

대화식 리베이스 편집기


실용적인 일상 예

최근에 새로운 기능을 커밋했습니다. 그 이후로 두 가지 버그 수정을했습니다. 그러나 이제는 내가 저지른 새로운 기능에서 버그 (또는 철자 오류)를 발견했습니다. 짜증나! 커밋 기록을 오염시키는 새로운 커밋을 원하지 않습니다!

내가하는 첫 번째 일은 실수를 고치고 코멘트로 새로운 커밋을하는 것 squash this into my new feature!입니다.

그런 다음 새로운 기능 (이 경우 ) 의 커밋 SHA 를 실행 git log하거나 gitk얻습니다 1ff9460.

다음으로를 사용하여 대화식 rebase 편집기를 불러옵니다 git rebase -i 1ff9460~. ~커밋 후 SHA 편집기에서 커밋이 포함 편집기를 알려줍니다.

다음으로 수정 ( fe7f1e0)이 포함 된 커밋을 기능 커밋 아래 로 이동 하고로 변경 pick합니다 fixup.

편집기를 닫을 때 수정 사항이 기능 커밋으로 찌그러지고 커밋 기록이 멋지고 깨끗하게 보입니다!

이것은 모든 커밋이 로컬 인 경우 잘 작동하지만 이미 원격으로 푸시 된 커밋을 변경하려고하면 동일한 분기를 체크 아웃 한 다른 개발자에게 실제로 문제가 발생할 수 있습니다!

여기에 이미지 설명을 입력하십시오


5
당신은 상단 하나를 선택하고 나머지를 스쿼시해야합니까? 대화 형 리베이스 편집기를 사용하는 방법을 자세히 설명하려면 답변을 편집해야합니다.
Kolob Canyon

2
예, pick1 행 에 그대로 두십시오 . squash또는 fixup1 행에서 커밋을 선택하면 git은 "오류 : 이전 커밋없이 '수정'할 수 없습니다"라는 메시지를 표시합니다. 그런 다음 수정 옵션을 제공합니다. " 'git rebase --edit-todo'로이 문제를 해결 한 다음 'git rebase --continue'를 실행할 수 있습니다." 또는 중단하거나 다시 시작할 수 있습니다. "또는 'git rebase --abort'로 리베이스를 중단 할 수 있습니다."
br3nt

56

TortoiseGit을 사용하면 다음과 같은 기능을 수행 할 수 있습니다 Combine to one commit.

  1. TortoiseGit 컨텍스트 메뉴 열기
  2. 고르다 Show Log
  3. 로그보기에서 관련 커밋을 표시하십시오.
  4. Combine to one commit상황에 맞는 메뉴에서 선택

커밋 결합

이 기능은 필요한 모든 단일 git 단계를 자동으로 실행합니다. 불행히도 Windows에서만 사용할 수 있습니다.


내가 아는 한 병합 커밋에는 작동하지 않습니다.
Thorkil Holm-Jacobsen

1
다른 사람들은 주석을 달지 않았지만 HEAD에없는 커밋에도 적용됩니다. 예를 들어, 필자는 푸시하기 전에 더 깔끔한 설명으로 수행 한 일부 WIP 커밋을 스쿼시해야했습니다. 아름답게 일했습니다. 물론, 나는 여전히 명령으로 그것을 수행하는 방법을 배울 수 있기를 바랍니다.
Charles Roberto Canato

55

이를 위해 다음 git 명령을 사용할 수 있습니다.

 git rebase -i HEAD~n

n (= 4 here)은 마지막 커밋 수입니다. 그런 다음 다음 옵션을 얻습니다.

pick 01d1124 Message....
pick 6340aaa Message....
pick ebfd367 Message....
pick 30e0ccb Message....

아래와 같이 업데이트 pick한 커밋 squash가장 최근에 다른 사람,

p 01d1124 Message....
s 6340aaa Message....
s ebfd367 Message....
s 30e0ccb Message....

자세한 내용은 링크를 클릭하십시오


48

이 기사를 바탕 으로이 방법이 사용 사례에서 더 쉽다는 것을 알았습니다.

내 'dev'브랜치는 96 커밋으로 'origin / dev'보다 앞서있었습니다 (따라서 커밋은 아직 원격으로 푸시되지 않았습니다).

변경 사항을 적용하기 전에 이러한 커밋을 하나로 스쿼시하고 싶었습니다. 분기를 'origin / dev'상태로 재설정하고 (96 커밋의 모든 변경 사항을 비 스테이지로 남겨 둡니다) 변경 사항을 한 번에 커밋합니다.

git reset origin/dev
git add --all
git commit -m 'my commit message'

1
내가 필요한 것만 기능 지점에서 커밋을 스쿼시 한 ​​다음 해당 커밋을 마스터로 가져옵니다.
David Victor

1
이것은 이전 커밋을 스쿼시하지 않습니다!
IgorGanapolsky

좀 더 @igorGanapolsky 좀 더 자세히 설명해 주시겠습니까?
trudolf

3
@trudolf 이것은 실제로 스쿼시되지 않습니다 (개인 커밋을 스쿼시로 선택). 이것은 한 번에 모든 변경 사항을 커밋하는 것입니다.
IgorGanapolsky

15
그렇기 때문에 모든 커밋을 하나로 묶습니다. 축하합니다!
trudolf

45

지점에서 커밋을 결합하려면 다음을 실행하십시오.

git rebase -i HEAD~(n number of commits back to review)

예:

git rebase -i HEAD~1

그러면 텍스트 편집기가 열리고 커밋을 함께 병합하려면 각 커밋 앞에서 'pick'을 'squash'로 전환해야합니다. 설명서에서 :

p, pick = 커밋 사용

s, squash = 커밋을 사용하지만 이전 커밋에 통합

예를 들어, 모든 커밋을 하나로 병합하려는 경우 'pick'은 첫 번째 커밋이며 이후의 모든 커밋 (첫 번째 아래에 배치)은 'squash'로 설정해야합니다. vim을 사용하는 경우 삽입 모드에서 : x 를 사용 하여 편집기를 저장하고 종료하십시오.

그런 다음 리베이스를 계속하십시오.

git rebase --continue

커밋 기록을 다시 작성하는 방법과 다른 방법에 대한 자세한 내용은 이 유용한 게시물을 참조하십시오.


2
--continue그리고 vim :x이 무엇을하는지 설명하십시오 .
not2qubit

rebase는 git add파일에서 올바른 구성 git rebase --continue을 수행 한 후 다음 커밋으로 이동하고 병합을 시작한 후 분기의 커밋을 통과하면서 블록 단위로 발생 합니다 . :x참조 VIM 사용하는 경우 파일의 변경 사항을 저장합니다 하나의 명령 인
aabiro

32

Anomies의 대답 은 좋지만, 이것에 대해 불안감을 느꼈기 때문에 몇 개의 스크린 샷을 추가하기로 결정했습니다.

0 단계 : 자식 로그

현재 위치를 확인하십시오 git log. 가장 중요한 것은 스쿼시 하지 않으려 는 첫 번째 커밋의 커밋 해시를 찾으십시오 . 따라서 :

여기에 이미지 설명을 입력하십시오

1 단계 : 자식 리베이스

git rebase -i [your hash]내 경우에는을 실행하십시오 .

$ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d

2 단계 : 원하는 것을 골라 / 스쿼시

필자의 경우, 처음 커밋에서 모든 것을 스쿼시하고 싶습니다. 순서는 처음부터 끝까지입니다 git log. 제 경우에는 다음을 원합니다.

여기에 이미지 설명을 입력하십시오

3 단계 : 메시지 조정

커밋을 하나만 선택하고 나머지는 스쿼시 한 ​​경우 하나의 커밋 메시지를 조정할 수 있습니다.

여기에 이미지 설명을 입력하십시오

그게 다야. 이 ( :wq) 를 저장하면 완료됩니다. 로 살펴보십시오 git log.


2
예를 들면 다음과 같습니다.git log
Timothy LJ Stewart

2
고맙지 만 사양 할게. 이것이 내가 커밋을 정확히하는 방법입니다.
Axalix

@Axalix 모든 라인을 제거 했습니까? 그것이 커밋을 잃는 방법입니다.
a3y3 2016 년

31

절차 1

1) 커밋 쇼트 해시 식별

# git log --pretty=oneline --abbrev-commit
abcd1234 Update to Fix for issue B
cdababcd Fix issue B
deab3412 Fix issue A
....

여기에서도 git log --oneline짧은 해시를 얻는 데 사용할 수 있습니다.

2) 마지막 두 커밋을 스쿼시 (병합)하려는 경우

# git rebase -i deab3412 

3) nano병합을 위한 편집기 가 열립니다 . 그리고 그것은 아래와 같습니다

....
pick cdababcd Fix issue B
pick abcd1234 Update to Fix for issue B
....

4) 단어 바꾸기 picksquash앞서 존재하는이 abcd1234. 이름을 바꾼 후에는 다음과 같아야합니다.

....
pick cdababcd Fix issue B
squash abcd1234 Update to Fix for issue B
....

5) 이제 nano편집기를 저장하고 닫습니다 . 보도 ctrl + o하고 Enter 키를 눌러 Enter저장합니다. 그런 다음을 눌러 ctrl + x편집기를 종료하십시오.

6) nano필요한 경우 편집기를 다시 열어 주석을 업데이트합니다 (필요한 경우).

7) 이제 성공적으로 뭉개졌습니다. 로그를 확인하여 확인할 수 있습니다.

# git log --pretty=oneline --abbrev-commit
1122abcd Fix issue B
deab3412 Fix issue A
....

8) 이제 repo로 밉니다. +지점 이름 앞에 부호 를 추가 하십시오. 이것은 강제 푸시를 의미합니다.

# git push origin +master

참고 : 이것은 git on ubuntushell을 기반으로 합니다. 다른 os ( Windows또는 Mac)를 사용하는 경우 위의 명령은 편집기를 제외하고 동일합니다. 다른 편집기를 얻을 수 있습니다.

절차 2

  1. 먼저 커밋에 필요한 파일을 추가하십시오.
git add <files>
  1. 그런 다음 --fixup옵션을 사용하여 커밋 OLDCOMMIT하고이 커밋을 병합 (스쿼시)해야합니다.
git commit --fixup=OLDCOMMIT

이제와 함께 HEAD 위에 새로운 커밋을 만듭니다 fixup1 <OLDCOMMIT_MSG>.

  1. 그런 다음 아래 명령을 실행하여 새 커밋을에 병합하십시오 (스쿼시) OLDCOMMIT.
git rebase --interactive --autosquash OLDCOMMIT^

^대한 이전 커밋을 의미합니다 OLDCOMMIT. 이 rebase명령은 편집기 (vim 또는 nano)에서 대화식 창을 열어 저장하고 종료하기 만하면됩니다. 이 옵션이 전달되면 최신 커밋이 이전 커밋 옆으로 자동 이동되고 작업이 fixup(스쿼시와 동일)로 변경됩니다. 그런 다음 리베이스가 계속되고 완료됩니다.

절차 3

  1. 마지막 커밋 수단에 새로운 변경 사항을 추가해야하는 경우 --amend와 함께 사용할 수 있습니다 git-commit.
    # git log --pretty=oneline --abbrev-commit
    cdababcd Fix issue B
    deab3412 Fix issue A
    ....
    # git add <files> # New changes
    # git commit --amend
    # git log --pretty=oneline --abbrev-commit
    1d4ab2e1 Fix issue B
    deab3412 Fix issue A
    ....  

여기에 --amend커밋 마지막에 새로운 변화를 병합 cdababcd하고 새로운 ID를 저지 생성1d4ab2e1

결론

  • 첫 번째 절차의 장점은 여러 커밋을 스쿼시하고 재정렬하는 것입니다. 그러나 수정 사항을 아주 오래된 커밋에 병합해야하는 경우이 절차는 어려울 것입니다.
  • 따라서 두 번째 절차는 커밋을 아주 오래된 커밋에 쉽게 병합하는 데 도움이됩니다.
  • 그리고 세 번째 절차는 마지막 커밋에 새로운 변경 사항을 스쿼시하는 경우에 유용합니다.

마지막 6 번째 커밋에 커밋 ID로 재설정 한 경우에도 마지막 두 커밋 만 업데이트합니다. 이유를 모릅니다
Carlos Liu

커밋 순서를 다시 정렬 할 수도 있습니다. 잘 작동합니다.
rashok

29

마지막 10 개의 커밋을 하나의 단일 커밋으로 스쿼시하려면 :

git reset --soft HEAD~10 && git commit -m "squashed commit"

스 쿼쉬 된 커밋으로 원격 브랜치를 업데이트하려는 경우 :

git push -f

여러 사람이 공유 브랜치에서 작업하는 경우 --force는 로컬 복사본으로 원격으로 맹목적으로 업데이트되므로 위험합니다. --force-with-lease는 마지막으로 가져온 이후 원격에서 다른 사람의 커밋을하지 않도록하여 더 좋을 수 있습니다.
bharath

27

feature-branchGolden Repository ( golden_repo_name) 에서 복제 된 원격 분기 ( ) 에있는 경우 커밋을 하나로 스쿼시하는 기술은 다음과 같습니다.

  1. 골든 레포 확인

    git checkout golden_repo_name
    
  2. 다음과 같이 새 지점 (골든 리포지토리)을 만듭니다.

    git checkout -b dev-branch
    
  3. 스쿼시는 이미 가지고있는 지역 지점과 합병

    git merge --squash feature-branch
    
  4. 변경 사항을 커밋하십시오 (dev-branch로 진행되는 유일한 커밋입니다)

    git commit -m "My feature complete"
    
  5. 지점을 로컬 리포지토리로 푸시

    git push origin dev-branch
    

git-svn을 통해 svn 브랜치를 동기화하기 위해 ~ 100 커밋을 스쿼시 했으므로 대화 형 rebasing보다 훨씬 빠릅니다!
현인

1
읽으면서 @Chris의 의견을 보았습니다. 이것은 내가했던 일입니다 (rebase --soft ...). 스택 오버 플로우가 더 이상 상단에 수백 개의 공감대로 답을하지 않는 것이 너무 나쁩니다 ...
sage

1
@sage에 동의하고, 그들이 언젠가는 그렇게 할 수 있기를 바랍니다.
Sandesh Kumar

이것이 올바른 방법입니다. Rebase 접근 방식은 좋지만 최후의 수단으로 스쿼시에만 사용해야합니다.
Axalix

20

정말 편리한 것 :
스쿼시하려는 커밋 해시를 찾으십시오 d43e15.

이제 사용

git reset d43e15
git commit -am 'new commit name'

2
이. 왜 더 많은 사람들이 이것을 사용하지 않습니까? 개별 커밋을 조정하고 스쿼시하는 것보다 빠릅니다.
a3y3 2016 년

17

이것은 슈퍼 듀퍼 클루 지이지만 멋진 방법으로 링에 던져 넣을 것입니다.

GIT_EDITOR='f() { if [ "$(basename $1)" = "git-rebase-todo" ]; then sed -i "2,\$s/pick/squash/" $1; else vim $1; fi }; f' git rebase -i foo~5 foo

번역 : 편집 할 파일 이름이 git-rebase-todo(대화 형 rebase prompt) 경우 첫 번째 "pick"을 제외한 모든 것을 "squash"로 변경하고 그렇지 않으면 vim을 생성 하는 git에 대한 새로운 "편집기"를 제공 하십시오. 숙청 된 커밋 메시지를 편집하려면 vim을 얻는다. (그리고 분명히 나는 ​​foo 브랜치에서 마지막 5 개의 커밋을 스쿼시하고 있었지만 원하는대로 변경할 수 있습니다.)

그래도 Mark Longair가 제안한 것을 할 것입니다 .


7
+1 : 재미 있고 유익합니다. GIT_EDITOR 환경 변수에 프로그램 이름보다 더 복잡한 것을 넣을 수 있다는 것이 전혀 분명하지 않습니다.
Mark Longair

16

모든 커밋을 단일 커밋으로 뭉개 버리려면 (예 : 프로젝트를 처음 공개 할 때) 다음을 시도하십시오.

git checkout --orphan <new-branch>
git commit

14

가장 쉬운 방법은 마스터에서 새 브랜치를 만들고 기능 브랜치를 병합하는 것입니다.

git checkout master
git checkout -b feature_branch_squashed
git merge --squash feature_branch

그런 다음 모든 변경 사항을 커밋 할 준비가되었습니다.


14

rebase없는 2020 간단한 솔루션 :

git reset --soft HEAD~2

git commit -m "new commit message"

git push --force

2는 마지막 두 커밋이 찌그러짐을 의미합니다. 당신은 그것을 임의의 숫자로 바꿀 수 있습니다


12

현재 스쿼시하려는 브랜치에 있고 마스터는 브랜치에서 시작한 브랜치이며 최신 커밋에는 커밋 메시지와 사용하려는 작성자가 포함되어 있으므로 항상 작동하는 간단한 단일 라이너.

git reset --soft $(git merge-base HEAD master) && git commit --reuse-message=HEAD@{1}

4
나는 커밋 스쿼시와 어리석게 복잡한 것에 대해 좌절감을 느꼈습니다. 마지막 메시지를 사용하고 하나의 커밋으로 스쿼시하십시오! 왜 그렇게 힘든가요 ???? 이 하나의 라이너가 나를 위해 그것을합니다. 화난 마음의 바닥에서 감사합니다.
Locane 19

12

예를 들어 https://bitbucket.org 와 같이 지점 (원격 저장소)의 단일 커밋에 대한 마지막 3 개의 커밋을 스쿼시하려는 경우

내가 한 일은

  1. git reset-소프트 헤드 ~ 3 &&
  2. 자식 커밋
  3. 자식 푸시 원점 (branch_name) --force

3
만약 당신이 force를 사용한다면 이전 커밋을 제거한 이후로 검색 할 수있는 방법이 없기 때문에 조심
하라

12

⚠️ 경고 : "마지막 X 커밋"이 모호 할 수 있습니다.

  (MASTER)  
Fleetwood Mac            Fritz
      ║                    ║
  Add Danny  Lindsey     Stevie       
    Kirwan  Buckingham    Nicks                                              
      ║         ╚═══╦══════╝     
Add Christine       ║          
   Perfect      Buckingham
      ║           Nicks            
    LA1974══════════╝                                    
      ║                  
      ║                  
    Bill <══════ YOU ARE EDITING HERE
  Clinton        (CHECKED OUT, CURRENT WORKING DIRECTORY)              

https://github.com/fleetwood-mac/band-history 리포지토리 의 매우 약식 기록 에서 Bill Clinton 커밋을 원래의 MASTERFleetwood Mac 커밋 으로 병합하기위한 풀 요청을 열었습니다 .

풀 요청을 열었고 GitHub에서 다음을 볼 수 있습니다.

네 가지 커밋 :

  • 대니 커완 추가
  • Christine Perfect 추가
  • LA1974
  • 빌 클린턴

아무도 전체 저장소 기록을 읽을 필요가 없다고 생각합니다. (실제로 저장소가 있으므로 위의 링크를 클릭하십시오!) 이러한 커밋을 스쿼시하기로 결정했습니다. 그래서 당신은 가서 실행 git reset --soft HEAD~4 && git commit합니다. 그런 다음 git push --forceGitHub에서 PR을 정리하십시오.

그리고 어떻게됩니까? 프리츠에서 빌 클린턴까지 한 번 커밋했습니다. 어제 잊어 버렸기 때문에이 프로젝트의 Buckingham Nicks 버전을 작업하고있었습니다. 그리고 git logGitHub에 표시되는 것과 일치하지 않습니다.

🐻 이야기의 교훈

  1. 당신이 얻을 원하는 정확한 파일 찾기 로를 , 그리고 git checkout그들
  2. 정확한 이전에 당신이 역사에서 유지하려는 커밋 찾아 git reset --soft
  3. 메이크업 git commit로부터 직접 그 뒤틀림을 에서 받는 사람

1
이 작업을 수행하는 가장 쉬운 방법은 100 %입니다. 현재 HEAD 원하는 올바른 상태 인 경우 # 1을 건너 뛸 수 있습니다.
Stan

이것이 첫 번째 커밋 기록을 다시 쓸 수있는 유일한 방법입니다.
Vadorequest

9

중간 커밋의 커밋 메시지에 신경 쓰지 않으면 사용할 수 있습니다.

git reset --mixed <commit-hash-into-which-you-want-to-squash>
git commit -a --amend

7

GitLab을 사용하는 경우 아래 그림과 같이 병합 요청에서 스쿼시 옵션을 클릭하면됩니다. 커밋 메시지는 병합 요청의 제목이됩니다.

여기에 이미지 설명을 입력하십시오


6
git rebase -i HEAD^^

여기서 ^의 개수는 X입니다.

(이 경우 마지막 커밋 두 개를 스쿼시하십시오)


6

다른 훌륭한 답변 외에도 git rebase -i커밋 순서와 항상 혼동되는 방법을 추가하고 싶습니다 . 이것이 내 워크 플로입니다.

  1. git rebase -i HEAD~[N]여기서 N은 가장 최근에 시작한 커밋 수입니다 . 그래서 git rebase -i HEAD~5"새로 지난 5 커밋 스쿼시"의미;
  2. 편집기가 팝업되어 병합하려는 커밋 목록을 보여줍니다. 이제 그것들은 역순 으로 표시됩니다 : 이전 커밋이 맨 위에 있습니다. 첫 번째 / 이전 커밋을 제외한 모든 커밋을 "스쿼시"또는 "s" 로 표시합니다. 시작점으로 사용됩니다. 편집기를 저장하고 닫으십시오.
  3. 새로운 커밋에 대한 기본 메시지와 함께 편집기가 다시 나타납니다. 스쿼시 완성!

소스 및 추가 읽기 : # 1 , # 2 .


6

이와 같은 워크 플로와 관련된 질문에 대한 답변은 어떻습니까?

  1. 여러 로컬 커밋, 여러 병합 FROM master와 혼합 ,
  2. 마침내 리모컨으로의 밀기
  3. 검토 자에 의해 PR 및 마스터 병합 . (그렇습니다. 개발자가 merge --squashPR 을 따르는 것이 더 쉬울 것이지만 팀은 프로세스 속도가 느려질 것이라고 생각했습니다.)

이 페이지에서 이와 같은 워크 플로를 보지 못했습니다. (내 눈이 될 수도 있습니다.) rebase올바르게 이해 하면 여러 번 병합하면 여러 가지 충돌 해결 이 필요합니다 . 나는 그것에 대해 생각조차하고 싶지 않습니다!

그래서 이것은 우리에게 효과가있는 것 같습니다.

  1. git pull master
  2. git checkout -b new-branch
  3. git checkout -b new-branch-temp
  4. 로컬에서 많은 편집 및 커밋, 정기적으로 마스터 병합
  5. git checkout new-branch
  6. git merge --squash new-branch-temp // 모든 변경 사항을 스테이지에 넣습니다.
  7. git commit 'one message to rule them all'
  8. git push
  9. 검토자는 PR을 수행하고 마스터에 병합합니다.

많은 의견에서 나는 당신의 접근 방식을 좋아합니다. 매우 편리하고 빠릅니다
Artem Solovev

5

더 일반적인 해결책은 'N'커밋을 지정하는 것이 아니라 스쿼시하려는 분기 / 커밋 ID를 찾는 것입니다. 특정 커밋까지 커밋을 계산하는 것보다 오류가 덜 발생합니다. 태그를 직접 지정하거나 실제로 계산하려면 HEAD ~ N을 지정할 수 있습니다.

워크 플로에서 지점을 시작하면 해당 지점에 대한 첫 번째 커밋이 목표를 요약합니다 (즉, 일반적으로 해당 기능에 대한 '최종'메시지로 공용 리포지토리에 푸시하는 것입니다). I는하고 싶은 git squash master첫 번째 메시지 다시 다음 내가 밀어 준비 해요.

별명을 사용합니다.

squash = !EDITOR="\"_() { sed -n 's/^pick //p' \"\\$1\"; sed -i .tmp '2,\\$s/^pick/f/' \"\\$1\"; }; _\"" git rebase -i

이렇게하면 이전에 찌그러진 기록이 덤프됩니다. 되돌릴 경우 콘솔에서 오래된 커밋 ID를 가져와 복구 할 수 있습니다. (Solaris 사용자는 GNU sed -i옵션을 사용하므로 Mac 및 Linux 사용자는 이에 적합해야합니다.)


별칭을 사용해 보았지만 sed replaces가 효과가 있는지 확실하지 않습니다. 그들은 어떻게해야합니까?
raine

첫 번째 sed는 콘솔에 기록을 덤프합니다. 두 번째 sed는 모든 'pick'을 'f'(fixup)로 바꾸고 편집기 파일을 그 자리에 다시 작성합니다 (-i 옵션). 두 번째는 모든 작업을 수행합니다.
Ethan

맞습니다 .N 개의 특정 커밋을 계산하면 오류가 발생하기 쉽습니다. 그것은 나를 여러 번 망쳐 놓았고 리베이스를 취소하려고 몇 시간을 낭비했습니다.
IgorGanapolsky

안녕하세요,이 워크 플로가 병합시 가능한 충돌을 숨길 수 있는지 알고 싶습니다. 따라서 두 가지 마스터와 슬레이브가 있는지 고려하십시오. 슬레이브가 마스터와 충돌 git squash master하고 슬레이브에서 체크 아웃 할 때 사용 합니다. 어떻게됩니까? 우리는 갈등을 숨길 것인가?
Sergio Bilello

@Sergio 이것은 기록을 다시 쓰는 경우이므로 이미 푸시 된 커밋을 스쿼시 한 ​​다음 스쿼시 된 버전을 다시 병합 / 리베이스하려고하면 충돌이 발생할 수 있습니다. (일부 사소한 경우도 사라질 수 있습니다.)
Ethan

5

문제는 "마지막"의 의미가 모호 할 수 있습니다.

예를 들어 git log --graph다음과 같이 출력됩니다 (단순화).

* commit H0
|
* merge
|\
| * commit B0
| |
| * commit B1
| | 
* | commit H1
| |
* | commit H2
|/
|

그런 다음 시간별 마지막 커밋은 H0, merge, B0입니다. 스쿼시하려면 커밋 H1에 병합 된 브랜치를 리베이스해야합니다.

문제는 H0에 H1과 H2가 포함되어 있으며 (B는 병합하지 않고 분기 후 더 많은 커밋) B0에는 없습니다. 따라서 최소한 H0, merge, H1, H2, B0의 변경 사항을 관리해야합니다.

rebase를 사용할 수는 있지만 다른 방법으로 대답 할 수 있습니다.

rebase -i HEAD~2

그러면 다른 답변에서 언급 한대로 선택 옵션이 표시됩니다.

pick B1
pick B0
pick H0

H0 대신 pick 대신 squash를 넣습니다.

pick B1
pick B0
s H0

저장 후 종료 후 rebase는 H1 이후에 커밋을 차례로 적용합니다. 즉, 충돌을 다시 해결하도록 요청합니다 (처음에는 HEAD가 H1이되고 적용되는 커밋이 누적 됨).

리베이스가 완료되면 찌그러진 H0 및 B0에 대한 메시지를 선택할 수 있습니다.

* commit squashed H0 and B0
|
* commit B1
| 
* commit H1
|
* commit H2
|

그냥 BO 일부 리셋을 할 경우 PS : (예를 들어, 사용 reset --mixed이 여기에 자세히 설명되어 https://stackoverflow.com/a/18690845/2405850 ) :

git reset --mixed hash_of_commit_B0
git add .
git commit -m 'some commit message'

그런 다음 H0, H1, H2의 B0 변경으로 스쿼시합니다. 분기 후 및 병합 전에 변경 사항을 완전히 커밋하지 않습니다.


4

1) git reset-소프트 헤드 ~ n

n-커밋 수, 스쿼시 필요

2) git commit -m "새로운 커밋 메시지"

3) 자식 푸시 원점 branch_name --force

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