Câu hỏi Squash X cuối cùng của tôi cam kết với nhau bằng cách sử dụng Git


Làm thế nào tôi có thể squash X cuối cùng của tôi cam kết với nhau thành một cam kết bằng cách sử dụng Git?


2322
2018-03-04 04:11


gốc


Câu hỏi tương tự: stackoverflow.com/questions/7275508/… - koppor
Liên quan: Git - kết hợp nhiều cam kết trước khi đẩy.
@matt TortoiseGit là công cụ của bạn. Nó cung cấp một chức năng duy nhất "Kết hợp với một cam kết" sẽ tự động gọi tất cả các bước trong nền. Thật không may chỉ có sẵn cho Windows. Xem câu trả lời của tôi dưới đây. - Matthias M
@Thomas: hãy giải thích cách "sử dụng git well" khi đánh giá phải được thực hiện trên mã cam kết và chúng tôi cần cập nhật dựa trên nhận xét đánh giá và cam kết lại, nhưng không muốn thúc đẩy việc đánh giá dựa trên đánh giá? - Jeff Learman
Cho squashing tối đa THE cam kết đầu tiên thấy điều này - stackoverflow.com/questions/1657017/… - goelakash


Các câu trả lời:


Sử dụng git rebase -i <after-this-commit> và thay thế "chọn" vào lần commit thứ hai và sau đó bằng "squash" hoặc "fixup", như được mô tả trong hướng dẫn.

Trong ví dụ này, <after-this-commit> hoặc là hàm băm SHA1 hoặc vị trí tương đối từ HEAD của nhánh hiện tại mà từ đó các commit được phân tích cho lệnh rebase. Ví dụ: nếu người dùng muốn xem 5 cam kết từ HEAD hiện tại trong quá khứ, lệnh git rebase -i HEAD~5.


1268
2018-03-04 04:18



Điều này, tôi nghĩ, trả lời câu hỏi này tốt hơn một chút stackoverflow.com/a/5201642/295797 - Roy Truelove
Có nghĩa là gì <after-this-commit>? - jtheletter
<after-this-commit> cam kết X + 1 tức là cha mẹ của cam kết cũ nhất mà bạn muốn bí. - joozek
Tôi thấy câu trả lời này quá ngắn gọn để hiểu rõ ràng. Một ví dụ sẽ giúp ích. - Ian Ollmann
Sự khác biệt giữa điều này rebase -i cách tiếp cận và reset --soft Là, rebase -icho phép tôi giữ lại tác giả cam kết, trong khi reset --soft cho phép tôi để recommit. Đôi khi tôi cần phải đè bẹp cam kết của yêu cầu kéo nhưng vẫn duy trì thông tin tác giả. Đôi khi tôi cần phải thiết lập lại mềm trên cam kết của riêng tôi. Upvotes cho cả hai câu trả lời tuyệt vời anyways. - zionyx


Bạn có thể làm điều này khá dễ dàng mà không cần git rebase hoặc là git merge --squash. Trong ví dụ này, chúng tôi sẽ bí mật 3 lần commit cuối cùng.

Nếu bạn muốn viết thông điệp cam kết mới từ đầu, điều này đủ:

git reset --soft HEAD~3 &&
git commit

Nếu bạn muốn bắt đầu chỉnh sửa thông báo cam kết mới bằng cách ghép nối các thông báo cam kết hiện có (ví dụ: tương tự như những gì bạn chọn / squash / squash /… / squash git rebase -i danh sách hướng dẫn sẽ bắt đầu bạn với), sau đó bạn cần trích xuất các thông điệp đó và chuyển chúng đến git commit:

git reset --soft HEAD~3 && 
git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"

Cả hai phương pháp này đều đè bẹp ba cam kết cuối cùng thành một cam kết mới trong cùng một cách. Việc thiết lập lại mềm chỉ cần điểm lại HEAD đến cam kết cuối cùng mà bạn không muốn bí. Cả chỉ mục và cây làm việc đều không được chạm vào bằng cách đặt lại mềm, để chỉ mục ở trạng thái mong muốn cho cam kết mới của bạn (nghĩa là nó đã có tất cả thay đổi từ các cam kết mà bạn sắp "vứt bỏ").


2603
2018-03-05 04:19



