2012-02-29 40 views
15

Đây là một tình huống khó giải thích, vì vậy hãy chịu đựng tôi. Tôi có một kho Mercurial với 2 chi nhánh chính, mặc địnhdev.Tại sao các Mercurial backouts trong một chi nhánh ảnh hưởng đến các chi nhánh khác?

Công việc thường được thực hiện trong chi nhánh được đặt tên là dev (một chi nhánh tính năng). Có thể có nhiều chi nhánh tính năng cùng một lúc. Khi công việc được hoàn thành trong nhánh đó, nó được hợp nhất trở lại thành dev.

Khi đến lúc chuẩn bị bản phát hành, một chi nhánh khác được đặt tên được tạo ra từ dev (chi nhánh phát hành). Đôi khi cần phải loại trừ toàn bộ các tính năng khỏi bản phát hành. Nếu đúng như vậy, thì bộ thay đổi hợp nhất từ ​​nơi mà chi nhánh tính năng được hợp nhất thành dev được sao lưu khỏi nhánh phát hành mới.

Khi chi nhánh phát hành đã sẵn sàng để được phát hành, nó được hợp nhất thành mặc định (vì vậy mặc định luôn đại diện cho trạng thái mã trong sản xuất). Công việc tiếp tục như bình thường trên chi nhánh dev chi nhánh và tính năng.

Sự cố xảy ra khi đến lúc thực hiện một bản phát hành khác, bao gồm cả tính năng được sao lưu trong bản phát hành trước. Chi nhánh phát hành mới được tạo như bình thường (tắt số dev). Nhánh phát hành mới này hiện chứa tính năng được sao lưu từ nhánh phát hành trước đó (kể từ khi backout được thực hiện trên nhánh phát hành và changeset hợp nhất vẫn nằm trên chi nhánh dev).

Lần này, khi chi nhánh phát hành đã sẵn sàng cho phát hành và được sáp nhập vào mặc định, bất kỳ thay đổi mà đã được sao lưu ra như là kết quả của backout hợp nhất trong ngành phát hành trước đó không được sáp nhập vào mặc định. Tại sao điều này là trường hợp? Vì nhánh phát hành mới chứa tất cả các thay đổi nhánh của tính năng (không có gì đã được sao lưu), tại sao chi nhánh mặc định cũng không nhận được tất cả các thay đổi này?

Nếu tất cả những điều trên là khó thực hiện, đây là ảnh chụp màn hình từ TortoiseHg hiển thị sự cố cơ bản. "Branch1" và "branch2" là các ngành chức năng, "giải phóng" và "release2" là nhánh phát hành:

enter image description here

+0

hg diff -r 9 -r 12 ??? –

Trả lời

25

Tôi tin rằng vấn đề là kết hợp công việc khác so với bạn nghĩ. Bạn viết

Vì nhánh phát hành mới chứa tất cả các thay đổi nhánh của tính năng (không có gì được sao lưu), tại sao nhánh mặc định cũng không nhận được tất cả các thay đổi này?

Khi bạn hợp nhất hai nhánh, không thể nghĩ về nó khi áp dụng tất cả thay đổi từ một chi nhánh lên nhánh khác. Vì vậy, chi nhánh default không "nhận" bất kỳ thay đổi nào từ release2. Tôi biết đây là cách chúng ta thường nghĩ về sự hợp nhất, nhưng nó không chính xác.

Điều gì thực sự xảy ra khi bạn hợp nhất hai changesets như sau:

  1. Mercurial tìm thấy tổ tiên chung cho hai changesets.

  2. Đối với mỗi tệp khác nhau giữa hai thay đổi Mercurial chạy three-way merge algorithm bằng cách sử dụng tệp tổ tiên, tệp trong changeset đầu tiên và tệp trong changeset thứ hai.

Trong trường hợp của bạn, bạn đang sáp nhập phiên bản 11 và 12. Các tổ tiên chung nhỏ nhất là phiên bản 8. Điều này có nghĩa rằng Mercurial sẽ chạy một ba chiều kết hợp giữa các tập tin từ đó sửa đổi:

  • Revision 8: không backout

  • Revision 11: tính năng chi nhánh đã được sao lưu ra

  • Revision 12: không backout

