Quy trình công việc Git và rebase vs câu hỏi hợp nhất


971

Tôi đã sử dụng Git được vài tháng trong một dự án với một nhà phát triển khác. Tôi có vài năm kinh nghiệm với SVN , vì vậy tôi đoán rằng tôi mang rất nhiều hành lý cho mối quan hệ.

Tôi đã nghe nói rằng Git là tuyệt vời để phân nhánh và hợp nhất, và cho đến nay, tôi chỉ không thấy nó. Chắc chắn, việc phân nhánh rất đơn giản, nhưng khi tôi cố gắng hợp nhất, mọi thứ sẽ biến thành địa ngục. Bây giờ, tôi đã quen với điều đó từ SVN, nhưng dường như tôi chỉ giao dịch một hệ thống phiên bản phụ cho một hệ thống khác.

Đối tác của tôi nói với tôi rằng các vấn đề của tôi xuất phát từ mong muốn hợp nhất của tôi và tôi nên sử dụng rebase thay vì hợp nhất trong nhiều tình huống. Ví dụ: đây là quy trình công việc mà anh ấy đã đặt ra:

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature
git checkout master
git merge my_new_feature

Về cơ bản, tạo một nhánh tính năng, LUÔN LUÔN rebase từ master sang nhánh và hợp nhất từ ​​nhánh trở lại master. Điều quan trọng cần lưu ý là chi nhánh luôn ở địa phương.

Đây là quy trình làm việc mà tôi bắt đầu với

clone remote repository
create my_new_feature branch on remote repository
git checkout -b --track my_new_feature origin/my_new_feature
..work, commit, push to origin/my_new_feature
git merge master (to get some changes that my partner added)
..work, commit, push to origin/my_new_feature
git merge master
..finish my_new_feature, push to origin/my_new_feature
git checkout master
git merge my_new_feature
delete remote branch
delete local branch

Có hai điểm khác biệt cơ bản (tôi nghĩ): Tôi luôn sử dụng hợp nhất thay vì khởi động lại và tôi đẩy nhánh tính năng của mình (và nhánh tính năng của tôi cam kết) vào kho lưu trữ từ xa.

Lý do của tôi cho chi nhánh từ xa là tôi muốn công việc của mình được sao lưu khi tôi đang làm việc. Kho lưu trữ của chúng tôi được tự động sao lưu và có thể được khôi phục nếu có sự cố. Máy tính xách tay của tôi không, hoặc không triệt để. Do đó, tôi ghét phải có mã trên máy tính xách tay của mình mà không được nhân đôi ở nơi khác.

Lý do của tôi cho việc hợp nhất thay vì rebase là hợp nhất dường như là tiêu chuẩn và rebase dường như là một tính năng nâng cao. Cảm giác ruột của tôi là những gì tôi đang cố gắng không phải là một thiết lập nâng cao, vì vậy việc rebase là không cần thiết. Tôi thậm chí đã đọc cuốn sách Lập trình thực dụng mới trên Git, và chúng bao gồm việc hợp nhất rộng rãi và hầu như không đề cập đến rebase.

Dù sao, tôi đã theo dõi quy trình làm việc của mình trên một chi nhánh gần đây và khi tôi cố gắng hợp nhất nó trở lại thành chủ, tất cả đã đi vào địa ngục. Có hàng tấn xung đột với những thứ không nên quan trọng. Những mâu thuẫn chỉ vô nghĩa với tôi. Tôi mất một ngày để sắp xếp mọi thứ, và cuối cùng lên đến đỉnh điểm buộc phải đẩy chủ nhân từ xa, vì chủ địa phương của tôi đã giải quyết xong mọi mâu thuẫn, nhưng người ở xa vẫn không vui.

Quy trình làm việc "chính xác" cho một cái gì đó như thế này là gì? Git được cho là làm cho việc phân nhánh và hợp nhất trở nên siêu dễ dàng, và tôi chỉ không thấy điều đó.

Cập nhật 2011-04-15

Đây có vẻ là một câu hỏi rất phổ biến, vì vậy tôi nghĩ rằng tôi sẽ cập nhật với kinh nghiệm hai năm kể từ lần đầu tiên tôi hỏi.

Nó chỉ ra rằng quy trình làm việc ban đầu là chính xác, ít nhất là trong trường hợp của chúng tôi. Nói cách khác, đây là những gì chúng tôi làm và nó hoạt động:

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git checkout master
git merge my_new_feature

Trên thực tế, quy trình làm việc của chúng tôi có một chút khác biệt, vì chúng tôi có xu hướng thực hiện hợp nhất squash thay vì hợp nhất thô. ( Lưu ý: Điều này còn gây tranh cãi, xem bên dưới. ) Điều này cho phép chúng tôi biến toàn bộ nhánh tính năng của mình thành một cam kết duy nhất trên bản gốc. Sau đó, chúng tôi xóa chi nhánh tính năng của chúng tôi. Điều này cho phép chúng tôi cấu trúc một cách hợp lý các cam kết của chúng tôi trên chủ, ngay cả khi chúng hơi lộn xộn trên các nhánh của chúng tôi. Vì vậy, đây là những gì chúng tôi làm:

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git checkout master
git merge --squash my_new_feature
git commit -m "added my_new_feature"
git branch -D my_new_feature