Ha! Tôi thích phương pháp này. Nó là một trong những đóng cửa với tinh thần của vấn đề. Thật đáng tiếc là nó đòi hỏi rất nhiều voodoo. Một cái gì đó như thế này nên được thêm vào một trong những lệnh cơ bản. Có thể git rebase --squash-recent, hoặc thậm chí git commit --amend-many. - Adrian Ratnapala
@ A-B-B: Nếu chi nhánh của bạn có bộ "thượng nguồn" thì bạn có thể sử dụng branch@{upstream} (hoặc chỉ @{upstream} cho nhánh hiện tại; trong cả hai trường hợp, phần cuối cùng có thể được viết tắt thành @{u}; xem gitrevisions). Điều này có thể khác với của bạn"Cam kết cuối cùng được đẩy" (ví dụ: nếu ai đó đã đẩy thứ gì đó được xây dựng trên đỉnh lần đẩy gần đây nhất của bạn và sau đó bạn tìm nạp), nhưng có vẻ như nó có thể gần với những gì bạn muốn. - Chris Johnsen
Loại kinda này yêu cầu tôi push -f nhưng nếu không thì thật đáng yêu, cảm ơn. - 2rs2ts
@ 2rs2ts git push -f âm thanh nguy hiểm. Hãy cẩn thận để chỉ squash cam kết địa phương. Không bao giờ chạm vào các cam kết đã được đẩy! - Matthias M
Cảm ơn bạn đã cung cấp giải pháp không yêu cầu nhập squash / fixup theo cách thủ công cho mọi cam kết đơn lẻ, không giống như tất cả đề xuất rebase tương tác ở nơi khác. - Antimony


Bạn có thể dùng git merge --squash cho điều này, đó là một chút thanh lịch hơn git rebase -i. Giả sử bạn đang làm chủ và bạn muốn đè bẹp 12 lần commit cuối cùng thành một.

CẢNH BÁO: Trước tiên, hãy đảm bảo bạn cam kết công việc của mình — hãy kiểm tra xem git status sạch sẽ (kể từ git reset --hard sẽ vứt bỏ các thay đổi theo giai đoạn và không được tổ chức)

Sau đó:

# Reset the current branch to the commit just before the last 12:
git reset --hard HEAD~12

# HEAD@{1} is where the branch was just before the previous command.
# This command sets the state of the index to be as it would just
# after a merge from that commit:
git merge --squash HEAD@{1}

# Commit those squashed changes.  The commit message will be helpfully
# prepopulated with the commit messages of all the squashed commits:
git commit

Các tài liệu cho git merge mô tả --squash tùy chọn chi tiết hơn.


Cập nhật: lợi thế thực sự duy nhất của phương pháp này trên đơn giản hơn git reset --soft HEAD~12 && git commit đề xuất bởi Chris Johnsen trong câu trả lời của anh ấy là bạn nhận được thông báo cam kết được chuẩn bị trước với mọi thông điệp cam kết mà bạn đang bẻ khóa.


575
2018-03-04 06:10



