Quản lý code với git

Git-Logo_720x260

Giới thiệu chung

Là một developer, có lẽ các bạn không lạ gì với khái niệm “thùng chứa”, “quản lý version”, “repository”.

Trước đây tôi đã từng có dịp sử dụng qua SVN nhưng giờ đây khi sử dụng git tôi cảm thấy yêu thích cách làm việc của nó hơn. Và cũng không phải tự nhiên là apple lại tích hợp git vào xcode.

Với SVN, nó quan niệm có một server chính đặt ở server đâu đó là các client kết nối vào 1 server chung. Với git thì khác. Git quan niệm rằng các thùng chứa là ngang hàng nhau và không có cái gì gọi là repo server.

Khái niệm các repo có vai trò ngang hàng nhau là một điều đặc biệt và là một thế mạnh rất lớn trong git. Điều đó có nghĩa là nếu có 3 người A,B,C cùng làm việc trong 1 project. Thì bản thân repo trên máy của người A, người B, và người C có thể kết nối được với nhau.

Máy người A kết nối được với máy người B và ngược lại, máy A cũng có thể kết nối được với máy C và ngược lại. Tương tự máy B có thể kết nối được với máy C và ngược lại. Như vậy là chúng ta có thể có đến 6 đường kết nối.

Để kết nối được với một repo khác người ta sử dụng một khái niệm gọi là remote.

Trên thực tế khi làm việc với nhau thì không như vậy, vì không phải máy ai cũng cài một “git server” để người khác kết nối được với mình. Thông thường thì chúng ta sẽ sử dụng một repo chung và các máy kết nối vào repo đó.

Có 2 “git repo server” được sử dụng nhiều là github.com và bitbucket.org. Để đơn giản, trong bài viết này tôi sử dụng “git repo server” để chỉ repo được đặt trên bitbucket hoặc github.

Trên thực tế khi có 2 người cùng làm việc với 1 project thì thông thường sẽ tạo một repo trên github hoặc bitbucket và repo trên máy người A sẽ kết nối với repo trên github và máy người B cũng kết nối với repo trên github/bitbucket. Từ đó source code của người A và người B sẽ được đồng bộ với nhau thông qua repo trên github/bitbucket.

Vì vậy, trước khi sử dụng git thì bạn nên đăng kí một tài khoản trên github.com hoặc bitbucket.org.

Cài đặt git

Để cài đặt git và tìm hiểu thông tin về git các bạn có thể truy cập website http://git-scm.com/

Sau khi cài đặt git thì bạn cũng cần cài thêm GUI Client cho phép tương tác bằng GUI trực quan. Với kinh nghiệm cá nhân tôi thì tôi thường hay sử dụng command của git để làm việc và chỉ sử dụng GUI Client trong trường hợp commit. Vì lúc commit sử dụng GUI sẽ trực quan cho chúng ta biết là mình đang commit những file nào và nó có thay đổi gì.

SSH Key

Chúng ta đều biết thông thường để kết nối với server sẽ cần username và password. Với git, để kết nối với server nó chứng thực máy bạn với server bằng ssh key.

ssh key sẽ có 2 key, một private key và một public key. Bạn sẽ tạo cả 2 key trong máy tính của bạn và đưa public key cho server chứa repo mà bạn muốn kết nối vào. Như vậy là máy tính của bạn đã được chứng thực. Sau này mỗi lần bạn lấy code từ server về hoặc đưa code lên server sẽ không cần nhập username và password nữa.

Chẳng may máy tính của bạn chứa repo quan trọng và nếu lỡ một ai đó đánh cắp máy tính của bạn. Bạn không muốn người ta can thiệp và repo của bạn thì chỉ việc lên repo server và xóa cái public key là xong.

Trên mac, ssh đã được cài đặt sẵn trên hệ điều hành nên để tạo ssh key khá đơn giản.

Để kiểm tra máy tính đã cài ssh chưa bạn mở terminal và sử dụng lệnh :
ssh -v

Tiếp theo, chúng ta sẽ sử dụng lệnh
ls -a ~/.ssh
để kiểm tra máy đã có ssh key chưa

Trong trường hợp này là ssh key chưa được tạo. Nếu máy bạn đã có ssh key thì sẽ tồn tại 2 file id_* trong máy như trường hợp dưới đây

Bây giờ để tạo ssh key bạn sử dụng lệnh
ssh-keygen

