Hg: Làm thế nào để thực hiện một cuộc nổi loạn như cuộc nổi loạn của git


207

Trong Git tôi có thể làm điều này:

1. Bắt đầu làm việc trên tính năng mới:
$ git co -b newfeature-123 # (một nhánh phát triển tính năng cục bộ)
thực hiện một vài cam kết (M, N, O)

thầy A --- B --- C
                \
newfeature-123 M --- N --- O

2. Kéo các thay đổi mới từ tổng thể ngược dòng:
$ git kéo
(bản cập nhật chính với ff-commits)

thầy A --- B --- C --- D --- E --- F
                \
newfeature-123 M --- N --- O

3. Rebase off master để tính năng mới của tôi 
có thể được phát triển dựa trên những thay đổi ngược dòng mới nhất:
(từ newfeature-123)
$ git rebase master

thầy A --- B --- C --- D --- E --- F
                            \
newfeature-123 M --- N --- O


Tôi muốn biết làm thế nào để làm điều tương tự trong Mercurial và tôi đã lướt web để tìm câu trả lời, nhưng điều tốt nhất tôi có thể tìm thấy là: git rebase - có thể làm điều đó

Liên kết đó cung cấp 2 ví dụ:
1. Tôi sẽ thừa nhận rằng: (thay thế các sửa đổi từ ví dụ này bằng các ví dụ từ ví dụ của riêng tôi)

hg lên -CF  
chi nhánh hg -f newfeature-123  
hg ghép -a -b newfeature-123 

không quá tệ, ngoại trừ việc nó bỏ lại phía sau MNO trước khi khởi động lại như một cái đầu không được trộn lẫn và tạo ra 3 cam kết mới M ', N', O 'đại diện cho chúng phân nhánh khỏi tuyến chính được cập nhật.

Về cơ bản vấn đề là tôi kết thúc với điều này:

thầy A --- B --- C --- D --- E --- F
                \ \
newfeature-123 \ M '--- N' --- O '
                  \
newfeature-123 M --- N --- O

Điều này là không tốt vì nó để lại phía sau địa phương, các cam kết không mong muốn nên được bỏ.

  1. Tùy chọn khác từ cùng một liên kết là
hg qimport -r M: O
hg qpop -a
hg lên F
chi nhánh hg newfeature-123
hg qpush -a
hg qdel -r qbase: qtip

và điều này không dẫn đến biểu đồ mong muốn:

thầy A --- B --- C --- D --- E --- F
                            \
newfeature-123 M --- N --- O

nhưng các lệnh này (tất cả 6 trong số chúng!) có vẻ phức tạp hơn nhiều so với

$ git rebase master

Tôi muốn biết nếu đây là tương đương duy nhất trong Hg hoặc nếu có một số cách khác có sẵn đơn giản như Git.


7
"điều này là không tốt vì nó để lại phía sau địa phương, những cam kết không mong muốn nên bị loại bỏ." - thực sự, git làm điều tương tự. Nó không thay đổi hoặc loại bỏ các xác nhận trong nhánh ban đầu, nó chỉ tạo ra các xác nhận mới áp dụng cùng một bộ thay đổi trên đầu trang chính. Bạn vẫn có thể truy cập những cái cũ bằng cách sử dụng git reflogvà chúng không hoàn toàn biến mất cho đến khi chúng được thu gom rác. Nếu bạn muốn giữ chúng xung quanh trong một nhánh có tên để bạn không phải sử dụng reflog, chỉ cần làm git branch feature-123_originaltrước khi nổi loạn.
MatrixFrog

5
Câu hỏi ngẫu nhiên: bạn đã tự mình vẽ các thay đổi / nhánh hoặc có công cụ nào thực hiện việc đó không?
Amir Rachum

2
Chỉ cần họ tự làm với TextWrangler được đặt thành "ghi đè".
jpswain

Câu trả lời:


235

VonC có câu trả lời mà bạn đang tìm kiếm , Phần mở rộng Rebase. Tuy nhiên, đáng để dành một hoặc hai giây suy nghĩ về lý do tại sao cả mq và rebase đều không được bật theo mặc định: bởi vì đồng bóng là tất cả về những thay đổi không thể xóa được. Khi tôi làm việc theo cách bạn mô tả, gần như hàng ngày, đây là mẫu tôi thực hiện:

1. Start working on a new feature:
$ hg clone mainline-repo newfeature-123
do a few commits (M, N, O)

master A---B---C
                \
newfeature-123   M---N---O

2. Pull new changes from upstream mainline:
$ hg pull

master A---B---C---D---E---F
                \
newfeature-123   M---N---O

3. merge master into my clone so that my new feature 
can be developed against the latest upstream changes:
(from newfeature-123)
$ hg merge F

master A---B---C---D---E---F
                \           \
newfeature-123   M---N---O---P

và đó thực sự là tất cả những gì cần thiết. Tôi kết thúc với một bản sao mới 123-Tôi có thể dễ dàng đẩy trở lại tuyến chính khi tôi hài lòng với nó. Quan trọng nhất, tuy nhiên, tôi không bao giờ thay đổi lịch sử . Ai đó có thể nhìn vào csets của tôi và xem những gì ban đầu được mã hóa và cách tôi phản ứng với những thay đổi trong dòng chính trong suốt công việc của tôi. Không phải ai cũng nghĩ rằng nó có giá trị, nhưng tôi là một người tin tưởng vững chắc rằng đó là công việc kiểm soát nguồn để cho chúng ta thấy không phải những gì chúng ta mong muốn đã xảy ra, nhưng những gì thực sự đã xảy ra - mọi thời hạn và mọi nhà tái cấu trúc đều để lại dấu vết không thể xóa nhòa và nổi loạn và các kỹ thuật chỉnh sửa lịch sử khác che giấu điều đó.

Bây giờ hãy chọn câu trả lời của VonC trong khi tôi đặt hộp xà phòng của mình đi. :)


18
Lưu ý: tất nhiên, Git không chính xác cho phép bạn viết lại lịch sử, chỉ để tạo một lịch sử mới một cách dễ dàng ( utcc.utoronto.ca/~cks/space/blog/tech/GitNewHistory ). Bằng cách thêm RebaseExtension, Mercurial cung cấp cùng một cách thuận tiện chính xác để thay thế một lịch sử cũ bằng một lịch sử mới. Tại sao? Bởi vì hợp nhất không phải lúc nào cũng là câu trả lời đúng, đặc biệt là khi thay đổi của bạn nên được xem là diễn biến trên đỉnh F chứ không phải ngược lại (P được sáp nhập trên đầu O)
VonC

19
VonC, tôi đồng ý, việc hợp nhất không phải lúc nào cũng là lựa chọn đúng đắn, nhưng tôi nghĩ sự khác biệt trong đó là điều người ta muốn lịch sử VCS của một người có thể nói với họ. Tôi nghĩ rằng lịch sử sẽ luôn có thể trả lời các câu hỏi như "Cách mà tôi đã cố gắng tích hợp nó lúc đầu không thành công và tôi nghĩ lúc đó là vô dụng". Các nhà khoa học giữ sổ ghi chép bằng bút với các trang được đánh số và các kỹ sư phần mềm AFAIC nên lưu từng byte mà họ đã gõ. Viết lại lịch sử, thậm chí cả cha mẹ, nhưng chắc chắn làExExtension, HistEdit, v.v ... vi phạm điều đó. Đó hoàn toàn là vấn đề lựa chọn cá nhân.
Ry4an Brase

14
+1 cho lựa chọn cá nhân. Do đó, với Git, tôi sử dụng rebase cho các phân kỳ tầm thường và hợp nhất cho không tầm thường. Điều này cho phép tôi lưu giữ lịch sử hợp nhất nơi tôi cảm thấy quan trọng, nhưng giữ cho nhật ký sạch sẽ và tuyến tính hầu hết thời gian.
Kos

16
Ngoài ra, tôi thực hiện rất nhiều cuộc nổi loạn tương tác vì trước tiên tôi có xu hướng thực hiện nhiều cam kết nhỏ, sau đó tham gia, gắn nhãn và dọn sạch chúng, sau đó hợp nhất chúng lại với (hoặc rebase trên đầu) nhánh chính. Tôi thích mã hóa và quản lý các thay đổi là các bước riêng biệt.
Kos

