Làm thế nào để squash cam kết trong git sau khi chúng đã được đẩy?


550

Điều này đưa ra một lời giải thích tốt về việc nghiền nát nhiều cam kết:

http://git-scm.com/book/en/Git-Branching-Rebasing

nhưng nó không hoạt động cho các cam kết đã được đẩy. Làm thế nào để tôi xóa sổ một số cam kết gần đây nhất trong cả repos địa phương và từ xa?

EDIT: Khi tôi làm git rebase -i origin/master~4 master, giữ cái đầu tiên là pick, đặt ba cái còn lại squash, rồi thoát (thông qua cx cc trong emacs), tôi nhận được:

$ git rebase -i origin/master~4 master
# Not currently on any branch.
nothing to commit (working directory clean)

Could not apply 2f40e2c... Revert "issue 4427: bpf device permission change option added"
$ git rebase -i origin/master~4 master
Interactive rebase already started

trong đó 2f40 là pickcam kết. Và bây giờ không ai trong số 4 cam kết xuất hiện git log. Tôi dự kiến ​​trình soạn thảo của mình sẽ được khởi động lại để tôi có thể nhập tin nhắn cam kết. Tôi đang làm gì sai?

Câu trả lời:


779

Squash cam kết cục bộ với

git rebase -i origin/master~4 master

và sau đó lực đẩy với

git push origin +master

Sự khác biệt giữa --force+

Từ các tài liệu của git push:

Lưu ý rằng --forceáp dụng cho tất cả các ref được đẩy, do đó sử dụng nó với push.defaultset matchinghoặc với nhiều đích đẩy được cấu hình với remote.*.pushcó thể ghi đè lên các ref khác với nhánh hiện tại (bao gồm cả ref được đặt phía sau đối tác từ xa của chúng). Để đẩy một lực đẩy chỉ vào một nhánh, sử dụng một cú đánh + trước refspec để đẩy (ví dụ: git push origin +masterbuộc một cú đẩy vào masternhánh).


30
bạn cũng có thểgit push --force origin master
Daenyth

7
Daenyth : Có, nhưng tôi luôn thích cú pháp này vì nó ngắn hơn.
Alan Haggai Alavi

86
Và tất nhiên, nhận ra rằng nếu bất kỳ ai khác có thể đã lấy từ kho lưu trữ từ xa, bạn có thể không muốn làm điều này - câu trả lời trong trường hợp đó là "bạn không."
Cascabel

10
Ngoài ra, tôi nghĩ rằng OP đang sao chép chính xác lệnh git rebase -i origin/mastervà thực sự muốn biết làm thế nào để rebase cam kết xa hơn thế, ví dụ git rebase -i origin/master~20 master.
Cascabel

6
gstackoverflow :+chỉ buộc các refspec được tiền tố bởi nó. --forcesẽ buộc tất cả các refspecs được đẩy. Xin vui lòng xem câu trả lời cập nhật.
Alan Haggai Alavi

120

Trên một nhánh tôi đã có thể làm điều đó như thế này (cho 4 lần xác nhận gần nhất)

git checkout my_branch
git reset --soft HEAD~4
git commit
git push --force origin my_branch

1
Làm điều này với lệnh mềm trên một nhánh đã được đẩy cuối cùng đã đẩy một tấn người khác cam kết với tôi.
cchamberlain 4/2/2016

3
Ở trên? Làm thế nào nó có thể nhiều hơn 4? Bạn có thể xây dựng?
jakob-r

Điều đó tôi không chắc chắn nhưng nó có liên quan đến việc cố gắng đè bẹp một cam kết đã được đẩy. Có vẻ như những người khác có kinh nghiệm tương tự ở đây - stackoverflow.com/questions/5189560/ từ
cchamberlain

4
Tôi đã chấp nhận điều này như câu trả lời mong đợi. Sạch sẽ hơn mà chấp nhận câu trả lời.
vikramvi

4
Đây là câu trả lời rõ ràng và được chấp nhận nhất chỉ trong 4 bước
Ameya Salagre

45

Khác biệt nhỏ với câu trả lời được chấp nhận, nhưng tôi đã gặp rất nhiều khó khăn trong việc đè bẹp và cuối cùng đã nhận được nó.

