Git & GitHub 快速入門


Posted by Nicolakacha on 2020-09-04

這篇主要是 Git 的筆記,也是第一週的作業,但其實自己現在用到 Git 的功能很少,所以也當成之後的參考資料

Git 版本控制 原理 & 基本指令

版本控制的原理

版本控制,就是把檔案所有歷史紀錄的版本都保存下來,方便之後比對、回顧。以前在做報告的時候,不知道大家有沒有類似的經驗,把新修改的檔案另存成一個個新檔案然後在檔名上加上各種…

  • 期末報告.doc
  • 期末報告_2.doc
  • 期末報告_最終版.doc
  • 期末報告_確定版.doc
  • 期末報告_最終確定版.doc
  • 最後變得自己和組員都搞不清楚哪個是哪個… 於是我們有了 Git 這套工具來更有系統的管理檔案的版本歷程

用簡易版控來模擬 Git

為了讓大家在之後能更了解 Git 在做什麼,這裡就用熟悉的資料夾、檔案、和複製貼上功能做一個和 Git 類似的簡易版控。首先我們有一個協作的專案資料夾

  • 專案資料夾
    • index.html
    • main.css
    • index.js

一開始只有你一個人在自己的電腦做,昨天稍微修改了 main.css 和 index.js,為了方便管理,你複製出一份新的,命名為專案資料夾_2

我們把剛才那個 專案資料夾_2 放在雲端空間上,開始和 A、B 同事一起協作吧:

  • A 同事把檔案抓下來,修改了一點 index.html,於是在自己的電腦裡面複製出一份新的叫做 專案資料夾_3 的檔案
  • B 同事也把檔案抓下來修改了一點 index.js,也在自己的電腦裡面複製了一個叫 專案資料夾_3 的檔案
  • 要在上傳同步回去的時候就發生都叫 專案資料夾_3 但修改內容不一樣的衝突情形了
  • 於是我們決定給每個人的版本一個亂碼,已識別不同的版本:
    • A 同事的 專案資料夾_3 變成了 fdfjkdslfhds
    • B 同事的 專案資料夾_3 變成了 jkojidesahal
  • 但是要如何識別這兩個亂碼的資料夾是誰的版本,是什麼時候的版本呢?於是我們做一個 版本紀錄.txt 以記錄不同的版本資訊:
    • 06/14 14:30, A, fdfjkdslfhds
    • 06/14 12:50, B, jkojidesahal
  • 在新增一個文件 最新版本.txt 用來記錄最新版本
    • latest: jkojidesahal
  • 我們可以把不需要進行版本控制的檔案移出資料夾,例如剛才紀錄版本資訊的 txt 檔,完成之後我們的版本控制目錄就會像這樣啦:
    • 最新版本.txt
    • 版本紀錄.txt
    • fdfjkdslfhd
      • index.html
      • main.css
      • index.js
    • jkojidesahal
      • index.html
      • main.css
      • index.js
        其實 Git 在做的事和上面的流程非常相似,等到我們實際操作過一次,就會覺得原來 Git 是一套很直觀易用的工具惹!

基礎指令

  1. 版本控制初始化 git init
    • 為此資料夾建立 git 版本控制初始化,該資料夾內會多一個 .git 的隱藏資料夾
  2. 查看版本控制狀態 git status
    • 可以查看有沒有檔案有做修改了但還沒 commit
    • 也可以查到現在在哪一個 branch
    • 按 q 可以跳出
  3. 加入檔案至版本控制
    git add 要加入的檔案 git add . //把此資料夾內全部檔案到加入版本控制

  4. 把檔案移出版本控制
    git rm --cached 要移出的檔案

  5. 讓 Git 幫忙刪掉檔案並執行 add
    git rm welcome.html

  6. 讓 Git 幫忙改檔名並執行 add
    git mv hello.html world.html

  7. 新建一個版本
    git commit -m "要為該版本建立的版本訊息"
    git commit -am "要為該版本建立的版本訊息" // 可以同時完成 add 和 commit,但是新加入此資料夾的檔案不適用哦

  8. 檢查
    git log // 可以看到每一個版本的 message、該版本號碼(對,長得像一串亂碼的那個)和建立日期
    git log —-oneline // 顯示版本的列表
    git log --oneline --grep="WTF:使⽤ --grep 參數,可以從 Commit 訊息裡⾯搜尋符合字樣的內容
    git log --oneline --author="Sherly\|Eddie" :可以搜尋作者( | 是或的功能)
    git log -S "word" :找到檔案內含有的字
    git log --oneline --since="9am" --until="12am" :找到時間區間內的 commit 紀錄
    git log <file_name>:看這個檔案的 Commit 紀錄
    git log -p <file_name>:看這個檔案到底每次的 Commit 做了什麼修改

  9. 看到是誰修改的
    git blame <file_name> 看看這個檔案的修改紀錄
    git blame -L 5, 10 <file_name> 看看這個檔案指定行數的修改紀錄

  10. 穿越過去與未來
    git checkout 那串版本號 // 可以到過去的版本
    git checkout master // 回到主 branch

  11. 我在 .gitignore,請忽略我
    在版本控制的資料夾內,可以指定某些檔案不一起做版本控制。

    • 步驟:
      1. touch .gitignore // 建立一個 .gitignore 檔案
      2. vim .gitignore // 用 vim 開啟 .gitignore,在裡面把要忽略的檔名打進去
        這樣做的好處是,就算使用 git add .,加進 .gitignore 的檔案也不會被加入版本控制內,往後不需要每次都要手動移出了。

