Tại sao git thực hiện hợp nhất chuyển tiếp nhanh theo mặc định?


641

Đến từ đồng bóng, tôi sử dụng các chi nhánh để tổ chức các tính năng. Đương nhiên, tôi cũng muốn thấy dòng chảy công việc này trong lịch sử của mình.

Tôi bắt đầu dự án mới của mình bằng git và hoàn thành tính năng đầu tiên của mình. Khi hợp nhất tính năng, tôi nhận ra git sử dụng chuyển tiếp nhanh, tức là nó áp dụng các thay đổi của tôi trực tiếp vào nhánh chính nếu có thể và quên đi chi nhánh của tôi.

Vì vậy, để suy nghĩ về tương lai: Tôi là người duy nhất làm việc trong dự án này. Nếu tôi sử dụng phương pháp mặc định của git (hợp nhất chuyển tiếp nhanh), lịch sử của tôi sẽ dẫn đến một nhánh chính khổng lồ. Không ai biết tôi đã sử dụng một nhánh riêng cho mỗi tính năng, vì cuối cùng tôi sẽ chỉ có nhánh chủ khổng lồ đó. Trông nó không chuyên nghiệp?

Vì lý do này, tôi không muốn hợp nhất chuyển tiếp nhanh và không thể hiểu tại sao nó là mặc định. Có gì hay về nó?


38
Lưu ý: xem thêm sandofsky.com/blog/git-workflow.html và tránh ' no-ff' với "điểm kiểm tra cam kết" phá vỡ hoặc đổ lỗi.
VonC

15
Bạn có hối hận khi sử dụng git trong dự án một người đàn ông không?
HaveAGuess

27
Tuyệt đối không! Trong thư mục công việc của tôi, tôi có 7 dự án một người mà tôi sử dụng git. Hãy để tôi nói lại rằng: Tôi đã bắt đầu nhiều dự án kể từ khi tôi hỏi câu hỏi này và tất cả chúng đều được phiên bản thông qua git. Theo như tôi biết, chỉ có git và mercurial hỗ trợ phiên bản địa phương, điều này rất cần thiết đối với tôi kể từ khi tôi quen với nó. Thật dễ dàng để thiết lập nó và bạn luôn có toàn bộ lịch sử bên mình. Trong các dự án nhóm thậm chí còn tốt hơn, vì bạn có thể cam kết mà không can thiệp bất cứ ai với mã tiến trình công việc của bạn. Ngoài ra, tôi sử dụng github để chia sẻ một số dự án của mình (ví dụ: micro-optparse) trong đó git là một yêu cầu.
Florian Pilz

7
@Cawas đúng, -no-ffhiếm khi là một ý tưởng hay, nhưng vẫn có thể giúp giữ lịch sử nội bộ tính năng trong khi chỉ ghi một cam kết trên nhánh chính. Điều đó có ý nghĩa đối với lịch sử tính năng lâu dài, khi bạn hợp nhất theo thời gian tiến trình của nó trên nhánh chính.
VonC

12
Nhân tiện, với câu hỏi của bạn về "Không phải [lịch sử nhánh tuyến tính] trông không chuyên nghiệp sao?". Không có gì không chuyên nghiệp về việc sử dụng một hệ thống mã nguồn với mặc định của nó. Đây không phải là về tính chuyên nghiệp. Đây là về việc xác định triết lý phân nhánh mà bạn đăng ký bạn. Ví dụ: @VonC liên kết với bài viết của Sandofsky nơi anh vô địch bằng cách sử dụng chuyển tiếp nhanh như một cách tiếp cận ưu việt. Không đúng hay sai, chỉ là những triết lý khác nhau cho những bối cảnh khác nhau.
Marplesoft

Câu trả lời:


718

Hợp nhất chuyển tiếp nhanh có ý nghĩa đối với các nhánh có thời gian ngắn, nhưng trong một lịch sử phức tạp hơn , việc sáp nhập không chuyển tiếp nhanh có thể làm cho lịch sử dễ hiểu hơn và giúp dễ dàng hoàn nguyên một nhóm các cam kết.

