Làm thế nào để sử dụng git merge --squash?


1209

Tôi có một máy chủ Git từ xa, đây là kịch bản mà tôi muốn thực hiện:

  • Đối với mỗi lỗi / tính năng tôi tạo một nhánh Git khác nhau

  • Tôi tiếp tục cam kết mã của mình trong chi nhánh Git đó với các tin nhắn Git không chính thức

  • Trong kho lưu trữ hàng đầu, chúng tôi phải thực hiện một cam kết cho một lỗi với thông báo Git chính thức

Vậy làm cách nào tôi có thể hợp nhất chi nhánh của mình với chi nhánh từ xa để họ chỉ nhận được một cam kết cho tất cả các đăng ký của tôi (tôi thậm chí muốn cung cấp thông báo cam kết cho việc này)?


1
Tôi không chắc chắn nếu tôi hoàn toàn hiểu bạn, nhưng bạn có thể muốn một "hợp nhất bạch tuộc".
MatrixFrog

27
Tôi thường sử dụng git rebase -i để thu gọn tất cả các cam kết của tôi thành một cam kết và viết lại thông điệp cam kết. Sau đó tôi gửi nó ngược dòng.
Edward Falk

17
git merge --squashthực hiện tất cả trên dòng lệnh trong một lần bắn và bạn chỉ hy vọng nó hoạt động. git rebase -iđưa ra một trình soạn thảo và cho phép bạn tinh chỉnh rebase. Nó chậm hơn, nhưng bạn có thể thấy những gì bạn đang làm. Ngoài ra, có sự khác biệt giữa rebase và merge mà hơi quá liên quan để giải quyết trong một bình luận.
Edward Falk

4
vấn đề với tất cả các câu trả lời này là bạn phải ở nhánh chính cục bộ và chạy lệnh merge --squash ... Tôi muốn chạy hợp nhất --squash từ nhánh tính năng chứ không phải nhánh chính..vì vậy Khi tôi hoàn thành, tôi có thể đẩy nhánh tính năng đến điều khiển từ xa và gửi PR, điều đó có thể không?
Alexander Mills

2
@AlexanderMills, tôi nghĩ bạn chỉ cần một nhánh tính năng thứ hai (được nhân bản từ nhánh chính). Thực hiện merge --squashtừ cái cũ sang cái mới, và sau đó hợp nhất nhánh mới để làm chủ. Nhánh cũ trở nên lỗi thời.
Gyromite

Câu trả lời:


2002

Nói rằng nhánh sửa lỗi của bạn được gọi bugfixvà bạn muốn hợp nhất nó vào master:

git checkout master
git merge --squash bugfix
git commit

Điều này sẽ lấy tất cả các xác nhận từ bugfixchi nhánh, ép chúng thành 1 cam kết và hợp nhất nó với masterchi nhánh của bạn .


Giải thích :

git checkout master

Chuyển sang masterchi nhánh của bạn .

git merge --squash bugfix

Nhận tất cả các cam kết từ bugfixchi nhánh và hợp nhất nó với chi nhánh hiện tại của bạn.

git commit

Tạo một cam kết duy nhất từ ​​các thay đổi được hợp nhất.

Việc bỏ qua -mtham số cho phép bạn sửa đổi một thông báo cam kết dự thảo có chứa mọi thông báo từ các cam kết bị nén của bạn trước khi hoàn tất cam kết của bạn.


222
Nếu bạn muốn giữ các tham chiếu đến các thông điệp cam kết cũ, bạn có thể viết git commit(không có -mparam) và bạn sẽ phải sửa đổi một thông điệp cam kết được phác thảo có chứa tất cả các thông điệp cam kết mà bạn đã xóa.
Alex

12
Bạn có thể đạt được điều tương tự bằng cách làm git commit --amend -m '...'sau này.
Janusz Lenar

19
Trong trường hợp xung đột hợp nhất xảy ra và bạn giải quyết các xung đột này, git commitsẽ không còn hiển thị thông báo cam kết hữu ích có chứa tất cả các thông điệp cam kết bạn đã xóa. Trong trường hợp đó, hãy thử git commit --file .git/SQUASH_MSG(thông qua stackoverflow.com/a/11230783/923560 ).
Abdull

23
Hãy nhớ rằng squashing sẽ theo mặc định thuộc tính cam kết với squasher . Để giữ tác giả ban đầu, bạn cần xác định rõ ràng như vậy:git commit -a --author="Author" --message="Issue title #id"
gabious 17/8/2016

5
git merge --squashcho phép bạn tạo một cam kết duy nhất trên đầu nhánh hiện tại có hiệu lực giống như sáp nhập một nhánh khác. Nhưng nó sẽ không tạo ra bản ghi hợp nhất, điều đó có nghĩa là yêu cầu kéo của bạn do kết quả sẽ không có thay đổi, nhưng sẽ không được đánh dấu là hợp nhất! Vì vậy, bạn sẽ chỉ cần xóa chi nhánh đó để được thực hiện.
am0wa

