nhánh chính và 'nguồn gốc / chủ' đã chuyển hướng, làm thế nào để 'phân chia' nhánh '?


965

Bằng cách nào đó chủ của tôi và chi nhánh gốc / chủ của tôi đã chuyển hướng.
Tôi thực sự không muốn họ phân kỳ.

Làm cách nào tôi có thể xem những khác biệt này và 'hợp nhất' chúng?


2
bạn có ý nghĩa gì khi chuyển hướng? Bạn có phản đối chủ của bạn sau khi đẩy nó?
hasen

13
Tôi nhận được một thông báo nói rằng "Chi nhánh và 'nguồn gốc / chủ' của bạn đã chuyển hướng, tương ứng # và có 1 và 1 cam kết khác nhau."
Frank

Tôi đã cập nhật câu trả lời của mình để phản ánh thông điệp cảnh báo "chuyển hướng" đó.
VonC

Câu trả lời được chấp nhận cho một câu hỏi khác cũng có thể hữu ích trong việc giải quyết một số trường hợp mà điều này có thể xảy ra (ví dụ: bạn đang cố gắng di chuyển chủ của mình, nhưng nó đã bị đẩy): stackoverflow.com/questions/2862590/
lindes

8
Lời giải thích tại blog này đã giúp tôi vô cùng nhiều hơn bất kỳ câu trả lời nào dưới đây: sebgoo.blogspot.com/2012/02/ mẹo

Câu trả lời:


1014

Bạn có thể xem lại sự khác biệt bằng:

git log HEAD..origin/master

trước khi kéo nó (tìm nạp + hợp nhất) (xem thêm "Làm thế nào để bạn có được git để luôn kéo từ một nhánh cụ thể?" )


Khi bạn có một tin nhắn như:

"Chi nhánh của bạn và 'origin / master' đã phân kỳ, # và có 1 và 1 cam kết khác nhau tương ứng."

, kiểm tra nếu bạn cần cập nhậtorigin . Nếu originđược cập nhật, thì một số cam kết đã được đẩy sang origintừ một repo khác trong khi bạn thực hiện các cam kết của riêng mình tại địa phương.

... o ---- o ---- A ---- B  origin/master (upstream work)
                   \
                    C  master (your work)

Bạn dựa trên cam kết C trên cam kết A vì đó là công việc mới nhất mà bạn đã tìm nạp từ thượng nguồn tại thời điểm đó.

Tuy nhiên, trước khi bạn cố gắng đẩy về nguồn gốc, một người khác đã đẩy cam kết B.
Lịch sử phát triển đã chuyển hướng thành các đường dẫn riêng biệt.

Sau đó bạn có thể hợp nhất hoặc rebase. Xem Pro Git: Git Branching - Rebasing để biết chi tiết.

Hợp nhất

Sử dụng lệnh git merge:

$ git merge origin/master

Điều này nói với Git để tích hợp các thay đổi từ origin/mastervào công việc của bạn và tạo một cam kết hợp nhất.
Biểu đồ của lịch sử bây giờ trông như thế này:

... o ---- o ---- A ---- B  origin/master (upstream work)
                   \      \
                    C ---- M  master (your work)

Hợp nhất mới, cam kết M, có hai cha mẹ, mỗi cha mẹ đại diện cho một con đường phát triển dẫn đến nội dung được lưu trữ trong cam kết đó.

Lưu ý rằng lịch sử đằng sau M bây giờ là phi tuyến tính.

Nổi loạn

Sử dụng lệnh git rebase:

$ git rebase origin/master

Điều này nói với Git phát lại cam kết C (công việc của bạn) như thể bạn đã dựa trên cam kết B thay vì A.
CVS và người dùng Subversion thường xuyên phản hồi các thay đổi cục bộ của họ trên đầu công việc ngược dòng khi họ cập nhật trước khi cam kết.
Git chỉ thêm phân tách rõ ràng giữa các bước cam kết và rebase.

Biểu đồ của lịch sử bây giờ trông như thế này:

... o ---- o ---- A ---- B  origin/master (upstream work)
                          \
                           C'  master (your work)

