Git đẩy bị từ chối sau khi rebase nhánh tính năng


919

OK, tôi nghĩ rằng đây là một kịch bản git đơn giản, tôi còn thiếu gì?

Tôi có một masterchi nhánh và một featurechi nhánh. Tôi làm một số công việc master, một số featurevà sau đó một số nữa master. Tôi kết thúc với một cái gì đó như thế này (thứ tự từ điển ngụ ý thứ tự cam kết):

A--B--C------F--G  (master)
       \    
        D--E  (feature)

Tôi không có vấn đề gì để git push origin mastergiữ cho mastercập nhật từ xa , cũng như với git push origin feature(khi bật feature) để duy trì một bản sao lưu từ xa cho featurecông việc của tôi . Cho đến bây giờ, chúng tôi tốt.

Nhưng bây giờ tôi muốn nổi featureloạn trên đỉnh của các F--Gcam kết trên chủ, vì vậy tôi git checkout featuregit rebase master. Vẫn tốt. Bây giờ chúng tôi có:

A--B--C------F--G  (master)
                 \
                  D'--E'  (feature)

Vấn đề: thời điểm tôi muốn sao lưu phần khởi động mới được featurephân nhánh git push origin feature, lực đẩy bị từ chối vì cây đã thay đổi do việc khởi động lại. Điều này chỉ có thể được giải quyết với git push --force origin feature.

Tôi ghét sử dụng --forcemà không chắc chắn tôi cần nó. Vì vậy, tôi cần nó? Liệu cuộc nổi loạn có nhất thiết ngụ ý rằng kế tiếp pushnên là ưu tiên --force?

Chi nhánh tính năng này không được chia sẻ với bất kỳ nhà phát triển khác, vì vậy tôi không có vấn đề de facto với việc đẩy mạnh vũ lực, tôi sẽ không để mất bất kỳ dữ liệu, câu hỏi là khái niệm hơn.

Câu trả lời:


682

Vấn đề là git pushgiả định rằng chi nhánh từ xa có thể được chuyển nhanh đến chi nhánh địa phương của bạn, đó là tất cả sự khác biệt giữa các chi nhánh địa phương và từ xa là ở địa phương có một số cam kết mới như thế:

Z--X--R         <- origin/some-branch (can be fast-forwarded to Y commit)
       \        
        T--Y    <- some-branch

Khi bạn thực hiện các git rebasecam kết D và E được áp dụng cho cơ sở mới và các cam kết mới được tạo. Điều đó có nghĩa là sau khi rebase bạn có một cái gì đó như thế:

A--B--C------F--G--D'--E'   <- feature-branch
       \  
        D--E                <- origin/feature-branch

Trong tình huống đó, chi nhánh từ xa không thể được chuyển nhanh đến địa phương. Mặc dù, về mặt lý thuyết, chi nhánh địa phương có thể được hợp nhất vào điều khiển từ xa (rõ ràng bạn không cần nó trong trường hợp đó), nhưng khi git pushthực hiện chỉ hợp nhất chuyển tiếp nhanh, nó sẽ ném và lỗi.

--forcetùy chọn nào chỉ là bỏ qua trạng thái của nhánh từ xa và đặt nó vào cam kết mà bạn đang đẩy vào nó. Vì vậy, git push --force origin feature-branchchỉ cần ghi đè origin/feature-branchvới địa phương feature-branch.

Theo tôi, việc khởi động lại các nhánh tính năng mastervà đẩy chúng trở lại kho lưu trữ từ xa là được miễn là bạn là người duy nhất làm việc trên nhánh đó.


68
Thành thật mà nói, việc kéo và sáp nhập phiên bản gốc của nhánh tính năng vào một loại bị loại bỏ sẽ loại bỏ toàn bộ ý tưởng về việc nổi loạn.
KL-7