129

Điều cuối cùng đã làm rõ điều này đối với tôi là một bình luận cho thấy:

git checkout main
git merge --squash feature

là tương đương với việc làm:

git checkout feature
git diff main > feature.patch
git checkout main
patch -p1 < feature.patch
git add .

Khi tôi muốn hợp nhất một nhánh tính năng với 105 (!!) cam kết và tất cả chúng được nén thành một, tôi không muốn git rebase -i origin/mastervì tôi cần giải quyết riêng các xung đột hợp nhất cho từng cam kết trung gian (hoặc ít nhất là các cam kết trung gian git không thể tìm ra chính nó). Việc sử dụng mang lại cho git merge --squashtôi kết quả mà tôi muốn, về một cam kết duy nhất để hợp nhất toàn bộ một nhánh tính năng. Và, tôi chỉ cần thực hiện tối đa một giải quyết xung đột thủ công.


75
Tôi đặc biệt khuyên bạn nên thực hiện hợp nhất trong nhánh tính năng trước git merge mastervà chỉ sau đó git merge --squash featuretrong nhánh chính.
dotancohen

8
@dotancohen Xin lỗi để nạo vét một bình luận cũ :) Những gì đạt được từ việc hợp nhất trong nhánh tính năng trước khi thực hiện git merge --squash featuretừ nhánh chính?
bitsmack

57
Trước tiên, bạn muốn hợp nhất chủ vào nhánh tính năng và xử lý mọi sửa lỗi thủ công trong nhánh tính năng của bạn. Điều đó cũng cho phép bạn chạy thử nghiệm và đảm bảo nhánh tính năng của bạn hoạt động chính xác. Sau đó, bạn được đảm bảo rằng bạn có thể tự động hợp nhất nhánh tính năng của mình thành chủ.
Dan Kohn

4
@dankohn Tôi đề nghị bạn thêm lời giải thích trong nhận xét trên vào câu trả lời của bạn.
guntbert

3
@bitsmack: bạn sẽ hợp nhất chủ vào tính năng trước. Điều này cho bạn cơ hội giải quyết xung đột trên tính năng trước khi hợp nhất tính năng này thành chủ
Mike

97

Bạn muốn hợp nhất với tùy chọn squash. Đó là nếu bạn muốn làm một chi nhánh tại một thời điểm.

git merge --squash feature1

Nếu bạn muốn hợp nhất tất cả các nhánh cùng một lúc với các cam kết duy nhất, thì trước tiên hãy rebase tương tác và xóa từng tính năng sau đó bạch tuộc hợp nhất:

git checkout feature1
git rebase -i master

Đổ vào một cam kết sau đó lặp lại cho các tính năng khác.

git checkout master
git merge feature1 feature2 feature3 ...

Sự hợp nhất cuối cùng đó là một "hợp nhất bạch tuộc" bởi vì nó hợp nhất rất nhiều chi nhánh cùng một lúc.

Hi vọng điêu nay co ich


3
Tại sao bạn lại nổi loạn?
Umair A.

12
@UmairAshraf đó là một rebase tương tác cung cấp cho bạn tùy chọn để thực hiện một quả bóng trong nhánh của bạn.
andho

1
Nổi loạn là một ý tưởng tồi. Đừng rebase cam kết đã được công bố
Sebi2020

1
@ Sebi2020 git merge --squash sẽ rebase các cam kết đã được công bố của bạn theo cách tồi tệ hơn một rebase tương tác. Một rebase tương tác (trên một nhánh tính năng) mang ít hoặc không có tác dụng phụ.
xiix

1
@xiix Điều này chỉ đúng nếu bạn là người duy nhất làm việc với nhánh tính năng. Đây không phải là một giả định bạn có thể thực hiện. Tôi khuyên bạn nên đọc các trang liên quan đến việc nổi loạn trên Git-SCM . Nó ghi " Không phản hồi các cam kết tồn tại bên ngoài kho lưu trữ của bạn và mọi người có thể đã làm việc dựa trên chúng. " Và nếu bạn không biết chắc chắn liệu mọi người đã làm việc dựa trên các cam kết được công bố hay chưa (vì bạn không thể biết vì sự phân cấp bản chất của git) bạn sẽ không làm điều đó.
Sebi2020

23

Nếu bạn đã git merge bugfixtrên main, bạn có thể bí merge bạn cam kết thành một với:

git reset --soft HEAD^1
git commit

git reset --soft HEAD^1dường như hoàn tác cam kết cuối cùng được thực hiện trước khi hợp nhất, ít nhất là trong trường hợp hợp nhất là một chuyển tiếp nhanh.
Jesper Matthiesen

