Git checkout là gì

Vì lệnh Git Fetch và Git Merge được sử dụng thường xuyên nên Git đã kết hợp hai lệnh này thành một lệnh đặc biệt gọi là Git Pull. Vậy Git Pull là gì? Nó được sử dụng như thế nào? Trong bài viết này, Got It sẽ mang đến cho các bạn một cái nhìn đầy đủ nhất về lệnh Git Pull.

  • Đọc thêm: Git và GitHub — liệu bạn đã thật sự biết cách sử dụng?

Git Pull là gì?

Git Pull là một lệnh dùng để tải xuống dữ liệu từ một Remote repository và cập nhật Local repository phù hợp với dữ liệu đó. Nói cách khác, Git Pull là lệnh hợp nhất các thay đổi từ Remote repository vào Local repository.

Về bản chất, Git Pull chính là sự kết hợp của 2 lệnh Git Fetch và Git Merge. Giai đoạn đầu, Git Pull sẽ thực thi lệnh Git Fetch ở phạm vi nhánh cục bộ mà HEAD được trỏ đến. Khi dữ liệu được tải xuống, Git Pull sẽ bắt đầu quy trình hợp nhất như Git Merge. Một merge commit mới sẽ được tạo và HEAD cũng được cập nhật để trỏ đến merge commit đó.

Git Pull là sự kết hợp của 2 lệnh Git fetch và Git Merge [Nguồn: javatpoint]

Mô hình hoạt động của Git Pull

Sau khi tìm hiểu Git Pull là gì, ở phần này chúng ta sẽ khám phá mô hình hoạt động của Git Pull. Về cơ bản, Git Pull sẽ hoạt động theo một quy trình cơ bản dưới đây:

  • Thực hiện lệnh Git Fetch để tải xuống dữ liệu từ Remote repository được chỉ định.
  • Chạy lệnh Git Merge để hợp nhất các nội dung tham chiếu từ xa và hướng về một local merge commit mới.

Để hiểu rõ hơn mô hình hoạt động của Git Pull, chúng ta sẽ phân tích một ví dụ sau:

Mô hình hoạt động của Git Pull [Nguồn: Atlassian]

Giả sử, chúng ta có một repository gồm một nhánh chính [master branch] và một nguồn từ xa [remote origin]. Git Pull sẽ tải xuống tất cả các thay đổi từ vị trí mà local và master chuyển hướng. Trong mô hình trên, vị trí tải xuống là E. Git Pull sẽ thực thi lệnh Git Fetch để “nạp” các remote commit được chuyển hướng tại A-B-C. Sau đó, local merge commit mới [H] được tạo ra có nội dung đồng bộ với các remote commit A-B-C.

Theo sơ đồ, H chính là một merge commit mới [Nguồn: Atlassian]

Câu lệnh Git Pull dùng để đồng bộ Remote repository vào Local repository thường có cấu trúc sau:

git pull origin branch

Trong đó, origin là tên Remote repository và branch là nhánh muốn đồng bộ.

Ví dụ, để đồng bộ master branch từ Remote repository vào Local repository, chúng ta sẽ dùng lệnh:

git pull origin master

Các tùy chọn phổ biến trong Git Pull

Giống như bất kỳ lệnh nào khác trong Git, lệnh Git Pull cũng có một số tùy chọn giúp sử dụng lệnh một cách tự nhiên và hiệu quả.

  • git pull : Lấy bản sao remote đã được chỉ định của nhánh hiện tại và hợp nhất nó vào bản sao local. Tùy chọn này giống như lệnh: git fetch origin/git merge origin .
  • git pull –no-commit: Đây là một tùy chọn Git Pull không có cam kết. Thực hiện tìm nạp nội dung remote nhưng không tạo ra merge commit mới.
  • git pull –rebase: Tùy chọn này là Git Rebase giúp hợp nhất remote branch với local branch nhanh chóng.
  • git pull –verbose: Cung cấp verbose output để hiển thị nội dung tải xuống và các chi tiết hợp nhất.

Sự khác biệt giữa Git Pull và Git Fetch

Trong Git, Git Pull và Git Fetch là 2 lệnh có chức năng tương đồng với nhau. Cả hai đều được sử dụng để tải xuống dữ liệu mới từ một Remote repository. Tuy nhiên, Git Fetch thường được coi là một phiên bản an toàn hơn của Git Pull.