6
Triết lý "lưu từng byte theo con đường phát triển" không thực sự phù hợp với các dự án nguồn mở lớn, nơi những người đóng góp đề xuất một bộ các bản vá, sau đó làm lại chúng dựa trên phản hồi từ các nhà bảo trì. Cuối cùng, kho dự án chính chỉ có phiên bản phù hợp của tất cả các thay đổi, với tất cả các thay đổi liên quan trong một cam kết duy nhất. Phản hồi tương tác git thực sự tốt để dọn dẹp các thay đổi đang hoạt động thành một chuỗi các cam kết không làm cho cây bị gãy và với các thông điệp cam kết phản ánh những gì bạn quyết định nói sau khi bạn hoàn thành công việc.
Peter Cordes

104

Bạn có thể đang tìm kiếm Rebase Extension . (được triển khai như một phần của SummerOfCode 2008 )

Trong những trường hợp đó, có thể hữu ích để "tách" các thay đổi cục bộ, đồng bộ hóa kho lưu trữ với dòng chính và sau đó nối các thay đổi riêng tư lên trên các thay đổi từ xa mới. Hoạt động này được gọi là rebase.

Bắt đầu từ :

văn bản thay thế

đến:

văn bản thay thế


Như nhận xét dưới đây bởi tủ quần áo :

Trong trường hợp bạn không kéo các thay đổi vào và bạn có hai nhánh trong repo của mình, bạn có thể thực hiện ( sử dụngkeepbranches ):

hg up newfeature-123 
hg rebase -d master --keepbranches

( --keepbranches: Kế thừa tên chi nhánh ban đầu.)

Mojca đề cập:

Tôi thích sử dụng hg rebase --source {L1's-sha} --dest {R2's-sha}, nhưng tôi không biết tôi có thể thêm --keepbranchesvào cuối.

Như minh họa dưới đây của Jonathan Blackburn :

 hg rebase -d default --keepbranches

1
Tôi đã xem phần Mở rộng Rebase, nhưng nó vẫn chưa rõ ràng đối với tôi. Bạn có thể vui lòng giải thích các bước để làm những gì tôi đã mô tả ở trên?
jpswain

6
Trong trường hợp bạn không kéo các thay đổi vào và bạn có hai nhánh trong repo của mình, bạn có thể làm: hg up newfeature-123tiếp theohg rebase -d master --keepbranches
tủ quần áo

2
Tôi tin rằng không có gì sai với rebase, đó chỉ là vấn đề lựa chọn. Vấn đề là rebase bị lạm dụng và tôi với @ Ry4an về điều này không viết lại lịch sử để bạn có thể biết điều gì xảy ra và khi nào.
Jorge Vargas

@stepcoat: cảm ơn bạn đã gợi ý cho việc sử dụng --keepbranches. Tôi thích sử dụng hg rebase --source {L1's-sha} --dest {R2's-sha}, nhưng tôi không biết tôi có thể thêm --keepbranchesvào cuối. Điều này được đề cập trong các câu trả lời xếp hạng thấp hơn khác, nhưng sẽ rất tốt nếu viết rõ ràng cho câu trả lời này.
Mojca

@Mojca Không có vấn đề. Tôi đã chỉnh sửa câu trả lời cho phù hợp.
VonC

44

Giả sử bạn có bản cài đặt Hg hiện đại, bạn chỉ cần thêm:

[extensions]
rebase = 

đến ~ / .hgrc.

Sau đó, bạn có thể sử dụng các lệnh hg rebase, hg pull --rebasehoặc hg help rebase.


2
Chỉ cần thêm vào nội dung này của lệnh thì bạn cần thực thi sẽ là: hg rebase -s o -d f
Giải pháp đơn giản

21

Tôi không nghĩ các câu trả lời ở trên đạt được mục tiêu của OP, đó là duy trì nhánh nhiệm vụ của anh ấy, chỉ cần phản đối lại một điểm sau đó trên nhánh mẹ.

Giả sử tôi bắt đầu với biểu đồ này (được tạo bằng phần mở rộng graphlog. Tình yêu đam mê nghiêm túc đối với graphlog).

@  9a4c0eb66429 Feature 3 commit 2 tip feature3
|
| o  af630ccb4a80 default againagainagain  
| |
o |  98bdde5d2185 Feature 3 branch commit 1  feature3
|/
o  e9f850ac41da foo   

Nếu tôi ở nhánh Feature3 và muốn loại bỏ nó khỏi cam kết một lần nữa, tôi hiểu rằng tôi sẽ chạy hg rebase -d default . Điều này có kết quả như sau:

