Git hợp nhất tổng thể vào nhánh tính năng


1017

Giả sử chúng ta có tình huống sau trong Git:

  1. Một kho lưu trữ đã tạo:

    mkdir GitTest2
    cd GitTest2
    git init
    
  2. Một số sửa đổi trong bản gốc diễn ra và được cam kết:

    echo "On Master" > file
    git commit -a -m "Initial commit"
    
  3. Feature1 rẽ nhánh chủ và một số công việc được thực hiện:

    git branch feature1
    git checkout feature1
    echo "Feature1" > featureFile
    git commit -a -m "Commit for feature1"
    
  4. Trong khi đó, một lỗi được phát hiện trong mã chủ và một nhánh hotfix được thiết lập:

    git checkout master
    git branch hotfix1
    git checkout hotfix1
    
  5. Lỗi được sửa trong nhánh hotfix và được hợp nhất trở lại vào bản gốc (có lẽ sau khi xem xét yêu cầu kéo / mã):

    echo "Bugfix" > bugfixFile
    git commit -a -m "Bugfix Commit"
    git checkout master
    git merge --no-ff hotfix1
    
  6. Phát triển trên Feature1 tiếp tục:

    git checkout feature1
    

Nói rằng tôi cần hotfix trong nhánh tính năng của mình, có thể do lỗi cũng xảy ra ở đó. Làm cách nào tôi có thể đạt được điều này mà không cần sao chép các cam kết vào nhánh tính năng của mình?

Tôi muốn ngăn chặn để có được hai cam kết mới trên nhánh tính năng của mình mà không liên quan đến việc triển khai tính năng. Điều này đặc biệt quan trọng đối với tôi nếu tôi sử dụng các yêu cầu kéo: Tất cả các cam kết này cũng sẽ được bao gồm trong yêu cầu kéo và phải được xem xét mặc dù điều này đã được thực hiện (vì hotfix đã có trong bản gốc).

Tôi không thể thực hiện git merge master --ff-only: "gây tử vong: Không thể tiến nhanh, hủy bỏ.", Nhưng tôi không chắc liệu điều này có giúp tôi không.


8
Nếu chi nhánh feature1là hoàn toàn địa phương, có một cái nhìn tại git rebase.
Jokester

19
Cảm ơn, với tư cách là một người mới bắt đầu, git rebasecó vẻ như là ma thuật đen đối với tôi ....
theomega

13
nếu nhánh đó là tính năng - thì không nên sửa lỗi ở đó (ít nhất nếu đó không phải là lỗi chặn) vì mục đích của nhánh này là hiển thị một tính năng mới. Lỗi sẽ được sửa khi được hợp nhất với bản gốc nơi có cam kết với bản sửa lỗi.
gipi

21
Có lẽ đáng chú ý cho người mới bắt đầu trong 3. git branch feature1git checkout feature1có thể được kết hợp thành git checkout -b feature1và 4. hoàn toàn có thể được giảm xuống thànhgit checkout -b hotfix1 master
Naruto Sempai

3
Bạn có sẵn lòng quay lại và thay đổi câu trả lời được chấp nhận không, bởi vì câu trả lời được chấp nhận hiện tại là khủng khiếp.
Omnifarious

Câu trả lời:


1218

Làm thế nào để chúng ta hợp nhất nhánh chủ vào nhánh tính năng? Dễ dàng:

git checkout feature1
git merge master

Không có điểm nào buộc phải hợp nhất chuyển tiếp nhanh ở đây, vì nó không thể được thực hiện. Bạn đã cam kết cả vào nhánh tính năng và nhánh chính. Chuyển tiếp nhanh là không thể bây giờ.

Hãy xem GitFlow . Đây là một mô hình phân nhánh cho git có thể được theo dõi, và bạn đã vô thức làm điều đó. Nó cũng là một phần mở rộng cho Git, bổ sung một số lệnh cho các bước tiến trình công việc mới tự động thực hiện mọi thứ mà bạn cần phải làm thủ công.

Vì vậy, những gì bạn đã làm ngay trong quy trình làm việc của bạn? Bạn có hai nhánh để làm việc, nhánh Feature1 của bạn về cơ bản là nhánh "phát triển" trong mô hình GitFlow.

Bạn đã tạo một nhánh hotfix từ chủ và hợp nhất lại. Và bây giờ bạn đang bị mắc kẹt.