Tranh cãi về Squash Merge - Như một số nhà bình luận đã chỉ ra, việc hợp nhất squash sẽ vứt bỏ tất cả lịch sử trên nhánh tính năng của bạn. Như tên của nó, nó đè bẹp tất cả các cam kết thành một. Đối với các tính năng nhỏ, điều này có ý nghĩa vì nó ngưng tụ nó thành một gói duy nhất. Đối với các tính năng lớn hơn, có lẽ đó không phải là một ý tưởng tuyệt vời, đặc biệt nếu các cam kết cá nhân của bạn đã là nguyên tử. Nó thực sự đi xuống sở thích cá nhân.

Yêu cầu kéo Github và Bitbucket (những người khác?) - Trong trường hợp bạn đang tự hỏi làm thế nào hợp nhất / rebase liên quan đến Yêu cầu kéo, tôi khuyên bạn nên làm theo tất cả các bước trên cho đến khi bạn sẵn sàng hợp nhất trở lại thành chủ. Thay vì kết hợp thủ công với git, bạn chỉ cần chấp nhận PR. Lưu ý rằng điều này sẽ không thực hiện hợp nhất squash (ít nhất là không theo mặc định), nhưng không squash, không chuyển tiếp nhanh là quy ước hợp nhất được chấp nhận trong cộng đồng Pull Request (theo như tôi biết). Cụ thể, nó hoạt động như thế này:

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git push # May need to force push
...submit PR, wait for a review, make any changes requested for the PR
git rebase master
git push # Will probably need to force push (-f), due to previous rebases from master
...accept the PR, most likely also deleting the feature branch in the process
git checkout master
git branch -d my_new_feature
git remote prune origin

Tôi đã yêu Git và không bao giờ muốn quay lại SVN. Nếu bạn đang vật lộn, chỉ cần kiên trì với nó và cuối cùng bạn sẽ thấy ánh sáng ở cuối đường hầm.


31
Thật không may, cuốn sách Lập trình thực dụng mới chủ yếu được viết từ việc sử dụng Git trong khi vẫn suy nghĩ trong SVN, và trong trường hợp này nó đã khiến bạn hiểu lầm. Trong Git, rebase giữ mọi thứ đơn giản khi chúng có thể. Kinh nghiệm của bạn có thể cho bạn biết rằng quy trình làm việc của bạn không hoạt động trong Git, không phải Git không hoạt động.
Paul

18
Tôi sẽ không đề xuất hợp nhất squash trong trường hợp này, vì nó không lưu thông tin về những gì được hợp nhất (giống như svn, nhưng không có mergeinfo ở đây).
Marius K

7
Yêu thích ghi chú ở phía dưới, tôi đã có một trải nghiệm tương tự về cuộc đấu tranh với Git, nhưng bây giờ đấu tranh để tưởng tượng không sử dụng nó. Cảm ơn vì lời giải thích cuối cùng, đã giúp rất nhiều cho rebasesự hiểu biết
Jon Phenow

6
Sau khi bạn hoàn thành tính năng, bạn không nên khởi động lại lần cuối trước khi hợp nhất new_feature thành chủ?
softarn