Cam kết C 'là một cam kết mới được tạo bởi lệnh git rebase.
Nó khác với C theo hai cách:

  1. Nó có một lịch sử khác: B thay vì A.
  2. Nội dung của nó chiếm những thay đổi ở cả B và C; nó giống như M từ ví dụ hợp nhất.

Lưu ý rằng lịch sử đằng sau C 'vẫn là tuyến tính.
Chúng tôi đã chọn (bây giờ) chỉ cho phép lịch sử tuyến tính trong cmake.org/cmake.git.
Cách tiếp cận này bảo tồn quy trình làm việc dựa trên CVS được sử dụng trước đây và có thể dễ dàng chuyển đổi.
Một nỗ lực để đẩy C 'vào kho lưu trữ của chúng tôi sẽ hoạt động (giả sử bạn có quyền và không ai bị đẩy trong khi bạn đang khởi động lại).

Lệnh git pull cung cấp một cách tốc ký để tìm nạp từ nguồn gốc và khởi động lại công việc cục bộ trên nó:

$ git pull --rebase

Điều này kết hợp các bước tìm nạp và rebase ở trên thành một lệnh.


5
Tôi đã tìm thấy điều này trong khi tìm kiếm cùng một vấn đề, bạn có thể giải thích lý do tại sao 'git reset - hard HEAD' không khắc phục được vấn đề không?
Neth

12
@Neth: bởi vì nó không phải là về sửa đổi theo giai đoạn (tức là sửa đổi có trong chỉ mục nhưng chưa được cam kết), mà là về các cam kết cục bộ (khác với các cam kết hiện diện trên điều khiển từ xa). git reset --hard HEADsẽ chỉ xóa bất kỳ sửa đổi không cam kết được lập chỉ mục cục bộ nào và sẽ không làm gì để điều hòa sự khác biệt giữa các cam kết cục bộ và từ xa . Chỉ một sự hợp nhất hoặc một cuộc nổi loạn sẽ mang lại hai tập hợp các cam kết (một cục bộ và một từ xa) cùng nhau.
VonC

4
Wow, cảm ơn vì phản ứng tuyệt vời này. Chúng tôi đã vô tình thực hiện "git pull" mà không có "--rebase" và "git rebase origin / master" chỉ là cách khắc phục!
mrooney

3
Làm thế nào về - Tôi chỉ muốn bỏ qua / kết xuất các thay đổi cục bộ của tôi và ở với chi nhánh địa phương của tôi, nơi điều khiển từ xa? Nói cách khác, tôi muốn masterchỉ ra Bví dụ của bạn.
CygnusX1

23
@ CygnusX1 sẽ là một câu git reset --hard origin/masternhư được đề cập trong câu trả lời ngay bên dưới: stackoverflow.com/a/8476004/6309
VonC

726

Tôi đã có điều này và đang bối rối về những gì đã gây ra nó, ngay cả sau khi đọc các phản hồi trên. Giải pháp của tôi là làm

git reset --hard origin/master

Sau đó, nó chỉ đặt lại bản sao chính (cục bộ) của tôi (mà tôi giả sử đã được vặn lên) đến đúng điểm, như được biểu thị bằng (gốc) gốc / từ xa.

CẢNH BÁO : Bạn sẽ mất tất cả các thay đổi chưa được đẩy tới origin/master.


21
vâng, nó có cảm giác hơi giống với tùy chọn người giả, nhưng nếu không có nguy hiểm thực sự và bạn đang ở đây để khắc phục nhanh - cách này vẫn hiệu quả (đối với tôi)
PandaWood

7
Điều này đòi hỏi phải ở trên nhánh chính trước ("git checkout master").
xanh lam

Điều này đã xảy ra với tôi một lần khi tôi nhận được thông tin mới nhất từ ​​nguồn gốc, sau đó một người khác đã đẩy mạnh nguồn gốc khiến cho cam kết trước đó về nguồn gốc được hoàn nguyên. Vì vậy, chi nhánh địa phương của tôi đã có cam kết hoàn nguyên, và khi tôi cố gắng rút mới nhất từ ​​nguồn gốc, nó nói rằng tôi phải hợp nhất. Trong trường hợp này, chỉ cần thiết lập lại - gốc nguồn / (nhánh) vì vấn đề đã được khắc phục trong nguồn gốc.
Landon Poch

