Git/Github = ## [0]Version Control - **版本控制是什麼?** - 版本控制系統(Version Control System, VCS) - 簡單來說如果你要手動版控就是在每個階段都建立一個專案的副本存著 - 但這樣搞你電腦要存很多檔案,而且可能會用錯版本 - 所以就有人生出版控系統了 - 可以想像成你的專案的修改日誌 - 會包含修改的人、修改時間、修改細節,還有修改的主題 - **本地端的版本控制** - 從頭到尾所有檔案都在同一個地方(local computer) - 紀錄專案變更歷史(version control) - ex. RCS - 那如果有超過一個人想要編輯專案怎麼辦? ![local](https://hackmd.io/_uploads/B1Wxtje1Ze.png) - **集中式版本控制系統** - Centralized Version Control Systems, CVCSs - 專案有多個人在用 - 有一個中央的伺服器紀錄這個專案的版本資訊 - 三大重點:同步、追溯、以及檔案的備份 - 同步:讓所有人對專案所做的變更都能夠同步,得到相同的內容 - 追溯:能夠回到專案在變化歷史中的任何一個版本,並且明白每個版本間變化的原因、以及究竟做了什麼變動 - 備份:讓所有的檔案集中保管於檔案庫中 - 鎖定模式:當有人想要修改並簽出該檔案後,檔案進入鎖定狀態,其他人無法加以修改直到簽出者將檔案簽回 - 對維持同步來說十分保險的作法,因為永遠不會有兩個或以上的開發者同時修改同一個檔案 - 開發者對於檔案修改的互斥,使得開發效率爛的可怕 - 合併模式:多人可同時修改同一檔案,當他們分別將檔案提交回集中的檔案庫時,若發生衝突的情況(超過一個人改了檔案中的同一個部分),便會自動進行合併,而若自動合併失敗,再要求人工進行衝突的調解 - ex. CVS、Subversion、Perforce - 缺點: - 中央伺服器發生故障的時候,如果當機一小時,這個小時之中,沒有人可以提交更新也無法協同合作 - 中心版本庫的硬碟發生損壞又沒有做適當的備份,那就會遺失所有資料 - 雞蛋全部放在同一個籃子裡的概念 - 檔案庫是集中控管,所以如果想要從檔案庫取得資訊或檔案或是將檔案提交至檔案庫,都必須在能夠連網的環境下 - 假設檔案已經做好修改想要提交,但要等到連網 - 為什麼不等到能聯網之後再提交就好了? - 每次提交的變更都是一個不可分割的最小單位 - 無法將已完成的修改提交出去就無法繼續修改其他的項目(會讓應被分為多次提交的內容混在一起) -> 降低工作效率 - 每個開發者的修改,常常是都在處理同一問題 - 有可能未到穩定、完備到足以提供其他開發者使用的程度就提交了 -> 讓集中檔案庫中的檔案處於不穩定、不成熟的狀態 - 若到完備再提交 - 在這個期間就得不到版本控制的好處了 - 失去版本控制的意義(版本資訊) ![centralized](https://hackmd.io/_uploads/ByQGtogkZx.png) - **分散式版本控制系統** - Distributed Version Control Systems, DVCSs - 檔案庫允許不只一份,每人都可在自己的一部或多部開發機器上建立檔案庫 - 不要再把所有東西都放在同一台伺服器上 - 讓每個人各自獨立的變更專案,彼此可分享自己變更的紀錄 - 存取時不只是取出最後一版的檔案,而是完整複製整個檔案庫 - 伺服器的角色:交換中心 - 檔案庫和檔案在本地的工作複本的差別: - 版本管理有關的資訊 ex. 提交訊息、版本變化的記錄 - 對版控系統的操作可直接在本機端的檔案庫進行 ex. 提交、分支 - ex. Git、Mercurial、Bazaar、Darcs ![distributed](https://hackmd.io/_uploads/Hku4KoxkWg.png) ## [1]What is Git? > Linux kernel 是規模相當大的開放原始碼軟體專案。 Linux kernel 在 1991 年到 2002 年間的維護工作,幾乎都是透過補丁和壓縮檔來完成的。 在 2002 年時,Linux kernel 開始採用名為 BitKeeper 的商業分散式版本控制系統。 > > 在 2005 年時,開發 Linux kernel 的社群與開發 BitKeeper 的商業公司的合作關係結束,也就無法再免費使用該工具。 這就迫使了 Linux 社群(特別是 Linux 之父 Linus Torvalds)基於使用 BitKeeper 所學到的經驗,來開發自有的工具。 這個系統必須達成下列目標: > 快速、簡潔的設計、完整支援非線性的開發(上千個同時進行的分支)、完全的分散式系統、能夠有效地處理像 Linux kernel 規模的專案(速度及資料大小) - 一個免費、開源的分散式版本控制系統 - 大部份的操作皆可在本地端完成 aka 離線能用 - 主要任務:記錄專案所有的修改歷史,並讓多人能安全地協作 - Linux 系統的開發者 Linus Torvalds 為管理 Linux kernel 開發而創造 #### 與其它版本控制系統的差別 - 處理資料的方式 - 其他系統大多紀錄一連串檔案更改的資訊 - 儲存一組基本的檔案以及這些檔案隨時間遞增的更動資料 ![deltas](https://hackmd.io/_uploads/H1SB6Gb1bg.png) - Git 每次提交(commit, 在 Git 儲存目前專案的狀態)時都會儲存整個專案當時的所有樣子 - 將這些資料視為小型檔案系統的一組快照(Snapshot) - 若檔案沒變不會重複存,直接將上一次相同的檔案參照到這次快照中 - 把資料視為一連串的快照 ![snapshots](https://hackmd.io/_uploads/HyIXJm-y-e.png) #### 檢查完整性 - 透過 SHA-1 雜湊為所有東西產生唯一識別碼:checksum - 內容有一個字不同 checksum 就會完全不同 - 防止竊改 #### 只增加資料 - 幾乎所有的動作都只是增加資料到 Git 的資料庫 - 提交快照到 Git 後很難會發生遺失的情況 - 使用 Git 的時候時可以像在玩玩具一樣,因為可以隨意操作而不會弄壞任何東西 ## [2]Git/Github - ~~Git(1+hub)~~ - Git 不是 GitHub - GitHub 是一個基於 Git 的雲端版本控制平台 - 用於儲存與管理程式碼 - 也被廣泛應用於: - 專案文件管理(Project Documentation) - 個人履歷與作品集展示(Digital Portfolio) - 團隊協作與版本追蹤(Collaboration & Version Control) - 學習筆記與知識管理(Knowledge Management) ### Github - 註冊 [GitHub](https://github.com/) 帳號 #### Repository - 一個 Repository(Repo,儲存庫)代表一個專案 - Repo 包含:原始碼、版本歷史(Git 紀錄)、討論區(Issues)、協作紀錄(Pull Requests) - 分散式協作 - 每個協作者都有自己的 local repo,包含所有的歷史紀錄、分支、設定 - 透過 push 把變更上傳,pull 把別人的變更拉回本地 - 中心伺服器是交流節點 - 新建 Repo: - 直接在 GitHub 上建立:空的遠端儲存庫 - 把本地已有的 Git 專案上傳(push)上去 #### Issues 無論是個人或公開專案,你都可以透過建立 issue 來表達自己的想法,如 回報 Bug、強化既有功能、建立工作事項與討論專案規劃。 每一份 issue 皆公開且有完整紀錄,能整合程式碼並且配合後續 CI/CD 功能確定問題是否解決。 - 專案管理系統內管理工作事項的功能 - 可以把你對這個 repo 的想法告訴開發者 - 回報 bug、討論、指派任務、建議... - Github 協作文化一個重要的部分 #### Pull Request (PR) - 對 repo 沒有修改權限時可用 - 你看到一個專案覺得某個地方可以優化,決定寫 - PR 可以請開發者把你的這個修改拉進去 - 除了合併,也包含討論、審查、測試的流程 - 協作最直接的方式,讓開源專案能安全的接受他人貢獻 #### Github Pages - 你的 repo 放的專案是個網頁 - Github 可以幫你把它變成公開的網站 #### GitHub 操作 - Push:把本地的修改歷史同步到 Github 的 repo,讓其他人也看得到 - Pull:把在 GitHub 上有更動 repo 同步更新至本機 - Clone:複製一份別人公開的 repo 到本機 - 下載檔案的概念,對方的 repo 不會被影響 - Fork:建立一個 repo 的副本在自己的 Github 頁面上 - 對方的 repo 不會被影響 ## [3]CLI - Command Line Interface:透過文字指令與電腦互動的介面 - CLI 是唯一可以使用**所有** Git 的功能的地方,因為大多數的圖形界面(Graphic User Interface, GUI)為了簡單起見只實作了 Git 的部分功能 ### 終端機 - 可以輸入文字指令來控制電腦的介面 - 常見終端機軟體: - macOS:Terminal(內建) - Windows:PowerShell、Command Prompt、Git Bash - 指令提示字元(Prompt) - 開啟終端機時,會看到類似這樣的畫面 ``` user@MacBook ~ % ``` - 代表系統正在等待你輸入命令 ### 常見指令 - 非 Git 專用 | Windows | MacOS/Linux | 說明 | | --- | --- | ----- | | cd | cd | 切換目錄 | | cd | pwd | 取得目前所在位置 | | dir | ls | 列出目前的檔案列表 | | mkdir | mkdir | 建立新的目錄 | | - | touch | 建立檔案 | | copy | cp | 複製檔案 | | move | mv | 移動檔案 | | del | rm | 刪除檔案(救不會來) | | cls | clear | 清除畫面上的內容 | - 打指令的格式:`提示字 指令 參數1 參數2 ... 參數n` #### `pwd` ``` # MacOS/Linux $ pwd /tmp # Windows D:\abc> cd D:\abc ``` - print working directory - 顯示目前所在資料夾的完整路徑 - Git 會根據你所在的位置判斷是否在 repo 中 - `.git` 存在的資料夾裡 Git 才會啟用版本控制功能 - `.git` 是 Git 專案的資料庫,負責儲存所有歷史紀錄與設定 - 每個被 Git 管理的專案都會有一個 `.git` - clone 一個專案 `.git` 也會被複製下來 #### `ls` - 列出目前資料夾中的所有檔案與資料夾 - 搭配參數 `-a` 顯示隱藏檔(檔名為`.`開頭) - `ls -a` 可檢查 Git 專案是否成功初始化(出現 `.git`) #### `cd` - 進入或離開資料夾 - `cd a`:進入`a`,`a`必須在原本使用者所在的目錄中 - `cd ..`:回到上一層目錄 - `cd ~`:回到根目錄 - 必須進入專案根目錄後再下 Git 指令 #### `mkdir` - 建立新的資料夾在當前目錄 - 建立後要再`cd`才會進到新建立的資料夾 - 先看[4] ### Git CLI #### 安裝 Git - https://git-scm.com/install/ - macOS:homebrew ``` $ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" $ brew install git ``` - Windows:https://git-scm.com/install/windows - `git --version`:驗證安裝成功 - 如果成功會出現`git version 2.xx.x` #### 使用者設定 - 所有 Git 相關的設定:`.gitconfig` - 這些設定允許控制 Git 各方面的外觀和行為 - 第一次使用 Git 時先設定你的姓名與電子郵件 ``` $ git config --global user.name "名字" $ git config --global user.email "電子郵件地址" ``` - 檢視目前設定 `git config --list` - 為每個專案設定不同作者:在專案根目錄下 ``` $ git config --local user.name "名字" $ git config --local user.email "電子郵件地址" ``` #### 建立一個 repo - 建立一個目錄 ``` $ mkdir repo-name $ cd repo-name $ git init # 初始化目錄,讓 Git 對這個目錄開始進行版控 Initialized empty Git repository in /資料夾目錄/.git/ ``` - `$ git init` - 在目錄中建立`.git` - 如果不想再被版控就把它刪了 - `$ git status` ``` On branch master No commits yet nothing to commit (create/copy files and use "git add" to track) ``` - 看一下現在的狀況 - 顯示目前有哪些修改、哪些檔案被追蹤 - `$ git add <檔名>` - 把檔案加入暫存區 - `$ git add .` :選擇當前目錄下的所有檔案 - `$ git add --all`:選擇專案裡所有檔案 - commit 前若再次更改要再 add 一次 - `$ git commit -m 'commit message'` - 把暫存區裡的東西提交到儲存庫 - `-m 'commit message'` 紀錄起來 - 沒加會跳出視窗叫你打 - 有點新手不友善 所以要記得加 - Git 每次的 Commit 都只會處理暫存區裡的內容 - [做一下這個](https://slides.com/justintsai-1/git#/1/8) - 有不想記錄的檔案 - 在專案裡放一個叫`.gitignore`的檔案,裡面放不想紀錄的檔案的檔名 - `$ git log` - 查看 Git 紀錄 - `$ git log --oneline --graph`:易讀性提高 - `$ git diff` - 看目前修改的版本和上一次的差異 - 只會顯示還沒 add 的東西 - `--cached`:暫存區和最後一次 commit 的差異 - `$ git reset` - 回復 commit - `$ git reset HEAD^`:回復到上一個 commit - `$ git reset {該 commit 的 SHA-1}`:回復到特定 commit - 轉生 - 邏輯上`rm`刪除的檔案就不回來 - 但 git 有不死圖騰 ``` $ rm important.txt # 靠完蛋了 $ git checkout important.txt # 回來ㄌ ``` - 其他的等等講到會講 ## [4]The Three Stages ### 工作區 Working Directory - 直接編輯的地方 - 在桌機上肉眼可見,可直接操作 - `$ git status`:Untracked Files / Changes not staged for commit - `$ git add <file>` 把檔案扔到暫存區 ### 暫存區 Staging Area - 數據暫時存放 - 紀錄哪些檔案即將要被提交到下一個 commit 版本中 - 用以針對不同狀況去操作不同指令來控制檔案 - `$ git status`:Changes to be committed、new file - `$ git commit <file>` 扔進 repo ### 儲存庫 Repository - 就是你知道的那個 repo ## [5]Commit - 一個快照 - 作者(Author)、時間(Timestamp)、訊息(Message)、唯一識別碼(Hash, SHA-1) - `$ git log` 查看完整 commit 歷史 - `$ git log --oneline --graph --decorate` ### HEAD - 目前所在版本的指標 - 指向當前分支的最新 commit ### How to Commit - 一次 commit 應該只完成一件事 - 一次處理很多事應拆分後再 commit - commit message 要寫到可以看出 Why 和 What - 分成 Header、Body、Footer - Header 用祈使句,可包含 Type ## [6]Branch - 一條版本線 - 在 commit 鏈上開分支 - 不同功能、修 bug、測試新東西,可以各自獨立 - 修改成熟前正在運行的服務(main/master branch)不會被影響 - 不同 branch 之間的 commit 紀錄是獨立的 ## [7]Conflict - Git 在合併時,遇到同一段內容在兩個分支被不同方式修改 - Conflict 表示 Git 無法自動決定要保留哪一個版本,需要人工處理 ![conflict](https://hackmd.io/_uploads/HkKf6dIbWx.png) - 直接改這個 - 先 `$ git add` 再 `$ git merge --continue` ## [8]Merge - 將另一個分支的歷史合併到目前所處的分支 - 合併後會產生新的 merge commit,保留兩條分支的歷史 ### rebase ``` $ git checkout <你要接在後面的那個 branch> $ git rebase <接在你前面 branch> ``` ![images (9)](https://hackmd.io/_uploads/H19YhFUZZg.png) - 將一個分支的 commit搬到另一個分支的最尾端 - 歷史變成線性的 - 線圖是一種展示 Commit 依賴性的工具 - 當兩個 Commit / branch 有前後依賴性可用 rebase - Rebase 完會需要 force push,蓋掉一些 commit - 一般 push 只允許把遠端指標往前推(fast-forward),而 rebase 後等於你想把遠端指標拉回去再換一條新的歷史 #### 範例 ![rebase-branch0](https://hackmd.io/_uploads/Bk7cCYIZZe.png) ``` $ git checkout cat $ git rebase dog ``` ![rebase-branch1](https://hackmd.io/_uploads/r1E90YUbZe.png) ### squash ``` $ git rebase -i HEAD~n ``` - 把一坨 commit 擠成同個 - 幾乎不會也不該用到 ## [9]Workflow - 要怎麼開 Branch - 每個 Commit 要在哪 - Merge 順序要是什麼 - 確保分支有系統和固定邏輯 ### Github Flow ![images (10)](https://hackmd.io/_uploads/H1PFMcUW-l.png) - master:最新穩定版本 - feature:有新功能或是要修東西時就開一個 branch - pull request:工程師修好了發 PR,開始程式碼審查、討論 - continuous deployment:PR 被批准後合併回主分支 ### Git Flow ![BlogImage](https://hackmd.io/_uploads/Hyi6EqLbWl.png) - **master**:所有經過測試且打好版本標籤(tag)的釋出 - **develop**:所有完成 code review 並準備整合的新功能先合併到 develop - 僅用於開發期間的自動化測試與驗證 - feature/*:每個新功能或需求對應一個 feature 分支 - 從 develop 分出,開發完後再合併回 develop - release/*:develop 上的功能達到下一個版本標準時切出 - 只做最終測試相關修正和文檔更新 - 同時合併到 master(打 tag)與 develop - hotfix/*:線上出現重大問題立刻切出來修 - 修完合併到 master(打 tag)與 develop 同步 ## [10]實作 ``` $ mkdir git-practice $ cd git-practice $ git init ``` ![Screenshot 2025-11-06 at 3.41.36 PM](https://hackmd.io/_uploads/Bk6VZAtyZl.png) - git 成功開始版控ㄌ --- ``` $ git status ``` ![Screenshot 2025-11-06 at 3.42.20 PM](https://hackmd.io/_uploads/SkIY-Ct1-e.png) - 現在在 master 上 - 還沒有 commit - 也沒有東西可以 commit --- ``` $ touch hellogit.txt $ git status ``` ![Screenshot 2025-11-06 at 3.46.55 PM](https://hackmd.io/_uploads/BkiOzAtk-g.png) - 建立了 hellogit.txt - Untracked files:檔案尚未被加到 Git 版控系統裡,還沒開始正式被 Git「追蹤」 --- ``` $ git add hellogit.txt $ git status ``` ![Screenshot 2025-11-06 at 3.50.27 PM](https://hackmd.io/_uploads/HJWUmCFybg.png) - 把 hellogit.txt 交給 Git 開始管控 - 從 Untracked 變成 new file - 檔案已被安置到暫存區,等待稍後一起被存到儲存庫裡 --- ``` $ git commit -m "Initialize" ``` ![Screenshot 2025-11-06 at 3.59.07 PM](https://hackmd.io/_uploads/SkYUHRFy-x.png) - 把現在暫存區的東西都 commit 了 --- ``` $ git branch dev ``` - 建立 dev 這個分支 ``` $ git checkout dev ``` ![Screenshot 2025-11-28 at 9.19.24 AM](https://hackmd.io/_uploads/S1W5Y_L-Zl.png) - 把 HEAD 切換到 dev ``` $ git checkout -b feature1 ```` ![Screenshot 2025-11-28 at 9.21.44 AM](https://hackmd.io/_uploads/SyYSK_UbWg.png) - 建立分支並切換到這個分支 --- ## References - https://slides.com/aaw/burn-chicken/#/6 - https://hackmd.io/@Sylvia-H/rkdKW2iaL - https://learn.microsoft.com/zh-tw/devops/develop/git/what-is-version-control - https://androidpollyanna.medium.com/git-基本原理-什麼是版本控制系統-4134d7024d18 - https://www.astralweb.com.tw/version-control-and-github-project-build/ - https://www.ithome.com.tw/voice/77088 - https://git-scm.com/book/zh-tw/v2 - https://willh.gitbook.io/gitpro/7d615cb13a55ac231e35e8658a897e0e/fbc71b2bb6fb6e6f999917afe5281632 - https://career-map.org/2025/02/18/%E3%80%90%E5%AE%8C%E6%95%B4%E6%8C%87%E5%8D%97%E3%80%91%E9%9D%9E%E5%B7%A5%E7%A8%8B%E5%B8%AB%E4%B9%9F%E8%83%BD%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B%E7%9A%84-github-%E4%BD%BF%E7%94%A8%E6%95%99%E5%AD%B8 - https://docs.github.com/en - https://ithelp.ithome.com.tw/m/articles/10259830#:~:text=GitHub%20Issue%20%E6%9C%89%E9%BB%9E%E5%83%8F%E6%98%AF,%E5%8A%9F%E8%83%BD%E7%A2%BA%E5%AE%9A%E5%95%8F%E9%A1%8C%E6%98%AF%E5%90%A6%E8%A7%A3%E6%B1%BA%E3%80%82 - https://medium.com/@brad61517/git101-%E5%BF%83%E5%BE%97-%E7%AD%86%E8%A8%98-github-%E6%93%8D%E4%BD%9C-push-pull-clone-fork-a414d4af64be - https://gitbook.tw/ - https://brew.sh/ - https://git-scm.com/install/ - https://learn.microsoft.com/en-us/windows/package-manager/winget/ - https://ithelp.ithome.com.tw/articles/10211790