24
Có thể tôi đã không hiểu chính xác về bạn, nhưng nếu bạn kéo nhánh tính năng, khởi động lại nó vào nhánh chính mới, bạn không thể đẩy nó trở lại mà không bị ép buộc, bởi vì phiên bản từ xa của nhánh tính năng không thể được chuyển nhanh sang mới (nổi loạn) phiên bản của nhánh tính năng. Đó chính xác là những gì OP mô tả trong câu hỏi của anh ấy. Nếu sau khi khởi động lại, nhưng trước khi đẩy, bạn thực hiện git pull feature-branch, thao tác kéo này sẽ tạo ra một cam kết hợp nhất mới (bằng cách hợp nhất các phiên bản từ xa và cục bộ của nhánh tính năng). Vì vậy, hoặc bạn nhận được một sự hợp nhất không cần thiết sau khi nổi loạn, hoặc bạn đẩy theo --force.
KL-7

6
Ah, tôi nghĩ rằng tôi đã nhận nó. Bạn đang mô tả cách tiếp cận giống như trong câu trả lời của Mark Longair. Nhưng nó tạo ra một cam kết hợp nhất. Nó có thể hữu ích trong một số trường hợp, nhưng tôi sử dụng rebase chủ yếu trong các nhánh tính năng của riêng tôi (do đó push --forcekhông phải là vấn đề) để giữ lịch sử tuyến tính mà không có bất kỳ cam kết hợp nhất nào.
KL-7

11
Vấn đề với „lực đẩy đẩy là, bạn thực sự có thể„ thả lỏng các thứ (cam kết trước đó), một điều mà bình thường KHÔNG BAO GIỜ có thể có trong bất kỳ Hệ thống kiểm soát phiên bản nào Vì lý do đó, ít nhất phải có một „chi nhánh chính các cài đặt để không chấp nhận lực đẩy , để hạn chế thiệt hại tiềm tàng. (Đặt tên cho bất kỳ điều nào sau đây: nhân viên gắt gỏng / bị sa thải, ngốc nghếch, mệt mỏi và làm việc quá sức 'quyết định' ...).
Frank Nocke 6/11/2015

13
--force-with-leasenhư @hardev đề xuất là một lựa chọn tuyệt vời
augustorsouza

466

Thay vì sử dụng -f hoặc - Force, các nhà phát triển nên sử dụng

--force-with-lease

Tại sao? Bởi vì nó kiểm tra chi nhánh từ xa để biết những thay đổi hoàn toàn là một ý tưởng tốt. Chúng ta hãy tưởng tượng rằng James và Lisa đang làm việc trên cùng một nhánh tính năng và Lisa đã đưa ra một cam kết. James bây giờ nổi loạn chi nhánh địa phương của mình và bị từ chối khi cố gắng đẩy. Tất nhiên James nghĩ rằng điều này là do rebase và sử dụng - Force và sẽ viết lại tất cả những thay đổi của Lisa. Nếu James đã sử dụng - cưỡng bức cho thuê, anh ta sẽ nhận được một cảnh báo rằng có những cam kết được thực hiện bởi người khác. Tôi không thấy lý do tại sao mọi người sẽ sử dụng - lực lượng thay vì - lực lượng cho thuê khi đẩy lùi sau một cuộc nổi loạn.


33
Giải thích tuyệt vời. git push --force-with-leaseđã cứu tôi một loạt.
ckib16

5
Đây là một nhận xét hữu ích, nhưng thực sự không phải là một câu trả lời cho câu hỏi.
Dallin

4
Đây là câu trả lời, nổi loạn để làm chủ / phát triển tạo ra một vấn đề, đây chính xác là lý do tại sao - lực lượng cho thuê tồn tại.
Tamir Daniely

3
Đây phải là câu trả lời được chấp nhận. Giải quyết chính xác vấn đề được mô tả - đẩy mạnh mà không ép buộc nếu có người khác cam kết trong thời gian đó.
Luzian

3
Tôi nghĩ rằng cả câu trả lời được chấp nhận và điều này giải quyết câu hỏi. Câu trả lời được chấp nhận giải thích lý do tại sao bạn cần phải ép buộc. Và điều này giải thích lý do tại sao --force-with-leasegiải quyết mối quan tâm của việc sử dụng--force
Jeff Rõ ràng

48

Tôi sẽ sử dụng thay vì "thanh toán -b" và nó dễ hiểu hơn.

git checkout myFeature
git rebase master
git push origin --delete myFeature
git push origin myFeature