Mô hình GitFlow yêu cầu bạn hợp nhất hotfix cũng với nhánh phát triển, đó là "Feature1" trong trường hợp của bạn.

Vì vậy, câu trả lời thực sự sẽ là:

git checkout feature1
git merge --no-ff hotfix1

Điều này thêm tất cả các thay đổi được thực hiện bên trong hotfix vào nhánh tính năng, nhưng chỉ những thay đổi đó. Chúng có thể xung đột với các thay đổi phát triển khác trong nhánh, nhưng chúng sẽ không xung đột với nhánh chính nếu cuối cùng bạn hợp nhất nhánh tính năng trở lại thành chủ.

Hãy rất cẩn thận với việc nổi loạn. Chỉ rebase nếu những thay đổi bạn đã lưu cục bộ vào kho lưu trữ của bạn, ví dụ: bạn không đẩy bất kỳ chi nhánh nào sang một số kho lưu trữ khác. Rebasing là một công cụ tuyệt vời để bạn sắp xếp các cam kết địa phương của mình thành một trật tự hữu ích trước khi đẩy nó ra thế giới, nhưng việc nổi loạn sau đó sẽ làm rối tung mọi thứ cho những người mới bắt đầu như bạn.


7
Không. Cam kết sửa lỗi chỉ xuất hiện một lần trong nhánh hotfix, mặc dù tên nhánh bị xóa sau khi được sáp nhập vào nhánh chính và nhánh phát. Cam kết hợp nhất chỉ hiển thị các thay đổi được giới thiệu bởi hợp nhất, trông giống như một cam kết trùng lặp. Nhưng đây là cách git hoạt động: Chi nhánh và hợp nhất trở lại. Công việc phát triển thực sự chỉ diễn ra trong các cam kết không hợp nhất và chỉ hợp nhất được chấp nhận nếu kết quả là phần mềm hoạt động.
Sven

42
Đây phải là câu trả lời được chấp nhận. Nó cũng hoạt động tốt với tính năng yêu cầu kéo của GitHub.
Nỗi nhớ.io

125
Tôi nghĩ rằng nó đáng chú ý là một git merge mastersẽ hợp nhất từ của bạn địa phương bản sao của bậc thầy, vì vậy ngay cả khi bạn đã thực hiện một git pulltrong ngành năng của bạn sau khi người khác sáp nhập một chi nhánh khác nhau vào tổng thể, bạn sẽ cần phải git checkout master, sau đó git pull, sau đó git checkout feature1một lần nữa và THEN git merge master.
chết tiệt

50
@damick Hoặc chỉ git fetchgit merge origin/master
Yngvar Kristiansen

20
@damick @ yngvar-kristiansen git pull origin mastersẽ tự động hợp nhất orgin/mastervới chi nhánh hiện tại
L422Y

613

Bạn sẽ có thể rebase chi nhánh của bạn trên master:

git checkout feature1
git rebase master

Quản lý tất cả các xung đột phát sinh. Khi bạn nhận được các xác nhận với các lỗi (đã có bản gốc), Git sẽ nói rằng không có thay đổi nào và có thể chúng đã được áp dụng. Sau đó, bạn tiếp tục rebase (trong khi bỏ qua các xác nhận đã có trong master) với

git rebase --skip

Nếu bạn thực hiện một git lognhánh trên nhánh tính năng của mình, bạn sẽ thấy cam kết sửa lỗi chỉ xuất hiện một lần và trong phần chính.

