# Git [TOC] {%hackmd @88u1wNUtQpyVz9FsQYeBRg/r1vSYkogS %} ###### tags: `Git` > 以`\190614師兄打卡系統\code\`此專案目錄為例,展示`git`。 這是一個桌面應用的開發,但以下不討論專案內容 # 安裝與環境 很簡單直接跳過吧 # 終端機及常用指令介紹 |--Windows--|--MacOS / Linux--|--說明--| --------------|:-----:|-----:| ----:|------------------------ cd | cd | 切換目錄 cd| pwd| 取得目前所在的位置 dir| ls| 列出目前的檔案列表 mkdir| mkdir| 建立新的目錄 無| touch| 建立檔案 copy| cp| 複製檔案 move| mv| 移動檔案 del| rm| 刪除檔案 cls| clear| 清除畫面上的內容 :::warning 由於我是用`Git Bash`所以要參照linux指令 ::: ## 查看Git版本 ``` // 其中 $ 為提示字元,在輸入指令時不用輸入該符號,否則會錯誤,若是使用 windows cmder 預設是 λ $ git --version git version 2.23.0.windows.1 ``` ## 路徑切換及查看 ```git= // cd到E的專案位置 s710262101@ios7188-826 MINGW64 ~ $ cd E:/我的雲端硬碟/drive/190614師兄打卡系統/code ``` ```git= // 確認當前路徑 s710262101@ios7188-826 MINGW64 /e/我的雲端硬碟/drive/190614師兄打卡系統/code $ pwd /e/我的雲端硬碟/drive/190614師兄打卡系統/code ``` 其他路徑相關指令 ```git= # 切換到 /tmp 目錄(絕對路徑) $ cd /tmp # 切換到 my_project 目錄(相對路徑) $ cd my_project # 往上一層目錄移動 $ cd .. # 切換到使用者的 home 目錄中的 project 裡的 namecards 目錄 # 這個 "~" 符號表示 home 目錄 $ cd ~/project/namecards/ # 顯示目前所在目錄 $ pwd ``` ## 檔案列表 > 使用 Git 前必知終端機指令:**檔案列表** `ls` 指令可列出在目前目錄所有的檔案及目錄,後面接的 `-al` 參數,`a` 是指連小數點開頭的檔案(例如.gitignore)也會顯示,`l` 則是完整檔案的權限、擁有者以及建立、修改時間: ```git= s710262101@ios7188-826 MINGW64 /e/我的雲端硬碟/drive/190614師兄打卡系統/code $ ls -al total 29 drwxr-xr-x 0 s710262101 1049089 0 十月 24 10:39 ./ drwxr-xr-x 0 s710262101 1049089 0 十月 24 10:40 ../ drwxr-xr-x 0 s710262101 1049089 0 十月 22 14:17 __pycache__/ drwxr-xr-x 0 s710262101 1049089 0 十月 22 11:02 build/ -rw-r--r-- 0 s710262101 1049089 2163 十月 24 10:39 data_tosql.py -rw-r--r-- 0 s710262101 1049089 1318 十月 22 11:45 database_connection.py -rw-r--r-- 0 s710262101 1049089 246 十月 22 07:29 desktop.ini drwxr-xr-x 0 s710262101 1049089 0 十月 22 14:19 dist/ -rw-r--r-- 0 s710262101 1049089 2304 十月 17 13:58 punchSystem-7c27e17aa7de.json -rw-r--r-- 0 s710262101 1049089 987 十月 22 11:02 pyqt5.spec -rw-r--r-- 0 s710262101 1049089 9933 十月 24 10:39 pyqt5_main.py -rw-r--r-- 0 s710262101 1049089 875 十月 22 14:17 pyqt5_main.spec -rw-r--r-- 0 s710262101 1049089 7410 十月 22 11:53 sqlite_op.py -rw-r--r-- 0 s710262101 1049089 0 十月 21 19:31 test.db ``` ## 建立檔案、目錄 > 建立檔案 `touch` ```git= s710262101@ios7188-826 MINGW64 /e/我的雲端硬碟/drive/190614師兄打卡系統/code $ touch test.html ``` :warning: 如果 `test.html` 這個檔案本來不存在,`touch` 指令會建立一個名為 `test.html` 的空白檔案;如果本來就已經存在,則只會改變這個檔案的*最後修改時間*,不會變更原本這個檔案的內容。 > 建立資料夾 `mkdir` ```git= s710262101@ios7188-826 MINGW64 /e/我的雲端硬碟/drive/190614師兄打卡系統/code $ mkdir justfortest ``` ![](https://i.imgur.com/9tUQAnG.png) :::info 以下省略 指令中的路徑::s710262101@ios7188-826 MINGW64 /e/我的雲端硬碟/drive/190614師兄打卡系統/code ::: ## 檔案操作 > `cp`複製檔案 > `mv`更改檔案名稱 > `rm`刪除檔案 ```git= $ cp test.html copy_test.html $ mv test.html new_test.html $ rm new_test.html ``` ![](https://i.imgur.com/QGcbV88.png) > 刪除在這個目錄裡所有的 html 檔: > ```git= $ rm *.html ``` # 使用者設定 ## 帳號及信箱設定 設定使用者的 Email 信箱以及使用者名稱。 ```git= $ git config --global user.name "Lee TsungTang" $ git config --global user.email "s710262101@gm.ntpu.edu.tw" ``` > 檢視目前設定 ```git= $ git config --list core.symlinks=false core.autocrlf=true core.fscache=true color.diff=auto color.status=auto color.branch=auto color.interactive=true help.format=html rebase.autosquash=true http.sslbackend=openssl http.sslcainfo=C:/Program Files/Git/mingw64/ssl/certs/ca-bundle.crt credential.helper=manager diff.astextplain.textconv=astextplain filter.lfs.clean=git-lfs clean -- %f filter.lfs.smudge=git-lfs smudge --skip -- %f filter.lfs.process=git-lfs filter-process --skip filter.lfs.required=true core.editor="D:\programfile\Microsoft VS Code\Code.exe" --wait user.name=Lee TsungTang user.email=s710262101@gm.ntpu.edu.tw ``` :waning_crescent_moon: 如果你有安裝過 Git 相關的圖形化介面工具,可能 git `config --list` 這個指令還會輸出其它額外的設定,但至少我們剛剛的這兩行設定會新增 `user.name` 跟 `user.email` 這兩個設定。 ## 設定檔的位置 所有 Git 相關的設定,預設會在自己帳號下的 `.gitconfig` 這個檔案裡,所以使用一般的文字編輯器,直接手動修改這個檔案也會有一樣的效果: 檔案:`~/.gitconfig` ![](https://i.imgur.com/vdMDv61.png) ![](https://i.imgur.com/zX50Q4e.png) ## 不同專案設定不同作者 前面設定時有 `--global` 的參數,意思是要做==全域(Global)的設定==。但偶爾會遇到一些特別的狀況,例如是要幫特定的專案設定不同的作者,可以在該專案目錄下進行 Git 設定的時候,加上 ==--local 參數==: 例如 ```git= $ git config --local user.name Sherly $ git config --local user.email sherly@5xruby.tw ``` :8ball: 如此在此專案操作時就會使用該使用者與Email操作。離開此專案後的git操作,則還是沿用Global的設定 # 新增、初始 Repository ## 初始化git目錄 由於前面已經切換到專案目錄了,此處可以直接初始化該目錄 ```git= $ git init Initialized empty Git repository in E:/我的雲端硬碟/drive/190614師兄打卡系統/code/.git/ ``` :waning_gibbous_moon: 初始化的目的是要讓 Git 開始對這個目錄進行版本控制。 其中,第 3 步這個指令會在這個目錄裡建立一個 `.git` 目錄,整個 Git 的精華就都是在這個目錄裡了。之後會詳細討論 ![](https://i.imgur.com/sj6BTA2.png) ### vscode 初始化git ![](https://i.imgur.com/ooj0f6f.png) ![](https://i.imgur.com/PSZZPeM.png) ## 如果目錄不想再被git控制.. 請直接移除`.git`目錄,Git即失去控制權 :warning: 整個專案目錄裡,什麼檔案或目錄刪了都救得回來,但 `.git` 目錄只要刪了就沒救了。 # 提交檔案給 Git 控管 ## 提交檔案 > 先以`git status` 這個指令來查詢現在這個目錄的「狀態」: > ```git= $ git status On branch master No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) __pycache__/ build/ data_tosql.py database_connection.py desktop.ini dist/ punchSystem-7c27e17aa7de.json pyqt5.spec pyqt5_main.py pyqt5_main.spec sqlite_op.py test.db nothing added to commit but untracked files present (use "git add" to track) ``` :waning_gibbous_moon: 目前所有檔案的狀態皆為`Untracked files`,意思是檔案尚未被加到 Git 版控系統裡,還沒開始正式被 Git「追蹤」 :waning_crescent_moon: `git status -s` 可以簡化輸出結果 > 先用指令建立一個內容為"hello, git"的`welcome.html` ```git= $ echo "hello, git" > welcom.html ``` ![](https://i.imgur.com/7k3kuOO.png) ## `git add` 提交 > 把`welcome.html`這個檔案交給 Git,讓 Git 開始「追蹤」它,使用的指令是 `git add` 後面加上`檔案名稱`: > ```git= $ git add 'welcom.html' warning: LF will be replaced by CRLF in welcom.html. The file will have its original line endings in your working directory ``` :waning_crescent_moon: 這warning是因為在 Windows 中的換行符號為 CRLF,Git 會在你提交的時候自動將 CRLF 轉換成 LF,而在 checkout 時將 LF 轉換成 CRLF,[不建議關閉](https://shazi.info/git-%E5%9C%A8-windows-%E9%80%B2%E8%A1%8C-add-%E5%87%BA%E7%8F%BE%E8%AD%A6%E5%91%8A%E3%80%8Cwarning-lf-will-be-replaced-by-crlf%E3%80%8D/) > 再次檢視`git status` ```git= $ git status On branch master No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: welcom.html Untracked files: .... 省略 ``` :waning_crescent_moon: `welcom.html`的狀態從`Untracked` 變成 `new file`,表示這個檔案被放到==暫存區(Staging Area)==,等待稍後跟其它檔案一起被存到儲存庫裡 > 可以一次把多個檔案提交,例如 > ```git= $ git add *.py s710262101@ios7188-826 MINGW64 /e/我的雲端硬碟/drive/190614師兄打卡系統/code (master) $ git status On branch master No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: data_tosql.py new file: database_connection.py new file: pyqt5_main.py new file: sqlite_op.py new file: welcom.html ``` :::warning 一次提交目錄裡面所有檔案到暫存區 `$ git add --all` ::: :::info **【狀況題】如果在 git add 之後又修改了那個檔案的內容?** 想像一下這個情境: 你新增了一個檔案叫做 `abc.txt`。 然後,執行 `git add abc.txt` 把檔案加至暫存區。 接著編輯 `abc.txt` 檔案。 完成編輯後,接著你可能會想要進行 `Commit`,把剛剛修改的內容存下來。這是新手可能會犯的錯誤之一,以為 `Commit` 指令就會把所有的異動都存下來,事實上這樣的想法是不太正確的。執行一下 `git status` 指令,看一下目前的狀態: ```git= $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: abc.txt Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: abc.txt ``` 結果會發現 `abc.txt` 這個檔案變成 2 個了。因為在第 2 步的確有把 abc.txt 加到暫存區,但在第 3 步又編輯了那個檔案,對 Git 來說,編輯的內容並==沒有再次被加到暫存區==,所以這時候在暫存區裡的資料還是你在第 2 步時候加進來的那個檔案。 如果確定這個修改必須再次使用 `git add abc.txt` 指令,再把 `abc.txt` 加至暫存區。 ::: ## `git commit` 暫存區到倉庫(Git Repository) 要讓暫存區的內容永久的存下來的話,使用的是 `git commit` 指令: ```git= $ git commit -m "init commit" [master (root-commit) d2f4c62] init commit 5 files changed, 560 insertions(+) create mode 100644 data_tosql.py create mode 100644 database_connection.py create mode 100644 pyqt5_main.py create mode 100644 sqlite_op.py create mode 100644 welcom.html ``` :waning_crescent_moon: `-m "init commit"`是要說明這次commit所做的更動 :warning: 要完成 Commit 指令才算完成整個流程 ## 關於 `git commit`.. ### 什麼東西會被commit到Repository ==「Git 每次的 Commit 都*只*會處理暫存區(Staging Area)裡的內容」==。也就是說,如果在執行 `git commit` 指令的時候,那些還沒被加到暫存區裡的檔案,就不會被 Commit 到儲存庫裡。 ### commit的說明訊息 在 Commit 的時候,如果沒有輸入這個訊息,Git 預設是不會讓你完成 Commit 這件事的。它最主要的目的就是告訴你自己以及其它人「這次的修改做了什麼」。以下是幾點關於訊息的建議: 1. 英文、中文都沒關係,重點是要簡單、清楚。 2. 儘量不要使用像 bug fixed 這樣模糊的描述,因為沒人知道你修了什麼 bug。但如果有搭配其它的系統使用,則可使用 #34 bug fixed,因為這樣可以知道這次的 Commit 修正了第 34 號的 bug。 :warning: 只要加上 `--allow-empty` 參數,沒東西也可以 Commit (不建議) ```git= $ git commit --allow-empty -m "空的" [master 76a5b84] 空的 ``` # 工作區、暫存區與儲存庫 在 Git 裡,主要可以分成「工作目錄(Working Directory)」、「暫存區(Staging Area)」以及「儲存庫(Repository)」三個區塊,透過不同的 Git 指令,可以把檔案移往不同的區域: 1. `git add` 指令把檔案從工作目錄移至暫存區(或索引)。 2. `git commit` 指令把暫存區的內容移至儲存庫。 ![](https://i.imgur.com/YxTrTI2.png) ## 一段式commit法 在 Commit 的時候多加一個 `-a` 的參數,縮短這個流程: > 先修改`welcom.html`的內容 ```git= $ echo 'text here' >> welcom.html ``` ![](https://i.imgur.com/WAswh1b.png) > 一段式commit ```git= $ git commit -a -m 'update content' warning: LF will be replaced by CRLF in welcom.html. The file will have its original line endings in your working directory [master d219677] update content 1 file changed, 1 insertion(+) ``` :waning_gibbous_moon: 只有一個檔案有變更,因此只有`welcom.html`重新commit :::info ==為何需要二段式commit== 當然,你也可以每來一件貨物就開倉門存一次、紀錄一次,但這樣一來開倉庫的次數就會非常多。老實說這樣也沒什麼過錯,只是因為太過零碎的 Commit,有時候要查閱紀錄可能會有點不太方便;另外,過於零碎的 Commit 也可能給你的同事帶來一些困擾,例如要進行 Code Review 的時候,比較有整理的 Commit 可以一次看到比較完整的內容,而不需要一個一個 Commit 慢慢看。 ::: :::info ==何時commit== 這個問題沒有標準答案,你可以很多檔案修改好再一口氣全部一起 Commit,也可只改一個字就 Commit。常見的 Commit 的時間點有: 1. 完成一個「任務」的時候:不管是大到完成一整個電子商務的金流系統,還是小至只加了一個頁面甚至只是改幾個字,都算是「任務」。 2. 下班的時候:雖然可能還沒完全搞定任務,但至少先 Commit 今天的進度,除了備份之外,也讓公司知道你今天有在努力工作。(然後帶回家繼續苦命的做?) 3. 你想要 Commit 的時候就可以 Commit。 ::: ## vscode storage & commit > 這個檔案目前的狀態`U`表示尚未被追蹤 ![](https://i.imgur.com/OD06E9W.png) > 加入 storage ![](https://i.imgur.com/aJrfRBM.png) > 可以一次加入所有檔案到 Staging Area ![](https://i.imgur.com/eVHOfCZ.png) > 加入成功後的狀態變成下圖這樣,接著commit ![](https://i.imgur.com/jp2iDA6.png) <br/> <br/> #### Inline Blame Annotations 如果有裝 `GitLens`插件 commit後可以直接在檔案內該行,看到commit的時間跟commit的資訊 ![](https://i.imgur.com/Tp1pfrE.png) :waning_crescent_moon: 這功能非常方便,可以很快地找到這行code的始作用者,正確找到要幹譙的人 > 如果繼續修改檔案,左邊綠色的highlight表示這些內容還還沒有commit。右側也能看到文字說明 ![](https://i.imgur.com/gxA3uyd.png) > 儲存一下,檢視source control的內容 > ![](https://i.imgur.com/FODokqE.png) :waning_crescent_moon: `M`表示這個檔案被修改,但尚未commit > 第二次commit後回到script檢視修改歷史 ![](https://i.imgur.com/JY18ARX.png) ![](https://i.imgur.com/A13lbc3.png) :waning_crescent_moon: 可以很方便的檢視每次commit到底做了什麼 # 檢視紀錄 >先再進行一次commit比較熱鬧 > ```git= $ touch index.html $ git add "index.html" $ git commit -m "create index page" [master a100b63] create index page 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 index.html ``` ## `git log`檢視紀錄 新的資訊會先print出來 結果包含三個重要的資訊: 1. commit的作者 2. 什麼時候commit的 3. 做了什麼(說明) ```git= $ git log commit a100b631386426b6215be3d24f044324cf096679 (HEAD -> master) Author: Lee TsungTang <s710262101@gm.ntpu.edu.tw> Date: Thu Oct 24 16:23:20 2019 +0800 create index page commit d21967747b8d0966663a1c0e4e278a4f9cfca414 Author: Lee TsungTang <s710262101@gm.ntpu.edu.tw> Date: Thu Oct 24 16:11:45 2019 +0800 update content commit d2f4c6210bc50da8bcb0078828a13b3336e271a5 Author: Lee TsungTang <s710262101@gm.ntpu.edu.tw> Date: Thu Oct 24 15:43:56 2019 +0800 init commit ``` :waning_crescent_moon: commit 內的內容可以看做是該次commit的==UID== > 用`--oneline` 跟 `--graph`這兩個參數讓輸出的結果更精簡 ```git= $ git log --oneline --graph * a100b63 (HEAD -> master) create index page * d219677 update content * d2f4c62 init commit ``` ### 查看特定author的commit `--author` > `--author='author name'`查找特定的commit ```git= $ git log --oneline --author='Lee TsungTang' a100b63 (HEAD -> master) create index page d219677 update content d2f4c62 init commit $ git log --oneline --author='hhh' ``` :waning_crescent_moon: 由於沒有其他作者,因此隨便寫一個不存在的author測試。結果不會返回任何commit > 可以再用 `|` 來查詢不同人的 Commit 紀錄」: 例如 ```git= $ git log --oneline --author="Sherly\|Eddie" ``` ### 查找特定的commit說明訊息的內容 `--grep` > 查找有"update"的commit ```git= $ git log --oneline --grep='update' d219677 update content ``` <!-- ### 查找特定commit檔案內的文字 `-S` --> ### 查找特定時間的commit `--since` `--until` `--after` ```git= $ git log --oneline --since='16:30pm' --until='17pm' 4fc4856 (HEAD -> master) add text in index page ``` ## VScode git log ### 查看整體紀錄 先安裝`Git history`插件 > vscode的Command Palette `ctrl` + `shift` + `p` > 輸入git找到下面這個,然後點進去 ![](https://i.imgur.com/5T2tUMM.png) > 進入histroy頁面,這個頁面可以檢視每次commit的內容、分支、作者......所有你想知道關於commit的資訊 ![](https://i.imgur.com/1oWKPn9.png) > 例如檢視某次commit狀態下某個檔案的內容 ![](https://i.imgur.com/dMqnngf.png) ### 檢視特定檔案的變更歷程 |方法1|方法2| |-|-| |![](https://i.imgur.com/0qe2XJv.png =60%x)|![](https://i.imgur.com/lxMuDN0.png )| > 檢視檔案每一次commit做了什麼更動 ![](https://i.imgur.com/NoOl3lV.png) # 在Git裡刪除檔案或變更檔名 在 Git 裡,不管是刪除檔案或是變更檔名,對 Git 來說都是一種「修改」。 ## 刪除檔案 `rm` > 嘗試刪除'welcom.html' ```git= $ rm welcom.html s710262101@ios7188-826 MINGW64 /e/我的雲端硬碟/drive/190614師兄打卡系統/code (master) $ git status On branch master Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) deleted: welcom.html ``` :waning_crescent_moon: 'welcom.html'的狀態變為`deleted` > 如果確定此次修改則加到暫存區 > ```git= $ git add welcom.html $ git status On branch master Changes to be committed: (use "git restore --staged <file>..." to unstage) deleted: welcom.html ``` > 接著commit > ```git= $ git commit -m 'delete welcom.html' [master 54fd25b] delete welcom.html 1 file changed, 4 deletions(-) delete mode 100644 welcom.html ``` :warning: 在git追蹤的狀況下「刪除檔案」也當做是一種「修改」看待就行了。 ## 用 Git 刪除檔案 `git rm` `git rm` = `rm` + `git add` ```git= $ git rm welcome.html rm 'welcome.html' ``` :waning_crescent_moon: 只要再commit就可以完成了 ### `–cached` 參數 `--cached`參數不會直接刪掉檔案,而是能讓檔案不被git控管 :watch: 下面這段沒有真的跑 ```git= $ git rm welcome.html --cached rm 'welcome.html' ``` > welcome.html 的狀態從原本已經在 Git 目錄裡的 tracked 變成 Untracked 了。 ```git= $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) deleted: welcome.html Untracked files: (use "git add <file>..." to include in what will be committed) welcome.html ``` ### `git checkout`救回被刪除的檔案 ```git= # 準備檔案 $ echo 'text here' >> 'index.html' $ git add 'index.html' $ git commit -m 'modified index.html for test' ``` > 刪除`index.html` ```git= $ rm index.html $ ls __pycache__/ data_tosql.py desktop.ini layout_test.py pyqt5.spec pyqt5_main.spec test.db build/ database_connection.py dist/ punchSystem-7c27e17aa7de.json pyqt5_main.py sqlite_op.py TEST.html ``` > 確認`git status` ```git= $ git status On branch master Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) deleted: index.html ... ``` :waning_crescent_moon: `index.html`處於deleted狀態 > `git checkout`救回檔案 ```git= $ git checkout index.html Updated 1 path from the index $ ls -al ... -rw-r--r-- 0 s710262101 1049089 5 十一月 14 16:41 index.html ... ``` :waning_gibbous_moon: 成功 > 理論上不要刪掉git 目錄都能夠把檔案救回來 ## 變更檔名 ### 直接改名 ```git= $ mv hello.html world.html # 把 hello.html 改成 world.html ``` ## 修改 Commit 紀錄 要修改 Commit 紀錄有好幾種方法: 1. 把 `.git` 目錄整個刪除(no!)。 2. 使用 `git rebase` 來修改歷史。 3. 先把 Commit 用 `git reset` 拆掉,整理後再重新 Commit。 4. :hash: 使用 `--amend` 參數來修改最後一次的 Commit。 :waning_crescent_moon: 此處介紹第4種 ```git= $ git log --oneline 54fd25b (HEAD -> master) delete welcom.html 4fc4856 add text in index page a100b63 create index page d219677 update content d2f4c62 init commit ``` >`--amend`參數修正最新一次commit的訊息 ```git= $ git commit --amend -m "delete welcom.html2" [master b872268] delete welcom.html2 Date: Thu Oct 24 17:41:44 2019 +0800 1 file changed, 4 deletions(-) delete mode 100644 welcom.html ``` > 成功改為 "delete welcom.html2" ```git= $ git log --oneline b872268 (HEAD -> master) delete welcom.html2 4fc4856 add text in index page a100b63 create index page d219677 update content d2f4c62 init commit ``` :::warning 雖然只是改紀錄的訊息,但對 Git 來說因為「Commit 的內容」改變了,所以 Git 會重新計算並產生一顆新的 `Commit` 物件,也就是這其實是一次==全新的 Commit==(只是看起來不像新的)。以上面這個例子為例,修改前的 Commit 物件的 SHA-1 值是 54fd25b,但改完訊息之後 SHA-1 值變成 b872268。雖然 Commit 的時間跟檔案的內容看起來並沒有被修改,但它仍是一次全新的 Commit。 ::: :warning: 通常會避免在`push`後修改 ## 追加commit 檔案 1. 使用 git reset 把最後一次的 Commit 拆掉,加入新檔案後再重新 Commit。 2. 使用 --amend 參數進行 Commit。 ```git= $ git add cinderella.html # 新檔案 $ git commit --amend --no-edit ``` :waning_crescent_moon: `--no-edit` 是不編輯commit訊息 ## 檢視特定檔案的 Commit 紀錄 `git log` + 檔名檢視單一檔案的紀錄 ```git= $ git log pyqt5_main.py commit 9f0f8ac26249edef2fccc9ae221a95281f13a032 (HEAD -> master) Author: Lee TsungTang <s710262101@gm.ntpu.edu.tw> Date: Thu Nov 14 14:32:14 2019 +0800 dynamic add punch widget commit 0e8612ec0a45320b0503e01eea75582ac3ceb5c6 Author: Lee TsungTang <s710262101@gm.ntpu.edu.tw> Date: Wed Nov 6 21:04:07 2019 +0800 補打卡widgets完成 commit d2f4c6210bc50da8bcb0078828a13b3336e271a5 Author: Lee TsungTang <s710262101@gm.ntpu.edu.tw> Date: Thu Oct 24 15:43:56 2019 +0800 init commit ``` > `-p` 參數可以看每次做了什麼修改 > ## 退回到哪次commit前的結果 `git reset` > 先弄一個新的檔案,並commit。等等準備拆掉 ```git= $ echo "test, git" > TEST.html $ git add "TEST.html" $ git status On branch master Changes to be committed: (use "git restore --staged <file>..." to unstage) new file: TEST.html ... $ git commit -m "準備被拆掉的test檔" [master 7c6139d] 準備被拆掉的test檔 1 file changed, 1 insertion(+) create mode 100644 TEST.html ``` > 檢視一下紀錄 ```git= git log commit 7c6139d881c39b31af73bc4a8186c0e77ccdcbb5 (HEAD -> master) Author: Lee TsungTang <s710262101@gm.ntpu.edu.tw> Date: Thu Nov 14 15:38:40 2019 +0800 準備被拆掉的test檔 ... ``` ### 相對位置 ```git= $ git reset 7c6139d^ # not run ``` :waning_crescent_moon: `^`表示要拆掉`7c6139d`前一次commit,如果要再往前拆可以用`^^`(往前2個commit),`7c6139d~5`(往前5個commit) > 因為目前的HEAD與master都指向`7c6139d`這個commit,因此上面這行也可以寫為 > ```git= $ git reset master^ # not run $ git reset HEAD^ # not run ``` ### 絕對位置 如果知道要退回哪次commit的狀態,可以直接指定: ```git= $ git reset 9f0f8ac # 檢視一下結果 $ git log --oneline 9f0f8ac (HEAD -> master) dynamic add punch widget 0e8612e 補打卡widgets完成 b872268 delete welcom.html2 4fc4856 add text in index page a100b63 create index page d219677 update content d2f4c62 init commit ``` :waning_crescent_moon: 成功退回前一次的commit ## reset的三種模式 - `--mixed`、`--soft` 以及 `--hard` > 預設為`--mixed` > 模式 |mixed 模式 |soft 模式 |hard 模式 |--|--|--|--| 工作目錄|不變|不變|丟掉 暫存區|丟掉|不變|丟掉 Commit 拆出來的檔案|丟回工作目錄|丟回暫存區|直接丟掉 :waning_crescent_moon: 因此預設的`--mixed`操作後,`TEST.html`檔案其是還在資料夾的目錄區內 事實上git拆掉的commit 以及丟掉的檔案也可以救回來(即使是`--hard`模式),因為`git reset`在git的概念中比較像往前回到某個狀態,所以當然也可以再往後回到某個狀態 # 分支 一個人開發時一直commit沒有太大問題,但當多人在同一個專案工作的時候,可能就不能這麼隨興的想 Commit 就 Commit,這時候分支就很好用。例如想要增加新功能,或是修正 Bug,或是想實驗看看某些新的做法,都可以另外做一個分支來進行,待做完確認沒問題之後再合併回來,不會影響正在運行的產品線。 在多人團隊共同開發的時候,甚至也可引入像 Git Flow / GitHub Flow / GitLab Flow 之類的開發流程,讓同一個團隊的人都可以用相同的方式進行開發,減少不必要的溝通成本。 ## `git branch` 檢視當前分支 > `git branch` 後面沒接任何參數,會檢視目前在哪一個分支 ```git= git branch * master ``` :waning_gibbous_moon: 前面的星號 ==*== 表示現在正在這個分支上。 ## `git branch <branch name>` 新增分支 > git branch後加上文字創造新分支 cat ```git= git branch cat git branch cat * master ``` :waning_crescent_moon: master為當前分支 ## `git branch -m <original name> <new name>` 分支換名稱 > cat rename為 tiger ```git= git branch -m cat tiger git branch tiger dog * master ``` ### vscoed 開啟及切換分支 > 左下角點選==master== (master表示現在的分支) ![](https://i.imgur.com/GhEhym0.png) > 選擇 Create new branch from 再選擇master ![](https://i.imgur.com/7P2o7mG.png) :waning_crescent_moon: Create new branch會以當前分支創建新分支 > 輸入新branch name ![](https://i.imgur.com/0xxSt8q.png) > 創建成功 > ![](https://i.imgur.com/aLmrF6R.png) ## `git branch -d <branch name>` 刪除分支 ```git= $ git branch -d dog Deleted branch dog (was e12d8ef). $ git branch cat * master ``` :::success 如果要刪的分支還沒被完全合併,Git 會有貼心小提示: error: The branch 'dog' is not fully merged. If you are sure you want to delete it, run 'git branch -D cat'. 如果要強制刪除可以改用`-D參數` ::: ## `git checkout` 切換分支 ```git= git checkout cat Switched to branch 'cat' git branch * cat dog master ``` ### Vscode切換分支 > 可以點選cat branch ![](https://i.imgur.com/Y74oW8z.png) > 選擇要切換的分支 > ![](https://i.imgur.com/jUYsUwd.png) ## 在分支中commit ```git= $ git checkout tiger Switched to branch 'tiger' $ touch cat1.html $ git add cat1.html $ git commit -m "add cat 1" [tiger 9419c7d] add cat 1 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 cat1.html $ touch cat2.html $ git add cat2.html $ git commit -m "add cat 2" [tiger ceb6856] add cat 2 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 cat2.html $ git log --oneline ceb6856 (HEAD -> tiger) add cat 2 9419c7d add cat 1 60e3c29 (master) secound commit 7694dc1 first commit ``` :waning_crescent_moon: 分支commit的進度比master快了兩次 > 檢視此時的檔案 ```git= $ ls -al total 2 -rw-r--r-- 0 s710262101 1049089 0 十二月 14 13:24 cat1.html -rw-r--r-- 0 s710262101 1049089 0 十二月 14 13:46 cat2.html -rw-r--r-- 0 s710262101 1049089 32 十二月 13 11:02 welcom.html ``` :waning_gibbous_moon: 多了`cat1.html` `cat2.html` > 切回master檢視 ```git= $ git checkout master Switched to branch 'master' $ ls -al total 2 -rw-r--r-- 0 s710262101 1049089 32 十二月 13 11:02 welcom.html ``` :warning: master裡面不會有剛剛在分支中新增的檔案,除非切回分支 ### Vscode中檢視分支 > `ctrl` + `shift` + `p` 開啟command palette,搜尋git history ![](https://i.imgur.com/864yrQL.png) > 在`master` 分支中只會看到此分支中的commit > ![](https://i.imgur.com/IZRVFbl.png) > 如果切換到`tiger`分支則能看到前面的commit歷程(包括master) ![](https://i.imgur.com/zgO7EGc.png) #### view git graph 如果有裝了Git Graph 插件,能提供另一種視覺化的git log ![](https://i.imgur.com/InxNa89.png) ![](https://i.imgur.com/DcBPRBo.png) > 同樣可以檢索每次commit的資訊與檔案 > ![](https://i.imgur.com/Ot22eNU.png) :waning_crescent_moon: Git Graph 支援即時更新,所以有時候滿方便的 ## `git merge <branch name>`合併分支 > master 分支來合併 tiger 分支,先切回 master 分支: ```git= $ git checkout master Switched to branch 'master' $ git merge tiger Updating 60e3c29..ceb6856 Fast-forward cat1.html | 0 cat2.html | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 cat1.html create mode 100644 cat2.html ``` > 檢視 master裡面的檔案 ```git= $ ls -al total 2 -rw-r--r-- 0 s710262101 1049089 0 十二月 14 15:07 cat1.html -rw-r--r-- 0 s710262101 1049089 0 十二月 14 15:07 cat2.html -rw-r--r-- 0 s710262101 1049089 32 十二月 13 11:02 welcom.html ``` :waning_crescent_moon: `cat1.html` `cat2.html` 出現在master裡面 ### vscode 中 merge branch > 一樣在command palette 裡面搜尋 git merge ![](https://i.imgur.com/IqCQqWY.png) ![](https://i.imgur.com/zTPo74T.png) > 合併完成後檢視 git graph > ![](https://i.imgur.com/Mf1kdZq.png) :waning_crescent_moon: 本來落後 2 個 Commit 的 master 分支,在進行合併之後,進度已經跟上 tiger 分支,跟它在同一個 Commit 上 > 理論上這時候兩個分支已經沒有分別了,可以刪除舊有的分支 ```git= $ git branch -d tiger Deleted branch tiger (was ceb6856). ``` ```git= git log -p pyqt5_main.py commit 9f0f8ac26249edef2fccc9ae221a95281f13a032 (HEAD -> master) Author: Lee TsungTang <s710262101@gm.ntpu.edu.tw> Date: Thu Nov 14 14:32:14 2019 +0800 dynamic add punch widget diff --git a/pyqt5_main.py b/pyqt5_main.py index 86aadc4..e14d4ab 100644 --- a/pyqt5_main.py +++ b/pyqt5_main.py @@ -13,7 +13,7 @@ from sqlite_op import * class App(QMainWindow): choice = '米開朗基羅廳' - choice_list = ['蘇格拉底廳', '洛克廳', '米開朗基羅廳', '國際會議廳B','柏拉圖廳','國際會議廳A','亞歷山大廳', '補打卡'] + choice_list = ['蘇格拉底廳', '洛克廳', '國際會議廳B','柏拉圖廳','國際會議廳A','亞歷山大廳', '補打卡'] .. 太長省略 ``` # Github ## git push 上傳專案到github > 先開一個新的repository >![](https://i.imgur.com/Tp8gPG1.png) ![](https://i.imgur.com/WDYUgEM.png) > setup 頁面 ![](https://i.imgur.com/qyTo8yl.png) ### 說明: 1. 如果是全新開始,請依「create a new repository on the command line」的內容指示進行;如果是要上傳現存專案,則依照「push an existing repository from the command line」選項進行。 2. 畫面中間上面,有兩個按鈕可以切換,分別是「HTTPS」以及「SSH」。至於要選擇哪一個可看個人喜好,但選擇 SSH 的話需要設定 SSH Key。 <br> > 要推上遠端git伺服器需要先設定端點的節點,例如 ```git= git remote add origin git@github.com:kaochenlong/practice-git.git ``` > - `git remote` 指令主要是跟遠端有關的操作。 - add 指令是指要加入一個遠端的節點。 - 這裡的 origin 是一個「代名詞」,指的是後面那串 GitHub 伺服器的位置。(可以自行變更) > push ```git= $ git push -u origin master Counting objects: 3, done. Writing objects: 100% (3/3), 228 bytes | 228.00 KiB/s, done. Total 3 (delta 0), reused 0 (delta 0) To github.com:kaochenlong/practice-git.git * [new branch] master -> master Branch master set up to track remote branch master from origin. ``` > -u 後的兩個參數分別為 <遠端位置><本地分支> > ![](https://i.imgur.com/Xef6vnG.png) > success # 參考資料 [Git book](https://zlargon.gitbooks.io/git-tutorial/content/) [為自己學Git](https://gitbook.tw/chapters/command-line/command-line.html) [Pro Git](https://git-scm.com/book/zh-tw/v2) [30 天精通 Git 版本控管](https://github.com/doggy8088/Learn-Git-in-30-days/blob/master/zh-tw/README.md)