khi bạn xóa, bạn không thể đẩy vào một nhánh thoát có chứa ID SHA khác nhau. Tôi chỉ xóa chi nhánh từ xa trong trường hợp này.


6
Điều này hoạt động rất tốt, đặc biệt nếu nhóm của bạn có một git hook từ chối tất cả các lệnh git đẩy - lực.
Ryan Thames

1
cảm ơn vì điều đó đã làm việc tốt Dưới đây là chi tiết về tôi đọc để hiểu rõ hơn. Điều này rất hữu ích khi bạn không muốn hoặc không thể thực hiện một động tác đẩy. Xóa chi nhánh từ xarebasing
RajKon

5
Điều này có kết quả tương tự push --force, vì vậy chỉ là một cách để ngăn chặn một repo git ngăn chặn --force. Như vậy, tôi không nghĩ rằng đây là một ý tưởng hay - hoặc là repo cho phép push --force, hoặc vì một lý do chính đáng, nó vô hiệu hóa nó. Câu trả lời của Nabi phù hợp hơn nếu --forcebị vô hiệu hóa trên repo từ xa, vì nó không có nguy cơ mất các cam kết từ các nhà phát triển khác hoặc gây ra sự cố.
Pickup Logan

19

Một giải pháp cho vấn đề này là thực hiện những gì mà kịch bản hợp nhất nổi loạn của msysGit thực hiện - sau khi rebase, hợp nhất trong phần đầu cũ featurevới -s ours. Bạn kết thúc với biểu đồ cam kết:

A--B--C------F--G (master)
       \         \
        \         D'--E' (feature)
         \           /
          \       --
           \    /
            D--E (old-feature)

... và sự thúc đẩy của featurebạn sẽ là một bước tiến nhanh.

Nói cách khác, bạn có thể làm:

git checkout feature
git branch old-feature
git rebase master
git merge -s ours old-feature
git push origin feature

(Không được thử nghiệm, nhưng tôi nghĩ điều đó đúng ...)


26
Tôi tin rằng lý do phổ biến nhất để sử dụng git rebase(thay vì hợp nhất mastertrở lại vào nhánh tính năng của bạn) là tạo ra lịch sử cam kết tuyến tính sạch. Với cách tiếp cận của bạn cam kết lịch sử thậm chí còn tồi tệ hơn. Và khi rebasing tạo ra các cam kết mới mà không có bất kỳ tham chiếu nào đến các phiên bản trước của chúng, tôi thậm chí không chắc rằng kết quả của sự hợp nhất này sẽ đầy đủ.
KL-7

6
@ KL-7: Toàn bộ vấn đề merge -s ourslà nó bổ sung một cách giả tạo một tham chiếu cha cho phiên bản trước. Chắc chắn, lịch sử không có vẻ sạch sẽ, nhưng người hỏi dường như đặc biệt bận tâm khi phải buộc featurechi nhánh đẩy , và điều này xoay quanh vấn đề đó. Nếu bạn muốn rebase, nó ít nhiều là cái này hay cái khác. :) Nói chung, tôi nghĩ thật thú vị khi dự án msysgit thực hiện điều này ....
Mark Longair

@ KL-7: Ngẫu nhiên, tôi đã trả lời 1 câu trả lời của bạn, đây rõ ràng là câu trả lời đúng - Tôi chỉ nghĩ rằng điều này cũng có thể thú vị.
Mark Longair

Nó chắc chắn là thú vị, ít nhất là đối với tôi. Cảm ơn bạn. Tôi đã thấy ourschiến lược trước đây nhưng tôi nghĩ nó chỉ áp dụng cho tình huống xung đột bằng cách tự động giải quyết chúng bằng các thay đổi trong chi nhánh của chúng tôi. Hóa ra nó hoạt động khác nhau. Và hoạt động theo cách đó rất hữu ích nếu bạn cần phiên bản được khởi động lại (ví dụ: để người bảo trì repo áp dụng nó một cách sạch sẽ master) nhưng muốn tránh đẩy mạnh (nếu nhiều lý do khác đang sử dụng nhánh tính năng của bạn).
KL-7

15