Để thảo luận chi tiết hơn, hãy xem tài liệu về sách Git trên git rebase( https://git-scm.com/docs/git-rebase ) bao gồm trường hợp sử dụng chính xác này.

================ Chỉnh sửa cho bối cảnh bổ sung ====================

Câu trả lời này được cung cấp cụ thể cho câu hỏi của @theomega, tính đến tình huống cụ thể của anh ấy. Lưu ý phần này:

Tôi muốn ngăn [...] cam kết trên nhánh tính năng của mình không liên quan đến việc triển khai tính năng.

Đánh bại chi nhánh riêng của mình trên chủ là chính xác những gì sẽ mang lại kết quả đó. Ngược lại, sáp nhập chủ vào chi nhánh của mình sẽ thực hiện chính xác những gì anh ta không muốn xảy ra : thêm một cam kết không liên quan đến việc triển khai tính năng mà anh ta đang thực hiện thông qua chi nhánh của mình.

Để giải quyết những người dùng đọc tiêu đề câu hỏi, bỏ qua nội dung và bối cảnh thực tế của câu hỏi và sau đó chỉ đọc câu trả lời hàng đầu một cách mù quáng giả sử nó sẽ luôn áp dụng cho trường hợp sử dụng (khác nhau) của họ, cho phép tôi giải thích:

  • chỉ rebase các nhánh riêng (tức là chỉ tồn tại trong kho lưu trữ cục bộ của bạn và chưa được chia sẻ với người khác). Đánh bại các chi nhánh được chia sẻ sẽ "phá vỡ" các bản sao mà người khác có thể có.
  • nếu bạn muốn tích hợp các thay đổi từ một chi nhánh (dù là chủ hay chi nhánh khác) vào một chi nhánh công khai (ví dụ: bạn đã đẩy chi nhánh để mở yêu cầu kéo, nhưng hiện tại có xung đột với chủ và bạn cần cập nhật chi nhánh của bạn để giải quyết những xung đột đó) bạn sẽ cần hợp nhất chúng vào (ví dụ git merge masternhư trong câu trả lời của @ Sven).
  • bạn cũng có thể hợp nhất các chi nhánh vào các chi nhánh tư nhân địa phương nếu đó là sở thích của bạn, nhưng lưu ý rằng nó sẽ dẫn đến các cam kết "nước ngoài" trong chi nhánh của bạn.

Cuối cùng, nếu bạn không hài lòng với thực tế rằng câu trả lời này không phù hợp nhất với tình huống của bạn mặc dù là cho @theomega, việc thêm một bình luận bên dưới sẽ không đặc biệt hữu ích: Tôi không kiểm soát câu trả lời nào được chọn, chỉ @theomega làm.


136
Không, nó không an toàn: nếu bạn nổi loạn, bạn đang thay đổi lịch sử của chi nhánh, điều này sẽ ảnh hưởng đến các nhà phát triển đã kéo chi nhánh. Theo mặc định, git sẽ không cho phép bạn đẩy một nhánh bị từ chối theo mặc định: bạn cần buộc cập nhật -fkhi nhấn để ghi đè lên nhánh bằng phiên bản bị khởi động lại. Hãy cẩn thận!
David Sulc

17
Làm thế nào để các đội chuyên nghiệp sử dụng git xử lý vấn đề này? Làm chỉ cần chú ý, suy nghĩ cẩn thận và sau đó làm một -f? Hoặc là hoàn thành công việc của tôi thiếu sót bởi vì tôi cần một -f?
theomega

30
Chà, tôi mạo hiểm quy tắc "thiêng liêng" là bạn không rebase (hay nói cách khác là thay đổi lịch sử cam kết) trên mã đã được chia sẻ: nó chỉ dành cho mã địa phương của bạn. Về cơ bản, bạn nên từ chối các thay đổi của mình để "dọn dẹp" trước khi chia sẻ nó. Trong trường hợp của bạn, bạn có thể đẩy một nhánh nổi loạn mới (với một tên khác) và yêu cầu các đồng nghiệp căn cứ vào các thay đổi của họ khỏi nhánh đó (tức là bằng cách loại bỏ nhánh địa phương của họ khỏi nhánh mới, như trên). Sau đó, xóa feature1khỏi Github.
David Sulc

19
Hầu hết các đội chuyên nghiệp mà tôi đã làm việc gần như không bao giờ sử dụng rebase - họ chỉ hợp nhất mọi thứ theo mặc định, do đó không có sửa đổi lịch sử nào xảy ra. Đây là cách làm việc ưa thích của tôi. Mặt khác, một số đội sử dụng rebase để 'dọn dẹp' các cam kết trước khi họ đẩy chúng (nhưng không bao giờ sau khi đẩy.)
Jonathan Hartley

11
Có, bạn KHÔNG BAO GIỜ nên từ chối các chi nhánh công cộng. Tuy nhiên, câu hỏi của OP dường như liên quan đến việc tích hợp các cam kết mới được thực hiện mastervào một chi nhánh tư nhân (ông đề cập đến "chi nhánh địa phương" của mình). Trong trường hợp đó, rebasevẫn ổn và là trường hợp sử dụng giống như "dọn dẹp" mà bạn đề cập.
David Sulc

69

Dựa trên bài viết này , bạn nên:

  • tạo chi nhánh mới dựa trên phiên bản mới của chủ

    git branch -b newmaster

  • hợp nhất nhánh tính năng cũ của bạn thành một nhánh mới

    git checkout newmaster

  • giải quyết xung đột trên nhánh tính năng mới

Hai lệnh đầu tiên có thể được kết hợp với git checkout -b newmaster.

Bằng cách này, lịch sử của bạn vẫn rõ ràng vì bạn không cần sáp nhập lại. Và bạn không cần phải quá thận trọng vì bạn không cần phải thực hiện một cuộc nổi loạn Git.


7
sẽ tốt hơn nếu bạn thực hiện lệnh git liên quan theo từng điểm. Nếu không, có vẻ như đây thực sự là lựa chọn an toàn và sạch sẽ hơn.
VirgileD

@zimi Có gì nếu chúng ta có một chi nhánh từ xa? Chúng tôi sẽ tạo lại nhánh tính năng cập nhật mới một lần nữa? Hoặc chúng ta chỉ có thể thiết lập từ xa ngược dòng?
HÓA

@VirgileD Tôi vừa đăng câu trả lời của riêng mình với nhiều chi tiết hơn, bao gồm các lệnh git liên quan.
jkdev

29

git merge

bạn có thể làm theo các bước dưới đây

1. hợp nhất origin/masterchi nhánh với featurechi nhánh

# step1: change branch to master, and pull to update all commits
$ git checkout master
$ git pull

# step2: change branch to target, and pull to update commits
$ git checkout feature
$ git pull

# step3: merge master to feature(⚠️ current is feature branch)
$ git merge master


2. hợp nhất featurechi nhánh với origin/masterchi nhánh

origin/masterlà nhánh chủ từ xa, trong khi masterlà nhánh chính cục bộ

$ git checkout master
$ git pull origin/master

$ git merge feature
$ git push origin/master





Cảm giác như rebase được thổi phồng! Tốt cũ hợp nhất :)!
Trước

