Git không tập trung dữ liệu mở một máy phục vụ như Subversion. Mà tất cả thao tác sẽ cập nhật dữ liệu ngay trên máy tính của bạn. Để cộng tác với người khác, bạn phải đẩy dữ liệu lên một máy phục vụ chung mà họ có quyền truy cập, và nhờ đó đồng bộ kho lưu trên máy bạn với kho lưu trên máy phục vụ. Không có sự khác biệt giữa máy khách và máy phục vụ dành cho Git, bởi vì kho lưu trữ ở máy khách đã đồng bộ với kho lưu trữ trên máy phục vụ.
Môt khi đã có kho lưu trữ Git, hoặc là bạn sẽ thiết lập máy của bạn thành một máy phục vụ sử dụng kho đó để chia sẻ, hoặc bạn sẽ tiến hành đồng bộ hoặc khảo sát sự khác biệt giữa kho của bạn với kho Git khác bằng hai thao tác đẩy và kéo.
Thao tác đẩy và kéo được tiến hành bất kỳ khi nào bạn có kết nối mạng
với kho Git khác, trong khi đó thao tác xác nhận (commit
)
được tiến hành hoàn tòan ở địa phương.
Tóm tắt: bạn lấy các thông tin mới từ kho Git khác về máy của
bạn bằng lệnh git fetch
(thao tác kéo), và chia sẻ những thay đổi do
bạn thực hiện bằng lệnh git push
(thao tác đẩy)).
Địa chỉ các kho Git của dự án được quản lý nhờ git remote
.
Vì Git không dùng một máy phục vụ chung để lưu dữ liệu như Subversion, các kho lưu trữ Git bình đẳng nhau, nằm trên các máy khác nhau và nhiệm vụ của bạn là đồng bộ những kho này với nhau. Từ đó, nảy sinh ra ý phải quản lý các kho cùng với kết nối tới chúng, kể cả phân quyền (đọc với kho này, được ghi vào kho kia,...).
Các kho ở máy phục vụ khác nhau không chỉ được phân biệt bằng địa chỉ URL đầy đủ, mà còn có thể bằng một tên ngắn gọn mà bạn gán cho địa chỉ đó. Như vậy,
việc sử dụng các kho sẽ dễ nhớ, đơn giản hơn rất nhiều. Bạn sử dụng lệnh
git remote
để quản lý danh sách các kho.
Khi dùng không có tham số, lệnh git remote
liệt kê
các tên của các địa chỉ kho xa đã biết. Mặc định, địa chỉ kho đầu tiên
khi bạn vừa thực hiện lệnh nhân bản một kho sẽ có tên 'origin' (gốc).
Khi có thêm tham số -v
, ngoài tên của kho thì địa chỉ
của kho cũng được in ra.
$ git remote origin $ git remote -v origin git@github.com:github/git-reference.git (fetch) origin git@github.com:github/git-reference.git (push)
Ở ví dụ trên, địa chỉ kho được liệt kê hai lần, bởi vì Git phân biệt địa chỉ kho để kéo và đẩy.
Ta có thể thêm nhiều địa chỉ kho khác nhau cho dự án, bằng lệnh
git remote add [tên] [địa chỉ]
. Ở đây, '[tên]' là do bạn
chọn để gợi nhớ, đại diện cho địa chỉ của kho (thường dài dòng khó nhớ).
Lấy ví dụ, bạn muốn chia sẻ chương trình Hello World với mọi người bằng cách đẩy nó lên GitHub (dịch vụ khác cũng tương tự), trước hết bạn tạo dự án ở GitHub, có tên là hw. Địa chỉ kho mà dịch vụ GitHub cung cấp cho dự án là "git@github.com:schacon/hw.git" (trong đó, @schacon@ được thay bởi tên của bạn ở GitHub). Ta sẽ thêm địa chỉ kho này cho dự án, với tên kho là 'github':
$ git remote $ git remote add github git@github.com:schacon/hw.git $ git remote -v github git@github.com:schacon/hw.git (fetch) github git@github.com:schacon/hw.git (push)
Ở tên tên của kho được chọn là 'github', có lẽ là một tên gợi nhớ; thực tế thì cũng như các nhánh, bạn có thể chọn tên bất kỳ nào cho địa chỉ của kho.
Lệnh git remote rm [tên kho]
sẽ bỏ đi một kho không
còn phù hợp với dự án (nếu bạn muốn giữ nguyên địa chỉ kho và đổi tên
kho thì hãy dùng git remote rename
$ git remote -v github git@github.com:schacon/hw.git (fetch) github git@github.com:schacon/hw.git (push) $ git remote add origin git://github.com/pjhyett/hw.git $ git remote -v github git@github.com:schacon/hw.git (fetch) github git@github.com:schacon/hw.git (push) origin git://github.com/pjhyett/hw.git (fetch) origin git://github.com/pjhyett/hw.git (push) $ git remote rm origin $ git remote -v github git@github.com:schacon/hw.git (fetch) github git@github.com:schacon/hw.git (push)
Nếu bạn muốn đổi tên hiện tại của một kho đầu xa mà không phải xóa và thêm vào lần
nữa, bạn dùng lệnh git remote rename [tên-cũ] [tên-mới]
$ git remote add github git@github.com:schacon/hw.git $ git remote -v github git@github.com:schacon/hw.git (fetch) github git@github.com:schacon/hw.git (push) $ git remote rename github origin $ git remote -v origin git@github.com:schacon/hw.git (fetch) origin git@github.com:schacon/hw.git (push)
Tóm tắt: với git remote
bạn xem được danh sách
tên các kho xa cùng địa của kho. Lệnh git remote add
dùng để thêm kho mới, còn git remote rm
dùng để xóa đi.
Lệnh git remote set-url
dùng để cập nhật địa chỉ đầu
xa
$ git remote -v github git@github.com:schacon/hw.git (fetch) github git@github.com:schacon/hw.git (push) origin git://github.com/pjhyett/hw.git (fetch) origin git://github.com/pjhyett/hw.git (push) $ git remote set-url origin git://github.com/github/git-reference.git $ git remote -v github git@github.com:schacon/hw.git (fetch) github git@github.com:schacon/hw.git (push) origin git://github.com/github/git-reference.git (fetch) origin git://github.com/github/git-reference.git (push)
Với trường hợp dưới đây, bạn có thể thêm địa chỉ đẩy khác khi kèm
theo cờ --push
để tách biệt hai kho đẩy và tải về.
$ git remote -v github git@github.com:schacon/hw.git (fetch) github git@github.com:schacon/hw.git (push) origin git://github.com/github/git-reference.git (fetch) origin git://github.com/github/git-reference.git (push) $ git remote set-url --push origin git://github.com/pjhyett/hw.git $ git remote -v github git@github.com:schacon/hw.git (fetch) github git@github.com:schacon/hw.git (push) origin git://github.com/github/git-reference.git (fetch) origin git://github.com/pjhyett/hw.git (push)
Thực chất, lệnh git remote set-url
thực thi lệnh
git config remote
nhưng trả về kết quả nếu xảy ra lỗi
còn lệnh git config remote
không trả về kết quả nếu bạn
gõ nhầm hoặc lệnh không thực hiện.
Chúng ta sẽ cập nhật kho đầu xa github
nhưng ta dùng
guhflub
trong cả hai trường hợp.
$ git remote -v github git@github.com:schacon/hw.git (fetch) github git@github.com:schacon/hw.git (push) origin git://github.com/github/git-reference.git (fetch) origin git://github.com/github/git-reference.git (push) $ git config remote.guhflub git://github.com/mojombo/hw.git $ git remote -v github git@github.com:schacon/hw.git (fetch) github git@github.com:schacon/hw.git (push) origin git://github.com/github/git-reference.git (fetch) origin git://github.com/github/git-reference.git (push) $ git remote set-url guhflub git://github.com/mojombo/hw.git fatal: No such remote 'guhflub'
Tóm tắt: bạn có thể cập nhật đường dẫn kho đầu xa với lệnh
git remote set-url
. Bạn cũng có thể tách riêng biệt địa
chỉ tải về và địa chỉ đẩy.
Sau khi thêm kho xa, thì tự nhiên dẫn tới nhu cầu cập nhật dữ
liệu ở nhánh xa về máy tính của bạn. Git có hai lệnh cho nhu cầu này:
Lệnh git fetch
sẽ giúp thông tin trên máy của bạn cập nhật
so với nhánh xa, bằng cách tải thông tin từ kho xa về (chỉ tải những
thông tin chưa có trên máy bạn) và đánh dấu để bạn biết bạn đã đồng
bộ dữ liệu với nhánh nào trên kho xa. Cách này còn gọi là "nhánh xa",
bởi vì sau khi cập nhật về, Git để nguyên đó mà không lấy ra và thay thế
các nội dung đang có trong thư mục làm việc. Việc dùng git fetch
giúp bạn kiểm tra các thay đổi, so sánh và thậm chí trộn các thay đổi
xa với thư mục làm việc của bạn.
Lệnh thứ hai để đồng bộ là git pull
. Thực ra, lệnh này
là một cách gộp: đầu tiên, Git thi hành git fetch
để
cập nhật thông tin từ xa xuống máy, rồi ngay sau đó tiến hành trộn
nhờ lệnh git merge
(việc trộn này chỉ diễn ra với nhánh
mà bạn đang làm việc). Một cách cá nhân, tác giả không thích lệnh này,
bởi nó không thật sự an toàn, bởi tốt nhất là chỉ nên trộn sau khi đã
kiểm tra, so sánh kỹ. Tuy nhiên, việc dùng git pull
cũng có cái hay mà bạn có thể tìm hiểu thêm ở
tài liệu chính thức.
Bây giờ, đi vào chi tiết. Giả sử các kho xa đã được thiết lập và
bạn muốn kéo thông tin từ xa về máy: bạn sẽ dùng lệnh git fetch [tên kho]
.
Sau đó, bạn có thể trộn bằng git merge [tên kho]/[tên nhánh]
.
Chẳng hạn, khi đang thực hiện dự án Hello World, thấy có ai đó đã sửa
đổi cho dự án này, và bạn muốn kéo các thay đổi của họ về máy, thì
các lệnh sẽ dùng như sau:
$ git fetch github remote: Counting objects: 4006, done. remote: Compressing objects: 100% (1322/1322), done. remote: Total 2783 (delta 1526), reused 2587 (delta 1387) Receiving objects: 100% (2783/2783), 1.23 MiB | 10 KiB/s, done. Resolving deltas: 100% (1526/1526), completed with 387 local objects. From github.com:schacon/hw 8e29b09..c7c5a10 master -> github/master 0709fdc..d4ccf73 c-langs -> github/c-langs 6684f82..ae06d2b java -> github/java * [new branch] ada -> github/ada * [new branch] lisp -> github/lisp
Thông tin ở trên cho biết, kể từ lần đồng bộ cuối cùng, đã có 5 nhánh được thêm hoặc cập nhật vào kho xa: hai nhánh 'ada' v 'lisp' là hoàn tòan mới, các nhánh còn lại 'master', 'c-langs' và 'java' được cập nhật.
Trong kết quả ở trên bạn thấy cách Git ánh xạ các nhánh với nhau.
Ví dụ, nhánh xa 'remote' tương ứng với nhánh 'github/master' trên máy
của bạn. Nhờ thế, để trộn nhánh xa 'master' với thư mục hiện tại,
bạn chỉ việc dùng lệnh git merge github/master
.
Bạn cũng có thể xem những thay đổi diễn ra ở nhánh xa nhưng chưa
được trộn vào nhánh 'master' của bạn bằng lệnh
git log github/master ^master
.
Để ý là, nếu bạn dùng tên 'origin' cho nhánh xa thì thay vì 'github/master'
như trong các ví dụ vừa rồi, bạn phải dùng origin/master
instead.
Ngoài ra, hầu hết các lệnh các tác dụng với nhánh địa phương cũng
có thể dùng với nhánh xa.
Trường hợp có nhiều nhánh xa, ngoài việc chỉ định lấy thông tin
từ một nhánh cụ thể bằng lệnh git fetch [tên kho]
bạn có thể chỉ định Git cập nhật từ tất cả các nhánh bằng lệnh
git fetch --all
.
Tóm tắt: Dùng git fetch [tên kho]
để đồng bộ
dữ liệu từ kho ở xa đến kho của bạn, để lấy về những dữ liệu có ở xa
mà chưa có trong thư mục làm việc, và sau đó bạn có thể so sánh,
khảo sát và trộn các kết quả khi cần thiết.
Sau khi thực hiện một số thay đổi cho dự án, xác nhận thay đổi
bằng lệnh git commit
, bạn có thể chia sẻ thay đổi này
với mọi người bằng cách đẩy cách kết quả lênh nhánh ở xa. Thao tác này
ngược với việc kéo đã mô tả ở trên. Lệnh để thực hiện đẩy là
git push [tên kho] [tên nhánh]
, sẽ chép nội dung nhánh
hiện tại thành nhánh ở kho xa. Ví dụ, ta sẽ đẩy nhánh 'master' lên kho xa:
$ git push github master Counting objects: 25, done. Delta compression using up to 2 threads. Compressing objects: 100% (25/25), done. Writing objects: 100% (25/25), 2.43 KiB, done. Total 25 (delta 4), reused 0 (delta 0) To git@github.com:schacon/hw.git * [new branch] master -> master
Thật dễ dàng :) Sau khi đẩy như trên, một lập trình viên khác dễ dàng kéo mọi thay đổi mà bạn thực hiện về máy tính của họ.
Trong ví dụ tiếp sau, ta tiếp tục đẩy nhánh 'erlang', thay vì 'master' như trên.
$ git push github erlang Counting objects: 7, done. Delta compression using up to 2 threads. Compressing objects: 100% (6/6), done. Writing objects: 100% (6/6), 652 bytes, done. Total 6 (delta 1), reused 0 (delta 0) To git@github.com:schacon/hw.git * [new branch] erlang -> erlang
Sau lệnh trên, khi người khác nhân bản kho ở Github, hoặc kéo thông tin từ kho đó, họ sẽ thấy nhánh 'erlang' để khảo sát hoặc trộn. Ví dụ cho thấy, bạn có thể tạo ra nhánh bất kỳ và đẩy lên kho xa mà bạn có quyền ghi. Nếu nhánh chưa có trên kho, nhánh mới sẽ được tạo ra, còn không thì nhánh cũ sẽ được cập nhật.
Vấn đề lớn nhất mà bạn có thể gặp khi đẩy dữ liệu lên kho xa,
là việc bạn và một người khác 'đồng thời' đẩy lên một nhánh của cùng một kho.
Nếu bạn là người thực hiện sau một chút, thì Git không cho phép bạn ghi
đè lên kết quả của người kia. Git sẽ dùng lệnh git log
ở phía máy phục vụ để kiểm tra xem kho địa phương của bạn có quá hạn
chưa; nếu đúng là quá hạn, Git từ chối và bạn phải kéo cập nhật từ
kho xa về, trộn trước khi đẩy lên kho.
Dưới đây là minh họa cho ý này:
$ git push github master To git@github.com:schacon/hw.git ! [rejected] master -> master (non-fast-forward) error: failed to push some refs to 'git@github.com:schacon/hw.git' To prevent you from losing history, non-fast-forward updates were rejected Merge the remote changes before pushing again. See the 'Note about fast-forwards' section of 'git push --help' for details.
Vì Git từ chối thay đổi mà bạn đẩy lên, bạn phải dùng
git fetch github; git merge github/master
rồi mới đẩy lên lần nữa.
Tóm tắt: Dùng git push [tên kho] [tên nhánh]
để
đẩy các thay đổi mà bạn thực hiện ở máy lên kho xa. Git sẽ xem xét trạng
thái của nhánh ở kho xa và thực hiện chuyển dữ liệu nếu có thể.
Trường hợp nhánh của bạn đã quá hạn (hay quá cũ) so với thông tin
được xác nhận trên kho xa, Git từ chối và bạn phải thực hiện kéo
các thông tin từ trên kho về máy để tiếp tục.