Làm thế nào để bạn squash cam kết thành một bản vá với bản vá định dạng git?


163

Tôi đã có tám cam kết trên một chi nhánh mà tôi muốn gửi email cho một số người chưa được giác ngộ. Cho đến nay, mọi thứ tôi làm đều cung cấp cho tôi 8 tệp vá hoặc bắt đầu cung cấp cho tôi các tệp vá cho mọi cam kết trong lịch sử của chi nhánh, kể từ đầu thời gian. Tôi đã sử dụng git rebase - tương tác để xóa sạch các cam kết, nhưng bây giờ mọi thứ tôi thử đều mang lại cho tôi hàng trăm bản vá lỗi từ đầu thời gian. Tôi đang làm gì sai?

git format-patch master HEAD # yields zillions of patches, even though there's 
                             # only one commit since master

Tôi tò mò về phương pháp cuối cùng bạn sẽ sử dụng trong số các đề xuất dưới đây. Hãy cho chúng tôi biết;)
VonC

4
Tôi sẽ sử dụng git diff theo đề xuất của Rob Di Marco. Nhưng tôi đã nghỉ làm được hai tuần, vừa mới chứng kiến ​​sự ra đời của bé gái thứ hai vào tối qua, nên sẽ mất một lúc trước khi tôi sử dụng nó! :)
Skiphoppy

2
Tôi rất thích xem git format-patch --squash master HEAD
schoetbi

1
Hãy thử master..IAD để chỉ định một phạm vi rev.
Konrad Kleine

Câu trả lời:


186

Tôi khuyên bạn nên làm điều này trên một nhánh bỏ đi như sau. Nếu các cam kết của bạn nằm trong nhánh "dòng mới" và bạn đã quay trở lại nhánh "chính" của mình, thì điều này sẽ thực hiện thủ thuật:

[adam@mbp2600 example (master)]$ git checkout -b tmpsquash
Switched to a new branch "tmpsquash"

[adam@mbp2600 example (tmpsquash)]$ git merge --squash newlines
Updating 4d2de39..b6768b2
Fast forward
Squash commit -- not updating HEAD
 test.txt |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

[adam@mbp2600 example (tmpsquash)]$ git commit -a -m "My squashed commits"
[tmpsquash]: created 75b0a89: "My squashed commits"
 1 files changed, 2 insertions(+), 0 deletions(-)

[adam@mbp2600 example (tmpsquash)]$ git format-patch master
0001-My-squashed-commits.patch

Hi vọng điêu nay co ich!


2
Đây là những gì tôi sử dụng khi tôi muốn giữ lịch sử cục bộ (trong trường hợp tôi cần chỉnh sửa bản vá). Nếu không, tôi chỉ sử dụng rebase -i và squash các xác nhận.
sebnow

5
đối với tôi, nó hoạt động đáng tin cậy hơn git comit -m "My squashed commits"nếu không nó sẽ thêm các tệp không bị theo dõi khác
Sebastian

Ngoài ra, bạn có thể chuyển sang một cam kết cụ thể thay vì chủ và thực hiện điều này để tạo một bản vá từ cam kết thành cam kết:git checkout 775ec2a && git checkout -b patch && git merge --squash branchname
Berry M.

131

Chỉ cần thêm một giải pháp nữa vào nồi: Nếu bạn sử dụng thay thế:

git format-patch master --stdout > my_new_patch.diff

Sau đó, nó vẫn sẽ là 8 bản vá ... nhưng tất cả chúng sẽ nằm trong một bản vá duy nhất và sẽ áp dụng như một bản vá với:

git am < my_new_patch.diff

13
Tôi thích giải pháp này. Điều đáng chú ý là đôi khi nó có thể tạo ra một bản vá lớn hơn nhiều so với phương pháp được mô tả bởi @Adam Alexander. Điều này là do một số tệp có thể được chỉnh sửa nhiều lần thông qua các cam kết. Phương pháp này xử lý từng cam kết riêng biệt, ngay cả khi một số tệp được hoàn nguyên. Nhưng hầu hết thời gian đây không phải là một vấn đề.
gumik

1
Tùy chọn này là tuyệt vời nếu bạn đang cố gắng tránh sáp nhập! (ví dụ: bạn muốn bỏ qua một số cam kết). Bạn vẫn có thể git rebase -i ...và đè bẹp tất cả xuống còn một.
Jorge Orpinel

21

Tôi luôn sử dụng git diff vì vậy trong ví dụ của bạn, một cái gì đó như

git diff master > patch.txt

15
Ngoại trừ bạn mất tất cả các thông điệp cam kết và siêu dữ liệu. Vẻ đẹp của git format-patch là có thể tái cấu trúc toàn bộ chi nhánh từ một tập hợp các bản vá.
mcepl

