Làm thế nào để sao chép cam kết từ chi nhánh này sang chi nhánh khác?


728

Tôi đã có hai chi nhánh từ chủ của mình:

  • v2.1 : (phiên bản 2) Tôi đã làm việc được vài tháng
  • wss : mà tôi đã tạo ngày hôm qua để thêm một tính năng cụ thể vào chủ của mình (trong sản xuất)

Có cách nào để sao chép các cam kết của ngày hôm qua từ wss sang v2.1 không?


Để chỉ cần sao chép các cam kết (hoặc một loạt các cam kết) từ chi nhánh này sang chi nhánh khác, câu trả lời này đã giúp tôi tốt nhất: stackoverflow.com/questions/1994463/
Kẻ

Câu trả lời:


566

Bạn thực sự nên có một quy trình làm việc cho phép bạn làm tất cả điều này bằng cách hợp nhất:

- x - x - x (v2) - x - x - x (v2.1)
           \
            x - x - x (wss)

Vì vậy, tất cả bạn phải làm là git checkout v2.1git merge wss. Nếu vì một lý do nào đó bạn thực sự không thể làm điều này và bạn không thể sử dụng git rebase để di chuyển nhánh wss của mình đến đúng nơi, lệnh lấy một cam kết duy nhất từ ​​một nơi nào đó và áp dụng nó ở nơi khác là git cherry-pick . Chỉ cần kiểm tra chi nhánh bạn muốn áp dụng nó và chạy git cherry-pick <SHA of commit to cherry-pick>.

Một số cách rebase có thể cứu bạn:

Nếu lịch sử của bạn trông như thế này:

- x - x - x (v2) - x - x - x (v2.1)
           \
            x - x - x (v2-only) - x - x - x (wss)

Bạn có thể sử dụng git rebase --onto v2 v2-only wssđể di chuyển wss trực tiếp lên v2:

- x - x - x (v2) - x - x - x (v2.1)
          |\
          |  x - x - x (v2-only)
           \
             x - x - x (wss)

Sau đó, bạn có thể hợp nhất! Nếu bạn thực sự, thực sự, thực sự không thể đến điểm mà bạn có thể hợp nhất, bạn vẫn có thể sử dụng rebase để thực hiện một số hiệu quả một vài quả anh đào cùng một lúc:

# wss-starting-point is the SHA1/branch immediately before the first commit to rebase
git branch wss-to-rebase wss
git rebase --onto v2.1 wss-starting-point wss-to-rebase
git checkout v2.1
git merge wss-to-rebase

Lưu ý: lý do cần một số công việc bổ sung để thực hiện việc này là vì nó tạo ra các cam kết trùng lặp trong kho lưu trữ của bạn. Đây thực sự không phải là một điều tốt - toàn bộ vấn đề dễ dàng phân nhánh và hợp nhất là có thể thực hiện mọi thứ bằng cách thực hiện một (các) cam kết và sáp nhập chúng vào bất cứ nơi nào chúng cần. Cam kết trùng lặp có nghĩa là một ý định không bao giờ hợp nhất hai nhánh đó (nếu bạn quyết định bạn muốn sau này, bạn sẽ gặp xung đột).


1
Không thể đồng ý nhiều hơn với câu trả lời này. +1. Xem thêm câu trả lời cũ của tôi để minh họa hậu quả của việc hái anh đào: stackoverflow.com/questions/881092/
mẹo

18
Câu trả lời tuyệt vời về cách làm điều này một cách thích hợp ! Tôi ước tôi cũng có thể bỏ phiếu hai lần cho nỗ lực tạo sơ đồ ASCII.
gotgenes

@VonC: Cảm ơn sự hỗ trợ và thông tin thêm về lý do tại sao không chọn cherry - Tôi biết tôi đã lướt qua một chút ở đó. @gotgenes: Cảm ơn! Tôi nghĩ rằng nó hoàn toàn xứng đáng với nỗ lực - chỉ cần nhìn vào trang chủ git-rebase. Không có cách nào tốt hơn để giải thích nó.
Cascabel

Về lý do tại sao bạn không thể hợp nhất - Hợp nhất Git không hoạt động tốt với git-svn. Để sao chép một loạt các cam kết từ một chi nhánh SVN sang một chi nhánh khác, tôi đã kết thúc việc chọn chúng và sau đó thực hiện một rebase / tua lại tương tác để xóa các git-svn-idtham chiếu không chính xác trước khi nhập dcommitlại. Mặc dù tôi có lẽ đã bỏ qua bước chọn anh đào và chỉ sử dụng một cuộc nổi loạn của chính nó.
Bob

1
Đây là trường hợp sử dụng của tôi: sửa lỗi nghiêm trọng đã được cam kết với nhánh tính năng. Tôi cần nó trong chủ để đi vào sản xuất bây giờ. Điều này sẽ cứu mông của tôi.
Thuyền trưởng Hypertext

910

Sử dụng

git cherry-pick <commit>

để áp dụng <commit>cho chi nhánh hiện tại của bạn .

Bản thân tôi có lẽ sẽ kiểm tra chéo các cam kết mà tôi chọn gitkvà chọn chúng bằng các nhấp chuột phải vào mục cam kết thay thế.


Nếu bạn muốn tự động hơn (với tất cả các nguy hiểm của nó) và giả sử tất cả các cam kết kể từ ngày hôm qua đã xảy ra trên wss, bạn có thể tạo danh sách các cam kết bằng cách sử dụng git log(với --prettyđề xuất của Jefromi)

git log --reverse --since=yesterday --pretty=%H

vì vậy mọi thứ cùng nhau giả sử bạn sử dụng bash