Chấp nhận thư mục mặt định để đặt ssh key.
Sau đó nếu nó hỏi password thì bạn có thể bỏ trống và hoặc password mà bạn muốn.
Và cuối cùng, nếu tạo thành công, bạn sẽ có thể thấy kết quả như sau :

Cài đặt public key vào tài khoản Bitbucket
Sau khi đã tạo xong ssh key ở trên đến bước này các bạn cần cung cấp thông tin public key cho bitbucket.
Trên terminal sử dụng lệnh

pbcopy < ~/.ssh/id_rsa.pub

để copy public key.

Trên bitbucket vào Manager accout -> SSH Keys và add key. Hãy đặt label là tên bất cứ gì bạn muốn và paste public key vào mục key. Click add key là xong.

Các lệnh hay sử dụng trong git

Tạo một repo

Để tạo mới repo bạn sử dụng lệnh

git init

Hãy thử đặt 1 file gì đó vào folder repo của bạn. Sau đó để add file này vào repo thì bạn phải sử dụng lệnh add.

git add filename

Trên xcode, khi bạn tạo mới 1 file thì nó tự động add vào repo của bạn rồi.

Tiếp tục để lưu những thay đổi vào repo trên máy bạn sử dụng lệnh

git commit -am "message"

Tham số -am là commit tất cả mọi thay đổi và cho biết kèm theo phía sau là message của thay đổi.

Theo kinh nghiệm làm việc của cá nhân tôi, khi commit thì tôi thường sử dụng GUI để commit. Nó sẽ cho phép mình kiểm soát commit theo 1 file hoặc nhiều file như mình mong muốn và không cần phải commit hết tất cả các thay đổi. Ngoài ra với GUI client, nó cho phép mình biết được những file nào đã được thay đổi và thay đổi những gì.

Khác với SVN, bạn commit không cần message. Với git nó yêu cầu chặc chẽ hơn, bạn phải nhập message để commmit.

Trong quá trình code, bạn nên thực hiện lệnh commit thường xuyên. Nên commit nếu có 1 dòng code quan trọng. Nên commit nếu bạn đã thực hiện xong 1 tính năng nào đó. Nên commit nếu bạn đã hết lỗi. Thông thường cứ mỗi sau 5-10ph, nếu đã hoàn thành một công việc gì đó bạn nên commit.

Commit code vừa là một khoảng thời gian nghỉ giữa, giúp bạn nhìn lại code của mình. Nó vừa là một cách giúp bạn kiểm soát code của mình.

Ví dụ trong quá trình code bạn đã thực hiện 5 lần commit :

commit1
commit2
commit3
commit4
commit5

Tại commit5 bạn tiếp tục code và lỡ tay xóa mất 1 file nào đó. Bạn có thể quay về tại thời điểm commit5 và file mới vừa xóa sẽ được phục hồi lại giống như tại thời điểm mà bạn thực hiện commit5.

Ngoài ra tại thời điểm commit5 bạn cũng có thể quay về tại thời điểm code của commit3. Khi bạn quay về commit3 đồng nghĩa với việc các code của commit4 và commit5 sẽ bị xóa mất.

Để thực hiện công việc xóa những thay đổi của code chúng ta sẽ sử dụng đến lệnh reset

Trở lại dòng một commit trước đó

Cấu trúc thường hay sử dụng của lệnh reset :
git reset --mode [commit]

Để tìm hiểu nhiều hơn về lệnh reset bạn sử dụng lệnh
git reset --help

–mode : thường được hay sử dụng là –hard : mode này sẽ xóa tất cả những thay đổi trước đó để đưa code của bạn về đúng tại thời điểm của [commit]. Ngoài ra có các mode khác như –soft, –mixed bạn có thể tự tìm hiểu thêm.

[commit] có thể là : HEAD hoặc HEAD^ hoặc HEAD^^

Như ví dụ phía trên chúng ta có 5 commit. Sau commit5 tôi thực hiện một số thay đổi nhưng không vừa ý và sau đó nếu tôi thực hiện lệnh
git reset --hard HEAD

Sau khi thực hiện lệnh này, code của tôi sẽ trở về như tại thời điểm commit5.

Còn nếu tôi thực hiện lệnh :
git reset --hard HEAD^

thì code của tôi sẽ trở về như tại thời điểm commit4. Như vậy bạn cũng có thể đoán được kết quả của
git reset --hard HEAD^^

Kết nối với repo khác

