Tại sao Git nói chi nhánh chính của tôi "đã được cập nhật" mặc dù nó không phải là?


118

Vấn đề cơ bản

Tôi vừa xóa TẤT CẢ mã khỏi một tệp trong dự án của mình và cam kết thay đổi đối với git cục bộ của tôi (có chủ đích). tôi đã làm

git pull upstream master

để tìm nạp và hợp nhất từ ​​ngược dòng (vì vậy về lý thuyết, mã đã xóa phải được quay lại).

Git cho tôi biết mọi thứ đều được cập nhật.

Mọi thứ chắc chắn KHÔNG được cập nhật - tất cả mã đã xóa đó vẫn bị xóa.

Thông tin liên quan khác

Tôi chỉ có một nhánh gọi là "chủ".

Gần đây tôi đã thiết lập "chủ" để theo dõi ngược dòng như vậy:

Branch master được thiết lập để theo dõi branch master từ xa từ phía trên.

Lệnh git branch -vvcho kết quả:

* master 7cfcb29 [upstream/master: ahead 9] deletion test

Tại sao tại sao điều này lại xảy ra? Tôi đang chuẩn bị gửi email cho người quản lý dự án của mình bất kỳ thay đổi nào mà tôi thực hiện đối với mã của chúng tôi.

Cập nhật

Tôi nghĩ điều đó là hiển nhiên, nhưng dù sao đây cũng là mục tiêu của tôi:

Nhận mã mới nhất trên hệ thống của tôi.

Xin thứ lỗi cho sự tức giận của tôi ở đây, nhưng tại sao một nhiệm vụ đơn giản như vậy lại phải khó khăn như vậy?


1
Rõ ràng là nếu bạn xóa nguồn của mình thì hãy thử kéo bằng "pull" đơn giản rằng git sẽ không ghi đè các thay đổi cục bộ của bạn (trong trường hợp này là xóa của bạn) mà không đảm bảo rằng bạn thực sự muốn ghi đè các thay đổi cục bộ của mình. Câu trả lời đầu tiên dường như giải thích chính xác cách bạn có thể làm về nó.
CashCow

Câu trả lời:


209

Tôi nghĩ rằng vấn đề cơ bản của bạn ở đây là bạn đang hiểu sai và / hoặc hiểu sai chức năng của git và tại sao nó lại làm như vậy.

Khi bạn sao chép một số kho lưu trữ khác, git sẽ tạo một bản sao của bất kỳ thứ gì ở "đằng kia". Nó cũng lấy các nhãn nhánh "của họ", chẳng hạn như mastervà tạo một bản sao của nhãn đó có "tên đầy đủ" trong cây git của bạn là (bình thường) remotes/origin/master(nhưng trong trường hợp của bạn, remotes/upstream/master). Hầu hết thời gian bạn cũng có thể bỏ qua remotes/phần này, vì vậy bạn có thể xem bản sao gốc đó là upstream/master.

Nếu bây giờ bạn thực hiện và cam kết (các) thay đổi đối với (các) tệp, bạn là người duy nhất có những thay đổi đó. Trong khi đó những người khác có thể sử dụng kho lưu trữ gốc (từ đó bạn đã tạo bản sao của mình) để tạo các bản sao khác và thay đổi các bản sao đó. Tất nhiên, họ là những người duy nhất có thay đổi. Tuy nhiên, cuối cùng, ai đó có thể có những thay đổi mà họ gửi lại cho chủ sở hữu ban đầu (thông qua "push" hoặc các bản vá hoặc bất cứ thứ gì).

Các git pulllệnh được chủ yếu chỉ là viết tắt cho git fetchtiếp theo git merge. Điều này rất quan trọng vì nó có nghĩa là bạn cần hiểu hai thao tác đó thực sự làm gì.

Các git fetchlệnh nói quay trở lại bất cứ nơi nào bạn được sao chép từ (hoặc có nếu không thiết lập như là một nơi để tìm nạp từ) và tìm thấy "mới thứ người khác thêm vào hoặc thay đổi hoặc gỡ bỏ". Những thay đổi đó được sao chép và áp dụng cho bản sao của bạn những gì bạn nhận được từ chúng trước đó . Chúng không được áp dụng cho công việc của riêng bạn, chỉ áp dụng cho công việc của họ.

