Câu hỏi Khi nào bạn sử dụng git rebase thay vì git merge?


Khi nào thì nên sử dụng git rebase so với git merge?

Tôi vẫn cần hợp nhất sau khi hoàn thành thành công?


1249
2018-04-29 20:26


gốc


Xem: stackoverflow.com/questions/457927/… - TStamper
điều này là tốt: atlassian.com/git/tutorials/merging-vs-rebasing - stackexchanger
Một vấn đề với những người muốn sử dụng rebase là nó ngăn cản họ đẩy mã của họ thường xuyên. Vì vậy, muốn lịch sử sạch sẽ ngăn cản họ chia sẻ mã của họ, điều tôi nghĩ là quan trọng hơn. - static_rtti
@static_rtti: Điều đó không đúng. Bạn đang sử dụng quy trình dựa trên rebase nếu nó không cho phép bạn thường xuyên thay đổi các thay đổi của mình. - juzzlin
Đó là một sự xấu hổ thực sự Andrew Arnott's câu trả lời và Pace's câu trả lời không được đăng trước đó, khi họ trả lời câu hỏi này toàn diện hơn các câu trả lời trước đó đã tích lũy được nhiều phiếu bầu. - Mark Booth


Các câu trả lời:


Phiên bản ngắn

  • Hợp nhất có tất cả các thay đổi trong một nhánh và kết hợp chúng vào một nhánh khác trong một lần commit.
  • Rebase nói rằng tôi muốn điểm mà tại đó tôi phân nhánh để chuyển đến một điểm khởi đầu mới

Vì vậy, khi nào bạn sử dụng một trong hai?

Hợp nhất

  • Giả sử bạn đã tạo một chi nhánh với mục đích phát triển một tính năng duy nhất. Khi bạn muốn mang những thay đổi đó trở lại làm chủ, bạn có thể muốn hợp nhất (bạn không quan tâm đến việc duy trì tất cả các cam kết tạm thời).

Rebase

  • Kịch bản thứ hai sẽ là nếu bạn bắt đầu thực hiện một số phát triển và sau đó một nhà phát triển khác đã thực hiện một thay đổi không liên quan. Bạn có thể muốn kéo và sau đó rebase để căn cứ các thay đổi của bạn từ phiên bản hiện tại từ repo.

973
2018-04-29 20:38



@Rob đề cập đến việc duy trì các cam kết tạm thời khi hợp nhất. Tôi tin rằng theo mặc định sáp nhập chi nhánh B (một chi nhánh tính năng bạn đã làm việc trên) vào nhánh M (nhánh chính) sẽ tạo ra một cam kết trong M cho mỗi cam kết đã được thực hiện trong B kể từ khi hai phân kỳ. Nhưng nếu bạn hợp nhất bằng cách sử dụng tùy chọn --squash, tất cả các cam kết được thực hiện trên nhánh B sẽ được "gộp lại với nhau" và được hợp nhất thành một cam kết duy nhất trên nhánh M, giữ nhật ký trên nhánh chính của bạn đẹp và sạch. Squashing có lẽ là những gì bạn muốn nếu bạn có nhiều nhà phát triển làm việc độc lập và sáp nhập trở lại làm chủ. - spaaarky21
Tôi tin rằng giả định của @ spaaarky21 về việc sáp nhập là không chính xác. Nếu bạn hợp nhất một nhánh B vào M chủ, thì sẽ chỉ có một cam kết duy nhất trên M (ngay cả khi B có nhiều lần commit), bất kể bạn có sử dụng một phép gộp đơn giản hay --squash. Những gì --squash sẽ làm là loại bỏ tham chiếu đến B làm cha mẹ. Một hình ảnh tốt là ở đây: syntevo.com/smartgithg/howtos.html?page=workflows.merge - jpeskin
@jpeskin Đó không phải là những gì tôi nhìn thấy. Tôi vừa làm một bài kiểm tra nhanh để xác minh. Tạo một thư mục với một tệp văn bản, init một repo mới, add tập tin và commit. Thanh toán chi nhánh tính năng mới (checkout -b feature.) Thay đổi tệp văn bản, cam kết và lặp lại để có hai cam kết mới trên nhánh tính năng. Sau đó checkout master và merge feature. Trong log, Tôi thấy cam kết ban đầu của tôi về chủ, tiếp theo là hai được sáp nhập từ tính năng. nếu bạn merge --squash feature, tính năng được sáp nhập vào master nhưng không cam kết, do đó, cam kết mới chỉ về chủ sẽ là một trong những bạn làm cho mình. - spaaarky21
@ spaaarky21 Có vẻ như cả hai chúng tôi đều nửa bên phải. Khi có thể kết hợp nhanh về phía trước (như trong ví dụ của bạn), git sẽ mặc định bao gồm tất cả các cam kết trong nhánh tính năng B (hoặc như bạn đề xuất, bạn có thể sử dụng --squash để kết hợp thành một cam kết đơn). Nhưng trong trường hợp có hai nhánh M và B khác nhau mà bạn đang hợp nhất, git sẽ không bao gồm tất cả các cam kết riêng lẻ từ chi nhánh B nếu sáp nhập vào M (dù bạn có sử dụng --squash) hay không. - jpeskin
Tại sao là "(bạn không quan tâm đến việc duy trì tất cả các cam kết tạm thời)" sang một bên vẫn còn trong câu trả lời này? Nó không có ý nghĩa trong '09 và nó không có ý nghĩa bây giờ. Ngoài ra, chắc chắn bạn sẽ chỉ muốn rebase nếu một nhà phát triển khác thực hiện thay đổi liên quan mà bạn cần - nếu họ thực hiện các thay đổi không liên quan, chi nhánh tính năng của bạn sẽ hợp nhất dễ dàng mà không có xung đột và lịch sử của bạn sẽ được duy trì. - Mark Booth


Nó đơn giản, với rebase bạn nói để sử dụng một chi nhánh như mới căn cứ cho công việc của bạn.

Nếu bạn có ví dụ như một chi nhánh master và bạn tạo chi nhánh để triển khai tính năng mới, giả sử bạn đặt tên cho nó cool-feature, tất nhiên là nhánh chính là cơ sở cho tính năng mới của bạn.

Bây giờ tại một thời điểm nhất định bạn muốn thêm tính năng mới mà bạn đã triển khai trong master chi nhánh. Bạn chỉ có thể chuyển sang master và hợp nhất cool-feature chi nhánh:

$git checkout master
$git merge cool-feature

nhưng theo cách này, một commit giả mới sẽ được thêm vào, nếu bạn muốn tránh spaghetti-history bạn có thể rebase:

$git checkout cool-feature
$git rebase master

và sau đó hợp nhất nó trong master:

$git checkout master
$git merge cool-feature

Lần này, vì nhánh chủ đề có cùng các cam kết của master cộng với các commit với tính năng mới, việc hợp nhất sẽ chỉ là một tiến nhanh.


289
2018-02-05 06:28



but this way a new dummy commit is added, if you want to avoid spaghetti-history - Làm thế nào là nó xấu? - アレックス
Ngoài ra, lá cờ --no-ff của hợp nhất là rất rất hữu ích. - Aldo 'xoen' Giambelluca
@ ア レ ッ ク ス là người dùng Sean Schofield đặt nó trong một bình luận: "Rebase cũng là tốt đẹp bởi vì một khi u cuối cùng kết hợp các công cụ ur trở lại vào master (đó là tầm thường như đã mô tả) bạn có nó ngồi ở" đầu "của lịch sử cam kết ur. được viết nhưng hợp nhất vài tuần sau đó, bạn không muốn chỉ hợp nhất chúng vào chủ bởi vì chúng bị "nhồi" vào đường chính trong lịch sử. Cá nhân tôi thích có thể thực hiện đăng nhập git và thấy rằng tính năng gần đây ở đầu "". Lưu ý ngày cam kết được giữ nguyên - rebase không thay đổi thông tin đó. " - Adrien Be
Tôi nghĩ rằng nó mang lặp lại ở đây - hãy nhớ rằng tất cả các điều khoản này (merge, rebase, fast-forward, vv) đang đề cập đến các thao tác cụ thể của đồ thị theo chu kỳ. Họ trở nên dễ dàng hơn để lý luận về mô hình tinh thần đó trong đầu. - Roy Tinker
@Aldo Không có gì "sạch" hoặc "gọn gàng" về lịch sử bị xóa. Nó thường bẩn thỉu và IMHO khủng khiếp vì bạn không biết điều gì thực sự xảy ra. Lịch sử Git "sạch" nhất là lịch sử thực sự xảy ra. :) - Marnen Laibow-Koser