$ git rebase -i HEAD~4
  • Ở màn hình tương tác mở ra, thay thế pick bằng squash ở trên cùng cho tất cả các cam kết mà bạn muốn squash.
  • Lưu và đóng trình chỉnh sửa thông qua esc --> :wq

Đẩy vào điều khiển từ xa bằng cách sử dụng:

$ git push origin branch-name --force

2
ngắn gọn và hiệu quả, công phu:
terwxqian

22

Rất nhiều vấn đề có thể tránh được bằng cách chỉ tạo một branchđể hoạt động và không hoạt động master:

git checkout -b mybranch

Các công việc sau đây cho các remotecam kết đã được đẩy và một hỗn hợp các remotecam kết được đẩy / localchỉ các cam kết:

# example merging 4 commits

git checkout mybranch
git rebase -i mybranch~4 mybranch

# at the interactive screen
# choose fixup for commit: 2 / 3 / 4

git push -u origin +mybranch

Tôi cũng có một số ghi chú yêu cầu kéo có thể hữu ích.


17

git rebase -i master

bạn sẽ mở trình soạn thảo vm và thông báo một cái gì đó như thế này

Pick 2994283490 commit msg1
f 7994283490 commit msg2
f 4654283490 commit msg3
f 5694283490 commit msg4
#Some message 
#
#some more

Ở đây tôi đã thay đổi lựa chọn cho tất cả các cam kết khác thành "f" (Viết tắt của bản sửa lỗi).

git push -f origin feature/feature-branch-name-xyz

điều này sẽ sửa chữa tất cả các cam kết thành một cam kết và sẽ loại bỏ tất cả các cam kết khác. Tôi đã làm điều này và nó đã giúp tôi.


3

Khi bạn đang làm việc với Gitlab hoặc Github, bạn có thể gặp rắc rối theo cách này. Bạn nén các cam kết của bạn với một trong các phương pháp trên. Tôi thích nhất là:

git rebase -i HEAD~4
or
git rebase -i origin/master

chọn squash hoặc fixup cho cam kết của bạn. Tại thời điểm này, bạn sẽ kiểm tra với trạng thái git. Và thông điệp có thể là:

    On branch ABC-1916-remote
    Your branch and 'origin/ABC-1916' have diverged,
    and have 1 and 7 different commits each, respectively.
      (use "git pull" to merge the remote branch into yours)

Và bạn có thể bị cám dỗ để kéo nó. KHÔNG LÀM GÌ hoặc bạn sẽ ở trong tình trạng tương tự như trước đây.

Thay vào đó hãy đẩy về nguồn gốc của bạn bằng:

git push origin +ABC-1916-remote:ABC-1916

+ Cho phép buộc chỉ đẩy vào một nhánh.


Không có gì sai khi kéo, đặc biệt nếu bạn có xung đột, chúng có thể được giải quyết dễ dàng hơn so với khi bạn buộc đẩy
Ray_Poly

2

Để xóa hai cam kết, một trong số đó đã được đẩy, trên một nhánh duy nhất, hoạt động sau đây:

git rebase -i HEAD~2
    [ pick     older-commit  ]
    [ squash   newest-commit ]
git push --force

Theo mặc định, điều này sẽ bao gồm thông báo cam kết của cam kết mới nhất dưới dạng nhận xét về cam kết cũ hơn.


2

1) git rebase -i HEAD~4

Để giải thích: Nó hoạt động trên các chi nhánh hiện tại; ĐẦU ~ 4 có nghĩa là đè bẹp bốn cam kết mới nhất; chế độ tương tác (-i)

2) Tại thời điểm này, trình soạn thảo đã mở, với danh sách các xác nhận, để thay đổi các xác nhận thứ hai và sau đó, thay thế chọn bằng squash sau đó lưu nó.

đầu ra: Đã khởi động lại và cập nhật thành công ref / Heads / tên chi nhánh.

3) git push origin refs/heads/branch-name --force

đầu ra:

remote:
remote: To create a merge request for branch-name, visit:
remote: http://xxx/sc/server/merge_requests/new?merge_request%5Bsource_branch%5D=sss
remote:To ip:sc/server.git
 + 84b4b60...5045693 branch-name -> branch-name (forced update)
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.