Git và một số điều cần biết!

Mục lục

  • Thế nào là Repository, Branch.

  • Làm thế nào để xoá một branch ở phía local, làm thế nào để xoá một branch remote.

  • Làm thế nào để push một branch ở local lên remote dưới một cái tên khác.

  • Thế nào là git rebase. Phân biệt rebase với merge.

  • Thế nào là git fetch. Phân biệt fetch với pull.

  • Thế nào là git stash .

  • Làm thế nào xoá bỏ trạng thái của một vài commit gần đây.

  • Làm thế nào để gộp một vài commit thành 1 commit duy nhất.

  • Phân biệt git reset, git reset --hard, git reset --soft.

  • Thế nào là cherry-pick, khi nào thì dùng cherry-pick.

  • Git Flow

    Nội dung

    Git là gì? Git là một hệ thống kiểm soát phiên bản (version control system - VCS) dùng để theo dõi các thay đổi trong các tập tin và cách phối hợp sao cho nhiều người có thể cùng làm việc trên những tập tin đó. Git được sử dụng chính trong lĩnh vực phát triển phần mềm, nhưng Git cũng có thể được sử dụng để theo dõi các thay đổi trong bất kỳ tập tin nào. Git là hệ thống điều khiển phiên bản phân tán, có tốc độ xử lý khá nhanh, đảm bảo toàn vẹn dữ liệu và hỗ trợ khá hiệu quả cho các workflow phân tán, phi tuyến tính. Github là gì? Mình biết là có rất nhiều bạn khi nghe nói đến Gitsẽ nghĩ ngay đến Github và có thể sẽ có một số hiểu lầm với họ. Cũng xin nhắc lại rằng, Git là tên gọi của một mô hình hệ thống. Như mình đã giải thích ở trên, các máy tính có thể clone lại mã nguồn từ một repository và Github chính là một dịch vụ máy chủ repository công cộng, mỗi người có thể tạo tài khoản trên đó để tạo ra các kho chứa của riêng mình để có thể làm việc. Tại sao nên sử dụng Git?

    • Git có nhiều ưu điểm, đặc biệt nó đã thay đổi cách các lập trình viên chia tách và nhập các nhánh phần mềm. Trong khi với SVN, việc này luôn là ác mộng vì các xung đột trong quá trình tách/nhập, còn với Git việc này quá dễ dàng.
    • Tốc độ thực hiện sao với SVN thì phải nói là khác nhau một trời một vực, đặc biệt là các thao tác checkout hay merge. Chưa nói đến kích cỡ (size) của local repository được nén rất nhỏ, giảm thiểu tối đa dung lượng lưu trữ chiếm trên ổ đĩa. 💪
    • Git hoạt động theo mô hình Local và Remote repositories: Bạn hoàn toàn có thể làm mọi thao tác/công việc trên bản sao (local) repository trên máy tính. Sau khi hoàn thiện công việc rồi có thể đẩy lên Remote repository để chia sẻ cho người khác.

    1. Thế nào là repository

    Repository : Tạm dịch là kho chứa. Có thể coi đây là nơi chứa source code, thông tin cần thiết, lịch sử thay đổi, nội dung thay đổi của từng cá nhân tác động đến project.

    • Tất cả dữ liệu của Repository đều được chứa trong thư mục bạn đang làm việc dưới dạng folder ẩn có tên là .git.

    • Nếu như bạn muốn theo dõi một dự án cũ trong Git, bạn cần ở trong thư mục của dự án đó và gõ lệnh sau: git init

    • Repository được chia làm 2 loại là : Local Repository và Remote Repository.

      • Local Repository : Là kho chứa trên máy tính cá nhân dành cho 1 người sử dụng, sẽ chứa các local branch.
      • Remote Repository : Là kho ch để chia sẻ giữa nhiều người và bố trí trên một máy chủ từ xa, chứa các remote branch.
    • Được hiểu là nhánh của Repository dùng để phân nhánh và ghi lại luồng của lịch sử. Branch đã phân nhánh sẽ không ảnh hưởng đến branch khác nên có thể tiến hành nhiều thay đổi đồng thời trong cùng 1 repository. Khi làm việc , mỗi nhánh hoạt động riêng biệt và không ảnh hưởng đến các nhánh khác. Khi tạo mới 1 project thì nhánh mặc định sẽ là nhánh "master".

    2. Làm thế nào để xoá một branch ở phía local, làm thế nào để xoá một branch remote.

    Branch : Local Branch : Chính là branch lưu ở trên máy tính cá nhân. Để kiểm tra branch, sử dụng lệnh :

     $ git branch
    

    Để tạo 1 branch mới và di chuyển sang branch này, sử dụng lệnh :

     $ git checkout -b <name_branch>
    

    VD:

     $ git checkout -b <new_branch>
    

    Kết quả là bạn sẽ thấy bạn đang đứng ở nhánh new_branch 😄 Để di chuyển sang một branch có sẵn, sử dụng lệnh:

     $ git branch <name_branch>
    

    Để xóa 1 branch đã được merge:

     $ git branch -d <name_branch>
    

    hoặc

     $ git branch --delete <branch_name>
    

    Để xóa 1 branch thẳng tay dù được merge hay chưa:

     $ git branch --delete --force <branch_name>
    

    hoặc

     $ git branch -D <branch_name>
    

    Thật tuyệt vời phải không =))) Remote Branch: Là branch lưu ở Remote Repository.

    Để hiển thị remote branch, sử dụng lệnh:

     $ git branch -r
    

    Để xóa 1 remote branch:

     $ git push --delete <name_remote> <name_branch>
    

    hoặc

     $ git push <name_remote> --delete <name_branch>
    

    3. Push 1 branch ở local lên remote với một cái tên khác.

    Giả sử remote branch trùng tên với local branch, ta sử dụng lệnh :

     $ git push <name_remote><name_branch>
    

    VD: Push từ nhánh develop của local lên nhánh develop của remote.

     $ git push origin develop
    

    Vậy trường hợp remote branch khác tên với local branch thì sao. Ta sẽ dùng lệnh:

     $ git push <remote_name><local_branch_name>:<remote_branch_name>
    

    VD: push từ nhánh develop của local branch lên nhánh master của remote branch:

     $ git push origin develop:master
    

    4. Phân biệt Git rebase với Git merge

  • Trong Git, có hai cách chính để tích hợp các thay đổi từ nhánh này vào nhánh khác: đó là mergerebase. rebase: giả sử bạn đang làm việc trên nhánh working_branch và trước đó bạn có nhánh rebase_branch Để rebase nhánh "rebase_branch" vào nhánh "working_branch" ta sử dụng lệnh:

     $ git rebase rebase_branch
    

    merge: bạn đang ở nhánh "working_branch" và trước đó có nhánh "merge_branch". Để merge 2 nhánh này ta dùng lệnh:

     $ git merge merge_branch
    

    VD cho dễ hiểu nhé 😄 Giả sử ban đầu đã có 3 commit A, B, C: Sau đó anh E tạo commit E, chị D tạo commit D và đều tách ra từ commit C. (chắc là xung đột tình cảm =))) ) Vậy giờ ta phải giải quyết xung đột cho 2 anh chị này bằng 2 cách mergerebase Merge : Tại đây, 2 commit cuối cùng của từng nhánh sẽ được tích hợp lại với nhau tạo thành 1 commit theo kiểu hình thoi. Khi thực hiện merge, các commit đã tồn tại không bị thay đổi, nó chỉ sinh ra 1 commit tích hợp 2 commit mới nhất. Tuy nhiên, nếu bạn có hàng chục commit D và E thì bạn có có hàng chục viên kim cương M lúc này bạn sẽ thấy log rối đến mức nào!? Rebase: Đầu tiên. Nó đi tới commit cha chung của 2 nhánh này. (Tức là nơi mà 2 nhánh này bắt đầu phân tách). Sau đó nó tìm sự khác biệt trên các commit, lưu lại các thay đổi đó vào một tập tin tạm thời, khôi phục lại nhánh hiện tại về cùng một commit với nhánh bạn đang rebase, và cuối cùng áp dụng lần lượt các thay đổi. Các commit của nhánh rebase_branch sẽ là commit mới nhất. Sau khi rebase, các bạn xem lịch sử commit bằng lệnh :

     $ git log --oneline
    

    sẽ thấy sự thay đổi commit. Tuy nhiên bạn không cần lo lắng vì như thế là đúng quy trình rồi =))) Lợi thế của rebase là kim cương hình dạng tránh được, và lịch sử vẫn đẹp đường thẳng. Vậy cả 2 cách đều có thể khiến mọi thứ "quay lại với nhau" . Tuy nhiên, tùy vào từng trường hợp mà ta nên sử dụng giữa rebase và merge.

    5. Thế nào là git stash ?

  • Git stash được sử dụng khi muốn lưu lại các thay đổi chưa commit, thường rất hữu dụng khi bạn muốn đổi sang 1 branch khác mà lại đang làm dở ở branch hiện tại.

  • Muốn lưu toàn bộ nội dung công việc đang làm dở, bạn có thể sử dụng git stash như sau:

     $ git stash save
    

    hoặc

     $ git stash
    
  • Bạn có thể git stash bao nhiêu lần tuỳ thích và mỗi lần đó git sẽ lưu toàn bộ lần thay đổi đó như 1 phần tử trong 1 stack. Một số câu lệnh với git stash mà bạn nên nhớ: git stash list: để xem các stash đã lưu git stash pop : để apply stash gần nhất git stash apply [email protected]{<index>} : để apply stash git stash drop [email protected]{<index>} : để xóa stash git stash clear : để xóa toàn bộ stash

  • Một điều nữa đó là các file đã stage sẽ trở thành unstaged sau khi apply stash

    6. Thế nào là git fetch. Phân biệt git fetch với pull?

    git fetch :

  • Là việc tải dữ liệu từ 1 remote repository khác, nhưng nó không tích hợp bất kì dữ liệu mới này vào các tập tin của bạn. Fetch được xem là tuyệt vời nhất cho việc xem những gì đã xảy ra trên remote repository kia. Vì vậy, bạn hãy yên tâm khi fetch. Nó không làm hỏng code, hay phá hoại bất cứ điều gì.
    Phân biệt git fetch và git pull

  • Git pull - sử dụng để update lại branch đang đứng hiện tại với thay đổi mới nhất từ remote repository kia. Điều này có nghĩa là nó không chỉ tải dữ liệu mới. nó cũng tích hợp các tập tin sao lưu hiện tại. Điều này có 1 số hậu quả:
    • Trước khi "git pull", hãy cố gắng hợp nhất tất cả thay đổi từ local.
    • Thứ 2 đó là mình nên khuyến khích sử dụng git pull với với 1 bản sao làm việc sạch sẽ. Điều này có nghĩa là bạn không nên có thay đổi bất kì nào ở local trước khi bạn "git pull".

    7. Làm thế nào xoá bỏ trạng thái của một vài commit gần đây?

    Cách 1 : sử dụng Git revert
  • Git revert có thể tạo một commit với với nội dung đảo ngược lại một commit cũ. Ví dụ commit cũ có mã là (commit_id) thì câu lệnh sẽ là:

     $ git revert <commit_id>
    
  • Nếu sau khi git revert bạn lại muốn quay lại trạng thái trước khi đảo ngược thì sao ? Câu trả lời là git revert lại chính revert commit vừa mới tạo.

    Cách 2: sử dụngGit reset

    • Lệnh này sẽ xoá toàn bộ các commit trước đó và đưa branch về trạng thái của commit có name_commit đã chọn.
      $ git reset --hard <commit_id>
    

    --hardcó nghĩa là bỏ commit đi và bỏ cả những thay đổi chưa được commit trong working space. Khi này môi trường sẽ hoàn toàn "sạch sẽ" như thời điểm trước khi commit.

    8. Làm thế nào để gộp một vài commit thành 1 commit duy nhất?

    • Khi bạn làm việc nhóm cũng như các dự án, sau khi bạn push code lên github. Một ngày kia nhận được yêu cầu bất chợt là phải cần sửa lại chỗ code này. Thế là bạn sẽ phải ngồi cải tiến code, coding style.
  • Sau khi chỉnh sửa, code của bạn lại được commit lên Github. Khi đó trên github của bạn sẽ có nhiều commit trên 1 request. Vì vậy bạn phải gộp các commit này thành 1 commit trước khi nó được merge. Vậy làm thế nào để gộp nhiều commit thành 1 commit?

  • Cách giải quết đưa ra là "rebase" các commit này.

  • Sử dụng lệnh dưới đây để thực hiện việc gộp commit.

    $ git rebase -i <id_commit_end> || $ git rebase -i HEAD~<index>
    

    <id_commit_end> là id của commit cuối trong nhóm cần gộp. <index>: số commit cần gộp. Sau đó, cửa sổ làm việc hiện lên, Ta có các lựa chọn pick|squash|fixup các commit trước khi save. Hoặc một cách gộp commit nữa mình rất hay dùng đó là sử dụng lệnh:git reset --soft <commit_id> Sử dụng lệnh này sẽ làm các file đã commit sẽ trở về trạng thái trước đó. Ví dụ: mình đã tạo ra các commit như sau: Giờ mình sẽ gộp commit 2 và commit 1 lại với nhau. Đầu tiên dùng lệnh : git rebase -i 58aa131 Commit nào muốn giữ thì xóa pick và thay bằng p, commit nào muốn gộp thì sẽ xóa pick thay bằng s. Ở đây mình sẽ chọn như hình: Sau đó ctrl + X , rồi nhấn Y để thoát. Kiểm tra lại bằng lệnh: git log --oneline sẽ thấy 2 commit này sẽ bị chuyển thành 1 commit mới nhưng tên commit vẫn là "commit 1".

    9. Phân biệt git reset, git reset --hard, git reset --soft.

    Git reset Sử dụng lệnh

     $ git reset <commit_id>
    

    khi ta muốn di chuyển HEAD đến commit reset và giữ nguyên tất cả thay đổi của file đến vị trị hiện tại. Tuy nhiên sẽ loại bỏ thay đổi khỏi stage. Lệnh :

     $ git reset --hard <commit_id>
    

    Để loại bỏ tất cả sự thay đổi của file sau commit reset và trở về commit reset. Hãy suy nghĩ trước khi sử dụng lệnh này. Tiếp theo, một lệnh rất hay dùng đó là:

     $ git reset --soft <commit_id>
    

    Câu lệnh này được khuyến khích sử dụng khi muốn di chuyển HEAD đến commit reset do có ưu điểm là sẽ giữ nguyên tất cả thay đổi của file và các thay đổi ở stage.

    10. Thế nào là cherry-pick. Khi nào thì sử dụng cherry-pick?

  • Bỏ qua cách phát âm phức tạp thì cherry-pick thực chất là một cách để checkout một commit tại branch nhất định về branch hiện tại. Hay nói một cách dân dã thì cherry-pick dùng để bưng các thay đổi trong một commit trên một nhánh nào đó áp dụng về nhánh hiện tại.

  • Khi nào thì áp dụng cherry-pick. Mình có 1 ví dụ như sau. VD: Hôm nay mình gặp phải một trường hợp cần apply cùng một thay đổi cho cả 2 branches khác nhau trong project. Giả sử bạn có 2 branches khác nhau cho 2 khách hàng khác nhau, tạm gọi là branch A và branch B. Và bạn đang fix một bug nghiêm trọng cùng tồn tại trên hệ thống của cả 2 khách hàng, bạn muốn chỉ commit một lần nhưng có thể apply vào luôn cả 2 branches. Bạn có thể dùng lệnh cherry-pick của Git để giải quyết, như sau:

      $ git add -A
      $ git commit -m "Awesome Bug Fixed :("
      $ git checkout master
      $ git checkout "new_branch"
      $ git cherry-pick A
    

    cherry-pick sẽ lấy commit cuối cùng ở branch A merge vào branch new_branch. VD: Giả sử mình đang ở "branch1". Mình có thêm vào file 1 dòng như sau: Sau đó mình add và commit như bình thường. Tiếp theo mình checkout về nhánh master : $ git checkout master Tiếp tục checkout sang nhánh "branch2" bằng lệnh: $ git checkout -b branch2 Để lấy commit vừa rồi của branch1 và áp dụng vào branch2 thì ta sẽ sử dụng lệnh: $ git cherry-pick branch1 Vào code kiểm tra sẽ thấy sự thay đổi. Quá hay phải không nào =)))

    11. Git Flow

  • Đây là một vấn đề kinh điển, và có rất nhiều bài viết về nó, tuy nhiên đa phần là dịch từ bản gốc ra và sao chép lại một vài câu lệnh, và câu hỏi đặt ra là trong thực tế, git flow diễn ra như thế nào?

  • Khi làm việc nhóm, điều quan trọng là phải đồng nhất về quy trình làm việc. Git cho phép bạn thực hiện bằng nhiều con đường, nhiều cách khác nhau.Tuy nhiên, nếu bạn không đồng nhất quy trình làm việc chung trong nhóm của bạn, sự nhầm lẫn là không thể tránh khỏi.

  • Dưới đây, chúng ta hãy cùng xem quy trình làm việc đã được phổ biến trong những năm gần đây về Git. Ta gọi đó là : Git Flow. Tóm lại, Git Flow là gì?

    • Có nhiều quan điểm nhưng theo mình git-flow cung cấp các lệnh thứ tự cho người dùng. Mỗi lệnh thực hiện nhiều tác vụ tự động và theo một thứ tự xác định trước. Qua các lệnh này, ta sẽ hình thành nên quy trình làm việc.
    • git-flow không phải là git, cũng không có nghĩa có thể thay thế cho git. Nó là 1 tập hợp các kịch bản kết hợp với các lệnh Git tiêu chuẩn một cách thông minh.
    • Nói 1 cách rõ ràng, bạn không phải sử dụng các tập lệnh git để sử dụng quy trình của git-flow. Bạn có thể dễ dàng tìm hiểu quy trình làm việc riêng lẻ - và đơn giản thực hiện các lệnh này một mình. Tuy vậy, các đoạn mã lệnh theo git-flow giúp bạn tránh khỏi phải ghi nhớ tất cả những điều này, giúp cho việc đơn giản hóa quá trình làm việc chuẩn.

    Kết Luận

    Có thể nói Git là một công nghệ rất tuyệt vời mà bất cứ ai học về CNTT, đặc biệt là làm việc với source code và làm việc nhóm nên biết. Lợi ích của Git là cực kỳ to lớn mà chúng ta vẫn còn chưa thể khai thác hết. Trên đây là những gì mình học được và cũng là một số chia sẻ của mình về Git. Mọi người có góp ý xin hãy để lại comment. Xin cảm ơn!