Để bổ sung câu trả lời của riêng tôi đề cập bởi TSamper,

  • một rebase thường là một ý tưởng hay để làm trước khi hợp nhất, bởi vì ý tưởng là bạn tích hợp vào nhánh của bạn Y công việc của chi nhánh B mà bạn sẽ hợp nhất.
    Nhưng một lần nữa, trước khi hợp nhất, bạn giải quyết mọi xung đột của bạn nhánh (ví dụ: "rebase", như trong "replay công việc của tôi trong chi nhánh của tôi bắt đầu từ một điểm gần đây từ chi nhánh B)
    Nếu được thực hiện đúng, việc hợp nhất tiếp theo từ chi nhánh của bạn với chi nhánh B có thể tua đi nhanh.

  • hợp nhất tác động trực tiếp đến nhánh đích B, có nghĩa là sự hợp nhất tốt hơn là tầm thường, nếu không thì nhánh đó B có thể mất nhiều thời gian để quay lại trạng thái ổn định (thời gian để bạn giải quyết tất cả các xung đột)


điểm sáp nhập sau khi rebase?

Trong trường hợp tôi mô tả, tôi rebase B vào nhánh của tôi, chỉ để có cơ hội phát lại tác phẩm của tôi từ một điểm gần đây hơn từ B, nhưng trong khi ở lại chi nhánh của tôi.
Trong trường hợp này, vẫn cần phải hợp nhất để đưa tác phẩm "được phát lại" của tôi lên B.

Kịch bản khác (được mô tả trong Git Ready ví dụ), là mang công việc của bạn trực tiếp vào B thông qua một rebase (mà bảo tồn tất cả các cam kết tốt đẹp của bạn, hoặc thậm chí cung cấp cho bạn cơ hội để sắp xếp lại chúng thông qua một rebase tương tác).
Trong trường hợp đó (nơi bạn rebase khi đang ở trong nhánh B), bạn đã đúng: không cần hợp nhất nữa:

Một cây git mặc định khi chúng ta chưa hợp nhất hoặc rebased

rebase1 

chúng tôi nhận được bằng cách rebasing:

rebase3

Kịch bản thứ hai đó là tất cả về: làm thế nào để tôi có được tính năng mới trở lại làm chủ.

Quan điểm của tôi, bằng cách mô tả kịch bản rebase đầu tiên, là nhắc nhở mọi người rằng một rebase cũng có thể được sử dụng như một bước sơ bộ cho điều đó (đó là "lấy lại tính năng mới vào master").
Bạn có thể sử dụng rebase để lần đầu tiên mang lại master "in" branch-feature mới: rebase sẽ phát lại các commit mới của feature HEAD master, nhưng vẫn còn trong nhánh mới của tính năng, hãy di chuyển điểm bắt đầu chi nhánh của bạn một cách hiệu quả từ một master cũ cam kết HEAD-master.
Điều đó cho phép bạn giải quyết mọi xung đột trong của bạn nhánh (nghĩa là, trong sự cô lập, trong khi cho phép chủ nhân tiếp tục phát triển song song nếu giai đoạn giải quyết mâu thuẫn của bạn mất quá nhiều thời gian).
Sau đó, bạn có thể chuyển sang chế độ chính và hợp nhất new-feature (hoặc rebase new-feature trên master nếu bạn muốn giữ lại các cam kết được thực hiện trong new-feature chi nhánh).

Vì thế:

  • "rebase vs. merge" có thể được xem là hai cách để nhập một tác phẩm trên, ví dụ: master.
  • Nhưng "rebase after merge" có thể là một luồng công việc hợp lệ để giải quyết xung đột đầu tiên trong sự cô lập, sau đó mang lại công việc của bạn.

254
2018-04-29 20:44



hợp nhất sau khi rebase là một bước tiến nhanh chóng mà không cần phải giải quyết xung đột. - obecalp
@obelcap: Thật vậy, đây là loại ý tưởng: bạn lấy tất cả xung đột vấn đề trong của bạn môi trường (rebase master trong nhánh mới của bạn), và sau đó là master, merge new-feature: 1 pico-second (tua nhanh) nếu master không có diễn biến - VonC
Rebase cũng tốt bởi vì một khi bạn cuối cùng kết hợp các công cụ của bạn trở lại thành master (đó là tầm thường như đã mô tả), bạn có nó ngồi ở "top" của lịch sử commit của bạn. Trên các dự án lớn hơn, nơi các tính năng có thể được viết nhưng được hợp nhất vài tuần sau đó, bạn không muốn chỉ hợp nhất chúng vào chủ bởi vì chúng bị "nhồi" vào đường chính trong lịch sử. Cá nhân tôi thích có thể làm git đăng nhập và thấy rằng tính năng gần đây ngay tại "đầu trang". Lưu ý ngày cam kết được giữ nguyên - rebase không thay đổi thông tin đó. - Sean Schofield
@ Joe: về tinh thần, bạn đang nói "phát lại bất kỳ thay đổi nào của tôi (được thực hiện trong sự cô lập trong nhánh riêng của tôi) trên đầu nhánh đó, nhưng để lại cho tôi trong nhánh riêng của tôi khi rebase được thực hiện". Đó là một cơ hội tốt để làm sạch lịch sử địa phương, tránh "cam kết điểm kiểm soát", chia đôi và chia nhỏ kết quả không chính xác. Xem "Luồng công việc Git": sandofsky.com/blog/git-workflow.html - VonC
@scoarescoare chìa khóa là để xem cách bạn thay đổi cục bộ tương thích trên đầu trang của nhánh thượng lưu mới nhất. Nếu một trong những cam kết của bạn giới thiệu một cuộc xung đột, bạn sẽ thấy nó ngay lập tức. Hợp nhất chỉ giới thiệu một cam kết (sáp nhập), có thể gây ra nhiều xung đột mà không có cách dễ dàng để xem cái nào, giữa các cam kết cục bộ của bạn, đã thêm xung đột. Vì vậy, ngoài lịch sử sạch hơn, bạn sẽ có được cái nhìn chính xác hơn về những thay đổi bạn giới thiệu, cam kết bằng cam kết (phát lại bởi rebase), trái ngược với tất cả các những thay đổi được giới thiệu bởi nhánh thượng nguồn (được gộp vào một lần hợp nhất). - VonC


Rất nhiều câu trả lời ở đây nói rằng việc hợp nhất biến tất cả các cam kết của bạn thành một, và do đó đề nghị sử dụng rebase để bảo toàn các cam kết của bạn. Điều này là không chính xác. Và một ý tưởng tồi nếu bạn đã đẩy các cam kết của mình.

Hợp nhất không không phải xóa bỏ các cam kết của bạn. Hợp nhất bảo tồn lịch sử! (chỉ cần nhìn vào gitk) Rebase viết lại lịch sử, đó là một điều xấu sau khi bạn bị đẩy nó.

Sử dụng hợp nhất - không rebase bất cứ khi nào bạn đã đẩy.

Đây là Linus '(tác giả của git) nhận lấy nó. Đó là một đọc thực sự tốt. Hoặc bạn có thể đọc phiên bản của riêng tôi về cùng một ý tưởng bên dưới.

Rebasing một nhánh trên master:

  • cung cấp ý tưởng không chính xác về cách các cam kết được tạo
  • ô nhiễm chủ với một loạt các cam kết trung gian có thể không được kiểm tra tốt
  • thực sự có thể giới thiệu các đoạn ngắt xây dựng trên các cam kết trung gian này vì những thay đổi đã được thực hiện để làm chủ giữa thời điểm nhánh chủ đề ban đầu được tạo và khi nó được rebased.
  • làm cho việc tìm kiếm những nơi tốt trong tổng thể để kiểm tra khó khăn.
  • Làm cho dấu thời gian trên các cam kết không phù hợp với thứ tự thời gian của chúng trong cây. Vì vậy, bạn sẽ thấy rằng cam kết A precedes cam kết B trong master, nhưng cam kết B là tác giả đầu tiên. (Gì?!)
  • Tạo ra nhiều mâu thuẫn hơn bởi vì các cam kết riêng lẻ trong nhánh chủ đề có thể liên quan đến xung đột hợp nhất và phải được giải quyết riêng lẻ (tiếp tục nằm trong lịch sử về những gì đã xảy ra trong mỗi lần commit).
  • là viết lại lịch sử. Nếu nhánh bị rebased bị đẩy đi bất cứ đâu (chia sẻ với bất kỳ ai khác ngoài bản thân bạn) thì bạn đã làm hỏng mọi người khác có nhánh đó kể từ khi bạn viết lại lịch sử.

Ngược lại, hợp nhất một nhánh chủ đề thành chủ:

  • bảo tồn lịch sử của nơi các nhánh chủ đề được tạo ra, bao gồm bất kỳ sự hợp nhất nào từ tổng thể tới nhánh chủ đề để giúp giữ cho nó hiện tại. Bạn thực sự có được ý tưởng chính xác về mã mà nhà phát triển đang làm việc khi họ xây dựng.
  • master là một nhánh được tạo thành chủ yếu là các phép hợp nhất, và mỗi commit hợp nhất thường là 'các điểm tốt' trong lịch sử an toàn để kiểm tra vì đó là nơi nhánh chủ đề đã sẵn sàng để được tích hợp.
  • tất cả các cam kết cá nhân của nhánh chủ đề đều được giữ nguyên, bao gồm thực tế là chúng nằm trong một nhánh chủ đề, vì vậy việc cô lập những thay đổi đó là tự nhiên và bạn có thể đi sâu vào những nơi cần thiết.
  • các xung đột hợp nhất chỉ phải được giải quyết một lần (tại điểm hợp nhất) để các thay đổi cam kết trung gian được thực hiện trong nhánh chủ đề không cần phải được giải quyết độc lập.
  • có thể được thực hiện nhiều lần suôn sẻ. Nếu bạn tích hợp nhánh chủ đề của bạn để làm chủ định kỳ, mọi người có thể tiếp tục xây dựng trên nhánh chủ đề và nó có thể tiếp tục được hợp nhất một cách độc lập.

153
2018-02-03 22:17



Ngoài ra, git merge có tùy chọn "--no-ff" (không chuyển tiếp nhanh) cho phép bạn hoàn nguyên tất cả các thay đổi được giới thiệu bởi một hợp nhất định thực sự dễ dàng. - Tiago
Chỉ cần làm cho nó rõ ràng hơn: Bạn tham khảo tình hình 'bất cứ khi nào bạn đã đẩy' - điều này nên được in đậm. Liên kết đến bài đăng Linus rất tuyệt, btw., Làm rõ nó. - honzajde
@AndrewArnott "Hầu hết các nhánh chủ đề sẽ có thể hợp nhất mà không có xung đột vào các nhánh mục tiêu của họ" Làm thế nào điều đó có thể xảy ra khi 20 dev đang làm việc trên 30 chi nhánh? Sẽ có sự hợp nhất trong khi bạn đang làm việc trên máy của bạn - vì vậy tất nhiên bạn phải cập nhật chi nhánh chủ đề từ mục tiêu trước khi tạo PR ... không? - ProblemsOfSumit
Không thường, @Sumit. Git có thể hợp nhất một trong hai hướng tốt ngay cả khi các thay đổi đã được thực hiện cho một trong hai hoặc cả hai nhánh. Chỉ khi cùng một dòng mã (hoặc rất gần) được sửa đổi trên hai nhánh bạn sẽ nhận được xung đột. Nếu điều đó xảy ra thường xuyên trên bất kỳ đội nào, nhóm nghiên cứu nên suy nghĩ lại cách họ phân phối công việc kể từ khi giải quyết xung đột là thuế và làm chậm chúng. - Andrew Arnott
Liên kết đã chết, vui lòng cập nhật - Warpzit


TL; DR

Nếu bạn có bất kỳ nghi ngờ, sử dụng hợp nhất.

Câu trả lời ngắn

Sự khác biệt duy nhất giữa rebase và merge là:

  • Cấu trúc cây kết quả của lịch sử (thường chỉ đáng chú ý khi nhìn vào một đồ thị cam kết) là khác nhau (một sẽ có các nhánh, cái kia sẽ không).
  • Hợp nhất chung sẽ tạo thêm một cam kết (ví dụ: nút trong cây).
  • Hợp nhất và rebase sẽ xử lý xung đột khác nhau. Rebase sẽ trình bày xung đột một cam kết tại một thời điểm hợp nhất sẽ trình bày tất cả cùng một lúc.

Câu trả lời ngắn gọn là chọn rebase hoặc merge dựa trên những gì bạn muốn lịch sử của bạn trông giống như.

Câu trả lời dài

Có một vài yếu tố bạn nên cân nhắc khi lựa chọn hoạt động nào để sử dụng.

Chi nhánh bạn có đang nhận được những thay đổi từ việc chia sẻ với các nhà phát triển khác bên ngoài nhóm của bạn (ví dụ: nguồn mở, công khai) không?

Nếu vậy, đừng rebase. Rebase phá hủy nhánh và các nhà phát triển đó sẽ có các kho bị hỏng / không phù hợp trừ khi họ sử dụng git pull --rebase. Đây là một cách hay để làm cho các nhà phát triển khác nhanh chóng thất vọng.

Đội ngũ phát triển của bạn có kỹ năng như thế nào?

Rebase là một hoạt động phá hoại. Điều đó có nghĩa, nếu bạn không áp dụng nó một cách chính xác, bạn có thể mất công việc đã cam kết và / hoặc phá vỡ sự nhất quán của các kho của nhà phát triển khác.

Tôi đã làm việc trên các đội nơi mà các nhà phát triển tất cả đến từ một thời điểm khi các công ty có thể đủ khả năng dành riêng cho nhân viên để đối phó với phân nhánh và sáp nhập. Những nhà phát triển không biết nhiều về Git và không muốn biết nhiều. Trong các đội này, tôi sẽ không có nguy cơ đề xuất rebasing vì bất kỳ lý do gì.

Bản thân chi nhánh có đại diện cho thông tin hữu ích không

Một số nhóm sử dụng mô hình chi nhánh cho mỗi chi nhánh, trong đó mỗi chi nhánh đại diện cho một đối tượng địa lý (hoặc bugfix, hoặc tính năng phụ, vv) Trong mô hình này chi nhánh giúp xác định các tập hợp các cam kết liên quan. Ví dụ, người ta có thể nhanh chóng hoàn nguyên một tính năng bằng cách hoàn nguyên hợp nhất của nhánh đó (để công bằng, đây là một hoạt động hiếm). Hoặc khác biệt một tính năng bằng cách so sánh hai nhánh (phổ biến hơn). Rebase sẽ phá hủy chi nhánh và điều này sẽ không đơn giản.

Tôi cũng đã làm việc trên các nhóm đã sử dụng mô hình chi nhánh cho mỗi nhà phát triển (tất cả chúng tôi đã có). Trong trường hợp này bản thân chi nhánh không truyền tải bất kỳ thông tin bổ sung nào (cam kết đã có tác giả). Sẽ không có hại gì khi rebasing.

Bạn có muốn hoàn nguyên hợp nhất vì bất kỳ lý do nào không?

Hoàn nguyên (như trong hoàn tác) một rebase là đáng kể khó khăn và / hoặc không thể (nếu rebase có xung đột) so với hoàn nguyên hợp nhất. Nếu bạn nghĩ rằng có một cơ hội bạn sẽ muốn hoàn nguyên sau đó sử dụng hợp nhất.

Bạn có làm việc theo nhóm không? Nếu vậy, bạn có sẵn sàng để có một cách tiếp cận tất cả hoặc không có gì trên chi nhánh này?

Hoạt động rebase cần phải được kéo với một tương ứng git pull --rebase. Nếu bạn đang làm việc một mình, bạn có thể nhớ những gì bạn nên sử dụng vào thời điểm thích hợp. Nếu bạn đang làm việc theo nhóm, điều này sẽ rất khó điều phối. Đây là lý do tại sao hầu hết các luồng công việc rebase đề xuất sử dụng rebase cho tất cả các lần hợp nhất (và git pull --rebase cho tất cả các kéo).

Thần thoại thông thường

Hợp nhất phá hủy lịch sử (cam kết squashes)

Giả sử bạn có hợp nhất sau:

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

Một số người sẽ nói rằng việc hợp nhất "phá hủy" lịch sử cam kết bởi vì nếu bạn nhìn vào nhật ký chỉ có nhánh chính (A - D), bạn sẽ bỏ lỡ các thông điệp cam kết quan trọng có trong B và C.

Nếu điều này đúng, chúng tôi sẽ không có những câu hỏi như thế này. Về cơ bản, bạn sẽ thấy B và C trừ khi bạn yêu cầu không thấy chúng một cách rõ ràng (sử dụng --first-parent). Điều này là rất dễ dàng để thử cho chính mình.

Rebase cho phép hợp nhất an toàn hơn / đơn giản hơn

Hai phương pháp tiếp cận hợp nhất một cách khác nhau, nhưng nó không phải là rõ ràng rằng một trong những luôn luôn là tốt hơn so với khác và nó có thể phụ thuộc vào công việc phát triển. Ví dụ: nếu nhà phát triển có xu hướng cam kết thường xuyên (ví dụ: có thể họ cam kết hai lần một ngày khi họ chuyển từ công việc sang nhà) thì có thể có rất nhiều cam kết cho một nhánh cụ thể. Nhiều người trong số những cam kết có thể không nhìn bất cứ điều gì giống như sản phẩm cuối cùng (tôi có xu hướng refactor phương pháp tiếp cận của tôi một lần hoặc hai lần cho mỗi tính năng). Nếu ai đó đang làm việc trên một khu vực có liên quan của mã và họ đã cố gắng để rebase thay đổi của tôi nó có thể là một hoạt động khá tẻ nhạt.

Rebase là cool / sexier / chuyên nghiệp hơn

Nếu bạn thích bí danh rm đến rm -rf để "tiết kiệm thời gian" sau đó có thể rebase là dành cho bạn.

My Two Cent

Tôi luôn nghĩ rằng một ngày nào đó tôi sẽ bắt gặp một kịch bản mà git rebase là công cụ tuyệt vời giúp giải quyết vấn đề. Giống như tôi nghĩ rằng tôi sẽ đi qua một kịch bản mà git reflog là một công cụ tuyệt vời mà giải quyết vấn đề của tôi. Tôi đã làm việc với git hơn năm năm nay. Nó đã không xảy ra.

Lịch sử lộn xộn chưa bao giờ thực sự là vấn đề đối với tôi. Tôi không bao giờ chỉ đọc lịch sử cam kết của mình như một cuốn tiểu thuyết thú vị. Phần lớn thời gian tôi cần một lịch sử, tôi sẽ sử dụng git blame hoặc git bisect anyways. Trong trường hợp đó, việc commit hợp nhất thực sự hữu ích với tôi bởi vì nếu việc hợp nhất đưa ra vấn đề là thông tin có ý nghĩa với tôi.

Cập nhật (4/2017)

Tôi cảm thấy có nghĩa vụ phải đề cập đến rằng tôi đã cá nhân làm mềm bằng cách sử dụng rebase mặc dù lời khuyên chung của tôi vẫn đứng. Gần đây tôi đã tương tác rất nhiều với Vật liệu góc 2 dự án. Họ đã sử dụng rebase để giữ một lịch sử cam kết rất sạch sẽ. Điều này đã cho phép tôi dễ dàng nhìn thấy những gì cam kết cố định một lỗi nhất định và liệu cam kết đó có được đưa vào bản phát hành hay không. Nó phục vụ như là một ví dụ tuyệt vời của việc sử dụng rebase một cách chính xác.


118
2018-04-13 02:16



Điều này cần nhiều upvotes hơn. - Legato
Nên là câu trả lời được xác thực. - Mik378
Đây chắc chắn là câu trả lời tốt nhất. Đặc biệt với nhận xét đã được làm sáng tỏ trong bản cập nhật mới nhất. Nó giúp giữ cho lịch sử git sạch sẽ và rõ ràng, nhưng sử dụng nó một cách an toàn. - zquintana
Tôi chủ yếu thích câu trả lời này. Nhưng: Rebase không tạo nên một lịch sử "sạch". Nó làm cho một lịch sử tuyến tính hơn, nhưng đó không phải là điều tương tự ở tất cả, vì ai biết bây giờ nhiều "bụi bẩn" mỗi cam kết đang ẩn? Lịch sử Git sạch nhất, rõ ràng nhất là lịch sử giữ cho chi nhánh và cam kết tính toàn vẹn. - Marnen Laibow-Koser
Đây là một câu trả lời thực sự tốt - Derek


Hợp nhất nghĩa là: Tạo một cam kết mới kết hợp các thay đổi của tôi vào đích.

Rebase có nghĩa là: Tạo một loạt các cam kết hoàn toàn mới, sử dụng bộ cam kết hiện tại của tôi làm gợi ý. Nói cách khác, tính toán những thay đổi của tôi sẽ trông như thế nào nếu tôi bắt đầu làm cho chúng từ điểm mà tôi đang làm lại. Sau khi rebase, do đó, bạn có thể cần phải kiểm tra lại các thay đổi của bạn và trong quá trình rebase, bạn có thể sẽ có một vài xung đột.

Cho điều này, tại sao bạn sẽ rebase? Chỉ để giữ cho lịch sử phát triển rõ ràng. Giả sử bạn đang làm việc trên tính năng X và khi bạn hoàn tất, bạn hợp nhất các thay đổi của mình. Đích đến giờ đây sẽ có một cam kết sẽ nói điều gì đó dọc theo dòng "Tính năng được thêm X". Bây giờ, thay vì hợp nhất, nếu bạn rebased và sau đó sáp nhập, lịch sử phát triển đích sẽ chứa tất cả các cam kết riêng lẻ trong một tiến trình logic duy nhất. Điều này làm cho việc xem lại các thay đổi sau này trở nên dễ dàng hơn nhiều. Hãy tưởng tượng bạn sẽ thấy khó khăn như thế nào khi xem lại lịch sử phát triển nếu 50 nhà phát triển đã hợp nhất các tính năng khác nhau mọi lúc.

Điều đó nói rằng, nếu bạn đã đẩy chi nhánh bạn đang làm việc trên thượng nguồn, bạn không nên rebase, nhưng hợp nhất thay vào đó. Đối với các nhánh chưa được đẩy lên thượng lưu, rebase, test và merge.

Một lần khác bạn có thể muốn rebase là khi bạn muốn loại bỏ các cam kết từ chi nhánh của bạn trước khi đẩy ngược dòng. Ví dụ: Cam kết giới thiệu một số mã gỡ lỗi sớm và các cam kết khác về việc mã hóa sạch sẽ. Cách duy nhất để làm điều này là bằng cách thực hiện một rebase tương tác: git rebase -i <branch/commit/tag>

CẬP NHẬT: Bạn cũng muốn sử dụng rebase khi bạn đang sử dụng Git để giao diện với một hệ thống điều khiển phiên bản không hỗ trợ lịch sử phi tuyến tính (ví dụ như subversion). Khi sử dụng cầu git-svn, điều rất quan trọng là những thay đổi bạn hợp nhất trở lại vào phiên bản lật đổ là một danh sách các thay đổi liên tục trên những thay đổi gần đây nhất trong thân cây. Chỉ có hai cách để làm điều đó: (1) Tạo lại các thay đổi theo cách thủ công và (2) Sử dụng lệnh rebase, nhanh hơn rất nhiều.

UPDATE2: Một cách bổ sung để nghĩ về việc rebase là nó cho phép một loại ánh xạ từ kiểu phát triển của bạn đến kiểu được chấp nhận trong kho lưu trữ mà bạn cam kết. Hãy nói rằng bạn thích cam kết trong những khối nhỏ, nhỏ. Bạn có một cam kết sửa chữa lỗi đánh máy, một cam kết để loại bỏ mã không sử dụng, v.v. Khi bạn đã hoàn thành những gì bạn cần làm, bạn có một loạt các cam kết. Bây giờ giả sử kho lưu trữ mà bạn cam kết khuyến khích các cam kết lớn, vì vậy đối với công việc bạn đang làm, người ta sẽ mong đợi một hoặc có thể hai cam kết. Làm thế nào để bạn lấy chuỗi cam kết của bạn và nén chúng vào những gì được mong đợi? Bạn sẽ sử dụng một rebase tương tác và squash cam kết nhỏ của bạn thành các khối nhỏ hơn. Điều này cũng đúng nếu ngược lại là cần thiết - nếu phong cách của bạn là một vài cam kết lớn, nhưng repo yêu cầu các chuỗi dài các cam kết nhỏ. Bạn sẽ sử dụng một rebase để làm điều đó là tốt. Nếu bạn đã hợp nhất thay vào đó, bây giờ bạn đã ghép kiểu cam kết của mình vào kho lưu trữ chính. Nếu có rất nhiều nhà phát triển, bạn có thể tưởng tượng sẽ khó khăn như thế nào để theo dõi lịch sử với nhiều kiểu cam kết khác nhau sau một thời gian.

UPDATE3: Does one still need to merge after a successful rebase? Có, bạn làm. Lý do là một rebase về cơ bản liên quan đến một "chuyển dịch" của các cam kết. Như tôi đã nói ở trên, các cam kết này được tính toán, nhưng nếu bạn có 14 cam kết từ điểm phân nhánh, thì giả sử không có gì sai với rebase của bạn, bạn sẽ có 14 cam kết trước (của điểm bạn đang rebasing lên) sau việc rebase được thực hiện. Bạn đã có một chi nhánh trước khi một rebase. Bạn sẽ có một chi nhánh có cùng độ dài sau đó. Bạn vẫn cần hợp nhất trước khi xuất bản các thay đổi của mình. Nói cách khác, rebase nhiều lần như bạn muốn (một lần nữa, chỉ khi bạn không đẩy các thay đổi của bạn ngược dòng). Chỉ hợp nhất sau khi bạn rebase.


65
2018-02-05 06:47



Việc hợp nhất với chương trình chính có thể dẫn đến tiến nhanh. Trong một chi nhánh tính năng có thể có một số cam kết, có lỗi nhỏ hoặc thậm chí không biên dịch. Nếu bạn chỉ thử nghiệm đơn vị trong một chi nhánh tính năng, một số lỗi trong tích hợp phiếu của tôi thông qua. Trước khi hợp nhất với chủ, kiểm tra tích hợp được yêu cầu và có thể hiển thị một số lỗi. Nếu chúng được cố định, tính năng này có thể được tích hợp. Vì bạn không muốn cam kết mã lỗi để làm chủ, việc rebase có vẻ cần thiết để ngăn chặn tất cả các commit-fast-forward. - mbx
@mbx git merge hỗ trợ --no-ff tùy chọn mà buộc nó để thực hiện một cam kết hợp nhất. - g.rocket


trước khi hợp nhất / rebase:

A <- B <- C    [master]
^
 \
  D <- E       [branch]

sau git merge master:

A <- B <- C
^         ^
 \         \
  D <- E <- F

sau git rebase master:

A <- B <- C <- D' <- E'

(A, B, C, D, E và F là các cam kết)

ví dụ này và nhiều thông tin minh họa tốt hơn về git có thể tìm thấy ở đây: http://excess.org/article/2008/07/ogre-git-tutorial/


54
2018-06-07 06:19





Câu này nhận được câu:

Nói chung cách để có được tốt nhất của cả hai thế giới là để rebase địa phương   những thay đổi bạn đã thực hiện nhưng chưa chia sẻ trước khi bạn đẩy chúng vào   để làm sạch câu chuyện của bạn, nhưng không bao giờ rebase bất cứ điều gì bạn đã đẩy   một vài nơi.

Nguồn: http://www.git-scm.com/book/en/v2/Git-Branching-Rebasing#Rebase-vs.-Merge


25
2018-01-05 13:50





Trong khi hợp nhất chắc chắn là cách dễ nhất và phổ biến nhất để tích hợp các thay đổi, nó không phải là cách duy nhất: Rebase là một phương tiện tích hợp thay thế.

Hiểu sự hợp nhất tốt hơn một chút

Khi Git thực hiện hợp nhất, nó sẽ tìm kiếm ba commit:

  • (1) Cam kết chung của tổ tiên Nếu bạn theo dõi lịch sử của hai nhánh trong một dự án, chúng luôn có ít nhất một cam kết chung: tại thời điểm này, cả hai nhánh có cùng nội dung và sau đó tiến hóa khác nhau.
  • (2) + (3) Điểm cuối của mỗi nhánh Mục tiêu của sự tích hợp là kết hợp các trạng thái hiện tại của hai nhánh. Do đó, các bản sửa đổi mới nhất tương ứng của họ có sự quan tâm đặc biệt. Kết hợp ba cam kết này sẽ dẫn đến sự tích hợp mà chúng ta đang hướng tới.

Cam kết nhanh hoặc chuyển tiếp

Trong những trường hợp rất đơn giản, một trong hai chi nhánh không có bất kỳ cam kết mới nào kể từ khi phân nhánh xảy ra - cam kết mới nhất của nó vẫn là tổ tiên chung.

enter image description here

Trong trường hợp này, việc thực hiện tích hợp đã chết đơn giản: Git chỉ có thể thêm tất cả các cam kết của nhánh khác lên trên cùng của cam kết tổ tiên chung. Trong Git, hình thức tích hợp đơn giản nhất này được gọi là hợp nhất "chuyển tiếp nhanh". Cả hai nhánh này đều chia sẻ cùng một lịch sử.

enter image description here

Tuy nhiên, trong nhiều trường hợp, cả hai nhánh đều di chuyển về phía trước. enter image description here

Để thực hiện tích hợp, Git sẽ phải tạo một cam kết mới có chứa sự khác biệt giữa chúng - cam kết hợp nhất.

enter image description here

Cam kết và cam kết của con người

Thông thường, một cam kết được tạo ra một cách cẩn thận bởi một con người. Đó là một đơn vị có ý nghĩa chỉ bao bọc các thay đổi có liên quan và chú thích chúng bằng một nhận xét.

Một cam kết hợp nhất có một chút khác biệt: thay vì được tạo bởi một nhà phát triển, nó được tạo tự động bởi Git. Và thay vì gói một tập hợp các thay đổi có liên quan, mục đích của nó là kết nối hai nhánh, giống như một nút thắt. Nếu bạn muốn hiểu một hoạt động hợp nhất sau này, bạn cần phải xem lịch sử của cả hai nhánh và biểu đồ cam kết tương ứng.

Tích hợp với Rebase

Một số người thích đi mà không có cam kết hợp nhất tự động như vậy. Thay vào đó, họ muốn lịch sử của dự án trông như thể nó đã phát triển thành một đường thẳng, đơn giản. Không có dấu hiệu nào cho thấy nó đã được chia thành nhiều nhánh tại một thời điểm nào đó.

enter image description here

Chúng ta hãy đi từng bước một. Kịch bản này giống như trong các ví dụ trước: chúng ta muốn tích hợp các thay đổi từ nhánh B vào nhánh A, nhưng bây giờ bằng cách sử dụng rebase.

enter image description here

Chúng tôi sẽ làm điều này trong ba bước

  1. git rebase branch-A // syncs the history with branch-A
  2. git checkout branch-A // change the current branch to branch-A
  3. git merge branch-B // merge/take the changes from branch-B to branch-A 

Đầu tiên, Git sẽ "hoàn tác" tất cả các cam kết trên nhánh A đã xảy ra sau khi các dòng bắt đầu phân nhánh (sau khi các tổ tiên chung cam kết). Tuy nhiên, tất nhiên, nó sẽ không loại bỏ chúng: thay vào đó bạn có thể nghĩ về những cam kết đó là "được cứu tạm thời".

enter image description here

Tiếp theo, nó áp dụng các cam kết từ nhánh B mà chúng ta muốn tích hợp. Tại thời điểm này, cả hai nhánh trông giống hệt nhau.

enter image description here

Trong bước cuối cùng, các cam kết mới trên nhánh A hiện được áp dụng lại - nhưng trên một vị trí mới, trên đầu các cam kết tích hợp từ nhánh B (chúng được dựa trên lại). Kết quả có vẻ như sự phát triển đã xảy ra theo một đường thẳng. Thay vì một cam kết hợp nhất có chứa tất cả các thay đổi kết hợp, cấu trúc cam kết ban đầu được giữ nguyên.

enter image description here

Cuối cùng bạn nhận được một nhánh sạch branch-A không có cam kết không mong muốn và được tạo tự động.

Chú thích: Lấy từ tuyệt vời bài đăng bởi git-tower. Các bất lợi của rebase cũng là một bài đọc tốt trong cùng một bài đăng.


23
2017-10-12 11:52





Cuốn sách chuyên nghiệp git như một lời giải thích thực sự tốt về trang rebasing.

Về cơ bản một hợp nhất sẽ có 2 cam kết và kết hợp chúng.

Một rebase sẽ đi đến tổ tiên chung trên 2 và từng bước áp dụng các thay đổi trên đầu trang của mỗi khác. Điều này làm cho lịch sử 'sạch hơn' và tuyến tính hơn.

Nhưng khi bạn rebase bạn từ bỏ các cam kết trước đó và tạo ra những cái mới. Vì vậy, bạn không bao giờ nên rebase một repo được công khai. Những người khác làm việc trên repo sẽ ghét bạn.

Vì lý do đó một mình tôi gần như độc quyền hợp nhất. 99% thời gian các chi nhánh của tôi không khác nhau nhiều, vì vậy nếu có xung đột thì chỉ có một hoặc hai nơi.


15
2017-12-20 21:41



Hợp nhất không kết hợp các cam kết - đó sẽ là viết lại lịch sử. Rebase làm điều đó. - kellyfj


Câu trả lời này được định hướng rộng rãi xung quanh Luồng Git. Các bảng đã được tạo ra với Bộ tạo bảng ASCIIvà cây lịch sử với lệnh tuyệt vời này (có răng cưa như git lg):

git log --graph --abbrev-commit --decorate --date=format:'%Y-%m-%d %H:%M:%S' --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%ad%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n''          %C(white)%s%C(reset) %C(dim white)- %an%C(reset)'

Các bảng theo thứ tự thời gian đảo ngược để phù hợp hơn với cây lịch sử. Xem thêm sự khác biệt giữa git merge và git merge --no-ff đầu tiên (bạn thường muốn sử dụng git merge --no-ff vì nó làm cho lịch sử của bạn nhìn gần hơn với thực tế):

git merge

Lệnh:

Time          Branch "develop"             Branch "features/foo"
------- ------------------------------ -------------------------------
15:04   git merge features/foo
15:03                                  git commit -m "Third commit"
15:02                                  git commit -m "Second commit"
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Kết quả:

* 142a74a - YYYY-MM-DD 15:03:00 (XX minutes ago) (HEAD -> develop, features/foo)
|           Third commit - Christophe
* 00d848c - YYYY-MM-DD 15:02:00 (XX minutes ago)
|           Second commit - Christophe
* 298e9c5 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git merge --no-ff

Lệnh:

Time           Branch "develop"              Branch "features/foo"
------- -------------------------------- -------------------------------
15:04   git merge --no-ff features/foo
15:03                                    git commit -m "Third commit"
15:02                                    git commit -m "Second commit"
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Kết quả:

*   1140d8c - YYYY-MM-DD 15:04:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/foo' - Christophe
| * 69f4a7a - YYYY-MM-DD 15:03:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 2973183 - YYYY-MM-DD 15:02:00 (XX minutes ago)
|/            Second commit - Christophe
* c173472 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git merge so với git rebase

Điểm đầu tiên: luôn hợp nhất các tính năng vào phát triển, không bao giờ rebase phát triển từ các tính năng. Đây là hệ quả của Quy tắc vàng của Rebasing:

Quy tắc vàng của git rebase không bao giờ sử dụng nó trên công cộngcác chi nhánh.

Nói cách khác:

Không bao giờ rebase bất cứ điều gì bạn đã đẩy một nơi nào đó.

Cá nhân tôi sẽ thêm: trừ khi đó là chi nhánh tính năng VÀ bạn và nhóm của bạn nhận thức được hậu quả.

Vậy câu hỏi về git merge so với git rebase chỉ áp dụng cho các chi nhánh tính năng (trong các ví dụ sau, --no-ff luôn được sử dụng khi hợp nhất). Lưu ý rằng vì tôi không chắc có một giải pháp nào tốt hơn (một cuộc tranh luận tồn tại), Tôi sẽ chỉ cung cấp cách cả hai lệnh hoạt động. Trong trường hợp của tôi, tôi thích sử dụng git rebase vì nó tạo ra một cây lịch sử đẹp hơn :)