17
Quy trình công việc của bạn mất tất cả lịch sử cam kết từ chi nhánh bị xóa :(
Max Nanasy

Câu trả lời:


372

"Xung đột" có nghĩa là "diễn biến song song của cùng một nội dung". Vì vậy, nếu nó biến thành "tất cả thành địa ngục" trong quá trình hợp nhất, điều đó có nghĩa là bạn có sự phát triển lớn trên cùng một tập tin.

Lý do tại sao một rebase sau đó tốt hơn so với hợp nhất là:

  • bạn viết lại lịch sử cam kết cục bộ của bạn với một trong những người chủ (và sau đó áp dụng lại công việc của bạn, giải quyết bất kỳ xung đột nào sau đó)
  • hợp nhất cuối cùng chắc chắn sẽ là một "chuyển tiếp nhanh", bởi vì nó sẽ có tất cả lịch sử cam kết của chủ, cộng với chỉ những thay đổi của bạn để áp dụng lại.

Tôi xác nhận rằng quy trình làm việc chính xác trong trường hợp đó (diễn biến trên tập hợp chung của tập tin) là rebase trước, sau đó hợp nhất .

Tuy nhiên, điều đó có nghĩa là, nếu bạn đẩy chi nhánh địa phương của mình (vì lý do dự phòng), thì chi nhánh đó sẽ không bị kéo (hoặc ít nhất là được sử dụng) bởi bất kỳ ai khác (vì lịch sử cam kết sẽ được viết lại bởi cuộc nổi loạn kế tiếp).


Về chủ đề đó (rebase sau đó hợp nhất quy trình làm việc), barraponto đề cập đến trong các bình luận hai bài viết thú vị, cả hai từ randyfay.com :

Sử dụng kỹ thuật này, công việc của bạn luôn đi đầu trong ngành công cộng giống như một bản vá được cập nhật với hiện tại HEAD.

(một kỹ thuật tương tự tồn tại cho chợ )


27
Để biết kỹ thuật cho phép khởi động lại chia sẻ, hãy xem phần
mềmwirl.blogspot.com / 2009/04 / Giả

2
randyfay.com/node/91randyfay.com/node/89 là những bài đọc tuyệt vời. những bài viết này làm cho tôi hiểu những gì là lo lắng với quy trình làm việc của tôi và một quy trình công việc lý tưởng sẽ là gì.
Capi Etheriel

chỉ cần nói thẳng ra, việc chuyển từ nhánh chính sang địa phương của bạn về cơ bản là để cập nhật bất kỳ lịch sử nào mà địa phương của bạn có thể đã bỏ lỡ mà chủ nhân có kiến ​​thức về sau khi hợp nhất?
hellatan

@dtan những gì tôi mô tả ở đây là nổi loạn cục bộ trên đỉnh của chủ. Bạn không cập nhật chính xác lịch sử địa phương, mà thay vào đó áp dụng lại lịch sử địa phương trên đỉnh chính để giải quyết bất kỳ xung đột nào trong chi nhánh địa phương.
VonC

386

TL; DR

Luồng công việc git rebase không bảo vệ bạn khỏi những người kém giải quyết xung đột hoặc những người đã quen với quy trình làm việc của SVN, như được đề xuất trong Tránh các thảm họa Git: Câu chuyện Gory . Nó chỉ làm cho việc giải quyết xung đột trở nên tẻ nhạt hơn đối với họ và khiến cho việc phục hồi từ giải quyết xung đột xấu trở nên khó khăn hơn. Thay vào đó, hãy sử dụng diff3 để không quá khó khăn ngay từ đầu.


Quy trình làm việc Rebase không tốt hơn để giải quyết xung đột!

Tôi rất ủng hộ việc làm sạch lịch sử. Tuy nhiên nếu tôi gặp phải một cuộc xung đột, tôi lập tức hủy bỏ cuộc nổi loạn và thay vào đó là hợp nhất! Nó thực sự giết tôi rằng mọi người đang đề xuất một quy trình công việc rebase như là một thay thế tốt hơn cho quy trình hợp nhất để giải quyết xung đột (chính xác là câu hỏi này là gì).

Nếu nó biến thành "tất cả thành địa ngục" trong quá trình hợp nhất, nó sẽ biến thành "tất cả xuống địa ngục" trong một cuộc nổi loạn, và có khả năng còn nhiều địa ngục nữa! Đây là lý do tại sao:

Lý do # 1: Giải quyết xung đột một lần, thay vì một lần cho mỗi lần xác nhận

Khi bạn rebase thay vì hợp nhất, bạn sẽ phải thực hiện giải quyết xung đột nhiều lần như bạn đã cam kết để rebase, cho cùng một cuộc xung đột!

Kịch bản thực

Tôi rẽ nhánh khỏi master để cấu trúc lại một phương thức phức tạp trong một nhánh. Công việc tái cấu trúc của tôi bao gồm 15 lần cam kết khi tôi làm việc để tái cấu trúc nó và nhận các đánh giá mã. Một phần trong quá trình tái cấu trúc của tôi liên quan đến việc sửa các tab và khoảng trắng hỗn hợp đã có trong bản gốc trước đó. Điều này là cần thiết, nhưng thật không may, nó sẽ xung đột với bất kỳ thay đổi nào được thực hiện sau đó đối với phương pháp này trong tổng thể. Chắc chắn, trong khi tôi đang làm việc với phương pháp này, một người nào đó thực hiện một thay đổi đơn giản, hợp pháp cho cùng một phương thức trong nhánh chính cần được hợp nhất với các thay đổi của tôi.

Khi đến lúc hợp nhất chi nhánh của tôi trở lại với chủ, tôi có hai lựa chọn:

git merge: Tôi nhận được một cuộc xung đột. Tôi thấy sự thay đổi mà họ đã thực hiện để làm chủ và hợp nhất nó với (sản phẩm cuối cùng) của chi nhánh của tôi. Làm xong.

git rebase: Tôi nhận được một cuộc xung đột với cam kết đầu tiên của mình . Tôi giải quyết xung đột và tiếp tục cuộc nổi loạn. Tôi nhận được một cuộc xung đột với cam kết thứ hai của tôi . Tôi giải quyết xung đột và tiếp tục cuộc nổi loạn. Tôi nhận được một cuộc xung đột với cam kết thứ ba của tôi . Tôi giải quyết xung đột và tiếp tục cuộc nổi loạn. Tôi nhận được một cuộc xung đột với cam kết thứ tư của tôi . Tôi giải quyết xung đột và tiếp tục cuộc nổi loạn. Tôi nhận được một cuộc xung đột với cam kết thứ năm của tôi . Tôi giải quyết xung đột và tiếp tục cuộc nổi loạn. Tôi nhận được một cuộc xung đột với cam kết thứ sáu của tôi . Tôi giải quyết xung đột và tiếp tục cuộc nổi loạn. Tôi có một cuộc xung đột với thứ bảy của tôicam kết. Tôi giải quyết xung đột và tiếp tục cuộc nổi loạn. Tôi nhận được một cuộc xung đột với cam kết thứ tám của tôi . Tôi giải quyết xung đột và tiếp tục cuộc nổi loạn. Tôi nhận được một cuộc xung đột với cam kết thứ chín của tôi . Tôi giải quyết xung đột và tiếp tục cuộc nổi loạn. Tôi nhận được một cuộc xung đột với cam kết thứ mười của tôi . Tôi giải quyết xung đột và tiếp tục cuộc nổi loạn. Tôi nhận được một cuộc xung đột với cam kết thứ mười một của tôi . Tôi giải quyết xung đột và tiếp tục cuộc nổi loạn. Tôi nhận được một cuộc xung đột với cam kết thứ mười hai của tôi . Tôi giải quyết xung đột và tiếp tục cuộc nổi loạn. Tôi nhận được một cuộc xung đột với cam kết thứ mười ba của tôi . Tôi giải quyết xung đột và tiếp tục cuộc nổi loạn. Tôi có một cuộc xung đột với thứ mười bốn của tôicam kết. Tôi giải quyết xung đột và tiếp tục cuộc nổi loạn. Tôi nhận được một cuộc xung đột với cam kết thứ mười lăm của tôi . Tôi giải quyết xung đột và tiếp tục cuộc nổi loạn.

Bạn phải đùa tôi nếu đây là quy trình làm việc ưa thích của bạn. Tất cả chỉ là một sửa chữa khoảng trắng xung đột với một thay đổi được thực hiện trên bản gốc và mọi cam kết sẽ xung đột và phải được giải quyết. Và đây là một kịch bản đơn giản chỉ có xung đột khoảng trắng. Trời cấm bạn có một cuộc xung đột thực sự tham gia thay đổi mã lớn trên các tập tin và phải quyết tâm nhiều lần.

Với tất cả các giải quyết xung đột thêm mà bạn cần làm, nó chỉ làm tăng khả năng bạn sẽ phạm sai lầm . Nhưng sai lầm là tốt trong git vì bạn có thể hoàn tác, phải không? Ngoại trừ ...

Lý do # 2: Với rebase, không có hoàn tác!

Tôi nghĩ rằng tất cả chúng ta có thể đồng ý rằng giải quyết xung đột có thể khó khăn, và cũng có một số người rất tệ trong việc này. Nó có thể rất dễ mắc sai lầm, đó là lý do tại sao nó tuyệt vời đến mức git làm cho nó dễ dàng hoàn tác!

Khi bạn hợp nhất một nhánh, git sẽ tạo một cam kết hợp nhất có thể bị loại bỏ hoặc sửa đổi nếu giải quyết xung đột kém. Ngay cả khi bạn đã đẩy cam kết hợp nhất xấu sang repo công khai / có thẩm quyền, bạn vẫn có thể sử dụng git revertđể hoàn tác các thay đổi được đưa ra bởi hợp nhất và thực hiện lại hợp nhất chính xác trong một cam kết hợp nhất mới.

Khi bạn khởi động lại một nhánh, trong trường hợp có khả năng giải quyết xung đột được thực hiện sai, bạn sẽ bị lừa. Bây giờ mọi cam kết đều chứa sự hợp nhất xấu và bạn không thể làm lại rebase *. Tốt nhất, bạn phải quay lại và sửa đổi từng cam kết bị ảnh hưởng. Không vui.

Sau một cuộc nổi loạn, không thể xác định được đâu là một phần của các cam kết và những gì được đưa ra là kết quả của việc giải quyết xung đột xấu.

* Có thể hoàn tác một rebase nếu bạn có thể đào các ref cũ ra khỏi nhật ký nội bộ của git hoặc nếu bạn tạo một nhánh thứ ba trỏ đến lần xác nhận cuối cùng trước khi khởi động lại.

Hãy giải quyết xung đột: sử dụng diff3

Lấy xung đột này làm ví dụ:

<<<<<<< HEAD
TextMessage.send(:include_timestamp => true)
=======
EmailMessage.send(:include_timestamp => false)
>>>>>>> feature-branch

Nhìn vào cuộc xung đột, không thể biết được mỗi chi nhánh đã thay đổi hoặc ý định của nó là gì. Đây là lý do lớn nhất theo tôi tại sao giải quyết xung đột là khó hiểu và khó khăn.

diff3 để giải cứu!

git config --global merge.conflictstyle diff3

Khi bạn sử dụng diff3, mỗi xung đột mới sẽ có phần thứ 3, tổ tiên chung được hợp nhất.

<<<<<<< HEAD
TextMessage.send(:include_timestamp => true)
||||||| merged common ancestor
EmailMessage.send(:include_timestamp => true)
=======
EmailMessage.send(:include_timestamp => false)
>>>>>>> feature-branch

Đầu tiên kiểm tra tổ tiên chung sáp nhập. Sau đó so sánh mỗi bên để xác định ý định của từng chi nhánh. Bạn có thể thấy rằng CHÍNH đã thay đổi EmailMessage thành TextMessage. Mục đích của nó là thay đổi lớp được sử dụng thành TextMessage, truyền các tham số tương tự. Bạn cũng có thể thấy rằng mục đích của nhánh tính năng là chuyển sai thay vì đúng cho tùy chọn: include_timestamp. Để hợp nhất những thay đổi này, kết hợp ý định của cả hai:

TextMessage.send(:include_timestamp => false)

Nói chung:

  1. So sánh tổ tiên chung với mỗi nhánh và xác định nhánh nào có thay đổi đơn giản nhất
  2. Áp dụng thay đổi đơn giản đó cho phiên bản mã của nhánh khác, để nó chứa cả thay đổi đơn giản hơn và phức tạp hơn
  3. Xóa tất cả các phần của mã xung đột khác với phần mà bạn vừa hợp nhất các thay đổi lại với nhau thành

Thay thế: Giải quyết bằng cách áp dụng thủ công các thay đổi của chi nhánh

Cuối cùng, một số xung đột là khủng khiếp để hiểu ngay cả với diff3. Điều này đặc biệt xảy ra khi diff tìm thấy các dòng chung không phổ biến về mặt ngữ nghĩa (ví dụ: cả hai nhánh tình cờ có một dòng trống ở cùng một nơi!). Ví dụ, một nhánh thay đổi thụt đầu dòng của lớp hoặc sắp xếp lại các phương thức tương tự. Trong những trường hợp này, một chiến lược giải quyết tốt hơn có thể là kiểm tra sự thay đổi từ một trong hai phía của hợp nhất và áp dụng khác biệt cho tệp khác.

Chúng ta hãy xem làm thế nào chúng ta có thể giải quyết một cuộc xung đột trong một kịch bản trong đó hợp nhất origin/feature1nơi lib/message.rbxung đột.

  1. Quyết định xem chi nhánh hiện đang được kiểm tra của chúng tôi ( HEAD, hoặc --ours) hoặc chi nhánh chúng tôi hợp nhất ( origin/feature1, hoặc --theirs) là một thay đổi đơn giản hơn để áp dụng. Sử dụng diff với triple dot ( git diff a...b) cho thấy những thay đổi đã xảy ra bkể từ lần phân kỳ cuối cùng của nó a, hay nói cách khác, so sánh tổ tiên chung của a và b với b.

    git diff HEAD...origin/feature1 -- lib/message.rb # show the change in feature1
    git diff origin/feature1...HEAD -- lib/message.rb # show the change in our branch
    
  2. Kiểm tra phiên bản phức tạp hơn của tập tin. Điều này sẽ loại bỏ tất cả các dấu hiệu xung đột và sử dụng phía bạn chọn.

    git checkout --ours -- lib/message.rb   # if our branch's change is more complicated
    git checkout --theirs -- lib/message.rb # if origin/feature1's change is more complicated
    
  3. Với sự thay đổi phức tạp đã được kiểm tra, kéo lên độ lệch của thay đổi đơn giản hơn (xem bước 1). Áp dụng mỗi thay đổi từ khác biệt này vào tệp xung đột.


4
Làm thế nào để hợp nhất tất cả các xung đột trong một lần hoạt động tốt hơn so với các cam kết cá nhân? Tôi đã nhận được các vấn đề từ việc hợp nhất các cam kết duy nhất (đặc biệt là từ những người không phá vỡ các cam kết thành các phần logic VÀ cung cấp các bài kiểm tra đầy đủ để xác minh). Ngoài ra, rebase không tệ hơn hợp nhất khi nói đến các tùy chọn sao lưu, sử dụng thông minh các rebase tương tác và các công cụ như tortoisegit (cho phép lựa chọn cam kết bao gồm) sẽ giúp ích rất nhiều.
Phổ

8
Tôi cảm thấy như tôi đã giải quyết lý do ở # 1. Nếu các cam kết riêng lẻ không nhất quán về mặt logic, thì càng có nhiều lý do để hợp nhất nhánh nhất quán logic, để bạn thực sự có thể hiểu được xung đột. Nếu cam kết 1 bị lỗi và cam kết 2 sửa nó, việc hợp nhất cam kết 1 sẽ gây nhầm lẫn. Có nhiều lý do chính đáng để bạn có thể nhận được 15 xung đột liên tiếp, như lý do tôi đã nêu ở trên. Ngoài ra lập luận của bạn cho rebase không tồi tệ hơn là hơi không có cơ sở. Rebase trộn các kết hợp xấu vào các cam kết tốt ban đầu và không để lại các cam kết tốt xung quanh để cho phép bạn thử lại. Hợp nhất nào.
Edward Anderson

6
Tôi hoàn toàn đồng ý với bạn nilbus. Bài tuyệt vời; Điều đó làm sáng tỏ một số điều. Tôi tự hỏi nếu rerere sẽ được giúp đỡ ở đây mặc dù. Ngoài ra, cảm ơn bạn đã gợi ý về việc sử dụng diff3, tôi chắc chắn sẽ chuyển cái đó ngay bây giờ.
derick

45
+1 vì đã nói với tôi về diff3 một mình - mức độ thường xuyên nhìn vào một cuộc xung đột khó hiểu nguyền rủa bất cứ ai chịu trách nhiệm không nói cho tôi biết tổ tiên chung đã nói gì. Cảm ơn rât nhiều.
Giăng

4
Đây phải là câu trả lời được chấp nhận. Luồng công việc rebase cũng rất khủng khiếp vì nó che giấu sự thật rằng có một sự khác biệt lớn trong cơ sở mã tại một thời điểm nào đó, có thể hữu ích để biết nếu bạn muốn hiểu cách bạn đang xem mã được viết. Chỉ những nhánh nhỏ không xung đột mới được chuyển sang chủ.
Robert Rüger

32

Trong quy trình làm việc của mình, tôi nổi loạn hết mức có thể (và tôi cố gắng thực hiện thường xuyên. Không để sự khác biệt tích lũy làm giảm đáng kể số lượng và mức độ nghiêm trọng của các vụ va chạm giữa các chi nhánh).

Tuy nhiên, ngay cả trong một quy trình làm việc chủ yếu dựa trên rebase, vẫn có một nơi để hợp nhất.

Nhớ lại rằng hợp nhất thực sự tạo ra một nút có hai cha mẹ. Bây giờ hãy xem xét tình huống sau: Tôi có hai tính năng độc lập A và B, và bây giờ muốn phát triển công cụ trên nhánh tính năng C phụ thuộc vào cả A và B, trong khi A và B đang được xem xét.

Những gì tôi làm sau đó, là như sau:

  1. Tạo (và thanh toán) chi nhánh C trên đầu A.
  2. Hợp nhất nó với B

Bây giờ nhánh C bao gồm các thay đổi từ cả A và B, và tôi có thể tiếp tục phát triển trên nó. Nếu tôi thực hiện bất kỳ thay đổi nào đối với A, thì tôi sẽ xây dựng lại biểu đồ của các nhánh theo cách sau:

  1. tạo nhánh T trên đỉnh mới của A
  2. hợp nhất T với B
  3. rebase C lên T
  4. xóa chi nhánh T

Bằng cách này, tôi thực sự có thể duy trì các biểu đồ nhánh tùy ý, nhưng thực hiện một việc phức tạp hơn tình huống được mô tả ở trên là quá phức tạp, do không có công cụ tự động để thực hiện việc khởi động lại khi cha mẹ thay đổi.


1
Bạn có thể đạt được điều tương tự chỉ với những cuộc nổi loạn. Việc hợp nhất thực sự không cần thiết ở đây (trừ khi bạn không muốn sao chép các cam kết - nhưng tôi hầu như không xem đó là một đối số).
odwl

1
Quả thực tôi không muốn nhân đôi các cam kết. Tôi muốn giữ cấu trúc trong chuyến bay của công việc của tôi sạch nhất có thể. Nhưng đó là vấn đề sở thích cá nhân và không nhất thiết phải phù hợp với mọi người.
Alex Gontmakher

Tôi 100% đồng ý với đoạn đầu tiên. (Câu trả lời của @ Edward hoạt động trong trường hợp không phải vậy, nhưng tôi muốn có tất cả các dự án trên thế giới hoạt động như bạn đề xuất). Phần còn lại của câu trả lời có vẻ hơi xa vời trong ý nghĩa rằng làm việc trên C trong khi A và B đang tiến hành là rất rủi ro (ít nhất là trong phạm vi nó thực sự phụ thuộc vào A và B), và thậm chí cuối cùng bạn có lẽ sẽ không giữ được sự hợp nhất (C sẽ bị từ chối trên đỉnh cao nhất & mới nhất).
Alois Mahdal

23

KHÔNG sử dụng nguồn gốc git đẩy --mirror DƯỚI MỌI BẤT CỨ ĐIỀU KIỆN NÀO.

Nó không hỏi bạn có chắc chắn muốn làm điều này không, và tốt hơn là bạn chắc chắn, bởi vì nó sẽ xóa tất cả các nhánh từ xa không có trên hộp cục bộ của bạn.

http://twitter.com/dysinger/status/1273652486


6
Hoặc không làm những việc mà bạn không chắc chắn kết quả sẽ ra sao? Một máy tôi đã sử dụng để quản trị viên có Instructions to this machine may lead to unintended consequences, loss of work/data, or even death (at the hands of the sysad). Remember that you are solely responsible for the consequences of your actions trong Bộ GTVT.
richo

sử dụng nó nếu bạn có một repo được nhân đôi (mặc dù trong trường hợp của tôi, nó hiện được thực thi bởi một người dùng đặc biệt tại repo nguồn trên hook sau nhận)
prusswan

14

Tôi có một câu hỏi sau khi đọc lời giải thích của bạn: Có thể là bạn chưa bao giờ làm

git checkout master
git pull origin
git checkout my_new_feature

trước khi thực hiện 'git rebase / merge master' trong nhánh tính năng của bạn?

Bởi vì chi nhánh chính của bạn sẽ không cập nhật tự động từ kho lưu trữ của bạn bè. Bạn phải làm điều đó với git pull origin. Có lẽ bạn sẽ luôn luôn nổi loạn từ một nhánh chủ địa phương không bao giờ thay đổi? Và sau đó đến thời gian đẩy, bạn đang đẩy vào một kho lưu trữ có (cục bộ) cam kết bạn chưa từng thấy và do đó việc đẩy không thành công.


13

Trong tình huống của bạn tôi nghĩ rằng đối tác của bạn là chính xác. Điều tuyệt vời khi nổi loạn là với người ngoài cuộc, những thay đổi của bạn trông giống như tất cả đều xảy ra theo một trình tự rõ ràng. Điều này có nghĩa là

  • những thay đổi của bạn rất dễ xem xét
  • bạn có thể tiếp tục thực hiện các cam kết nhỏ, đẹp và tuy nhiên bạn có thể đặt các bộ cam kết đó thành công khai (bằng cách hợp nhất thành chủ) cùng một lúc
  • Khi bạn nhìn vào nhánh chính công cộng, bạn sẽ thấy một loạt các cam kết khác nhau cho các tính năng khác nhau của các nhà phát triển khác nhau nhưng tất cả chúng sẽ không được trộn lẫn với nhau

Bạn vẫn có thể tiếp tục đẩy chi nhánh phát triển riêng của mình đến kho lưu trữ từ xa để sao lưu nhưng những người khác không nên coi đó là một nhánh "công khai" vì bạn sẽ nổi loạn. BTW, một lệnh dễ dàng để làm điều này làgit push --mirror origin .

Bài viết Phần mềm đóng gói sử dụng Git thực hiện một công việc khá hay để giải thích sự đánh đổi trong việc hợp nhất so với việc nổi loạn. Đó là một bối cảnh hơi khác nhau nhưng các hiệu trưởng đều giống nhau - về cơ bản nó phụ thuộc vào việc các chi nhánh của bạn là công khai hay riêng tư và cách bạn dự định tích hợp chúng vào tuyến chính.


1
Liên kết đến phần mềm Đóng gói bằng git không hoạt động nữa. Tôi không thể tìm thấy một liên kết tốt để chỉnh sửa câu trả lời ban đầu.
Chetan

Bạn không nên phản chiếu origin, bạn nên phản chiếu đến kho lưu trữ dự phòng chuyên dụng thứ ba.
Miral

12

Dù sao, tôi đã theo dõi quy trình làm việc của mình trên một chi nhánh gần đây và khi tôi cố gắng hợp nhất nó trở lại thành chủ, tất cả đã đi vào địa ngục. Có hàng tấn xung đột với những thứ không nên quan trọng. Những mâu thuẫn chỉ vô nghĩa với tôi. Tôi mất một ngày để sắp xếp mọi thứ, và cuối cùng lên đến đỉnh điểm buộc phải đẩy chủ nhân từ xa, vì chủ địa phương của tôi đã giải quyết xong mọi mâu thuẫn, nhưng người ở xa vẫn không vui.

Trong cả quy trình làm việc của đối tác cũng như đề xuất của bạn, bạn sẽ không gặp phải những xung đột không có ý nghĩa. Ngay cả khi bạn đã có, nếu bạn đang theo dõi các quy trình công việc được đề xuất thì sau khi giải quyết, không cần phải có lực đẩy 'bắt buộc'. Điều đó cho thấy rằng bạn chưa thực sự hợp nhất chi nhánh mà bạn đang đẩy, nhưng đã phải đẩy một chi nhánh không phải là hậu duệ của mẹo từ xa.

Tôi nghĩ bạn cần xem xét kỹ những gì đã xảy ra. Người khác có thể (cố tình hay không) lặp lại nhánh chính từ xa giữa việc bạn tạo nhánh cục bộ và điểm mà bạn đã cố gắng hợp nhất nó trở lại vào nhánh cục bộ?

So với nhiều hệ thống kiểm soát phiên bản khác mà tôi thấy rằng việc sử dụng Git liên quan đến việc chống lại công cụ ít hơn và cho phép bạn xử lý các vấn đề cơ bản đối với các luồng nguồn của mình. Git không thực hiện phép thuật, do đó, các thay đổi xung đột gây ra xung đột, nhưng nó sẽ giúp bạn dễ dàng thực hiện việc viết bằng cách theo dõi dấu ngoặc đơn cam kết.


Bạn đang ám chỉ rằng OP có một số rebase chưa được phát hiện hoặc sai lầm trong quy trình của mình, phải không?
krosenvold

8

"Ngay cả khi bạn là một nhà phát triển duy nhất chỉ có một vài chi nhánh, bạn vẫn nên tập thói quen sử dụng rebase và hợp nhất đúng cách. Mẫu công việc cơ bản sẽ như sau:

  • Tạo nhánh B mới từ nhánh A hiện có

  • Thêm / cam kết thay đổi trên nhánh B

  • Rebase cập nhật từ chi nhánh A

  • Hợp nhất các thay đổi từ nhánh B lên nhánh A "

https://www.atlassian.com/git/tutorials/merging-vs-rebasing/


7

Từ những gì tôi đã quan sát, git merge có xu hướng giữ các nhánh tách biệt ngay cả sau khi hợp nhất, trong khi rebase sau đó hợp nhất kết hợp nó thành một nhánh duy nhất. Cái sau xuất hiện sạch hơn nhiều, trong khi trước đây, sẽ dễ dàng hơn để tìm ra những cam kết thuộc về nhánh nào ngay cả sau khi sáp nhập.


4

Với Git, không có luồng công việc chính xác nào. Sử dụng bất cứ điều gì nổi thuyền của bạn. Tuy nhiên, nếu bạn liên tục gặp xung đột khi sáp nhập các chi nhánh, có lẽ bạn nên phối hợp nỗ lực của mình tốt hơn với (các) nhà phát triển đồng nghiệp của mình? Âm thanh như hai bạn tiếp tục chỉnh sửa các tập tin giống nhau. Ngoài ra, hãy coi chừng các từ khóa khoảng trắng và lật đổ (ví dụ: Id $ Id $ USD và các từ khóa khác).


0

Tôi chỉ sử dụng quy trình làm việc rebase, vì nó rõ ràng hơn (không chỉ trong GitKraken, mà còn trong Intellij và trong gitk, nhưng tôi khuyên bạn nên sử dụng công cụ đầu tiên): bạn có một nhánh, nó bắt nguồn từ chủ và nó quay trở lại chính . Khi sơ đồ sạch và đẹp, bạn sẽ biết rằng không có gì đi xuống địa ngục .

nhập mô tả hình ảnh ở đây

Quy trình làm việc của tôi gần giống với quy trình của bạn, nhưng chỉ có một điểm khác biệt nhỏ: Tôi squashcam kết thành một trong chi nhánh địa phương trước khi rebasechi nhánh của tôi thay đổi mới nhất master, bởi vì:

rebasehoạt động trên cơ sở từng cam kết

có nghĩa là, nếu bạn có 15 cam kết thay đổi cùng một dòng với master , bạn phải kiểm tra 15 lần nếu bạn không ép, nhưng điều quan trọng là kết quả cuối cùng, phải không?

Vì vậy, toàn bộ quy trình làm việc là:

  1. Thanh toán mastervà kéo để đảm bảo rằng bạn có phiên bản mới nhất

  2. Từ đó, tạo một chi nhánh mới

  3. Làm công việc của bạn ở đó, bạn có thể tự do cam kết nhiều lần, và đẩy ra xa, không phải lo lắng, vì đó là chi nhánh của bạn .

  4. Nếu ai đó nói với bạn, "này, PR / MR của tôi đã được phê duyệt, bây giờ nó được hợp nhất thành chủ", bạn có thể lấy chúng / kéo chúng. Bạn có thể làm điều đó bất cứ lúc nào, hoặc trong bước 6.

  5. Sau khi thực hiện tất cả công việc của bạn, hãy cam kết chúng và nếu bạn có một số cam kết, hãy xóa chúng (tất cả đều là công việc của bạn và việc bạn thay đổi một dòng mã bao nhiêu lần không quan trọng; điều quan trọng duy nhất là phiên bản cuối cùng). Đẩy nó hay không, nó không thành vấn đề.

  6. Thanh toán master, pullmột lần nữa để đảm bảo bạn có masterđịa phương mới nhất . Sơ đồ của bạn phải giống như thế này:

nhập mô tả hình ảnh ở đây

Như bạn có thể thấy, bạn đang ở chi nhánh địa phương, bắt nguồn từ tình trạng lỗi thời master, trong khi master(cả địa phương và từ xa) đã tiến lên phía trước với những thay đổi của đồng nghiệp của bạn.

  1. Kiểm tra lại chi nhánh của bạn và rebase thành chủ. Bây giờ bạn sẽ chỉ có một cam kết, vì vậy bạn chỉ giải quyết xung đột một lần . (Và trong GitKraken, bạn chỉ phải kéo chi nhánh của mình vào mastervà chọn "Rebase"; một lý do khác khiến tôi thích nó.) Sau đó, bạn sẽ giống:

nhập mô tả hình ảnh ở đây

  1. Vì vậy, bây giờ, bạn có tất cả các thay đổi mới nhất master, kết hợp với các thay đổi trên chi nhánh của bạn. Bây giờ bạn có thể đẩy vào điều khiển từ xa của bạn, và, nếu bạn đã đẩy trước đó, bạn sẽ phải buộc đẩy; Git sẽ cho bạn biết rằng bạn không thể đơn giản nhanh chóng chuyển tiếp. Điều đó là bình thường, vì cuộc nổi loạn, bạn đã thay đổi điểm bắt đầu của chi nhánh. Nhưng bạn không nên sợ hãi: sử dụng vũ lực một cách khôn ngoan . Cuối cùng, điều khiển từ xa cũng là chi nhánh của bạn để bạn không ảnh hưởng masterngay cả khi bạn làm sai điều gì đó.

  2. Tạo PR / MR và đợi cho đến khi được phê duyệt, do đó mastersẽ có đóng góp của bạn. Chúc mừng! Vì vậy, bây giờ bạn có thể kiểm tra master, kéo các thay đổi của bạn và xóa chi nhánh địa phương của bạn để dọn sạch sơ đồ. Chi nhánh từ xa cũng nên bị xóa, nếu điều này không được thực hiện khi bạn hợp nhất nó thành chủ.

Sơ đồ cuối cùng là sạch sẽ và rõ ràng một lần nữa:

nhập mô tả hình ảnh ở đây

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.