Git Pull và Git Fetch đều tải xuống dữ liệu từ Remote repository [Nguồn: ToolsQA]

Git Fetch chỉ tải xuống nội dung từ Remote repository mà không làm thay đổi trạng thái của Local repository. Trong khi đó, Git Pull sẽ tải xuống nội dung và cố gắng thay đổi trạng thái của Local repository cho phù hợp với nội dung đó. Điều này có thể dẫn đến hiện tượng xung đột hợp nhất [merge-conflict] trong Git.

Việc sử dụng Git Fetch thường được dùng trong trường hợp có nhiều người làm việc trên cùng một nhánh. Còn Git Pull chỉ nên sử dụng khi có một người làm việc trên nhánh để hạn chế xung đột. Bạn chỉ nên dùng lệnh Git Pull trên một thư mục làm việc sạch [không có thay đổi đã cam kết].

Trên đây là một bức tranh tổng quan về lệnh Git Pull trong Git. Hy vọng bài viết này sẽ giúp các bạn hiểu rõ khái niệm Git Pull là gì và cách hoạt động của Git Pull. Nếu bạn muốn trở thành một lập trình viên, hãy khám phá Git Pull ngay nhé!

Tìm hiểu thêm:

  • Git Rebase là gì?
  • Git Commit là gì?

Có lẽ chúng ta đã quá quen với Git, một trong những công cụ để quản lí repository [hay còn gọi là version control] hiệu quả nhất hiện nay.

Và developer không thể sống thiếu Git được phải không nào? Bên cạnh một số câu lệnh đơn giản chúng ta sử dụng hằng ngày, sau đây chúng ta cùng tìm hiểu kỹ lại một lần nữa những câu lệnh từ cơ bản đến nâng cao.

Hiểu rõ và thành thạo Git command giúp bạn làm việc hiệu quả hơn rất nhiều.

Git

Bên cạnh git commands chúng ta vẫn có thể sử dụng git GUI như Source Tree, Git Kraken, Github for Desktop, … Nếu bạn sử dụng VS Code thì đã có sẵn Git với một số chức năng cơ bản và công thêm sẽ rất tốt nếu bạn có thể sử dụng command bởi vì command giúp bạn trong cool ngầu hơn, xịn xò hơn 😀

Bạn có thể tìm hiểu một số extension trong VS Code như Git history & GitLens thì cũng đã đủ làm việc với Git.

Một số trường hợp chỉ có git commands mới làm được như: update branch dưới background [khi bạn ở một branch bạn cũng có thê pull code của một branch khác mà không cần chuyển branch qua lại], và một số TH bạn có thể chuyển branch nhanh và tiện hơn.

TL;DR [Tóm tắt nếu bạn không muốn đọc nhiều]

Ngoài những lệnh Git thông thường bạn nên biết như git clone, git add, git commit, git checkout, git merge, git init, git status, git push, git pull, git fetch, git log, git merge, git stash. Những lệnh này đa số dễ hiểu và thông dụng.

Bạn nên dành thời gian tìm hiểu thêm một số lệnh đặc biệt trong git như git rebase, git reset, git revert.

Một số lệnh thường dùng trong Git:

// 1. Chuyển qua làm việc trên nhánh feature git checkout feature git checkout master git checkout -b new-branch // Bạn có thể checkout từ một commit và sau đó nó tạo thành một Detached HEAD // nên lưu ý trường hợp này bên dưới nếu ko muốn mất nhánh :] git checkout HEAD~3 git checkout commit-id git checkout commit-id -b new-branch // 2. merge code từ nhánh master vào nhánh feature git checkout feature git merge master // 3. rebase code cho nhánh feature: merge code từ nhánh master vào nhánh // feature và đổi "base" cho nhánh feature - tham khảo "base" là gì bên dưới git checkout feature git rebase master // 4. Xoá lệnh merge vừa thực hiện git merge --abort // 5. Git fetch git fetch git fetch origin master:master // 6. Sự khác nhau giữa git reset vs. git revert // Những option khi reset --mixed, --soft, --hard git reset HEAD~2 git reset commit-id git revert HEAD~1 git revert commit-id // 7. Sử dụng cherry-pick git cherry-pick commit-id // 8. Lưu lại những file thay đổi khi checkout qua nhánh khác git stash git stash push git stash push -m "my-stash" git stash pop git stash apply stash@{2} git stash list git stash clear // 9. Push code lên remote git push git push --set-upstream origin my-branch