Như ban đầu tôi đã giới thiệu. Quản lý source là cho phép mình quản lý version code và đồng bộ code với người khác. Ngoài ra nếu bạn code 1 project một mình, bạn cũng có thể sử dụng bitbucket như là một server backup data của bạn và kiểm soát công việc đã làm.

Để tiếp tục, bạn hãy lên bitbucket và tạo thử cho mình một repo.
Sau khi tạo một repo bạn có thể sẽ thấy
git repo, bitbucket

Trong mục ssh chứa link để bạn clone repo này về máy mình.

Để clone một repo từ một repo khác, sử dụng terminer đi đến folder mà bạn muốn chứa repo và sử dụng lệnh

git clone repo-url

Nếu máy bạn đã chứa một repo rồi và bạn muốn đồng bộ repo này với repo mới tạo trên bitbucket thì sao ?
Đây cũng là trường hợp bạn sử dụng xcode. Khi bạn tạo mới một project và tick vào “Create local git repository for this project” thì project bạn tạo ra chính là một git repo.

xcode, git, repo

Như vậy lúc này hãy sử dụng terminal và đi đến folder chứa project này, sau đó chúng ta sẽ kết nối local repo này với bitbucket bằng cách sử dụng lệnh :
git remote add origin repo-url

Trong đó origin là tên của remote. Một remote chính thường có tên là origin. Nếu bạn clone một repo từ server về thì nó tự động tạo một remote kết nối với server đó có tên là origin.

Bạn cũng có thể thay thế origin bằng một tên khác do bạn nghĩ ra. Nhưng nếu nó là remote kết nối với repo chính thì nên có tên là origin.

Như ban đầu tôi đã giới thiệu, một repo client có thể kết nối với nhiều repo khác bằng remote. Như vậy để kết nối với nhiều repo khác nhau bạn chỉ cần tạo nhiều remote tương ứng.

Để liệt kê tất cả các remote có trong repo đó, bạn sử dụng lệnh
git remote -v

Để đổi tên một remote chúng ta sử dụng lệnh :
git remote rename remote-source-name remote-destination-name

Để xóa một remote trong repo, chúng ta sử dụng lệnh
git remote rm remote-destination-name

Câu lệnh này chỉ xóa đường link dẫn repo của chúng ta đến repo đích, nó không ảnh hưởng dữ liệu của 2 repo đang có.

Lờ đi các file không cần thiết

Nếu 2 người cùng làm việc với một project, ví dụ như một project ios bằng xcode. Xcode sẽ tạo ra một số file mà chúng ta không nên đưa nó lên server. Ví dụ như file chứa thông tin các điểm debug, nếu đưa những file này lên repo server thì sẽ gây conflic với người khác và điều này là không cần thiết.

Git cho phép chúng ta cấu hình các file và các folder có thể lờ đi không xuất hiện trong GUI commit để chúng ta có thể làm việc tiện hơn. Để cấu hình nó, chúng ta sẽ tạo một file có tên
.gitignore
ở thư mục chính của ứng dụng.

Thông thường, nếu là một project xcode, file .gitignore của bạn nên như sau :

Nhánh

Khi bạn tạo mới một repo mặc định bạn sẽ ở nhánh master của repo đó.

Tạo sao phải sử dụng nhánh ?
Bạn đang ở nhánh master, bạn muốn thử nghiệm một tính năng mới và không muốn ảnh hưởng gì đến code hiện có ? Bạn có thể tạo ra một nhánh B, mọi thứ trong nhánh B tạo ra sẽ được kết thừa hết những gì đã có trong master. Sau đó bạn có thể thử mọi thứ bạn muốn ở nhánh B. Đến khi bạn quay lại nhánh master thì code của bạn lại trở về như cũ.

Nếu những thay đổi trong nhánh B không mang lại hiệu quả gì bạn có thể xóa nó và không ảnh hưởng gì hết. Còn nếu tính năng mà bạn thực hiện trong nhánh B có ích, bạn có thể sử dụng lệnh merge để hợp nhất nhánh B vào nhánh master và giờ đây nhánh master của bạn có hết những tính năng trong nhánh B như bạn đã thử nghiệm.

Khi nào sử dụng nhánh ?

