Git 커밋 트리밍 / Git 기록 스 쿼싱
몇 분마다 Git 브랜치에 내 코드를 확인하고 댓글은 "다시 시작되는 모든 것이 깨졌습니다"라는 말과 다른 어리 석음이됩니다.
그런 다음 몇 분 / 시간 / 일마다 "버그 수정 # 22.55, 세 번째"와 같은 실제 댓글로 심각한 커밋을 수행합니다. 이 두 개념을 어떻게 분리 할 수 있습니까? 자주하는 커밋을 모두 제거하고 진지한 커밋은 그대로두고 싶습니다.
지금 (이 항목의 후반부) 새로운 Git1.7 수정으로 답변 을 편집했습니다 ! --autosquash
빠른 커밋 순서 변경 및 메시지 편집을위한 작업 및 옵션.
첫째, Git1.7 이전에 수행 된 클래식 스 쿼싱 프로세스입니다.
(Git1.7은 동일한 프로세스를 가지고 있으며 수동 재정렬과 달리 자동 커밋 재정렬의 가능성과 깔끔한 스 쿼싱 메시지에 의해서만 더 빨라졌습니다.)
자주 사용하는 모든 체크인을 제거하고 진지한 체크인은 그대로두고 싶습니다.
이것을 스 쿼싱 커밋 이라고 합니다.
이 Git 준비 기사 에 "comit 정리"의 좋은 예가 있습니다 .
(참고 : 리베이스 대화 형 기능 은 2007 년 9 월부터 제공되었으며 커밋을 스쿼시, 분할 또는 제거 또는 재정렬 할 수 있습니다. GitPro 페이지 참조 )
주의 사항 : 외부 저장소로 푸시되지 않은 커밋에만이 작업을 수행하십시오. 다른 사람이 삭제할 커밋을 기반으로 작업을 수행하면 많은 충돌이 발생할 수 있습니다. 다른 사람과 공유 한 기록을 다시 쓰지 마십시오.
마지막 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
와 함께 있는 마지막 4 개의 커밋을 사용하여 리베이스합니다HEAD~4
.
우리는 모든 것을 하나의 커밋으로 스쿼시 할 것입니다.
따라서 파일의 처음 네 줄을 이렇게 변경하면 트릭이됩니다.
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.
그리고 우리가 다시 역사를 보면 ...
참고 : "커밋 스 쿼싱 "목적을 위해 Git1.7 (2010 년 2 월)은 두 가지 새로운 요소를 도입 했습니다 (코멘트에서 Dustin 이 언급 했음 ).
- "
git rebase -i
"fixup
이 (가) 변경 사항을 찌그러 뜨리지 만 기존 로그 메시지에 영향을주지 않는 새로운 작업 " "을 학습했습니다 .- "
git rebase -i
"또한--autosquash
새로운 "수정"작업과 함께 유용한 옵션을 배웠습니다 .
--autosquash
이 Thechnosorcery Networks 블로그 항목 에는 둘 다 (수정 작업 및 옵션)가 설명되어 있습니다. 이러한 기능은 지난 2009 년 6 월 부터 요리 되고 있으며 작년 12 월에 더 많은 논의가 있었습니다.
fixup
행동이나 지시가가가의 편집 목록을 커밋 당신이 수동으로 다시 정렬했을 커밋 부수입니다 rebase --interactive
당신이 그냥 저장할 수 있습니다 (빠른 메시지 에디션 단계를 만들 것입니다 두 번째 커밋 메시지를 무시하고, 상기가있을 것이다 커밋 숙청 첫 번째 커밋 메시지 만 해당)
결과 커밋 메시지는 첫 번째 커밋 메시지 만됩니다 .
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
이 --autosquash
옵션은 커밋 재정렬 프로세스를 자동으로 만드는 것입니다.
어떤 커밋을하고 싶은지 알고 있다면“
squash! $other_commit_subject
” 메시지로 커밋 할 수 있습니다 . 그런 다음 실행@git rebase --interactive --autosquash commitish@
하면 줄이 자동으로 스쿼시로 설정되고 $ other_commit_subject라는 제목으로 커밋 아래에 배치됩니다.
(실제로 다른 커밋 메시지 squash!
의 시작 부분 만 사용할 수 있습니다. )
$ vim Foo.txt
$ git commit -am "Change all the 'Bar's to 'Foo's"
[topic 8374d8e] Change all the 'Bar's to 'Foo's
1 files changed, 2 insertions(+), 2 deletions(-)
$ vim Bar.txt
$ git commit -am "Change all the 'Foo's to 'Bar's"
[topic 2d12ce8] Change all the 'Foo's to 'Bar's
1 files changed, 1 insertions(+), 1 deletions(-)
$ vim Foo.txt
$ git commit -am "squash! Change all the 'Bar's"
[topic 259a7e6] squash! Change all the 'Bar's
1 files changed, 2 insertions(+), 1 deletions(-)
보다? 여기서 세 번째 커밋은 첫 번째 커밋 메시지 의 시작 부분 만 사용 합니다.
A rebase --interactive --autosquash
는 스쿼시 된 커밋을 관련 커밋 아래로 이동합니다.
pick 8374d8e Change all the 'Bar's to 'Foo's
squash 259a7e6 squash! Change all the 'Bar's
pick 2d12ce8 Change all the 'Foo's to 'Bar's
메시지 에디션은 다음과 같습니다.
# This is a combination of 2 commits.
# The first commit's message is:
Change all the 'Bar's to 'Foo's
# This is the 2nd commit message:
squash! Change all the 'Bar's
Meaning by default you would keep the squashing operation recorded in the commit message.
But with the fixup! directive, you could keep that squashing "invisible" in the commit message, while still benefiting from the automatic commit reordering with the --autosquash
option (and the fact that your second commit message is based on the first commit you want to be squashed with).
pick 8374d8e Change all the 'Bar's to 'Foo's
fixup cfc6e54 fixup! Change all the 'Bar's
pick 2d12ce8 Change all the 'Foo's to 'Bar's
The message by default will be:
# This is a combination of 2 commits.
# The first commit's message is:
Change all the 'Bar's to 'Foo's
# The 2nd commit message will be skipped:
# fixup! Change all the 'Bar's
Notice that the
fixup!
commit’s message is already commented out.
You can just save out the message as-is, and your original commit message will be kept.
Very handy for including changes when you realize that you forgot to add part of an earlier commit.
Now if you want to fixup or squash based on the previous commit you just did, Jacob Helwig (the author of the Technosorcery Networks blog entry) recommends the following aliases:
[alias]
fixup = !sh -c 'git commit -m \"fixup! $(git log -1 --format='\\''%s'\\'' $@)\"' -
squash = !sh -c 'git commit -m \"squash! $(git log -1 --format='\\''%s'\\'' $@)\"' -
And for doing a rebase interactive which will always benefit from the automatic reordering of commits meant to be squashed:
[alias]
ri = rebase --interactive --autosquash
Update for Git 2.18 (Q2 2018): "git rebase -i
" sometimes left intermediate "# This is a combination of N commits
" message meant for the human consumption inside an editor in the final result in certain corner cases, which has been fixed.
See commit 15ef693, commit dc4b5bc, commit e12a7ef, commit d5bc6f2 (27 Apr 2018) by Johannes Schindelin (dscho
).
(Merged by Junio C Hamano -- gitster
-- in commit 4a3bf32, 23 May 2018)
rebase --skip
: clean up commit message after a failed fixup/squashDuring a series of fixup/squash commands, the interactive rebase builds up a commit message with comments. This will be presented to the user in the editor if at least one of those commands was a
squash
.In any case, the commit message will be cleaned up eventually, removing all those intermediate comments, in the final step of such a fixup/squash chain.
However, if the last fixup/squash command in such a chain fails with merge conflicts, and if the user then decides to skip it (or resolve it to a clean worktree and then continue the rebase), the current code fails to clean up the commit message.
This commit fixes that behavior.
The fix is quite a bit more involved than meets the eye because it is not only about the question whether we are
git rebase --skip
ing a fixup or squash. It is also about removing the skipped fixup/squash's commit message from the accumulated commit message. And it is also about the question whether we should let the user edit the final commit message or not ("Was there a squash in the chain that was not skipped?").For example, in this case we will want to fix the commit message, but not open it in an editor:
pick <- succeeds fixup <- succeeds squash <- fails, will be skipped
This is where the newly-introduced
current-fixups
file comes in real handy. A quick look and we can determine whether there was a non-skipped squash. We only need to make sure to keep it up to date with respect to skipped fixup/squash commands. As a bonus, we can even avoid committing unnecessarily, e.g. when there was only one fixup, and it failed, and was skipped.To fix only the bug where the final commit message was not cleaned up properly, but without fixing the rest, would have been more complicated than fixing it all in one go, hence this commit lumps together more than a single concern.
Git 2.19 (Q3 2018) fixes a bug: When "git rebase -i
" is told to squash two or more commits into one, it labeled the log message for each commit with its number.
It correctly called the first one "1st commit", but the next one was "commit #1
", which was off-by-one(!).
See commit dd2e36e (15 Aug 2018) by Phillip Wood (phillipwood
).
(Merged by Junio C Hamano -- gitster
-- in commit 36fd1e8, 20 Aug 2018)
rebase -i
: fix numbering in squash messageCommit e12a7ef ("
rebase -i
: Handle "combination of<n>
commits" withGETTEXT_POISON
", 2018-04-27, Git 2.18) changed the way that individual commit messages are labelled when squashing commits together.
In doing so a regression was introduced where the numbering of the messages is off by one. This commit fixes that and adds a test for the numbering.
Using Soft Reset Instead of Rebase to Squash GIT History
I think the length of VonC's answers speaks volumes -- literally -- about how complicated git rebase
is. This is my extension of another answer to a question of mine.
- You have a branch
ticket-201
that you branched frommaster
. You want to pretend that all the commits fromticket-201
never happened, but that you did all the work in one shot. - Soft reset to the branch point using
git reset --soft hash
wherehash
should be a commit hash that is inticket-201
's log. - Commit your changes using add then commit. Now the branch history will only have the first commit and the new one with the new stuff.
Making Up Histories From Arbitrary Commits in Different Branches
Using resets you can rewrite the history as you see fit, though your edits will lose the charm of having the right timestamp. Assuming you don't care about that (the times/dates on your files will be enough, perhaps?), or if you want to fiddle with the commits as you go, you can follow these steps:
- Checkout a new branch at
commit0
(pretend that's a hash):git checkout -b new-history commit0
- Now you can get the files from
commit5
:git reset --hard commit5
- Switch back to your index point:
git reset --soft commit0
- Commit and this will be the second commit in the branch.
This idea is simple, effective and flexible.
Using Squash Instead
Recently, I've been working in another branch and using squash
. The other branch is called temp, and then I use git merge temp --squash
to bring it into the real branch that gets pushed to the server.
Workflow is something like this, assuming I'm working in Ticket65252
:
git branch -d temp #remove old temp bbranch
git checkout -b temp
# work work work, committing all the way
git checkout Ticket65252
git merge temp --squash
git commit -m "Some message here"
Advantages over using rebase
? Way less complicated.
Advantages over using reset --hard
and then reset --soft
? Less confusing and slightly less error prone.
Use git rebase -i to pick and squash your commits together.
참고 URL : https://stackoverflow.com/questions/2302736/trimming-git-commits-squashing-git-history
'Program Tip' 카테고리의 다른 글
숫자 선택기 대화 상자를 만드는 방법은 무엇입니까? (0) | 2020.11.17 |
---|---|
transform (rotate) 설정으로 Z-index가 취소됩니다. (0) | 2020.11.17 |
최소 비교 횟수로 배열에서 두 번째로 큰 요소 찾기 (0) | 2020.11.17 |
콘솔 응용 프로그램의 경우 "종료시" (0) | 2020.11.17 |
기존 DataTable에 값이있는 새 열을 추가하는 방법은 무엇입니까? (0) | 2020.11.17 |