@  89dada24591e Feature 3 commit 2 tip 
|
o  77dcce88786d Feature 3 branch commit 1  
|
o  af630ccb4a80 default againagainagain  
|
o  e9f850ac41da foo  

Nhiệm vụ đã hoàn thành? Tôi không nghĩ vậy. Vấn đề là khi các cam kết trên nhánh Feature3 bị từ chối trên một lần nữa, nhánh Feature3 đã bị xóa . Cam kết của tôi đã được chuyển đến chi nhánh mặc định, đó là điều tôi đang cố gắng tránh ở nơi đầu tiên.

Trong Git, kết quả sẽ như thế này:

@  9a4c0eb66429 Feature 3 commit 2 tip
|
o  98bdde5d2185 Feature 3 branch commit 1 **feature3**
|
o  af630ccb4a80 default againagainagain
|
o  e9f850ac41da foo

Lưu ý rằng nhánh Feature3 vẫn tồn tại, hai cam kết vẫn nằm trên nhánh Feature3 và không hiển thị trên mặc định. Không bảo toàn nhánh nhiệm vụ, tôi không thấy điều này có chức năng khác với hợp nhất.

CẬP NHẬT : Tôi phát hiện ra --keepbranchescờ được hỗ trợ bởi hg rebase và tôi rất vui khi báo cáo mọi thứ là okey-dokey. Sử dụng hg rebase -d default --keepbranches, tôi sao chép chính xác hành vi Git mà tôi mong muốn. Một vài bí danh sau đó và tôi nổi loạn như doanh nghiệp của không ai.


7
Chỉ cần phát hiện ra cờ --keepbranches trên rebase. Vấn đề được giải quyết. Đây là mã của tôi, tôi sẽ đặt nó làm mặc định, nhưng đó chỉ là tôi.
Jonathan Blackburn

1
Tôi nghĩ sẽ hữu ích nếu bạn thêm chút thông tin đó vào phản hồi của bạn ở trên - tôi mất một lúc để tìm thấy nó.
HansMari

3

Vì một số người đã ca ngợi rằng họ nghĩ rằng thật tốt khi giữ mọi lần lặp lại của mọi thứ, tôi sẽ chỉ ra rằng đối với các dự án nguồn mở lớn hơn, việc chấp nhận các thay đổi đầy đủ của sự hợp nhất và lặp lại phát triển sẽ tạo ra lịch sử sửa đổi chính tuyến lộn xộn và thực hiện lịch sử sửa đổi ít hữu ích hơn để xem phiên bản hiện tại đã đến đó như thế nào.

Điều này hoạt động tốt khi những thay đổi được gửi được xem xét bởi những người không viết chúng, trước khi chúng được chấp nhận, vì vậy những thay đổi đi vào dòng chính thường được gỡ lỗi và hoạt động. Sau đó, khi bạn quay lại nguồn gốc của một dòng, bạn sẽ thấy tất cả các thay đổi đi kèm với nó, không phải là một điểm nào đó ở giữa sự phát triển của thay đổi mà nó là một phần của.

Trang cộng tác viên x265 giải thích cách cam kết lại một tập hợp các thay đổi bạn đang thực hiện, để chúng sẵn sàng gửi dự án x265. (Bao gồm cả việc sử dụng TortoiseHG để cam kết một số nhưng không phải tất cả các thay đổi trong một tệp riêng lẻ, như giai đoạn git gui / unstage diff hunk cho cam kết).

Quá trình này là để hg được cập nhật vào mẹo ngược dòng, và sau đó nhận tất cả các thay đổi của bạn không được cam kết trong thư mục làm việc. Đặt bất kỳ thứ gì không phải là một phần của những gì bạn muốn gửi, sau đó chia phần còn lại thành nhiều cam kết riêng biệt phù hợp, với các thông điệp cam kết tốt đẹp.

Tôi đoán bạn sẽ sao chép / dán và sau đó chỉnh sửa các thông báo cam kết từ các lần lặp trước của một bản vá mà bạn đang sửa đổi. Hoặc có lẽ bạn có thể ghép các cam kết cũ của mình (cherry-pick bằng ngôn ngữ git), sau đó sửa đổi từng cái một, để lấy các thông điệp cam kết cũ của bạn làm điểm bắt đầu để chỉnh sửa.

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.