Trong một ba chiều hợp nhất, một sự thay đổi luôn Trumps không thay đổi. Mercurial thấy rằng các tập tin đã được thay đổi giữa 8 và 11 và nó thấy không có thay đổi giữa 8 và 12. Vì vậy, nó sử dụng phiên bản thay đổi từ phiên bản 11 trong hợp nhất. Điều này áp dụng cho bất kỳ thuật toán hợp nhất ba chiều nào. Bảng merge đầy đủ trông như thế này ở đâu old, new ... là những nội dung của hunks phù hợp trong ba tập tin:

ancestor local other -> merge 
old  old old  old (nobody changed the hunk) 
old  old new  new (they changed the hunk) 
old  new old  new (you changed the hunk) 
old  new new  new (hunk was cherry picked onto both branches) 
old  foo bar  <!> (conflict, both changed hunk but differently) 

Tôi sợ rằng một merge changeset shouldn't be backed out ở tất cả vì hành vi hợp đáng ngạc nhiên này. Mercurial 2.0 và sau đó sẽ hủy bỏ và khiếu nại nếu bạn cố gắng backout một hợp nhất.

Nói chung, người ta có thể nói rằng thuật toán hợp nhất ba chiều giả định rằng tất cả thay đổi là tốt. Vì vậy, nếu bạn hợp nhất branch1 thành dev và sau đó hoàn tác việc hợp nhất với một bản sao lưu, sau đó thuật toán hợp nhất sẽ nghĩ rằng trạng thái "tốt hơn" so với trước đây. Điều này có nghĩa là bạn không thể chỉ hợp nhất lại branch1 thành dev sau này để nhận lại các thay đổi được sao lưu.

Điều bạn có thể làm là sử dụng "kết hợp giả" khi bạn nhập vào default. Bạn chỉ cần hợp nhất và luôn giữ những thay đổi từ chi nhánh phát hành bạn đang sáp nhập vào default:

$ hg update default 
$ hg merge release2 --tool internal:other -y 
$ hg revert --all --rev release2 
$ hg commit -m "Release 2 is the new default" 

Điều đó sẽ phụ bước vấn đề và lực lượng default được giống như release2. Điều này giả định rằng hoàn toàn không có thay đổi được thực hiện trên default mà không được sáp nhập vào một chi nhánh phát hành.

Nếu bạn phải có khả năng tạo bản phát hành có các tính năng bị bỏ qua, thì cách "đúng" là không hợp nhất các tính năng đó. Sáp nhập là một cam kết mạnh mẽ: bạn nói với Mercurial rằng changeset hợp nhất bây giờ có tất cả những thứ tốt từ cả tổ tiên của nó. Miễn là Mercurial sẽ không cho phép bạn pick your own base revision when merging, thuật toán kết hợp ba chiều sẽ không cho phép bạn thay đổi ý định về một bản sao lưu.

Tuy nhiên, những gì bạn có thể làm là để phát lại bản sao lưu. Điều này có nghĩa là bạn giới thiệu lại các thay đổi từ chi nhánh tính năng của bạn trên chi nhánh phát hành của bạn. Vì vậy, bạn bắt đầu với một đồ thị như

release: ... o --- o --- m1 --- m2 
         / /
feature-A: ... o --- o /
          /
feature-B: ... o --- o --- o 

Bây giờ bạn quyết định rằng Một đặc điểm là xấu và bạn backout hợp nhất:

release: ... o --- o --- m1 --- m2 --- b1 
         / /
feature-A: ... o --- o /
          /
feature-B: ... o --- o --- o 

Sau đó bạn kết hợp tính năng khác vào chi nhánh phát hành của bạn:

release: ... o --- o --- m1 --- m2 --- b1 --- m3 
         / /   /
feature-A: ... o --- o /   /
          /   /
feature-B: ... o --- o --- o   /
             /
feature-C: ... o --- o --- o --- o --- o 

Nếu bây giờ bạn muốn giới thiệu lại tính năng A, bạn có thể sao lưu b1:

release: ... o --- o --- m1 --- m2 --- b1 --- m3 --- b2 
         / /   /