Một trong những thế mạnh khác của git là nhánh. Với git, việc quản lý nhánh rất dễ dàng. Ví dụ trong một project, một người làm giao diện và một người xử lý dữ liệu thì 2 người có thể tạo 2 nhánh A,B khác nhau để làm việc và cứ cuối mỗi ngày thì người A này sẽ đồng bộ code nhánh A của mình với nhánh B của người B và người B cũng sẽ đồng bộ code nhánh B của mình với nhánh A của người A. Và kết quả là cứ cuối mỗi ngày nhánh A và nhánh B sẽ có code giống nhau. Giả sử công việc của 2 người hết 10 ngày, sau 10 ngày thì sẽ đồng bộ code của 2 nhánh với nhánh master.

Nghe quy trình trên có vẻ phức tạp nhưng thực ra việc này giúp giảm bớt rất nhiều conflict và công việc của 2 người vẫn làm song song và không ảnh hưởng nhiều đến người khác. Giúp công việc develop tốt hơn.

Nhánh master thông thường là nhánh chính của ứng dụng. Ví dụ bạn thử nghiệm một tính năng mới và muốn không ảnh hưởng đến code chính bạn có thể tạo một nhánh mới và sau khi xong sẽ hợp nhất lại với nhánh master. Việc hợp nhất 2 nhánh lại được gọi là merge.

Sử dụng nhánh thế nào ?

Để liệt kê các nhánh hiện có trong repo, sử dụng lệnh :
git branch -v

Kết quả của câu lệnh này trông như sau:
git branch

Trong đó dấu “*” cho biết mình hiện đang ở nhánh ipad.

Để tạo mới một nhánh sử dụng lệnh :
git branch branch-name

Câu lệnh trên sẽ tạo ra một nhánh có tên branch-name. Để chuyển đổi qua một nhánh khác bạn sử dụng lệnh :
git checkout branch-name

Để hợp nhất 2 nhánh chúng ta sẽ sử dụng lệnh merge. Để merge nhánh B vào nhánh A có nghĩa là chúng ta sẽ đưa tất cả những thay đổi trong nhánh B vào nhánh A. Nhánh A sau khi merge sẽ bao gồm code của nhánh B. Tùy vào thứ tự dòng code sẽ xảy ra conflic hay không. Nhánh B sau khi được merge vào nhánh A thì code trong nhánh B không có gì thay đổi.

Để merge nhánh B vào nhánh A đầu tiên chúng ta sẽ phải đảm bảo rằng chúng ta đang ở nhánh A. Nếu hiện tại bạn chưa phải ở nhánh A thì hãy sử dụng lệnh checkout để chuyển qua nhánh A, và sau đó sử dụng lệnh merge :
git checkout A
git merge B

Kết quả của lệnh merge là dữ liệu của nhánh B sẽ được đưa vào nhánh A và nếu không xảy ra conflic thì tất cả các thay đổi sẽ được tự động commit vào local repo. Nếu xảy ra conflic thì bạn phải tự tay sửa conflic và sau đó commit dữ liệu bằng command line (git không cho commit bằng GUI trong trường hợp này).

Đồng bộ code trên server

Đến thời điểm hiện tại bạn đã hiểu câu lệnh add để đưa 1 file vào repo, câu lệnh commit được dùng để xác nhận với local repo rằng mọi sự thay đổi trong file này cần được repo ghi nhận lại để so sánh với các sự thay đổi khác (nếu có) sau này.

Tất cả những thay đổi trong repo hiện tại mới chỉ xảy ra trên local. Để đưa những thay đổi trong máy của mình đến với máy khác và lấy dữ liệu những thay đổi từ máy khác về chúng ta sử dụng đến 2 câu lệnh khác là pushpull

Để push (đẩy dữ liệu lên) và pull (lấy dữ liệu về) từ một repo khác thì chúng ta cần phải có remote đến repo đó. Trong trường hợp này là chúng ta push và pull từ bitbucket.

Để lấy dữ liệu từ repo khác về sử dụng lệnh pull với cấu trúc sau :
git pull remote-name branch-name

Lệnh pull bao gồm cả tên remote và tên nhánh.
Như vậy thông thường để pull dữ liệu từ bitbucket chúng ta sẽ dùng lệnh :
git pull origin master

Trong git, câu lệnh git pull origin master cũng có thể được viết :
git pull

Để đẩy dữ liệu từ local lên repo server chúng ta sử dụng lệnh push:
git push remote-name branch-name

Trong git, câu lệnh git push origin master cũng có thể được viết :
git push

Câu lệnh này sẽ đưa tất cả dữ liệu của nhánh branch-name đã được commit lên repo server.