96
Có lẽ bạn nên cảnh báo người dùng rằng điều này sẽ khiến họ mất tất cả các thay đổi chưa được đẩy về nguồn gốc
Pedro Loureiro

6
@PedroLoureiro Cam kết thực sự bị mất, bạn vẫn có thể tìm thấy các cam kết với git refloghoặc xem chúng trong gitk --all. Tuy nhiên, tất nhiên, thiết lập lại cứng là một điều khác hơn là một rebase.
sebkraTable 11/03/2016

52
git pull --rebase origin/master 

là một lệnh duy nhất có thể giúp bạn hầu hết thời gian.

Chỉnh sửa: Kéo các xác nhận từ nguồn gốc / bản gốc và áp dụng các thay đổi của bạn theo lịch sử chi nhánh mới được kéo.


89
vui lòng đề cập đến những gì lệnh này, mọi người có thể chạy nó và kết thúc việc này
Baz1nga

1
Nếu không có vấn đề gì, bạn nên kết thúc với chủ của mình chứa tất cả các thay đổi gốc / chủ cộng với tất cả các cam kết cục bộ của bạn sẽ được phát lại trên đầu trang. Có vẻ tốt với tôi.
Philipp Claßen

7
Ngoại trừ khi có sự khác biệt thực sự và nó khiến bạn rơi vào một cuộc nổi loạn bị hủy bỏ.
ffledgling

Điều này dẫn đến một lỗi: lỗi: Không thể hợp nhất trong các thay đổi. Bản vá không thành công tại 0024 Mô hình Yêu cầu và Phản hồi
IgorGanapolsky

32

Tôi thấy mình trong tình huống này khi tôi đã cố gắng để rebase một chi nhánh đã được theo dõi một chi nhánh từ xa, và tôi đã cố gắng để rebase nó trên tổng thể. Trong trường hợp này nếu bạn cố gắng nổi loạn, rất có thể bạn sẽ thấy chi nhánh của mình bị chuyển hướng và nó có thể tạo ra một mớ hỗn độn không dành cho git nubees!

Giả sử bạn đang ở chi nhánh my_remote_tracking_branch, được phân nhánh từ chủ

$ git status

# Trên nhánh my_remote_tracking_branch

không có gì để cam kết (thư mục làm việc sạch sẽ)

Và bây giờ bạn đang cố gắng để nổi loạn từ chủ như:

git rebase chủ

DỪNG NGAY và tiết kiệm cho mình một số rắc rối! Thay vào đó, sử dụng hợp nhất như:

chủ git merge

Có, bạn sẽ kết thúc với các cam kết thêm trên chi nhánh của bạn. Nhưng trừ khi bạn sẵn sàng cho các nhánh "không phân kỳ", đây sẽ là một quy trình công việc trơn tru hơn nhiều so với việc khởi động lại. Xem blog này để được giải thích chi tiết hơn nhiều.

Mặt khác, nếu chi nhánh của bạn chỉ là một chi nhánh địa phương (tức là chưa được đẩy đến bất kỳ điều khiển từ xa nào), bạn chắc chắn nên thực hiện một rebase (và chi nhánh của bạn sẽ không phân kỳ trong trường hợp này).

Bây giờ nếu bạn đang đọc bài viết này bởi vì bạn đã một "tách ra" kịch bản do rebase như vậy, bạn có thể trở lại các cam kết cuối cùng từ gốc (tức là trong tình trạng bỏ tách ra) bằng cách sử dụng:

thiết lập lại git - nguồn gốc gốc / my_remote_tracking_branch


5
Một nguyên tắc nhỏ là sử dụng rebasenếu nhánh bạn đang nổi loạn chưa được công bố (và được người khác sử dụng). Nếu không, sử dụng merge. Nếu bạn rebase các nhánh đã được xuất bản (và đã sử dụng), bạn phải phối hợp một âm mưu để viết lại lịch sử trên mọi nhà phát triển đã sử dụng chi nhánh của bạn.
Mikko Rantalainen