Cảnh báo : Không chuyển tiếp nhanh cũng có tác dụng phụ tiềm ẩn. Vui lòng xem lại https://sandofsky.com/blog/git-workflow.html , tránh 'no-ff' với "điểm kiểm tra cam kết" phá vỡ hoặc đổ lỗi, và xem xét cẩn thận liệu đây có phải là cách tiếp cận mặc định của bạn không master.

văn bản thay thế
(Từ nvie.com , Vincent Driessen , đăng " Mô hình phân nhánh Git thành công ")

Kết hợp một tính năng đã hoàn thành để phát triển

Các tính năng đã hoàn thành có thể được hợp nhất vào nhánh phát triển để thêm chúng vào bản phát hành sắp tới:

$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff myfeature
Updating ea1b82a..05e9557
(Summary of changes)
$ git branch -d myfeature
Deleted branch myfeature (was 05e9557).
$ git push origin develop

Các --no-fflá cờ gây ra việc hợp nhất để luôn luôn tạo ra một mới cam kết đối tượng, ngay cả khi việc hợp nhất có thể được thực hiện với một nhanh về phía trước. Điều này tránh mất thông tin về sự tồn tại lịch sử của một nhánh tính năng và các nhóm cùng nhau tất cả các cam kết cùng nhau thêm tính năng này.

Jakub Narębski cũng đề cập đến các cấu hìnhmerge.ff :

Theo mặc định, Git không tạo ra một cam kết hợp nhất bổ sung khi hợp nhất một cam kết là hậu duệ của cam kết hiện tại. Thay vào đó, đầu của chi nhánh hiện tại được chuyển tiếp nhanh.
Khi được đặt thành false, biến này báo cho Git tạo một cam kết hợp nhất bổ sung trong trường hợp đó (tương đương với việc đưa ra --no-fftùy chọn từ dòng lệnh).
Khi được đặt thành ' only', chỉ những phép hợp nhất chuyển tiếp nhanh như vậy mới được phép (tương đương với việc đưa ra --ff-onlytùy chọn từ dòng lệnh).


Chuyển tiếp nhanh là mặc định vì:

  • các nhánh sống ngắn rất dễ tạo và sử dụng trong Git
  • các nhánh sống ngắn thường cô lập nhiều cam kết có thể được tổ chức lại một cách tự do trong nhánh đó
  • những cam kết đó thực sự là một phần của nhánh chính: một khi được tổ chức lại, nhánh chính được chuyển tiếp nhanh để bao gồm chúng.

Nhưng nếu bạn dự đoán một quy trình công việc lặp lại trên một nhánh chủ đề / tính năng (nghĩa là tôi hợp nhất, sau đó tôi quay lại nhánh tính năng này và thêm một số cam kết nữa), thì chỉ hữu ích khi chỉ hợp nhất trong nhánh chính, thay vì tất cả các cam kết trung gian của nhánh tính năng.

Trong trường hợp này, bạn có thể kết thúc cài đặt loại tệp cấu hình này :

[branch "master"]
# This is the list of cmdline options that should be added to git-merge 
# when I merge commits into the master branch.

# The option --no-commit instructs git not to commit the merge
# by default. This allows me to do some final adjustment to the commit log
# message before it gets commited. I often use this to add extra info to
# the merge message or rewrite my local branch names in the commit message
# to branch names that are more understandable to the casual reader of the git log.

# Option --no-ff instructs git to always record a merge commit, even if
# the branch being merged into can be fast-forwarded. This is often the
# case when you create a short-lived topic branch which tracks master, do
# some changes on the topic branch and then merge the changes into the
# master which remained unchanged while you were doing your work on the
# topic branch. In this case the master branch can be fast-forwarded (that
# is the tip of the master branch can be updated to point to the tip of
# the topic branch) and this is what git does by default. With --no-ff
# option set, git creates a real merge commit which records the fact that
# another branch was merged. I find this easier to understand and read in
# the log.

mergeoptions = --no-commit --no-ff