基礎 Git 流程

  1. 在 CLI 中切換到要做版本控制的資料夾的位置 cd D:\文件\test-git-demo
  2. 讓 Git 初始化 git init
  3. 建立 .gitignore,決定那些檔案要忽略不做版控
    touch .gitignore // 建立 .gitignore
    vim .gitignore // 打開 .gitignore,把不要版控的檔案名稱打進去
  4. 把檔案加入版本控制 COMMIT git add 檔案名稱
    也可以用 git add . 一次加入全部
  5. 把所有修改生成一個版本 ADD git commit -m "message"
    git commit -am "message" // 可以同時完成步驟 4 和 5
  6. 在各版本中穿梭
    git checkout 版本號碼 // 可以切換各個版本
    git checkout master // 回到主 branch

多人協作就用 Branch

  1. 我們可以把整個專案稱為 repository,簡稱 repo,這裡面一開始就存在的 branch 叫做 master,也就是主要的 branch
  2. 多人同時開發時,我們可以基於 master 再複製建立出不同的 branch 出來個別進行開發
  3. 開發完再合併進去 master,如此就可以在不影響 master 的情況下同時進行不同支線的開發,譬如開一條 branch 是用來修復 bug,另一條 branch 是在做增加新功能,各自開發完之後在合併融入 master

HEAD 到底是誰?

其實也是一組 SHA-1 值,用來指向當前最新的 commit

Branch 指令

  1. 建立名為 new-branch 的新 branch git branch new-branch
  2. 查查看現在有哪些 branch git branch -v
  3. 刪除 branch git branch -d 要刪除的 branch
  4. 刪除的 branch 如果還沒合併 git branch -D 要刪除的 branch
  5. 切換到另一個 branch git checkout 要切到哪一個 branch
  6. 新建並切換到一個 branch git checkout -b sister
  7. 把 branch 給合併進來 git merge 要合併進來的 branch
    確認自己在哪一個 branch,git merge 這裡要填入的是你要把哪一個 branch 合併進來
  8. merge 時不要使用快轉模式合併(保留分支紀錄):git merge <要合併進來的 branch> —no-ff

Rebase 指令

  1. 在 A 分支時執行 git rebase B :我是 A 分支,我現在要重新定義我的參考基準,並且
    將使⽤ B 分支當做我新的參考基準
    Rebase
  2. 取消 rebase,回到 rebase 之前:
    git reset ORGIN_HEAD --hard

Conflict 怎麼解決

兩個 branch 內的同一個檔案都有被更改,因為電知道要以哪一個為準,要 merge 時就會發生衝突

文字檔

  • 兩個 branch 內的同一個檔案都有被更改,因為電腦不知道要以哪一個為準,要 merge 的時候就會發生衝突
  • 發生衝突時,手動把該檔案打開,可以看到它把有衝突的地方標記起來,只要手動修改出,確認需要的內容,並存檔,再重新 merge 一次即可順利解決 conflict

非文字檔

  • 決定要以誰的為準:
    • 以我的為準:git checkout —ours <file_name>
    • 以我的為準:git checkout —theirs <file_name>

互動模式

