Câu trả lời ngắn
Bạn đã bỏ qua thực tế rằng bạn đã chạy git push
, gặp lỗi sau và sau đó tiếp tục chạy git pull
:
To git@bitbucket.org:username/test1.git
! [rejected] dev -> dev (non-fast-forward)
error: failed to push some refs to 'git@bitbucket.org:username/test1.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Mặc dù Git cố gắng tỏ ra hữu ích, nhưng lời khuyên 'git pull' của nó rất có thể không phải là điều bạn muốn làm .
Nếu bạn là:
- Làm việc trên một "tính năng chi nhánh" hoặc "chi nhánh phát triển" một mình , sau đó bạn có thể chạy
git push --force
để cập nhật từ xa với cam kết sau rebase bạn ( theo câu trả lời của user4405677 ).
- Làm việc trên một nhánh với nhiều nhà phát triển cùng một lúc, thì có lẽ bạn không nên sử dụng
git rebase
ngay từ đầu. Để cập nhật dev
các thay đổi từ master
, bạn nên, thay vì chạy git rebase master dev
, hãy chạy git merge master
trong khi bật dev
( theo câu trả lời của Justin ).
Giải thích dài hơn một chút
Mỗi băm cam kết trong Git dựa trên một số yếu tố, một trong số đó là băm của cam kết đứng trước nó.
Nếu bạn sắp xếp lại các cam kết, bạn sẽ thay đổi các băm cam kết; phục hồi (khi nó làm điều gì đó) sẽ thay đổi các băm cam kết. Cùng với đó, kết quả của quá trình chạy git rebase master dev
, nơi dev
không đồng bộ với master
, sẽ tạo ra các cam kết mới (và do đó băm) có cùng nội dung với nội dung trên dev
nhưng với các cam kết master
được chèn trước chúng.
Bạn có thể rơi vào tình huống như thế này theo nhiều cách. Tôi có thể nghĩ ra hai cách:
- Bạn có thể có những cam kết
master
mà bạn muốn làm cơ sở cho dev
công việc của mình
- Bạn có thể có các cam kết
dev
đã được đẩy đến một điều khiển từ xa, sau đó bạn sẽ tiến hành thay đổi (đổi từ khóa thông báo cam kết, sắp xếp lại các cam kết, cam kết bí, v.v.)
Hãy hiểu rõ hơn điều gì đã xảy ra — đây là một ví dụ:
Bạn có một kho lưu trữ:
2a2e220 (HEAD, master) C5
ab1bda4 C4
3cb46a9 C3
85f59ab C2
4516164 C1
0e783a3 C0
Sau đó, bạn tiến hành thay đổi cam kết.
git rebase --interactive HEAD~3 # Three commits before where HEAD is pointing
(Đây là nơi bạn sẽ phải nghe lời tôi: có một số cách để thay đổi các cam kết trong Git. Trong ví dụ này, tôi đã thay đổi thời gian C3
, nhưng bạn sẽ chèn các cam kết mới, thay đổi thông báo cam kết, sắp xếp lại các cam kết, cam kết chặt chẽ với nhau, v.v.)
ba7688a (HEAD, master) C5
44085d5 C4
961390d C3
85f59ab C2
4516164 C1
0e783a3 C0
Đây là nơi điều quan trọng cần lưu ý rằng các băm cam kết là khác nhau. Đây là hành vi được mong đợi vì bạn đã thay đổi điều gì đó (bất cứ điều gì) về chúng. Điều này không sao, NHƯNG:
Cố gắng đẩy sẽ hiển thị cho bạn một lỗi (và gợi ý rằng bạn nên chạy git pull
).
$ git push origin master
To git@bitbucket.org:username/test1.git
! [rejected] master -> master (non-fast-forward)
error: failed to push some refs to 'git@bitbucket.org:username/test1.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Nếu chúng tôi chạy git pull
, chúng tôi thấy nhật ký này:
7df65f2 (HEAD, master) Merge branch 'master' of bitbucket.org:username/test1
ba7688a C5
44085d5 C4
961390d C3
2a2e220 (origin/master) C5
85f59ab C2
ab1bda4 C4
4516164 C1
3cb46a9 C3
0e783a3 C0
Hoặc, được hiển thị theo cách khác:
Và bây giờ chúng tôi có các cam kết trùng lặp tại địa phương. Nếu chúng tôi chạy, git push
chúng tôi sẽ gửi chúng đến máy chủ.
Để tránh đến giai đoạn này, chúng tôi có thể đã chạy git push --force
(thay vào đó chúng tôi đã chạy git pull
). Điều này sẽ gửi cam kết của chúng tôi với các băm mới đến máy chủ mà không có vấn đề gì. Để khắc phục sự cố ở giai đoạn này, chúng tôi có thể đặt lại về trước khi chạy git pull
:
Nhìn vào reflog ( git reflog
) để biết giá trị băm cam kết trước khi chúng ta chạy git pull
.
070e71d HEAD@{1}: pull: Merge made by the 'recursive' strategy.
ba7688a HEAD@{2}: rebase -i (finish): returning to refs/heads/master
ba7688a HEAD@{3}: rebase -i (pick): C5
44085d5 HEAD@{4}: rebase -i (pick): C4
961390d HEAD@{5}: commit (amend): C3
3cb46a9 HEAD@{6}: cherry-pick: fast-forward
85f59ab HEAD@{7}: rebase -i (start): checkout HEAD~~~
2a2e220 HEAD@{8}: rebase -i (finish): returning to refs/heads/master
2a2e220 HEAD@{9}: rebase -i (start): checkout refs/remotes/origin/master
2a2e220 HEAD@{10}: commit: C5
ab1bda4 HEAD@{11}: commit: C4
3cb46a9 HEAD@{12}: commit: C3
85f59ab HEAD@{13}: commit: C2
4516164 HEAD@{14}: commit: C1
0e783a3 HEAD@{15}: commit (initial): C0
Ở trên, chúng ta thấy đó ba7688a
là cam kết mà chúng ta đã thực hiện trước khi chạy git pull
. Với hàm băm cam kết đó trong tay, chúng ta có thể đặt lại về đó ( git reset --hard ba7688a
) và sau đó chạy git push --force
.
Và chúng tôi đã hoàn thành.
Nhưng chờ đã, tôi tiếp tục làm việc dựa trên các cam kết trùng lặp
Nếu bằng cách nào đó, bạn không nhận thấy rằng các cam kết đã bị trùng lặp và tiếp tục tiếp tục làm việc ở đầu các cam kết trùng lặp, bạn thực sự đã tự làm rối mình. Kích thước của mớ hỗn độn tỷ lệ thuận với số lượng cam kết bạn có trên các bản sao.
Cái này trông như thế nào:
3b959b4 (HEAD, master) C10
8f84379 C9
0110e93 C8
6c4a525 C7
630e7b4 C6
070e71d (origin/master) Merge branch 'master' of bitbucket.org:username/test1
ba7688a C5
44085d5 C4
961390d C3
2a2e220 C5
85f59ab C2
ab1bda4 C4
4516164 C1
3cb46a9 C3
0e783a3 C0
Hoặc, được hiển thị theo cách khác:
Trong trường hợp này, chúng tôi muốn xóa các cam kết trùng lặp, nhưng vẫn giữ các cam kết mà chúng tôi đã dựa trên chúng — chúng tôi muốn giữ lại từ C6 đến C10. Như với hầu hết mọi thứ, có một số cách để thực hiện điều này:
Hoặc:
- Tạo một nhánh mới tại lần cam kết 1 được sao chép cuối cùng ,
cherry-pick
mỗi lần cam kết (bao gồm từ C6 đến C10) vào nhánh mới đó và coi nhánh mới đó là chính tắc.
- Chạy
git rebase --interactive $commit
, $commit
cam kết ở đâu trước cả hai cam kết được sao chép 2 . Tại đây chúng tôi có thể xóa hoàn toàn các dòng cho các bản sao.
1 Không quan trọng bạn chọn cái nào trong hai cái ba7688a
hoặc 2a2e220
hoạt động tốt.
2 Trong ví dụ, nó sẽ là 85f59ab
.
TL; DR
Đặt advice.pushNonFastForward
thành false
:
git config --global advice.pushNonFastForward false