Làm thế nào để tôi squash hai cam kết không liên tiếp?


182

Tôi là một chút mới cho toàn bộ tính năng nổi loạn trong git. Hãy nói rằng tôi đã thực hiện các cam kết sau:

A -> B -> C -> D

Sau đó, tôi nhận ra rằng Dcó một bản sửa lỗi phụ thuộc vào một số mã mới được thêm vào Avà các cam kết này thuộc về nhau. Làm thế nào để tôi squash A& Dcùng nhau và rời khỏi B& Cmột mình?

Câu trả lời:


256

Bạn có thể chạy git rebase --interactivevà sắp xếp lại D trước B và ép D vào A.

Git sẽ mở một trình soạn thảo và bạn thấy một tệp như thế này, ví dụ: git rebase --interactive HEAD~4

pick aaaaaaa Commit A
pick bbbbbbb Commit B
pick ccccccc Commit C
pick ddddddd Commit D

# Rebase aaaaaaa..ddddddd onto 1234567 (4 command(s))
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

Bây giờ bạn thay đổi tập tin trông như thế này:

pick aaaaaaa Commit A
squash ddddddd Commit D
pick bbbbbbb Commit B
pick ccccccc Commit C

Và git bây giờ sẽ kết hợp các thay đổi của A và D lại với nhau thành một cam kết và đặt B và C sau đó. Khi bạn không muốn giữ thông điệp cam kết của D, thay vào đó squash, bạn sẽ sử dụng fixuptừ khóa. Để biết thêm về fixup, bạn có thể tham khảo git rebasetài liệu hoặc xem câu hỏi này có một số câu trả lời hay.


5
Ban đầu, tôi đọc nó là "rebase D lên A, squash D thành A, sau đó rebase B lên DA". Không rõ câu trả lời rằng điều này có thể được thực hiện bằng cách sắp xếp lại các dòng trong trình soạn thảo văn bản.
Victor Sergienko

3
Nếu chi nhánh của bạn là cục bộ, bạn sẽ gặp There is no tracking information for the current branchlỗi khi khởi động lại. Trong trường hợp này, bạn cần chỉ định số lượng cam kết bạn muốn làm việc, như thế này : git rebase -i HEAD~4. Xem câu trả lời này .
johndodo

1
Tôi sử dụng chế độ tương tác (git rebase -i) trong nhiều năm, tôi chỉ nhận ra nó có thể được sắp xếp lại . Cảm ơn
CalvinChe

43

Lưu ý: Bạn không nên thay đổi các cam kết đã được đẩy sang một repo khác bằng bất kỳ cách nào trừ khi bạn biết hậu quả .

git log --oneline -4

D commit_message_for_D
C commit_message_for_C
B commit_message_for_B
A commit_message_for_A

git rebase --interactive

pick D commit_message_for_D
pick C commit_message_for_C
pick B commit_message_for_B
pick A commit_message_for_A

Nhập i(Đặt VIM ở chế độ chèn)

Thay đổi danh sách thành như thế này (Bạn không phải xóa hoặc bao gồm thông báo cam kết). Đừng viết sai chính tả squash! :

pick C commit_message_for_C
pick B commit_message_for_B
pick A commit_message_for_A
squash D

Nhập Escrồi ZZ(Lưu và thoát VIM)

# This is a combination of 2 commits.
# The first commit's message is:

commit_message_for_D

# This is the 2nd commit message:

commit_message_for_A

Kiểu i

Thay đổi văn bản thành những gì bạn muốn thông điệp cam kết mới trông như thế nào. Tôi khuyên bạn nên mô tả về các thay đổi trong cam kết AD:

new_commit_message_for_A_and_D

Nhập EscrồiZZ

git log --oneline -4

E new_commit_message_for_A_and_D
C commit_message_for_C
B commit_message_for_B

git show E

(You should see a diff showing a combination of changes from A and D)

Bây giờ bạn đã tạo một cam kết mới E. Cam kết ADkhông còn trong lịch sử của bạn nhưng không biến mất. Bạn vẫn có thể khôi phục chúng tại thời điểm này và trong một thời gian bằng cách git rebase --hard D( git rebase --hardsẽ phá hủy mọi thay đổi cục bộ! ).


3

Đối với những người sử dụng SourceTree :

Hãy chắc chắn rằng bạn chưa đẩy các cam kết.

  1. Kho lưu trữ> Rebase tương tác ...
  2. Kéo D (cam kết mới hơn) ở ngay trên A (cam kết cũ hơn)
  3. Đảm bảo cam kết D được tô sáng
  4. Nhấp chuột Squash with previous

1

Rebase tương tác hoạt động tốt cho đến khi bạn có nhánh tính năng lớn với 20-30 cam kết và / hoặc vài lần hợp nhất từ ​​chủ hoặc / và sửa các xung đột trong khi bạn đang cam kết trong chi nhánh của mình. Ngay cả với việc tìm kiếm các cam kết của tôi thông qua lịch sử và thay thế pickbằng squashkhông hoạt động ở đây. Vì vậy, tôi đã tìm kiếm một cách khác và tìm thấy bài viết này . Tôi đã thay đổi để làm việc này trên nhánh riêng:

git checkout master
git fetch
git pull
git merge branch-name
git reset origin/master
git branch -D branch-name
git checkout -b branch-name
git add --all
#Do some commit
git push -f --set-upstream origin branch-name

Trước đó, tôi đã nhận được yêu cầu kéo của mình với khoảng ~ 30 lần xác nhận với 2-3 lần hợp nhất từ ​​chủ + sửa lỗi xung đột. Và sau này tôi đã nhận được PR rõ ràng với một cam kết.

PS ở đây là bash script để thực hiện các bước này trong automode.


Giải pháp đầu tiên trong bài viết đó thực sự rất hay, cảm ơn vì liên kết
Hoody

-1

$ git thanh toán chính

$ git log - dòng

D
C
B
A

$ git rebase --onto ĐẦU ^^ ^

$ git log - dòng

D
A

Tôi nghĩ bạn có ý nghĩa --oneline? Và có vẻ như bạn đã đánh rơi CBđó không phải là điều OP dự định.
bstpierre

Không làm việc cho tôi. Nó di chuyển cả ĐẦU và chủ của tôi xuống A, nhưng không hợp nhất D thành A ( git show A) và D, C và B bị mất trong nhật ký giới thiệu của tôi. Phải git rebase Dquay lại.
Nate
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.