Biên Soạn: Trần Thiên Trọng – tranthientrong.it@gmail.com BASIC Version Control System A version control system ( VCS ), theo dõi lịch sử thay đổi của file, khi bạn modify file, VCS sẽ ghi lại và lưu từng thay đổi. Cho phép bạn khôi phục phiên bản trước đó của bất kỳ lúc nào. In short, a Version Control System like Git makes it easy to: • Keep track of code history • Collaborate on code as a team • See who made which changes • Deploy code to staging or production VCS có rất nhiều loại khác nhau, nhưng đều có đặc điểm chung là track history change of file/directory và loại được sử dụng nhiều nhất là Git Distributed Version Control System Git: Distributed Version Control System (DVCS). Không giống như các hệ thống VCS sử dụng 1 server trung tâm, Git là sử dụng Distributed Architect, nghĩa là những người contributing 1 repository có thể có toàn bộ copy của repository đó trên máy local của họ. Git không kết nối liên tục với central repository, Dev có thể làm việc ở bất kỳ đâu và collab một cách không đồng bộ Tuy nhiên khi ta merge lại vào master branch ta cần phải update tất cả các snapshot mới nhất của nó, Git đảm bảo rằng chúng ta luôn ở version cuối cùng trước khi cho người khác xem sự thay đổi của 1 repository. Git thường được sử dụng để phát triển phần mềm mã nguồn mở và thương mại. Repository repository: Kho lưu trữ Repository (repo) là một thư mục nằm ở trung tâm để lưu trữ tất cả file của bạn. Khi bạn tạo repository với các tệp và thư mục của mình, bạn có thể bắt đầu tracking changes và versions. Repository lưu giữ tất cả các commits, a snapshot of all your files at a point in time that have been made. Snapshot Trong computing: • Snapshot có thể được định nghĩa là State của hệ thống tại một thời điểm cụ thể trong hệ thống máy tính. • Nếu chúng ta thường xuyên chụp snapshot, mỗi thư mục hoặc tệp của chúng ta có thể được đưa trở lại trạng thái đó, nếu chúng ta bị nhiễm bất kỳ vi-rút nào, chỉ cần Restore toàn bộ ổ đĩa, thư mục hoặc tệp về State trước khi bị nhiễm vi-rút. Backup khác Versoning: • Backup là toàn bộ bản copy của Data. Nếu một folder có 10GB data, nghĩa là file backup sẽ chiếm 10GB, và nếu cứ mỗi tiếng chúng ta backup 1 lần thì sau 10 tiếng chúng ta có 10 file backup 10GB. • Snapshot chiếm một lượng nhỏ dung lượng đủ để ta khôi phục lại các thay đổi ở State này và trở lại State trước Đặc điểm: • Mỗi lần ta commit, 1 snapshot được tạo ra ghi lại tất cả state của project tại thời điểm đó. • Tất cả các Snapshot là thứ tạo nên hisory của 1 project • Snapshot sau đó sẽ được chứa trong Git Directory HEAD Khi làm việc với Git, chỉ một nhánh có thể được check tại một thời điểm - và đây được gọi là nhánh "HEAD". HEAD trỏ đến current checkout snapshot. HEAD còn được coi là "current branch". Khi bạn chuyển đổi các branch với git checkout, HEAD sẽ thay đổi để trỏ đến đỉnh của branch mới. Có thể coi HEAD là một bookmark để Git biết bạn đang ở đâu, cho dù bạn có nhiều repo, nhưng bạn vẫn có thể quay lại nơi bạn bookmarked Commit Message Commit Message giống như Clean Code, viết ngu sẽ không ai hiểu. Một cấu trúc đơn giản nhất là: Summarize changes in around 50 characters or less Short Description ( Max 50 ) More detailed explanatory text, if necessary. Wrap it to about 72 characters or so. Explain the problem that this commit is solving. Focus on why you are making this change as opposed to how (the code explains that). Are there side effects or other unintuitive consequences of this change. Detail Description ( Max 72 each line ) Further paragraphs come after blank lines. - Bullet points are okay, too - Typically a hyphen or asterisk is used for the bullet, preceded by a single space, with blank lines in between, but conventions vary here If you use an issue tracker, put references to them at the bottom, like this: Resolves: #123 See also: #456, #789 Để xem được thông tin của commit Commit ID Commit ID: - 40 characters được gọi là Hash - Hash được tạo ra bởi thuật toán encrypt gọi là SHA1 Tất cả thông tin trong commit: - Author - Date - Message - Snapshot SHA1 Algorithm string 40 characters Tỷ lệ để xảy ra Collision là cực kỳ nhỏ và gần như không thể xảy ra Branch Về cơ chế: Git Branch là một pointer đến snapshot, nó trỏ đến snapshot cuối cùng trong history. Chúng ta có thể nói rằng các branch tạo ra một line of development tách biệt trong project. Bạn có thể tận dụng lợi thế của việc phân nhánh khi làm việc trên các tính năng mới hoặc sửa lỗi vì nó tách biệt công việc của bạn với công việc của các thành viên khác trong nhóm. Các nhánh khác nhau có thể được hợp nhất thành bất kỳ một nhánh nào miễn là chúng thuộc cùng một repo. • Default Branch mà Git tạo ra cho bạn là Master Branch, master branch không khác gì branch bình thường, tuy không bắt buộc nhưng thông thường Master Branch là branch tốt nhất về trạng thái của dự án. • Trong thực tế, ta thường tạo một nhánh mới cho mỗi tác vụ (tức là một nhánh để sửa lỗi, một nhánh cho các tính năng mới, v.v.). Đến đây, ta tự hỏi tại sao pointer lại gọi là branch ? Nếu bạn thực hiện một số thay đổi và commit, nó sẽ lưu trữ một con trỏ đến commit ngay trước nó. Và vì thế series các snapshot trỏ vào nhau được gọi là branch ( vì nó nhìn giống nhánh cây ) GIT LOCAL Install GIT On Local PC Step 1: Install HomeBrew $ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" $ brew install git Step 2: Install git Config Git Ta biết VSC track ai là người modify file, để làm được thì trước hết ta phải nói cho Git biết ta là ai. $ git config --global user.email "tranthientrong.it@gmail.com" Config Username & Email $ git config --global user.name "trongkami" --global để set cho tất cả repo Xem Config $ git config -l Create Local Repository Có 2 cách tạo repo: 1. Tạo repo riêng trên local 2. Hoặc clone để tạo copy của remote repo $ git init Step 1: Vào directory mình muốn $ cd Step 2: Biến directory đó thành reposity $ git init Bạn sẽ thấy một directory .git, đây được gọi là Git Directory: • Khi chúng ta clone 1 repo, Git Directory cũng được copy vào local PC. • Khu vực bên ngoài Git Directory được gọi là Working Tree, đây là version hiện tại của project của bạn $ git add Các file được đặt trong Working Tree không tự động được track bởi Git mà ta phải ra lệnh cho chúng Yêu cầu Git track file * file này chỉ trong working tree Thêm tất cả file trong working tree vào Stage $ git add <file name> $ git add -A Lúc này File được tracked sẽ vào Staging Area $ git commit Để commit tất cả change trong stage $ git commit Một màn hình hiện lên để ta nhập comment về commit này. THÀNH CÔNG!! Ta có thể dùng lệnh khác để add luôn comment Để commit + comment $ git commit -m "comment" Git Workflow Có ba thành phần chính của một dự án Git: Repository Repo là “vùng chứa” tất cả file trong dự án của bạn. Working Tree Working Tree bao gồm các tệp mà bạn hiện đang làm việc. Staging - index Đây là file ghi các thông tin về change, về file mới bị được commit. Index so sánh các tệp trong Working Tree với các tệp trong repo. Khi bạn thực hiện một thay đổi trong Working Tree, index đánh dấu tệp là đã sửa đổi trước khi nó được commit. $ git status $ git status Để biết thông tin về working tree và những change trong stage đây là thông tin trong stage area $ git log --graph Git có hổ trợ tính năng quá bá đó là illustrate hình ảnh GIT branch Để Illustrate GIT branch Hoặc khi bạn chỉ muốn nhìn thấy 1 dòng commit: Tracking File Tracked File là File đã có trong Snapshot Sau khi File được tracked, nó có 3 states: MODIFED ----> STAGED ----> COMMITED $ git --graph MODIFIED STAGED How To Track File Yêu cầu Git track file *chỉ trong working tree $ git add <file name> Chúng ta có thể xem file đã được track hay chưa? Nếu track rồi thì đang ở đâu? $ git status Demo: Ta thêm một file abc.txt vào Repo, lúc này file này chưa được track 1. MODIFED State • Nếu file đã được track, mọi thay đổi trong file sẽ cho nó vào MODIFED COMMITED $ git status cho ta 3 thông tin: - Ta đang trong Master brach - file newFile đang trong MODIFIED - file này đang không trong STAGED • 2. STAGED Stage $ git add để file vào STAGED $ git status cho ta 3 thông tin: - Ta đang trong Master brach - file newFile đang trong MODIFIED - file này trong STAGED • 3. COMMITED STATE $ git commit để vào COMMITED Lưu ý: Nó sẽ được snapshot trước khi vào Git Directory Skipping Stage + Commit Cho file vào Stage + Commit tất cả File đã được tracked $ git commit -a -a tương ứng với all Sẽ dùng lệnh này thường xuyên vì không phải lúc nào cũng có thể stage, commit từng file Demo: Thay đổi 2 file trong repo Vì 2 file này đã được Track, ta có thể tiết kiệm thời gian vừa Stage + Commit cả 2 file cùng lúc Get Information Of Change $ git log Để xem được những commit ta đã thực hiện $ git log $ git log -p Để xem được thông tin chi tiết từng thay đổi trong mỗi commit $ git log -p comment của commit file abc.txt + thông tin mới được thêm - thông tin bị bỏ $ git show Chúng ta cũng có thể xem chi tiết của riêng 1 commit Để xem được thông tin chi tiết từng của 1 commit $ git show < commit id > $ git add -p $ git add - p Xem được những thay đổi trước khi vào STAGE Nó sẽ hiện lên 1 dòng để ta chọn có STAGED những thay đổi này hay không Stage this hunk ( y/n ) ? Lệnh này cho phép chúng ta kiểm tra những thứ được thay đổi trước khi STAGED Delete & Rename File $ git rm Remove file nghĩa là remove khỏi Git Directory + Working Tree luôn ( Untrack ) Về vật lý thì nó vẫn nằm trong directory đó chứ không vào trash. Nhưng nó sẽ không được nhận diện trong Git Remove file khỏi Git Reposity $ git rm < file name > Lúc này file cũng vào STAGED, chúng ta phải commit để hoàn toàn remove file khỏi Git Lúc này ta nhận ra 1 điều: - add nghĩa là add vào Git Repo ( dù file đã trong Working Tree ) - rm nghĩa là remove khỏi Git Repo + Working Tree $ git mv Rename file $ git mv [tên cũ] [tên mới] Để ý sẽ lấy lệnh này giống với lệnh move, đúng vậy, bạn có thể dùng lệnh này để di chuyển file vào các sub-directory trong working tree Ignore .git file Đối với những file được script tạo ra sẵn, rất khó chịu vì những file này không liên quan đến project, ví dụ như ở OSX luôn xuất hiện file.DS_Store Ta không để xoá file đó đi ( vì nó sẽ được tạo lai ) mà phải Track 1 file ĐỐI NGHỊCH với file đó với đuôi là [tên file đó].gitignore Step 1: Tạo file đối nghịch .gitignore $ echo [tên file] > .gitignore Step 2: Track File $ git add .gitignore Step 3: Commit File $ git commit -m Chưa ignore Sau khi track file ignore Undo Reverse lại quá khứ là tính năng mạnh mẽ nhất của VSC. UN-MODIFED git checkout Từ checkout trong TA nghĩa là "Kiểm tra lại trước khi rời" Chúng ta có thể hiểu là nó sẽ rời khỏi MODIFED. Để ta toàn quyền chỉnh sửa lại trước khi vào MODIFED lần nữa Để rời khỏi MODIFED $ git checkout [tên file] Demo: Ta lỡ thêm những thứ ta không muốn, tuy nhiên ta đã ở trong MODIFED rồi, lúc này ta vẫn có thể ra UN-STAGED git reset Rời khỏi STAGE $ git reset [tên file] Như ta đã biết thì trong STAGE không chỉ có MODIFED mà còn có: - new file ( track ) - renamed - deleted reset sẽ biến tất cả những thứ đó thành màu đỏ ( nghĩa là chưa được vô STAGE ) modified: Ta lỡ cho file.txt vào STAGED Ta cho file.txt ra khỏi STAGED, trở về trạng thái lúc trước là MODIFED new file: Ta lỡ Track file new.txt Ta cho new.txt ra khỏi STAGE, trở về trạng thái trước đó là Un-Track renamed: Ta lỡ đổi tên file.txt -> 123.txt Ta cho 123.txt ra khỏi STAGE, trở về trạng thái trước đó là file.txt UN-COMMITED git commit --amend Lệnh này để Override Commit message Để override commit message gần nhất $ git commit --amend git revert Chúng ta có thể revert commit gần nhất sử dụng HEAD hoặc revert commit trong quá khứ sử dụng Commit ID Để tạo commit mới revert lại commit gần nhất $ git revert HEAD Để bỏ lệnh revert $ git revert --abort HEAD Chúng ta dùng HEAD để xác định snapshot gần nhất ( aka commit ) Git reverse không phải là Ctrl + Z, mà là nó tạo ra một commit mới chứa lệnh trái ngược với commit trước đó. Ví dụ: Nếu commit gần nhất có stage là add Thì trong reverse commit, nó sẽ có change là revert "add" -> Việc này giúp history của project consistent, ghi chép đúng những gì xảy ra. Demo: Trong file của chúng ta có thứ phá hỏng mọi thứ và ta đã lỡ commit Gõ $ git revert HEAD, editor hiện ra. Chúng ta nên thêm Message vào để giải thích tại sao phải revert Để ý bạn sẽ thấy cú pháp của câu lệnh commit và revert rất giống nhau, đương nhiên vì revert cũng chính là commit mà Khi $ git log -p ra, ta sẽ thấy commit này Để tạo commit mới revert lại commit trong quá $ git revert [commit ID] khứ important: Hiện tại chỉ có thể reverst commit HEAD thôi, nếu muốn commit sâu hơn thì phải Merge hay gì đó, sẽ học sau BRANCHING & MERGING Xem Branch Để xem có bao nhiêu branch $ git branch Lệnh git branch có thể create / delete / modify branch chứ không chỉ dừng lại ở đây. Create Branch Để tạo branch mới $ git branch [tên branch mới] Switch Branch Để switch branch khác $ git checkout [tên branch] HEAD lúc này cũng nhảy sang branch mới Thanks to ZSH mới biết gõ đang ở branch nào $ git checkout -b Tạo Branch mới + switch sang Branch đó $ git checkout -b <branch name> Giờ hãy thử tạo một commit trên branch này Lúc này khi gọi $ git log -p ta thấy là HEAD đã chuyển đến current snapshot Nhìn vào hình này, ta có thể illustrate GIT như sau: Tất cả các commit sau đều tham chiếu đến commit trước, tuy nhiên chỉ cần 2 pointer là master và new_feature thì ta đã có thể hình dung ra được 2 branch mới ( đây trả lời cho câu hỏi tại sau pointer lại construct được thành branch ) commit 987a chính là nơi rẻ 2 nhánh. Delete Branch Delete Branch *Không thể delete branch hiện tại đang ở $ git branch -d <branch name> important: Chúng ta có thể dễ dàng delete 1 branch chưa có sự thay đổi nào, tuy nhiên nếu có sự thay đổi và chưa được merge vào master branch, git sẽ không delete liền Merge Branchs Để Merge Branch nào đó vào Branch hiện tại $ git merge <branch name> Un-Merge $ git merge --abort Sau khi merge, $ log -p bạn sẽ thấy: ( HEAD -> master, new_feature ) Như bạn đã biết, Branch thật chất là một references đến commit cuối. Vì thế khi merge 2 branch lại với nhau, 2 có nghĩa là gì. pointer sẽ tham chiếu vào 1 commit, và HEAD đánh dấu bạn đang làm việc trên branch nào Conflict What cause Conflict Git giúp việc hợp nhất trở nên cực kỳ dễ dàng. Hầu hết, Git sẽ tìm ra cách tự động tích hợp các thay đổi mới. Tuy nhiên, xung đột thường nảy sinh khi hai người thay đổi các dòng giống nhau trong một file hoặc nếu một branch đã xóa file trong khi một branch khác đang modify. Trong rường hợp này, Git không thể tự động xác định branch nào mới là chính xác ( không có prority giữa các branch kể cả master, cũng không có synchronization giữa các branch ) Demo: Ta sẽ sử đổi dòng "con meo" tại 2 branch: • master • new_feature Switch to new_feature branch Bây giờ 2 branch có commit bị conflict nhau ( cùng thay đổi line of code giống nhau ) Giờ ta merge lại với nhau: Booommmm ! CONFLICT Có thể dùng lệnh $ git status để kiểm tra tình trạng CONFLICT Solve Conflict Git đã add những comment trong file feature.txt, tuy nhiên chúng ta phải dùng editor mở file đó len Mở File sử dụng VSCode $ code [tên file] Nếu muốn sử dụng cả 2, ta chỉ việc: • bỏ <<<<<<< HEAD • bỏ >>>>>>> new_feature Giờ thì kiểm tra lại: Git còn cho ta biết conflict fixed Khi commit, trong commit message được GIT viết sẵn dòng "Merge branch" GIT REMOTE What is Git Hub: Như ta đã biết Git là Distributed VCS, nghĩa là mỗi người có full copy của repo trên máy tính của họ. Chúng ta có thể biến PC của mình thành server để host repo của mình và để người khác clone repo, lúc này repo của chúng ta là gọi là remote repo. Tuy nhiên để thuận tiện, người ta thường đặt repo của mình lên một server chung và để người khác clone về. Một trong những Server chung đó là GitHub, GitHub là Web-based remote repository hosting service for Git Ngoài các tính năng chính của Git, GitHub cung cấp nhiều service khác. Đầu tiên chúng ta cần hiểu là không có cách nào để link đồng bộ 2 Repository lại với nhau. Mà phải hiểu là: - Ta push LOCAL REMOTE - Hoặc pull REMOTE LOCAL Create Remote Repo Chỉ việc click new Local to Remote $ git remote add origin Lệnh này tạo một remote mới được gọi là origin tại. Khi bạn làm điều này, trong các lệnh push của mình, bạn có thể đẩy đến nguồn gốc $ git remote add origin < git http > ( orifin ) thay vì phải nhập toàn bộ URL. $ git push -u origin master Lệnh push các commit trong nhánh LOCAL có tên là master đến REMOTE có tên là origin $ git push -u origin master -u Để tạo copy branch từ local lên remote ( hay từ hoa mỹ gọi là upstream ) Lưu ý khi push lên bạn sẽ bị yêu cầu nhập Username và Password. Password này không phải password tài khoản mà là Access Token lấy trong Github $ git push Bạn có thể dùng lệnh này khi đã upstream. ( xem cách upstream ) $ git push không phải là update cả local repo của ta lên remote repo mà nó sẽ gửi những commit ta thực hiện tại local trên remote repo, tất nhiên nếu trong commit có add thêm file thì nó cũng update file lên luôn Giờ ta muốn tất cả những thay đổi đó lên remote repo. Update remote branch with local commit $ git push Demo: Ta tạo file.txt và push lên remote Lưu ý: Ở LOCAL nếu chúng ta chưa update nội dung mới nhất ở REMOTE, thì push sẽ bị refected Xem REMOTE $ git remote -v Ta có thể xem trạng thái của REMOTE $ git remote -v $ git remote show origin Xem thông tin remote nhánh origin $ git remote show origin Demo: Tạo 1 file new trên remote Tiếp theo gọi remote show origin Dấu hiệu nhận biết có thông tin mới Remote to Local $ git clone Ta có thể copy REMOTE về LOCAL, biến chúng thành local repo. ( lưu ý việc này không tạo nên mới liên hệ nào giữa LOCAL – REMOTE ) Để clone git từ GitHub về máy $ git clone [URL] Demo: Ta sẽ Clone JavaMultiThreading về Local https://github.com/TranThienTrong/JavaMultithreading Các bước để clone: Step 1: Copy URL Step 2: Clone về máy mình RESULT: Bây giờ chúng ta đã có local repo y chang remote repo $ git pull Khác vơi clone, pull dùng để chọn tuỳ branch mà bạn muốn merge vào Để kéo nội dung từ branch bắt kỳ của REMOTE và merge vào branch hiện tại $ git pull <URL> <branch> URL đây là URL mà local repo clone về, thông thường người ta dùng origin, git sẽ tự hiểu branch branch mà bạn muốn pull về $ git pull thực hiện hai điều: 1. $ git fetch để tải content từ remote repo 2. $ git merge để merge remote content refs and heads into a new local merge commit. Ví dụ, ta có 2 branch, local và remote git pull sẽ tải xuống tất cả các thay đổi từ điểm mà local và remote bị chia ra. git pull sẽ fetch-tìm nạp các commit từ remote bị chia ra là A-B-C. Sau đó nó sẽ gọi Merge với local brach. Demo: Ta tạo remote_file.txt trong GitHub Một 1 branch mới ở local repo, sau đó pull về Update Remote Change Như đã biết, GitHub cho chúng ta tính Synchronization, khi remote repo ta clone về bị thay đổi, thay vì lên web và xem lại ta có thể dùng. $ git fetch Fetch = tạo một Branch mới từ Remote Tạo thêm branch từ REMOTE Branch mới tên là origin/master $ git fetch $ git fetch sẽ downloads commits, files, và tất cả mọi thứ từ REMOTE -> LOCAL. Nó tạo 1 branch mới tên là origin/master để chứa các commit đó. important: git fetch không merge vào local branch của bạn. Nó tạo ra các branch hiện có trong remote Dùng $ git status sẽ cho ta biết ta đã OUTDATED from origin/master bao nhiêu commit $ git merge origin Merge origin/master branch $ git merge origin Lúc này local branh có thêm commit ( chưa cập nhật ) từ origin/master branch GREAT!! REMOTE BRANCHING Remote Branch Downstream Khi sử dụng GIT, bạn đang " downstream hạ nguồn" khi bạn sao chép (clone, checkout, v.v.) từ một remote repo. Thông tin chảy "xuống hạ ngườn " đến bạn. Khi bạn thực hiện các thay đổi, bạn thường muốn gửi chúng trở lại "thượng nguồn" để chúng đưa nó vào kho lưu trữ đó để mọi người lấy từ cùng một Upstream nguồn đang làm việc với tất cả các thay đổi giống nhau. Tại sao bạn nên thiết lập Upstream Branch cho Local Branch? Giả sử current local HEAD branch hiện tại của bạn có tên là "feature". Khi bạn set "origin / feature" sẽ tạo ra mỗi quan hệ gọi là tracking. Mối quan hệ này rất hữu ích vì hai lý do: • push và pull trở nên dễ dàng hơn rất nhiều. Với local branch, bạn có thể chỉ cần sử dụng các lệnh viết tắt "git pull" và "git push" - thay vì phải suy nghĩ về các tham số chính xác như trong "git push origin feature" ( bạn phải checkout vào feature mới có thể gọi tắt ) • Git hiện cũng có thể cho bạn biết về các commit chưa được đồng bộ hóa mà bạn chưa pull hoặc fetch về. $ git push -u origin <branch> Trước giờ ta chỉ học là push lên master (merge các local commit vào remote master ) nhưng trong thực tế ta chỉ nên push local branch của mình lên và đợi người khác quyết định có merge hay không. Để tạo copy branch từ local lên remote $ git push -u orrigin <branch name> -u Để tạo copy branch từ local lên remote ( hay từ hoa mỹ gọi là upstream ) [branch name] Bắt buộc phải cùng tên với branch local Demo: Ta đang làm việc với local branch tên là feature Step 1: Ta tạo feature.txt và commit như bình thường Step 2: Ta tạo branch feature lên remote repo luôn Giờ thì trong Remote Repo xuất hiện luôn Branch Feature với file feature.txt Còn branch Master vẫn bình thường Lúc này feature branch tracking origin / feature branch Khi gọi push hoặc pull ở master nó không phải ứng vì nó chỉ tracking origin / master Khi gọi push hoặc pull ở feature vì đã tracking rồi nên nó tự động lên origin / feature $ git push –delete origin !important Lệnh này không delete local branch của bạn Để delete remote branche $ git push --delete origin [remote branch name] Tracking / Copy Branch $ git fetch Fetching is what you do when you want to see what everybody else has been working on. Tạo thêm branch từ remote repo $ git fetch $ git checkout origin/ Để copy 1 local branch và track remote branch * Chúng sẽ có cùng tên, tuy nhiên remote branch sẽ có prefix là origon/ $ git fetch $ git checkout origin/[tên remote branch] A 'tracking branch' in Git is a local branch that is connected to a remote branch. When you push and pull on that branch, it automatically pushes and pulls to the remote branch that it is connected with. Khi bạn clone một remote repo, nó thường tự động cho master branch để track origin/master. Đó là lý do tại sao git push và git pull hoạt động hiệu quả mà không có arguments nào khác. Tuy nhiên, bạn có thể thiết lập các tracking branches khác nếu muốn ĐẶC BIỆT: Khi bạn track branch cũng có nghĩa là bạn tạo 1 local branch có TẤT CẢ NỘI DUNG của remote branch này Demo: Đầu tiên ta tạo branch hello tạo remote $ git remote show origin cũng cho ta biết hello branch là new và chưa được track Step 1 Bắt buộc phải gọi $ git fetch, lúc đó mới track được Step 2 $ git checkout branch đó Giờ thì origon/hello đã được tracked Ta có thể thấy branch hello trong local $ git remote update Để fetch tất cả các branch trong remote repo, ta có thể gọi lệnh này, còn việc có merge nó hay không tuỳ thuộc vào bạn Demo: Tạo 3 branches mới trên GitHub Fetch cả 3. $ git rebase !important lệnh này không thay thế merge mà nó làm cách merge dễ nhìn và debug hơn $ checkout [current branch] Để thay đổi base của [current branch] Git merge adds a new commit, preserving the history. $ git rebase [base branch] Git rebase moves a feature branch into a master Để đơn giản nhất thì bạn --graph ra Như bạn thấy feature branch và master branch rất lộn xộn, nếu merge vào rất khó nhìn. trong khi feature chỉ có 3 commits là khác master WALLA Lưu ý, có thấy vẫn có master và feature không, vì chúng vẫn đang là 2 branch riêng lẻ, chưa merge với nhau. Chúng ta dùng $ git rebase để biến 3-ways thành fast-forward merge !important Git Rebase sẽ báo Conflict dù ta chưa Merge. Cách xử lý thì vẫn là mở Editor thôi SOLVING CONFLICT Conflict with remote commit • Thông thường CONFLICT chỉ xảy ra ở Git Pull. • Không phải vì Git Push không xảy ra CONFLICT, mà vì GIT yêu cầu ta luôn có phiên bản mới nhất của remote repo trước khi push, nên commit ta push lên luôn là cuối cùng và dễ dàng fast-forward merge. Tất nhiên thì nếu ta push lên 1 branch A, người khác ở 1 branch B, khi merge lại vẫn có thể CONFLICT như thường Ở remote nếu chúng ta chưa update nội dung mới ở remote, thì push sẽ bị refected Để có thể update, ta phải dùng $ git pull Tuy nhiên nếu ta pull xuống thì commit bị CONFLICT với commit gần nhất của ta thì sau ( điều kiện để CONFLICT vẫn như local ) git graph ra thì ta sẽ thấy branch hiện tại ( local master ) không thể merge với origin/master Nhìn vào hình này, ta có được 2 thông tin 1. local branch chưa update và push không có hiệu lực 2. commit cuối của local branch và commit cuối của origin/master CONFLICT với nhau Resolve Conflict Giống như ở Local Branch, ta giải quyết conflict bằng việc mở editor Mở File sử dụng VSCode $ code [tên file] Nếu muốn sử dụng cả 2, ta chỉ việc: • bỏ <<<<<<< HEAD • bỏ >>>>>>> Giờ chúng ta cứ commit như bình thường là merge thành công từ pull, đồng thời pull sẽ update branch của ta nên ta có thể push luôn THÀNH CÔNG, THÀNH CÔNG, ĐẠI THÀNH CÔNG COLLABORATION Best Practice 1 Luôn synchronize local branch trước khi bắt đầu việc gì đó 2 Good commit message very important Tránh 1 commit thay đổi nhiều thứ 1 commit chỉ nên làm 1 việc: 3 4 • rename • fix • add feature • delete • Nên có một branch phụ về các feature • Và nhớ thường xuyên merge từ master về feature, để tránh nhiều sự CONFLICT khi merge lại trong master Master là branch chứa phiên bản mới nhất 5 6 Nhưng phiên bản stable nhất nên để 1 branch riêng Rebase sẽ làm thay đổi commit ID, vì thế không nên rebase change sẽ push lên remote repo Pull Request Fork & Pull Request GitHub fork là một bản copy của repo nằm trong account của bạn chứ không phải account mà bạn đã fork-tách dữ liệu từ đó. Sau khi fork một repo, bạn sở hữu forked copy. Nghĩa là bạn có thể chỉnh sửa nội dung của fork repo của mình mà không ảnh hưởng đến repo chính. Vậy sự khác nhau giữ fork và clone là gì? Khi bạn nói rằng bạn đang Forking repo, về cơ bản, bạn đang tạo một bản cpoy của origin repo sử dụng ID GitHub của mình. Điểm chính cần lưu ý ở đây là bất kỳ thay đổi nào được thực hiện đối với origin repo sẽ được phản ánh trở lại Fork Repo của bạn (bạn cần fetch và merge). Tuy nhiên, nếu bạn thực hiện bất kỳ thay đổi nào đối với Fork Repo của mình, bạn sẽ phải tạo một Pull Request đến origin repo. Nếu origin repo của bạn được admin của origin repo chấp thuận, thì các thay đổi của bạn sẽ được commit / merge với origin repo. Mặt khác, các thay đổi được thực hiện trên cloned repository có thể được đẩy trực tiếp upstream repository sử dụng push mà không cần hỏi người sở hữu repo đó. Đối với điều này, người dùng phải có quyền WRITE. Nếu người dùng không có quyền WRITE, cách duy nhất để thực hiện là thông qua fork request. Trong trường hợp đó, các thay đổi được thực hiện trong cloned repo trước tiên được push đến forked repository và sau đó một pull request được tạo. It is a better option to fork before clone if the user is not declared as a contributor and it is a third party repository (not of the organization). Demo: Step 1: Tìm 1 repo không phải của mình Click Fork Step 2: Đây là giao diện của Fork Repo của mình. Có chữ forked from origin repo Step 3: Thay đổi file trong fork repo Step 4: Có 2 lựa chọn: 1. commit vào master branch của FORK repo 2. Tạo branch với tên TranThienTrong-patch-1 và Pull Request Step 5: • Giao diện commit trong TranThienTrong-patch-1 branch. • Click Create Pull Request Clone The Fork Repo Ở ví dụ trước, ta đã Fork Repo về, giờ ta sẽ clone nó Demo: Ta sẽ tạo 1 file và push lên fork remote, với branch là origin/trong Đây là giao diện của GitHub khi tạo Pull Request. Lưu ý là Fork Repo và Pull Request không phải chức năng gốc của Git, vì thế ta không làm trên Terminal được, nhưng hầu hết GitLab, GitBucket... đều có chức năng này. Sau khi Create Pull Request, ta tạo ra 1 pull request Edit Pull Request Một project có tab riêng để chứa các Pull Request, và đồng nghiệp hoặc chủ origon repo có thể vào comment để sửa, đó là lúc ta cần chức năng update thay vì lại tạo mới Ta sẽ push commit mới lên fork repo của chúng ta Lúc này ở tab tự động xuất hiện commit này, chứng tỏ là Git tự nhận diện trong branch là banch mà ta muốn merge với origin. Và thật ra, nếu ta muốn tạo 1 pull request riêng cho commit này, ta phải tạo 1 branch mới, chứng tỏ Git đã bắt buộc Branch này phải là Branch trong Pull Request -> Pull Request là một yêu cầu Merge Branch hơn là yêu cầu về commit Squashing Commit git base -i [branch] Nếu chúng ta có quá nhiều thay đổi, 5-6 commit nhưng giờ chúng ta chỉ giữ lại 1 commit thì sao Lúc này chúng ta sử dụng Rebase, tuy nhiên Rebase có rất nhiều loại, để biết sử dụng loại nào, ta thêm -i $ checkout [current branch] Để thay đổi base của [current branch] -i để mở ra interactive rebase $ git rebase -i [base branch] step 1: gõ lệnh step 2: Mặc định khi rebase chúng ta dùng 'pick' nghĩa là sử dụng cả 2 commit step 3: Sử dụng squash cho commit thứ 2 Squash giúp chúng ta sử dụng chức năng của commit này nhưng hợp nó lại với commit trước đó. step 4 Cửa sổ hiện lên để ta gõ commit message THÀNH CÔNG: Giờ thì trong lịch sử chỉ còn 1 commit git push -f Để ép việc push lên branch của pull request $ git push -f Vì branch sử dụng để pull request là của mình, vì thế cứ việc force push thoải mái, nhưng ko được dùng với public branch Nhưng, tại sao chuyện này lại xảy ra được, trong thực tế không thể nào thay đổi commit. Đây là thực tế, 2 lệnh commit kia vẫn còn đó, tuy nhiên ta chỉ tạo ra một commit mới có chức năng ( pick commit 1, squash commit 2) mà thôi Ta không thể push bình thường mà phải force forward sử dụng git push -f Giờ thì xem, không còn phân nhánh nữa, tất cả chỉ có 1 commit mà thôi Code Review Trong tab File changed, bạn có thể thêm comment vào Review tổng thể chỉ dành cho chủ origin Review code: Sau khi thêm review: PRACTICE Upload whole local repo to remote repo Create Local Repo 1. Initialize the local directory as a Git repository. $ git init 2. Stages add file $git add -A 3. Commit all $ git commit -m Create & Clone empty remote Repo 1. Create a new repository on GitHub. To avoid errors, do not initialize the new repository with README, license, or gitignore files. 2. Clone remote repo Stage Remote Repo 3. Push to remote repo DONE!!!