Có thể có hoặc không có trường hợp chỉ có một nhà phát triển trên nhánh này, đó là bây giờ (sau khi rebase) không phù hợp với nguồn gốc / tính năng.

Vì vậy, tôi sẽ đề nghị sử dụng trình tự sau:

git rebase master
git checkout -b feature_branch_2
git push origin feature_branch_2

Vâng, chi nhánh mới, điều này sẽ giải quyết vấn đề này mà không cần một lực lượng, mà tôi nghĩ nói chung là một nhược điểm lớn của git.


3
Rất tiếc phải nói nhưng: „Tiếp tục tạo các chi nhánh, để tránh ép buộc các phần tử hiện có không giúp ích gì cho các nhà phát triển tính năng cô đơn (có thể ghi đè) cũng như nhiều người làm việc trên một nhánh tính năng (cần giao tiếp với chi nhánh đó. để di chuyển qua, folks). - Nó giống như phiên bản thủ công hơn (Luận án_00.doc, Luận văn_01.doc, ... phạm), trong một hệ thống phiên bản ...
Frank Nocke

2
Ngoài ra, điều này không giúp ích gì khi bạn mở PR github trên một tên chi nhánh, bạn phải tạo PR mới cho tên chi nhánh mới mà bạn đã đẩy.
gprasant

1
@frankee Một nửa đúng từ kinh nghiệm của tôi. Đối với một nhà phát triển cô đơn, ừ, chỉ cần đẩy mạnh là đủ dễ dàng, nhưng đó là thói quen có thể cắn bạn sau này. + một dev mới vừa tham gia? hoặc có thể một số hệ thống CI không sử dụng - thiết lập lại? Đối với một nhóm hợp tác, tôi nghĩ việc truyền đạt tên chi nhánh mới là đủ dễ dàng, điều này cũng có thể dễ dàng được viết theo kịch bản + đối với một nhóm, tôi sẽ đề nghị khởi động lại cục bộ hoặc khi chi nhánh sẵn sàng hợp nhất, không phải trong ngày làm việc hàng ngày , cam kết thêm ít gây rắc rối hơn là xử lý các xung đột rebase / merge do đó.
JAR.JAR.beans 6/10/2015

@gprasant cho PR, một lần nữa, tôi nghĩ rằng điều này sẽ sai khi bắt đầu lại, tôi thực sự muốn thấy các cam kết duy nhất với các bản sửa lỗi PR. Một rebase (squash) chỉ nên xảy ra sau đó như là một phần của việc hợp nhất thành chủ và khi PR hoàn tất và sẵn sàng (vì vậy không cần phải mở PR mới).
JAR.JAR.beans 6/10/2015

13

Cách của tôi để tránh lực đẩy là tạo ra một nhánh mới và tiếp tục làm việc trên nhánh mới đó và sau khi ổn định, loại bỏ nhánh cũ đã bị loại bỏ:

  • Rebasing chi nhánh đã kiểm tra tại địa phương
  • Chi nhánh từ chi nhánh nổi loạn sang chi nhánh mới
  • Đẩy nhánh đó như một nhánh mới vào điều khiển từ xa. và xóa chi nhánh cũ trên remote

1
Tại sao không có tình yêu cho lựa chọn này? Nó chắc chắn là sạch nhất, đơn giản nhất, an toàn nhất.
cdmo

Bởi vì tôi có khoảng 200 hệ thống theo dõi tên chi nhánh và nó phải là một tên cụ thể cho nhiệm vụ và nếu tôi bắt đầu thực hiện đổi tên chi nhánh, mỗi lần đẩy tôi sẽ mất trí.
Tamir Daniely

@TamirDaniely Tôi chưa thử, nhưng xóa chi nhánh cũ (từ xa) trước khi đẩy và đẩy chi nhánh mới có cùng tên cũ có giải quyết được vấn đề của bạn không?
Nabi

2
@Nabi Đó chính xác là những gì - cưỡng bức cho thuê, ngoại trừ nó cũng xác minh rằng không có cam kết mới nào không phải của bạn.
Tamir Daniely

12

Những người khác đã trả lời câu hỏi của bạn. Nếu bạn rebase một nhánh, bạn sẽ cần phải đẩy nhánh đó.

