Tóm lược
Theo mặc định, git pull
tạo các xác nhận hợp nhất sẽ thêm nhiễu và độ phức tạp vào lịch sử mã. Ngoài ra, pull
giúp bạn dễ dàng không nghĩ về những thay đổi của bạn có thể bị ảnh hưởng bởi những thay đổi đến.
Các git pull
lệnh là an toàn quá lâu vì nó chỉ thực hiện hòa trộn nhanh về phía trước. Nếugit pull
được định cấu hình để chỉ thực hiện hợp nhất chuyển tiếp nhanh và khi không thể hợp nhất chuyển tiếp nhanh, thì Git sẽ thoát với một lỗi. Điều này sẽ cho bạn cơ hội nghiên cứu các cam kết đến, suy nghĩ về cách chúng có thể ảnh hưởng đến các cam kết cục bộ của bạn và quyết định hướng hành động tốt nhất (hợp nhất, rebase, thiết lập lại, v.v.).
Với Git 2.0 và mới hơn, bạn có thể chạy:
git config --global pull.ff only
để thay đổi hành vi mặc định thành chỉ chuyển tiếp nhanh. Với các phiên bản Git trong khoảng 1.6.6 đến 1.9.x, bạn sẽ phải tập thói quen gõ:
git pull --ff-only
Tuy nhiên, với tất cả các phiên bản của Git, tôi khuyên bạn nên định cấu hình git up
bí danh như thế này:
git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'
và sử dụng git up
thay vì git pull
. Tôi thích bí danh này hơn git pull --ff-only
bởi vì:
- nó hoạt động với tất cả các phiên bản (không cổ) của Git,
- nó tìm nạp tất cả các nhánh ngược dòng (không chỉ là nhánh bạn đang làm việc) và
- nó làm sạch
origin/*
các nhánh cũ không còn tồn tại ở thượng nguồn.
Có vấn đề với git pull
git pull
Không tệ nếu nó được sử dụng đúng cách. Một số thay đổi gần đây đối với Git đã giúp sử dụng git pull
đúng cách dễ dàng hơn , nhưng thật không may, hành vi mặc định của đồng bằng git pull
có một số vấn đề:
- nó giới thiệu những phi tuyến không cần thiết trong lịch sử
- nó làm cho nó dễ dàng vô tình giới thiệu lại các cam kết đã được cố tình từ chối ngược dòng
- nó sửa đổi thư mục làm việc của bạn theo những cách không thể đoán trước
- tạm dừng những gì bạn đang làm để xem xét công việc của người khác gây phiền nhiễu với
git pull
- nó làm cho nó khó có thể nổi loạn chính xác vào nhánh từ xa
- nó không dọn sạch các nhánh đã bị xóa trong repo từ xa
Những vấn đề này được mô tả chi tiết hơn dưới đây.
Lịch sử phi tuyến
Theo mặc định, git pull
lệnh tương đương với việc chạy git fetch
theo sau git merge @{u}
. Nếu có các xác nhận chưa được đánh dấu trong kho lưu trữ cục bộ, phần hợp nhất của việc git pull
tạo một cam kết hợp nhất.
Không có gì xấu về các cam kết hợp nhất, nhưng chúng có thể nguy hiểm và cần được đối xử tôn trọng:
- Hợp nhất cam kết vốn đã khó kiểm tra. Để hiểu những gì một sự hợp nhất đang làm, bạn phải hiểu sự khác biệt cho tất cả các bậc cha mẹ. Một khác thường thông thường không truyền tải thông tin đa chiều này tốt. Ngược lại, một loạt các cam kết bình thường rất dễ xem xét.
- Hợp nhất giải quyết xung đột là khó khăn và các lỗi thường không bị phát hiện trong một thời gian dài vì các cam kết hợp nhất rất khó để xem xét.
- Sáp nhập có thể lặng lẽ thay thế các hiệu ứng của cam kết thông thường. Mã không còn là tổng của các cam kết gia tăng, dẫn đến sự hiểu lầm về những gì thực sự thay đổi.
- Hợp nhất cam kết có thể phá vỡ một số lược đồ tích hợp liên tục (ví dụ: chỉ tự động xây dựng đường dẫn cha mẹ đầu tiên theo quy ước giả định rằng cha mẹ thứ hai chỉ ra các công việc chưa hoàn thành đang tiến hành).
Tất nhiên có một thời gian và một nơi để hợp nhất, nhưng sự hiểu biết khi hợp nhất nên và không nên được sử dụng có thể cải thiện tính hữu ích của kho lưu trữ của bạn.
Lưu ý rằng mục đích của Git là giúp dễ dàng chia sẻ và tiêu thụ sự phát triển của một cơ sở mã, không ghi lại chính xác lịch sử chính xác như đã diễn ra. (Nếu bạn không đồng ý, hãy xem xét rebase
lệnh và lý do tại sao nó được tạo.) Các cam kết hợp nhất được tạo bằng cách git pull
không truyền đạt ngữ nghĩa hữu ích cho người khác. Họ chỉ nói rằng ai đó đã tình cờ đẩy vào kho lưu trữ trước khi bạn thực hiện xong các thay đổi của mình. Tại sao những cam kết hợp nhất nếu chúng không có ý nghĩa với người khác và có thể nguy hiểm?
Có thể cấu hình git pull
để rebase thay vì hợp nhất, nhưng điều này cũng có vấn đề (sẽ thảo luận sau). Thay vào đó, git pull
nên được cấu hình để chỉ thực hiện hợp nhất chuyển tiếp nhanh.
Giới thiệu lại các cam kết bị loại bỏ
Giả sử ai đó khởi động lại một nhánh và lực đẩy nó. Điều này thường không nên xảy ra, nhưng đôi khi cần thiết (ví dụ: để xóa tệp nhật ký 50GiB đã vô tình được gửi và đẩy). Việc hợp nhất được thực hiện bởi git pull
sẽ hợp nhất phiên bản mới của nhánh ngược dòng vào phiên bản cũ vẫn còn tồn tại trong kho lưu trữ cục bộ của bạn. Nếu bạn đẩy kết quả, dĩa và ngọn đuốc sẽ bắt đầu theo cách của bạn.
Một số người có thể lập luận rằng vấn đề thực sự là cập nhật lực lượng. Có, nói chung nên tránh đẩy lực bất cứ khi nào có thể, nhưng đôi khi chúng không thể tránh khỏi. Các nhà phát triển phải được chuẩn bị để đối phó với các cập nhật lực lượng, bởi vì đôi khi chúng sẽ xảy ra. Điều này có nghĩa là không hợp nhất một cách mù quáng trong các cam kết cũ thông qua một thông thường git pull
.
Điều chỉnh thư mục làm việc bất ngờ
Không có cách nào để dự đoán thư mục hoặc chỉ mục làm việc sẽ trông như thế nào cho đến khi git pull
hoàn thành. Có thể có các xung đột hợp nhất mà bạn phải giải quyết trước khi bạn có thể làm bất cứ điều gì khác, nó có thể giới thiệu tệp nhật ký 50GiB trong thư mục làm việc của bạn vì ai đó đã vô tình đẩy nó, nó có thể đổi tên một thư mục bạn đang làm việc, v.v.
git remote update -p
(hoặc git fetch --all -p
) cho phép bạn xem xét các cam kết của người khác trước khi bạn quyết định hợp nhất hoặc nổi loạn, cho phép bạn lập kế hoạch trước khi thực hiện hành động.
Khó khăn khi xem xét các cam kết khác của nhân dân
Giả sử bạn đang thực hiện một số thay đổi và người khác muốn bạn xem lại một số cam kết họ vừa đẩy. git pull
Hoạt động hợp nhất (hoặc rebase) sửa đổi thư mục và chỉ mục làm việc, có nghĩa là thư mục và chỉ mục làm việc của bạn phải sạch.
Bạn có thể sử dụng git stash
và sau đó git pull
, nhưng bạn sẽ làm gì khi xem xét xong? Để quay lại nơi bạn đã ở, bạn phải hoàn tác việc hợp nhất được tạo bởi git pull
và áp dụng stash.
git remote update -p
(hoặc git fetch --all -p
) không sửa đổi thư mục hoặc chỉ mục làm việc, do đó, an toàn để chạy bất cứ lúc nào, ngay cả khi bạn đã thay đổi và / hoặc thay đổi không theo giai đoạn. Bạn có thể tạm dừng những gì bạn đang làm và xem xét cam kết của người khác mà không phải lo lắng về việc xóa hoặc hoàn thành cam kết mà bạn đang thực hiện.git pull
không cung cấp cho bạn sự linh hoạt.
Nổi loạn lên một Chi nhánh từ xa
Một mô hình sử dụng Git phổ biến là thực hiện git pull
để mang lại những thay đổi mới nhất theo sau là git rebase @{u}
loại bỏ cam kết hợp nhất được git pull
giới thiệu. Nó đủ phổ biến mà Git có một số tùy chọn cấu hình để giảm hai bước vào một bước duy nhất bằng cách nói git pull
để thực hiện một rebase thay vì một hợp nhất (xem branch.<branch>.rebase
, branch.autosetuprebase
và pull.rebase
tùy chọn).
Thật không may, nếu bạn có một cam kết hợp nhất chưa được đánh dấu mà bạn muốn giữ lại (ví dụ: một cam kết hợp nhất một nhánh tính năng được đẩy vào master
), không phải là một rebase-pull ( git pull
với branch.<branch>.rebase
được đặt thành true
) cũng không phải là một pull-pull ( git pull
hành vi mặc định ) theo sau rebase sẽ hoạt động. Điều này là do git rebase
loại bỏ sự hợp nhất (nó tuyến tính hóa DAG) mà không có --preserve-merges
tùy chọn. Hoạt động kéo lại rebase không thể được cấu hình để duy trì các phép hợp nhất và một phép kéo hợp nhất theo sau là git rebase -p @{u}
sẽ không loại bỏ sự hợp nhất gây ra bởi phép kéo hợp nhất. Cập nhật: Đã thêm Git v1.8.5 git pull --rebase=preserve
và git config pull.rebase preserve
. Những nguyên nhân git pull
phải làm git rebase --preserve-merges
sau khi tìm nạp các cam kết ngược dòng. (Cảm ơn funkaster hỗ trợ!)
Dọn dẹp các chi nhánh đã xóa
git pull
không cắt tỉa các nhánh theo dõi từ xa tương ứng với các nhánh đã bị xóa khỏi kho lưu trữ từ xa. Ví dụ: nếu ai đó xóa chi nhánh foo
khỏi repo từ xa, bạn vẫn sẽ thấy origin/foo
.
Điều này dẫn đến việc người dùng vô tình hồi sinh các chi nhánh bị giết vì họ nghĩ rằng chúng vẫn còn hoạt động.
Một thay thế tốt hơn: Sử dụng git up
thay vìgit pull
Thay vì git pull
, tôi khuyên bạn nên tạo và sử dụng git up
bí danh sau :
git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'
Bí danh này tải xuống tất cả các cam kết mới nhất từ tất cả các nhánh ngược dòng (cắt tỉa các nhánh chết) và cố gắng chuyển nhanh chi nhánh địa phương đến cam kết mới nhất trên nhánh ngược dòng. Nếu thành công, sau đó không có cam kết địa phương, do đó không có nguy cơ xung đột hợp nhất. Chuyển tiếp nhanh sẽ thất bại nếu có các cam kết cục bộ (chưa được xử lý), cho bạn cơ hội để xem xét các cam kết ngược dòng trước khi thực hiện hành động.
Điều này vẫn sửa đổi thư mục làm việc của bạn theo những cách không thể đoán trước, nhưng chỉ khi bạn không có bất kỳ thay đổi cục bộ nào. Không giống như git pull
, git up
sẽ không bao giờ thả bạn đến một dấu nhắc mong đợi bạn khắc phục xung đột hợp nhất.
Một lựa chọn khác: git pull --ff-only --all -p
Sau đây là một thay thế cho git up
bí danh trên :
git config --global alias.up 'pull --ff-only --all -p'
Phiên bản git up
này có hành vi tương tự như git up
bí danh trước đó , ngoại trừ:
- thông báo lỗi khó hiểu hơn một chút nếu nhánh cục bộ của bạn không được cấu hình với nhánh ngược dòng
- nó dựa vào một tính năng không có giấy tờ (
-p
đối số, được chuyển đến fetch
) có thể thay đổi trong các phiên bản tương lai của Git
Nếu bạn đang chạy Git 2.0 hoặc mới hơn
Với Git 2.0 và mới hơn, bạn có thể định cấu hình git pull
để chỉ thực hiện hợp nhất chuyển tiếp nhanh theo mặc định:
git config --global pull.ff only
Điều này gây ra git pull
hành động như vậy git pull --ff-only
, nhưng nó vẫn không tìm nạp tất cả các cam kết ngược dòng hoặc dọn sạch origin/*
các nhánh cũ vì vậy tôi vẫn thích git up
.