2009-05-20 29 views
130

Chúng tôi hiện đang sử dụng subversion trên một codebase tương đối lớn. Mỗi bản phát hành có nhánh riêng và các bản sửa lỗi được thực hiện đối với thân cây và được di chuyển vào các nhánh phát hành bằng cách sử dụng svnmerge.pyMercurial: Các chi nhánh được đặt tên so với nhiều kho lưu trữ

Tôi tin rằng thời gian đã đến để kiểm soát nguồn tốt hơn và tôi đã chơi đùa với Mercurial trong một thời gian .

Dường như có hai trường quản lý cấu trúc phát hành như vậy bằng Mercurial. Mỗi bản phát hành đều có repo riêng và các bản sửa lỗi được thực hiện đối với nhánh phát hành và được đẩy tới nhánh chính (và bất kỳ nhánh phát hành mới nào khác.) HOẶC sử dụng các chi nhánh được đặt tên trong một kho lưu trữ duy nhất (hoặc nhiều bản sao phù hợp.)

Trong cả hai trường hợp, có vẻ như tôi có thể đang sử dụng thứ gì đó như cấy ghép cho các thay đổi cherrypick để đưa vào các nhánh phát hành.

Tôi yêu cầu bạn; những thành tích tương đối của mỗi cách tiếp cận là gì?

Trả lời

129

Sự khác biệt lớn nhất là cách các tên chi nhánh được ghi lại trong lịch sử. Với các chi nhánh được đặt tên, tên chi nhánh là nhúng vào mỗi changeset và do đó sẽ trở thành một phần không thể thay đổi của lịch sử. Với các bản sao sẽ có không có bản ghi nào vĩnh viễn về nơi một thay đổi cụ thể xuất phát từ đó.

Điều này có nghĩa là các bản sao rất tuyệt vời cho các thử nghiệm nhanh nơi bạn không muốn ghi tên chi nhánh và các nhánh được đặt tên tốt cho các nhánh dài hạn ("1.x", "2.x" và tương tự).

Cũng lưu ý rằng một kho lưu trữ đơn lẻ có thể dễ dàng chứa nhiều nhánh trọng lượng nhẹ trong Mercurial. Các nhánh trong kho như vậy có thể được đánh dấu để bạn có thể dễ dàng tìm lại chúng. Hãy nói rằng bạn đã nhân bản kho công ty khi nó trông như thế này:

[a] --- [b] 

Bạn hack đi và làm cho [x][y]:

[a] --- [b] --- [x] --- [y] 

Mean trong khi một người nào đó đặt [c][d] vào kho, vì vậy khi bạn kéo bạn có biểu đồ lịch sử như sau:

 
      [x] --- [y] 
     /
[a] --- [b] --- [c] --- [d] 

Ở đây có hai người đứng đầu kho lưu trữ ngle. Bản sao làm việc của bạn sẽ luôn luôn phản ánh một changeset duy nhất, cái gọi là bản sao thay đổi cha mẹ làm việc. Kiểm tra điều này với:

% hg parents 

Giả sử báo cáo là [y]. Bạn có thể nhìn thấy người đứng đầu với

% hg heads 

và điều này sẽ báo cáo [y][d].Nếu bạn muốn cập nhật kho của bạn để kiểm tra một sạch của [d], sau đó chỉ cần làm (thay [d] với số sửa đổi cho [d]):

% hg update --clean [d] 

Sau đó bạn sẽ thấy rằng hg parents báo cáo [d]. Điều này có nghĩa là cam kết tiếp theo của bạn sẽ có [d] làm phụ huynh. do đó bạn có thể sửa chữa một lỗi bạn đã nhận thấy trong các chi nhánh chính và tạo changeset [e]:

 
      [x] --- [y] 
     /
[a] --- [b] --- [c] --- [d] --- [e] 

Để đẩy changeset [e] chỉ, bạn cần làm

% hg push -r [e] 

nơi [e] là băm changeset. Theo mặc định, hg push sẽ chỉ so sánh các vị trí lưu trữ và thấy rằng [x], [y][e] bị thiếu, nhưng bạn có thể không muốn chia sẻ [x][y].