Để thực hiện thành công lệnh push này thì nó yêu cầu repo local của bạn phải là mới nhất so với repo trên server.

Ví dụ về pull và push
Để hình dung về pull và push hãy xem ví dụ sau :
Người A (repo A) và người B (repo B) cùng làm việc với một trong một project và cùng kết nối với repo server C.

Vào lúc 10h:00 ngày 1/5/2013 cả 2 cùng pull dữ liệu từ server C.
Vậy là repo A và repo B và repo C có code giống nhau
Sau khi pull về, người A tiếp tục code. Người B thì nhân ngày 1/5 đã đi chơi cả ngày.
Vào lúc 24h:00 ngày 1/5/2013, trước khi đi ngủ, người A đã dùng lệnh push để đẩy dữ liệu mà mình đã code cả ngày lên server.

So với lần pull gần nhất của người A (10h:00 hôm nay) thì dữ liệu trên repo server C chưa có thay đổi gì (Vì chưa có ai push code lên), nên dữ liệu của người A là mới nhất so với repo server C.
Và như vậy A push code lên server thành công.

Vào lúc 8h:00 ngày 2/5/2013, người B đi chơi về và ngồi vào bàn code. Đến 12h:00 cùng ngày, sau khi thực hiện lệnh commit cuối cùng, người B thực hiện việc đẩy code lên. Tuy nhiên server báo không thành công.

Lý do là, lần pull gần nhất của người B là 10h:00 ngày 1/5/2013, trong khi đó repor server C đã có sự thay đổi vào lúc 24h:00 ngày 1/5/2013 nên tại repo B đã bị out-update.

Để tiếp tục người B phải thực hiện lện pull từ server về. Lúc này sẽ xảy ra 2 trường hợp

Nếu những gì người B đã code không trùng với bất kì 1 file nào của người A. Lúc này người B pull về thì hệ thống sẽ tự động merge code của người B và code trên server. Sau đó người B có thể push bình thường.

Nếu người B code trùng 1 file nào đó mà người A đã code thì sẽ dẫn đến conflic và sau khi pull về hệ thống sẽ báo bị conflic tại file nào. Người B mở file đó lên để sửa conflic. Sau đó commit và có thể push lên server.

Kinh nghiệm : Trước khi code nên pull dữ liệu rồi hãy tiếp tục.

Conflic là gì ?
Conflic là trường hợp có 2 sự thay đổi trong một dòng code và máy tính không thể tự quyết định dòng code nào là “đúng”. Đúng ở đây có nghĩa là “ý đồ của lập trình viên”.

Thông thường trong 1 file bị conflic sẽ xuất hiện

Đoạn này thể hiện nó bị conflic khi 2 người cùng code chung vào 1 đoạn nào đó trong file. Bây giờ bạn chỉ việc nhìn vào đó và tự quyết định dòng code nào giữ lại, dòng nào xóa bỏ.

Tôi muốn đi về những đoạn code tại commit2, nhưng commit2 đã được push lên repo khác thì làm thế nào?

Trong phần trước tôi đã giới thiệu cho các bạn về lệnh reset để xóa bỏ những thay đổi và trở về một commit trước đó. Tuy nhiên lệnh reset chỉ áp dụng với các commit chưa thực hiện lệnh push.

Xem ví dụ sau :
git commit -am “commit1″
git commit -am “commit2″
git push
git commit -am “commit4″
git commit -am “commit5″

Như vậy là các commit1 và commit2 đã được push lên server còn commit4 và commit5 thì chưa.
Bạn chỉ thực hiện được lệnh reset với commit4 và commit5.

Để thực hiện đi về những thay đổi mà bạn đã push lên server thì bạn tự tìm hiểu thêm nhé !!!

Tham khảo thêm : http://www.expressmagazine.net/posts/view/840/git-he-thong-quan-ly-source-code

Tạ Phước Hải
(http://taphuochai.com/gioi-thieu-ve-git-quan-ly-version-code/)

SSS Full-stack Engineer

Love Silicon Straits and want to know more about our company culture, working environment or job vacancies?
Find out more at careers.siliconstraits.vn.

Silicon Straits
Be Challenged. Be Inspired. Be Different.




  • Wild Daisy

    A very detail tutorial. Great work! Thank you!

  • Lê Phú Khánh Huy

    Thank you!!!!!!!

  • lylientieu

    Thanks for tutorial. :)

Posted by

on August 16, 2013

in , ,

Comments

Follow us for more later

or subscribe with