Ở đây, Item là 1 generic. Khi bạn extending 1 generic type, bạn không cần khai báo lại <Item>. Item là kiểu dữ liệu generic đã được khai báo trong toàn bộ struct.
Giả sử bạn muốn khai báo func isFirstItem(:) như sau:
Lỗi này tương tự như ở phần 1, khi không phải mọi kiểu dữ liệu trong Swift đều có thể sử dụng toán tử (==) để so sánh. Vậy ở đây, bạn có thể extend generic với 1 mệnh đề where như sau:
Vì kiểu dữ liệu truyền vào của biến b là Person, mà Person thì không tuân theo Equatable, nên b không có func isFirstItem(:)
Note:
Việc extend 1 generic type với mệnh đề where cho phép bạn thêm 1 điều kiện mới cho extension, vì vậy func isFirstItem(:)chỉ được thêm vào extension khi Item thuộc kiểu Equatable.
Generic subclass
Có 2 cách để kế thừa 1 class cha là kiểu generic như sau:
Tạo 1 class con kế thừa những thuộc tính và func của class cha nhưng vẫn giữ cho class con là kiểu generic.
Tạo 1 class con nhưng là 1 class cụ thể.
Ở trong ví dụ trên, dễ thấy:
Box là một generic subclass của class Package vì bạn muốn chiếc hộp này đựng được bất kì thứ gì, vì vậy Box vẫn là kiểu generic nên khi khởi tạo bạn phải truyền vào một kiểu dữ liệu cụ thể.
RosePackage là một subclass cụ thể của class Package, gói RosePackage này sẽ dùng chỉ để đựng hoa hồng.
Trong trường hợp bạn muốn tạo các tableview khác nhau, thì sẽ dẫn đến việc duplicate các đoạn code này.
-> Sử dụng generic để giảm thiểu duplicate code.
Example1:
RoseTableViewController là 1 subclass cụ thể của 1 generic class.
Ở đây, bạn sử dụng generic để giảm duplicate code như sau:
Tạo 1 generic class tên BaseTableViewController chứa các thuộc tính, func cơ bản, và khai báo 1 placeholder T kiểu UITableViewCell để dễ dàng truyền các kiểu cell khác nhau vào.
Tạo 1 class RoseCell kiểu UITableViewCell.
Tạo class RoseTableViewController extends BaseTableViewController, class này dùng cell kiểu RoseCell nên chúng ta truyền kiểu RoseCell vào.
override lại các thuộc tính -> func numberOfRows tự thay đổi theo.
Example2 :
Trong trường hợp bạn muốn mỗi cell có item kiểu dữ liệu bất kì, bạn có thể khai báo 1 class BaseCell kiểu generic, rồi khởi tạo các cell mới extends BaseCell.
Note:
U ở phần khai báo BaseTableViewController và U ở phần khai báo BaseCell là khác nhau, ở đây mình đặt đều là U cho bạn đọc đỡ confuse.
Generic cho phép bạn viết function và type 1 cách linh hoạt, dễ dàng tái sử dụng, có thể hoạt động với bất kì loại nào tùy theo các yêu cầu mà bạn xác định. Với generic, bạn có thể tránh sự trùng lặp của code mà vẫn thể hiện í định của nó 1 cách rõ ràng.
Ở phần 1 của bài viết, bạn sẽ học được:
Generics là gì ?
Tại sao chúng hữu dụng ?
Cách sử dụng generic đơn giản.
Getting Started
Giả sử bạn được yêu cầu viết 1 phương thức tính tổng 2 số với kiểu dữ liệu bất kì. Thông thường, bạn sẽ viết các func riêng cho các kiểu dữ liệu riêng.
Dễ dàng nhận thấy, func addInt(::) và func addDouble(::) là 2 func khác nhau nhưng có thân hàm giống nhau. Bạn không chỉ có 2 func, mà code của bạn bị lặp lại.
-> Generic có thể được sử dụng để giảm 2 hàm này thành 1 và loại bỏ sự trùng lặp code.
Note
Trong các func ở trên, kiểu dữ liệu của num1 và num2 là giống nhau. Nếu kiểu dữ liệu của chúng khác nhau thì không thể cộng 2 số.
Generic Function
Generic func có thể hoạt động với bất kì kiểu dữ liệu nào. Dưới đây là code generic của addInt(::) ở trên, tên là addTwoNumbers:
Tưởng tượng bạn có 1 UITextfield, UITextfield đó có 1 trường gọi là placeholder. placeholder sẽ được thay thế bằng text khi bạn nhập text vào UITextfield.
Tương tự như vậy, func addTwoNumbers(::) sử dụng kiểu dữ liệu T như là 1 placeholder. Kiểu dữ liệu T sẽ được thay thế bằng 1 kiểu dữ liệu xác định do bạn quyết định như Int, Double, … mỗi khi func addTwoNumbers(::) dược gọi. Ta có thể thấy trong func addTwoNumbers(::) ở trên, num1 và num2 có cùng kiểu dữ liệu là T.
Note:
Sự khác biệt giữa generic func và non-generic func là: Ở tên của generic func thì có 1 kiểu dữ liệu placeholder (T) được khai báo trong cặp ngoặc nhọn (<T>). Cặp ngoặc nhọn nói với Swift rằng T là kiểu dữ liệu placeholder.
Bạn có thể thay T bằng các các tên khác như A, B, C, Element, … tùy bạn muốn. Việc thay đổi tên chỉ có tác dụng thay đổi tên của kiểu dữ liệu placeholder, chứ không thay đổi gì về tác dụng. Nên tránh đặt tên trùng với các kiểu dữ liệu xác định như Int, String, Double, … để tránh gây nhầm lẫn.
Và luôn đặt tên bắt đầu bằng chữ cái in hoa để người đọc hiểu rằng đó là 1 placeholder cho 1 kiểu dữ liệu chứ không phải cho 1 giá trị.
Tuy nhiên, khi khai báo func addTwoNumbers(::) như trên, bạn sẽ gặp lỗi như sau:
Lỗi này có nghĩa là, bạn không thể áp dụng toán tử + cho kiểu dữ liệu T vì không phải mọi kiểu dữ liệu trong Swift đều có thể cộng với nhau bằng toán tử (+). Để áp dụng được toán tử (+) cho kiểu dữ liệu T, bạn làm như sau:
Ở đây, bạn khai báo 1 protocol Summable có chứa toán tử (+) và cho T extends Summable. Khi đó có nghĩa là, kiểu dữ liệu xác định mà bạn muốn đưa vào để thay T thì kiểu dữ liệu đó phải extends Summable, và hiển nhiên là chúng sẽ phải khai báo toán tử (+).
Sử dụng generic:
Khi bạn gọi hàm addTwoNumbers, bạn sẽ thay T bằng 1 kiểu dữ liệu xác định. Tuy nhiên, vì T là kiểu Summable, nên kiểu dữ liệu xác định mà bạn muốn truyền vào cũng phải extends Summable và khai báo toán tử + cho kiểu dữ liệu đó.
Example 1: Gọi hàm addTwoNumbers cho 2 số kiểu Int:
Trước hết, phải cho Int extend Summable.
extension Int: Summable {}
Note: Vì bản thân các kiểu dữ liệu như Int, Double, String, … đã có chứa sẵn toán tử (+), nên không cần khai báo toán tử (+) cho chúng nữa.
let number1: Int = 5
let number2: Int = 10
let total = addTwoNumbers<Int>(num1: number1, num2: number2) // 15
Example 2: Khởi tạo và khai báo hàm addTwoNumbers(::) cho 2 số kiểu String :v
Note: Bạn có thể gọi hàm theo cách: addTwoNumbers<String>(num1: string1, num2: string2), hoặc bỏ <String> đi, vì Swift sẽ dựa vào kiểu dữ liệu bạn truyền vào num1, num2 để tự hiểu kiểu dữ liệu xác định bạn muốn thay cho T là String.
Điểm mạnh của IJKPlayer là low latency, nó có độ trễ khá thấp khi streaming, nhưng giả sử phát sinh tình huống cần record một đoạn video khi đang streaming thì phải làm thế nào? IJKPlayer không support sẵn.
Sau khi tham khảo 1 số blog của các bạn…..Trung Quốc và build được thành công thì mình share lại thông tin cho ai cần(vâng, ko hiểu tại sao cứ liên quan đến video, camera,Streaming thì tài liệu chỉ có thể tìm thấy bên….Trung Quốc. Không phủ nhận, các bạn ý giỏi thật )
Trước hết, các bạn cần build được IJKPlayer cho Android đã nhé, cụ thể có thể xem link bên dưới
OK, Sau khi các bạn biết cách chuẩn bị môi trường và build thành công, chúng ta cần tiến hành chỉnh sửa một chút trong thư viện IJKplayer(Mình sẽ không đề cập đến vấn đề pháp lý, licence ở đây)
Chúng ta cần check out source code và build thử nhé
git clone https://github.com/baka3k/IjkPlayerRecorder.git
cd IjkPlayerRecorder
// Khởi tạo project build cho Android
./init-android.sh
//IJKPlayer có sử dụng ffmpeg - nên việc build ffmpeg là bắt buộc
cd android/contrib
./compile-ffmpeg.sh clean
./compile-ffmpeg.sh all
//quay lại thư mục IJK và build IJKPlayer
cd ..
./compile-ijk.sh all
Nếu thành công – tức là bạn đã setup đầy đủ, có thể build IJKPlayer được, sẵn sàng cho việc thêm code để thêm chức năng recorder cho IJKPlayer.
Tiếp tục nhé
1.Trong thư mục ijkmedia/ijkplayer bạn tìm đến file ff_ffplay.h và khai báo các hàm sau:
Hôm nay mình sẽ tiếp tục với Git, bài này thực ra định để trong Series: Git from noob to master, tuy nhiên thì thấy cũng không hay ho lắm, nên quyết định tách riêng.
Những ai cần đọc bài này
Thực ra thì ai cũng đọc được, nhưng dùng thì không phải ai cũng cần, ví dụ nếu bạn quen thuộc với git graph (Tool Git GUI nào cũng có) và bạn thực sự hiểu thì cũng không cần lắm, tuy nhiên có một số command vẫn cần thiết, vì Tool không bao giờ toàn năng cả.
Super Strong git help everyday
git help everyday
Câu lệnh này thực sự strong,
Nó sẽ chỉ cho bạn biết các câu lệnh bạn cần dùng với các use-case, role khác nhau, ví dụ: Bạn là thằng dev đơn thuần hay là thằng quản lý repository.
Git log với các tham số
Thường thì ae sẽ làm như này
git log
hay ấn nhiều hơn một chút
git log --oneline --graph
để có cái sự đẹp đẽ như này
Về cơ bản đều sử dụng git log, git log hỗ trợ một số tham số dòng lệnh, rất mạnh mẽ, đặc biệt là khi được sử dụng kết hợp để filter, đây là mấy cái khá hay ho
–author = "Son Hoang" – Chỉ hiển thị các cam kết được thực hiện bởi một tác giả nào đó, tất nhiên là dùng tên hiển thị
–name-only – Chỉ hiển thị tên của các tệp đã thay đổi
–oneline – Hiển thị dữ liệu cam kết được nén thành một dòng
–graph – Hiển thị cây phụ thuộc cho tất cả các xác nhận
–reverse – Hiển thị các cam kết theo thứ tự ngược lại (Cam kết cũ nhất trước)
— after – Hiển thị tất cả các cam kết đã xảy ra sau một số ngày nhất định
— before – Hiển thị tất cả các cam kết đã xảy ra trước một số dữ liệu nhất định
Ví dụ: tôi cần tạo report hàng tuần được gửi mỗi thứ Sáu. Vì vậy, mỗi thứ Sáu, tôi sẽ chạy: git log --author="Alex Kras" --after="1 week ago" --oneline, chỉnh sửa một chút là xong.
Anw: git log có nhiều param khi sử dụng CLI, nó thực sự mạnh và khá tiện hơn so với các GUI Tool, hãy đọc man git-log, một cái nữa --pretty khá là hay ho :).
Có nhiều cách để play RTSP trên Android, ứng cử viên hàng đầu là VideoView sẵn có trên Android SDK. Tuy nhiên Video View có rất nhiều hạn chế khi chúng ta cần custom lại, ví dụ chỉnh sửa thêm thắt vào protocol, add các hiệu ứng hình ảnh vào video khi đang play, record, chuyển đổi các track..etc. Khi đó chúng ta phải lựa chọn một giải pháp khác, cụ thể ở đây mình đang nói đến
Exoplayer
IJKPlayer
Exoplayer (https://github.com/google/ExoPlayer) open source, apache licence, java core, rất dễ dàng để anh em lập trình android/java tiếp xúc nhưng điểm trừ của nó là độ trễ – low latency
Khi test với 1 số server RTSP, ExoPlayer cho ra độ trễ vào khoảng 1.2s đến 2.5s tùy chất lượng video out put. Còn IJKPlayer(https://github.com/bilibili/ijkplayer) thì có thể đạt 0.6s đến 0.8s. Điểm trừ của IJKPlayer là licence, IJKPlayer sử dụng FFMPEG, một thư viện rất nổi tiếng, nên nếu ko muốn dính dáng đến pháp lý khi bán sản phẩm, bạn nên cẩn thận khi lựa chọn nó cho khách hàng hoặc nhúng vào sản phẩm.
Để build được IJKPlayer cũng khá đơn giản(nếu bạn chuẩn bị đủ môi trường), hướng dẫn này mình viết chạy trên môi trường MAC OS nhé, Window hoặc Ubuntu cách làm tương tự, tuy nhiên môi trường chuẩn bị sẽ khác đi 1 chút
Check out source code
Các bạn có thể lấy source IJK từ nhánh chính ở trên hoặc từ repo bên dưới, repo này mình đã add thêm một vài chỉnh sửa liên quan đến RTSP và Recorder
Cài đặt NDK cho Mac: mình test thực tế thì thấy build trên bản NDK 10, NDK16(khá cũ) thì ok nhất, ko cần sửa lỗi và chỉnh lại config build, tất nhiên các bạn pro hơn có thể thử với version NDK khác
git clone https://github.com/baka3k/IjkPlayerRecorder.git
cd IjkPlayerRecorder
./init-android.sh
cd android/contrib
./compile-ffmpeg.sh clean
./compile-ffmpeg.sh all
cd ..
./compile-ijk.sh all
Sau khi build xong các bạn có thể tìm thấy file .so trong thư mục
Trong một vài lần chém gió về Git, thấy mọi người có vẻ chưa chú ý nhiều đến Git và sử dụng nó một cách hiệu quả.
Nhân đây xin mạn phép chém gió sơ qua để mọi người hoàn thiện.
Git command
Merge và Rebase (cont.)
Tiếp theo lần trước, mình sẽ bắt đầu với merge
Tại sao cần "merge"?.
Đơn giản là vì chúng ta lập trình cùng nhau, nếu bạn quen dùng SVN, sẽ thấy là khi 2 người cùng làm việc (tất nhiên là song song với nhau), sẽ nảy sinh nhu cầu tạo một phiên bản là sự kết hợp giữa 2 phiên bản source code của 2 người
Để merge được chúng ta cần 2 branch, dưới dây là một ví dụ
Chúng ta có 2 issue 55 và 56 trên hệ thống tracking, một cách nào đó developer có thể giải quyết 2 issues bằng một commit nhưng không phải lúc nào cũng thế và đặc biệt là không phải lúc nào cũng tốt, như là khi 1 trong 2 issues đó chưa được giải quyết triệt để tức là commit đó có vấn đề, rất khó để trace.
Mọi người nên làm cách clear hơn, 1 hoặc 2 developer sẽ fix nó, ở các nhánh khác nhau.
git checkout -b issue55
git checkout -b issue56
# -b for new branch
Họ modify source code ở mỗi nhánh và commit
Giờ chúng ta cần đưa phần thay đổi của 2 branch issue55 và issue56 vào master.
Như vậy mỗi khi merge, chúng ta kết hợp 2 branch (hoặc commit, phần 3 mình sẽ giải thích kỹ hơn tại sao lại là commit) vào một nhánh, hoặc nghĩ đơn giản hơn là đưa những thay đổi ở nhánh này vào nhánh kia bằng một commit mới (chứa toàn bộ thay đổi).
Ops!!! Git merge conflict
Tại sao conflict, đơn giản thôi, khi ở nhánh bạn merge vào, trong ví dụ issues55 là master bạn cũng có thay đổi. Ở ví dụ này là chúng ta có như sau
Mình có thay đổi ở master và issues1 cùng một dòng, git sẽ không biết chọn sự thay đổi nào. Nhiêu bạn sẽ sợ conflict và không biết làm thế nào và hỏi, câu trả lời là không thế nào cả, người merge sẽ là người quyết định. Có thể là chọn một trong 2, chọn cả 2 hoặc có thể phải làm lại cả đoạn đó.
Nghe nè: đừng bao giờ giải quyết conflict khi bạn không biết người khác vừa thay đổi cái gì, thật đấy.
Rebase
Cũng là một cách khác để đưa những thay đổi từ nhánh này sang nhánh khác nhưng khác hơn một chút: Bạn sẽ không tạo thêm commit mới mà đưa toàn bộ commit từ nhánh này sang nhánh kia.
Quay lại với ví dụ conflict ở bên trên. Khi merge master vào nhánh issues1 chúng ta có
.
Rebase thì sao:
git checkout issues1
git rebase master
.... # conflict and resolve
git add HelloWorldApp.java # add conflict file to tracking again
git rebase --continue
.
Như vậy có thể thấy là thay vì tạo commit mới, rebase sẽ đưa toàn bộ commit ở branch issues1 từ lúc branch out đặt lên cuối branch master, như vậy coi như là chúng ta không branch out từ một điểm trong quá khứ mà branch out từ lastest commit của master. À quên, vẫn có conflict và giải quyết nó như bình thường nhé :D.
Advance Rebase (super strong)
Thế giờ nếu có một nhánh khác được branch out từ nhánh issues1 mình có rebase lên master được không.
Câu trả lời là có:
Giả sử chúng ta có nhánh issues2 được branch out từ master và issues3 được branch out từ issues2.
.
git rebase --onto master issues2 issues3
Về cơ bản nó có ý nghĩa như sau:
Hãy checkout nhánh issues3, và tìm các commit từ commit chung của nhánh issues3 và issues2, sau đó apply chúng vào nhánh master.
.
Chú ý: không được rebase các commit mà bạn đã push lên repository. Nếu không ai cũng ghét và mọi người sẽ sỉ nhục và coi thường.
Cơ bản là thế này, khi rebase bạn bỏ đi các commit và chuyển chúng sang chỗ khác. Khi bạn sửa các commit bằng rebase, từ một nhánh bạn pull từ repository, và push lên. Mọi người sẽ phải apply lại commit của họ và sẽ xảy ra nhất nhiều conflict khi bạn pull change các commit đó của họ về local.
Nghe có vẻ cứt bò, tuy nhiên sự thật là việc không add một trong trống ở cuối file thực sự là một tội ác
No Newline at End of File
Bạn đã bao giờ nhìn thấy dòng chữ ở git
hay ở vim
Ờ thế tại sao git lại báo thế, nó đến từ một quyết định trong quá khứ của Unix
Một tệp nguồn không trống sẽ kết thúc bằng một ký tự dòng mới, không được đặt ngay trước ký tự dấu gạch chéo ngược (backslash).
Vì đây là mệnh đề, nên chúng ta sẽ kiểm tra việc vi phạm quy tắc
Vì vậy, nó chỉ ra rằng, theo POSIX, mọi tệp văn bản (bao gồm các tệp nguồn Ruby và JavaScript) phải kết thúc bằng một ký tự \n, hoặc mới newline (không phải là một dòng mới). Điều này đóng vai trò là eol, hoặc kết thúc của dòng nhân vật. Nó là một dòng Terminator.
Bây giờ thì gần như mọi chương trình text editor đều hỗ trợ việc tự động thêm 1 dòng trống vào cuối file, ví dụ:
vim: mặc định rồi, nên là cứ để nó mặc định thôi
VSCode: dùng setting Files: Insert Final Newline
Sublime: dùng setting ensure_newline_at_eof_on_save
Tóm lại là anh em nên thêm 1 dòng vào cuối file code nếu chưa có thì hãy sửa như sau:
Thực sự vấn đề này mình cũng không sure lắm là do đâu, và cũng không chắc vấn đề này có phải là corrupted hay không nhưng sẽ note lại một chút về case này.
Nạn nhân
Ngô Hải Đoàn, 1995, Android Dev. Anh ta đang làm việc trên PC, đột nhiên tòa nhà bị mất điện, khi bật máy lại anh ta phát hiện ra anh ta không thể thực hiện các câu lệnh git trên local-repository của mình nữa và mình là thằng cave được gọi support.
Phán đoán
Trước khi vào phần phán đoán thì mình có hỏi về source code đã commit chưa, nếu commit rồi thì khả năng recover được rất cao.
Câu hỏi thứ 2, source code đó có quan trọng không, nếu có thể re-work trong dưới 30m thì chắc sẽ không có node này.
Mình sẽ kết thúc phần phán đoán ở đây vì chả có gì để phán đoán cả
Kiểm tra một chút.
Trong khi voọc máy của Đoàn thì mình có cố re-init repository nhưng có vẻ không khả quan, kiểm tra một lúc thì có vẻ như các object và ref của git vẫn còn khá ổn. Bạn có thể tự kiểm tra thư mục .git trong local repository của mình.
Như vậy, nhìn chung khả năng cao branch/commit quan trọng vẫn ổn.
Thử recover.
Mình có sử dụng một số câu lệnh git cơ bản như git log hay git init để thử lấy lại commit-id nhưng không thành công.
Đến đây nhìn chung bạn không thể làm gì với repo này nữa, nên mình sẽ chuyển toàn bộ object của repo này sang một thư mục khác mà vừa clone về. (copy thư mục object trong .git của thư mục cũ sang chỗ tương ứng của thư mục mới)
Đến đây bạn có một repository giống với remote (chỗ mà bạn clone về) và cộng thêm các commit mà bạn đã commit hoặc đã pull về từ remote.
Tại sao lại copy objects
Nhìn chung nếu bạn đã commit thì không sợ mất content. Bởi khi commit git sẽ sinh ra các file object trong thư mục objects, công với log trong logs và một số ref hay info.
Object name sẽ dài 40 ký tự (SHA1)
Thư mục object của bạn sẽ trông như này
Tên thư mục sẽ là 2 ký tự đầu trong 40 ký tự.
Trong thư mục sẽ có các file với tên là 38 ký tự còn lại.
Objects sẽ chứa thông tin về file (changed content)
để xem được thì cần dùng câu lệnh git(do git mã hóa nên chỉ git xem đc :D): git cat-file
Đến đấy các bạn sẽ đặt câu hỏi thế git quản lý các file thành commit như thế nào
Từ các objects thì git sẽ dựng lên tree là các phiên bản của thư mục bao gồm các objects.
Cứ thế, bằng cách duyệt tree, ta sẽ có 1 tree hoàn chỉnh gồm content của cả một phiên bản (commit).
Cuối cùng commit sẽ bao gồm 1 tree
Tóm lại cứ còn objects là bạn còn tất cả, việc còn lại là đi tìm commit-id để recover.
Tìm commit
Có một vài chỗ để bạn có thể tìm GIT commit-id, thứ nhất đó là thư mục refs
Refs
Refs chỉ đơn giản là references của commit. Refs dùng để bạn nhớ lastest commit bằng cách lưu commit id vào thư mục refs.
Ở đây lastest commit của nhánh master đang là b94514784b0b86e84bdd2d66903de5bf7f9722c2.
Vậy nhớ được tên nhánh là có thể recover lại được. Lúc này bạn sẽ recover toàn bộ tree của nhánh đó và mặc nhiên các commit base trên commit này sẽ có thể trace được.
logs
Một chỗ nữa, toàn năng hơn là logs. Log lưu lại lịch sử của từng nhánh.
Bạn có thể xem mình đã tunglamgi trên từng nhánh.
Đến đây bạn có commit-id rồi việc còn lại là recover bằng câu lệnh git checkout commit-id
Btw
Nhìn chung xử lý mấy việc lặt vặt này khá hay và tạo cho mình nhiều cơ hội xử lý các thủ thuật về git.
Anw: Dù mất kì chuyện gì xảy ra đừng bao giờ xóa thư mục .git
😀
Trong một vài lần chém gió về Git, thấy mọi người có vẻ chưa chú ý nhiều đến Git và sử dụng nó một cách hiệu quả.
Nhân đây xin mạn phép chém gió sơ qua để mọi người hoàn thiện.
Git là gì
Câu hỏi cơ bản và đơn giản nhưng thay vì trích dẫn bất kì định nghĩa nào mình sẽ quay sang từ khóa và hinh ảnh
SCM – Source code management
Linus Torvarlds (Linux)
Thay thế BitKeeper trong dự án Linux Kernel
Cài đặt
Git cung cấp bộ cài cho tất cả các OS hệ POSIX: Linux, Windows, macOS trên git-scm.
Sơ lược lịch sử
.
Torvalds thổ lộ rằng: ông chưa bao giờ muốn thực hiện quản lý source code, đó là công việc nhàm chán nhất trong thế giới máy tính (trừ database ra).
Torvalds cùng các đồng đội sử dụng rất nhiều SCM khác nhau cho đến khi họ thấy phù hợp với BitKeeper(BK), thậm chí BK là nhân tố quan trong trong việc phát triển nhanh chóng của Linux Kernel. Vấn đề duy nhất của BK là một phần mềm độc quyền, tất nhiên có bản miễn phí với các hạn chế cho các open-source project. Larry McVoy(CEO BitMover công ty phát triển BK) vừa muốn hỗ trợ các dự án phần mềm tự do vừa muốn bảo vệ lợi ích của BK cũng như BitMover tất nhiên chừng nào hội phần mềm tự do chưa đe dọa đến lợi ích.
Năm 2005, Andrew "Tridge" Tridgell đã dịch ngược BK, gần như ngay lập tức BK bỏ việc hỗ trợ Linux, ngược lại Linus cũng ngừng sử dụng BK để quản lý mã nguồn, tuy nhiên Linus chỉ có 3 tháng để chuyển toàn bộ source code sang công cụ mới khi BK chính thức ngừng hoàn toàn việc hỗ trợ. Điều đó nảy sỉnh việc phát triển Git.
Quá trình phát triển bắt đầu từ ngày 3 tháng 4 nằm 2005, Torvalds công bố dự án vào ngày 6, Git có thể cài đặt trên PC vào ngày 7 và tính năng merge branch được release vào ngày 18. Đến ngày 16/6 Git thực hiện quản lý việc release Linux Kernel v2.6.12
Có lẽ việc ôn lại lịch sử sẽ kết thúc ở đây, phần tiếp theo sẽ quay lại thực tế sử dụng Git
Một số khái niệm trong Git
Repository
Repository là khái niệm cơ bản của Git tương đương với project trong GitLab, Gerrit
Git có thể tạo bằng câu lệnh git init, thư mục trở thành local repository trong khi đó các repository online như trên GitLab được gọi là remote repository.
Lịch sử làm việc đều được lưu trong thư mục khởi tạo git
Remote
Repository hosted in server. Remote repository chính thường được gọi là Origin
Các local clone đều liên kết đến remote.
1 local clone can have 0, 1 or many remote.
Branch – nhánh
Branch cũng là một khái niệm mới trong Git mà cần phải làm quen.
Branch là các version song song với nhau của Repo.
Người dùng làm việc trên các branch mà không làm ảnh hưởng đến live version.
Một vài branch đặc biệt
master – branch mặc định
HEAD – branch hiện tại mà người dùng đang làm việc
origin/master, origin/xxx, origin2/xxx là các nhánh ở remote repository
Checkout
Checkout là cách người ta chuyển nhánh đề làm việc
Ví dụ: Bạn lấy project trên GitLab về và cần chuyển qua nhánh master 2 để làm việc bạn cần checkout sang nhánh master 2: git checkout master2,
hoặc cũng có thể bạn cần tạo ra một nhánh mới hãy thêm flag -b trong câu lệnh: git checkout -b dev_another_function.
Commit
Nếu như Branch là nhánh cây, cành cây thì có thể coi commit chính là các nút (node) hoặc cũng có thể là một điểm nào đó trên nhánh.
Người ta lưu lịch sử làm việc trong các commit, nói cách khác nếu bạn commit dữ liệu của bạn không thể mất trừ khi bạn xóa hoàn toàn project git.
Cách tạo commit cũng đơn giản: git commit -m "Commit Message" hoặc cũng có thể đơn giản là git commit sau đó làm theo hướng dẫn với giao diện text editor(thường là vi, vim, nano, etc, ..)
Mỗi commit đều có message và một ID đặc biệt (SHA –hash), từ đó có thể trace được commit
Nói đến đây mình đột nhiên nhiên phát hiện ra trình bay theo hướng này sẽ khiến mọi người khó hiểu một chút, vì vậy ngay sau đây mình sẽ trình bày về commit một cách rõ hơn, theo một cách nhin khác
Git Concept
2 hình trên đây đã trình bày cách sử dụng git ở local một cách cơ bản
Nhìn chung một file sẽ có 4 trạng thái như hình thứ nhất
Untracked: Trong working directory nhưng git không quản lý, đó là nhưng file tạo mới, được copy vào hay đơn giản là bạn vừa chạy git rm fileName.
Unmodified: Rất dễ hiểu, đó là file git quản lý, git ghi nhận rằng file đó chưa có sự thay đổi so vớicommit hiện tại (HEAD). Có 2 cách để chuyển file vào trạng thái này đó là thêm các file untracked git add hay git commit tức là lưu sự thay đổi đó lại (file sẽ được ghi nhận là ko thay đổi so với commit mới :D, đôi chút confuse).
Modified: Là file từ trạng thái unmodified và đã bị edit, tất nhiên khi Crtl + Z toàn bộ step, file sẽ trở lại unmodified.
Staged: là trạng thái chuẩn bị được commit, thông thường người dùng không để ý đến trạng thái này, trạng thái này được trình bày khá rõ ràng ở hình thứ 2.
Một trạng thái kiểu như git đã ghi nhận sự thay đổi nhưng chưa tạo commit. Để chuyển sang trạng thái staged sử dụng câu lệnh git stage.
Một tips nhỏ với stage, khi bạn cần commit lên nhánh A mà lại làm việc trên nhánh B thì bạn có thể stage toàn bộ file cần thay đổi và checkout sang nhánh A và commit. Một cách khác đa số mọi người sẽ áp dụng đó là commit trên nhánh B, cherry-pick(sẽ nói ở dưới đây) commit sang A, ngoài ra cũng có cách chuyên nghiệp hơn đó là tạo pack diff.
Merge và Rebase
Từ thực tế làm việc trong nhóm sẽ nảy sinh các trường hợp phải kết hợp code của nhiều developer để tạo nên một bản hoàn chỉnh thay vì tiếp tục phát triển song song mãi điều đó nảy sinh ra 2 khái niệm trong git là merge và rebase.