Bạn nói điều này là 'thanh lịch' hơn git rebase -i, nhưng bạn không đưa ra lý do tại sao. Dự kiến ​​-1ing này bởi vì có vẻ như với tôi rằng trên thực tế điều ngược lại là đúng và đây là một hack; không phải bạn thực hiện nhiều lệnh hơn cần thiết chỉ để buộc git merge làm một trong những điều git rebase được thiết kế đặc biệt cho? - Mark Amery
@Mark Amery: Có nhiều lý do khác nhau mà tôi nói rằng điều này là thanh lịch hơn. Ví dụ, nó không liên quan đến việc tạo một trình soạn thảo không cần thiết và sau đó tìm kiếm và thay thế cho một chuỗi trong tệp "cần làm". Sử dụng git merge --squash cũng dễ sử dụng hơn trong tập lệnh. Về cơ bản, lý do là bạn không cần "tương tác" của git rebase -i ở tất cả cho điều này. - Mark Longair
Một lợi thế khác là git merge --squash ít có khả năng sản xuất xung đột hợp nhất khi đối mặt với di chuyển / xóa / đổi tên so với rebasing, đặc biệt nếu bạn đang hợp nhất từ ​​một chi nhánh địa phương. (từ chối trách nhiệm: chỉ dựa trên một trải nghiệm, sửa tôi nếu điều này không đúng trong trường hợp chung!) - Cheezmeister
Tôi luôn luôn rất miễn cưỡng khi nói đến thiết lập lại cứng - Tôi muốn sử dụng một thẻ tạm thời thay vì HEAD@{1} chỉ ở bên an toàn, v.d. khi công việc của bạn bị gián đoạn trong một giờ do mất điện, v.v. - Tobias Kienzler
@B T: Phá hủy cam kết của bạn? :( Tôi không chắc bạn ngụ ý gì về điều đó. Bất cứ điều gì bạn cam kết sẽ dễ dàng có thể quay trở lại từ bản tin của git. Nếu bạn có công việc không được cam kết, nhưng các tệp đã được dàn dựng, bạn vẫn có thể nội dung của họ trở lại, mặc dù điều đó sẽ hiệu quả hơn. Nếu công việc của bạn thậm chí không được dàn dựng, tuy nhiên, tôi e rằng có rất ít việc có thể được thực hiện; đó là lý do tại sao câu trả lời nói lên phía trước: "Trước tiên hãy kiểm tra xem trạng thái git có sạch không (kể từ khi git reset --hard sẽ vứt bỏ các thay đổi theo giai đoạn và không được thay đổi)". - Mark Longair


Tôi khuyên bạn nên tránh git reset khi có thể - đặc biệt là cho người mới sử dụng Git. Trừ khi bạn thực sự cần tự động hóa quy trình dựa trên con số của cam kết, có một cách kỳ lạ hơn ...

  1. Đặt các cam kết bị đè bẹp trên nhánh đang hoạt động (nếu chúng chưa sẵn sàng) - hãy sử dụng gitk cho mục này
  2. Xem chi nhánh mục tiêu (ví dụ: 'chính')
  3. git merge --squash (working branch name)
  4. git commit

Thông điệp cam kết sẽ được chuẩn bị sẵn dựa trên bóng quần.


136
2018-03-14 23:24



Đây là phương pháp an toàn nhất: không reset soft / hard (!!), hoặc reflog được sử dụng! - TeChn4K
Nó sẽ là tuyệt vời nếu bạn mở rộng trên (1). - Adam
@ Adam: Về cơ bản, điều này có nghĩa là sử dụng giao diện GUI của gitk để gắn nhãn dòng mã mà bạn đang đè bẹp và cũng gắn nhãn nền tảng để đổ vào đó. Trong trường hợp bình thường, cả hai nhãn này sẽ tồn tại, vì vậy bước (1) có thể được bỏ qua. - nobar
Lưu ý rằng phương thức này không đánh dấu nhánh đang hoạt động khi được hợp nhất hoàn toàn, vì vậy việc loại bỏ nó đòi hỏi phải xóa bỏ việc xóa. :( - Kyrstellaine
Đối với (1), tôi đã tìm thấy git branch your-feature && git reset --hard HEAD~N cách thuận tiện nhất. Tuy nhiên, nó liên quan đến git thiết lập lại một lần nữa, mà câu trả lời này đã cố gắng để tránh. - eis


Dựa trên Câu trả lời của Chris Johnsen,

Thêm bí danh "bí danh" toàn cầu từ bash: (hoặc Git Bash trên Windows)

git config --global alias.squash '!f(){ git reset --soft HEAD~${1} && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; };f'

... hoặc sử dụng Dấu nhắc Lệnh của Windows:

git config --global alias.squash "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"


Của bạn ~/.gitconfig bây giờ sẽ chứa bí danh này:

[alias]
    squash = "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"


Sử dụng:

git squash N

... Tự động làm đổ N cam kết, bao gồm.

Lưu ý: Thông báo cam kết kết quả là sự kết hợp của tất cả các cam kết bị đè nén, theo thứ tự. Nếu bạn không hài lòng với điều đó, bạn luôn có thể git commit --amendđể sửa đổi nó theo cách thủ công. (Hoặc, chỉnh sửa bí danh để phù hợp với sở thích của bạn.)


97
2018-02-19 19:21



Thú vị, nhưng tôi muốn thay vì nhập tin nhắn cam kết đè bẹp bản thân mình, như một bản tóm tắt mô tả của nhiều cam kết của tôi, hơn là nó tự động nhập cho tôi. Vì vậy, tôi muốn chỉ định git squash -m "New summary."và có N được xác định tự động là số lần cam kết chưa được xóa. - A-B-B
@ A-B-B, Điều này nghe giống như một câu hỏi riêng biệt. (Tôi không nghĩ nó chính xác là những gì OP hỏi, tôi chưa bao giờ cảm thấy cần thiết cho nó trong công việc bí mật của mình). - EthanB
Điều này khá ngọt ngào. Cá nhân tôi muốn một phiên bản sử dụng thông điệp cam kết từ lần đầu tiên của các cam kết bị đè bẹp cùng nhau. Sẽ tốt cho những thứ như chỉnh sửa khoảng trắng. - funroll
@funroll Đồng ý. Chỉ cần thả tin nhắn cam kết cuối cùng là một nhu cầu siêu phổ biến đối với tôi. Chúng ta có thể nghĩ rằng ... - Steve Clay
@ A-B-B bạn có thể sử dụng git commit --amend để thay đổi thêm thông điệp, nhưng bí danh này cho phép bạn có một khởi đầu tốt về những gì nên ở trong thông điệp cam kết. - dashesy


Nếu bạn sử dụng TortoiseGit, bạn có thể thực hiện chức năng Combine to one commit:

  1. Mở menu ngữ cảnh TortoiseGit
  2. Lựa chọn Show Log
  3. Đánh dấu các cam kết có liên quan trong giao diện nhật ký
  4. Lựa chọn Combine to one commit từ menu ngữ cảnh

Combine commits

Hàm này sẽ tự động thực hiện tất cả các bước git đơn lẻ cần thiết. Không may chỉ có sẵn cho Windows.


46
2017-11-06 12:51



Theo tôi biết, điều này sẽ không hoạt động cho các cam kết hợp nhất. - Thorkil Holm-Jacobsen
Mặc dù nó không được nhận xét bởi bất kỳ người nào khác, điều này thậm chí làm việc cho các cam kết không có ở HEAD. Ví dụ, nhu cầu của tôi là để bí mật một số cam kết WIP tôi đã làm với một mô tả lành mạnh hơn trước khi đẩy. Làm việc rất đẹp. Tất nhiên, tôi vẫn hy vọng tôi có thể học cách làm điều đó bằng lệnh. - Charles Roberto Canato


Nhờ vào bài đăng trên blog tiện dụng này Tôi thấy rằng bạn có thể sử dụng lệnh này để squash 3 commit cuối cùng:

git rebase -i HEAD~3

Đây là tiện dụng vì nó hoạt động ngay cả khi bạn đang ở trên một chi nhánh địa phương không có thông tin theo dõi / repo từ xa.

Lệnh sẽ mở trình soạn thảo rebase tương tác, sau đó cho phép bạn sắp xếp lại, squash, reword, vv như bình thường.


46
2018-05-17 06:19





Dựa trên bài viết này Tôi tìm thấy phương pháp này dễ dàng hơn cho usecase của tôi.

Chi nhánh 'dev' của tôi đã đi trước 'origin / dev' bởi 96 cam kết (vì vậy các cam kết này chưa được đẩy tới điều khiển từ xa).

Tôi muốn đè bẹp những cam kết này thành một trước khi thúc đẩy sự thay đổi. Tôi đặt trước để thiết lập lại nhánh thành trạng thái của 'origin / dev' (điều này sẽ để lại tất cả các thay đổi từ 96 commit không có lề) và sau đó commit các thay đổi cùng một lúc:

git reset origin/dev
git add --all
git commit -m 'my commit message'

29
2018-05-22 00:41



Đúng thứ tôi cần. Squash xuống cam kết từ chi nhánh của tôi, và sau đó tôi git anh đào chọn mà cam kết vào chủ của tôi. - David Victor
Điều này không làm giảm các cam kết trước đó! - Igor Ganapolsky
bạn có thể giải thích thêm một chút về @igorGanapolsky không? - trudolf
@trudolf Đây không phải là thực sự squashing (chọn cam kết cá nhân để bí). Đây là nhiều cam kết tất cả các thay đổi của bạn cùng một lúc. - Igor Ganapolsky
có, do đó nó squashes tất cả các cam kết của bạn thành một. Xin chúc mừng! - trudolf