feature-A: ... o --- o /   /
          /   /
feature-B: ... o --- o --- o   /
             /
feature-C: ... o --- o --- o --- o --- o 

Chúng ta có thể thêm các vùng đồng bằng để đồ thị để hiển thị tốt hơn những gì thay đổi ở đâu và khi:

     +A  +B  -A  +C  --A 
release: ... o --- o --- m1 --- m2 --- b1 --- m3 --- b2 

Sau backout thứ hai này, bạn có thể kết hợp lại với feature-A trong trường hợp changesets mới đã được thêm vào ở đó. Đồ thị bạn đang sáp nhập trông giống như:

release: ... o --- o --- m1 --- m2 --- b1 --- m3 --- b2 
         / /   /
feature-A: ... o -- a1 - a2/   /
          /   /
feature-B: ... o --- o --- o   /
             /
feature-C: ... o --- o --- o --- o --- o 

và bạn hợp nhất a2b2. Tổ tiên chung sẽ là a1. Điều này có nghĩa là những thay đổi duy nhất bạn sẽ cần cân nhắc trong quá trình hợp nhất ba chiều là những thay đổi giữa a1a2a1b2. Tại đây, b2 đã có số lượng lớn các thay đổi "trong" a2 để hợp nhất sẽ nhỏ.

+0

Một cách khác để suy nghĩ về nó là bạn đã lấy một bản sao của một cái gì đó (tổ tiên chung cuối cùng trước khi phân nhánh), và đưa những bản đó cho hai người khác nhau. Bây giờ, một trong những người đó làm một cái gì đó cho bản sao của anh ấy, và khi sau này bạn muốn hòa hợp hai bản sao lại thành một bản, bạn có trường hợp "anh ta làm một cái gì đó cho bản sao của anh ấy" và "người kia không làm gì cả", Mercurial hạnh phúc sẽ thực hiện các thay đổi đã được thực hiện trên một nhánh và áp dụng chúng, bất kể chúng là gì. –

+0

Martin - giải thích tuyệt vời, sẽ học hỏi và rèn luyện trong đá. Những lời khuyên nào bạn có cho những người không may, những người ** phải làm cho phát hành ** với các tính năng bỏ qua (nếu backout là xấu, như chúng ta thấy) –

+0

@LazyBadger: cảm ơn cho bình luận. Tôi đã cố gắng đưa ra một số lời khuyên về việc xử lý điều này. –

0

Câu trả lời của Martin, như thường lệ, về tiền, nhưng tôi chỉ muốn thêm 2p của mình.

Một cách khác để suy nghĩ về điều này là backout không loại bỏ bất cứ điều gì, nó cho biết thêm sự thay đổi ngược lại.

Vì vậy, khi bạn hợp nhất bạn không thực hiện:

Branch after changes <-> Branch before changes => Result with changes 

bạn đang thực hiện:

Branch after changes <-> Branch after changes with removal of changes => Result with changes removed. 

Về cơ bản phiên bản đầu tiên đã được thực hiện tồi tệ. Nó sẽ là tốt hơn để anh đào chọn các tính năng vào bản phát hành, hơn bao gồm tất cả mọi thứ và các tính năng lựa chọn anh đào. Graft có thể giúp bạn ở đây, nhưng tôi đã không cố gắng sử dụng nó trong sự tức giận chưa biết tất cả những cạm bẫy.

+1

"Sẽ tốt hơn nếu bạn chọn các tính năng trong bản phát hành, bao gồm tất cả mọi thứ và tính năng lựa chọn anh đào" - hầu như không thể trong cuộc sống thực với chu kỳ phát hành dài hợp lý –

+0

Cấp - bạn sẽ không chọn cherry mỗi changeset. Đó chắc chắn là một cách chữa cháy điên rồ. Chọn điểm chi nhánh của bạn một cách cẩn thận và không để các tính năng không mong muốn vào bản phát hành của bạn. Cherry chọn sau đó. –

+0

Nếu bản phát hành của tôi có 10-20 tính năng và cùng một số lượng sửa lỗi (cũng hợp nhất theo mặc định) - đó là số thực - việc chọn cherry sẽ trở thành roulette Nga –

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