Các git merge lệnh phức tạp hơn và là nơi bạn sẽ bị thất bại. Những gì nó làm, được đơn giản hóa một chút, là so sánh "những gì bạn đã thay đổi trong bản sao của mình" với "những thay đổi bạn đã tìm nạp từ người khác và do đó được thêm vào tác phẩm của bạn". Nếu những thay đổi của bạn và những thay đổi của chúng dường như không mâu thuẫn với nhau, mergethao tác này sẽ tổng hợp chúng lại với nhau và cung cấp cho bạn một "cam kết hợp nhất" gắn kết sự phát triển của bạn và sự phát triển của chúng với nhau (mặc dù có một trường hợp rất phổ biến "dễ dàng" mà bạn không có thay đổi và bạn nhận được "tua đi nhanh").

Các tình huống mà bạn đang gặp phải hiện nay là một trong đó bạn đã thực hiện những thay đổi và cam kết họ chín lần, trên thực tế, vì thế mà "trước 9" -và họ đã thực hiện không thay đổi. Vì thế,fetch không lấy gì một cách nghiêm túc, rồi mergenhận lấy sự thiếu thay đổi của họ và cũng không làm gì cả.

Những gì bạn muốn là xem, hoặc thậm chí có thể "đặt lại" thành phiên bản mã "của họ".

Nếu bạn chỉ muốn xem nó, bạn có thể chỉ cần xem phiên bản đó:

git checkout upstream/master

Điều đó cho git biết rằng bạn muốn di chuyển thư mục hiện tại đến nhánh có tên đầy đủ thực sự là remotes/upstream/master. Bạn sẽ thấy mã của họ kể từ lần cuối cùng bạn chạy git fetchvà nhận được mã mới nhất của họ.

Nếu bạn muốn từ bỏ tất cả các thay đổi của riêng mình, điều bạn cần làm là thay đổi ý tưởng của git về việc masternên đặt tên cho bản sửa đổi mà nhãn của bạn . Hiện tại nó đặt tên cho cam kết gần đây nhất của bạn. Nếu bạn quay lại chi nhánh đó:

git checkout master

thì git resetlệnh sẽ cho phép bạn "di chuyển nhãn", như nó vốn có. Vấn đề còn lại duy nhất (giả sử bạn thực sự sẵn sàng từ bỏ tất cả những gì bạn đã không) là tìm nơi nhãn nên trỏ đến.

git logsẽ cho phép bạn tìm các tên số — những thứ như 7cfcb29— là tên vĩnh viễn (không bao giờ thay đổi) và có một số cách đặt tên khác vô lý, nhưng trong trường hợp này, bạn chỉ muốn có tên upstream/master.

Để di chuyển nhãn, hãy xóa sạch các thay đổi của chính bạn (bất kỳ thay đổi nào bạn đã cam kết thực sự có thể khôi phục được trong một thời gian nhưng sẽ khó hơn rất nhiều sau đó nên hãy rất chắc chắn):

git reset --hard upstream/master

Lệnh --hardcho git xóa sạch những gì bạn đang làm, di chuyển nhãn nhánh hiện tại và sau đó kiểm tra cam kết đã cho.

Việc thực sự muốn git reset --hardvà quét sạch một đống công việc không phải là điều quá phổ biến . Một phương pháp an toàn hơn (giúp khôi phục công việc đó dễ dàng hơn rất nhiều nếu bạn quyết định một số trong số đó là đáng giá) là đổi tên nhánh hiện có của bạn:

git branch -m master bunchofhacks

và sau đó tạo một nhánh cục bộ mới có tên masterlà "theo dõi" (tôi không thực sự thích thuật ngữ này vì tôi nghĩ nó gây nhầm lẫn cho mọi người nhưng đó là thuật ngữ git :-)) master gốc (hoặc ngược dòng):

git branch -t master upstream/master

mà sau đó bạn có thể tự làm với:

git checkout master

Những gì ba lệnh cuối cùng làm (có các phím tắt để biến nó thành chỉ hai lệnh) là thay đổi tên được dán trên nhãn hiện có, sau đó tạo một nhãn mới, sau đó chuyển sang tên đó:

trước khi làm bất cứ điều gì:

C0 -    "remotes/upstream/master"
    \
     \- C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 --- C8 --- C9    "master"

sau git branch -m:

C0 -    "remotes/upstream/master"
    \
     \- C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 --- C8 --- C9    "bunchofhacks"

sau git branch -t master upstream/master:

C0 -    "remotes/upstream/master", "master"
    \
     \- C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 --- C8 --- C9    "bunchofhacks"

Đây C0là cam kết mới nhất (một cây nguồn hoàn chỉnh) mà bạn nhận được khi lần đầu tiên thực hiện git clone. C1 đến C9 là cam kết của bạn.

Lưu ý rằng nếu bạn muốn git checkout bunchofhacksvà sau đó git reset --hard HEAD^^, điều này sẽ thay đổi hình ảnh cuối cùng thành:

C0 -    "remotes/upstream/master", "master"
    \
     \- C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 -    "bunchofhacks"
                                                      \
                                                       \- C8 --- C9

Lý do là HEAD^^đặt tên bản sửa đổi lên từ phần đầu của nhánh hiện tại (ngay trước khi đặt lại sẽ là bunchofhacks), và reset --hardsau đó di chuyển nhãn. Các cam kết C8 và C9 giờ đây hầu như không thể nhìn thấy (bạn có thể sử dụng những thứ như reflog và git fsckđể tìm chúng nhưng nó không còn tầm thường nữa). Nhãn của bạn là của bạn để di chuyển theo cách bạn muốn. Các fetchlệnh sẽ chăm sóc của những người mà bắt đầu với remotes/. Thông thường, so sánh "của bạn" với "của họ" (vì vậy nếu họ có remotes/origin/mauvetên, bạn sẽ đặt tên cho mìnhmauve cũng ), nhưng bạn có thể nhập "của họ" bất cứ khi nào bạn muốn đặt tên / xem cam kết mà bạn nhận được "từ họ". (Hãy nhớ rằng "một cam kết" là toàn bộ cây nguồn. Bạn có thể chọn một tệp cụ thể từ một cam kết, git showchẳng hạn như


12
Tôi đã học được rất nhiều từ câu trả lời xuất sắc của bạn. Nhưng tôi vẫn bối rối không hiểu tại sao tôi lại nhận được thông báo trạng thái git: "Chi nhánh của bạn được cập nhật với 'origin / master'" khi repo ngược dòng đi trước repo hiện tại của tôi vài lần. Tôi có thể cập nhật bằng git pull, nhưng làm cách nào để biết rằng tôi cần kéo nếu thông báo cho biết tôi đã cập nhật?
Christopher Werby

@ChristopherWerby: có vẻ như bạn đang chạy git mergedưới dạng lệnh dòng lệnh (đó là điều tôi tự làm, đó là một phương pháp hợp lý). Tuy nhiên, lưu ý rằng git pullbắt đầu bằng cách chạy trước git fetch, sau đó chạy git merge(hoặc git rebasenếu bạn yêu cầu nó làm điều đó thay thế). Đó là bước tìm nạp thực sự mang lại các cam kết mới được hợp nhất (hoặc chỉ phục hồi).

@torek Có lệnh nào khác git statusmà tôi có thể sử dụng để xem liệu repo ngược dòng có trước repo hiện tại của tôi không? Thông báo mà tôi nhận được với nội git statusdung "Chi nhánh của bạn được cập nhật với 'origin / master'" khiến tôi bối rối khi nghĩ rằng mình đã có những thay đổi mới nhất trong khi không có. Đôi khi, tôi nhận được một thông báo có nội dung như "origin / master is 5 commit from your branch" (paraphrasing). Nhưng nó không nhất quán.
Christopher Werby

1
Điều này vẫn chưa thực sự trả lời tại sao lại git statusnói rằng "Chi nhánh của bạn được cập nhật" khi ngay lập tức git fetchkéo xuống hơn một trăm đối tượng và giải quyết gần một trăm delta, đề cập đến một số nhánh mới và một vài thẻ mới và thay đổi chính (điều khiển từ xa theo dõi) cam kết của người đứng đầu chi nhánh - và sau đó là một trạng thái git khác một cách vui vẻ, và thật đáng ghét, vẫn cho biết "Chi nhánh của bạn đã được cập nhật". Rõ ràng có nhiều lô hàng đến từ nguồn gốc xuất xứ nhưng chi nhánh lại “có xuất xứ” cả trước và sau?
NeilG

1
git pullchạy git fetchtrước, sau đó git merge(hoặc lệnh thứ hai khác mà bạn chọn). Các git fetchbước cập nhật bạn nhớ của Git họ tình trạng của Git, bằng cách gọi lên họ Git và nhận được bất cứ điều gì mới từ họ. Của bạn git statuschỉ kiểm tra bộ nhớ Git của bạn về Git của họ.
torek

18

Tôi đã có vấn đề tương tự như bạn.

tôi đã làm git status git fetch git pull , nhưng chi nhánh của tôi vẫn ở phía sau nguồn gốc. Tôi có các thư mục và tệp được đẩy sang điều khiển từ xa và tôi thấy các tệp trên web, nhưng trên cục bộ của tôi, chúng bị thiếu.

Cuối cùng, các lệnh này đã cập nhật tất cả các tệp và thư mục trên cục bộ của tôi:

git fetch --all
git reset --hard origin/master 

hoặc nếu bạn muốn một chi nhánh

git checkout your_branch_name_here
git reset --hard origin/your_branch_name_here

1
cảm ơn! điều này thực sự đã hoạt động. Hãy nhớ rằng tên chi nhánh có phân biệt chữ hoa chữ thường!
André Ramon

4

Bất kỳ thay đổi nào bạn cam kết, chẳng hạn như xóa tất cả các tệp dự án của bạn, sẽ vẫn còn nguyên sau một lần kéo. Tất cả những gì cần làm là hợp nhất các thay đổi mới nhất từ ​​một nơi khác vào nhánh của riêng bạn và nếu nhánh của bạn đã xóa mọi thứ, thì tốt nhất bạn sẽ gặp xung đột hợp nhất khi các thay đổi ngược dòng ảnh hưởng đến các tệp bạn đã xóa. Vì vậy, trong ngắn hạn, tất cả mọi thứ đều được cập nhật.

Nếu bạn mô tả kết quả bạn muốn có thay vì "tất cả các tệp đã bị xóa", có thể ai đó có thể đề xuất một hướng hành động thích hợp.

Cập nhật:

NHẬN MÃ MÃ MỚI NHẤT TRÊN HỆ THỐNG CỦA TÔI

Điều bạn có vẻ không hiểu là bạn đã có mã gần đây nhất, là của bạn. Nếu những gì bạn thực sự muốn là xem tác phẩm mới nhất của người khác trên nhánh chính, chỉ cần làm:

git fetch upstream
git checkout upstream/master

Lưu ý rằng điều này sẽ không khiến bạn phải (lại) ngay lập tức bắt đầu công việc của mình. Nếu bạn cần biết cách hoàn tác điều gì đó bạn đã làm hoặc hoàn nguyên các thay đổi mà bạn hoặc người khác đã thực hiện, vui lòng cung cấp chi tiết. Ngoài ra, hãy xem xét việc đọc kiểm soát phiên bản dùng để làm gì, vì bạn dường như hiểu sai mục đích cơ bản của nó.


Trên thực tế, tôi muốn phiên bản mới nhất của mã của người khác trên hệ thống của mình. Tuy nhiên, hai lệnh đó đã không làm được điều đó. Tất cả những gì tôi nhận được là "đã có trên chi nhánh chủ". Mã trong tệp của tôi không phản ánh mã của người khác.
user1971506

Xin lỗi, lẽ ra phải như vậy upstream/master. Đã cập nhật câu trả lời của tôi.
Ryan Stewart

3

Như các áp phích khác nói, kéo các thay đổi hợp nhất từ ​​ngược dòng vào kho lưu trữ của bạn. Nếu bạn muốn thay thế những gì có trong kho lưu trữ của mình bằng những gì có trong thượng nguồn, bạn có một số tùy chọn. Tắt còng, tôi sẽ đi cùng

git checkout HEAD^1  # Get off your repo's master.. doesn't matter where you go, so just go back one commit
git branch -d master  # Delete your repo's master branch
git checkout -t upstream/master  # Check out upstream's master into a local tracking branch of the same name

1

Câu trả lời hàng đầu là tốt hơn nhiều về độ rộng và chiều sâu của thông tin được đưa ra, nhưng có vẻ như nếu bạn muốn vấn đề của mình được khắc phục gần như ngay lập tức và đừng bận tâm về một số nguyên tắc cơ bản của kiểm soát phiên bản, bạn có thể ...

  1. Chuyển sang chế độ chính

    $ git checkout upstream master
    
  2. Xóa chi nhánh không mong muốn của bạn. (Lưu ý: nó phải có cờ -D, thay vì cờ -d bình thường vì nhánh của bạn có nhiều cam kết trước nhánh cái.)

    $ git branch -d <branch_name>
    
  3. Tạo một chi nhánh mới

    $ git checkout -b <new_branch_name>
    

1

Mặc dù không có câu trả lời nào trong số này phù hợp với tôi, nhưng tôi đã có thể khắc phục sự cố bằng lệnh sau.

git fetch origin

Điều này đã làm một mẹo cho tôi.


0

Chỉ là một lời nhắc nhở thân thiện nếu bạn có các tệp cục bộ không có trong github và chưa git statusnói

Chi nhánh của bạn được cập nhật với 'origin / master'. không có gì để cam kết, cây làm việc sạch sẽ

Nó có thể xảy ra nếu các tệp ở .gitignore

Thử chạy

cat .gitignore 

và xem những tệp này có hiển thị ở đó không. Điều đó sẽ giải thích tại sao git không muốn di chuyển chúng đến điều khiển từ xa.

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.