Giữa các chi nhánh tính năng

git merge

Lệnh:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- --------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git merge --no-ff features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Kết quả:

*   c0a3b89 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 37e933e - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| *   eb5e657 - YYYY-MM-DD 15:07:00 (XX minutes ago)
| |\            Merge branch 'features/foo' into features/bar - Christophe
| * | 2e4086f - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | |           Fifth commit - Christophe
| * | 31e3a60 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | |           Fourth commit - Christophe
* | |   98b439f - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ \            Merge branch 'features/foo' - Christophe
| |/ /
|/| /
| |/
| * 6579c9c - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 3f41d96 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* 14edc68 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git rebase

Lệnh:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git rebase features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Kết quả:

*   7a99663 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 708347a - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * 949ae73 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * 108b4c7 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| |           Fourth commit - Christophe
* |   189de99 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| |/
| * 26835a0 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * a61dd08 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* ae6f5fc - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

Từ develop đến một chi nhánh tính năng

git merge

Lệnh:

Time           Branch “develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09                                                                    git commit -m “Sixth commit"
15:08                                                                    git merge --no-ff development
15:07   git merge --no-ff features/foo
15:06                                                                    git commit -m “Fifth commit"
15:05                                                                    git commit -m “Fourth commit"
15:04                                    git commit -m “Third commit"
15:03                                    git commit -m “Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m “First commit"