phương thức tạo nhánh và squash có lợi ích là kết hợp tất cả các thông điệp cam kết thành một, nhưng phương pháp này nhanh hơn nhiều nếu bạn muốn viết tin nhắn cam kết của riêng mình
woojoo666

trên thực tế, một tin nhắn kết hợp có thể được thực hiện bằng cách sử dụng git log, xem câu trả lời của tôi để biết ví dụ
woojoo666

2
Tùy chọn đẹp, nhưng xem xét rằng 'git diff' không thể bao gồm các khác biệt nhị phân (có thể có tùy chọn để thực hiện).
Santiago Villafuerte

18

Đây là bản phóng tác của câu trả lời của Adam Alexander, trong trường hợp những thay đổi của bạn thuộc nhánh chính. Điều này làm như sau:

  • Tạo một nhánh "tmpsquash" mới từ điểm chúng ta muốn (tìm khóa SHA đang chạy "git --log" hoặc với gitg. Chọn cam kết bạn muốn là đầu tmpsquash, các cam kết sau đó sẽ là chủ các cam kết bị đè bẹp).
  • Hợp nhất các thay đổi từ chủ đến tmpsquash.
  • Cam kết thay đổi squash thành tmpsquash.
  • Tạo các bản vá với các cam kết bị đè bẹp.
  • Quay trở lại chi nhánh

laura@rune:~/example (master)$ git branch tmpsquash ba3c498878054e25afc5e22e207d62eb40ff1f38
laura@rune:~/example (master)$ git checkout tmpsquash
Switched to branch 'tmpsquash'
laura@rune:~/example (tmpsquash)$ git merge --squash master
Updating ba3c498..40386b8
Fast-forward
Squash commit -- not updating HEAD

[snip, changed files]

11 files changed, 212 insertions(+), 59 deletions(-)
laura@rune:~/example  (tmpsquash)$ git commit -a -m "My squashed commits"
[test2 6127e5c] My squashed commits
11 files changed, 212 insertions(+), 59 deletions(-)
laura@rune:~/example  (tmpsquash)$ git format-patch master
0001-My-squashed-commits.patch
laura@rune:~/example  (tmpsquash)$ git checkout master
Switched to branch 'master'
laura@rune:~/example  (master)$

Thực hiện các thay đổi trong bản gốc là một kiểu chống đối nếu bạn sẽ không tự đẩy chúng ra xa. Và nếu bạn muốn, có lẽ bạn không cần phải tạo các tệp vá cho chúng.
ivan_pozdeev

18

Như bạn đã biết, git format-patch -8 HEADsẽ cung cấp cho bạn tám bản vá.

Nếu bạn muốn 8 cam kết của mình xuất hiện dưới dạng một và không ngại viết lại lịch sử của chi nhánh ( o-o-X-A-B-C-D-E-F-G-H), bạn có thể:

git rebase -i
// squash A, B, C, D, E ,F, G into H

hoặc, và nó sẽ là một giải pháp tốt hơn, phát lại tất cả 8 cam kết của bạn từ X(cam kết trước 8 cam kết của bạn) trên một nhánh mới

git branch delivery X
git checkout delivery
git merge --squash master
git format-patch HEAD

Theo cách đó, bạn chỉ có một cam kết trên nhánh "phân phối" và nó thể hiện tất cả 8 cam kết cuối cùng của bạn


git merge masterchỉ đơn giản là tiến nhanh và không ép bí. Thay đổi để git merge --squash mastercó được tất cả các cam kết bị đè bẹp và hiển thị trong khu vực được tổ chức. Dòng chảy của bạn sẽ làm việc với sự thay đổi này. (Tôi đang sử dụng phiên bản git 2.1.0.)
Stunner

5

Định dạng vá giữa hai thẻ:

git checkout <source-tag>
git checkout -b <tmpsquash>
git merge --squash <target-tag>
git commit -a -m "<message>"
git format-patch <source-tag>

5

Cách dễ nhất là sử dụng git diffvà thêm vào git lognếu bạn muốn thông báo cam kết kết hợp mà phương thức squash sẽ xuất ra. Ví dụ: để tạo bản vá giữa cam kết abcd1234:

git diff abcd..1234 > patch.diff
git log abcd..1234 > patchmsg.txt

Sau đó khi áp dụng các bản vá:

git apply patch.diff
git add -A
git reset patch.diff patchmsg.txt
git commit -F patchmsg.txt

Đừng quên --binaryđối số git diffkhi xử lý các tệp không phải là văn bản, ví dụ: hình ảnh hoặc video.


0

Dựa trên câu trả lời của Adam Alexandre:

git checkout newlines
## must be rebased to master
git checkout -b temporary
# squash the commits
git rebase -i master
git format-patch master
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.