使用 rebase -i <SHA-1> :這裡要用的 SHA-1 code 是指從現在到該次的 commit 這段期間,會進入互動模式

  1. pick 保留這次的 commit 不做修改
  2. reword 修改當次 commit 的 message
  3. squash 和前一次的 commit 合併 // 用來把多個零散的 commit 整理成一個 commit
  4. 把一個 commit 拆成多個
    edit  // 指令用在要分的 commit
    git reset HEAD~1 // 拆除該 commit
    git add A
    git commit -m "111"
    git add B
    git commit -m "222"
    //即可拆成 111 和 222 兩個 commit,各含有 A 和 B 的修改內容
    
  5. 改 commit 順序:直接搬動位置
  6. drop 刪除 commit
    ## Reset, Rebase, Revert 的差別

Tag 標籤

通常在開發軟體有完成特定的⾥程碑,例如軟體版號 1.0.0 或是 beta-release 之類的,這時就很適合使⽤標籤做標記。

lightweight tage

git tag <tag_name> <SHA-1>
輕量標籤則是用來個⼈使用或是暫時標記用途

annotated tag

git tag <tag_name> <SHA-1> -a -m "This is a tag"
有附註標籤主要用來做像是軟體版號之類的用途

刪除標籤

git tag -d <tag_name>

分支和標籤的區別

分支會隨著 commit 而移動,但標籤不會

Cherry-pick

可以用來複製某次或某些 commit 來用
git cherry-pick <SHA-1> 該次的 commit 就會被接到現在的 commit 之上了
git cherry-pick <SHA-1> <SHA-1> <SHA-1> 也可以一次撿好幾個來用
git cherry-pick <SHA-1> --no-commit 撿過來但先不要合併, 該 commit 會被放到暫存區(也就是 add 完的狀態)等待 commit

把檔案真正從 Git 裡面刪掉

以刪掉 database.yml 為例:

  1. git filter-branch -f --tree-filter "rm -f config/database.yml" 把所有 commit 裡的 abc.yml 刪掉,強制覆寫 filter-branch 的備份點。
  2. rm .git/refs/original/refs/heads/master
    斷掉這個剛才做 filter-branch 的備份點
  3. git reflog expire --all --expire=now
    讓 reflog 立刻過期
  4. git fsck --unreachable
    找到 unreachable 的物件
  5. git gc --prune=now
    啟動資源回收機制
  6. 如果內容已經 push,記得用 git push -f 把線上的紀錄蓋掉

Dangling 跟 Unreachable 物件有什麼不同?

  1. Unreachable 物件:沒有任何物件或指標指著它,所以如同字⾯說的「無法到達」。雖然
    它「無法到達」,但它可以指著其它物件。
  2. Dangling 物件:跟 Unreachable 物件⼀樣,沒有任何物件或指標指著它,就完全是懸在天
    邊的⼀顆物件。

GitHub

可以理解為放 repo 的雲端

push

  • 把本地的 repo 同步推上 GitHub
  • git remote add origin https://github.com/Nicolakacha/test.git //新增一個節點
  • git push -u origin master
    把本地的 master 分支推上去後,在 Server 上更新 master 分支的進度,或是如果不
    存在該分支的話,就建立⼀個 master 分支
    // -u 是 upstream 的意思:每個分支可以設定一個「上游」,下次執行 git push 指令而不加任何參數時,它就會用來當預設值
  • git push origin master: cat
    這樣當把本地端的 master 分支推上去之後,就不會在線上建立 master 分支,而是建立(或
    更新進度)⼀個叫做 cat 的分支

fetch

Git 看了⼀下線上版本的內容後,把你目前線上有但你這邊沒有的內容抓了⼀份下來
git merge origin/master
抓下來以後,需要把本地的 branch 和抓下來的 branch merge起來

pull

  • 其實就等於替代了上面 fetch 加上 merge
  • 把 GitHub 上的 repo 同步拉下到本地 git pull origin master

clone

  • 把別人的 repo 下載一份到本地
  • 不能修改,也不能 push 回去別人 GitHub 上的 repo git clone <SSH address>

fork

  • 把別人的 repo 複製一份叉到自己的 GitHub 上

多人協作的 Git & GitHub 流程

  1. 在 GitHub 上 fork 別人的 repo
  2. pull到 local
  3. 開新 branch
  4. 開始在這個新 branch 裡面開發 <- 做上面的基礎 Git 流程
  5. 開發完之後 push 自己的 GitHub
  6. 發起 pull request 請求把這個 branch merge 到原本的 repo 裡
  7. 在上面討論,等待對方確認是否可以 merge
  8. merge 成功,刪掉 branch

GitHub Page

GitHub Page 是 GitHub 上免費提供可以做靜態網頁展示的的功能: GitHub Page
GitHub 官方建議的工作流程參考 GitHub Flow

狀況實例