OP thêm vào trong các ý kiến:

Tôi thấy một số ý nghĩa trong việc chuyển tiếp nhanh cho các nhánh [tồn tại ngắn], nhưng biến nó thành hành động mặc định có nghĩa là git giả định rằng bạn ... thường có các nhánh [tồn tại ngắn]. Hợp lý?

Câu trả lời của Jefromi:

Tôi nghĩ rằng tuổi thọ của các chi nhánh rất khác nhau từ người dùng này đến người dùng khác. Tuy nhiên, trong số những người dùng có kinh nghiệm, có lẽ có xu hướng có nhiều chi nhánh ngắn hơn.

Đối với tôi, một nhánh tồn tại trong thời gian ngắn là một nhánh mà tôi tạo ra để làm cho một thao tác nhất định trở nên dễ dàng hơn (đánh lại, có khả năng hoặc vá nhanh và thử nghiệm), sau đó xóa ngay lập tức sau khi tôi hoàn thành.
Điều đó có nghĩa là nó có thể được hấp thụ vào nhánh chủ đề mà nó rẽ nhánh và nhánh chủ đề sẽ được hợp nhất thành một nhánh. Không ai cần biết những gì tôi đã làm trong nội bộ để tạo ra một loạt các cam kết thực hiện tính năng đã cho.

Tổng quát hơn, tôi thêm:

nó thực sự phụ thuộc vào quy trình phát triển của bạn :

  • nếu nó là tuyến tính, một nhánh có ý nghĩa.
  • Nếu bạn cần cách ly các tính năng và làm việc với chúng trong một thời gian dài và liên tục hợp nhất chúng, một số nhánh có ý nghĩa.

Xem " Khi nào bạn nên phân nhánh? "

Trên thực tế, khi bạn xem xét mô hình nhánh Mercurial, nó nằm ở một nhánh cốt lõi trên mỗi kho lưu trữ (mặc dù bạn có thể tạo các đầu ẩn danh, dấu trang và thậm chí các nhánh được đặt tên )
Xem "Git và Mercurial - So sánh và tương phản" .

Mercurial, theo mặc định, sử dụng các bộ mã nhẹ vô danh, theo thuật ngữ của nó được gọi là "người đứng đầu".
Git sử dụng các nhánh có tên nhẹ, với ánh xạ tiêm để ánh xạ tên của các nhánh trong kho lưu trữ từ xa thành tên của các nhánh theo dõi từ xa.
Git "buộc" bạn đặt tên cho các nhánh (tốt, ngoại trừ một nhánh không tên duy nhất, đó là một tình huống gọi là " ĐẦU tách rời "), nhưng tôi nghĩ rằng điều này hoạt động tốt hơn với các luồng công việc nặng nhánh như luồng công việc của nhánh chủ đề, nghĩa là nhiều chi nhánh trong một mô hình kho lưu trữ duy nhất.


Ồ nó rất nhanh. ;) Tôi biết về tùy chọn --no-ff, nhưng chỉ tìm hiểu về chuyển tiếp nhanh sau khi tôi làm hỏng tính năng của mình. Tôi thấy một số ý nghĩa trong việc chuyển tiếp nhanh đối với các nhánh sống ngắn, nhưng làm cho nó trở thành hành động mặc định có nghĩa là git giả định rằng bạn thường có các nhánh sống ngắn như vậy. Hợp lý?
Florian Pilz

@Florian: Tôi tin rằng đây là một quan điểm hợp lý của quy trình. Tôi đã thêm một ví dụ về cấu hình sẽ thiết lập theo cách bạn muốn quản lý hợp nhất thành chủ.
VonC

Cảm ơn, tập tin cấu hình sẽ giúp tránh những vấn đề như vậy. :) Chỉ áp dụng cấu hình này cục bộ với "git config Branch.master.mergeoptions '--no-ff'"
Florian Pilz

2
@BehrangSaeedzadeh: rebasing là một chủ đề khác (chưa được đề cập trong trang này): miễn là bạn chưa đẩy featurechi nhánh của mình đến một repo công khai, bạn có thể khởi động lại nó masternhiều lần như bạn muốn. Xem stackoverflow.com/questions/5250817/
Mạnh

