Làm cách nào tôi có thể nén các cam kết X cuối cùng của mình thành một cam kết bằng Git?
Làm cách nào tôi có thể nén các cam kết X cuối cùng của mình thành một cam kết bằng Git?
Câu trả lời:
Sử dụng git rebase -i <after-this-commit>
và thay thế "chọn" trên lần xác nhận thứ hai và tiếp theo bằng "squash" hoặc "fixup", như được mô tả trong hướng dẫn .
Trong ví dụ này, <after-this-commit>
là hàm băm SHA1 hoặc vị trí tương đối từ đầu của nhánh hiện tại mà từ đó các cam kết được phân tích cho lệnh rebase. Ví dụ: nếu người dùng muốn xem 5 lần xác nhận từ CHÍNH hiện tại trong quá khứ thì lệnh là git rebase -i HEAD~5
.
<after-this-commit>
gì?
<after-this-commit>
là cam kết X + 1 tức là cha mẹ của cam kết cũ nhất mà bạn muốn xóa.
rebase -i
phương pháp này và reset --soft
là, rebase -i
cho phép tôi giữ lại tác giả cam kết, trong khi reset --soft
cho phép tôi đề xuất. Đôi khi tôi cần phải xóa các xác nhận của các yêu cầu kéo mà 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. Upvote cho cả hai câu trả lời tuyệt vời dù sao.
Bạn có thể làm điều này khá dễ dàng mà không cần git rebase
hoặc git merge --squash
. Trong ví dụ này, chúng tôi sẽ xóa 3 cam kết 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 điệp cam kết mới bằng cách ghép các thông điệp cam kết hiện có (nghĩa là tương tự với git rebase -i
danh sách hướng dẫn chọn / squash / squash / chế độ / squash sẽ bắt đầu với bạn), thì bạn cần trích xuất các tin nhắn đó và vượt qua họ để git commit
:
git reset --soft HEAD~3 &&
git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"
Cả hai phương thức này đều nén ba cam kết cuối cùng thành một cam kết mới theo cùng một cách. Thiết lập lại mềm chỉ trỏ lại TRƯỚC vào cam kết cuối cùng mà bạn không muốn xóa. Cả chỉ mục và cây làm việc đều không được chạm vào thiết lập lại mềm, để lại 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ả các thay đổi từ các cam kết mà bạn sắp vứt bỏ đi).
git rebase --squash-recent
, hoặc thậm chí git commit --amend-many
.
branch@{upstream}
(hoặc chỉ @{upstream}
cho chi 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 phần chính xác ). Điều này có thể khác với lần đẩy cuối cùng của bạn (ví dụ: nếu ai đó đã đẩy thứ gì đó được xây dựng trên đỉnh đẩy gần đây nhất của bạn và sau đó bạn đã lấy nó), nhưng có vẻ như nó có thể gần với những gì bạn muốn.
push -f
nhưng nếu không thì thật đáng yêu, cảm ơn.
git push --force
sau đó để có được cam kết
Bạn có thể sử 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 là chủ và bạn muốn kết hợp 12 lần cam kết cuối cùng thành một.
CẢNH BÁO: Trước tiên, hãy đảm bảo rằng bạn cam kết kiểm tra công việc của mình git status
sạch sẽ (vì git reset --hard
sẽ loại bỏ các thay đổi được dàn dựng và không theo giai đoạn)
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 đểgit merge
mô tả các --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 so với git reset --soft HEAD~12 && git commit
đề xuất đơn giản hơn của Chris Johnsen trong câu trả lời của anh ấy là bạn nhận được thông điệp cam kết được chuẩn bị trước với mọi thông điệp cam kết mà bạn đang sử dụng.
git rebase -i
, nhưng bạn không đưa ra lý do tại sao. Dự kiến -1 điều này bởi vì dường như với tôi rằng thực tế thì điều ngược lại là đúng và đây là một vụ hack; không phải bạn đang thực hiện nhiều lệnh hơn mức cần thiết chỉ để bắt buộc git merge
thực hiện một trong những điều git rebase
được thiết kế riêng cho?
git merge --squash
cũng dễ dàng hơn để sử dụng trong một kịch bản. Về cơ bản, lý do là bạn không cần "tính tương tác" của git rebase -i
việc này.
git merge --squash
là ít có khả năng tạo ra xung đột hợp nhất khi đối mặt với việc di chuyển / xóa / đổi tên so với việc khởi động lại, đặc biệt nếu bạn 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 kinh nghiệm, hãy sửa cho tôi nếu điều này không đúng trong trường hợp chung!)
HEAD@{1}
chỉ để ở bên an toàn, ví dụ như khi quy trình làm việc của bạn bị gián đoạn trong một giờ do mất điện, v.v.
Tôi khuyên bạn nên tránh git reset
khi có thể - đặc biệt là đối với người mới chơi Git. Trừ khi bạn thực sự cần tự động hóa một quy trình dựa trên một số cam kết, còn có một cách ít kỳ lạ hơn ...
git merge --squash (working branch name)
git commit
Thông điệp cam kết sẽ được chuẩn bị trước dựa trên bí đao.
gitk
để gắn nhãn cho dòng mã mà bạn đang đè bẹp và cũng gắn nhãn cho cơ sở để đè bẹp. 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.
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 việc thiết lập lại git một lần nữa, điều mà câu trả lời này đã cố gắng tránh.
Dựa trên câu trả lời của Chris Johnsen ,
Thêm bí danh "squash" 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"
~/.gitconfig
Bây giờ
bạn nên 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
... Bao gồm tự động nén các N
cam kết cuối cùng , 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ó bằng tay. (Hoặc, chỉnh sửa bí danh để phù hợp với sở thích của bạn.)
git squash -m "New summary."
và đã N
xác định tự động là số lần xác nhận chưa được xử lý.
git commit --amend
để tiếp tục thay đổi tin nhắn, 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 có trong thông điệp cam kết.
Nhờ 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 để xóa 3 lần xác nhận cuối cùng:
git rebase -i HEAD~3
Điều này rất hữu ích vì nó hoạt động ngay cả khi bạ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, tua lại, v.v. như bình thường.
Sử dụng trình soạn thảo rebase tương tác:
Trình soạn thảo rebase tương tác hiển thị ba lần xác nhận cuối cùng. Ràng buộc này được xác định bởi HEAD~3
khi chạy lệnh git rebase -i HEAD~3
.
Cam kết gần đây nhất HEAD
, được hiển thị đầu tiên trên dòng 1. Các dòng bắt đầu bằng một #
nhận xét / tài liệu.
Các tài liệu hiển thị là khá rõ ràng. Trên bất kỳ dòng nào, bạn có thể thay đổi lệnh từ pick
thành lệnh bạn chọn.
Tôi thích sử dụng lệnh fixup
này vì "ép" các thay đổi của cam kết thành cam kết trên dòng trên và loại bỏ thông điệp của cam kết.
Như cam kết trên dòng 1 là HEAD
, trong hầu hết các trường hợp, bạn sẽ để nó như là pick
. Bạn không thể sử dụng squash
hoặc fixup
vì không có cam kết nào khác để ép cam kết vào.
Bạn cũng có thể thay đổi thứ tự của các cam kết. Điều này cho phép bạn squash hoặc sửa lỗi cam kết không liền kề theo thời gian.
Một ví dụ thực tế hàng ngày
Gần đây tôi đã cam kết một tính năng mới. Kể từ đó, tôi đã cam kết hai bản sửa lỗi. Nhưng bây giờ tôi đã phát hiện ra một lỗi (hoặc có thể chỉ là lỗi chính tả) trong tính năng mới mà tôi đã cam kết. Thật khó chịu! Tôi không muốn một cam kết mới làm ô nhiễm lịch sử cam kết của tôi!
Điều đầu tiên tôi làm là sửa lỗi và đưa ra một cam kết mới với nhận xét squash this into my new feature!
.
Sau đó tôi chạy git log
hoặc gitk
lấy SHA cam kết của tính năng mới (trong trường hợp này 1ff9460
).
Tiếp theo, tôi đưa lên trình soạn thảo rebase tương tác với git rebase -i 1ff9460~
. Các ~
sau khi cam kết SHA nói với trình biên tập bao gồm cam kết rằng trong trình soạn thảo.
Tiếp theo, tôi di chuyển cam kết chứa fix ( fe7f1e0
) sang bên dưới cam kết tính năng và thay đổi pick
thành fixup
.
Khi đóng trình chỉnh sửa, bản sửa lỗi sẽ được nén vào cam kết tính năng và lịch sử cam kết của tôi sẽ trông đẹp và sạch sẽ!
Điều này hoạt động tốt khi tất cả các xác nhận là cục bộ, nhưng nếu bạn cố gắng thay đổi bất kỳ cam kết nào đã được đẩy sang điều khiển từ xa, bạn thực sự có thể gây ra sự cố cho các nhà phát triển khác đã kiểm tra cùng chi nhánh!
pick
trong dòng 1. Nếu bạn chọn squash
hoặc fixup
cho cam kết trên dòng 1, git sẽ hiển thị thông báo "lỗi: không thể 'sửa lỗi" mà không có cam kết trước đó ". Sau đó, nó sẽ cung cấp cho bạn tùy chọn để sửa nó: "Bạn có thể sửa lỗi này bằng 'git rebase --edit-todo' và sau đó chạy 'git rebase --continue'." hoặc bạn chỉ có thể hủy bỏ và bắt đầu lại: "Hoặc bạn có thể hủy bỏ cuộc nổi loạn với 'git rebase --abort'.".
Nếu bạn sử dụng TortoiseGit, bạn có thể thực hiện chức năng Combine to one commit
:
Show Log
Combine to one commit
từ menu ngữ cảnhHàm này tự động thực hiện tất cả các bước git đơn cần thiết. Đáng tiếc chỉ có sẵn cho Windows.
Để làm điều này, bạn có thể sử dụng lệnh git sau đây.
git rebase -i HEAD~n
n (= 4 ở đây) là số lần xác nhận cuối cùng. Sau đó, bạn có các tùy chọn sau,
pick 01d1124 Message....
pick 6340aaa Message....
pick ebfd367 Message....
pick 30e0ccb Message....
Cập nhật như bên dưới pick
một cam kết và squash
những người khác vào gần đây nhất,
p 01d1124 Message....
s 6340aaa Message....
s ebfd367 Message....
s 30e0ccb Message....
Để biết chi tiết, nhấp vào Liên kết
Dựa trên bài viết này tôi 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 lần xác nhận (vì vậy những cam kết này chưa được đẩy đến điều khiển từ xa).
Tôi muốn dẹp những cam kết này thành một trước khi thay đổi. Tôi đặt trước để đặt lại chi nhánh về trạng thái 'origin / dev' (điều này sẽ để lại tất cả các thay đổi từ 96 lần xác nhận không được thực hiện) và sau đó cam kết các thay đổi cùng một lúc:
git reset origin/dev
git add --all
git commit -m 'my commit message'
Trong nhánh bạn muốn kết hợp các cam kết trên, hãy chạy:
git rebase -i HEAD~(n number of commits back to review)
thí dụ:
git rebase -i HEAD~1
Điều này sẽ mở trình soạn thảo văn bản và bạn phải chuyển 'chọn' trước mỗi cam kết với 'squash' nếu bạn muốn các cam kết này được hợp nhất với nhau. Từ tài liệu:
Ví dụ: nếu bạn đang tìm cách hợp nhất tất cả các cam kết thành một, 'chọn' là cam kết đầu tiên bạn thực hiện và tất cả các cam kết trong tương lai (được đặt bên dưới đầu tiên) nên được đặt thành 'squash'. Nếu sử dụng vim, hãy sử dụng : x trong chế độ chèn để lưu và thoát trình chỉnh sửa.
Sau đó để tiếp tục cuộc nổi loạn:
git rebase --continue
Để biết thêm về điều này và các cách khác để viết lại lịch sử cam kết của bạn, hãy xem bài đăng hữu ích này
--continue
và vim :x
làm.
git add
cấu hình đúng trong các tệp bạn sử dụng git rebase --continue
để chuyển sang cam kết tiếp theo và bắt đầu hợp nhất. :x
là một lệnh sẽ lưu các thay đổi của tệp khi sử dụng vim, hãy xem điều này
Câu trả lời của Anomies là tốt, nhưng tôi cảm thấy không an toàn về điều này nên tôi quyết định thêm một vài ảnh chụp màn hình.
Xem bạn đang ở đâu với git log
. Quan trọng nhất, hãy tìm hàm băm cam kết của lần xác nhận đầu tiên mà bạn không muốn ép. Vì vậy, chỉ có:
Thực thi git rebase -i [your hash]
, trong trường hợp của tôi:
$ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d
Trong trường hợp của tôi, tôi muốn xóa sạch mọi thứ trên cam kết lần đầu tiên. Thứ tự là từ đầu đến cuối, vì vậy chính xác theo cách khác như trong git log
. Trong trường hợp của tôi, tôi muốn:
Nếu bạn chỉ chọn một cam kết và đè bẹp phần còn lại, bạn có thể điều chỉnh một thông báo cam kết:
Đó là nó. Khi bạn lưu cái này ( :wq
), bạn đã hoàn thành. Có một cái nhìn với nó git log
.
git log
1) Xác định hàm băm ngắn
# git log --pretty=oneline --abbrev-commit
abcd1234 Update to Fix for issue B
cdababcd Fix issue B
deab3412 Fix issue A
....
Ở đây thậm chí git log --oneline
cũng có thể được sử dụng để có được băm ngắn.
2) Nếu bạn muốn squash (hợp nhất) hai lần xác nhận cuối cùng
# git rebase -i deab3412
3) Điều này mở ra một nano
trình soạn thảo để hợp nhất. Và nó trông như dưới đây
....
pick cdababcd Fix issue B
pick abcd1234 Update to Fix for issue B
....
4) Đổi tên từ pick
để squash
mà có mặt trước abcd1234
. Sau khi đổi tên nó sẽ giống như dưới đây.
....
pick cdababcd Fix issue B
squash abcd1234 Update to Fix for issue B
....
5) Bây giờ lưu và đóng nano
trình chỉnh sửa. Nhấn ctrl + o
và nhấn Enter
để lưu. Và sau đó nhấn ctrl + x
để thoát khỏi trình soạn thảo.
6) Sau đó, nano
biên tập viên lại mở để cập nhật ý kiến, nếu cần cập nhật.
7) Bây giờ nó bị đè bẹp thành công, bạn có thể xác minh nó bằng cách kiểm tra nhật ký.
# git log --pretty=oneline --abbrev-commit
1122abcd Fix issue B
deab3412 Fix issue A
....
8) Bây giờ đẩy để repo. Lưu ý để thêm +
dấu trước tên chi nhánh. Điều này có nghĩa là buộc phải đẩy.
# git push origin +master
Lưu ý: Điều này dựa trên việc sử dụng git trên ubuntu
shell. Nếu bạn đang sử dụng các os ( Windows
hoặc Mac
) khác nhau thì các lệnh trên đều giống nhau ngoại trừ trình soạn thảo. Bạn có thể nhận được trình soạn thảo khác nhau.
git add <files>
--fixup
tùy chọn và OLDCOMMIT
nên trên đó chúng ta cần hợp nhất (squash) cam kết này.git commit --fixup=OLDCOMMIT
Bây giờ điều này tạo ra một cam kết mới trên đầu trang với fixup1 <OLDCOMMIT_MSG>
.
OLDCOMMIT
.git rebase --interactive --autosquash OLDCOMMIT^
Ở đây ^
có nghĩa là cam kết trước đó OLDCOMMIT
. rebase
Lệnh này mở cửa sổ tương tác trên trình soạn thảo (vim hoặc nano) mà chúng ta không cần phải làm gì chỉ cần lưu và thoát là đủ. Bởi vì tùy chọn được chuyển cho điều này sẽ tự động chuyển cam kết mới nhất sang bên cạnh cam kết cũ và thay đổi thao tác thành fixup
(tương đương với squash). Sau đó rebase tiếp tục và kết thúc.
--amend
thể được sử dụng với git-commit
. # git log --pretty=oneline --abbrev-commit
cdababcd Fix issue B
deab3412 Fix issue A
....
# git add <files> # New changes
# git commit --amend
# git log --pretty=oneline --abbrev-commit
1d4ab2e1 Fix issue B
deab3412 Fix issue A
....
Ở đây --amend
hợp nhất các thay đổi mới với lần xác nhận cuối cùng cdababcd
và tạo ID cam kết mới1d4ab2e1
Để xóa 10 lần xác nhận cuối cùng thành 1 lần xác nhận duy nhất:
git reset --soft HEAD~10 && git commit -m "squashed commit"
Nếu bạn cũng muốn cập nhật chi nhánh từ xa với cam kết bị đè bẹp:
git push -f
Nếu bạn ở trên một nhánh từ xa (được gọi feature-branch
) được sao chép từ Kho lưu trữ vàng ( golden_repo_name
), thì đây là kỹ thuật để nén các cam kết của bạn thành một:
Kiểm tra repo vàng
git checkout golden_repo_name
Tạo một nhánh mới từ nó (repo vàng) như sau
git checkout -b dev-branch
Squash hợp nhất với chi nhánh địa phương của bạn mà bạn đã có
git merge --squash feature-branch
Cam kết các thay đổi của bạn (đây sẽ là cam kết duy nhất có trong nhánh dev)
git commit -m "My feature complete"
Đẩy chi nhánh vào kho lưu trữ cục bộ của bạn
git push origin dev-branch
Điều gì có thể thực sự thuận tiện:
Tìm hàm băm cam kết bạn muốn đè lên trên, nói d43e15
.
Bây giờ sử dụng
git reset d43e15
git commit -am 'new commit name'
Đây là siêu nhân đôi, nhưng theo một cách hay, vì vậy tôi sẽ ném nó vào vòng:
GIT_EDITOR='f() { if [ "$(basename $1)" = "git-rebase-todo" ]; then sed -i "2,\$s/pick/squash/" $1; else vim $1; fi }; f' git rebase -i foo~5 foo
Dịch: cung cấp một "trình soạn thảo" mới cho git, nếu tên tệp cần chỉnh sửa là git-rebase-todo
(dấu nhắc phản hồi tương tác) thay đổi tất cả trừ "chọn" đầu tiên thành "squash", và nếu không thì sẽ sinh ra vim - để khi bạn được nhắc để chỉnh sửa thông điệp cam kết bị đè bẹp, bạn nhận được vim. (Và rõ ràng là tôi đã đè bẹp năm lần cam kết cuối cùng trên nhánh foo, nhưng bạn có thể thay đổi điều đó theo cách bạn muốn.)
Tôi có lẽ sẽ làm những gì Mark Longair đề nghị , mặc dù.
Nếu bạn muốn squish mọi cam kết thành một cam kết duy nhất (ví dụ: khi phát hành dự án công khai lần đầu tiên), hãy thử:
git checkout --orphan <new-branch>
git commit
Tôi nghĩ rằng cách dễ nhất để làm điều này là bằng cách tạo một nhánh mới ra khỏi master và thực hiện hợp nhất --squash của nhánh tính năng.
git checkout master
git checkout -b feature_branch_squashed
git merge --squash feature_branch
Sau đó, bạn có tất cả các thay đổi đã sẵn sàng để cam kết.
Một lớp lót đơn giản luôn hoạt động, với điều kiện là bạn hiện đang ở nhánh bạn muốn ép, chủ là nhánh mà nó có nguồn gốc và cam kết mới nhất chứa thông điệp cam kết và tác giả bạn muốn sử dụng:
git reset --soft $(git merge-base HEAD master) && git commit --reuse-message=HEAD@{1}
ví dụ: nếu bạn muốn xóa 3 cam kết cuối cùng cho một cam kết duy nhất trong một nhánh (kho lưu trữ từ xa) chẳng hạn: https://bitbucket.org
Những gì tôi đã làm là
(MASTER)
Fleetwood Mac Fritz
║ ║
Add Danny Lindsey Stevie
Kirwan Buckingham Nicks
║ ╚═══╦══════╝
Add Christine ║
Perfect Buckingham
║ Nicks
LA1974══════════╝
║
║
Bill <══════ YOU ARE EDITING HERE
Clinton (CHECKED OUT, CURRENT WORKING DIRECTORY)
Trong lịch sử viết tắt này của kho lưu trữ https://github.com/fleetwood-mac/band-history, bạn đã mở một yêu cầu kéo để hợp nhất trong cam kết của Bill Clinton vào cam kết MASTER
Fleetwood Mac ban đầu .
Bạn đã mở một yêu cầu kéo và trên GitHub bạn thấy điều này:
Bốn cam kết:
Suy nghĩ rằng không ai sẽ quan tâm để đọc toàn bộ lịch sử kho lưu trữ. (Thực sự có một kho lưu trữ, nhấp vào liên kết ở trên!) Bạn quyết định xóa sạch các cam kết này. Vì vậy, bạn đi và chạy git reset --soft HEAD~4 && git commit
. Sau đó, bạn git push --force
vào GitHub để dọn dẹp PR của bạn.
Và những gì sẽ xảy ra? Bạn chỉ thực hiện một cam kết duy nhất nhận được từ Fritz đến Bill Clinton. Bởi vì bạn đã quên rằng ngày hôm qua bạn đã làm việc trên phiên bản Buckingham Nicks của dự án này. Và git log
không khớp với những gì bạn thấy trên GitHub.
git checkout
họgit reset --soft
đógit commit
mà warps trực tiếp từ từ cho đếngit rebase -i HEAD^^
trong đó số lượng của ^ là X
(trong trường hợp này, đè bẹp hai lần xác nhận cuối cùng)
Ngoài các câu trả lời xuất sắc khác, tôi muốn thêm làm thế nào git rebase -i
luôn làm tôi bối rối với thứ tự cam kết - cũ hơn mới hơn hoặc ngược lại? Vì vậy, đây là quy trình làm việc của tôi:
git rebase -i HEAD~[N]
, trong đó N là số lần xác nhận tôi muốn tham gia, bắt đầu từ lần gần đây nhất . Vì vậy, git rebase -i HEAD~5
có nghĩa là "ép 5 lần cuối cùng vào một cái mới";Điều gì về một câu trả lời cho câu hỏi liên quan đến một quy trình công việc như thế này?
merge --squash
sau khi PR, nhưng nhóm nghĩ rằng điều đó sẽ làm chậm quá trình.)Tôi chưa thấy một quy trình làm việc như thế trên trang này. (Đó có thể là đôi mắt của tôi.) Nếu tôi hiểu rebase
chính xác, nhiều sự hợp nhất sẽ yêu cầu nhiều giải pháp xung đột . Tôi thậm chí không muốn nghĩ về điều đó!
Vì vậy, điều này dường như làm việc cho chúng tôi.
git pull master
git checkout -b new-branch
git checkout -b new-branch-temp
git checkout new-branch
git merge --squash new-branch-temp
// đặt tất cả các thay đổi trong giai đoạngit commit 'one message to rule them all'
git push
Tôi tìm thấy một giải pháp chung chung hơn không phải là chỉ định các cam kết 'N', mà là chi nhánh / commit-id mà bạn muốn đè lên trên. Điều này ít xảy ra lỗi hơn so với việc đếm các cam kết lên đến một cam kết cụ thể, chỉ cần xác định trực tiếp thẻ hoặc nếu bạn thực sự muốn đếm, bạn có thể chỉ định HEAD ~ N.
Trong quy trình làm việc của mình, tôi bắt đầu một chi nhánh và cam kết đầu tiên của tôi trên chi nhánh đó tóm tắt mục tiêu (nghĩa là thông thường tôi sẽ đẩy thông điệp 'cuối cùng' cho tính năng đến kho lưu trữ công cộng.) Vì vậy, khi tôi hoàn thành Tôi muốn làm là git squash master
quay lại tin nhắn đầu tiên và sau đó tôi sẵn sàng đẩy.
Tôi sử dụng bí danh:
squash = !EDITOR="\"_() { sed -n 's/^pick //p' \"\\$1\"; sed -i .tmp '2,\\$s/^pick/f/' \"\\$1\"; }; _\"" git rebase -i
Điều này sẽ loại bỏ lịch sử bị đè bẹp trước khi nó làm như vậy, điều này mang đến cho bạn cơ hội phục hồi bằng cách lấy ID xác nhận cũ ra khỏi bàn điều khiển nếu bạn muốn hoàn nguyên. (Người dùng Solaris lưu ý rằng nó sử dụng -i
tùy chọn GNU sed , người dùng Mac và Linux sẽ ổn với điều này.)
git squash master
khi chúng tôi được kiểm tra trên nô lệ. chuyện gì sẽ xảy ra chúng ta sẽ che giấu xung đột?
Trong câu hỏi nó có thể mơ hồ những gì có nghĩa là "cuối cùng".
ví dụ git log --graph
đầu ra như sau (đơn giản hóa):
* commit H0
|
* merge
|\
| * commit B0
| |
| * commit B1
| |
* | commit H1
| |
* | commit H2
|/
|
Sau đó, lần xác nhận cuối cùng theo thời gian là H0, hợp nhất, B0. Để đè bẹp chúng, bạn sẽ phải khởi động lại nhánh đã hợp nhất của mình trên cam kết H1.
Vấn đề là H0 chứa H1 và H2 (và thường là nhiều cam kết hơn trước khi hợp nhất và sau khi phân nhánh) trong khi B0 thì không. Vì vậy, bạn phải quản lý các thay đổi từ H0, hợp nhất, H1, H2, B0 ít nhất.
Có thể sử dụng rebase nhưng theo cách khác thì các câu trả lời khác được đề cập:
rebase -i HEAD~2
Điều này sẽ cho bạn thấy các tùy chọn lựa chọn (như được đề cập trong các câu trả lời khác):
pick B1
pick B0
pick H0
Đặt bí đao thay vì chọn H0:
pick B1
pick B0
s H0
Sau khi lưu và thoát rebase sẽ áp dụng lần lượt các xác nhận sau H1. Điều đó có nghĩa là nó sẽ yêu cầu bạn giải quyết xung đột một lần nữa (trong đó ĐẦU TIÊN sẽ là H1 và sau đó tích lũy các cam kết khi chúng được áp dụng).
Sau khi rebase sẽ kết thúc, bạn có thể chọn tin nhắn cho H0 và B0 bị bẹp:
* commit squashed H0 and B0
|
* commit B1
|
* commit H1
|
* commit H2
|
PS Nếu bạn chỉ thực hiện một số thiết lập lại thành BO: (ví dụ: sử dụng reset --mixed
được giải thích chi tiết hơn tại đây https://stackoverflow.com/a/18690845/2405850 ):
git reset --mixed hash_of_commit_B0
git add .
git commit -m 'some commit message'
sau đó bạn nén vào các thay đổi B0 của H0, H1, H2 (mất hoàn toàn cam kết cho các thay đổi sau khi phân nhánh và trước khi hợp nhất.