打錯字了,想改最後一次的 commit message

git commit --amend -m "message"
// 雖然這只是改訊息,不管如何它就是修改了⼀次的歷史,所以請儘量不要在已經 Push
出去之後再修改,否則可能會造成其它⼈的困擾

Commit 之後,想要再把某一個檔案加到這次的 commit

git add <file> // 一樣先加入檔案
git commit --amend --no-edit // 把檔案併入最後一次的 commit

我 commit 了但後悔了

git reset HEAD^ --hard // 完全拆掉該 commit
git reset HEAD^ --soft // 剛才那個修改的檔案還是在,只是在沒有 commit 的狀態
git reset HEAD~1 // 相對路徑:拆掉HEAD,回到上一個 commit
git reset 6d873ad~1 // 絕對路徑:回到 6d873ad 的上一個 commit
git revert HEAD --no-edit 再做⼀個新的 Commit,來取消你不要的 Commit

還沒 commit 但後悔了想回到原來的狀態

git checkout -- <file> // 拋棄剛才的改變
git checkout . //拋棄剛才所有檔案的改變

用 reset 把 commit 拆掉之後,又想救回來

  1. 使用 git reflog 或是 git log -g 看到紀錄
  2. 使用 git reset cd3d692 --hard 把拆掉的 commit 再組回來

只 add 部分內容

git add -p 會詢問是部是要加整個區塊進去,如果選 y ,整個檔案會被加進去
如果選 e 就會進入編輯器,可以把不要 add 的部分刪掉

改 branch 的名稱

git branch -m <new_branch_name>

想把本地端沒有的遠端 branch 抓下來

  • git checkout <github_branch_name>
  • 但如果遠端的 branch 如果不是從 local 端發出,或在 local 端之外有新的 commit,就沒辦法直接下載完整的 branch,要先 git fetch 抓到遠端 branch 的新狀況,然後把新狀況 merge 進我們 local 端的 branch,或更直接的開一個同名的 branch 並 pull 下來之後,才是完整的 branch。
  • 參考資料

不小心把 branch 刪掉,想要救回來

  1. 使用 reflog 找到該 branch 物件最後指向的 commit 的 SHA-1值
  2. 使用 git branch <branch_name> <剛才找到的SHA-1> 把一個新的 branch 標籤貼在這個 commit 上
  3. git checkout <branch_name> 切換過去看看,這樣就救回來啦

想要一口氣清除忽略的檔案

git clean -fX

檔案被刪掉想要救回來

git checkout <file_name>

不小心把重要檔案放在 Git 裡怎麼刪掉?

使用 filter-branch :一次大量修改 commit
git filter-branch --tree-filter "rm -f <file_name>" 一次全部移除所有 commit 裡面的這個檔案
唔...又後悔了,想要變回來怎麼辦?使用指令:git reset refs/original/refs/heads/master --hard

push 不上去怎麼辦?

可能因為本地電腦裡的內容較舊,需要先拉回一份線上版本再推:

  1. git pull --rebase // 加上 rebase 可以不產生為了 合併分支而產生的 commit
  2. git push 重新再 push 一次

Git Hook

預先設計好腳本,在某件事發生的時候觸發我們寫好的腳本
像是可以在 commit 之前檢查程式碼有沒有符合規範

怎麼寫作業和交作業?

  1. 把課綱複製一份到自己的 GitHub 上
  2. 把自己 GitHub 上的課綱 clone 到本機
  3. 新開一個 branch,在新的 branch 上寫作業
    git branch week1
  4. 把修改的內容加入倒版本控制裡面並 commit 新建一個版本
    git commit -am "版本名稱"
  5. 把在本機的 branch 推回遠端
    git push origin week1
  6. 在 GitHub 上 create pull request,至此完成作業
  7. 到學習系統的作業列表按新增作業,貼上 PR 連結
  8. 如果 pull request 被 merged,就代表作業被改完了
  9. 把遠端的 master 和本機的 master 同步
    git pull origin master
  10. 把本地原來新增出來用來寫作業的 branch 刪除
    git branch -d week1

怎麼同步老師的 repo?

  1. git pull <web URL.> master
  2. git push origin master

#Git







Related Posts

JavaScript 程式執行原理:hw1 Event Loop

JavaScript 程式執行原理:hw1 Event Loop

AI輔導室|馬賽克拼貼效果

AI輔導室|馬賽克拼貼效果

[ 筆記 ] Express 01 - 基本架構 MVC

[ 筆記 ] Express 01 - 基本架構 MVC


Comments