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ư master
và 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 pull
lệnh được chủ yếu chỉ là viết tắt cho git fetch
tiế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 fetch
lệ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, merge
thao 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 merge
nhậ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 fetch
và 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 master
nê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 reset
lệ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 log
sẽ 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 --hard
cho 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 --hard
và 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 master
là "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 C0
là 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 bunchofhacks
và 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 --hard
sau đó 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 fetch
lệ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/mauve
tê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 show
chẳng hạn như