Rebase và một kho lưu trữ được chia sẻ thường không hợp nhau. Đây là viết lại lịch sử. Nếu những người khác đang sử dụng nhánh đó hoặc đã phân nhánh từ nhánh đó thì rebase sẽ khá khó chịu.

Nói chung, rebase hoạt động tốt cho quản lý chi nhánh địa phương. Quản lý chi nhánh từ xa hoạt động tốt nhất với sự hợp nhất rõ ràng (--no-ff).

Chúng tôi cũng tránh sáp nhập chủ vào một nhánh tính năng. Thay vào đó, chúng tôi rebase thành chủ nhưng với một tên nhánh mới (ví dụ: thêm hậu tố phiên bản). Điều này tránh được vấn đề nổi loạn trong kho lưu trữ được chia sẻ.


5
Bạn có thể thêm một ví dụ xin vui lòng?
Thermech

8

Điều gì là sai với một git merge mastertrên featurechi nhánh? Điều này sẽ bảo tồn công việc bạn đã có, trong khi giữ nó tách biệt với nhánh chính.

A--B--C------F--G
       \         \
        D--E------H

Chỉnh sửa: Ah xin lỗi đã không đọc tuyên bố vấn đề của bạn. Bạn sẽ cần lực khi bạn thực hiện a rebase. Tất cả các lệnh sửa đổi lịch sử sẽ cần --forceđối số. Đây là một sự không an toàn để ngăn bạn mất việc (cũ DE sẽ bị mất).

Vì vậy, bạn đã thực hiện một git rebasecái làm cho cây trông giống như (mặc dù bị ẩn một phần DEkhông còn trong một nhánh có tên):

A--B--C------F--G
       \         \
        D--E      D'--E'

Vì vậy, khi cố gắng đẩy featurechi nhánh mới của bạn (có D'E'trong đó), bạn sẽ thua DE.


3
Không có gì sai với điều đó, và tôi biết nó sẽ hoạt động. Đó không phải là thứ tôi cần. Như tôi đã nói, câu hỏi mang tính khái niệm nhiều hơn là thực tế.
Yuval Adam

4

Đối với tôi sau đây các bước dễ dàng làm việc:

1. git checkout myFeature
2. git rebase master
3. git push --force-with-lease
4. git branch -f master HEAD
5. git checkout master
6. git pull

Sau khi thực hiện tất cả các thao tác trên, chúng ta cũng có thể xóa nhánh myFeature bằng lệnh sau:

git push origin --delete myFeature

3

Các công việc sau đây cho tôi:

git push -f origin branch_name

và nó không loại bỏ bất kỳ mã nào của tôi.

Nhưng, nếu bạn muốn tránh điều này thì bạn có thể làm như sau:

git checkout master
git pull --rebase
git checkout -b new_branch_name

sau đó bạn có thể chọn tất cả các cam kết của mình cho chi nhánh mới. git cherry-pick COMMIT ID và sau đó đẩy chi nhánh mới của bạn.


5
-flà một bí danh cho --force, đó là những gì câu hỏi đang cố gắng tránh nếu có thể.
epochengine

1

Vì OP hiểu vấn đề, chỉ cần tìm giải pháp đẹp hơn ...

Làm thế nào về điều này như là một thực hành?

  • Có trên nhánh phát triển tính năng thực tế (nơi bạn không bao giờ nổi loạn và ép buộc, vì vậy các nhà phát triển tính năng đồng nghiệp của bạn không ghét bạn). Ở đây, thường xuyên lấy những thay đổi từ chính với một sự hợp nhất. Lịch sử lộn xộn , vâng, nhưng cuộc sống rất dễ dàng và không ai bị gián đoạn trong công việc của mình.

  • Có một nhánh phát triển tính năng thứ hai, trong đó một thành viên trong nhóm tính năng đẩy tất cả các tính năng cam kết, thực sự bị phản đối, thực sự bị ép buộc. Vì vậy, gần như sạch sẽ dựa trên một cam kết chủ gần đây. Khi tính năng hoàn tất, đẩy nhánh đó lên trên đỉnh của chủ.

Có thể có một tên mẫu cho phương thức nà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.