Cách hợp nhất một cam kết cụ thể trong Git


1035

Tôi đã rẽ nhánh từ một kho lưu trữ trong GitHub và cam kết một cái gì đó cụ thể với tôi. Bây giờ tôi tìm thấy kho lưu trữ ban đầu có một tính năng tốt đó là HEAD.

Tôi muốn hợp nhất nó mà không có cam kết trước đó. Tôi nên làm gì? Tôi biết cách hợp nhất tất cả các cam kết:

git branch -b a-good-feature
git pull repository master
git checkout master
git merge a-good-feature
git commit -a
git push

Nếu bạn đang cố gắng làm điều này liên quan đến github, bài viết này sẽ hướng dẫn bạn thực hiện điều đó. markosullivan.ca/how-to-handle-a-pull-request-from-github
johndpope

Câu trả lời:


1176

' git cherry-pick' nên là câu trả lời của bạn ở đây.

Áp dụng thay đổi được giới thiệu bởi một cam kết hiện có.

Đừng quên đọc câu trả lời của bdonlan về hậu quả của việc hái anh đào trong bài đăng này:
"Kéo tất cả các cam kết từ một chi nhánh, đẩy các cam kết được chỉ định sang một mục khác" , trong đó:

A-----B------C
 \
  \
   D

trở thành:

A-----B------C
 \
  \
   D-----C'

Vấn đề với cam kết này là git xem xét các cam kết bao gồm tất cả lịch sử trước chúng

Trong đó C 'có SHA-1ID khác .
Tương tự như vậy, cherry chọn một cam kết từ chi nhánh này sang chi nhánh khác về cơ bản liên quan đến việc tạo ra một bản vá, sau đó áp dụng nó, do đó cũng làm mất lịch sử theo cách đó.

Việc thay đổi ID cam kết này sẽ phá vỡ chức năng hợp nhất của git giữa những thứ khác (mặc dù nếu được sử dụng một cách tiết kiệm, sẽ có những heuristic sẽ viết về điều này).
Quan trọng hơn, mặc dù, nó bỏ qua các phụ thuộc chức năng - nếu C thực sự sử dụng một chức năng được xác định trong B, bạn sẽ không bao giờ biết .


1
@ openid000: "nhiều nhánh hạt mịn hơn": đó thực sự là chính xác những gì bdonlan gợi ý trong câu trả lời của anh ấy.
VonC

8
Lưu ý: "git rebase" cũng thay đổi SHA-1. Xem thêm "git rebase vs git merge ( stackoverflow.com/questions/804115/git-rebase-vs-git-merge ) và" git workflow "( stackoverflow.com/questions/457927/ tựa ) cho các trường hợp" git rebase "là hợp pháp.
VonC

1
Giữa "các nhánh hạt mịn", "cherry-pick" và "rebase", sau đó bạn sẽ có tất cả các khả năng để quản lý mã trong các nhánh bằng git.
VonC

@VonC "chi nhánh hạt mịn" có. Tôi đã có hai chi nhánh và tôi đã thực hiện nâng cấp lên một mô-đun trực quan cụ thể trong một chi nhánh. Mô-đun này độc lập với tất cả các mã khác, vì vậy cherry-pick rất thuận tiện để áp dụng các thay đổi này cho chi nhánh khác
Cheeku

1
Nhưng lưu ý rằng khi bạn hợp nhất, cả hai cam kết C 'và C sẽ thắng thế trong lịch sử cam kết.
Rahul Shah

738

Bạn có thể sử dụng git cherry-pick để tự áp dụng một cam kết duy nhất cho chi nhánh hiện tại của mình.

Thí dụ: git cherry-pick d42c389f


68
+1 cho bài đăng cũ của bạn về hái cherry ( stackoverflow.com/questions/880957/ )). Tôi lấy tự do để sao chép một đoạn trích trong câu trả lời của riêng tôi ở trên.
VonC

4
Có thể git cherry-pick d42choặc git cherry-pick d42c3 sẽ làm việc. Git thật thông minh. ;)
guneysus

2
gây tử vong: sửa đổi xấu
Ievgen Naida

2
Tôi chỉ thực hiện lệnh này và nó dường như đã hoạt động, nhưng khi tôi thực hiện trạng thái git, nó nói "không có gì để cam kết, làm việc cây sạch". Khi tôi làm mới trang Cam kết trong trang web bitbucket của mình, nó không hiển thị. Nhưng nó xuất hiện khi tôi thực hiện git log. Và tôi thấy mã sửa đổi. Ai đó có thể giải thích nếu tôi phải làm thêm bất kỳ bước nào không?
Ray

1
Thật không may, điều này không trả lời câu hỏi: Nó không thực sự tạo ra một sự hợp nhất. Không có tổ tiên chỉ vào d42c389f. Có thể OP không quan tâm đến việc tạo ra một sự hợp nhất mỗi lần, nhưng sự khác biệt đôi khi có vấn đề.
LarsH

29

Hãy thử lấy một ví dụ và hiểu:

Tôi có một nhánh, nói chủ , chỉ vào X <commit-id> và tôi có một nhánh mới chỉ vào Y <sha1>.

Trong đó Y <commit-id> = <master> nhánh cam kết - vài lần xác nhận

Bây giờ nói cho nhánh Y tôi phải đóng khoảng cách giữa các nhánh chính và nhánh mới. Dưới đây là quy trình chúng tôi có thể làm theo:

Bước 1:

git checkout -b local origin/new

trong đó địa phương là tên chi nhánh. Bất kỳ tên có thể được đưa ra.

Bước 2:

  git merge origin/master --no-ff --stat -v --log=300