Nếu bugfix cũng ảnh hưởng đến bạn, bạn muốn kết hợp nó với chi nhánh tính năng của bạn:

% hg update [y] 
% hg merge 

Điều đó sẽ rời khỏi đồ thị dưới kho của bạn trông như thế này:

 
      [x] --- [y] ----------- [z] 
     /     /
[a] --- [b] --- [c] --- [d] --- [e] 

nơi [z] là việc hợp nhất giữa [y][e]. Bạn cũng có thể đã chọn để ném chi nhánh đi:

% hg strip [x] 

điểm chính của tôi của câu chuyện này là thế này: một bản sao duy nhất có thể dễ dàng thể hiện một số bài nhạc của sự phát triển. Điều này luôn đúng với "đồng bằng hg" mà không sử dụng bất kỳ tiện ích mở rộng nào. Tuy nhiên, bookmarks extension là một trợ giúp tuyệt vời. Nó sẽ cho phép bạn gán tên (bookmark) cho changesets. Trong trường hợp trên, bạn sẽ muốn có một dấu trang trên đầu phát triển của bạn và một trên đầu thượng nguồn. Dấu trang có thể được đẩy và kéo với Mercurial 1.6 và đã trở thành một tính năng tích hợp trong Mercurial 1.8.

Nếu bạn đã chọn để làm cho hai máy nhái, tạo bản sao phát triển của bạn sẽ trông như thế này sau khi thực hiện [x][y]:

[a] --- [b] --- [x] --- [y] 

Và bản sao ngược dòng của bạn sẽ chứa:

[a] --- [b] --- [c] --- [d] 

Bây giờ bạn chú ý đến lỗi và sửa nó. Ở đây bạn không cần phải hg update vì bản sao ngược dòng đã sẵn sàng để sử dụng.Bạn cam kết và tạo [e]:

[a] --- [b] --- [c] --- [d] --- [e] 

Để bao gồm các bugfix trong bản sao phát triển của bạn, bạn kéo nó ở đó:

 
[a] --- [b] --- [x] --- [y] 
      \ 
      [c] --- [d] --- [e] 

và hợp nhất:

 
[a] --- [b] --- [x] --- [y] --- [z] 
      \     /
      [c] --- [d] --- [e] 

Đồ thị sức trông khác nhau, nhưng nó có cùng cấu trúc và kết quả cuối cùng là như nhau. Sử dụng các bản sao bạn phải làm một cuốn sách nhỏ ít tinh thần hơn.

Chi nhánh được đặt tên không thực sự đi vào ảnh ở đây vì chúng khá tùy chọn. Mercurial chính nó đã được phát triển bằng cách sử dụng hai dòng vô tính trong nhiều năm trước khi chúng tôi chuyển sang sử dụng các chi nhánh được đặt tên. Chúng tôi duy trì một chi nhánh được gọi là 'ổn định' ngoài chi nhánh 'mặc định' và thực hiện các bản phát hành của chúng tôi dựa trên nhánh 'ổn định'. Xem trang standard branching trong wiki để biết mô tả về quy trình làm việc được đề xuất.

+1

nếu changeset đến từ một người dùng khác, mà có thể đã được ghi lại, do đó, sử dụng nhân bản là không có gì xấu. Khi đẩy một tính năng mới nó thường không thú vị để biết bạn đã làm điều đó từ một repo riêng biệt. Ngoài ra còn có một phần mở rộng localbranch, cung cấp cho bạn một chi nhánh địa phương duy nhất. Hữu ích khi nhân bản repo được kết hợp với chi phí cao (thời gian/không gian). –

+2

tham chiếu đến: 'nhái rất tuyệt vời cho thí nghiệm nhanh' - Không, chúng không phải! Điều gì sẽ xảy ra nếu bạn có một vài tập tin trong repo? Nhân bản sẽ mất nhiều thời gian (bất cứ lúc nào trên 1 phút) trong khi nhánh chỉ chuyển đổi một lúc (<1 giây). Vẫn sử dụng các chi nhánh được đặt tên sẽ gây ô nhiễm thay đổi. Nó không phải là một kết thúc chết? Hoặc tôi đang thiếu một cái gì đó? – seler