git rebase – giúp cho history sạch đẹp hơn, nhưng nguy hiểm nếu bạn rebase master hoặc main brach. Bạn có thể tham khảo thêm những quy tắc vàng của rebase Tóm lại nên sử dụng git merge, nếu cần thiết chỉ sử dụng rebase trên nhánh feature, ko được rebase nhánh public như master 🙂

git fetch origin master:master – khi bạn đang đứng ở một nhánh feature bạn có thể pull code mới nhất của master mà ko cần checkout qua master để pull

Bên cạnh commit-id, chúng ta có thể chỉ định cha của HEAD [ví dụ HEAD~1 là cha của HEAD, HEAD~2 là cha của HEAD~1, …] để git revert hay reset về vị trí một commit trên history của một nhánh. Ví dụ chúng ta đang đứng trên nhánh master, chúng ta có thể hình dung sơ đồ các HEAD được đánh thứ tự như sau:

Kiến thức chung

Bạn có thể tự hỏi rằng tại sao mỗi commit id trong Git lại là một chuỗi ký tự lằng nhằng khó nhớ. Bởi vì đó là cách mà Git nhận biết thay đổi khi chúng ta sửa một file nào đó, git sẽ tiến hành chạy giải thuật hash lại tất cả content và chỉ cần thay đổi một ký tự trong một file thì một hash mới sẽ được tạo [giải thuật SHA1], mỗi hash như là một commit id, nó là duy nhất trong history.

Những commit trong Git như là một danh sách liên kết, mỗi commit chứa thông tin về: commit id, tree, parent [trỏ về commit trước đó], author, ..

Một git model điển hình

Chúng ta sẽ tìm hiểu kỹ hơn về git commands trong phần tiếp theo.

git init

Tạo mới một Git repo và add thư mục .git vào trong dự án của bạn.

Thông thường để cho đơn giản, bạn có thể lên Github vào tạo một repo rỗng [ko có README.md] file từ đầu và sau đó sử dụng một số lệnh Github đề nghị để tạo một repo liên kết giữa remote và local như sau:

echo "# myapp" >> README.md git init git add README.md git commit -m "first commit" git branch -M main git remote add origin :thaunguyen/myapp.git git push -u origin main

git clone

Clone một repo có sẵn trên github. Bạn có thể clone bằng HTTPS link hay SSH links. Thực chất bên trong git clone, đầu tiên nó sẽ chạy git init trước, sau đó git clone sẽ copy tất cả files từ remote về local.

git add

Thêm những file mà bạn đã tạo mới hay sửa đỗi trong working directory lên staging area.

git add [thêm một file cụ thể vào staging]

git add . [thêm tất cả các file đang bị thay đổi vào staging]

Trong sơ đồ ở trên chúng ta có 3 thành phần:

  • Working Directory là thư mục chúng ta đang làm việc, bao gồm những file ở máy local
  • Staging Area [hay là Index] là khu vực lưu những file chuẩn bị được commit.
  • History [hay là Commit Area] là khu vực lưu tất cả những file sau khi commit từ Staging Area.

Một flow thông thường là chúng ta sửa một vài file ở working directory [local] => sau đó chúng ta sử dụng git add để đẩy những file đó lên staging và sau đó sử dụng git commit để commit lên history của Git.

  • git add files[tên files cần add], git add -a, git add .
  • git reset files[tên files cần reset] hoặc git reset HEAD~1 hay commit id
  • git checkout files[tên file cần checkout]

Bạn có thể sử dụng git status để kiểm tra xem hiện tại những file của mình đang sửa đổi ở trạng thái nào [local, staging, hay đã commit]

git commit

Khi bạn có một commit mới f0cec thì HEAD và con trỏ được di chuyển tới commit đó và nó trỏ về cha của nó là commit ed489

Minh hoạ cho git commit:

Khi bạn lỡ commit sai message bạn có thể sử dụng lệnh: git commit --amend -m "Update commit message" để sửa đổi lại commit trước đó mà nó sẽ không tạo thêm commit mới.