27

Câu trả lời của Zimi mô tả quá trình này nói chung. Dưới đây là chi tiết cụ thể:

  1. Tạo và chuyển sang một chi nhánh mới. Hãy chắc chắn rằng nhánh mới được dựa trên masterđể nó sẽ bao gồm các hotfix gần đây.

    git checkout master
    git branch feature1_new
    git checkout feature1_new
    
    # Or, combined into one command:
    git checkout -b feature1_new master
    
  2. Sau khi chuyển sang nhánh mới, hợp nhất các thay đổi từ nhánh tính năng hiện có của bạn. Điều này sẽ thêm các cam kết của bạn mà không cần sao chép các cam kết hotfix.

    git merge feature1
    
  3. Trên nhánh mới, giải quyết mọi xung đột giữa tính năng của bạn và nhánh chính.

Làm xong! Bây giờ sử dụng chi nhánh mới để tiếp tục phát triển tính năng của bạn.


2
Vấn đề với điều này là một nhà phát triển lãng phí thời gian liên tục sinh ra các nhánh mới khi họ cần cập nhật chống lại chủ. Chúng tôi sẽ tạo ra rất nhiều chi nhánh, có thể 3 lần mỗi ngày trong khi làm việc tích cực. Bạn nên viết hướng dẫn về dọn dẹp tất cả các nhánh rác địa phương và cách loại bỏ chúng trên điều khiển từ xa. Chúng tôi cũng cần lời khuyên về việc đặt tên cho tất cả các chi nhánh này để chúng tôi không bị nhầm lẫn. Không có bit đó, điều này sẽ biến một hệ thống chi nhánh thành hỗn loạn.
pauljohn32

4
Bạn nói đúng, điều này không nên được thực hiện mọi lúc. Chỉ khi (1) các thay đổi trên bản gốc là cần thiết cho tính năng của bạn hoặc (2) bạn sắp hợp nhất chi nhánh của mình với chủ và có thể có xung đột. Và để tránh sự lộn xộn, bạn có thể xóa chi nhánh của mình sau khi nó được hợp nhất.
jkdev

11

Đây là một kịch bản bạn có thể sử dụng để hợp nhất nhánh chính của bạn vào nhánh hiện tại của bạn.