1
Thật không may, tôi đã không đọc tin nhắn này trước khi thực hiện git rebase master...
Vitaly Isaev

Nếu tôi thực hiện git rebase master khi ở nhánh 'foobar' thì về mặt kỹ thuật foobar sẽ được chuyển hướng từ origin / foobar cho đến khi tôi thực hiện git push -f, phải không?
tái hiện


1
git reset --hard origin/my_remote_tracking_branchlà những gì thực sự làm việc
roothahn

24

Trong trường hợp của tôi, đây là những gì tôi đã làm để gây ra thông điệp chuyển hướng: Tôi đã làm git pushnhưng sau đó đã làm git commit --amendđể thêm một cái gì đó vào thông điệp cam kết. Sau đó tôi cũng đã làm một cam kết khác.

Vì vậy, trong trường hợp của tôi, điều đó chỉ có nghĩa là nguồn gốc / chủ đã hết hạn. Bởi vì tôi biết không có ai khác chạm vào nguồn gốc / chủ, cách khắc phục là không đáng kể: git push -f ( -fcó nghĩa là lực lượng)


8
+1 git push -fđể ghi đè lên các thay đổi đã cam kết trước đó và được đẩy về nguồn gốc. Tôi cũng chắc chắn không ai khác chạm vào kho lưu trữ.
zacharydl

4
Lệnh rất mạo hiểm. Vui lòng viết một thông tin ngắn liên quan đến yếu tố rủi ro của lệnh.
J4cK

1
@Trickster: Tôi đã mô tả rủi ro: "vì tôi biết không ai khác chạm vào nguồn gốc / chủ nhân". Tôi tin rằng, trong trường hợp đó, đây không phải là một lệnh rủi ro.
Darren Cook

1
Nếu ai đó cam kết với chủ và sau đó một người chạy lệnh git push -f thì đó là lệnh có rủi ro cao
J4cK

11

Trong trường hợp của tôi, tôi đã đẩy các thay đổi sang origin/mastervà sau đó nhận ra rằng tôi không nên làm như vậy :-( Điều này rất phức tạp bởi thực tế là các thay đổi cục bộ nằm trong một cây con. Vì vậy, tôi đã quay lại cam kết tốt cuối cùng trước địa phương "xấu" thay đổi (sử dụng SourceTree) và sau đó tôi nhận được "thông báo phân kỳ".

Sau khi sửa lỗi cục bộ của tôi cục bộ (các chi tiết không quan trọng ở đây), tôi muốn "quay ngược thời gian" origin/masterchi nhánh từ xa để nó sẽ đồng bộ lại với cục bộ master. Giải pháp trong trường hợp của tôi là:

git push origin master -f

Lưu ý công tắc -f(lực). Điều này đã xóa "những thay đổi xấu" đã bị đẩy sang origin/masternhầm lẫn và bây giờ các chi nhánh địa phương và từ xa đã đồng bộ.

Xin lưu ý rằng đây là một hoạt động có khả năng phá hủy, vì vậy chỉ thực hiện nếu bạn chắc chắn 100% rằng "di chuyển trở lại" chủ thời gian từ xa là ổn.


Luôn luôn hữu ích nhưng chắc chắn không trả lời câu hỏi.
Thibault D.

1
@TrictaultD. ngay cả khi nó không, đây chính xác là những gì tôi đang tìm kiếm.
Neil Chowdhury

Tôi đang nhận được You are not allowed to force push code to a protected branch on this project.. Tôi đang cố gắng đẩy đến ngã ba của tôi.
BanAnanas

Tôi đã phải gỡ bỏ bảo vệ trên gitlab repo stackoverflow.com/questions/32246503/
Khăn

5

Tôi biết có rất nhiều câu trả lời ở đây, nhưng tôi nghĩ rằng nó git reset --soft HEAD~1đáng được chú ý, bởi vì nó cho phép bạn giữ những thay đổi trong cam kết cục bộ (không bị đẩy) trong khi giải quyết trạng thái chuyển hướng. Tôi nghĩ rằng đây là một giải pháp linh hoạt hơn so với kéo rebase, bởi vì cam kết cục bộ có thể được xem xét và thậm chí chuyển sang chi nhánh khác.

Chìa khóa đang sử dụng --soft, thay vì khắc nghiệt --hard. Nếu có nhiều hơn 1 cam kết, một biến thể HEAD~xsẽ hoạt động. Vì vậy, đây là tất cả các bước giải quyết tình huống của tôi (tôi đã có 1 cam kết cục bộ và 8 cam kết trong điều khiển từ xa):

1) git reset --soft HEAD~1 để hoàn tác cam kết địa phương. Đối với các bước tiếp theo, tôi đã sử dụng giao diện trong SourceTree, nhưng tôi nghĩ các lệnh sau cũng sẽ hoạt động:

2) git stash để stash thay đổi từ 1). Bây giờ tất cả các thay đổi đều an toàn và không còn phân kỳ nữa.

3) git pull để có được những thay đổi từ xa.

4) git stash pop hoặc git stash applyđể áp dụng các thay đổi được lưu trữ cuối cùng, theo sau là một cam kết mới, nếu muốn. Bước này là tùy chọn, cùng với 2) , khi muốn dọn rác các thay đổi trong cam kết cục bộ. Ngoài ra, khi muốn cam kết với một chi nhánh khác, bước này nên được thực hiện sau khi chuyển sang chi nhánh mong muốn.


Trên thực tế, những ngày này, pull --rebasedù sao đi nữa, stash sẽ tự động. stackoverflow.com/a/30209750/6309
VonC

4

Để xem sự khác biệt:

git difftool --dir-diff master origin/master

Điều này sẽ hiển thị những thay đổi hoặc khác biệt giữa hai nhánh. Trong araxis (Yêu thích của tôi), nó hiển thị nó theo kiểu khác thư mục. Hiển thị từng tệp đã thay đổi. Sau đó tôi có thể nhấp vào một tệp để xem chi tiết về các thay đổi trong tệp.


1
Cách sử dụng thú vị của git-dir: +1
VonC

3

Trong trường hợp của tôi, điều này là do không cam kết giải quyết xung đột của tôi.

Vấn đề là do chạy git pulllệnh. Thay đổi trong nguồn gốc dẫn đến xung đột với repo địa phương của tôi, mà tôi đã giải quyết. Tuy nhiên, tôi đã không cam kết chúng. Giải pháp tại thời điểm này là cam kết các thay đổi ( git committệp đã giải quyết)

Nếu bạn cũng đã sửa đổi một số tệp kể từ khi giải quyết xung đột, git statuslệnh sẽ hiển thị các sửa đổi cục bộ dưới dạng sửa đổi cục bộ không được chỉnh sửa và hợp nhất độ phân giải như sửa đổi cục bộ theo giai đoạn. Điều này có thể được giải quyết một cách chính xác bằng cách cam kết các thay đổi từ hợp nhất trước git commit, sau đó thêm và cam kết các thay đổi chưa được xử lý như bình thường (ví dụ: bởi git commit -a).


0

Tôi đã có cùng một thông báo khi tôi đang cố gắng chỉnh sửa tin nhắn cam kết cuối cùng, về cam kết đã được đẩy, bằng cách sử dụng: git commit --amend -m "New message" Khi tôi đẩy các thay đổi bằng cách sử dụng git push --force-with-lease repo_name branch_name không có vấn đề gì.


0

Gặp vấn đề này khi tôi tạo một nhánh dựa trên nhánh A bằng

git checkout -b a

và sau đó tôi thiết lập luồng lên của nhánh a thành nhánh B

git branch -u origin/B

Sau đó tôi nhận được thông báo lỗi ở trên.

Một cách để giải quyết vấn đề này cho tôi là,

  • Xóa chi nhánh a
  • Tạo một nhánh mới b bằng
git checkout -b b origin/B
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.