Program Tip

Git 커밋 트리밍 / Git 기록 스 쿼싱

programtip 2020. 11. 17. 20:58
반응형

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새로운 "수정"작업과 함께 유용한 옵션을 배웠습니다 .

--autosquashThechnosorcery 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/squash

During 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 --skiping 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 message

Commit e12a7ef ("rebase -i: Handle "combination of <n> commits" with GETTEXT_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.

  1. You have a branch ticket-201 that you branched from master. You want to pretend that all the commits from ticket-201 never happened, but that you did all the work in one shot.
  2. Soft reset to the branch point using git reset --soft hash where hash should be a commit hash that is in ticket-201's log.
  3. 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:

  1. Checkout a new branch at commit0 (pretend that's a hash): git checkout -b new-history commit0
  2. Now you can get the files from commit5: git reset --hard commit5
  3. Switch back to your index point: git reset --soft commit0
  4. 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

반응형