Giả sử tôi có lịch sử cam kết sau trên chi nhánh địa phương của mình:
A -- B -- C
Làm cách nào để chèn một cam kết mới giữa A
và B
?
Giả sử tôi có lịch sử cam kết sau trên chi nhánh địa phương của mình:
A -- B -- C
Làm cách nào để chèn một cam kết mới giữa A
và B
?
Câu trả lời:
Nó thậm chí còn dễ hơn câu trả lời của OP.
git rebase -i <any earlier commit>
. Điều này sẽ hiển thị một danh sách các cam kết trong trình soạn thảo văn bản được cấu hình của bạn.a1b2c3d
). Trong trình chỉnh sửa của bạn, cho dòng đó, thay đổi pick
thành edit
.a1b2c3d
) như thể nó vừa được cam kết .git commit
( KHÔNG sửa đổi, không giống như hầu hết edit
các s). Điều này tạo ra một cam kết mới sau khi bạn chọn.git rebase --continue
. Điều này phát lại các lần xác nhận liên tiếp, để lại cam kết mới của bạn được đặt đúng chỗ.Coi chừng rằng điều này sẽ viết lại lịch sử, và phá vỡ bất cứ ai khác cố gắng kéo.
A -- B -- C -- D
thay vì mong muốn A -- D -- B -- C
.
D
có thể là một cam kết bất cứ nơi nào. Giả sử chúng ta có A - B - C
và chúng ta có một số cam kết D
thậm chí không có trong nhánh này. Chúng tôi biết SHA của nó tuy nhiên, chúng tôi có thể làm git rebase -i HEAD~3
. Bây giờ giữa A
và B
pick
các dòng, chúng tôi chèn một dòng mới pick
cho biết pick SHA
, đưa ra hàm băm mong muốn D
. Nó không cần phải là hàm băm đầy đủ, chỉ cần rút gọn. git rebase -i
chỉ chọn cherry bất cứ cam kết nào được liệt kê bởi pick
các dòng trong bộ đệm; họ không phải là người ban đầu mà nó liệt kê cho bạn.
break
từ khóa trong trình chỉnh sửa trên dòng riêng của mình ở giữa hai lần xác nhận (hoặc trên dòng đầu tiên, để chèn một cam kết trước khi cam kết được chỉ định của bạn).
Hóa ra khá đơn giản, câu trả lời được tìm thấy ở đây . Giả sử bạn đang ở trên một chi nhánh branch
. Thực hiện các bước sau:
tạo một nhánh tạm thời từ cam kết sau khi bạn muốn chèn cam kết mới (trong trường hợp này là cam kết A
):
git checkout -b temp A
thực hiện các thay đổi và cam kết chúng, tạo ra một cam kết, hãy gọi nó là N
:
git commit -a -m "Message"
(hoặc git add
theo sau git commit
)
rebase các cam kết bạn muốn có sau lần xác nhận mới (trong trường hợp này là cam kết B
và C
) vào cam kết mới:
git rebase temp branch
(có thể bạn cần sử dụng -p
để duy trì sự hợp nhất, nếu có - nhờ vào một nhận xét không còn tồn tại của ciekawy )
xóa chi nhánh tạm thời:
git branch -d temp
Sau này, lịch sử trông như sau:
A -- N -- B -- C
Tất nhiên có thể một số xung đột sẽ xuất hiện trong khi nổi loạn.
Trong trường hợp chi nhánh của bạn không phải là cục bộ, điều này sẽ giới thiệu lịch sử viết lại, do đó có thể gây ra vấn đề nghiêm trọng.
git push --force
thay đổi repo từ xa.
git rebase temp branch -Xtheirs
. Câu trả lời hữu ích cho việc tiêm vào một kịch bản!
git rebase temp branch
, nhưng trước đó git branch -d temp
, tất cả những gì bạn phải làm là khắc phục và xử lý các xung đột và vấn đề git rebase --continue
, tức là không cần phải cam kết bất cứ điều gì, v.v.
Giải pháp thậm chí dễ dàng hơn:
Tạo cam kết mới của bạn ở cuối, D. Bây giờ bạn có:
A -- B -- C -- D
Sau đó chạy:
$ git rebase -i hash-of-A
Git sẽ mở trình soạn thảo của bạn và nó sẽ trông như thế này:
pick 8668d21 B
pick 650f1fc C
pick 74096b9 D
Chỉ cần di chuyển D lên đầu như thế này, sau đó lưu và thoát
pick 74096b9 D
pick 8668d21 B
pick 650f1fc C
Bây giờ bạn sẽ có:
A -- D -- B -- C
Giả sử rằng lịch sử cam kết là preA -- A -- B -- C
, nếu bạn muốn chèn một cam kết giữa A
và B
, các bước như sau:
git rebase -i hash-of-preA
Git sẽ mở trình soạn thảo của bạn. Nội dung có thể như thế này:
pick 8668d21 A
pick 650f1fc B
pick 74096b9 C
Thay đổi đầu tiên pick
thành edit
:
edit 8668d21 A
pick 650f1fc B
pick 74096b9 C
Lưu và thoát.
Sửa đổi mã của bạn và sau đó git add . && git commit -m "I"
git rebase --continue
Bây giờ lịch sử cam kết Git của bạn là preA -- A -- I -- B -- C
Nếu bạn gặp phải một cuộc xung đột, Git sẽ dừng lại ở cam kết này. Bạn có thể sử dụng git diff
để xác định vị trí đánh dấu xung đột và giải quyết chúng. Sau khi giải quyết tất cả các xung đột, bạn cần sử dụng git add <filename>
để nói với Git rằng xung đột đã được giải quyết và sau đó chạy lại git rebase --continue
.
Nếu bạn muốn hoàn tác rebase, hãy sử dụng git rebase --abort
.
Đây là một chiến lược tránh thực hiện "chỉnh sửa hack" trong cuộc nổi loạn được thấy trong các câu trả lời khác mà tôi đã đọc.
Bằng cách sử dụng, git rebase -i
bạn có được một danh sách các cam kết kể từ khi cam kết đó. Chỉ cần thêm một "break" ở đầu tập tin, điều này sẽ khiến rebase bị phá vỡ tại thời điểm đó.
break
pick <B's hash> <B's commit message>
pick <C's hash> <C's commit message>
Sau khi ra mắt, git rebase
bây giờ sẽ dừng lại ở điểm "phá vỡ". Bây giờ bạn có thể chỉnh sửa các tập tin của bạn và tạo cam kết của bạn bình thường. Sau đó bạn có thể tiếp tục cuộc nổi loạn với git rebase --continue
. Điều này có thể gây ra xung đột bạn sẽ phải khắc phục. Nếu bạn bị lạc, đừng quên bạn luôn có thể hủy bỏ bằng cách sử dụng git rebase --abort
.
Chiến lược này có thể được khái quát hóa để chèn một cam kết ở bất cứ đâu, chỉ cần đặt "ngắt" tại vị trí bạn muốn chèn một cam kết.
Sau khi viết lại lịch sử, đừng quên git push -f
. Các cảnh báo thông thường về người khác lấy chi nhánh của bạn được áp dụng.
rebase
ở đây. Không có nhiều khác biệt cho dù bạn sẽ tạo ra cam kết trong quá trình rebase hay trước đó.
Nhiều câu trả lời tốt ở đây rồi. Tôi chỉ muốn thêm một giải pháp "không rebase", trong 4 bước đơn giản.
Tóm lược
git checkout A
git commit -am "Message for commit D"
git cherry-pick A..C
git branch -f master HEAD
Giải trình
(Lưu ý: một ưu điểm của giải pháp này là bạn không chạm vào chi nhánh của mình cho đến bước cuối cùng, khi bạn chắc chắn 100% là bạn ổn với kết quả cuối cùng, do đó bạn có bước "xác nhận trước" rất tiện dụng cho phép thử nghiệm AB .)
Trạng thái ban đầu (tôi đã giả sử master
cho tên chi nhánh của bạn)
A -- B -- C <<< master <<< HEAD
1) Bắt đầu bằng cách chỉ vào đúng vị trí
git checkout A
B -- C <<< master
/
A <<< detached HEAD
(Tùy chọn ở đây, thay vì tách ra CHÍNH, chúng ta có thể tạo một nhánh tạm thời git checkout -b temp A
, mà chúng ta sẽ cần phải xóa ở cuối quá trình. Cả hai biến thể đều hoạt động như bạn muốn vì mọi thứ khác vẫn giữ nguyên)
2) Tạo cam kết D mới được chèn
# at this point, make the changes you wanted to insert between A and B, then
git commit -am "Message for commit D"
B -- C <<< master
/
A -- D <<< detached HEAD (or <<< temp <<< HEAD)
3) Sau đó mang theo bản sao của các cam kết bị thiếu B và C cuối cùng (sẽ là cùng một dòng nếu có nhiều cam kết hơn)
git cherry-pick A..C
# (if any, resolve any potential conflicts between D and these last commits)
B -- C <<< master
/
A -- D -- B' -- C' <<< detached HEAD (or <<< temp <<< HEAD)
(thoải mái kiểm tra AB ở đây nếu cần)
Bây giờ là thời điểm để kiểm tra mã của bạn, kiểm tra bất cứ điều gì cần kiểm tra và bạn cũng có thể tìm / so sánh / kiểm tra những gì bạn có và những gì bạn sẽ nhận được sau các hoạt động.
4) Tùy thuộc vào các thử nghiệm của bạn giữa C
và C'
, nó ổn hoặc là KO.
(EITHER) 4-OK) Cuối cùng, di chuyển ref củamaster
git branch -f master HEAD
B -- C <<< (B and C are candidates for garbage collection)
/
A -- D -- B' -- C' <<< master
(HOẶC) 4-KO) Chỉ cần giữ master
nguyên
Nếu bạn đã tạo một nhánh tạm thời, chỉ cần xóa nó với git branch -d <name>
, nhưng nếu bạn đã đi theo tuyến đường Tách rời, không có hành động nào cần thiết vào thời điểm này, các cam kết mới sẽ đủ điều kiện để thu gom rác ngay sau khi bạn gắn lại HEAD
vớigit checkout master
Trong cả hai trường hợp (OK hoặc KO), tại thời điểm này, chỉ cần kiểm tra master
lại để gắn lại HEAD
.