Kết quả:

*   9e6311a - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 3ce9128 - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| *   d0cd244 - YYYY-MM-DD 15:08:00 (XX minutes ago)
| |\            Merge branch 'develop' into features/bar - Christophe
| |/
|/|
* |   5bd5f70 - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| * | 4ef3853 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | |           Third commit - Christophe
| * | 3227253 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ /            Second commit - Christophe
| * b5543a2 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * 5e84b79 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/            Fourth commit - Christophe
* 2da6d8d - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git rebase

Lệnh:

Time           Branch “develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09                                                                    git commit -m “Sixth commit"
15:08                                                                    git rebase development
15:07   git merge --no-ff features/foo
15:06                                                                    git commit -m “Fifth commit"
15:05                                                                    git commit -m “Fourth commit"
15:04                                    git commit -m “Third commit"
15:03                                    git commit -m “Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m “First commit"

Kết quả:

*   b0f6752 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 621ad5b - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * 9cb1a16 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * b8ddd19 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/            Fourth commit - Christophe
*   856433e - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\            Merge branch 'features/foo' - Christophe
| * 694ac81 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 5fd94d3 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* d01d589 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

Ghi chú bên

git cherry-pick

Khi bạn chỉ cần một cam kết cụ thể, git cherry-pick là một giải pháp tốt đẹp ( -x tùy chọn nối thêm một dòng có nội dung "(anh đào được hái từ cam kết ...)"đối với nội dung thư cam kết ban đầu, vì vậy thường là ý tưởng hay khi sử dụng nó - git log <commit_sha1> để xem nó):