4
@BehrangSaeedzadeh: một cuộc nổi loạn tự nó sẽ không tạo ra một tuyến tính lịch sử. Đó là cách bạn tích hợp các thay đổi của featurechi nhánh của bạn trở lại masterđể làm cho lịch sử nói tuyến tính hay không. Một sự hợp nhất nhanh chóng đơn giản sẽ làm cho nó tuyến tính. Điều này có ý nghĩa nếu bạn đã xóa lịch sử của featurechi nhánh đó trước khi hợp nhất chuyển tiếp nhanh, chỉ để lại các cam kết quan trọng, như được đề cập trong stackoverflow.com/questions/7425541/ .
VonC

42

Hãy để tôi mở rộng một chút về câu trả lời rất toàn diện của VonC :


Đầu tiên, nếu tôi nhớ chính xác, thực tế là Git theo mặc định không tạo ra các cam kết hợp nhất trong trường hợp chuyển tiếp nhanh xuất phát từ việc xem xét "kho lưu trữ bằng nhau" một nhánh, trong đó kéo tương hỗ được sử dụng để đồng bộ hai kho lưu trữ đó (a quy trình làm việc bạn có thể tìm thấy như ví dụ đầu tiên trong hầu hết tài liệu của người dùng, bao gồm "Hướng dẫn sử dụng Git" và "Kiểm soát phiên bản theo ví dụ"). Trong trường hợp này, bạn không sử dụng pull để hợp nhất chi nhánh nhận ra đầy đủ, bạn sử dụng nó để theo kịp các công việc khác. Bạn không muốn có sự thật phù du và không quan trọng khi bạn thực hiện đồng bộ hóa được lưu và lưu trữ trong kho lưu trữ, được lưu cho tương lai.

Lưu ý rằng tính hữu ích của các nhánh tính năng và có nhiều nhánh trong kho lưu trữ chỉ xuất hiện muộn hơn, với việc sử dụng rộng rãi hơn các VCS với sự hỗ trợ hợp nhất tốt và thử các quy trình làm việc dựa trên hợp nhất khác nhau. Đó là lý do tại sao Mercurial ban đầu chỉ hỗ trợ một nhánh trên mỗi kho lưu trữ (cộng với các mẹo ẩn danh để theo dõi các nhánh từ xa), như đã thấy trong các phiên bản cũ hơn của "Mercurial: The Definitive Guide".


Thứ hai, khi tuân theo các thực tiễn tốt nhất về sử dụng các nhánh tính năng , cụ thể là các nhánh tính năng đó nên bắt đầu từ phiên bản ổn định (thường là từ phiên bản trước), để có thể chọn và chọn các tính năng để bao gồm bằng cách chọn các nhánh tính năng để hợp nhất, bạn thường không ở trong tình huống chuyển tiếp nhanh ... điều này làm cho vấn đề này được khắc phục. Bạn cần lo lắng về việc tạo một hợp nhất thực sự và không chuyển tiếp nhanh khi hợp nhất một nhánh đầu tiên (giả sử rằng bạn không đặt các thay đổi cam kết đơn trực tiếp trên 'master'); tất cả các sự hợp nhất sau này tất nhiên là trong tình huống không chuyển tiếp nhanh.

HTH


Về các nhánh tính năng từ các bản phát hành ổn định: Điều gì xảy ra nếu một tính năng tôi đang phát triển cho bản phát hành tiếp theo phụ thuộc vào các thay đổi cho một tính năng khác mà tôi đã phát triển và sáp nhập thành chủ? Chắc chắn điều đó có nghĩa là tôi phải tạo nhánh tính năng thứ hai này từ chủ?
dOxxx

1
@dOxxx: Có, có những trường hợp ngoại lệ, ví dụ như một nhánh xây dựng trên nhánh kia (trực tiếp hoặc sau khi hợp nhất trước đó thành chủ).
Jakub Narębski
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.