Hợp nhất các xác nhận từ nhánh chính sang nhánh mới và cũng tạo một cam kết hợp nhất của thông điệp tường trình với các mô tả một dòng từ tối đa <n> các cam kết thực tế đang được hợp nhất.

Để biết thêm thông tin và thông số về hợp nhất Git, vui lòng tham khảo:

git merge --help

Ngoài ra nếu bạn cần hợp nhất một cam kết cụ thể, thì bạn có thể sử dụng:

git cherry-pick <commit-id>

bạn đã thay đổi định nghĩa Ytrong câu 3d của bạn? "Tôi có một chi nhánh mới chỉ vào Y" so với "Bây giờ nói cho chi nhánh Y", âm thanh như Y từng là một cam kết và sau đó nó trở thành một chi nhánh
Purefan

làm thế nào tôi có thể làm điều đó từ một điểm cam kết nhất định của một chi nhánh mà tôi muốn hợp nhất
Kulbhushan Singh

3

Trong trường hợp sử dụng của tôi, chúng tôi có nhu cầu tương tự đối với CI CD. Chúng tôi đã sử dụng dòng git với các nhánh phát triển và chủ. Các nhà phát triển có thể tự do hợp nhất các thay đổi trực tiếp để phát triển hoặc thông qua yêu cầu kéo từ một nhánh tính năng. Tuy nhiên, để thành thạo, chúng tôi chỉ hợp nhất các cam kết ổn định từ nhánh phát triển theo cách tự động thông qua Jenkins.

Trong trường hợp này, làm cherry-pick không phải là một lựa chọn tốt. Tuy nhiên, chúng tôi tạo một nhánh cục bộ từ id xác thực sau đó hợp nhất nhánh cục bộ đó thành chủ và thực hiện xác minh sạch mvn (chúng tôi sử dụng maven). Nếu thành công thì phát hành phiên bản sản xuất artifact cho nexus bằng cách sử dụng plugin phát hành maven với tùy chọn localCheckout = true và pushChanges = false. Cuối cùng khi mọi thứ thành công, sau đó đẩy các thay đổi và gắn thẻ vào nguồn gốc.

Một đoạn mã mẫu:

Giả sử bạn đang làm chủ nếu được thực hiện thủ công. Tuy nhiên trên jenkins, khi bạn kiểm tra repo, bạn sẽ ở nhánh mặc định (chính nếu được cấu hình).

git pull  // Just to pull any changes.
git branch local-<commitd-id> <commit-id>  // Create a branch from the given commit-id
git merge local-<commit-id>  // Merge that local branch to master.
mvn clean verify   // Verify if the code is build able
mvn <any args> release:clean release:prepare release:perform // Release artifacts
git push origin/master  // Push the local changes performed above to origin.
git push origin <tag>  // Push the tag to origin

Điều này sẽ cung cấp cho bạn toàn quyền kiểm soát với một địa ngục hợp nhất hoặc xung đột không sợ hãi.

Hãy tư vấn trong trường hợp có bất kỳ lựa chọn tốt hơn.


3

Các câu trả lời hàng đầu mô tả cách áp dụng các thay đổi từ một cam kết cụ thể cho chi nhánh hiện tại. Nếu đó là những gì bạn có nghĩa là "làm thế nào để hợp nhất", thì chỉ cần sử dụng cherry-pick như họ đề xuất.

Nhưng nếu bạn thực sự muốn hợp nhất , tức là bạn muốn có một cam kết mới với hai cha mẹ - cam kết hiện có trên nhánh hiện tại và cam kết bạn muốn áp dụng các thay đổi từ - thì việc chọn cherry sẽ không thực hiện được điều đó.

Có lịch sử hợp nhất thực sự có thể là mong muốn, ví dụ, nếu quá trình xây dựng của bạn tận dụng lợi thế của tổ tiên để tự động đặt chuỗi phiên bản dựa trên thẻ mới nhất (sử dụng git describe).

Thay vì chọn cherry, bạn có thể thực hiện git merge --no-commitvà sau đó điều chỉnh chỉ mục theo cách thủ công để xóa mọi thay đổi bạn không muốn.

Giả sử bạn đang ở chi nhánh Avà bạn muốn hợp nhất cam kết ở đầu chi nhánh B:

git checkout A
git merge --no-commit B

Bây giờ bạn đã được thiết lập để tạo một cam kết với hai cha mẹ, mẹo hiện tại là AB. Tuy nhiên, bạn có thể có nhiều thay đổi được áp dụng hơn bạn muốn, bao gồm các thay đổi từ các cam kết trước đó trên nhánh B. Bạn cần hoàn tác những thay đổi không mong muốn này, sau đó cam kết.

(Có thể có một cách dễ dàng để đặt trạng thái của thư mục làm việc và chỉ mục trở lại trạng thái trước khi hợp nhất, để bạn có một bảng xếp hạng rõ ràng để chọn cherry mà bạn muốn ở vị trí đầu tiên. Nhưng Tôi không biết làm thế nào để đạt được danh sách sạch đó git checkout HEADgit reset HEADcả hai sẽ loại bỏ trạng thái hợp nhất, đánh bại mục đích của phương pháp này.)

Vì vậy, thủ công hoàn tác những thay đổi không mong muốn. Ví dụ, bạn có thể

git revert --no-commit 012ea56

cho mỗi cam kết không mong muốn 012ea56.

Khi bạn hoàn thành việc điều chỉnh mọi thứ, hãy tạo cam kết của bạn:

git commit -m "Merge in commit 823749a from B which tweaked the timeout code"

Bây giờ bạn chỉ có thay đổi bạn muốn và cây tổ tiên cho thấy bạn đã hợp nhất về mặt kỹ thuật từ B.

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.