Lệnh:

Time           Branch “develop"              Branch "features/foo"                Branch "features/bar"           
------- -------------------------------- ------------------------------- -----------------------------------------
15:10   git merge --no-ff features/bar                                                                            
15:09   git merge --no-ff features/foo                                                                            
15:08                                                                    git commit -m “Sixth commit"             
15:07                                                                    git cherry-pick -x <second_commit_sha1>  
15:06                                                                    git commit -m “Fifth commit"             
15:05                                                                    git commit -m “Fourth commit"            
15:04                                    git commit -m “Third commit"                                             
15:03                                    git commit -m “Second commit"                                            
15:02   git checkout -b features/bar                                                                              
15:01   git checkout -b features/foo                                                                              
15:00   git commit -m “First commit"                                                                              

Kết quả:

*   50839cd - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 0cda99f - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * f7d6c47 - YYYY-MM-DD 15:03:00 (XX minutes ago)
| |           Second commit - Christophe
| * dd7d05a - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * d0d759b - YYYY-MM-DD 15:05:00 (XX minutes ago)
| |           Fourth commit - Christophe
* |   1a397c5 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| |/
|/|
| * 0600a72 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * f4c127a - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* 0cf894c - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git pull --rebase

Không chắc tôi có thể giải thích nó tốt hơn Derek Gourlay... Về cơ bản, sử dụng git pull --rebase thay vì git pull :) Điều gì còn thiếu trong bài báo, là bạn có thể kích hoạt nó theo mặc định:

git config --global pull.rebase true

git rerere

Một lần nữa, giải thích độc đáo đây. Nhưng đặt đơn giản, nếu bạn bật nó, bạn sẽ không phải giải quyết cùng một cuộc xung đột nhiều lần nữa.


14
2018-02-10 17:26