+0

OK seler; Âm thanh như một sửa đổi đối số ban đầu của anh ấy; Bản sao là tốt nơi mà các bản sao của nhiều bản sao hoàn chỉnh không quan trọng đối với bạn, hoặc khi bạn có thể sử dụng các liên kết/liên kết của hg để giảm thiểu chi phí của các bản sao làm việc cục bộ riêng biệt trên mỗi nhánh. –

5

Sự khác biệt chính, theo như tôi biết, là điều bạn đã nêu: có tên phân nhánh nằm trong một kho lưu trữ duy nhất. Các nhánh được đặt tên có mọi thứ tiện dụng ở một nơi. Repos riêng biệt nhỏ hơn và dễ di chuyển xung quanh. Lý do có hai trường tư tưởng về điều này là không có người chiến thắng rõ ràng. Bất kỳ đối số của bên nào có ý nghĩa nhất đối với bạn có lẽ là đối số bạn nên đi cùng, vì có thể môi trường của chúng tương tự nhất với môi trường của bạn.

29

Tôi nghĩ bạn muốn toàn bộ lịch sử trong một repo. Sinh ra một repo ngắn hạn là cho các thí nghiệm ngắn hạn, chứ không phải các sự kiện lớn như bản phát hành.

Một trong những thất vọng của Mercurial là dường như không có cách nào dễ dàng để tạo ra một chi nhánh ngắn ngủi, chơi với nó, từ bỏ nó và thu gom rác. Chi nhánh là mãi mãi. Tôi thông cảm với không bao giờ muốn từ bỏ lịch sử, nhưng các chi nhánh siêu rẻ, dùng một lần là một tính năng git mà tôi thực sự muốn thấy trong hg.

+20

Bạn có thể dễ dàng tạo một chi nhánh như vậy: "cập nhật hg" cho điểm chi nhánh của bạn, chỉnh sửa và "hg commit". Bạn vừa mới tạo một dòng phát triển khác nhau - các cam kết mới sẽ mở rộng nhánh này. Sử dụng "hg clone -r" để loại bỏ nó hoặc loại bỏ nội tuyến bằng "hg strip". Vì vậy, xin đừng thất vọng, hoặc đến danh sách gửi thư của Mercurial với các yêu cầu tính năng của bạn. –

+8

Có vẻ như 'hg strip' là thứ tôi muốn. Tại sao các nhánh yêu cầu tài liệu trực tuyến không thể bị xóa? –

+1

Norman: bạn phải kích hoạt một phần mở rộng như mq hoặc histedit (~ 'git rebase -i') để có được hành vi phá hoại với Mercurial. Sau đó, bạn có thể loại bỏ các nhánh không mong muốn. –

2

Tôi nghĩ rằng đó rõ ràng là một quyết định thực tế tùy thuộc vào tình hình hiện tại, ví dụ: kích thước của một tính năng/thiết kế lại. Tôi nghĩ rằng dĩa thực sự tốt cho những người đóng góp với vai trò không-chưa-committer để tham gia nhóm phát triển bằng cách chứng minh khả năng của họ với chi phí kỹ thuật không thể bỏ qua.

14

Bạn nên làm cả hai.

Bắt đầu với câu trả lời được chấp nhận từ @Norman: Sử dụng một kho lưu trữ với một chi nhánh được đặt tên cho mỗi bản phát hành.

Sau đó, có một bản sao cho mỗi nhánh phát hành để xây dựng và thử nghiệm.