Commit ed489 là commit sai và sau đó –amend tạo ra một commit mới là 4ca87. Nhưng trong history của bạn commit ed489 sẽ bị huỷ bỏ và ko nằm trong history.

Những lưu ý khi sử dụng git commit

  • Nên chia nhỏ commit theo từng mục đích, nên tránh sử dụng git add . để commit tất cả các file
  • Commit nhỏ giúp người review code dễ đọc và hiểu code bạn hơn
  • Commit nhỏ dễ reset và revert code hơn
  • Viết commit message rõ ràng về thay đổi của bạn, đảm bảo người đọc hiểu bạn thay đổi gì.

git checkout

Dùng để thay đổi branch làm việc, hay bạn có thể checkout một commit trước đó trên branch hiện tại của bạn.

Bạn đang ở branch main và checkout qua branch stable.

Hay bạn có thể dùng checkout để tạo branch mới [branch new] hoàn toàn từ một branch và chuyển qua làm việc trên branch mới tạo. Ví dụ như:

  • git pull: lấy code mới nhất từ remote về working directory [local]
  • git push: đưa những thay đổi ở local branch lên remote branch
  • git fetch: lấy tất cả thay đổi ở remote về local, ví dụ như: có tag mới, branch mới ở remote.

Xem lại sơ đồ ở trên.

Detached HEAD

Thỉnh thoảng bạn muốn checkout từ một commit id của một nhánh [để chỉ muốn tạo một nhánh mới từ những thay đổi của commit đó]. Bạn có thể sử dụng lệnh:

git check

git checkout HEAD~3

Sau khi HEAD trỏ về commit b325c, chúng ta có một Detached HEAD như sau:

Với Detached HEAD nếu bạn không tạo một nhánh từ đây thì khi bạn checkout sang nhánh khác [ví dụ nhánh main] thì Detached HEAD sẽ mất.

Cho nên nếu bạn muốn lưu lại Detached HEAD, bạn phải tạo một nhánh mới như sau:

Bạn chạy lệnh git checkout -b new để tạo một nhánh mới hoàn toàn từ Detached HEAD. Và bạn đã lưu nhánh new lại, bạn có thể tự do checkout sang nhánh khác.

Hay bạn có thể thực hiện quá trình này bằng cú pháp rút gọn như sau:

git checkout -b new

Lệnh này vừa checkout từ một commit và sau đó tạo một nhánh mới tên new.

git reset

git reset dùng để thay đổi con trỏ trong một branch về một commit trước đó. Và nó sẽ thay đổi working directory và staging của nhánh hiện tại chúng ta đang đứng.

Ví dụ như chúng ta reset con trỏ về HEAD~3 [trỏ về trước HEAD 3 commit] trên một nhánh như sau:

Working directory và staging sẽ thay đổi dựa vào những option bạn chọn như sau:

  • –mixed [default]: giữ lại những file bạn đang sửa trên disk, xoá những file trên staging.
  • –soft: giữ lại những file hiện tại bạn đang sửa trên disk và staging. Chuyển commit nhưng ko làm thay đổi file đang sửa và ko xoá file trên staging.
  • –hard: xoá bỏ tất cả files thay đổi trên disk [working directory] và staging. Tất cả những file trên disk và trên stage đều bị reset về giống hệt như commit ở HEAD~3.

Nếu bạn không truyền commit id thì mặc định là git reset HEAD. Nó không làm thay đổi con trỏ trên branch nhưng nó sẽ xoá file bạn đang thay đổi tuỳ thuộc vào option[–mixed, –soft, –hard] mà bạn chọn.

git revert

git reset không tạo mới commit, trong khi đó git revert sẽ “undo” lại tất cả những thay đổi của một commit mà chúng ta chỉ định và tạo mới một commit mới trên history.

Lệnh git revert C2 sẽ tạo ra một commit mới C4 và bỏ [undo] lại tất cả thay đổi của commit C2.

git merge

Merge có nghĩa là lấy tất cả thay đổi của một nhánh khác để apply cho nhánh đang đứng. Ví dụ merge master branch vào feature branch hay ngược lại merge feature branch vào master branch để release một bản production mới.

Bạn đang đứng ở feature và chạy git merge master