for commit in $(git log --reverse --since=yesterday --pretty=%H);
do
    git cherry-pick $commit
done

Nếu có điều gì đó không ổn ở đây (có rất nhiều tiềm năng) bạn sẽ gặp rắc rối vì việc này hoạt động trên thanh toán trực tiếp, do đó, hãy thực hiện thủ công bằng tay hoặc sử dụng rebase như đề xuất của Jefromi.


Tất cả các trình giữ chỗ cho tùy chọn --pretty đều nằm trong trang chủ git-log. Bạn có thể nhận bất kỳ định dạng nào bạn muốn - đặc biệt hữu ích để nhận các trường bạn muốn cho tập lệnh ở dạng dễ phân tích cú pháp.
Cascabel

Tôi cũng muốn chỉ ra rằng, giả sử bạn thực sự muốn tạo các cam kết trùng lặp, phương thức sử dụng git rebasetrong câu trả lời của tôi mạnh mẽ hơn. Đặc biệt, bằng cách sử dụng một vòng lặp for như thế này, nếu một trong số các cherry-picks thất bại, nó vẫn sẽ cố gắng thực hiện tất cả phần còn lại của chúng. Điều này ... rất rất không tốt, hãy nói.
Cascabel

2
Đã đồng ý. Đó là lý do tại sao tôi không bao giờ sử dụng nó, nhưng làm nó bằng tay. Nhưng cherry-pick vẫn là câu trả lời, ít nhất là cho tiêu đề câu hỏi. Tôi đã sửa đổi các phản ứng.
Benjamin Bannier

1
Ai đó đã cam kết với một chi nhánh cũ / không chính xác và cherry-pick hãy để tôi đưa cam kết đó vào đúng chi nhánh (trong khi vẫn giữ họ làm người đi lại). Hoàn hảo.
Patrick

8
Một cảnh tượng hiếm hoi, một gitcâu trả lời đơn giản và đi thẳng vào giải pháp, thay vì uốn khúc qua những rắc rối của git để chứng minh người trả lời hiểu rõ như thế nào.
Przemek D

74

git cherry-pick : Áp dụng các thay đổi được giới thiệu bởi một số cam kết hiện có

Giả sử chúng ta có nhánh A với (X, Y, Z) cam kết. Chúng tôi cần phải thêm những cam kết để chi nhánh B . Chúng tôi sẽ sử dụng các cherry-pickhoạt động.

Khi chúng ta sử dụng cherry-pick, chúng ta nên thêm cam kết về chi nhánh B theo thứ tự thời gian tương tự mà các cam kết xuất hiện trong Chi nhánh Một .

cherry-pick không hỗ trợ một loạt các cam kết, nhưng nếu bạn có các cam kết hợp nhất trong phạm vi đó, nó sẽ thực sự phức tạp

git checkout B
git cherry-pick SHA-COMMIT-X
git cherry-pick SHA-COMMIT-Y
git cherry-pick SHA-COMMIT-Z

Ví dụ về quy trình làm việc:

nhập mô tả hình ảnh ở đây

Chúng ta có thể sử dụng cherry-pickvới các tùy chọn

-e hoặc --edit : Với tùy chọn này, git cherry-pick sẽ cho phép bạn chỉnh sửa thông báo cam kết trước khi cam kết.

-n hoặc --no-commit : Thông thường lệnh sẽ tự động tạo một chuỗi các xác nhận. Cờ này áp dụng các thay đổi cần thiết để chọn từng cam kết có tên cho cây làm việc của bạn và chỉ mục mà không thực hiện bất kỳ cam kết nào. Ngoài ra, khi tùy chọn này được sử dụng, chỉ mục của bạn không phải khớp với cam kết CHÍNH. Lựa chọn anh đào được thực hiện so với trạng thái bắt đầu của chỉ mục của bạn.

Đây là một bài viết thú vị liên quan cherry-pick.


19

Bạn có thể tạo một bản vá từ các cam kết mà bạn muốn sao chép và áp dụng bản vá cho nhánh đích.


16
Ngay cả khi bạn vì một lý do nào đó thực sự muốn sử dụng patch (es) thay vì cherry-pick (s) / rebase, cách đơn giản để thực hiện là với git format-patch <revision range>git am *.patch.
Cascabel

Nó đòi hỏi checkoutmột chi nhánh khác.
CoolMind

12

Hoặc nếu bạn ít hơn về phía nhà truyền giáo Bạn có thể làm một cách xấu xí mà tôi đang sử dụng. Trong triển khai_template có các cam kết tôi muốn sao chép trên chủ của mình dưới dạng triển khai chi nhánh

git branch deploy deploy_template
git checkout deploy
git rebase master

Điều này sẽ tạo ra triển khai nhánh mới (tôi sử dụng -f để ghi đè lên nhánh triển khai hiện có) trên triển khai_template, sau đó khởi động lại nhánh mới này lên master, không bị chạm vào.


1

Đối với trường hợp đơn giản là chỉ sao chép lần xác nhận cuối cùng từ nhánh wss sang v2.1, bạn chỉ cần lấy id xác thực ( git log --oneline | head -n 1) và thực hiện:

git checkout v2.1
git merge <commit>

Điều này đòi hỏi phải kiểm tra đến một chi nhánh khác.
CoolMind

1

Lệnh cherry-pick có thể đọc danh sách các xác nhận từ đầu vào tiêu chuẩn.

Lệnh cherry-picks sau đây được ủy quyền bởi người dùng John tồn tại trong nhánh "phát triển" nhưng không thuộc nhánh "phát hành" và thực hiện theo thứ tự thời gian.

git log develop --not release --format=%H --reverse --author John | git cherry-pick --stdin
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.