Một lưu ý quan trọng là ngay cả khi bạn sử dụng nhiều kho, bạn nên tránh sử dụng transplant để di chuyển các thay đổi giữa chúng vì 1) nó thay đổi băm và 2) nó có thể giới thiệu các lỗi rất khó phát hiện khi có mâu thuẫn thay đổi giữa changeset bạn cấy ghép và nhánh mục tiêu. Bạn muốn thực hiện việc hợp nhất thông thường thay vào đó (và không có premerge: luôn trực quan kiểm tra việc hợp nhất), điều này sẽ dẫn đến những gì @mg nói ở cuối câu trả lời của mình:

Biểu đồ có thể khác, nhưng nó có cùng một cấu trúc và kết quả cuối cùng là như nhau.

verbosely hơn, nếu bạn sử dụng nhiều kho, các "thân cây" kho (hoặc mặc định, chính, phát triển, bất cứ điều gì) chứa ALL changesets trong ALL kho. Mỗi kho lưu trữ/chi nhánh chỉ đơn giản là một nhánh trong thân cây, tất cả được sáp nhập ngược lại một chiều hoặc ngược lại với thân cây, cho đến khi bạn muốn để lại một bản phát hành cũ phía sau. Do đó, sự khác biệt thực sự duy nhất giữa repo chính và repo đơn trong lược đồ chi nhánh được đặt tên đơn giản là các nhánh có được đặt tên hay không.

Điều đó sẽ làm cho nó rõ ràng lý do tại sao tôi nói "bắt đầu với một repo". Đó là repo duy nhất là nơi duy nhất bạn sẽ cần phải tìm bất kỳ changeset trong bất kỳ phiên bản. Bạn có thể thêm các thay đổi trên các nhánh phát hành để phiên bản. Đó là khái niệm rõ ràng và đơn giản, và làm cho hệ thống quản trị đơn giản, vì nó là điều duy nhất mà hoàn toàn phải có sẵn và phục hồi tất cả các thời gian.

Nhưng sau đó bạn vẫn cần duy trì một bản sao cho mỗi nhánh/bản phát hành mà bạn cần xây dựng và thử nghiệm. Nó tầm thường như bạn có thể hg clone <main repo>#<branch> <branch repo>, và sau đó hg pull trong repo chi nhánh sẽ chỉ kéo changesets mới trên nhánh đó (cộng với changesets tổ tiên trên các nhánh trước đó đã được sáp nhập).

Thiết lập này thích hợp nhất với kernel linux cam kết mô hình puller đơn (không nó cảm thấy tốt để hành động như Chúa Linus. Tại công ty chúng tôi chúng ta gọi là vai trò tích hợp), như repo chính là chỉ điều mà các nhà phát triển cần phải sao chép và puller cần phải kéo vào. Việc duy trì các repo chi nhánh hoàn toàn là để quản lý phát hành và có thể hoàn toàn tự động. Các nhà phát triển không bao giờ cần phải kéo từ/đẩy đến repos chi nhánh.


Đây là ví dụ về @ mg được thiết lập lại này. Điểm bắt đầu:

[a] - [b] 

Tạo một chi nhánh được đặt tên cho phiên bản phát hành, nói "1.0", khi bạn được phát hành alpha. Cam kết sửa lỗi trên đó:

[a] - [b] ------------------ [m1] 
     \    /
      (1.0) - [x] - [y] 

(1.0) không phải là một thay đổi thực sự vì chi nhánh được đặt tên không tồn tại cho đến khi bạn cam kết. (Bạn có thể thực hiện một cam kết tầm thường, chẳng hạn như thêm thẻ, để đảm bảo các nhánh được đặt tên được tạo đúng cách.)

Hợp nhất [m1] là chìa khóa để thiết lập này. Không giống như một kho lưu trữ dành cho nhà phát triển nơi có thể có số lượng đầu không giới hạn, bạn KHÔNG muốn có nhiều đầu trong repo chính của bạn (ngoại trừ chi nhánh phát hành cũ, đã chết như đã đề cập trước đó). Vì vậy, bất cứ khi nào bạn có các thay đổi mới trên các nhánh phát hành, bạn phải hợp nhất chúng trở lại nhánh mặc định (hoặc một nhánh phát hành sau) ngay lập tức. Điều này đảm bảo rằng mọi sửa lỗi trong một bản phát hành cũng được bao gồm trong tất cả các bản phát hành sau này.