Tất cả những commit, thay đổi [màu xanh dương] sẽ được merge vào nhánh feature [màu xanh lá] và tạo thành một commit mới ở cuối nhánh feature. Hai commit màu xanh dương được gộp vào một commit màu xanh lá.

git rebase

Cũng giống như git merge, git rebase sẽ lấy tất cả thay đổi của một nhánh khác để apply cho nhánh đang đứng. Nhưng với history đẹp hơn git merge 😀

Vi dụ: bạn đang đứng ở nhanh topic và chạy git rebase main

Sau khi chạy lệnh rebase thì nhánh topic sẽ chứa tất cả những commit mới của main. Và hai commit màu vàng được thay thế cho 2 commit cũ trên nhánh topic [mỗi tên gạch nối]. Và hiện tại nhánh topic sẽ bao gồm 6 commit [2 commit trên nhánh topic trước khi rebase và 4 commit của nhánh main].

Với rebase để dễ hình dung, bạn có thể hiểu rebase có nghĩa là thay đổi “base” của một nhánh [ví dụ ở đây là nhánh topic]. Trước khi rebase, “base” của nhánh topic trỏ vào commit a47c3, nhưng sau khi rebase, chúng ta chỉ đơn giản đổi “base” trỏ về HEAD của main [commit da985].

Git rebase không thêm node mới [commit mới] vào history như git merge [trước và sau rebase chỉ có 6 commit – không tạo thêm commit thừa cho history – clean history 😀 ]

Và khi đọc vào history của nhánh topic sau khi rebase bạn nhìn thấy được những thay đổi gì đã xảy ra trên nhánh main với các commit a47c3, b325c, c10b9, da985.

Khác với git merge, trong hợp này nếu bạn chạy git merge main thì khi nhìn vào history của nhánh topic bạn sẽ không thấy những commit từ main, bởi vì nó đã gom lại chỉ một commit duy nhất như sau:

Tuỳ thuộc vào bạn chọn git rebase hay git merge, nhưng tôi thường sử dụng git merge vì tôi không cần một clean history và đôi khi git rebase gây khó hiểu và cách sử dụng của git merge dễ hiểu hơn đối với tôi.

Bạn có thể thấy giải thích cách hoạt động của rebase khá khó và gây nhầm lẫn cho nhiều dev. Nên để đơn giản bạn có thể sử dụng git merge.

git rebase giúp cho history sạch đẹp hơn, nhưng nguy hiểm nếu bạn rebase master hoặc main branch. Bạn có thể tham khảo thêm những quy tắc vàng của rebase

Tóm lại nên sử dụng git merge, nếu cần thiết chỉ sử dụng rebase trên nhánh feature, không được rebase nhánh public như master 🙂

git cherry-pick

git cherry-pick tạo ra một bản copy của một commit từ một nhánh khác và apply vào nhánh hiện tại với cùng message và thay đổi từ commit đó.

Bạn đang đứng ở nhánh main và cherry-pick một commit 2c33a từ nhánh topic vào nhánh main. Kết quả như hình trên, có một commit mới f142b được tạo ra trên nhánh main.

git stash

Khi bạn thay đổi một số file và bạn muốn checkout sang nhánh khác. Lúc này git sẽ không cho bạn checkout sang nhánh khác nếu bạn chưa commit những thay đổi hiện tại, hay nếu bạn cố tình đi qua nhánh khác có thể những file bạn đang thay đổi [sửa, thêm] sẽ bị mất.

Trong trường hợp này bạn có thể dùng git stash dùng để lưu lại những thay đổi trên working directory để sử dụng lại sau này.

Một số lệnh git stash thường dùng:

  • git stash [tạo một stash]
  • git stash push [tạo một stash], git stash push -m “my-stash” [tạo một stash với message để dễ nhớ]
  • git stash pop stash@{2} [xoá một stash] – stash@{2} được lưu như một stash id trong danh sách các stash, stash list là một ngăn xếp [stack]
  • git stash apply stash@{2} [apply stash trở lại nhánh hiện tại]
  • git stash list [liệt kê tất cả các stash]
  • git stash clear [xoá tất cả stash]

Xem thêm những thủ thuật xịn cho Javascript tại Youtube của tôi.

Tham khảo

A Visual Git Reference

Using Git Effectively

Video liên quan

Chủ Đề