@JesperMatthiesen trong trường hợp chuyển tiếp nhanh bạn không nhận được một cam kết hợp nhất, vì vậy bạn sẽ làm git reset --soft HEAD^<number-of-commits-to-squash>.
qwertzguy

Điều này giúp tôi dẹp mọi thứ thành một cam kết duy nhất sau khi hợp nhất xuôi dòng.
killjoy

18

Hợp nhất newFeaturechi nhánh mastervới một cam kết tùy chỉnh:

git merge --squash newFeature && git commit -m 'Your custom commit message';

Nếu thay vào đó, bạn làm

git merge --squash newFeature && git commit

bạn sẽ nhận được một thông báo cam kết sẽ bao gồm tất cả các newFeaturecam kết chi nhánh mà bạn có thể tùy chỉnh.

Tôi giải thích cặn kẽ ở đây: https://youtu.be/FQNAIacelT4


10

Tôi biết câu hỏi này không phải về Github cụ thể, nhưng vì Github được sử dụng rộng rãi và đây là câu trả lời tôi đang tìm kiếm, tôi sẽ chia sẻ nó ở đây.

Github có khả năng thực hiện hợp nhất squash, tùy thuộc vào các tùy chọn hợp nhất được kích hoạt cho kho lưu trữ.

Nếu hợp nhất squash được bật, tùy chọn "Squash and merge" sẽ xuất hiện trong danh sách thả xuống dưới nút "Hợp nhất".

Ảnh chụp màn hình của tính năng Github "Squash and merge"


GitHub sử dụng email mặc định được liên kết với tài khoản của bạn. Nếu bạn có nhiều địa chỉ email và bạn cần sử dụng địa chỉ phụ, bạn không thể sử dụng GH UI.
Luca Guidi

4

Giả sử bạn đã làm việc trong tính năng / task1 với nhiều lần xác nhận.

  1. Chuyển đến chi nhánh dự án của bạn (dự án / my_project)

    git checkout project/my_project
    
  2. Tạo một nhánh mới (Feature / task1_ormsfix)

    git checkout -b feature/task1_bugfix
    
  3. Marge với --squashtùy chọn

    git merge --squash feature/task1
    
  4. Tạo một cam kết duy nhất

    git commit -am "add single comments"
    
  5. Đẩy chi nhánh của bạn

    git push --set-upstream origin feature/task1_bugfix
    

1

Cho Git

Tạo một tính năng mới

thông qua Terminal / Shell:

git checkout origin/feature/<featurename>
git merge --squash origin/feature/<featurename>

Điều này không cam kết nó, cho phép bạn xem xét nó đầu tiên.

Sau đó, cam kết và hoàn thành tính năng từ chi nhánh mới này và xóa / bỏ qua cái cũ (cái mà bạn đã phát triển).


@Melebius Tham chiếu duy nhất đến "SourceTree" là trong câu của bạn, nếu đó là một thẻ hoặc câu hỏi trước đó: Nó không còn tồn tại nữa.
Jordan Stefanelli

1
@JordanStefanelli SourceTree đã được sử dụng trong phiên bản gốc của câu trả lời này . Cảm ơn đã thông báo nó đã được sửa!
Melebius

1

nếu bạn gặp lỗi: Không thể cam kết vì bạn có các tệp chưa được trộn.

git checkout master
git merge --squash bugfix
git add .
git commit -m "Message"

đã sửa tất cả các tệp Xung đột

git add . 

bạn cũng có thể sử dụng

git add [filename]

0

Để đè bẹp chi nhánh địa phương của bạn trước khi đẩy nó:

  1. kiểm tra chi nhánh trong câu hỏi để làm việc nếu nó chưa được kiểm tra.

  2. Tìm sha của cam kết lâu đời nhất bạn muốn giữ.

  3. Tạo / kiểm tra một nhánh mới (tmp1) từ cam kết đó.

    git checkout -b tmp1 <sha1-of-commit>

  4. Hợp nhất các nhánh ban đầu vào một nhánh mới.

    git merge --squash <original branch>

  5. Cam kết các thay đổi đã được tạo bởi hợp nhất, với thông báo cam kết tóm tắt.

    git commit -m <msg>

  6. Kiểm tra các chi nhánh ban đầu bạn muốn bí.

    git checkout <branch>

  7. Đặt lại về cam kết ban đầu sha bạn muốn giữ.

    git reset --soft <sha1>

  8. Khởi động lại nhánh này dựa trên nhánh tmp1 mới.

    git rebase tmp1

  9. Đó là nó - bây giờ xóa chi nhánh tmp1 tạm thời một khi bạn chắc chắn mọi thứ đều ổn.


0

Bạn có thể sử dụng công cụ tôi đã tạo để làm cho quá trình này dễ dàng hơn: git-squash . Ví dụ: để xóa tất cả các xác nhận trên nhánh tính năng đã được phân nhánh từ nhánh chính, hãy viết:

git squash master
git push --force
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.