Trong sự phát triển trong khi đó trên chi nhánh mặc định vẫn tiếp tục hướng tới việc phát hành tiếp theo:

  ------- [c] - [d] 
     /
[a] - [b] ------------------ [m1] 
     \    /
      (1.0) - [x] - [y] 

Và như thường lệ, bạn cần phải hợp nhất hai người đứng đầu về chi nhánh mặc định:

  ------- [c] - [d] ------- 
     /      \ 
[a] - [b] ------------------ [m1] - [m2] 
     \    /
      (1.0) - [x] - [y] 

Và đây là 1.0 bản sao nhánh:

[a] - [b] - (1.0) - [x] - [y] 

Bây giờ, bạn nên tập thêm nhánh phát hành tiếp theo . Nếu đó là 2.0 thì nó chắc chắn sẽ phân nhánh mặc định. Nếu là 1.1, bạn có thể chọn phân nhánh 1.0 hoặc mặc định. Bất kể, bất kỳ changeset mới trên 1.0 nên được sáp nhập đầu tiên vào nhánh tiếp theo, sau đó để mặc định. Điều này có thể được thực hiện tự động nếu không có xung đột, kết quả chỉ là một hợp nhất trống.


Tôi hy vọng ví dụ này làm cho các điểm trước đó của tôi rõ ràng.Tóm lại, những lợi thế của phương pháp này là:

  1. Kho chứa độc quyền chứa toàn bộ thay đổi và lịch sử phiên bản.
  2. Quản lý phát hành rõ ràng và đơn giản.
  3. Quy trình làm việc rõ ràng và đơn giản cho nhà phát triển và người tích hợp.
  4. Tạo điều kiện lặp lại quy trình làm việc (đánh giá mã) và tự động hóa (hợp nhất tự động trống).

CẬP NHẬT hg tự does this: các main repo chứa mặc định, ngành ổn định, và stable repo là chi nhánh bản sao ổn định. Tuy nhiên, nó không sử dụng branched version, vì các thẻ phiên bản dọc nhánh ổn định là đủ tốt cho các mục đích quản lý phát hành của nó.

0

Tôi thực sự khuyên bạn không nên sử dụng các nhánh được đặt tên cho các phiên bản. Đó thực sự là những gì các thẻ cho. Các nhánh được đặt tên có nghĩa là cho các chuyển hướng lâu dài, như chi nhánh stable.

Vậy tại sao không chỉ sử dụng thẻ? Một ví dụ cơ bản:

  • Phát triển xảy ra trên một nhánh đơn
  • Bất cứ khi nào một thông cáo được tạo ra, bạn đánh dấu nó cho phù hợp
  • Phát triển chỉ tiếp tục trên từ đó
  • Nếu bạn có một số lỗi để sửa chữa (hoặc bất cứ điều gì) trong một thông cáo nào đó, bạn chỉ cần cập nhật để nó thẻ, thực hiện thay đổi và cam kết

Điều đó sẽ tạo ra một, người đứng đầu giấu tên mới trên nhánh default, aka. một nhánh ẩn danh, hoàn toàn tốt trong hg. Sau đó, bạn có thể nhập bất kỳ điểm nào, lỗi sửa lỗi sẽ trở lại trong bản phát triển chính. Không cần cho các chi nhánh được đặt tên.

+0

Điều này phụ thuộc rất nhiều vào quy trình của bạn. Một ứng dụng web, ví dụ, hoạt động tốt với một hệ thống phân cấp chi nhánh ổn định/thử nghiệm/devel. Khi xây dựng phần mềm máy tính để bàn, chúng tôi thường có một nhánh phát triển (mặc định) cũng như từ một đến ba (!) Các chi nhánh khác nhau trong việc bảo trì. Thật khó để dự đoán khi chúng ta có thể cần phải xem lại một chi nhánh, và có một sự thanh lịch nhất định về việc có một nhánh theo dõi một phiên bản major.minor. –

Các vấn đề liên quan