Kịch bản thực hiện như sau:

  • Chuyển sang nhánh chính
  • Kéo nhánh chính
  • Chuyển về chi nhánh hiện tại của bạn
  • Hợp nhất nhánh chính vào nhánh hiện tại của bạn

Lưu mã này dưới dạng tệp bó (.bat) và đặt tập lệnh ở bất kỳ đâu trong kho lưu trữ của bạn. Sau đó bấm vào nó để chạy nó và bạn đã được thiết lập.

:: This batch file pulls current master and merges into current branch

@echo off

:: Option to use the batch file outside the repo and pass the repo path as an arg
set repoPath=%1
cd %repoPath%

FOR /F "tokens=*" %%g IN ('git rev-parse --abbrev-ref HEAD') do (SET currentBranch=%%g)

echo current branch is %currentBranch%
echo switching to master
git checkout master
echo.
echo pulling origin master
git pull origin master
echo.
echo switching back to %currentBranch%
git checkout %currentBranch%
echo.
echo attemting merge master into %currentBranch%
git merge master
echo.
echo script finished successfully
PAUSE

10

Bạn có thể thực hiện "chọn anh đào" để kéo (các) cam kết chính xác mà bạn cần vào nhánh tính năng của mình.

Làm một git checkout hotfix1để có được trên nhánh hotfix1. Sau đó thực hiện git logđể có được hàm băm SHA-1 (chuỗi lớn các chữ cái và số ngẫu nhiên xác định duy nhất một cam kết) của cam kết trong câu hỏi. Sao chép đó (hoặc 10 ký tự đầu tiên hoặc lâu hơn).

Sau đó, git checkout feature1để quay lại chi nhánh tính năng của bạn.

Sau đó, git cherry-pick <the SHA-1 hash that you just copied>

Điều đó sẽ kéo cam kết đó và chỉ cam kết đó vào nhánh tính năng của bạn. Sự thay đổi đó sẽ nằm trong chi nhánh - bạn chỉ cần "chọn anh đào". Sau đó, tiếp tục công việc, chỉnh sửa, cam kết, đẩy, v.v. vào nội dung trái tim của bạn.

Cuối cùng, khi bạn thực hiện hợp nhất khác từ một nhánh vào nhánh tính năng của mình (hoặc ngược lại), Git sẽ nhận ra rằng bạn đã hợp nhất trong cam kết cụ thể đó , biết rằng nó không phải thực hiện lại và chỉ cần "Bỏ qua" nó.


Tôi không coi đây là một ý tưởng tốt. Sau đó, IMO, cam kết hotfix sẽ thực sự hiển thị trong lịch sử của nhánh tính năng của bạn, điều mà về cơ bản bạn không muốn.
Martin Pecka

1
Cuối cùng, khi bạn thực hiện một sự hợp nhất khác từ một nhánh vào nhánh tính năng của bạn (hoặc ngược lại), git sẽ nhận ra rằng bạn đã hợp nhất [...], - đó có phải là cách nó thực sự hoạt động không? Tôi không nghĩ rằng git mergecác tác phẩm trong bản phát lại này của Cam kết cam kết với bạn - mà bạn dường như đang ám chỉ (về và chỉ cần bỏ qua nó). Trộn lẫn hái anh đào và sáp nhập rõ ràng có thể dẫn đến các vấn đề; xem: news.ycombinator.com/item?id=3947950
Guildenstern

0

Tôi đang ở trong nhánh tính năng và thực hiện tái cấu trúc. Tôi muốn hợp nhất các thay đổi chính bây giờ với nhánh tính năng của tôi. Tôi ở xa phía sau. Lưu ý Tôi không muốn kéo các thay đổi chính về cục bộ của mình vì nhánh tính năng của tôi có các mô-đun được di chuyển từ nơi này sang nơi khác. Tôi tìm thấy chỉ thực hiện dưới đây mà không kéo không hoạt động. nó nói "Đã cập nhật."

 //below does not get the latest from remote master to my local feature branch without git pull
    git checkout master 
    git fetch 
    git checkout my-feature-branch 
    git merge master

Điều này dưới đây hoạt động, lưu ý sử dụng git merge origin / master:

 git checkout master 
    git fetch 
    git checkout my-feature-branch 
    git merge origin/master
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.