├── Git-Flow ├── README.md └── README_en.md ├── LICENSE ├── Multiple_SSH_Keys_settings.md ├── Multiple_SSH_Keys_settings_en.md ├── README.md ├── README_en.md ├── git_submodule_turorial.md ├── git_submodule_turorial_en.md ├── git_subtree_turorial.md ├── git_subtree_turorial_en.md └── pr-tutorial ├── README.md └── README_en.md /Git-Flow/README.md: -------------------------------------------------------------------------------- 1 | [English Version](README_en.md) 2 | 3 | # Git-Flow 基本教學 :memo: 4 | 5 | [Git-Flow Tutorials - youtube](https://youtu.be/zXlta66thZY) 6 | 7 | [Git-Flow SmartGit Tutorials - youtube](https://youtu.be/ualXHytifbg) 8 | 9 | 在開始要了解 Git-Flow 之前,建議大家先對 Git 有基本的認識, 10 | 11 | 可以參考我之前寫的 [Git-Tutorials GIT基本使用教學](https://github.com/twtrubiks/Git-Tutorials) 12 | 13 | ## 什麼是 Git-Flow 14 | 15 | 在 Git 中有很多指令, 如果你使用 Git-Flow ,會多一些額外的功 16 | 17 | 能,但這先功能都只是由基本功能下去組合出來的,講簡單一點 18 | 19 | ,就是 A = B + C + D 的概念 ,當然,也不用擔心需要額外記指令 20 | 21 | ,有 GUI 介面 ( 有很多套可以使用,我是使用 [SmartGit](http://www.syntevo.com/smartgit/) ) ,而且很方便。 22 | 23 | ## 為什麼要使用 Git-Flow 24 | 25 | 當你一個人在用 Git 的時候一定很爽,你想怎麼 commit 或 26 | 27 | 者怎麼 merge 甚至在哪個分支下開發你都可以自己決定,但 28 | 29 | 如果是多人共同開發呢 ? 每個人都有自己的習慣,這樣豈不 30 | 31 | 是天下大亂,於是 Git-Flow 就產生了。 32 | 33 | ## Git-Flow 並不是一個新技術,他只是一個規範 34 | 35 | 很多人第一次看到 Git-Flow 都會想,哇!這個什麼東西阿,好潮 36 | 37 | 哦之類的,但他只是 **一種規範** ,並不是一種新技術,當一整個 38 | 39 | 團隊都遵守某一種工作流程,比較不會發生問題。 40 | 41 | Git-Flow 是訂出來的一種規範,如果你或公司對 Git 非常了解, 42 | 43 | 你們也可以自己定義屬於你們自己的 Flow ,不要遵守 Git-Flow , 44 | 45 | 是完全不會有衝突的。 46 | 47 | ## Git-Flow Model 48 | 49 | 在 Git-Flow 中,有兩個主要的分支: 50 | 51 | ***Master*** 52 | 53 | 這個分支是非常穩定的,任何時刻都是 production-ready ,包含最新的 release ,也就是產品的原始碼,當任何一個人使用你的產品,第一眼看到的都是 master,**我們不能直接在 master 分支上進行任何的 commit ,只能從 Release 或 Hotfix merge 回來**。 54 | 55 | ***Develop*** 56 | 57 | 開發中的分支,develop 分支是由 mater 分支分出來的,該 ( develop ) 分支提供整合不同即將 release 的 feature ( 功能 ) ,所以該分支有可能不穩定 ( 有bug ) 。通常我們也不會直接在該分支下 commit,而是透過 merge 的方式將 feature ( 功能 ) merge 進來。 58 | 59 | Master 以及 Develop 這兩個分支非常重要,理論上要保護好這兩個分支,避免被意外刪除。 60 | 61 | 除了 **Master** 以及 **Develop** 這兩個分支之外,Git-Flow 還提供其他的分支: 62 | 63 | ***Feature*** 64 | 65 | 功能開發,該分支是由 develop 分支分出來的,最後會被合併回 develop 分支。舉個例子,假設目前有 A 和 B 兩個功能經由兩個人下去開發,這兩個人會分別從 develop 分支分 A 和 B 兩個分支出來,當完成後,再 merge 回 develop 。 66 | 67 | ***Release*** 68 | 69 | 當我們認為 develop 已經是一個很穩定的版本時,我們會進入 release,該分支是由 develop 分支分出來的。通常我們會在該分支底下再做一次全面的測試以及上線前的準備 ( 發佈版本的記錄 ),完成 release 後,我們會 merge 回 master 以及 develop。 70 | 71 | ***Hotfix*** 72 | 73 | 該分支是由 master 分支分出來的,通常是已經上線了,但突然發現一個非常緊急的 bug ,這時候我們就會開一個 Hotfix 出來修復該 bug ,完成後我們會再 merge 回 master 以及 develop。 74 | 75 | ## 使用 SmartGit 來完成 Git-Flow 76 | 77 | 類似的 GUI 介面有很多,大家可以找適合自己的使用,我在這邊用 [SmartGit](http://www.syntevo.com/smartgit/) 介紹。 78 | 79 | ### 安裝 SmartGit 80 | 81 | 請到 [SmartGit](http://www.syntevo.com/smartgit/) 下載符合自己的作業系統, 82 | 83 | 安裝基本上不會有甚麼問題,比較需要注意的地方是如果你是非商用版, 84 | 85 | 請選擇非商用 ( 如下圖 ) ,假如是商用版,請購買 License 86 | 87 | ![](http://i.imgur.com/Qo6iy0l.jpg) 88 | 89 | 設定一些東西即可完成 90 | 91 | ![](http://i.imgur.com/thRWcvv.jpg) 92 | 93 | 進到 SmartGit 後,請先選好專案,接著我們按 Git-Flow 的圖示 ( 如下圖 ) 94 | 95 | ![](http://i.imgur.com/MbJPEIF.jpg) 96 | 97 | 通常我們 Git-Flow Type 會選 **Full** , 也就是完整的分支 98 | 99 | ![](http://i.imgur.com/xnUn9vS.jpg) 100 | 101 | 你會發現左下角的兩個主要分支跑出來了 102 | 103 | ![](http://i.imgur.com/XfWWByZ.jpg) 104 | 105 | 如要新增任何的 Feature 或 Release ,可以點選 Git-Flow 的圖示 106 | 107 | ![](http://i.imgur.com/0147G33.jpg) 108 | 109 | 更多詳細的介紹可參考 110 | 111 | [Git-Flow SmartGit Tutorials - youtube](https://youtu.be/ualXHytifbg) 112 | 113 | ## Reference 114 | 115 | * [gitflow](https://github.com/nvie/gitflow) 116 | 117 | ## Donation 118 | 119 | 文章都是我自己研究內化後原創,如果有幫助到您,也想鼓勵我的話,歡迎請我喝一杯咖啡 :laughing: 120 | 121 | ![alt tag](https://i.imgur.com/LRct9xa.png) 122 | 123 | [贊助者付款](https://payment.opay.tw/Broadcaster/Donate/9E47FDEF85ABE383A0F5FC6A218606F8) 124 | 125 | ## License 126 | 127 | MIT license 128 | -------------------------------------------------------------------------------- /Git-Flow/README_en.md: -------------------------------------------------------------------------------- 1 | # Git-Flow Basic Tutorial :memo: 2 | 3 | [English Version](README_en.md) 4 | 5 | [Git-Flow Tutorials - youtube](https://youtu.be/zXlta66thZY) 6 | 7 | [Git-Flow SmartGit Tutorials - youtube](https://youtu.be/ualXHytifbg) 8 | 9 | Before you start to understand Git-Flow, it is recommended that you have a basic understanding of Git. 10 | 11 | You can refer to the [Git-Tutorials GIT Basic Usage Tutorial](https://github.com/twtrubiks/Git-Tutorials) I wrote before. 12 | 13 | ## What is Git-Flow 14 | 15 | There are many commands in Git. If you use Git-Flow, you will have some extra features. 16 | 17 | But these features are just combinations of basic functions. To put it simply, 18 | 19 | it's the concept of A = B + C + D. Of course, you don't have to worry about memorizing extra commands. 20 | 21 | There is a GUI interface (there are many sets to use, I use [SmartGit](http://www.syntevo.com/smartgit/)), and it is very convenient. 22 | 23 | ## Why use Git-Flow 24 | 25 | When you are using Git by yourself, it must be very cool. You can decide how to commit or 26 | 27 | how to merge, or even which branch to develop on. But 28 | 29 | what if it's a multi-person collaborative development? Everyone has their own habits, wouldn't that 30 | 31 | be chaos? So Git-Flow was born. 32 | 33 | ## Git-Flow is not a new technology, it is just a specification 34 | 35 | Many people, when they first see Git-Flow, will think, wow! What is this thing? It's so trendy. 36 | 37 | But it is just **a specification**, not a new technology. When a whole 38 | 39 | team follows a certain workflow, problems are less likely to occur. 40 | 41 | Git-Flow is a set of specifications. If you or your company are very familiar with Git, 42 | 43 | you can also define your own Flow. Not following Git-Flow 44 | 45 | will not cause any conflicts at all. 46 | 47 | ## Git-Flow Model 48 | 49 | In Git-Flow, there are two main branches: 50 | 51 | ***Master*** 52 | 53 | This branch is very stable and is production-ready at all times. It contains the latest release, which is the source code of the product. When anyone uses your product, the first thing they see is master. **We cannot make any commits directly on the master branch, we can only merge back from Release or Hotfix**. 54 | 55 | ***Develop*** 56 | 57 | The development branch. The develop branch is branched from the master branch. This (develop) branch provides integration of different upcoming release features. So this branch may be unstable (have bugs). Usually, we don't commit directly on this branch either, but merge features in through merging. 58 | 59 | Master and Develop are two very important branches. In theory, these two branches should be well protected to avoid accidental deletion. 60 | 61 | In addition to the **Master** and **Develop** branches, Git-Flow also provides other branches: 62 | 63 | ***Feature*** 64 | 65 | Feature development. This branch is branched from the develop branch and will eventually be merged back into the develop branch. For example, suppose there are two features, A and B, being developed by two people. These two people will respectively branch out two branches, A and B, from the develop branch. When they are finished, they will merge back to develop. 66 | 67 | ***Release*** 68 | 69 | When we think that develop is already a very stable version, we will enter the release. This branch is branched from the develop branch. Usually, we will do a full test and pre-launch preparation (release version record) on this branch. After completing the release, we will merge back to master and develop. 70 | 71 | ***Hotfix*** 72 | 73 | This branch is branched from the master branch. Usually, it is already online, but suddenly a very urgent bug is found. At this time, we will open a Hotfix to fix the bug. After completion, we will merge back to master and develop. 74 | 75 | ## Using SmartGit to complete Git-Flow 76 | 77 | There are many similar GUI interfaces. You can find one that suits you. I will use [SmartGit](http://www.syntevo.com/smartgit/) for the introduction here. 78 | 79 | ### Installing SmartGit 80 | 81 | Please go to [SmartGit](http://www.syntevo.com/smartgit/) to download the one that matches your operating system. 82 | 83 | There should be no problems with the installation. The place to pay more attention to is that if you are a non-commercial version, 84 | 85 | please choose non-commercial (as shown below). If it is a commercial version, please purchase a license. 86 | 87 | ![](http://i.imgur.com/Qo6iy0l.jpg) 88 | 89 | Set some things to complete. 90 | 91 | ![](http://i.imgur.com/thRWcvv.jpg) 92 | 93 | After entering SmartGit, please select the project first, and then we click the Git-Flow icon (as shown below). 94 | 95 | ![](http://i.imgur.com/MbJPEIF.jpg) 96 | 97 | Usually, we will choose **Full** for Git-Flow Type, which is the complete branch. 98 | 99 | ![](http://i.imgur.com/xnUn9vS.jpg) 100 | 101 | You will find that the two main branches in the lower left corner have appeared. 102 | 103 | ![](http://i.imgur.com/XfWWByZ.jpg) 104 | 105 | To add any Feature or Release, you can click the Git-Flow icon. 106 | 107 | ![](http://i.imgur.com/0147G33.jpg) 108 | 109 | For more detailed introduction, please refer to 110 | 111 | [Git-Flow SmartGit Tutorials - youtube](https://youtu.be/ualXHytifbg) 112 | 113 | ## Reference 114 | 115 | * [gitflow](https://github.com/nvie/gitflow) 116 | 117 | ## Donation 118 | 119 | The articles are all original after my own research and internalization. If it has helped you and you want to encourage me, you are welcome to buy me a cup of coffee :laughing: 120 | 121 | 122 | 123 | [Sponsor Payment](https://payment.opay.tw/Broadcaster/Donate/9E47FDEF85ABE383A0F5FC6A218606F8) 124 | 125 | ## License 126 | 127 | MIT license 128 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Multiple_SSH_Keys_settings.md: -------------------------------------------------------------------------------- 1 | [English Version](Multiple_SSH_Keys_settings_en.md) 2 | 3 | # Multiple SSH Keys settings for different github account 4 | 5 | 如果你對這個真的很陌生,建議看影片 :smile: 6 | 7 | * [Youtube Tutorial - Multiple SSH Keys settings for different github account](https://youtu.be/gDxG-4tF7B8) 8 | 9 | ## 前言 10 | 11 | 有時候我們可能會遇到這種情境,就是同一台電腦上同時需要有多個 github 帳號,為什麼呢? 12 | 13 | 當你工作的電腦和你自己的 side project 開發都在同一台電腦的時候,你就需要這篇文章的幫忙 14 | 15 | 了 :kissing_smiling_eyes: 16 | 17 | 相信我,你不會希望用公司的帳號推 commit 到自己的 side project 上 :sweat_smile: 18 | 19 | ## 教學 20 | 21 | 第一步,先產生 ssh key,如果你不知道怎麼產生, 22 | 23 | 可參考 [Generating a new SSH key](https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/) 或是 [github基本教學 - 從無到有](https://www.youtube.com/watch?v=py3n6gF5Y00), 24 | 25 | 打開你的 Git Bash, 26 | 27 | ```cmd 28 | ssh-keygen -t rsa -b 4096 -C "blue555236sa56@gmail.com" 29 | ``` 30 | 31 | ( 請將信箱換成自己的 ) 32 | 33 | 這裡的重點是記得自行輸入新的 rsa 名稱,像是下圖的 `/c/Users/twtrubiks/.ssh/id_rsa_rubik` 這樣, 34 | 35 | 這樣才不會覆蓋舊有的 rsa, 36 | 37 | ![alt tag](https://i.imgur.com/2Msr51U.png) 38 | 39 | 接下來,你的 `.ssh` 路徑底下會多出你剛剛建立的檔案 40 | 41 | ![alt tag](https://i.imgur.com/B4g9FQO.png) 42 | 43 | 再把你的 `id_rsa_rubik.pub` key 貼到你的 gihub 上, 44 | 45 | ![alt tag](https://i.imgur.com/tfQzFcJ.png) 46 | 47 | 通常你新增成功的時候,信箱會收到一封信。 48 | 49 | 再輸入下方指令 50 | 51 | ```cmd 52 | ssh-agent -s 53 | ``` 54 | 55 | 上面這個指令一定要執行,如果跳過這個步驟,就會出現以下錯誤, 56 | 57 | Could not open a connection to your authentication agent. 58 | 59 | 所以請記得一定要執行。 60 | 61 | 在 `.ssh` 目錄底下建立一個名稱為 `config` 的檔案 ( 檔名就是 `config`,副檔名不用填 ), 62 | 63 | ![alt tag](https://i.imgur.com/SJBxro6.png) 64 | 65 | 下方是一個範例, 66 | 67 | ```config 68 | # github - twtrubiks 69 | Host github.com 70 | HostName github.com 71 | IdentityFile ~/.ssh/id_rsa 72 | IdentitiesOnly yes 73 | User twtrubiks 74 | 75 | # github - blue-rubiks 76 | Host blue.github.com 77 | HostName github.com 78 | IdentityFile ~/.ssh/id_rsa_rubik 79 | IdentitiesOnly yes 80 | User blue-rubiks 81 | ``` 82 | 83 | 可用以下指令測試,如果顯示出自己的使用者名稱,代表設定成功 84 | 85 | ```cmd 86 | ssh -T git@github.com 87 | ``` 88 | 89 | ![alt tag](https://i.imgur.com/rdLf4iX.png) 90 | 91 | ```cmd 92 | ssh -T git@blue.github.com 93 | ``` 94 | 95 | ![alt tag](https://i.imgur.com/YTNHPfN.png) 96 | 97 | ya :heart_eyes: 我們設定成功惹~ 98 | 99 | 如果有問題,也可以用 debug 模式看問題出在哪裡,通常是 `config` 輸入有誤 100 | 101 | ```cmd 102 | ssh -vT git@github.com 103 | ``` 104 | 105 | 接下來先和大家說明一下, 106 | 107 | ```cmd 108 | git config --global user.name "blue-rubiks" 109 | git config --global user.email "blue555236sa56@gmail.com" 110 | ``` 111 | 112 | `--global` 顧名思義就是全域的意思,如果沒輸入,就代表是目錄底下的 repo 會生效, 113 | 114 | 現在我們 clone 一個 repo 來玩玩看 (你可以自己建立一個) 115 | 116 | ```cmd 117 | git clone git@github.com:blue-rubiks/github-ssh-test.git 118 | ``` 119 | 120 | 接著,切換到目錄資料夾底下,輸入以下指令 121 | 122 | ```cmd 123 | git config user.name "blue-rubiks" 124 | git config user.email "blue555236sa56@gmail.com" 125 | ``` 126 | 127 | 注意,這邊沒加上 `--global`,所以只有該目錄底下的 repo 會生效, 128 | 129 | 我們找到 repo 中的 `.git` 資料夾 , 再找到 `config` 檔案,內容大致如下 130 | 131 | ![alt tag](https://i.imgur.com/vT6GiYR.png) 132 | 133 | 主要就是修改第 9 行成第 10 行,修改為你設定的 `Host`, 134 | 135 | 最後,可以安心 commit 以及 push 了。 136 | 137 | 如果你真的聽不懂我在說什麼,建議看影片說明,我會帶大家操作一波 :laughing: 138 | 139 | ## 執行環境 140 | 141 | * Win 10 142 | 143 | ## Reference 144 | 145 | * [Multiple SSH Keys settings for different github account](https://gist.github.com/jexchan/2351996) 146 | 147 | ## Donation 148 | 149 | 文章都是我自己研究內化後原創,如果有幫助到您,也想鼓勵我的話,歡迎請我喝一杯咖啡 :laughing: 150 | 151 | ![alt tag](https://i.imgur.com/LRct9xa.png) 152 | 153 | [贊助者付款](https://payment.opay.tw/Broadcaster/Donate/9E47FDEF85ABE383A0F5FC6A218606F8) 154 | 155 | ## License 156 | 157 | MIT licens 158 | -------------------------------------------------------------------------------- /Multiple_SSH_Keys_settings_en.md: -------------------------------------------------------------------------------- 1 | # Multiple SSH Keys settings for different github account 2 | 3 | [English Version](Multiple_SSH_Keys_settings_en.md) 4 | 5 | If you are really unfamiliar with this, I suggest watching the video :smile: 6 | 7 | * [Youtube Tutorial - Multiple SSH Keys settings for different github account](https://youtu.be/gDxG-4tF7B8) 8 | 9 | ## Foreword 10 | 11 | Sometimes we may encounter a situation where we need to have multiple GitHub accounts on the same computer. Why? 12 | 13 | When your work computer and your own side project development are on the same computer, you will need the help of this article :kissing_smiling_eyes: 14 | 15 | Trust me, you don't want to use your company's account to push commits to your own side project :sweat_smile: 16 | 17 | ## Tutorial 18 | 19 | First, generate an ssh key. If you don't know how to generate one, 20 | 21 | you can refer to [Generating a new SSH key](https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/) or [Basic GitHub Tutorial - From Scratch](https://www.youtube.com/watch?v=py3n6gF5Y00). 22 | 23 | Open your Git Bash. 24 | 25 | ```cmd 26 | ssh-keygen -t rsa -b 4096 -C "blue555236sa56@gmail.com" 27 | ``` 28 | 29 | (Please replace the email with your own) 30 | 31 | The key point here is to remember to enter a new rsa name yourself, like `/c/Users/twtrubiks/.ssh/id_rsa_rubik` in the picture below. 32 | 33 | This way, you won't overwrite the old rsa. 34 | 35 | 36 | 37 | Next, you will have a new file you just created in your `.ssh` path. 38 | 39 | 40 | 41 | Then paste your `id_rsa_rubik.pub` key to your GitHub. 42 | 43 | 44 | 45 | Usually, when you add it successfully, you will receive an email. 46 | 47 | Then enter the command below: 48 | 49 | ```cmd 50 | ssh-agent -s 51 | ``` 52 | 53 | The above command must be executed. If you skip this step, the following error will occur: 54 | 55 | `Could not open a connection to your authentication agent.` 56 | 57 | So please remember to execute it. 58 | 59 | Create a file named `config` in the `.ssh` directory (the file name is `config`, no extension is needed). 60 | 61 | 62 | 63 | Below is an example: 64 | 65 | ```config 66 | # github - twtrubiks 67 | Host github.com 68 | HostName github.com 69 | IdentityFile ~/.ssh/id_rsa 70 | IdentitiesOnly yes 71 | User twtrubiks 72 | 73 | # github - blue-rubiks 74 | Host blue.github.com 75 | HostName github.com 76 | IdentityFile ~/.ssh/id_rsa_rubik 77 | IdentitiesOnly yes 78 | User blue-rubiks 79 | ``` 80 | 81 | You can use the following command to test. If your own username is displayed, it means the setting is successful. 82 | 83 | ```cmd 84 | ssh -T git@github.com 85 | ``` 86 | 87 | 88 | 89 | ```cmd 90 | ssh -T git@blue.github.com 91 | ``` 92 | 93 | 94 | 95 | ya :heart_eyes: we have set it up successfully~ 96 | 97 | If there is a problem, you can also use debug mode to see where the problem is. Usually, it is an error in the `config` input. 98 | 99 | ```cmd 100 | ssh -vT git@github.com 101 | ``` 102 | 103 | Next, let me explain to you first. 104 | 105 | ```cmd 106 | git config --global user.name "blue-rubiks" 107 | git config --global user.email "blue555236sa56@gmail.com" 108 | ``` 109 | 110 | `--global` means global. If it is not entered, it means that the repo under the directory will take effect. 111 | 112 | Now let's clone a repo to play with (you can create one yourself). 113 | 114 | ```cmd 115 | git clone git@github.com:blue-rubiks/github-ssh-test.git 116 | ``` 117 | 118 | Next, switch to the directory folder and enter the following command: 119 | 120 | ```cmd 121 | git config user.name "blue-rubiks" 122 | git config user.email "blue555236sa56@gmail.com" 123 | ``` 124 | 125 | Note that `--global` is not added here, so it only takes effect for the repo under that directory. 126 | 127 | We find the `.git` folder in the repo, and then find the `config` file. The content is roughly as follows: 128 | 129 | 130 | 131 | The main thing is to modify line 9 to line 10, changing it to the `Host` you set. 132 | 133 | Finally, you can commit and push with peace of mind. 134 | 135 | If you really don't understand what I'm talking about, I suggest watching the video explanation. I will walk you through the operation :laughing: 136 | 137 | ## Execution Environment 138 | 139 | * Win 10 140 | 141 | ## Reference 142 | 143 | * [Multiple SSH Keys settings for different github account](https://gist.github.com/jexchan/2351996) 144 | 145 | ## Donation 146 | 147 | The articles are all original after my own research and internalization. If it has helped you and you want to encourage me, you are welcome to buy me a cup of coffee :laughing: 148 | 149 | 150 | 151 | [Sponsor Payment](https://payment.opay.tw/Broadcaster/Donate/9E47FDEF85ABE383A0F5FC6A218606F8) 152 | 153 | ## License 154 | 155 | MIT license 156 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [English Version](README_en.md) 2 | 3 | # Git-Tutorials 基本使用教學 :memo: 4 | 5 | 因為小弟覺得這東西蠻有趣的,所以就簡單寫個教學文,順便記錄一下 :memo:,希望能幫助想學的人 :smile: 6 | 7 | 如果教學有誤再請糾正 :sweat_smile: 8 | 9 | 基本使用指令以及安裝可參考小弟之前拍的影片 10 | 11 | * [Youtube Tutorial - github基本教學 - 從無到有](https://www.youtube.com/watch?v=py3n6gF5Y00) 12 | 13 | 影片教學包含如何產生 **SSH key** 14 | 15 | 如果步驟正確且沒出錯誤,可以在路徑下找到 **.ssh資料夾**,裡面有 **id_rsa** 以及 **id_rsa.pub** 兩個檔案, 16 | 17 | 這兩個就是 SSH Key, **id_rsa是私鑰** ,不能洩露出去, **id_rsa.pub是公鑰** ,可以很放心的告訴任何人。 18 | 19 | 安裝完 Git 之後,要做的第一件事情就是去設定自己的名字和信箱 20 | 21 | ```cmd 22 | git config --global user.name "twtrubiks" 23 | git config --global user.email "twtrubiks@gmail.com" 24 | ``` 25 | 26 | 可以輸入以下來確認是否輸入成功 27 | 28 | ```cmd 29 | git config --global user.name 30 | git config --global user.email 31 | ``` 32 | 33 | ![alt tag](https://i.imgur.com/5mpS7Ij.jpg) 34 | 35 | Git 設定資料查看,可執行以下指令 ( 文章末會有較詳細的教學 ): 36 | 37 | ```cmd 38 | git config --list 39 | ``` 40 | 41 | ## git init 指令 42 | 43 | 初始化 git 44 | 45 | ```cmd 46 | git init 47 | ``` 48 | 49 | 也可以指定資料夾 50 | 51 | ```cmd 52 | git init 53 | ``` 54 | 55 | ## git clone 指令 56 | 57 | 複製如圖位置網址 ( 不要複製我的哦~ 複製你自己的 ) 58 | ![alt tag](https://i.imgur.com/EJ5JNjt.jpg) 59 | 60 | git clone ( 複製的網址 ) SSH / HTTPS 61 | 62 | ( 如果你要使用 https 的方式, 請接著看 [Personal Access Tokens](https://github.com/twtrubiks/Git-Tutorials#personal-access-tokens) ) 63 | 64 | ```cmd 65 | git clone git@github.com:twtrubiks/test.git 66 | ``` 67 | 68 | 第一次會出現 SSH 警告,選 YES 即可。 69 | 70 | 如圖 ( 下載成功 ),在你的下載路徑下就會多出一個資料夾 71 | 72 | ![alt tag](https://i.imgur.com/iIkTlqf.jpg) 73 | 74 | ## Personal Access Tokens 75 | 76 | * [Youtube Tutorial - GitHub 教學 - Personal Access Tokens](https://youtu.be/aJRRVCB85k8) 77 | 78 | 從 2021/8/13 開始, 如果你用 https 的方式你會發現 79 | 80 | ![alt tag](https://i.imgur.com/6YIJSaj.png) 81 | 82 | ```text 83 | remote: Support for password authentication was removed on August 13, 2021. 84 | remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication. 85 | fatal: Authentication failed for 'https://github.com/xxxxx.git/' 86 | ``` 87 | 88 | 這時候如果我們不想加入 ssh key, 也不想透過加入共同協做的方式, 89 | 90 | 可以透過這個 Personal Access Tokens (你可以把他想成臨時的權限), 91 | 92 | 先到你的 github 裡的 Settings -> Developer settings, 93 | 94 | 選 Personal Access Tokens, 產生你的 token 95 | 96 | ![alt tag](https://i.imgur.com/zPVlOjf.png) 97 | 98 | 時間這邊你可以自己定義多久後會過期, 99 | 100 | 下面的部份則是這個 token 有哪些權限, 101 | 102 | ![alt tag](https://i.imgur.com/NNJcYRM.png) 103 | 104 | 設定完之後, 就可以複製你的 token 105 | 106 | ![alt tag](https://i.imgur.com/q4htIBn.png) 107 | 108 | 再回去用 https clone 的方式, 109 | 110 | 原本是使用, 帳號 + password (已經不能使用了), 111 | 112 | 現在改成, 帳號 + 剛剛的 token 就可以順利 clone 了. 113 | 114 | ### 如何改善(加速)大型 repo git clone 速度 115 | 116 | * [Youtube Tutorial - 如何改善(加速)大型 repo git clone 速度](https://youtu.be/YHX0qkQa1UI) 117 | 118 | 有時候我們會需要 clone 很大的 repo,執行 `git clone` 都需要很長的時間,是不是有方法可以 119 | 120 | 加速 clone 的速度呢 :question: 121 | 122 | 直接開始動手嘗試 ( 使用 [django](https://github.com/django/django) 當範例 ), 123 | 124 | `git clone git@github.com:django/django.git` 125 | 126 | ( 你會發現 clone 需要一些時間 :triumph:) 127 | 128 | ![alt tag](https://i.imgur.com/yMH6L8F.png) 129 | 130 | 接著查看 log,`git log` 131 | 132 | ![alt tag](https://i.imgur.com/vJkFTr2.png) 133 | 134 | 嘗試切換 branch `git checkout stable/2.2.x` 135 | 136 | ![alt tag](https://i.imgur.com/UtxJ2ER.png) 137 | 138 | 開始改善(加速) clone 的時間, 139 | 140 | 可以透過 `--depth` 這個參數來完成,簡單說明一下他的功能,當我們一般執行 clone 之後, 141 | 142 | 接著執行 `git log` 你會發現有大量的 log,在某修情況下,你可能不需要那麼多的 log, 143 | 144 | 也就是說你可能只需要最近 10 筆的 history commit,甚至你只需要 1 筆 ( 也就是根本不需要 145 | 146 | history commit ),這時候就很適合使用 `--depth`。 147 | 148 | `git clone git@github.com:django/django.git --depth 1` 149 | 150 | ( 你會發現這次快很多了 ) 151 | 152 | ![alt tag](https://i.imgur.com/yvkZUZI.png) 153 | 154 | 接著查看 log,`git log` 155 | 156 | ( 會變快的原因是因為我們只保留最新的一筆 history commit , 157 | 158 | 如果你需要最近 10 筆,改成 --depth 10 即可 ) 159 | 160 | ![alt tag](https://i.imgur.com/at9Zzq3.png) 161 | 162 | 但是會有一個問題,當嘗試切換 branch `git checkout stable/2.2.x` 163 | 164 | ( 你會發現你無法切換 remote branch :scream: 165 | 166 | 原因是因為使用 `--depth` 相當於是 `--single-branch`, 167 | 168 | 所以當然沒有其他的 branch。 ) 169 | 170 | ![alt tag](https://i.imgur.com/gDaeq1W.png) 171 | 172 | 也就是說以下兩條指令其實是相等的 173 | 174 | ```cmd 175 | git clone git@github.com:django/django.git --depth 1 176 | git clone git@github.com:django/django.git --depth 1 --single-branch 177 | ``` 178 | 179 | 為了解決這個問題,比較好的做好應該是這樣 180 | 181 | ```cmd 182 | git clone git@github.com:django/django.git --depth 1 --no-single-branch 183 | ``` 184 | 185 | ( 這個和 `--single-branch` 比會稍微久一點點,因為每個 branch 的最新一個 history commit 都要 clone 下來 ) 186 | 187 | 這樣的話,就可以保留 remote 的 branch 了, 188 | 189 | ![alt tag](https://i.imgur.com/BkLKVZz.png) 190 | 191 | 成功切換 remote 的 branch, `git checkout stable/2.2.x`。 192 | 193 | ![alt tag](https://i.imgur.com/VCvcSTr.png) 194 | 195 | 最後稍微整理, 196 | 197 | 如要 clone 最近一次的 history,而且也需要其他 branch,使用如下, 198 | 199 | `git clone git@github.com:django/django.git --depth 1 --no-single-branch` 200 | 201 | 如果你想要指定分支, 加上 `-b`, 202 | 203 | `git clone git@github.com:django/django.git --depth 1 --no-single-branch -b stable/3.1.x` 204 | 205 | 如要 clone 最近一次的 history,而且**不需要**其他 branch,使用如下, 206 | 207 | `git clone git@github.com:django/django.git --depth 1 --single-branch` 208 | 209 | or 210 | 211 | `git clone git@github.com:django/django.git --depth 1` 212 | 213 | 更多詳細參數說明請參考 [git clone](https://git-scm.com/docs/git-clone) 214 | 215 | ## git status 指令 216 | 217 | ```cmd 218 | git status 219 | ``` 220 | 221 | 可以讓我們觀看目前的 repository ( repo 容器 )。 222 | 223 | ![alt tag](https://i.imgur.com/5Gt98Vh.jpg) 224 | 225 | 意思是目前你的工作區是乾淨的。 226 | 227 | ## 工作區與暫存區 ( Stage ) 228 | 229 | git add 意思是把要送出的文件放到暫存區 ( Stage ) , 230 | 231 | 然後執行 232 | 233 | git commit 就可以把暫存區 ( Stage ) 裡所有修改的內容送到目前的分支上。 234 | 235 | 一旦送出 ( git commit ) 後,如果你又沒有對工作區做任何修改,那麼工作區就是"乾淨"的。 236 | 237 | git commit -m "xxxxx" 指令,-m 後面輸入的內容是本次修改 ( 送出 ) 的說明, 238 | 239 | 盡量輸入一眼就可以看出這次送出修改了什麼的內容 240 | ( 方便以後回去觀看能快速了解此次 commit 修改了什麼 )。 241 | 242 | 以下 demo 為在一個資料夾內新增一個 Hello.py 檔案 243 | 244 | 然後使用 git status 觀看目前的 repository ( repo 容器 ),你會看到 Hello.py 未被追蹤,如下圖 245 | 246 | ![alt tag](https://i.imgur.com/dvj1DQh.jpg) 247 | 248 | 可以使用如下指令 249 | 250 | ```cmd 251 | git add Hello.py 252 | ``` 253 | 254 | 額外補充,下面這個指令很有趣,大家可以玩玩看 255 | 256 | ```cmd 257 | git add -p 258 | ``` 259 | 260 | 接著再使用 261 | 262 | git commit -m "文字" 263 | 264 | ```cmd 265 | git commit -m "add Hello.py" 266 | ``` 267 | 268 | 再使用 git status,你會發現工作區變乾淨了。如下圖 269 | 270 | ![alt tag](https://i.imgur.com/6VrieNb.jpg) 271 | 272 | 補充,如果只有輸入 273 | 274 | ```cmd 275 | git commit 276 | ``` 277 | 278 | ![alt tag](https://i.imgur.com/yZxKGTU.jpg) 279 | 280 | 這時會跳出編輯視窗 281 | 282 | ![alt tag](https://i.imgur.com/htNQ0dJ.jpg) 283 | 284 | 這時可以按鍵盤的 **Ins鍵** ( 或按鍵盤上的 **英文字 i** ) 即可輸入文字 285 | 286 | ![alt tag](https://i.imgur.com/NFy16dp.jpg) 287 | 288 | 輸入完先按 **Esc鍵** ,按完後底下的 INSERT 會消失,接著直接打 **:wq** ,再按 enter 就會儲存並離開了。 289 | 290 | 更多參數可參考 [https://git-scm.com/docs/git-commit](https://git-scm.com/docs/git-commit) 說明。 291 | 292 | **如何修改最後一次的commit呢 ?** 293 | 294 | 有時候我們 commit 完之後,才發現自己的 commit 內容手殘打錯了 295 | 296 | 這時候可以使用如下指令,他會跳出編輯視窗給你編輯你上一次的 commit 內容。 297 | 298 | ```cmd 299 | git commit --amend 300 | ``` 301 | 302 | 又或是我們 commit 完之後,才發現自己漏了幾個檔案沒有 add 進去 303 | 304 | 這時候可以使用如下指令 305 | 306 | ```cmd 307 | git commit -m "init commit" 308 | git add missing_file.py 309 | git commit --amend 310 | ``` 311 | 312 | 如上狀況為當我 git commit -m "init commit" 之後, 313 | 314 | 我發現我漏掉了 **missing_file.py** 這個檔案 ( commit 前忘記 add 進去 ) , 315 | 316 | 這時候就可以使用 git commit --amend 來修改最後一次的 commit 。 317 | 318 | 有時候我們會為了方便,直接使用下面的指令一次加入全部的檔案 319 | 320 | ```cmd 321 | git add . 322 | ``` 323 | 324 | 但是加完後發現其實有些檔案是不需要 add 進入的,這時候就可以使用如下指令去取消 add 325 | 326 | ```cmd 327 | git reset HEAD 328 | ``` 329 | 330 | 範例,路徑下有 A.py 以及 B.py 這兩個檔案,然後我使用 **git add .** 加入, 331 | ![alt tag](https://i.imgur.com/0S7TcEB.jpg) 332 | 333 | 但加入完我發現其實 B.py 我還沒有要 add 進入,所以我這時候就可以使用 **git reset HEAD B.py** 去還原。 334 | 335 | ![alt tag](https://i.imgur.com/3iAyEEx.jpg) 336 | 337 | ## git push 指令 338 | 339 | ```cmd 340 | git push 341 | ``` 342 | 343 | 將程式 push 到 github ( or bitbucket 之類 )上 , 如下圖 344 | 345 | ![alt tag](https://i.imgur.com/d61Pau6.jpg) 346 | 347 | ## 版本控制 - 歷史記錄 348 | 349 | ```cmd 350 | git log 351 | ``` 352 | 353 | 按 **小寫q** 可退出 354 | 355 | ![alt tag](https://i.imgur.com/j11afCP.jpg) 356 | 357 | 如果覺得版面太雜,可以使用下列指令 358 | 359 | ```cmd 360 | git log --pretty=oneline 361 | ``` 362 | 363 | 按 **小寫q** 可退出 364 | 365 | ![alt tag](https://i.imgur.com/jz2cwUA.jpg) 366 | 367 | 如果你想要看某個檔案或某個資料夾的變動, 可以這樣使用, 範例如下 368 | 369 | * [Youtube Tutorial - Git Log:資料夾移動後 Log 消失?用 --follow 找回檔案歷史!(等待新增)](xx) 370 | 371 | ```cmd 372 | git log -- folder 373 | ``` 374 | 375 | 如果追蹤個別檔案, 可以加上 `--follow` 376 | 377 | ```cmd 378 | git log --follow -- folder/demo.py 379 | ``` 380 | 381 | 舉個例子, 我有一個資料夾結構如下 382 | 383 | ```cmd 384 | ❯ tree 385 | . 386 | ├── production 387 | │   ├── a 388 | │   │   ├── a1.txt 389 | │   │   ├── a2.txt 390 | │   │   └── a3.txt 391 | │   └── b 392 | │   └── b1.txt 393 | └── test 394 | ``` 395 | 396 | `production` 下的歷史紀錄 397 | 398 | ```cmd 399 | ❯ git log -- production/a 400 | 401 | * b9a481b - a3.txt 402 | * 02fce19 - a2.txt 403 | * 1e4aa1b - create 404 | ``` 405 | 406 | 假設今天把 `production` 底下的 `a` 資料夾移動到 `test` 資料夾底下, 407 | 408 | 資料夾結構變成如下, 然後 commit 409 | 410 | ```cmd 411 | ❯ tree 412 | . 413 | ├── production 414 | │   └── b 415 | │   └── b1.txt 416 | └── test 417 | └── a 418 | ├── a1.txt 419 | ├── a2.txt 420 | └── a3.txt 421 | ``` 422 | 423 | 目前整個 repo 的 git log 如下 424 | 425 | ```cmd 426 | ❯ git log 427 | 428 | * 5707ce2 - move 429 | * b9a481b - a3.txt 430 | * 02fce19 - a2.txt 431 | * 1e4aa1b - create 432 | * 1b7fb87 - Initial commit 433 | ``` 434 | 435 | 然後再看 `a` 資料夾, 如下 436 | 437 | ```cmd 438 | ❯ git log -- test/a 439 | 440 | * 5707ce2 - move 441 | ``` 442 | 443 | 你會發現前面的 `a2.txt` 以及 `a3.txt` 的 git 的追蹤紀錄消失了 !!! 444 | 445 | 方法一, 追蹤原資料夾, 446 | 447 | 雖然這個檔案已經不存在了, 但是對 git 來說紀錄還是在的 448 | 449 | ```cmd 450 | # 這邊你會發現資料夾已經不存在的, 因為已經移動了 451 | ❯ ls production/a 452 | 453 | ls: cannot access 'production/a': No such file or directory 454 | 455 | # 但是還是可以追蹤到歷史紀錄 456 | ❯ git log -- production/a 457 | 458 | * 5707ce2 - move 459 | * b9a481b - a3.txt 460 | * 02fce19 - a2.txt 461 | * 1e4aa1b - create 462 | ``` 463 | 464 | 方法二, 加上 `--follow`, 這個可以追蹤個別檔案 (移動後的資料夾不要用它, 因為會看不到過去紀錄) 465 | 466 | ```cmd 467 | ❯ git log --follow -- test/a/a1.txt 468 | 469 | * 5707ce2 - move 470 | * 1e4aa1b - create 471 | ``` 472 | 473 | 另外底下也是一個看 log 的方式( 很酷 :satisfied:),有 GUI 的感覺( 來源為文章最後的連結 ) 474 | 475 | ```cmd 476 | git log --graph --pretty=format:"%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset" --abbrev-commit --date=relative 477 | ``` 478 | 479 | ![alt tag](https://i.imgur.com/XNQisuf.png) 480 | 481 | Git 中,使用 HEAD 表示目前的版本, 482 | 483 | ```cmd 484 | git reset --hard HEAD 485 | ``` 486 | 487 | ![alt tag](https://i.imgur.com/pkFO8pk.jpg) 488 | 489 | 如果現在要把目前版本退回到上一個版本,就可以使用 git reset 指令: 490 | 491 | 上一個版本就是HEAD~1, 492 | 493 | ```cmd 494 | git reset --hard HEAD~1 495 | ``` 496 | 497 | ![alt tag](https://i.imgur.com/ZThoaUT.jpg) 498 | 499 | 上上一個版本就是HEAD~2, 500 | 501 | 如果要指定回到某個特定版本: 502 | 503 | ![alt tag](https://i.imgur.com/KrCOC71.jpg) 504 | 505 | ```cmd 506 | git reset --hard ad41df36b7 507 | ``` 508 | 509 | `--hard` 這個參數,有三種選擇,分別為 `--mixed`( default )`--hard` `--soft`, 510 | 511 | `--hard` 這個參數簡單解釋就是將之前的 commit 都丟掉( 完全 **不保留** )。 512 | 513 | `--soft` 這個參數簡單解釋就是將之前的 commit 都丟掉,但 **保留** 你之前工作區的狀態。 514 | 515 | `--hard` 和 `--soft` 這兩個我覺得用文字不好說明,我建議大家自己可以動手玩玩看,就可以了解他們之間的差異。 516 | 517 | `--soft` 很適合使用在將多個無意義的 commit 合併成一個 commit。 518 | 519 | ![alt tag](https://i.imgur.com/6RVutiK.jpg) 520 | 521 | 版本號 ( ad41df36b7 ) 沒必要全部都寫,寫前幾位就可以了,Git 會自動去找。 522 | 523 | 當你退回到某個版本,突然隔天後悔了,想恢復到之前的新版本該怎麼做呢? 524 | 525 | 找不到新版本的 commit id 該怎麼辦呢? 526 | 527 | 這時候就可以使用一個指令 528 | 529 | ```cmd 530 | git reflog 531 | ``` 532 | 533 | ![alt tag](https://i.imgur.com/MaRlZZr.jpg) 534 | 535 | 接著看你要回到哪個版本,再使用 git reset 即可。 536 | 537 | ```cmd 538 | git reset --hard 642e7af 539 | ``` 540 | 541 | 有時候想消除( 覆蓋 )已經 push 出去的 commit,這時候我們可以使用 542 | 543 | ```cmd 544 | git push --force 545 | ``` 546 | 547 | 或是更簡短的寫法 548 | 549 | ```cmd 550 | git push -f 551 | ``` 552 | 553 | 可以強制 push。先回到某個版本,然後再強制 push。 554 | 555 | ***注意!在多人專案共同開發時,盡量不要用 --force 這種方法,因為有時候會害到別人,建議可以使用 revert 。*** 556 | 557 | 因為上面這個原因,所以建議用另一種比較安全的方式 558 | 559 | ```cmd 560 | git push --force-with-lease 561 | ``` 562 | 563 | 可以確保你沒有隨便丟掉別人的 commit。( 如果有人比你早 commit push 上去,你就會無法 push 到 remote ) 564 | 565 | ## checkout 566 | 567 | 也請參考 [git switch](https://github.com/twtrubiks/Git-Tutorials#git-switch) 和 [git restore](https://github.com/twtrubiks/Git-Tutorials#git-restore). 568 | 569 | `git checkout -- file` 可以丟棄工作區的修改: 570 | 571 | ```cmd 572 | git checkout -- hello.py 573 | ``` 574 | 575 | 命令 git checkout -- hello.py 意思就是,把 hello.py 文件在工作區的修改全部撤銷 ( 丟棄 ) , 576 | 577 | 讓這個檔案回到最近一次 git commit 或 git add 時的狀態。 578 | 579 | ![alt tag](https://i.imgur.com/SrCo4kH.jpg) 580 | 581 | 當然也可以用 git reset 指令直接回到某個 commit。 582 | 583 | ```cmd 584 | git reset --hard xxxxxx 585 | ``` 586 | 587 | ```cmd 588 | git reset --hard 201f40604ec3b6fa8 589 | ``` 590 | 591 | ## 刪除 592 | 593 | 有兩種狀況,一種是確定要從版本庫中刪除該檔案,那就用命令 git rm 刪掉,並且 git commit: 594 | 595 | ```cmd 596 | rm hello.py 597 | git rm hello.py 598 | git commit -m "remove hello.py" 599 | ``` 600 | 601 | ![alt tag](https://i.imgur.com/sLMTDX7.jpg) 602 | 603 | 另一種狀況是刪錯了,使用 git checkout 可以輕鬆還原檔案: 604 | 605 | ```cmd 606 | rm hello.py 607 | git checkout -- hello.py 608 | ``` 609 | 610 | ![alt tag](https://i.imgur.com/5X2NcfS.jpg) 611 | 612 | ## 新建與 合併 ( merge ) 分支 branch 613 | 614 | 在說明分支 branch 之前,先給大家一個觀念。 615 | 616 | 通常開發的時候,大家都是從 **master** 做一個分支 branch 出去,最後再 **merge** 回 master, 617 | 618 | 為什麼要這麼做呢 ? 因為要確保大家都是使用最新的 **master** 619 | 620 | 使用 git branch 指令查看目前的分支: 621 | 622 | ```cmd 623 | git branch 624 | ``` 625 | 626 | ![alt tag](https://i.imgur.com/SVblXD2.jpg) 627 | 628 | 首先創建一個分支,bug1 分支 ( 名稱可以隨便取 ),然後切換到 bug1 分支: 629 | 630 | ```cmd 631 | git branch bug1 632 | git checkout bug1 633 | ``` 634 | 635 | git branch bug1 為創造一個名稱為 bug1 的分支, 636 | 637 | git checkout bug1 為切換到一個名稱為 bug1 的分支底下。 638 | 639 | ![alt tag](https://i.imgur.com/JtGBHk4.jpg) 640 | 641 | 以上兩行指令,相當於下列一行指令 642 | 643 | ```cmd 644 | git checkout -b bug1 645 | ``` 646 | 647 | (這邊教大家一個小技巧, 以下這個指令可以快速切換上一個分支, 和 `cd -` 概念一樣 :exclamation:) 648 | 649 | ```cmd 650 | git checkout - 651 | ``` 652 | 653 | 我們在 bug1 分支上進行任何修改操作, 654 | 655 | 然後再把工作成果 ( 補充一下,修改任何內容後請記得使用 git add 指令和 git commit 指令 ) 合併到 master 分支上: 656 | 657 | ```cmd 658 | git checkout master 659 | git merge bug1 660 | ``` 661 | 662 | ![alt tag](https://i.imgur.com/pF4xDUE.jpg) 663 | 664 | git checkout master 為切換到一個名稱為 master 的分支底下。 665 | 666 | git merge bug1 指令用於合併 ( bug1分支 ) 指定分支到目前分支 ( master ) 底下。 667 | 668 | 如果非常順利, git merge 的訊息裡會出現 Fast-forward,合併速度非常快。 669 | 670 | 當然不是每次合併都能很順利的出現 Fast-forward,很多時候會出現衝突 CONFLICT 。 671 | 672 | 如果順利合併 ( merge ) 完成後,就可以刪除 (本機) bug1 分支: 673 | 674 | ```cmd 675 | git branch -d dev 676 | ``` 677 | 678 | ![alt tag](https://i.imgur.com/LmKKWxR.jpg) 679 | 680 | 如果要丟掉一個沒有被合併過的分支,可以使用 git branch -D 分支名稱 強行刪除 (本機)。 681 | 682 | ```cmd 683 | git branch -D dev 684 | ``` 685 | 686 | 那如果今天要刪除 remote 端的 branch 該怎麼辦呢 :question: 687 | 688 | * [Youtube Tutorial - git 刪除查看遠端的分支 branch](https://youtu.be/0JQrT7nfm_c) 689 | 690 | ```cmd 691 | git push origin --delete {remote_branch} 692 | ``` 693 | 694 | 補充,git branch 也可以修改名稱,而且 commit id 是不會改變的,使用方法也很簡單, 695 | 696 | 可參考 git-branch [文件](https://git-scm.com/docs/git-branch#git-branch--m),使用方法如下, 697 | 698 | ```text 699 | git branch -m 700 | ``` 701 | 702 | 原本的 b1 branch 分支的 log 如下, 703 | 704 | ![alt tag](https://i.imgur.com/b1K1EUy.png) 705 | 706 | 現在將 b1 branch 修改成 b2 branch, 707 | 708 | ![alt tag](https://i.imgur.com/Twz5kRm.png) 709 | 710 | 如果你仔細和剛剛的 log 比較,你會發現 log 的 commit id 是不會改變的, 711 | 712 | ![alt tag](https://i.imgur.com/qMjqV3Z.png) 713 | 714 | ## 使用特定 commit id 建立 branch 715 | 716 | 有時候我們會想測試某個 commit 的狀態, 這時候可以直接利用 commit id 去建立一個 branch, 717 | 718 | 方法如下, 719 | 720 | ```cmd 721 | git checkout -b new_branch 722 | ``` 723 | 724 | 這樣就會依照你指定的 commit id 去建立出一個 branch. 725 | 726 | ## 新建分支 branch 並 push 727 | 728 | 相信大家有時候在 github 上面都會看到,如下圖,很多分支 729 | 730 | ![alt tag](https://i.imgur.com/wrIdlzS.jpg) 731 | 732 | 那我們要如何建立分支呢? 首先,我們先看下面這張圖 733 | 734 | ![alt tag](https://i.imgur.com/3U092a1.jpg) 735 | 736 | 有一個 v1 的分支,並且我在分支上增加一個 g.py 並且 commit。 737 | 738 | 接下來要 **第一次** git push 的時候, 你會發現有錯誤提示 739 | 740 | 請使用以下指令才是正確的 741 | 742 | ```cmd 743 | git push --set-upstream origin v1 744 | ``` 745 | 746 | 也可以使用 747 | 748 | ```cmd 749 | git push -u origin v1 750 | ``` 751 | 752 | 更多詳細說明可參考 [https://git-scm.com/docs/git-push#git-push--u](https://git-scm.com/docs/git-push#git-push--u) 753 | 754 | ![alt tag](https://i.imgur.com/1fuS2VY.jpg) 755 | 756 | 接下來你可以到網頁上看 ( 這裡用 bitbucket 當作範例 ) ,你會發現有分支 v1 了 757 | 758 | ![alt tag](https://i.imgur.com/lOtzsk8.jpg) 759 | 760 | 如果是第一次使用 git clone ,你會發現你只有 master 分支 , 761 | 762 | 這時候我們先查看遠端還有什麼分支, 763 | 764 | ```cmd 765 | git branch -r 766 | ``` 767 | 768 | ```cmd 769 | git branch --remote 770 | ``` 771 | 772 | `--remote` 或 `-r` 都可以. 773 | 774 | 假設遠端有一個名稱為 develop 的分支, 775 | 776 | 我們只要 checkout 到該分支底下就可以了 777 | 778 | ```cmd 779 | git checkout develop 780 | ``` 781 | 782 | ## git switch 783 | 784 | [Youtube Tutorial - git switch 和 git restore 教學](https://youtu.be/JL_bSOGDR-k) 785 | 786 | 請先確認目前的 git 版本, 更新方法可參考 [git 更新](https://github.com/twtrubiks/Git-Tutorials#git-%E6%9B%B4%E6%96%B0). 787 | 788 | 在 git 2.23 版本開始, 增加了 `git switch` 和 `git restore`, 這兩個指令主要是 789 | 790 | 要更清楚的劃分功能, 主要是來代替 `git checkout`. 791 | 792 | 你其實可以想成 `git checkout` = `git switch` + `git restore`. 793 | 794 | 官方文件可參考 [git-switch](https://git-scm.com/docs/git-switch) 795 | 796 | ```cmd 797 | git switch [] (-c|-C) [] 798 | ``` 799 | 800 | 切換到一個已經存在的 branch (如果該 branch 不存在則指令無效) 801 | 802 | ```cmd 803 | git switch 804 | ``` 805 | 806 | 建立 new-branch 並且切換到 new-branch 分支 807 | 808 | ```cmd 809 | git switch -c 810 | ``` 811 | 812 | `-c` `--create` 813 | 814 | `-C` `--force-create` 815 | 816 | 依照 commit_id (或前 N 的 commit 點) 建立 new-branch 並且切換到 new-branch 分支 817 | 818 | ```cmd 819 | git switch -c 820 | git switch -c HEAD~2 821 | ``` 822 | 823 | (這邊教大家一個小技巧, 以下這個指令可以快速切換上一個分支, 和 `cd -` 概念一樣 :smile:) 824 | 825 | ```cmd 826 | git switch - 827 | ``` 828 | 829 | ## git restore 830 | 831 | [Youtube Tutorial - git switch 和 git restore 教學](https://youtu.be/JL_bSOGDR-k) 832 | 833 | 請先確認目前的 git 版本, 更新方法可參考 [git 更新](https://github.com/twtrubiks/Git-Tutorials#git-%E6%9B%B4%E6%96%B0). 834 | 835 | 在 git 2.23 版本開始, 增加了 `git switch` 和 `git restore`, 這兩個指令主要是 836 | 837 | 要更清楚的劃分功能, 主要是來代替 `git checkout`. 838 | 839 | 你其實可以想成 `git checkout` = `git switch` + `git restore`. 840 | 841 | 官方文件可參考 [git-restore](https://git-scm.com/docs/git-restore) 842 | 843 | 以下兩個指令是相同的. 844 | 845 | ```cmd 846 | git checkout 847 | git restore 848 | ``` 849 | 850 | 還原目前資料夾全部的檔案 851 | 852 | ```cmd 853 | git restore . 854 | ``` 855 | 856 | 還原目前資料夾底下結尾是 `*.py` 的全部檔案 857 | 858 | ```cmd 859 | git restore '*.py' 860 | ``` 861 | 862 | 如果你的 `git` 版本比較新, 你應該會發現這個指令你以前好像沒看過 :smile: 863 | 864 | ![alt tag](https://i.imgur.com/IHqfVrn.png) 865 | 866 | ```cmd 867 | git restore --staged 868 | ``` 869 | 870 | ## git pull 871 | 872 | 通常在開始工作或要 push 之前,會先從遠端抓取分支, 873 | 874 | ```cmd 875 | git pull 876 | ``` 877 | 878 | 如果有衝突,要先解衝突。 879 | 880 | 這邊補充一下 `-C` 這個參數的意思, 它的意思代表指定 folder 路徑, 881 | 882 | 有時候我們可能不想先 `cd` 進去資料夾, 再進行 pull, 這時候, 883 | 884 | 就很適合使用它 :smile: 885 | 886 | ```cmd 887 | git [-C ] pull 888 | ``` 889 | 890 | 舉例, 891 | 892 | ```cmd 893 | cd git_folder 894 | git pull 895 | ``` 896 | 897 | 可以直接簡化為 898 | 899 | ```cmd 900 | git -C git_folder pull 901 | ``` 902 | 903 | ## git fetch 904 | 905 | 可以先簡單想成 **git pull = git fetch + git merge** 906 | 907 | 我們先來看下面這張圖, **git fetch + git merge** 908 | 909 | ![alt tag](https://i.imgur.com/COuWByw.png) 910 | 911 | 再看這張圖 **git pull** 912 | 913 | ![alt tag](https://i.imgur.com/8FGuA75.png) 914 | 915 | 這樣是不是清楚多了!!! 916 | 917 | 多補充一個參數 `--prune`, 918 | 919 | * [Youtube Tutorial - git fetch 指令 prune 參數說明](https://youtu.be/ZMpMv1P1Q1Q) 920 | 921 | 這個主要的功能是刪除 remote 無效的 branch, 922 | 923 | 有時候明明已經把遠端的 branch 刪除, 但是你執行 `git branch --remote`, 924 | 925 | 卻會發現你還看的到那些 branch 的分支 (但明明網頁上的分支已經被移除了 :sweat:) 926 | 927 | 常常會發生在 pull 端(非工作端)的機器 (如果不懂這句話的意思建議看影片說明 :smile:) 928 | 929 | 這時候就可以同步一下本機和遠端的分支, 使用以下的指令 930 | 931 | `git fetch --prune` 932 | 933 | ## git rebase 934 | 935 | 什麼是 rebase 呢 ? git rebase 就是避免多餘 ( 沒有意義 ) 的 merge !!! 先看看下面兩張圖 936 | 937 | 補充 : 938 | 939 | ck = checkout 940 | 941 | br = branch 942 | 943 | st = status 944 | 945 | cm = commit 946 | 947 | 可以自行設定。 948 | 949 | 圖一 950 | 951 | ![alt tag](https://i.imgur.com/mWY0f2J.png) 952 | 953 | 圖二 954 | 955 | ![alt tag](https://i.imgur.com/QVZc5P5.png) 956 | 957 | 圖一 和 圖二 你喜歡看哪種圖 ? 答案很明顯,是 圖一 !! 958 | 959 | **rebase** 的目的主要就是盡量讓圖都像 圖一 960 | 961 | 用講的大家一定霧煞煞,所以我直接實戰給大家看。 962 | 963 | 先示範 **沒有使用 rebase** 的範例 964 | 965 | 目前分支 966 | 967 | ![alt tag](https://i.imgur.com/E0ahfnD.png) 968 | 969 | ![alt tag](https://i.imgur.com/Lb4dB0V.png) 970 | 971 | 以上說明 : 先建立 v1 branch,接著 add 後再 commit。 972 | 973 | 假設現在又有人 push 了,以下模擬 pull ,自己加上一個 commit 974 | 975 | ![alt tag](https://i.imgur.com/hFKX4yJ.png) 976 | 977 | 以上說明 : 自己在 master 分支上加 t2.txt , 並且commit ( 模擬 pull ) 978 | 979 | 接下來,切換到 master 分支下和 v1 branch 分支 合併,並且 push 980 | 981 | ![alt tag](https://i.imgur.com/0sCH2Q1.png) 982 | 983 | 你會發現,顯示出來的圖並不漂亮,如下圖 984 | 985 | ![alt tag](https://i.imgur.com/zbIPdyb.png) 986 | 987 | 示範 **使用 rebase** 的範例 988 | 989 | 前面的部份基本上一樣 990 | 991 | ![alt tag](https://i.imgur.com/E0ahfnD.png) 992 | 993 | ![alt tag](https://i.imgur.com/Lb4dB0V.png) 994 | 995 | 以上說明 : 先建立 v1 branch,接著 add 後再 commit。 996 | 997 | 假設現在又有人 push 了,以下模擬 pull ,自己加上一個 commit 998 | 999 | ![alt tag](https://i.imgur.com/hFKX4yJ.png) 1000 | 1001 | 以上說明 : 自己在 master 分支上加 t2.txt , 並且 commit ( 模擬 pull ) 1002 | 1003 | ***差異的部份*** 1004 | 1005 | ![alt tag](https://i.imgur.com/45ZXGiK.png) 1006 | 1007 | 以上說明 : 先切換到 v1 分支,然後使用以下指令 1008 | 1009 | ```cmd 1010 | git rebase master 1011 | ``` 1012 | 1013 | ![alt tag](https://i.imgur.com/Lpd9Kjr.png) 1014 | 1015 | 以上說明 : 再切回 master 分支,並且使用 merge 合併 v1 分支,最後在 push 1016 | 1017 | 你看~ 是不是變的整齊又漂亮多了呢? 1018 | 1019 | ![alt tag](https://i.imgur.com/1jBI7pw.png) 1020 | 1021 | git rebase 就是將 master 的最新 commit 接回來,再補上自己分支的 commit。 1022 | 1023 | 以上就是 git rebase 的介紹, 1024 | 1025 | 另一種作法, 剛剛是必須切換到 v1 分支底下, 才執行指令, 1026 | 1027 | 如果你現在在任何分支(像是 master 分支), 你可以使用以下指令 1028 | 1029 | ```cmd 1030 | git rebase master v1 1031 | ``` 1032 | 1033 | 就是後面指定 v1, 執行完後它會自動幫你切換到 v1 分支上, 1034 | 1035 | 結果都是一樣的. 1036 | 1037 | 另外, 還有一個指令是 `git rebase --onto` 1038 | 1039 | ```cmd 1040 | git rebase --onto 1041 | ``` 1042 | 1043 | 其實概念上都是一樣的, 就是你想要 rebase 到哪個 new base-commit 上, 1044 | 1045 | 後面放 current base-commit 而已. 1046 | 1047 | 可以搭配 git graph 觀看, 或是看 git 的文檔 `git rebase --help` 1048 | 1049 | ## git rebase interactive 1050 | 1051 | 小弟我當初年輕,一直以為 `git rebase` 就只是讓 commit log 看起來比較乾淨而已,結果無意間發現, 1052 | 1053 | `git rebase` 的 interactive 超強,所以,這邊就來介紹 `git rebase` 的強大功能 :smirk: 1054 | 1055 | 以下是 git rebase interactive 可以使用的指令,這些說明是我從 git 中複製出來的,等等會顯示給大家看, 1056 | 1057 | ```cmd 1058 | # Commands: 1059 | # p, pick = use commit 1060 | # r, reword = use commit, but edit the commit message 1061 | # e, edit = use commit, but stop for amending 1062 | # s, squash = use commit, but meld into previous commit 1063 | # f, fixup = like "squash", but discard this commit's log message 1064 | # x, exec = run command (the rest of the line) using shell 1065 | # d, drop = remove commit 1066 | ``` 1067 | 1068 | 如果大家想要更進一步的了解,請參考 [INTERACTIVE MODE](https://git-scm.com/docs/git-rebase#_interactive_mode), 1069 | 1070 | pick 沒什麼好講的,就使用這個 commit 而已 :smile: 1071 | 1072 | ### reword 1073 | 1074 | [Youtube Tutorial - git rebase interactive - reword - PART 1](https://youtu.be/JhY0rR2wQq0) 1075 | 1076 | ```cmd 1077 | # Commands: 1078 | # p, pick = use commit 1079 | # r, reword = use commit, but edit the commit message 1080 | ``` 1081 | 1082 | 以下為官方的說明 1083 | 1084 | ```txt 1085 | If you just want to edit the commit message for a commit, replace the command "pick" with the command "reword". 1086 | ``` 1087 | 1088 | 說明已經很清楚了,就是可以編輯 commit message。 1089 | 1090 | ( 不能修改 commit 內容,也就是 files 內容 ) 1091 | 1092 | 假設,現在我們有一個 git log 是這樣, 1093 | 1094 | ![alt tag](https://i.imgur.com/6bWnJnK.png) 1095 | 1096 | commit id 2659f65 有 Typo,正確的 commit message 應該是 add c.py 才對, 1097 | 1098 | 所以現在要修正他,我們的目標 commit id 為 2659f65,指令為 1099 | 1100 | ```cmd 1101 | git rebase -i 1102 | ``` 1103 | 1104 | after-this-commit 這個是什麼意思 :question: 1105 | 1106 | 簡單說,就是要選當下的 commit id 的上一個, 1107 | 1108 | 以這個例子來說,我們的目標 commit id 為 2659f65,但指令我們必須下 1109 | 1110 | ```cmd 1111 | git rebase -i f0a761d 1112 | ``` 1113 | 1114 | ![alt tag](https://i.imgur.com/d15nGjx.png) 1115 | 1116 | 這樣應該就很清楚了,總之,記得要選擇目標 commit id 的上一個就對了。 1117 | 1118 | 當你按下 ENTER 之後,你應該會看到下圖 1119 | 1120 | ![alt tag](https://i.imgur.com/4ISGcW1.png) 1121 | 1122 | A 的部份就是我們要修改的目標,B 的部分就是說明 ( 前面貼給大家看的東西 ), 1123 | 1124 | 接著,按 i 進入編輯模式,然後將目標改成 r 或是 reword 都可以,接著輸入 `:wq` 1125 | 1126 | ![alt tag](https://i.imgur.com/zPeHuDa.png) 1127 | 1128 | 接著我們再按下 ENTER,會再跳出一次畫面,這時候,你就將 commit 訊息修改成 1129 | 1130 | 正確的,將 add c.py Typo 修改為 add c.py 1131 | 1132 | ![alt tag](https://i.imgur.com/brYbNqy.png) 1133 | 1134 | 輸入 `:wq` 之後,再 ENTER ( 完成 ) 1135 | 1136 | ![alt tag](https://i.imgur.com/kitKqrm.png) 1137 | 1138 | 我們再用 log 確認一下( 如下圖 ),的確修改成功了,成功將訊息修改為 add c.py, 1139 | 1140 | ![alt tag](https://i.imgur.com/rWojGIu.png) 1141 | 1142 | 這邊有個地方要和大家提一下,就是 commit id 會改變,我把改變的地方框出來給各位看, 1143 | 1144 | 修改前 1145 | 1146 | ![alt tag](https://i.imgur.com/6i6Wv35.png) 1147 | 1148 | 修改後 1149 | 1150 | ![alt tag](https://i.imgur.com/mvj96U2.png) 1151 | 1152 | 簡單來說,就是目前 commit id 之後的 commit id 都會改變 ( 有點繞口 :sweat_smile: ) 1153 | 1154 | 這邊補充一下,只要你用了 rebase,就會看到類似下面的圖, 1155 | 1156 | ![alt tag](https://i.imgur.com/iiDf44q.png) 1157 | 1158 | origin/master 就是指遠端 ( romote ) 的 repo,它是和你說你現在的 repo 已經和 origin/master 1159 | 1160 | 不一樣了,所以,這時候你如果要 push,請使用 `git push --force-with-lease`。 1161 | 1162 | 這邊可能有人會問,如果我希望修改第一個 commit 該怎麼辦 :question: 1163 | 1164 | 這時候可以使用, 1165 | 1166 | ```cmd 1167 | git rebase -i --root 1168 | ``` 1169 | 1170 | ### edit 1171 | 1172 | [Youtube Tutorial - git rebase interactive - edit - PART 2](https://youtu.be/TCKjQppHxxQ) 1173 | 1174 | ```cmd 1175 | # Commands: 1176 | # p, pick = use commit 1177 | # e, edit = use commit, but stop for amending 1178 | ``` 1179 | 1180 | 以下為官方的說明 1181 | 1182 | ```txt 1183 | By replacing the command "pick" with the command "edit", you can tell git rebase to stop after applying that commit, so that you can edit the files and/or the commit message, amend the commit, and continue rebasing. 1184 | ``` 1185 | 1186 | 簡單說,reword 只可以修改 commit message,而 edit 不只可以修改 commit message ,還可以修改 files 內容。 1187 | 1188 | 先來看看下面這張圖 1189 | 1190 | ![alt tag](https://i.imgur.com/9j0JnKw.png) 1191 | 1192 | 這圖很明顯 add a.py -> add b.py -> add c.py -> add d.py ,現在我想在 add c.py 和 add d.py 中再加一個東西, 1193 | 1194 | 也就是變成 add a.py -> add b.py -> add c.py -> add c1.py -> add d.py 這樣。 1195 | 1196 | 增加一個 add c1.py 的情境時就可以使用 edit 了,( 以下我就不說那麼詳細了,我直接講重點 ), 1197 | 1198 | 先執行以下指令 ( 我們的目標是 a7ed6ff ,所以選他的上一個 commit id,也就是 f0a761d ) 1199 | 1200 | ```cmd 1201 | git rebase -i f0a761d 1202 | ``` 1203 | 1204 | 這次我們將 pick 修改成 e 或是 edit ( 如下圖 ) 1205 | 1206 | ![alt tag](https://i.imgur.com/bKrLIl3.png) 1207 | 1208 | 當你按下 ENTER 之後,你會看到下圖, 1209 | 1210 | ![alt tag](https://i.imgur.com/whkCzok.png) 1211 | 1212 | A 的部份是可以修改 commit message, 1213 | 1214 | B 的部份則是和你說當你修改 ( 滿足 ) 完畢,可以執行 `git rebase --continue`, 1215 | 1216 | A 的部份我們不做了,但我們現在來加工吧 ( 增加 c1.py ), 1217 | 1218 | 首先,我們建立一個 c1.py 檔案,然後 `git add c1.py`,接著 commit 他 ( 如下圖 ) 1219 | 1220 | ![alt tag](https://i.imgur.com/frYBUfT.png) 1221 | 1222 | 剛剛有說過了,當你滿足時,可執行 `git rebase --continue`,收工 1223 | 1224 | ![alt tag](https://i.imgur.com/sjnEn0H.png) 1225 | 1226 | 再用 log 確認一下,太神了 :satisfied: 成功加上去了 1227 | 1228 | ![alt tag](https://i.imgur.com/irECwLH.png) 1229 | 1230 | ### squash 1231 | 1232 | [Youtube Tutorial - git rebase interactive - squash fixup - PART 3](https://youtu.be/bfrZrbEHis0) 1233 | 1234 | ```cmd 1235 | # Commands: 1236 | # p, pick = use commit 1237 | # s, squash = use commit, but meld into previous commit 1238 | ``` 1239 | 1240 | 以下為官方的說明 1241 | 1242 | ```text 1243 | The suggested commit message for the folded commit is the concatenation of the commit messages of the first commit and of those with the "squash" command, 1244 | ``` 1245 | 1246 | 簡單說,你如果想要將多個 commit 合併成一個,使用 squash 就對了,( 以下我就不說那麼詳細了,我直接講重點 ), 1247 | 1248 | 這次的目標是要將 commit id fc45824 以及 commit id a7ed6ff 合併起來 ( 如下圖 ) 1249 | 1250 | ![alt tag](https://i.imgur.com/v8XwOTN.png) 1251 | 1252 | 先執行以下指令 1253 | 1254 | ```cmd 1255 | git rebase -i f0a761d 1256 | ``` 1257 | 1258 | 接著你會看到下圖,我們將 fc45824 這個 cmmit 的 pick 修改成 s 或 squash 1259 | 1260 | ( 他會合併他的前一個,也就是 a7ed6ff ) 1261 | 1262 | ![alt tag](https://i.imgur.com/rgWkvVp.png) 1263 | 1264 | ( 如果你要合併多個 commit,就多個都改成 s 或 squash, 注意, 有順序性 :exclamation: :exclamation: ) 1265 | 1266 | 將著按下 ENTER,會看到下圖 1267 | 1268 | ![alt tag](https://i.imgur.com/pB6yllA.png) 1269 | 1270 | 這時候他已經合併了這兩個 commit,我們就可以輸入新的 commit message, 1271 | 1272 | 這邊我們輸入 add c.py and c1.py 1273 | 1274 | ![alt tag](https://i.imgur.com/m9E6KUp.png) 1275 | 1276 | 再按 ENTER ( 成功 ) 1277 | 1278 | ![alt tag](https://i.imgur.com/X0O7I5H.png) 1279 | 1280 | 可以再用 log 確認一下,我們成功將兩個 commit 合併了 1281 | 1282 | ![alt tag](https://i.imgur.com/r53KIev.png) 1283 | 1284 | c.py 以及 c1.py 都存在,代表我們成功了 :satisfied: 1285 | 1286 | ![alt tag](https://i.imgur.com/WhkLDGa.png) 1287 | 1288 | ### fixup 1289 | 1290 | [Youtube Tutorial - git rebase interactive - squash fixup - PART 3](https://youtu.be/bfrZrbEHis0) 1291 | 1292 | ```cmd 1293 | # Commands: 1294 | # p, pick = use commit 1295 | # f, fixup = like "squash", but discard this commit's log message 1296 | ``` 1297 | 1298 | 以下為官方的說明 1299 | 1300 | ```text 1301 | omits the commit messages of commits with the "fixup" command. 1302 | ``` 1303 | 1304 | 其實這個和 squash 很像,通常如果我們要忽略一個 commit message 但保留 commit 的內容,我們就會使用 fixup, 1305 | 1306 | 目標,這邊我們想要移除 fc45824 的個 commit ( 但保留 commit 的內容 ) 1307 | 1308 | ![alt tag](https://i.imgur.com/AFrd0UA.png) 1309 | 1310 | 先執行以下指令 1311 | 1312 | ```cmd 1313 | git rebase -i f0a761d 1314 | ``` 1315 | 1316 | 將 fc45824 的 pick 修改成 f 或 fixup ( 如下圖 ) 1317 | 1318 | ( 他會移除 fc45824 這個 commit message ,但保留 commit 的內容 ) 1319 | 1320 | ![alt tag](https://i.imgur.com/aDH1y1n.png) 1321 | 1322 | 接著 ENTER,成功 rebase 1323 | 1324 | ![alt tag](https://i.imgur.com/BMs2h8r.png) 1325 | 1326 | 可以再用 log 確認一下,我們忽略了 add c1.py 這個 commit 1327 | 1328 | ![alt tag](https://i.imgur.com/bgYJa6T.png) 1329 | 1330 | 但是 c.py 以及 c1.py 都存在 ( 只忽略 commit message ), 1331 | 1332 | ![alt tag](https://i.imgur.com/tYrB3F9.png) 1333 | 1334 | 看到這裡,大家其實可以想一想 squash 和 fixup 真的非常類似, 1335 | 1336 | 只不過 squash 可以修改 commit message。 1337 | 1338 | 簡單一點,單純想要忽略某一個 commit message 時,使用 fixup, 1339 | 1340 | 想要合併 commit 並修改 commit message 時,使用 squash。 1341 | 1342 | ### exec 1343 | 1344 | [Youtube Tutorial - git rebase interactive - exec drop - PART 4](https://youtu.be/u8imRiiSyzk) 1345 | 1346 | ```cmd 1347 | # Commands: 1348 | # p, pick = use commit 1349 | # x, exec = run command (the rest of the line) using shell 1350 | ``` 1351 | 1352 | 以下為官方的說明 1353 | 1354 | ```text 1355 | You may want to check that your history editing did not break anything by running a test, or at least recompiling at intermediate points in history by using the "exec" command (shortcut "x") 1356 | ``` 1357 | 1358 | 這個功能我比較少用,但還是說一下,簡單說,就是他可以用來 check 你的 1359 | 1360 | rebase 改動是不是影響到整體 ( 用 exec command 確認 )。 1361 | 1362 | 聽不太懂 :question: 沒關係,假如我今天做了一大堆的 rabase 更動,但我想確認我這樣做了之後, 1363 | 1364 | 對整體是不是有影響,也就是可以在更動時,順便跑你的 test 去確認整體是正常 work。 1365 | 1366 | 還是聽不懂 :question: 也沒關係,我用一個範例給大家看 1367 | 1368 | ![alt tag](https://i.imgur.com/iu1bEOw.png) 1369 | 1370 | 如上圖,假如我想要在我更動中做一些 test 去確保我的更動不會影響整體, 1371 | 1372 | ( 雖然這邊都是 pick,也就是沒改動,但方便說明,大家請自行想像有改動 :sweat_smile: ) 1373 | 1374 | ![alt tag](https://i.imgur.com/2c9ycmS.png) 1375 | 1376 | A 的部份 echo "test sucess" 這個自然不用有問題, 1377 | 1378 | 但是 B 的部分就會出問題,因為根本沒有 error 這個指令, 1379 | 1380 | 當如果執行到 shell 有錯誤時,他會停下來,讓你修正, 1381 | 1382 | 如下圖,我們停在了 add c.py 這個 commit 上,因為接下來得 test error 了 1383 | 1384 | ![alt tag](https://i.imgur.com/yVB3naC.png) 1385 | 1386 | 這時候我們可以修正問題,修正完了之後,再執行 `git rebase --continue`。 1387 | 1388 | ![alt tag](https://i.imgur.com/YBD0d9V.png) 1389 | 1390 | 這個功能我想應該是讓你去邊修改邊跑你自己的 test,確保改動都正常。 1391 | 1392 | ### drop 1393 | 1394 | [Youtube Tutorial - git rebase interactive - exec drop - PART 4](https://youtu.be/u8imRiiSyzk) 1395 | 1396 | ```cmd 1397 | # Commands: 1398 | # p, pick = use commit 1399 | # d, drop = remove commit 1400 | ``` 1401 | 1402 | 以下為官方的說明 1403 | 1404 | ```text 1405 | To drop a commit, replace the command "pick" with "drop", or just delete the matching line. 1406 | ``` 1407 | 1408 | 這個就簡單多了,移除這個 commit ( 包含 commit 內容 ), 1409 | 1410 | 假設我們的 log 如下, 1411 | 1412 | ![alt tag](https://i.imgur.com/zz5arVp.png) 1413 | 1414 | 這次的目標是移除 f0a761d 和 980bd9a 和 1539219 這些 commit, 1415 | 1416 | 先執行以下指令 1417 | 1418 | ```cmd 1419 | git rebase -i 8f13aaa 1420 | ``` 1421 | 1422 | 將 pick 修改成 d 或 drop ( 如下圖 ) 1423 | 1424 | ![alt tag](https://i.imgur.com/Goc1LH1.png) 1425 | 1426 | 按 ENTER 之後,再用 log 確認一下, 1427 | 1428 | ![alt tag](https://i.imgur.com/u7z2Y3U.png) 1429 | 1430 | 從上圖可以發現,我們已經成功的移除 f0a761d 和 980bd9a 和 1539219 這些 commit, 1431 | 1432 | 並且也看到 commit 內容也都被移除了,只剩下 a.py 而已。 1433 | 1434 | ## git pull 補充 1435 | 1436 | 既然介紹完了 `git fetch` 以及 `git rebase` 之後,接下來我要再補充一些 `git pull` 額外的 options 參數 1437 | 1438 | ```cmd 1439 | git pull [] [ […​]] 1440 | ``` 1441 | 1442 | 更多詳細指令可參考 [https://git-scm.com/docs/git-pull#_options](https://git-scm.com/docs/git-pull#_options)。 1443 | 1444 | 這裡簡單整理一下, 1445 | 1446 | ```cmd 1447 | git pull = git fetch + git merge 1448 | git pull --rebase = git fetch + git rebase 1449 | ``` 1450 | 1451 | 在 [git-rebase](https://github.com/twtrubiks/Git-Tutorials#git-rebase) 中已經讓大家了解到使用 git-rebase 可以讓 code review 的人 1452 | 1453 | 看起來比較舒服,所以就使用 `git pull --rebase` 吧 ( 前提是你要知道你在幹嘛 :smile: )。 1454 | 1455 | 這邊我模擬 `git pull` 以及 `git pull --rebase` 的差異,順便加上衝突的情況,因為步驟蠻多的, 1456 | 1457 | 所以如果你想了解更多他的概念,請參考以下手把手教學, 1458 | 1459 | [Youtube Tutorial - git pull vs git pull --rebase](https://youtu.be/8h0K-2OaeSk) 1460 | 1461 | 使用 `git pull` 後的結果,code review 的人一定翻桌 ( 如下圖 ) :triumph: 1462 | 1463 | 這邊我有順便模擬衝突的時候,你會發現如果使用 `git pull` 會多一個 commit (也就是下方的 "fix conflict")。 1464 | 1465 | ![alt tag](https://i.imgur.com/CNgKR3y.png) 1466 | 1467 | 使用 `git pull --rebase` 後的結果,code review 的人表示溫馨 ( 如下圖 ) :innocent: 1468 | 1469 | 這邊我有順便模擬衝突的時候,你會發現如果使用 `git pull --rebase` 並不會像剛剛一樣多了一個 commit, 1470 | 1471 | 原因是因為當我們使用 `git pull --rebase` 造成衝突時,修好衝突的內容之後,git add xxxx,接著我們會 1472 | 1473 | 直接執行 `git rebase --continue`。 1474 | 1475 | ![alt tag](https://i.imgur.com/RKMo9ue.png) 1476 | 1477 | 假設今天你執行了 `git pull --rebase` 之後,發現很難受 :fearful:,想要取消, 1478 | 1479 | 直接執行 `git rebase --abort` 即可回到之前的狀態。 1480 | 1481 | 額外補充小技巧, 1482 | 1483 | * [Youtube Tutorial - git autostash 參數說明](https://youtu.be/kg2PyZr7l5k) 1484 | 1485 | 說明 `--autostash`, 1486 | 1487 | 一般來說, 如果我們工作到一半, 突然想要直接 `git pull --rebase`, 又不想 commit, 1488 | 1489 | 流程大約會像下面這樣 1490 | 1491 | ```cmd 1492 | git stash # 將目前的改動存進去 stash 中 1493 | git pull --rebase 1494 | git stash pop # 將之前的改動從 stash 中 pop 出來 1495 | # 如果有衝突再去解決衝突 1496 | ``` 1497 | 1498 | 但如果每次都要執行這麼多指令其實會有點煩 :sweat: 1499 | 1500 | 但可以透過一個參數來解決, 也就是 1501 | 1502 | `git pull --rebase --autostash` 1503 | 1504 | 以上這段指令基本上就是幫你執行了剛剛上面那一串的東西, 1505 | 1506 | 如果有衝突, 就再修正衝突即可 :smile: 1507 | 1508 | ## git-cherry-pick 1509 | 1510 | 看影片會更清楚,手把手帶大家動手做 [Youtube Tutorial - git-cherry-pick](https://youtu.be/x3UtKUvlDdI) 1511 | 1512 | git-cherry-pick 這個指令大家可能會比較陌生 :confused: 1513 | 1514 | 沒關係,我們先來看 [官方](https://git-scm.com/docs/git-cherry-pick) 的說明 1515 | 1516 | ```text 1517 | git-cherry-pick - Apply the changes introduced by some existing commits 1518 | ``` 1519 | 1520 | 看完官方說明還是 :question: :question: :question: 1521 | 1522 | 沒關係,我來假設一個情境 ( 理解完它你就了解了 git-cherry-pick 的用途了 ), 1523 | 1524 | 假設現在 master 分支的 log 如下圖 1525 | 1526 | ![alt tag](https://i.imgur.com/cMcn6yE.png) 1527 | 1528 | 然後有一個 v1 的分支 log 如下圖 1529 | 1530 | ![alt tag](https://i.imgur.com/OZ7JLke.png) 1531 | 1532 | 現在我希望 merge v1 分支中的 14dee93 - add d.py 這個 commit 1533 | 1534 | ( 因為 14dee93 這個 commit 實在太棒了或是因為某些原因只需要這個 commit ) 1535 | 1536 | 遇到上述這種情況,就很適合使用 git-cherry-pick,也就是說我想要其他分支中的某幾個 commit 而已, 1537 | 1538 | 不需要全部,換句話說,就是撿其他分支中的 commit 過來使用。 1539 | 1540 | 了解了適合的使用情境,接下來我們就來實戰 :smirk: 1541 | 1542 | 首先,我想要 v1 分支中的 14dee93 - add d.py 這個 commit, 1543 | 1544 | 所以我先切到 master 分支,接著執行 1545 | 1546 | ```cmd 1547 | git cherry-pick 14dee93 1548 | ``` 1549 | 1550 | 如果你想要一次撿很多的分支過來也是可以,直接使用空白隔開即可 1551 | 1552 | ```cmd 1553 | git cherry-pick 14dee93 xxxxxx xxxxxx xxxxxx xxxxx 1554 | ``` 1555 | 1556 | 如果你想一次撿一個區間的 commits, 可以使用以下的指令 1557 | 1558 | ```cmd 1559 | git cherry-pick A^..B 1560 | ``` 1561 | 1562 | (A 和 B 代表你的 commits id) 1563 | 1564 | 如果沒有衝突,就會看到如下圖 1565 | 1566 | ![alt tag](https://i.imgur.com/YITXxMk.png) 1567 | 1568 | 再觀看一下 master 的 log 1569 | 1570 | ![alt tag](https://i.imgur.com/iGEIDZL.png) 1571 | 1572 | 你會發現我們成功把 v1 分支中的 14dee93 - add d.py 這個 commit 拿過來 1573 | 1574 | 使用了,但現在它的 commit id 卻是 ab70429,這個是正常的,因為它需要 1575 | 1576 | 重新新計算 :smile: 1577 | 1578 | 其實,你會發現 git-cherry-pick 沒有想像中的困難 :satisfied: 1579 | 1580 | 在 cherry-pick 時,難免會遇到衝突,這邊我就再多做一個衝突的範例, 1581 | 1582 | 假設 master 的 log 如下 1583 | 1584 | ![alt tag](https://i.imgur.com/pttbQ5U.png) 1585 | 1586 | v1 分支中的 log 如下,我想要它的 3a2f29a - add c.py and print world 這個 commit 1587 | 1588 | ![alt tag](https://i.imgur.com/RFibHS6.png) 1589 | 1590 | v2 分支中的 log 如下,我想要它的 553587b - add f.py 這個 commit 1591 | 1592 | ![alt tag](https://i.imgur.com/I6L2Fwq.png) 1593 | 1594 | 接下來我們就切回 master,然後 cherry-pick 這兩個 commit, 1595 | 1596 | 這時候你會發現,它衝突了 :fearful: 1597 | 1598 | ![alt tag](https://i.imgur.com/fAtQET0.png) 1599 | 1600 | 使用 `git status` 看一下狀態,其實 A 的部分都教你如何解衝突了 1601 | 1602 | ![alt tag](https://i.imgur.com/J8ZpPng.png) 1603 | 1604 | 首先,我們先將 c.py 修正後,執行 `git add c.py`,接著再按照 A 的部份 1605 | 1606 | 執行 `git cherry-pick --continue`,就時候會跳出一個編輯視窗, 1607 | 1608 | ![alt tag](https://i.imgur.com/giylVAL.png) 1609 | 1610 | 輸入完 commit message 之後,再輸入 `wq`,就會看到下圖 1611 | 1612 | ![alt tag](https://i.imgur.com/rA8wMbO.png) 1613 | 1614 | 最後,再觀看 log, 1615 | 1616 | ![alt tag](https://i.imgur.com/lEP648c.png) 1617 | 1618 | 我們成功將我們要的 commit merge 到我們的 master 分支上了 :kissing_smiling_eyes: 1619 | 1620 | 想了解更多的使用方法,可參考官方文件 1621 | [https://git-scm.com/docs/git-cherry-pick](https://git-scm.com/docs/git-cherry-pick)。 1622 | 1623 | ## git revert 1624 | 1625 | 假設我 commit history 為 A1 -> A2 -> A3 -> A4 -> A5 -> A6 1626 | 1627 | 我現在想要回 A4 這個 commit , 這時候我就可以使用 git revert !! 1628 | 1629 | 先 revert A6 1630 | 1631 | ```cmd 1632 | git revert A6 1633 | ``` 1634 | 1635 | 再 revert A5 1636 | 1637 | ```cmd 1638 | git revert A5 1639 | ``` 1640 | 1641 | 假如你再看現在的 commit history , 他會長的像這樣 1642 | 1643 | A1 -> A2 -> A3 -> A4 -> A5 -> A6 -> A6_revert -> A5_revert 1644 | 1645 | 這時候,其實你的 commit 就是在 A4 這個位置 。 1646 | 1647 | 使用 git revert 的好處,就是可以保留 commit history, 萬一你又後悔了, 1648 | 1649 | 也可以在 revert 回去。 1650 | 1651 | 如果你想要 revert 最新的 commit, 只需要使用 HEAD 1652 | 1653 | ```cmd 1654 | git revert HEAD 1655 | ``` 1656 | 1657 | ## 解決衝突 1658 | 1659 | 在進行合併的時候,有時候會顯示出 **衝突conflicts** ,這時候就必須手動解決衝突後再送出。 1660 | 1661 | 通常我目前最容易遇到衝突 conflicts ,就是使用 pull 這個指令的時候 1662 | 1663 | ![alt tag](https://i.imgur.com/Eph0Vw1.jpg) 1664 | 1665 | 仔細看這張圖,如果使用**pull**這個指令,會幫你 **自動 merge** ( 如圖裡的 Auto-merging Hello.py ), 1666 | 1667 | 然後接著看 CONFLICT ( content ) : Merge conflict in Hello.py ,又說 Automatic merge failed, 1668 | 1669 | 就是告訴你, Hello.py 這個檔案有衝突,然後你必須手動下去解決衝突。 1670 | 1671 | git status 可以告訴我們衝突的文件。 1672 | 1673 | ![alt tag](https://i.imgur.com/vlVcXn8.jpg) 1674 | 1675 | 打開衝突文件我們會看到 Git 用 <<<<<<<,=======,>>>>>>> 標記出不同分支的內容,我們修改完畢後再提交: 1676 | 1677 | ![alt tag](https://i.imgur.com/rlPOaxn.jpg) 1678 | 1679 | 通常我們會手動下去修改衝突 conflicts,然後再加個 commit 1680 | 1681 | ```cmd 1682 | git add Hello.py 1683 | git commit -m "conflict fixed" 1684 | ``` 1685 | 1686 | ### 假設今天我們想要放棄這個 merge 我們該怎麼做呢 ? 1687 | 1688 | ```cmd 1689 | git merge --abort 1690 | ``` 1691 | 1692 | 或 1693 | 1694 | ```cmd 1695 | git reset --hard HEAD 1696 | ``` 1697 | 1698 | 可以取消這次的 merge 回到 merge 前。 1699 | 1700 | ## git stash 指令 1701 | 1702 | * [Youtube Tutorial - git stash 指令](https://youtu.be/CN065MNHtMY) 1703 | 1704 | 很多時候,我們正在開發一個新功能又或是 debug,然後突然有一個功能需要緊急修正, 1705 | 1706 | 但你又不想 commit 現在的狀況,因為根本沒意義,事情只做了一半,這時候 **stash** 1707 | 1708 | 這個實用的指令就派上用場了。 1709 | 1710 | 舉個例子,假設我們改了 A.py 和 B.py 這兩個檔案 1711 | 1712 | ![alt tag](https://i.imgur.com/7xX0T1T.jpg) 1713 | 1714 | 然後,現在突然有一個 bug 必須馬上(立刻)處理, 1715 | 1716 | 但是,啊我手上的事情還沒做完阿~~~~ 1717 | 1718 | 這時候,可以利用以下指令 1719 | 1720 | ```cmd 1721 | git stash 1722 | ``` 1723 | 1724 | ![alt tag](https://i.imgur.com/cYCH8mV.jpg) 1725 | 1726 | 假如你想要更清楚自己這次的 stash 原因是什麼,或是這是正在開發什麼功能 1727 | 可以使用以下指令 1728 | 1729 | 範例 1730 | 1731 | ```cmd 1732 | git stash save "我是註解" 1733 | ``` 1734 | 1735 | ```cmd 1736 | git stash save -u "feature" 1737 | ``` 1738 | 1739 | 參數說明 1740 | 1741 | `-u` | `--include-untracked` 1742 | 1743 | `-a` | `--all` 1744 | 1745 | ![alt tag](https://i.imgur.com/nGS11Px.jpg) 1746 | 1747 | 接下來你可以使用 status 指令,你會發現變乾淨了 1748 | 1749 | ![alt tag](https://i.imgur.com/Xf53GfM.jpg) 1750 | 1751 | 並且可以使用下列的指令來觀看 stash 裡面的東西 1752 | 1753 | ```cmd 1754 | git stash list 1755 | ``` 1756 | 1757 | ![alt tag](https://i.imgur.com/jQPiYiX.jpg) 1758 | 1759 | 然後你很努力地解決這個 bug,commit 完之後, 1760 | 可以再使用下列的指令把 stash 取回來,這指令取回後也會刪除 stash 1761 | 1762 | ```cmd 1763 | git stash pop 1764 | ``` 1765 | 1766 | 假設今天你有很多的 stash,你可以指定,如下 (選自己喜歡的用法) 1767 | 1768 | ```cmd 1769 | git stash pop 0 1770 | git stash pop stash@{0} 1771 | ``` 1772 | 1773 | ![alt tag](https://i.imgur.com/zVF7no2.jpg) 1774 | 1775 | 你會發現剛剛的東西回來了~ 1776 | 1777 | 如果你希望使用 stash 取回之後,不希望刪除 stash ,可以使用下列的指令 1778 | 1779 | ```cmd 1780 | git stash apply 1781 | ``` 1782 | 1783 | 如下圖,你可以發現取回後, stash 並沒有被刪除 1784 | 1785 | ![alt tag](https://i.imgur.com/w3Ip3iW.jpg) 1786 | 1787 | 如果你只是想要刪除暫存,可以使用下列的指令 1788 | 1789 | ```cmd 1790 | git stash clear 1791 | ``` 1792 | 1793 | 從下圖可以發現,stash 裡面的東西被我們刪除了 1794 | 1795 | ![alt tag](https://i.imgur.com/PvzufbQ.jpg) 1796 | 1797 | 如果你想丟棄指定的 stash,可以使用 (選自己喜歡的用法) 1798 | 1799 | ```cmd 1800 | git stash drop 0 1801 | git stash drop stash@{0} 1802 | ``` 1803 | 1804 | ## git tag 1805 | 1806 | [Youtube Tutorial - git tag 教學](https://youtu.be/azciLlpr3Gs) 1807 | 1808 | 查看 tag 1809 | 1810 | ```cmd 1811 | git tag 1812 | ``` 1813 | 1814 | ![alt tag](https://i.imgur.com/8f6zGfm.png) 1815 | 1816 | 指定關鍵字 1817 | 1818 | ```cmd 1819 | git tag -l "v1.*" 1820 | ``` 1821 | 1822 | `-l` `--list` 1823 | 1824 | git tag 有 輕量級標籤(lightweight tag) 和 附註標籤(annotated tag). 1825 | 1826 | 輕量級標籤(lightweight tag) 1827 | 1828 | 如果想要建立一個輕量級的標籤,請不要指定 `-a` `-s`(GPG-signed) `-m` 1829 | 1830 | ```cmd 1831 | git tag tag_name [commit_id] 1832 | ``` 1833 | 1834 | 如果只使用 `git tag tag_name` , 而沒加上後面 Commit id, 1835 | 1836 | 則會自動把 tag 放在目前的這個 Commit id 上. 1837 | 1838 | 顯示註解 1839 | 1840 | ```cmd 1841 | git show v1.1-light 1842 | ``` 1843 | 1844 | 附註標籤(annotated tag) 1845 | 1846 | ```cmd 1847 | git tag -a v1.1 -m "version 1.1" 1848 | ``` 1849 | 1850 | `-a` 就是標籤名稱 `--annotate` 1851 | 1852 | `-m` 代表該標籤說明(註解) 1853 | 1854 | 在指定的 commit 上設 tag 1855 | 1856 | ```cmd 1857 | git tag -a v1.2 -m "version 1.1" [commit_id] 1858 | ``` 1859 | 1860 | 顯示註解 1861 | 1862 | ```cmd 1863 | git show v1.1 1864 | ``` 1865 | 1866 | 輕量級標籤(lightweight tag) 和 附註標籤(annotated tag) 的差別就是是否能看到更多的細節, 1867 | 1868 | 附註標籤(annotated tag) 多了更多的資訊. 1869 | 1870 | 輕量級標籤(lightweight tag) 如下 1871 | 1872 | ![alt tag](https://i.imgur.com/4cUkdyQ.png) 1873 | 1874 | 附註標籤(annotated tag) 如下 1875 | 1876 | ![alt tag](https://i.imgur.com/DQWB1uh.png) 1877 | 1878 | 當你執行 `git push` 預設是不會將 tag 推到 remote. 1879 | 1880 | 需要執行以下的指令, push tag 到 remote 端 1881 | 1882 | ```cmd 1883 | git push origin [tagname] 1884 | ``` 1885 | 1886 | 一次 push 很多 tags (將會把你全部不在 remote 端的 tag 都 push 上去.) 1887 | 1888 | ```cmd 1889 | git push origin --tags 1890 | ``` 1891 | 1892 | 當其他人執行 `git clone` 或 `git fetch` 就可以拿到這些 tags. 1893 | 1894 | 移除本地 tag 1895 | 1896 | ```cmd 1897 | git tag -d [tagname] 1898 | ``` 1899 | 1900 | 刪除 remote tag 1901 | 1902 | ```cmd 1903 | git push --delete origin [tagname] 1904 | ``` 1905 | 1906 | ## git show 1907 | 1908 | 一般來說,我只用他來看這個 commit 修改了哪些東西 1909 | 1910 | ```cmd 1911 | git show 1912 | ``` 1913 | 1914 | ![alt tag](https://i.imgur.com/rjpl8VL.png) 1915 | 1916 | ```cmd 1917 | git show [] […​] 1918 | ``` 1919 | 1920 | 其他更詳細的介紹,請參考 [https://git-scm.com/docs/git-show](https://git-scm.com/docs/git-show) 1921 | 1922 | ## git diff 1923 | 1924 | 以下為官方說明 1925 | 1926 | ```text 1927 | Show changes between commits, commit and working tree, etc 1928 | ``` 1929 | 1930 | 這邊舉幾個例子, 1931 | 1932 | 檔案還沒進入暫存區 ( Stage ),也就是執行 git add xxx 之前, 1933 | 1934 | 可以看做了那些修改, 1935 | 1936 | ![alt tag](https://i.imgur.com/nj5Gz5P.png) 1937 | 1938 | 也可以看 commits 之間的差異 1939 | 1940 | ![alt tag](https://i.imgur.com/JMJ48jO.png) 1941 | 1942 | 其他更詳細的介紹,請參考 [https://git-scm.com/docs/git-diff](https://git-scm.com/docs/git-diff) 1943 | 1944 | ## git diff-tree 1945 | 1946 | git-diff-tree - Compares the content and mode of blobs found via two tree objects 1947 | 1948 | 比較兩個 blob (commit) 的差異. 1949 | 1950 | 文件可參考 [git-diff-tree](https://git-scm.com/docs/git-diff-tree/en) 1951 | 1952 | 直接看範例 1953 | 1954 | ```cmd 1955 | git diff-tree -r --no-commit-id --name-status -a --diff-filter=ACDMRT <其中一個commit id> <要比較的commit-id> > changes.txt 1956 | ``` 1957 | 1958 | `-r` 代表 Recurse into sub-trees. 1959 | 1960 | `--no-commit-id` This flag suppressed the commit ID output. 1961 | 1962 | `--name-status` Show only the name(s) and status of each changed file. 1963 | 1964 | `--text` `-a` Treat all files as text. 1965 | 1966 | `--diff-filter=[(A|C|D|M|R|T|U|X|B)…​[*]]` 1967 | 1968 | Added (A), Copied (C), Deleted (D), Modified (M), Renamed (R), 1969 | 1970 | have their type (i.e. regular file, symlink, submodule, …​) 1971 | 1972 | changed (T), are Unmerged (U), are Unknown (X), 1973 | 1974 | or have had their pairing Broken (B). 1975 | 1976 | 執行後打開 change.txt 會看到差異的檔案名稱. 1977 | 1978 | ```text 1979 | M addons/account/i18n/account.pot 1980 | M addons/account/i18n/ar.po 1981 | M addons/account/i18n/az.po 1982 | M addons/account/i18n/be.po 1983 | M addons/account/i18n/bg.po 1984 | M addons/account/i18n/ca.po 1985 | M addons/account/i18n/cs.po 1986 | ...... 1987 | ``` 1988 | 1989 | ## git archive 1990 | 1991 | 延續上面的例子, 如果想要打包不同的 commit 之間的差異檔案 (不想要整包匯出, 因為太大了, 只想找出差異檔案), 1992 | 1993 | 這時候可以搭配 archive 指令, 範例如下 1994 | 1995 | ```cmd 1996 | git archive --format=zip --output=files-diff.zip HEAD $(git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT <其中一個commit id> <要比較的commit-id>) 1997 | ``` 1998 | 1999 | 這樣匯出來的 zip, 就是這兩個 commit 之間差異的完整檔案. 2000 | 2001 | ## git grep 2002 | 2003 | 以下為官方說明 2004 | 2005 | ```text 2006 | git-grep - Print lines matching a pattern 2007 | ``` 2008 | 2009 | 簡單說,就是可以幫你找出符合的 pattern,舉個例子,我希望找出內容 2010 | 2011 | 有包含 hello 這個 pattern 的檔案,這時候,就可以執行以下指令 2012 | 2013 | ```cmd 2014 | git grep "hello" 2015 | ``` 2016 | 2017 | ![alt tag](https://i.imgur.com/t5vxvvp.png) 2018 | 2019 | 會顯示出該 pattern 在個檔案以及哪段程式碼有用到。 2020 | 2021 | 其他更詳細的介紹,請參考 [https://git-scm.com/docs/git-grep](https://git-scm.com/docs/git-grep) 2022 | 2023 | ## git clean 2024 | 2025 | 刪除未被追蹤的檔案, 2026 | 2027 | `git clean -n` 2028 | 2029 | `-n, --dry-run` Don’t actually remove anything, just show what would be done 2030 | 2031 | 這個指定是告訴你會刪除哪些資料, 不會真的刪除. 2032 | 2033 | 範例如下, 2034 | 2035 | ```cmd 2036 | ❯ git status 2037 | On branch master 2038 | 2039 | No commits yet 2040 | 2041 | Untracked files: 2042 | (use "git add ..." to include in what will be committed) 2043 | test.py 2044 | 2045 | nothing added to commit but untracked files present (use "git add" to track) 2046 | 2047 | ❯ git clean -n 2048 | Would remove test.py 2049 | ``` 2050 | 2051 | 如果你執行以下的指令, 就會真的刪除, 2052 | 2053 | `git clean -df` 2054 | 2055 | 詳細說明可使用 `git clean --help` 觀看, 2056 | 2057 | 範例如下, 2058 | 2059 | ```cmd 2060 | ❯ git status 2061 | On branch master 2062 | 2063 | No commits yet 2064 | 2065 | Untracked files: 2066 | (use "git add ..." to include in what will be committed) 2067 | test.py 2068 | 2069 | nothing added to commit but untracked files present (use "git add" to track) 2070 | 2071 | ❯ git clean -df 2072 | Removing test.py 2073 | 2074 | ❯ git status 2075 | On branch master 2076 | 2077 | No commits yet 2078 | 2079 | nothing to commit (create/copy files and use "git add" to track) 2080 | ``` 2081 | 2082 | 還記得前面介紹的 `git reset` 指令嗎, 基本上它可以搭配 `git clean` 一起使用, 2083 | 2084 | `git clean` 影響沒有被 track 的檔案 2085 | 2086 | `git reset` 影響有被 track 的檔案 2087 | 2088 | 結合以上, 可以回到一個指定的 commit 乾淨的狀態, 2089 | 2090 | ```cmd 2091 | git reset --hard HEAD 2092 | git clean -df 2093 | git status 2094 | ``` 2095 | 2096 | 建議大家自己操作一下. 2097 | 2098 | ## git Submodule 2099 | 2100 | 由於這個內容稍微比較多,所以我另外寫了一篇, 2101 | 2102 | * [Youtube Tutorial PART 1 - git Submodule tutorial - how to create submodule](https://youtu.be/IDMWLJCbCGo) 2103 | 2104 | * [Youtube Tutorial PART 2 - git Submodule tutorial - how to update submodule](https://youtu.be/ogZoZOVyAYI) 2105 | 2106 | * [Youtube Tutorial PART 3 - git Submodule tutorial - how to clone submodule](https://youtu.be/f5_O5Iu6pJo) 2107 | 2108 | * [Youtube Tutorial PART 4 - git Submodule tutorial - how to remove submodule](https://youtu.be/imndFN7AvFA) 2109 | 2110 | [git Submodule tutorial :memo:](https://github.com/twtrubiks/Git-Tutorials/blob/master/git_submodule_turorial.md) 2111 | 2112 | ## git Subtree 2113 | 2114 | 由於這個內容稍微比較多,所以我另外寫了一篇, 2115 | 2116 | * [Youtube Tutorial PART 1 - git subtree tutorial - how to create subtree](https://youtu.be/kEvgK2gH_vg) 2117 | 2118 | * [Youtube Tutorial PART 2 - git subtree tutorial - how to push subtree](https://youtu.be/Df3zc1VOqN8) 2119 | 2120 | * [Youtube Tutorial PART 3 - git subtree tutorial - how to pull/create subtree](https://youtu.be/dE-D2yrD4ws) 2121 | 2122 | [git subtree tutorial :memo:](https://github.com/twtrubiks/Git-Tutorials/blob/master/git_subtree_turorial.md) 2123 | 2124 | ## git 其他設定 2125 | 2126 | 我們已經設定了 user.name 以及 user.email ,但 Git 上其實還有很多可設定的東西 2127 | 2128 | 有時候,我們必須把某些檔案 ( 文件夾 ) 放到 Git 工作目錄中,但又不能提交它們, 2129 | 2130 | 像是密碼設定或是編譯器 IDE 產生出來的東西之類的, 2131 | 2132 | 每次 git status 都會看到紅紅的 Untracked files ,通常會覺得有點煩...... 2133 | 2134 | 這問題 Git 也幫我們想過,只要在 Git 工作區的根目錄下新建一個特殊的 **.gitignore** 文件 , 2135 | 2136 | 然後把要忽略的文件 ( 檔案 ) 名稱輸入進去, Git 就會自動忽略這些文件。 2137 | 2138 | 當然不需要自己從頭寫 .gitignore 文件, GitHub 已經幫我們準備了一些文件 [gitignore](https://github.com/github/gitignore) 2139 | 2140 | **.gitignore** 檔案直接放在目錄底下即可 2141 | 2142 | ![alt tag](https://i.imgur.com/8rHPsII.jpg) 2143 | 2144 | ### .gitignore 檔案格式範例 2145 | 2146 | ![alt tag](https://i.imgur.com/W3cxk9r.jpg) 2147 | 2148 | ### .gitignore (Temporarily and Permanently) 2149 | 2150 | 主要分 暫時(Temporarily) 和 永久(Permanently) 的ignore, 2151 | 2152 | * Temporarily ignore 2153 | 2154 | 適合使用在 settings 的檔案,有時候我們在開發的時候,都會有自己的設定, 2155 | 2156 | 但這個設定未必是大家都需要的,這時候就可以暫時先忽略這個檔案的改變。 2157 | 2158 | 暫時忽略某個檔案 2159 | 2160 | ```cmd 2161 | git update-index --skip-worktree 2162 | ``` 2163 | 2164 | 恢復(Resume)暫時忽略某個檔案 2165 | 2166 | ```cmd 2167 | git update-index --no-skip-worktree 2168 | ``` 2169 | 2170 | * Permanently ignore 2171 | 2172 | 這邊補充一個情境,假設今天 file 這個檔案已經被 commit 到 git 中了, 2173 | 2174 | 但是我想把他加入 .gitignore,這樣該怎麼辦 :question: 2175 | 2176 | 如果你在 .gitignore 中加入 file,你會發現還是沒有被 ignore :confused: 2177 | 2178 | ![alt tag](https://i.imgur.com/o922paa.png) 2179 | 2180 | 這時候,正確的做法應該是要先執行已下指令, 2181 | 2182 | ```cmd 2183 | git rm --cached 2184 | ``` 2185 | 2186 | 執行完後再 commit 即可 ( 檔案不會從系統上刪除,只是要更新 git 的 index 而已 ) 2187 | 2188 | ![alt tag](https://i.imgur.com/RJZ08OQ.png) 2189 | 2190 | 這時候可以再嘗試更新 file 的內容,你會發現它成功被 ignore 了 :smile: 2191 | 2192 | ### git alias 2193 | 2194 | 有時候常常手殘 key 錯指令或是記不起來 2195 | 2196 | 如果我們打 git st 就表示 git status 那該有多棒!!! 2197 | 2198 | 所以我們可以自己設定,讓 Git 以後打 **git st = git status** 2199 | 如下圖,原本不能使用 git st ,設定完之後就可以使用了。 2200 | 2201 | ```cmd 2202 | git config --global alias.st status 2203 | ``` 2204 | 2205 | ![alt tag](https://i.imgur.com/4NNasgB.jpg) 2206 | 2207 | ```cmd 2208 | git config --global alias.br branch 2209 | ``` 2210 | 2211 | ![alt tag](https://i.imgur.com/NIc71AO.jpg) 2212 | 2213 | ```cmd 2214 | git config --global alias.ck checkout 2215 | ``` 2216 | 2217 | ```cmd 2218 | git config --global alias.sw switch 2219 | ``` 2220 | 2221 | ```cmd 2222 | git config --global alias.cm commit 2223 | ``` 2224 | 2225 | ```cmd 2226 | git config --global alias.lg "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative" 2227 | ``` 2228 | 2229 | 將前面這一大串變成一個別名,這樣以後只需要執行 `git lg` 即可, 2230 | 2231 | ![alt tag](https://i.imgur.com/IvQLsMR.png) 2232 | 2233 | 可能有人會問,那這個設定檔文件在哪裡呢? 2234 | 2235 | 通常會在你的使用者底下,例如我這台電腦使用者為 HJ,設定檔文件就會在 **C:\Users\HJ** 底下, 2236 | 2237 | 他是一個 **隱藏文件.gitconfig** ,打開他的話格式如下。 2238 | 2239 | ![alt tag](https://i.imgur.com/iXjIqv9.jpg) 2240 | 2241 | 不知道大家有沒有注意到 `--global` 這個參數,他代表的意思是全域的,如果說你今天是執行 2242 | 2243 | ```cmd 2244 | git config alias.stu status 2245 | ``` 2246 | 2247 | 代表只有在該目錄底下時才會有作用。 2248 | 2249 | 那這個有什麼用呢? 試想一種情境,假設你在特定的資料夾底下,想要使用特定的信箱去 push,而其他的資料夾, 2250 | 2251 | 則一樣使用公司的信箱,這時候,就非常適合使用這種方法完成。 2252 | 2253 | 更多資訊細節可使用以下命令查看 2254 | 2255 | ```cmd 2256 | man git-config 2257 | ``` 2258 | 2259 | ### git 更新 2260 | 2261 | ```cmd 2262 | sudo add-apt-repository ppa:git-core/ppa 2263 | sudo apt-get update 2264 | sudo apt-get install git 2265 | ``` 2266 | 2267 | ![alt tag](https://i.imgur.com/WrQNZln.png) 2268 | 2269 | ## 使用 Git 一次 Push 到多個不同的遠端 ( remote ) 2270 | 2271 | 假如有一天 github 掛了,這樣是不是就不能 work 了,你可能會說本地端還有 ? 2272 | 2273 | 但......多備份絕對是好事 !! 再這裡介紹如何一次 Push 到多個不同的遠端 ( remote ) 2274 | 2275 | 這裡用 [Bitbucket](https://bitbucket.org/product) 當作範例 2276 | 2277 | 先使用下方指令查看 2278 | 2279 | ```cmd 2280 | git remote -v 2281 | ``` 2282 | 2283 | ![alt tag](https://i.imgur.com/Qb5VHoP.png) 2284 | 2285 | git remote 這個指令的更多說明可參考官方文件 [git-remote](https://git-scm.com/docs/git-remote)。 2286 | 2287 | 接著我們使用下列指令新增一個 origin 的遠端 2288 | 2289 | ```cmd 2290 | git remote set-url --add origin 2291 | ``` 2292 | 2293 | ```cmd 2294 | git remote set-url --add origin git@github.com:twtrubiks/test2.git 2295 | ``` 2296 | 2297 | ![alt tag](https://i.imgur.com/FKzexVE.png) 2298 | 2299 | 我們再用 git remote -v 查看一次,你會發現多了剛剛新增的遠端 ( remote ) 2300 | 2301 | ![alt tag](https://i.imgur.com/p1q7C4b.png) 2302 | 2303 | 最後我們再 push 2304 | 2305 | ![alt tag](https://i.imgur.com/6VKh8Bz.png) 2306 | 2307 | 仔細看,是不是一次 push 到多個不同的遠端 ( remote ),非常方便!! 2308 | 2309 | ***GitHub*** 2310 | 2311 | ![alt tag](https://i.imgur.com/JljPJHJ.png) 2312 | 2313 | ***Bitbucket*** 2314 | 2315 | ![alt tag](https://i.imgur.com/rkYHNl4.png) 2316 | 2317 | P.S 設定檔在資料夾底下的隱藏檔 ".git" 底下,裡面有一個 config 2318 | 2319 | ![alt tag](https://i.imgur.com/41xb8eu.png) 2320 | 2321 | 補充幾個 git remote 的指令,他也支援 rename 以及 remove , 2322 | 2323 | 現在的 remote 如下, 2324 | 2325 | ![alt tag](https://i.imgur.com/rr9SE3g.png) 2326 | 2327 | 讓我們重新命名 remote,語法如下, 2328 | 2329 | ```text 2330 | git remote rename 2331 | ``` 2332 | 2333 | ```text 2334 | git remote rename origin2 origin 2335 | ``` 2336 | 2337 | 執行後,你會發現 remote 成功被修改成 origin 了, 2338 | 2339 | ![alt tag](https://i.imgur.com/ixP1H7Z.png) 2340 | 2341 | 接下來我們試試 remove,語法如下, 2342 | 2343 | ```text 2344 | git remote remove 2345 | ``` 2346 | 2347 | ```text 2348 | git remote remove origin 2349 | ``` 2350 | 2351 | 成功刪除,現在 remote 是空的了, 2352 | 2353 | ![alt tag](https://i.imgur.com/OQFRWDg.png) 2354 | 2355 | 接下來我們嘗試新增一個 remote,指令如下, 2356 | 2357 | ```text 2358 | git remote add [-t ] [-m ] [-f] [--[no-]tags] [--mirror=] 2359 | ``` 2360 | 2361 | ```cmd 2362 | git remote add origin git@github.com:blue-rubiks/t11.git 2363 | ``` 2364 | 2365 | ![alt tag](https://i.imgur.com/cKsiBBs.png) 2366 | 2367 | 如果我們想修改 origin 的 url,可以使用 2368 | 2369 | ```cmd 2370 | git remote set-url origin git@blue.github.com:blue-rubiks/t11.git 2371 | ``` 2372 | 2373 | ![alt tag](https://i.imgur.com/LJICTNM.png) 2374 | 2375 | ## Multiple SSH Keys settings for different github account 2376 | 2377 | * [Youtube Tutorial - Multiple SSH Keys settings for different github account](https://youtu.be/gDxG-4tF7B8) 2378 | 2379 | [Multiple SSH Keys settings for different github account](https://github.com/twtrubiks/Git-Tutorials/blob/master/Multiple_SSH_Keys_settings.md) 2380 | 2381 | ## Git-Flow 基本教學以及概念 2382 | 2383 | * [Git-Flow Tutorials - youtube](https://youtu.be/zXlta66thZY) 2384 | 2385 | * [Git-Flow SmartGit Tutorials - youtube](https://youtu.be/ualXHytifbg) 2386 | 2387 | [Git-Flow 基本教學以及概念](https://github.com/twtrubiks/Git-Tutorials/tree/master/Git-Flow) 2388 | 2389 | ## PR (Pull Request) 教學 2390 | 2391 | * [Youtube Tutorial - github PR (Pull Request) 教學](https://youtu.be/bXOdD-bKfkA) - [文章快速連結](https://github.com/twtrubiks/Git-Tutorials/tree/master/pr-tutorial#github-pr-pull-request-%E6%95%99%E5%AD%B8) 2392 | 2393 | * [Youtube Tutorial - github CLI PR 教學 - gh](https://youtu.be/AD8X11lq3gQ) - [文章快速連結](https://github.com/twtrubiks/Git-Tutorials/tree/master/pr-tutorial#github-cli-pr-%E6%95%99%E5%AD%B8) 2394 | 2395 | [PR (Pull Request) 教學](https://github.com/twtrubiks/Git-Tutorials/tree/master/pr-tutorial) 2396 | 2397 | ## Linux 注意事項 2398 | 2399 | * [Youtube Tutorial - Linux 教學 - git 乎略 file mode (chmod) 改變](https://youtu.be/QCh2k903Yak) 2400 | 2401 | 這邊是和大家說一些同時在 windows 以及 linux 底下使用 git 可能會遇到的問題. 2402 | 2403 | 首先, 在 linux 底下執行以下指令 2404 | 2405 | ```cmd 2406 | sudo chmod -R 777 folder 2407 | ``` 2408 | 2409 | git 會默認它為改變, 要怎麼把它忽略呢 ? 請執行以下指令 , 2410 | 2411 | ```cmd 2412 | git config core.fileMode false 2413 | ``` 2414 | 2415 | 也可參考這篇文章 [Git ignore file mode (chmod) changes](https://stackoverflow.com/questions/1580596/how-do-i-make-git-ignore-file-mode-chmod-changes) 2416 | 2417 | ### 格式化 2418 | 2419 | `core.autocrlf` 2420 | 2421 | Windows 使用 Enter (Carriage Return 簡寫為 CR) 和 換行(Line Feed 簡寫為 LF) 這兩個字元來定義換行, 2422 | 2423 | 而 Mac 和 Linux 只使用一個換行 (Line Feed 簡寫為 LF) 字元. 2424 | 2425 | 所以會導致跨平台協作時出問題. 2426 | 2427 | 在 windows 上可以這樣設定 ( 代表 LF 會被轉換成 CRLF) 2428 | 2429 | ```cmd 2430 | git config --global core.autocrlf true 2431 | ``` 2432 | 2433 | Linux 或 Mac 系統 2434 | 2435 | ```cmd 2436 | git config --global core.autocrlf input 2437 | ``` 2438 | 2439 | 以上這樣設定, 會在 Windows 上保留 CRLF,而在 Mac 和 Linux 以及 repo 中保留 LF. 2440 | 2441 | 如果你想更深入的了解, 可參考 [格式化-core.autocrlf](https://git-scm.com/book/zh-tw/v1/Git-客製化-Git-設定#格式化與空格). 2442 | 2443 | ### 修改 editor 2444 | 2445 | ```cmd 2446 | git config --global core.editor "vim" 2447 | ``` 2448 | 2449 | ## Reference 2450 | 2451 | * [13 Git tips for Git's 13th birthday](https://opensource.com/article/18/4/git-tips) 2452 | 2453 | ## Donation 2454 | 2455 | 文章都是我自己研究內化後原創,如果有幫助到您,也想鼓勵我的話,歡迎請我喝一杯咖啡 :laughing: 2456 | 2457 | 綠界科技ECPAY ( 不需註冊會員 ) 2458 | 2459 | ![alt tag](https://payment.ecpay.com.tw/Upload/QRCode/201906/QRCode_672351b8-5ab3-42dd-9c7c-c24c3e6a10a0.png) 2460 | 2461 | [贊助者付款](http://bit.ly/2F7Jrha) 2462 | 2463 | 歐付寶 ( 需註冊會員 ) 2464 | 2465 | ![alt tag](https://i.imgur.com/LRct9xa.png) 2466 | 2467 | [贊助者付款](https://payment.opay.tw/Broadcaster/Donate/9E47FDEF85ABE383A0F5FC6A218606F8) 2468 | 2469 | ## 贊助名單 2470 | 2471 | [贊助名單](https://github.com/twtrubiks/Thank-you-for-donate) 2472 | -------------------------------------------------------------------------------- /README_en.md: -------------------------------------------------------------------------------- 1 | [中文版](README.md) 2 | 3 | # Git-Tutorials Basic Usage Guide :memo: 4 | 5 | I found this stuff quite interesting, so I wrote a simple tutorial to document it :memo:, hoping to help those who want to learn :smile: 6 | 7 | If there are any mistakes in the tutorial, please feel free to correct me :sweat_smile: 8 | 9 | For basic commands and installation, you can refer to a video I made earlier: 10 | 11 | * [Youtube Tutorial - Basic GitHub Tutorial - From Scratch](https://www.youtube.com/watch?v=py3n6gF5Y00) 12 | 13 | The video tutorial includes how to generate an **SSH key**. 14 | 15 | If the steps are correct and there are no errors, you should find a **.ssh folder** in the path, containing two files: **id_rsa** and **id_rsa.pub**. 16 | 17 | These two are the SSH Key. **id_rsa is the private key** and should not be disclosed. **id_rsa.pub is the public key**, which you can safely share with anyone. 18 | 19 | After installing Git, the first thing to do is to set your name and email: 20 | 21 | ```cmd 22 | git config --global user.name "twtrubiks" 23 | git config --global user.email "twtrubiks@gmail.com" 24 | ``` 25 | 26 | You can enter the following to confirm if the input was successful: 27 | 28 | ```cmd 29 | git config --global user.name 30 | git config --global user.email 31 | ``` 32 | 33 | 34 | 35 | To view Git configuration data, you can execute the following command (a more detailed tutorial will be at the end of the article): 36 | 37 | ```cmd 38 | git config --list 39 | ``` 40 | 41 | ## git init command 42 | 43 | Initialize git 44 | 45 | ```cmd 46 | git init 47 | ``` 48 | 49 | You can also specify a folder 50 | 51 | ```cmd 52 | git init 53 | ``` 54 | 55 | ## git clone command 56 | 57 | Copy the URL from the location shown in the picture (don't copy mine~ copy your own) 58 | 59 | 60 | git clone (copied URL) SSH / HTTPS 61 | 62 | (If you want to use the https method, please see [Personal Access Tokens](https://github.com/twtrubiks/Git-Tutorials#personal-access-tokens)) 63 | 64 | ```cmd 65 | git clone git@github.com:twtrubiks/test.git 66 | ``` 67 | 68 | The first time, an SSH warning will appear, just select YES. 69 | 70 | As shown in the picture (download successful), a new folder will appear in your download path. 71 | 72 | 73 | 74 | ## Personal Access Tokens 75 | 76 | * [Youtube Tutorial - GitHub Tutorial - Personal Access Tokens](https://youtu.be/aJRRVCB85k8) 77 | 78 | Starting from August 13, 2021, if you use the https method, you will find: 79 | 80 | 81 | 82 | ```text 83 | remote: Support for password authentication was removed on August 13, 2021. 84 | remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication. 85 | fatal: Authentication failed for 'https://github.com/xxxxx.git/' 86 | ``` 87 | 88 | At this point, if we don't want to add an ssh key, nor add collaborators, we can use Personal Access Tokens (you can think of it as a temporary permission). 89 | 90 | First, go to your GitHub's Settings -> Developer settings, 91 | 92 | Select Personal Access Tokens, and generate your token. 93 | 94 | 95 | 96 | You can define how long the token will be valid. 97 | 98 | The section below defines the permissions for this token. 99 | 100 | 101 | 102 | After setting it up, you can copy your token. 103 | 104 | 105 | 106 | Then go back and use the https clone method. 107 | 108 | Originally, you used username + password (no longer usable). 109 | 110 | Now, change it to username + the token you just got, and you can clone successfully. 111 | 112 | ### How to improve (speed up) git clone for large repos 113 | 114 | * [Youtube Tutorial - How to improve (speed up) git clone for large repos](https://youtu.be/YHX0qkQa1UI) 115 | 116 | Sometimes we need to clone very large repos, and `git clone` takes a long time. Is there a way to speed up the cloning process? :question: 117 | 118 | Let's try it out (using [django](https://github.com/django/django) as an example). 119 | 120 | `git clone git@github.com:django/django.git` 121 | 122 | (You will find that cloning takes some time :triumph:) 123 | 124 | 125 | 126 | Next, check the log, `git log` 127 | 128 | 129 | 130 | Try switching branches `git checkout stable/2.2.x` 131 | 132 | 133 | 134 | Let's start improving (speeding up) the clone time. 135 | 136 | You can do this with the `--depth` parameter. Simply put, when we normally clone and then run `git log`, you will see a large number of logs. In some cases, you may not need so many logs. 137 | 138 | That is, you may only need the last 10 history commits, or even just 1 (meaning you don't need history commits at all). This is where `--depth` is very suitable. 139 | 140 | `git clone git@github.com:django/django.git --depth 1` 141 | 142 | (You will find it's much faster this time) 143 | 144 | 145 | 146 | Next, check the log, `git log` 147 | 148 | (The reason it's faster is that we only keep the latest history commit. If you need the last 10, change it to --depth 10) 149 | 150 | 151 | 152 | But there will be a problem when you try to switch branches `git checkout stable/2.2.x` 153 | 154 | (You will find that you cannot switch to a remote branch :scream: 155 | 156 | The reason is that using `--depth` is equivalent to `--single-branch`, so of course, there are no other branches.) 157 | 158 | 159 | 160 | That is to say, the following two commands are actually equivalent: 161 | 162 | ```cmd 163 | git clone git@github.com:django/django.git --depth 1 164 | git clone git@github.com:django/django.git --depth 1 --single-branch 165 | ``` 166 | 167 | To solve this problem, a better way is: 168 | 169 | ```cmd 170 | git clone git@github.com:django/django.git --depth 1 --no-single-branch 171 | ``` 172 | 173 | (This will be slightly slower than `--single-branch` because the latest history commit of each branch needs to be cloned.) 174 | 175 | This way, you can keep the remote branches. 176 | 177 | 178 | 179 | Successfully switched to the remote branch, `git checkout stable/2.2.x`. 180 | 181 | 182 | 183 | Finally, a quick summary: 184 | 185 | To clone the latest history and also need other branches, use the following: 186 | 187 | `git clone git@github.com:django/django.git --depth 1 --no-single-branch` 188 | 189 | If you want to specify a branch, add `-b`: 190 | 191 | `git clone git@github.com:django/django.git --depth 1 --no-single-branch -b stable/3.1.x` 192 | 193 | To clone the latest history and **do not** need other branches, use the following: 194 | 195 | `git clone git@github.com:django/django.git --depth 1 --single-branch` 196 | 197 | or 198 | 199 | `git clone git@github.com:django/django.git --depth 1` 200 | 201 | For more detailed parameter descriptions, please refer to [git clone](https://git-scm.com/docs/git-clone) 202 | 203 | ## git status command 204 | 205 | ```cmd 206 | git status 207 | ``` 208 | 209 | Allows us to view the current repository (repo container). 210 | 211 | 212 | 213 | This means your working directory is currently clean. 214 | 215 | ## Working Directory and Staging Area (Stage) 216 | 217 | `git add` means to put the files to be committed into the staging area (Stage). 218 | 219 | Then execute 220 | 221 | `git commit` to send all modified content in the staging area (Stage) to the current branch. 222 | 223 | Once committed (`git commit`), if you haven't made any changes to the working directory, then the working directory is "clean". 224 | 225 | `git commit -m "xxxxx"` command, the content after -m is the description of this modification (commit). 226 | 227 | Try to enter content that makes it clear at a glance what was modified in this commit (for easy future reference to quickly understand what was changed in this commit). 228 | 229 | The following demo shows adding a Hello.py file to a folder. 230 | 231 | Then use `git status` to view the current repository (repo container), you will see that Hello.py is untracked, as shown below: 232 | 233 | 234 | 235 | You can use the following command: 236 | 237 | ```cmd 238 | git add Hello.py 239 | ``` 240 | 241 | Extra tip, this command is very interesting, everyone can play with it: 242 | 243 | ```cmd 244 | git add -p 245 | ``` 246 | 247 | Then use 248 | 249 | `git commit -m "text"` 250 | 251 | ```cmd 252 | git commit -m "add Hello.py" 253 | ``` 254 | 255 | Use `git status` again, and you will find that the working directory is clean. As shown below: 256 | 257 | 258 | 259 | Additionally, if you only enter 260 | 261 | ```cmd 262 | git commit 263 | ``` 264 | 265 | 266 | 267 | An editor window will pop up. 268 | 269 | 270 | 271 | At this point, you can press the **Ins key** (or the **letter i** on the keyboard) to enter text. 272 | 273 | 274 | 275 | After entering the text, first press the **Esc key**. After pressing it, the INSERT at the bottom will disappear. Then type **:wq** and press enter to save and exit. 276 | 277 | For more parameters, please refer to the [https://git-scm.com/docs/git-commit](https://git-scm.com/docs/git-commit) documentation. 278 | 279 | **How to modify the last commit?** 280 | 281 | Sometimes after we commit, we realize we made a typo in the commit message. 282 | 283 | At this time, you can use the following command, which will pop up an editor window for you to edit your last commit message. 284 | 285 | ```cmd 286 | git commit --amend 287 | ``` 288 | 289 | Or, after we commit, we realize we missed adding a few files. 290 | 291 | At this time, you can use the following command: 292 | 293 | ```cmd 294 | git commit -m "init commit" 295 | git add missing_file.py 296 | git commit --amend 297 | ``` 298 | 299 | The above situation is when after `git commit -m "init commit"`, 300 | 301 | I found that I missed the **missing_file.py** file (forgot to add it before committing). 302 | 303 | At this time, you can use `git commit --amend` to modify the last commit. 304 | 305 | Sometimes, for convenience, we directly use the following command to add all files at once: 306 | 307 | ```cmd 308 | git add . 309 | ``` 310 | 311 | But after adding, we find that some files did not need to be added. At this time, you can use the following command to un-add them: 312 | 313 | ```cmd 314 | git reset HEAD 315 | ``` 316 | 317 | Example: There are two files, A.py and B.py, in the path, and I use **git add .** to add them. 318 | 319 | 320 | But after adding, I realize that I don't want to add B.py yet, so I can use **git reset HEAD B.py** to revert it. 321 | 322 | 323 | 324 | ## git push command 325 | 326 | ```cmd 327 | git push 328 | ``` 329 | 330 | Push the code to GitHub (or Bitbucket, etc.), as shown below: 331 | 332 | 333 | 334 | ## Version Control - History 335 | 336 | ```cmd 337 | git log 338 | ``` 339 | 340 | Press **lowercase q** to exit. 341 | 342 | 343 | 344 | If you find the layout too cluttered, you can use the following command: 345 | 346 | ```cmd 347 | git log --pretty=oneline 348 | ``` 349 | 350 | Press **lowercase q** to exit. 351 | 352 | 353 | 354 | If you want to see the changes of a specific file or folder, you can use it like this, for example: 355 | 356 | * [Youtube Tutorial - Git Log: Log disappears after moving a folder? Use --follow to find file history! (To be added)](xx) 357 | 358 | ```cmd 359 | git log -- folder 360 | ``` 361 | 362 | If tracking individual files, you can add `--follow` 363 | 364 | ```cmd 365 | git log --follow -- folder/demo.py 366 | ``` 367 | 368 | For example, I have a folder structure as follows: 369 | 370 | ```cmd 371 | ❯ tree 372 | . 373 | ├── production 374 | │ ├── a 375 | │ │ ├── a1.txt 376 | │ │ ├── a2.txt 377 | │ │ └── a3.txt 378 | │ └── b 379 | │ └── b1.txt 380 | └── test 381 | ``` 382 | 383 | History log under `production`: 384 | 385 | ```cmd 386 | ❯ git log -- production/a 387 | 388 | * b9a481b - a3.txt 389 | * 02fce19 - a2.txt 390 | * 1e4aa1b - create 391 | ``` 392 | 393 | Suppose today we move the `a` folder under `production` to the `test` folder, 394 | 395 | The folder structure becomes as follows, then commit: 396 | 397 | ```cmd 398 | ❯ tree 399 | . 400 | ├── production 401 | │ └── b 402 | │ └── b1.txt 403 | └── test 404 | └── a 405 | ├── a1.txt 406 | ├── a2.txt 407 | └── a3.txt 408 | ``` 409 | 410 | The current git log of the entire repo is as follows: 411 | 412 | ```cmd 413 | ❯ git log 414 | 415 | * 5707ce2 - move 416 | * b9a481b - a3.txt 417 | * 02fce19 - a2.txt 418 | * 1e4aa1b - create 419 | * 1b7fb87 - Initial commit 420 | ``` 421 | 422 | Then look at the `a` folder, as follows: 423 | 424 | ```cmd 425 | ❯ git log -- test/a 426 | 427 | * 5707ce2 - move 428 | ``` 429 | 430 | You will find that the git tracking records for `a2.txt` and `a3.txt` have disappeared!!! 431 | 432 | Method 1, track the original folder. 433 | 434 | Although this file no longer exists, the record is still in git. 435 | 436 | ```cmd 437 | # Here you will find that the folder no longer exists because it has been moved 438 | ❯ ls production/a 439 | 440 | ls: cannot access 'production/a': No such file or directory 441 | 442 | # But you can still track the history 443 | ❯ git log -- production/a 444 | 445 | * 5707ce2 - move 446 | * b9a481b - a3.txt 447 | * 02fce19 - a2.txt 448 | * 1e4aa1b - create 449 | ``` 450 | 451 | Method 2, add `--follow`. This can track individual files (don't use it for moved folders, as you won't see past records). 452 | 453 | ```cmd 454 | ❯ git log --follow -- test/a/a1.txt 455 | 456 | * 5707ce2 - move 457 | * 1e4aa1b - create 458 | ``` 459 | 460 | Also, here is another way to view the log (very cool :satisfied:), feels like a GUI (source is from the link at the end of the article). 461 | 462 | ```cmd 463 | git log --graph --pretty=format:"%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset" --abbrev-commit --date=relative 464 | ``` 465 | 466 | 467 | 468 | In Git, HEAD is used to represent the current version. 469 | 470 | ```cmd 471 | git reset --hard HEAD 472 | ``` 473 | 474 | 475 | 476 | If you now want to revert the current version to the previous version, you can use the `git reset` command: 477 | 478 | The previous version is HEAD~1. 479 | 480 | ```cmd 481 | git reset --hard HEAD~1 482 | ``` 483 | 484 | 485 | 486 | The version before that is HEAD~2. 487 | 488 | To revert to a specific version: 489 | 490 | 491 | 492 | ```cmd 493 | git reset --hard ad41df36b7 494 | ``` 495 | 496 | The `--hard` parameter has three options: `--mixed` (default), `--hard`, `--soft`. 497 | 498 | `--hard` simply means to discard all previous commits (completely **not preserved**). 499 | 500 | `--soft` simply means to discard all previous commits, but **preserve** the state of your previous working directory. 501 | 502 | I think `--hard` and `--soft` are not easy to explain with words. I suggest everyone try them out to understand the difference. 503 | 504 | `--soft` is very suitable for merging multiple meaningless commits into one commit. 505 | 506 | 507 | 508 | The version number (ad41df36b7) doesn't need to be written in full, just the first few digits are enough, Git will find it automatically. 509 | 510 | When you revert to a certain version and suddenly regret it the next day, how do you restore to the previous new version? 511 | 512 | What if you can't find the commit id of the new version? 513 | 514 | At this time, you can use a command: 515 | 516 | ```cmd 517 | git reflog 518 | ``` 519 | 520 | 521 | 522 | Then see which version you want to go back to, and use `git reset`. 523 | 524 | ```cmd 525 | git reset --hard 642e7af 526 | ``` 527 | 528 | Sometimes you want to eliminate (overwrite) a commit that has already been pushed. At this time, we can use: 529 | 530 | ```cmd 531 | git push --force 532 | ``` 533 | 534 | Or a shorter way: 535 | 536 | ```cmd 537 | git push -f 538 | ``` 539 | 540 | You can force push. First, go back to a certain version, and then force push. 541 | 542 | ***Note! When working on a multi-person project, try not to use methods like --force, because sometimes it will harm others. It is recommended to use revert.*** 543 | 544 | For the above reason, it is recommended to use a safer way: 545 | 546 | ```cmd 547 | git push --force-with-lease 548 | ``` 549 | 550 | This can ensure that you don't accidentally discard someone else's commit. (If someone commits and pushes before you, you will not be able to push to the remote). 551 | 552 | ## checkout 553 | 554 | Please also refer to [git switch](https://github.com/twtrubiks/Git-Tutorials#git-switch) and [git restore](https://github.com/twtrubiks/Git-Tutorials#git-restore). 555 | 556 | `git checkout -- file` can discard modifications in the working directory: 557 | 558 | ```cmd 559 | git checkout -- hello.py 560 | ``` 561 | 562 | The command `git checkout -- hello.py` means to undo (discard) all modifications to the hello.py file in the working directory, 563 | 564 | returning this file to the state of the last `git commit` or `git add`. 565 | 566 | 567 | 568 | Of course, you can also use the `git reset` command to directly revert to a certain commit. 569 | 570 | ```cmd 571 | git reset --hard xxxxxx 572 | ``` 573 | 574 | ```cmd 575 | git reset --hard 201f40604ec3b6fa8 576 | ``` 577 | 578 | ## Deletion 579 | 580 | There are two situations. One is that you are sure you want to delete the file from the repository. In that case, use the `git rm` command to delete it, and then `git commit`: 581 | 582 | ```cmd 583 | rm hello.py 584 | git rm hello.py 585 | git commit -m "remove hello.py" 586 | ``` 587 | 588 | 589 | 590 | The other situation is that you deleted it by mistake. You can easily restore the file using `git checkout`: 591 | 592 | ```cmd 593 | rm hello.py 594 | git checkout -- hello.py 595 | ``` 596 | 597 | 598 | 599 | ## Creating and merging branches 600 | 601 | Before explaining branches, let me give you a concept. 602 | 603 | Usually, during development, everyone creates a branch from **master**, and finally **merges** it back to master. 604 | 605 | Why do this? To ensure that everyone is using the latest **master**. 606 | 607 | Use the `git branch` command to view the current branch: 608 | 609 | ```cmd 610 | git branch 611 | ``` 612 | 613 | 614 | 615 | First, create a branch, `bug1` branch (the name can be anything), and then switch to the `bug1` branch: 616 | 617 | ```cmd 618 | git branch bug1 619 | git checkout bug1 620 | ``` 621 | 622 | `git branch bug1` creates a branch named `bug1`. 623 | 624 | `git checkout bug1` switches to the branch named `bug1`. 625 | 626 | 627 | 628 | The above two commands are equivalent to the following one command: 629 | 630 | ```cmd 631 | git checkout -b bug1 632 | ``` 633 | 634 | (Here's a little trick, the following command can quickly switch to the previous branch, the concept is the same as `cd -` :exclamation:) 635 | 636 | ```cmd 637 | git checkout - 638 | ``` 639 | 640 | We make any modifications on the `bug1` branch, 641 | 642 | and then merge the work results (note: remember to use `git add` and `git commit` commands after any modification) to the master branch: 643 | 644 | ```cmd 645 | git checkout master 646 | git merge bug1 647 | ``` 648 | 649 | 650 | 651 | `git checkout master` switches to the branch named master. 652 | 653 | The `git merge bug1` command is used to merge the specified branch (`bug1` branch) into the current branch (master). 654 | 655 | If it goes very smoothly, "Fast-forward" will appear in the `git merge` message, and the merge speed is very fast. 656 | 657 | Of course, not every merge can go smoothly with "Fast-forward". Many times, conflicts will occur. 658 | 659 | If the merge is completed successfully, you can delete the (local) `bug1` branch: 660 | 661 | ```cmd 662 | git branch -d dev 663 | ``` 664 | 665 | 666 | 667 | To discard a branch that has not been merged, you can use `git branch -D branch_name` to force delete (local). 668 | 669 | ```cmd 670 | git branch -D dev 671 | ``` 672 | 673 | So what if you want to delete a remote branch today? :question: 674 | 675 | * [Youtube Tutorial - git delete and view remote branches](https://youtu.be/0JQrT7nfm_c) 676 | 677 | ```cmd 678 | git push origin --delete {remote_branch} 679 | ``` 680 | 681 | Additionally, `git branch` can also be renamed, and the commit id will not change. The usage is also very simple. 682 | 683 | You can refer to the git-branch [documentation](https://git-scm.com/docs/git-branch#git-branch--m). The usage is as follows: 684 | 685 | ```text 686 | git branch -m 687 | ``` 688 | 689 | The log of the original `b1` branch is as follows: 690 | 691 | 692 | 693 | Now rename the `b1` branch to the `b2` branch: 694 | 695 | 696 | 697 | If you compare it carefully with the previous log, you will find that the commit id of the log does not change. 698 | 699 | 700 | 701 | ## Create a branch using a specific commit id 702 | 703 | Sometimes we want to test the state of a certain commit. At this time, we can directly use the commit id to create a branch. 704 | 705 | The method is as follows: 706 | 707 | ```cmd 708 | git checkout -b new_branch 709 | ``` 710 | 711 | This will create a branch based on the commit id you specified. 712 | 713 | ## Create a new branch and push 714 | 715 | I believe everyone has seen many branches on GitHub, as shown in the picture below: 716 | 717 | 718 | 719 | So how do we create a branch? First, let's look at the picture below: 720 | 721 | 722 | 723 | There is a `v1` branch, and I added a `g.py` and committed it on the branch. 724 | 725 | Next, when you **first** `git push`, you will find an error message. 726 | 727 | Please use the following command, which is the correct one: 728 | 729 | ```cmd 730 | git push --set-upstream origin v1 731 | ``` 732 | 733 | You can also use: 734 | 735 | ```cmd 736 | git push -u origin v1 737 | ``` 738 | 739 | For more detailed instructions, please refer to [https://git-scm.com/docs/git-push#git-push--u](https://git-scm.com/docs/git-push#git-push--u) 740 | 741 | 742 | 743 | Next, you can go to the webpage to see (here using Bitbucket as an example), you will find that there is a `v1` branch. 744 | 745 | 746 | 747 | If this is the first time you use `git clone`, you will find that you only have the master branch. 748 | 749 | At this time, let's first check what other branches are on the remote. 750 | 751 | ```cmd 752 | git branch -r 753 | ``` 754 | 755 | ```cmd 756 | git branch --remote 757 | ``` 758 | 759 | `--remote` or `-r` are both fine. 760 | 761 | Suppose there is a branch named `develop` on the remote. 762 | 763 | We just need to checkout to that branch. 764 | 765 | ```cmd 766 | git checkout develop 767 | ``` 768 | 769 | ## git switch 770 | 771 | [Youtube Tutorial - git switch and git restore tutorial](https://youtu.be/JL_bSOGDR-k) 772 | 773 | Please check your current git version first. For update methods, please refer to [git update](https://github.com/twtrubiks/Git-Tutorials#git-%E6%9B%B4%E6%96%B0). 774 | 775 | Starting from git version 2.23, `git switch` and `git restore` have been added. These two commands are mainly to more clearly divide functions, mainly to replace `git checkout`. 776 | 777 | You can think of `git checkout` = `git switch` + `git restore`. 778 | 779 | Official documentation can be found at [git-switch](https://git-scm.com/docs/git-switch) 780 | 781 | ```cmd 782 | git switch [] (-c|-C) [] 783 | ``` 784 | 785 | Switch to an existing branch (if the branch does not exist, the command is invalid) 786 | 787 | ```cmd 788 | git switch 789 | ``` 790 | 791 | Create `new-branch` and switch to the `new-branch` branch 792 | 793 | ```cmd 794 | git switch -c 795 | ``` 796 | 797 | `-c` `--create` 798 | 799 | `-C` `--force-create` 800 | 801 | Create `new-branch` based on commit_id (or the Nth previous commit point) and switch to the `new-branch` branch 802 | 803 | ```cmd 804 | git switch -c 805 | git switch -c HEAD~2 806 | ``` 807 | 808 | (Here's a little trick, the following command can quickly switch to the previous branch, the concept is the same as `cd -` :smile:) 809 | 810 | ```cmd 811 | git switch - 812 | ``` 813 | 814 | ## git restore 815 | 816 | [Youtube Tutorial - git switch and git restore tutorial](https://youtu.be/JL_bSOGDR-k) 817 | 818 | Please check your current git version first. For update methods, please refer to [git update](https://github.com/twtrubiks/Git-Tutorials#git-%E6%9B%B4%E6%96%B0). 819 | 820 | Starting from git version 2.23, `git switch` and `git restore` have been added. These two commands are mainly to more clearly divide functions, mainly to replace `git checkout`. 821 | 822 | You can think of `git checkout` = `git switch` + `git restore`. 823 | 824 | Official documentation can be found at [git-restore](https://git-scm.com/docs/git-restore) 825 | 826 | The following two commands are the same. 827 | 828 | ```cmd 829 | git checkout 830 | git restore 831 | ``` 832 | 833 | Restore all files in the current folder 834 | 835 | ```cmd 836 | git restore . 837 | ``` 838 | 839 | Restore all files ending with `*.py` in the current folder 840 | 841 | ```cmd 842 | git restore '*.py' 843 | ``` 844 | 845 | If your `git` version is newer, you should find that you have never seen this command before :smile: 846 | 847 | 848 | 849 | ```cmd 850 | git restore --staged 851 | ``` 852 | 853 | ## git pull 854 | 855 | Usually, before starting work or pushing, you will first pull the branch from the remote. 856 | 857 | ```cmd 858 | git pull 859 | ``` 860 | 861 | If there are conflicts, you must resolve them first. 862 | 863 | Here's a supplement on the `-C` parameter. It means to specify the folder path. 864 | 865 | Sometimes we may not want to `cd` into the folder first and then pull. At this time, it is very suitable to use it :smile: 866 | 867 | ```cmd 868 | git [-C ] pull 869 | ``` 870 | 871 | Example: 872 | 873 | ```cmd 874 | cd git_folder 875 | git pull 876 | ``` 877 | 878 | Can be simplified to: 879 | 880 | ```cmd 881 | git -C git_folder pull 882 | ``` 883 | 884 | ## git fetch 885 | 886 | You can simply think of **git pull = git fetch + git merge** 887 | 888 | Let's first look at the picture below, **git fetch + git merge** 889 | 890 | 891 | 892 | Then look at this picture **git pull** 893 | 894 | 895 | 896 | Isn't it much clearer now!!! 897 | 898 | An additional parameter `--prune` is added. 899 | 900 | * [Youtube Tutorial - git fetch command prune parameter explanation](https://youtu.be/ZMpMv1P1Q1Q) 901 | 902 | The main function of this is to delete invalid branches on the remote. 903 | 904 | Sometimes, even though the remote branch has been deleted, when you execute `git branch --remote`, you will find that you can still see those branches (but the branches on the webpage have been removed :sweat:) 905 | 906 | This often happens on the pull side (non-working side) machine (if you don't understand what this means, it is recommended to watch the video explanation :smile:) 907 | 908 | At this time, you can synchronize the local and remote branches using the following command: 909 | 910 | `git fetch --prune` 911 | 912 | ## git rebase 913 | 914 | What is rebase? `git rebase` is to avoid redundant (meaningless) merges!!! First, look at the two pictures below. 915 | 916 | Note: 917 | 918 | ck = checkout 919 | 920 | br = branch 921 | 922 | st = status 923 | 924 | cm = commit 925 | 926 | You can set it yourself. 927 | 928 | Picture 1 929 | 930 | 931 | 932 | Picture 2 933 | 934 | 935 | 936 | Which picture do you prefer to look at, Picture 1 or Picture 2? The answer is obvious, it's Picture 1!! 937 | 938 | The main purpose of **rebase** is to make the graph look like Picture 1 as much as possible. 939 | 940 | It's confusing to just talk about it, so I'll show you a practical example. 941 | 942 | First, an example **without using rebase**. 943 | 944 | Current branch 945 | 946 | 947 | 948 | 949 | 950 | The above explanation: First create a v1 branch, then add and commit. 951 | 952 | Suppose someone else has pushed now. The following simulates a pull, adding a commit yourself. 953 | 954 | 955 | 956 | The above explanation: Add t2.txt on the master branch yourself, and commit (simulating a pull). 957 | 958 | Next, switch to the master branch and merge with the v1 branch, and push. 959 | 960 | 961 | 962 | You will find that the displayed graph is not beautiful, as shown below: 963 | 964 | 965 | 966 | Example of **using rebase**. 967 | 968 | The previous parts are basically the same. 969 | 970 | 971 | 972 | 973 | 974 | The above explanation: First create a v1 branch, then add and commit. 975 | 976 | Suppose someone else has pushed now. The following simulates a pull, adding a commit yourself. 977 | 978 | 979 | 980 | The above explanation: Add t2.txt on the master branch yourself, and commit (simulating a pull). 981 | 982 | ***The different part*** 983 | 984 | 985 | 986 | The above explanation: First switch to the v1 branch, then use the following command: 987 | 988 | ```cmd 989 | git rebase master 990 | ``` 991 | 992 | 993 | 994 | The above explanation: Then switch back to the master branch, and use merge to merge the v1 branch, and finally push. 995 | 996 | See~ Isn't it much neater and more beautiful? 997 | 998 | 999 | 1000 | `git rebase` is to bring back the latest commit from master, and then add the commit of your own branch. 1001 | 1002 | The above is the introduction of `git rebase`. 1003 | 1004 | Another way, just now you had to switch to the v1 branch to execute the command. 1005 | 1006 | If you are now on any branch (like the master branch), you can use the following command: 1007 | 1008 | ```cmd 1009 | git rebase master v1 1010 | ``` 1011 | 1012 | That is, specify v1 at the end. After execution, it will automatically switch you to the v1 branch. 1013 | 1014 | The result is the same. 1015 | 1016 | In addition, there is another command `git rebase --onto` 1017 | 1018 | ```cmd 1019 | git rebase --onto 1020 | ``` 1021 | 1022 | The concept is actually the same, it's just which new base-commit you want to rebase to. 1023 | 1024 | Just put the current base-commit at the end. 1025 | 1026 | You can view it with `git graph`, or look at the git documentation `git rebase --help`. 1027 | 1028 | ## git rebase interactive 1029 | 1030 | When I was young, I always thought that `git rebase` was just to make the commit log look cleaner. But I accidentally discovered that the interactive mode of `git rebase` is super powerful. So, here I will introduce the powerful features of `git rebase` :smirk: 1031 | 1032 | The following are the commands that can be used in `git rebase interactive`. I copied these descriptions from git, and I will show them to you later. 1033 | 1034 | ```cmd 1035 | # Commands: 1036 | # p, pick = use commit 1037 | # r, reword = use commit, but edit the commit message 1038 | # e, edit = use commit, but stop for amending 1039 | # s, squash = use commit, but meld into previous commit 1040 | # f, fixup = like "squash", but discard this commit's log message 1041 | # x, exec = run command (the rest of the line) using shell 1042 | # d, drop = remove commit 1043 | ``` 1044 | 1045 | If you want to learn more, please refer to [INTERACTIVE MODE](https://git-scm.com/docs/git-rebase#_interactive_mode). 1046 | 1047 | There is nothing much to say about `pick`, it just uses this commit :smile: 1048 | 1049 | ### reword 1050 | 1051 | [Youtube Tutorial - git rebase interactive - reword - PART 1](https://youtu.be/JhY0rR2wQq0) 1052 | 1053 | ```cmd 1054 | # Commands: 1055 | # p, pick = use commit 1056 | # r, reword = use commit, but edit the commit message 1057 | ``` 1058 | 1059 | The following is the official description: 1060 | 1061 | ```txt 1062 | If you just want to edit the commit message for a commit, replace the command "pick" with the command "reword". 1063 | ``` 1064 | 1065 | The description is already very clear, it is to edit the commit message. 1066 | 1067 | (You cannot modify the commit content, that is, the content of the files) 1068 | 1069 | Suppose, now we have a git log like this: 1070 | 1071 | 1072 | 1073 | Commit id `2659f65` has a typo. The correct commit message should be `add c.py`. 1074 | 1075 | So now we need to correct it. Our target commit id is `2659f65`. The command is: 1076 | 1077 | ```cmd 1078 | git rebase -i 1079 | ``` 1080 | 1081 | What does `after-this-commit` mean? :question: 1082 | 1083 | Simply put, you need to select the one before the current commit id. 1084 | 1085 | In this example, our target commit id is `2659f65`, but we must issue the command: 1086 | 1087 | ```cmd 1088 | git rebase -i f0a761d 1089 | ``` 1090 | 1091 | 1092 | 1093 | This should be very clear. In short, remember to choose the one before the target commit id. 1094 | 1095 | When you press ENTER, you should see the following picture: 1096 | 1097 | 1098 | 1099 | Part A is the target we want to modify, and part B is the description (the stuff I posted for you earlier). 1100 | 1101 | Next, press `i` to enter edit mode, then change the target to `r` or `reword`, and then enter `:wq`. 1102 | 1103 | 1104 | 1105 | Then we press ENTER again, and another screen will pop up. At this time, you change the commit message to the correct one, changing `add c.py Typo` to `add c.py`. 1106 | 1107 | 1108 | 1109 | After entering `:wq`, press ENTER again (done). 1110 | 1111 | 1112 | 1113 | Let's check the log again (as shown below). It has been successfully modified, and the message has been changed to `add c.py`. 1114 | 1115 | 1116 | 1117 | There is one thing I want to mention to everyone here, that is, the commit id will change. I will frame the changed part for you to see. 1118 | 1119 | Before modification: 1120 | 1121 | 1122 | 1123 | After modification: 1124 | 1125 | 1126 | 1127 | Simply put, the commit ids after the current commit id will all change (a bit of a tongue twister :sweat_smile:). 1128 | 1129 | Here's a supplement, as long as you use rebase, you will see a picture similar to the one below. 1130 | 1131 | 1132 | 1133 | `origin/master` refers to the remote repo. It tells you that your current repo is now different from `origin/master`. So, at this time, if you want to push, please use `git push --force-with-lease`. 1134 | 1135 | Someone might ask here, what if I want to modify the first commit? :question: 1136 | 1137 | At this time, you can use: 1138 | 1139 | ```cmd 1140 | git rebase -i --root 1141 | ``` 1142 | 1143 | ### edit 1144 | 1145 | [Youtube Tutorial - git rebase interactive - edit - PART 2](https://youtu.be/TCKjQppHxxQ) 1146 | 1147 | ```cmd 1148 | # Commands: 1149 | # p, pick = use commit 1150 | # e, edit = use commit, but stop for amending 1151 | ``` 1152 | 1153 | The following is the official description: 1154 | 1155 | ```txt 1156 | By replacing the command "pick" with the command "edit", you can tell git rebase to stop after applying that commit, so that you can edit the files and/or the commit message, amend the commit, and continue rebasing. 1157 | ``` 1158 | 1159 | Simply put, `reword` can only modify the commit message, while `edit` can not only modify the commit message but also the content of the files. 1160 | 1161 | Let's look at the picture below first: 1162 | 1163 | 1164 | 1165 | This picture clearly shows `add a.py` -> `add b.py` -> `add c.py` -> `add d.py`. Now I want to add something between `add c.py` and `add d.py`, that is, to make it `add a.py` -> `add b.py` -> `add c.py` -> `add c1.py` -> `add d.py`. 1166 | 1167 | You can use `edit` when you want to add `add c1.py`. (I won't go into so much detail below, I'll just get to the point). 1168 | 1169 | First, execute the following command (our target is `a7ed6ff`, so choose its previous commit id, which is `f0a761d`). 1170 | 1171 | ```cmd 1172 | git rebase -i f0a761d 1173 | ``` 1174 | 1175 | This time we change `pick` to `e` or `edit` (as shown below): 1176 | 1177 | 1178 | 1179 | When you press ENTER, you will see the picture below: 1180 | 1181 | 1182 | 1183 | Part A is where you can modify the commit message. 1184 | 1185 | Part B tells you that when you are done modifying (satisfied), you can execute `git rebase --continue`. 1186 | 1187 | We won't do part A, but let's do some processing now (add `c1.py`). 1188 | 1189 | First, we create a `c1.py` file, then `git add c1.py`, and then commit it (as shown below): 1190 | 1191 | 1192 | 1193 | As I said just now, when you are satisfied, you can execute `git rebase --continue`, and you're done. 1194 | 1195 | 1196 | 1197 | Check the log again, it's amazing :satisfied: it was added successfully. 1198 | 1199 | 1200 | 1201 | ### squash 1202 | 1203 | [Youtube Tutorial - git rebase interactive - squash fixup - PART 3](https://youtu.be/bfrZrbEHis0) 1204 | 1205 | ```cmd 1206 | # Commands: 1207 | # p, pick = use commit 1208 | # s, squash = use commit, but meld into previous commit 1209 | ``` 1210 | 1211 | The following is the official description: 1212 | 1213 | ```text 1214 | The suggested commit message for the folded commit is the concatenation of the commit messages of the first commit and of those with the "squash" command, 1215 | ``` 1216 | 1217 | Simply put, if you want to merge multiple commits into one, just use `squash`. (I won't go into so much detail below, I'll just get to the point). 1218 | 1219 | The goal this time is to merge commit id `fc45824` and commit id `a7ed6ff` (as shown below): 1220 | 1221 | 1222 | 1223 | First, execute the following command: 1224 | 1225 | ```cmd 1226 | git rebase -i f0a761d 1227 | ``` 1228 | 1229 | Then you will see the picture below. We change the `pick` of the `fc45824` commit to `s` or `squash`. 1230 | 1231 | (It will merge with its previous one, which is `a7ed6ff`) 1232 | 1233 | 1234 | 1235 | (If you want to merge multiple commits, just change them all to `s` or `squash`. Note, there is an order :exclamation: :exclamation:) 1236 | 1237 | Then press ENTER, and you will see the picture below: 1238 | 1239 | 1240 | 1241 | At this time, it has already merged these two commits. We can enter a new commit message. 1242 | 1243 | Here we enter `add c.py and c1.py`. 1244 | 1245 | 1246 | 1247 | Press ENTER again (success). 1248 | 1249 | 1250 | 1251 | You can check the log again. We have successfully merged the two commits. 1252 | 1253 | 1254 | 1255 | Both `c.py` and `c1.py` exist, which means we have succeeded :satisfied: 1256 | 1257 | 1258 | 1259 | ### fixup 1260 | 1261 | [Youtube Tutorial - git rebase interactive - squash fixup - PART 3](https://youtu.be/bfrZrbEHis0) 1262 | 1263 | ```cmd 1264 | # Commands: 1265 | # p, pick = use commit 1266 | # f, fixup = like "squash", but discard this commit's log message 1267 | ``` 1268 | 1269 | The following is the official description: 1270 | 1271 | ```text 1272 | omits the commit messages of commits with the "fixup" command. 1273 | ``` 1274 | 1275 | This is actually very similar to `squash`. Usually, if we want to ignore a commit message but keep the content of the commit, we will use `fixup`. 1276 | 1277 | Goal: Here we want to remove the commit `fc45824` (but keep the content of the commit). 1278 | 1279 | 1280 | 1281 | First, execute the following command: 1282 | 1283 | ```cmd 1284 | git rebase -i f0a761d 1285 | ``` 1286 | 1287 | Change the `pick` of `fc45824` to `f` or `fixup` (as shown below). 1288 | 1289 | (It will remove the commit message of `fc45824`, but keep the content of the commit) 1290 | 1291 | 1292 | 1293 | Then ENTER, and rebase successfully. 1294 | 1295 | 1296 | 1297 | You can check the log again. We have ignored the `add c1.py` commit. 1298 | 1299 | 1300 | 1301 | But both `c.py` and `c1.py` exist (only the commit message is ignored). 1302 | 1303 | 1304 | 1305 | Seeing this, everyone can actually think about it. `squash` and `fixup` are very similar. 1306 | 1307 | It's just that `squash` can modify the commit message. 1308 | 1309 | To put it simply, when you just want to ignore a certain commit message, use `fixup`. 1310 | 1311 | When you want to merge commits and modify the commit message, use `squash`. 1312 | 1313 | ### exec 1314 | 1315 | [Youtube Tutorial - git rebase interactive - exec drop - PART 4](https://youtu.be/u8imRiiSyzk) 1316 | 1317 | ```cmd 1318 | # Commands: 1319 | # p, pick = use commit 1320 | # x, exec = run command (the rest of the line) using shell 1321 | ``` 1322 | 1323 | The following is the official description: 1324 | 1325 | ```text 1326 | You may want to check that your history editing did not break anything by running a test, or at least recompiling at intermediate points in history by using the "exec" command (shortcut "x") 1327 | ``` 1328 | 1329 | I use this function less often, but I'll still talk about it. Simply put, it can be used to check if your rebase changes have affected the whole thing (use the `exec` command to confirm). 1330 | 1331 | Don't understand? :question: It's okay. Suppose I made a lot of rebase changes today, but I want to confirm if my changes have affected the whole thing. That is, I can run your tests while making changes to confirm that the whole thing is working normally. 1332 | 1333 | Still don't understand? :question: It's okay, I'll use an example to show you. 1334 | 1335 | 1336 | 1337 | As shown in the picture above, suppose I want to do some tests during my changes to ensure that my changes will not affect the whole thing. 1338 | 1339 | (Although they are all `pick` here, which means no changes, but for the convenience of explanation, please imagine that there are changes :sweat_smile:) 1340 | 1341 | 1342 | 1343 | Part A, `echo "test sucess"`, naturally has no problem. 1344 | 1345 | But part B will have a problem, because there is no `error` command at all. 1346 | 1347 | When the shell has an error during execution, it will stop and let you fix it. 1348 | 1349 | As shown in the picture below, we stopped at the `add c.py` commit, because the next test failed. 1350 | 1351 | 1352 | 1353 | At this time, we can fix the problem. After fixing it, execute `git rebase --continue`. 1354 | 1355 | 1356 | 1357 | I think this function is for you to run your own tests while modifying to ensure that the changes are normal. 1358 | 1359 | ### drop 1360 | 1361 | [Youtube Tutorial - git rebase interactive - exec drop - PART 4](https://youtu.be/u8imRiiSyzk) 1362 | 1363 | ```cmd 1364 | # Commands: 1365 | # p, pick = use commit 1366 | # d, drop = remove commit 1367 | ``` 1368 | 1369 | The following is the official description: 1370 | 1371 | ```text 1372 | To drop a commit, replace the command "pick" with "drop", or just delete the matching line. 1373 | ``` 1374 | 1375 | This is much simpler. Remove this commit (including the commit content). 1376 | 1377 | Suppose our log is as follows: 1378 | 1379 | 1380 | 1381 | The goal this time is to remove the commits `f0a761d`, `980bd9a`, and `1539219`. 1382 | 1383 | First, execute the following command: 1384 | 1385 | ```cmd 1386 | git rebase -i 8f13aaa 1387 | ``` 1388 | 1389 | Change `pick` to `d` or `drop` (as shown below): 1390 | 1391 | 1392 | 1393 | After pressing ENTER, check the log again. 1394 | 1395 | 1396 | 1397 | From the picture above, you can see that we have successfully removed the commits `f0a761d`, `980bd9a`, and `1539219`. 1398 | 1399 | And we also see that the commit content has been removed, only `a.py` is left. 1400 | 1401 | ## git pull supplement 1402 | 1403 | Now that we have introduced `git fetch` and `git rebase`, I will add some additional options for `git pull`. 1404 | 1405 | ```cmd 1406 | git pull [] [ […​]] 1407 | ``` 1408 | 1409 | For more detailed commands, please refer to [https://git-scm.com/docs/git-pull#_options](https://git-scm.com/docs/git-pull#_options). 1410 | 1411 | Here is a simple summary: 1412 | 1413 | ```cmd 1414 | git pull = git fetch + git merge 1415 | git pull --rebase = git fetch + git rebase 1416 | ``` 1417 | 1418 | In [git-rebase](https://github.com/twtrubiks/Git-Tutorials#git-rebase), everyone has already understood that using `git-rebase` can make it more comfortable for code reviewers. So just use `git pull --rebase` (provided you know what you are doing :smile:). 1419 | 1420 | Here I simulate the difference between `git pull` and `git pull --rebase`, and also add the case of conflicts. Because there are many steps, if you want to understand more about its concept, please refer to the following step-by-step tutorial: 1421 | 1422 | [Youtube Tutorial - git pull vs git pull --rebase](https://youtu.be/8h0K-2OaeSk) 1423 | 1424 | The result after using `git pull`, the code reviewer will definitely flip the table (as shown below) :triumph: 1425 | 1426 | Here I also simulated the case of conflicts. You will find that if you use `git pull`, there will be an extra commit (which is "fix conflict" below). 1427 | 1428 | 1429 | 1430 | The result after using `git pull --rebase`, the code reviewer will feel warm (as shown below) :innocent: 1431 | 1432 | Here I also simulated the case of conflicts. You will find that if you use `git pull --rebase`, there will not be an extra commit like just now. 1433 | 1434 | The reason is that when we use `git pull --rebase` and cause a conflict, after fixing the conflict content, `git add xxxx`, then we will directly execute `git rebase --continue`. 1435 | 1436 | 1437 | 1438 | Suppose you executed `git pull --rebase` today and found it very uncomfortable :fearful:, and want to cancel it. 1439 | 1440 | Just execute `git rebase --abort` to return to the previous state. 1441 | 1442 | Additional tips: 1443 | 1444 | * [Youtube Tutorial - git autostash parameter explanation](https://youtu.be/kg2PyZr7l5k) 1445 | 1446 | Explanation of `--autostash`. 1447 | 1448 | Generally, if we are in the middle of work and suddenly want to directly `git pull --rebase` without committing, the process will be something like this: 1449 | 1450 | ```cmd 1451 | git stash # Stash the current changes 1452 | git pull --rebase 1453 | git stash pop # Pop the previous changes from the stash 1454 | # If there are conflicts, resolve them 1455 | ``` 1456 | 1457 | But it's a bit annoying to have to execute so many commands every time :sweat: 1458 | 1459 | But you can solve it with a parameter, which is: 1460 | 1461 | `git pull --rebase --autostash` 1462 | 1463 | The above command basically helps you execute the string of things just now. 1464 | 1465 | If there are conflicts, just fix them :smile: 1466 | 1467 | ## git-cherry-pick 1468 | 1469 | Watching the video will be clearer. Step-by-step guide to do it yourself [Youtube Tutorial - git-cherry-pick](https://youtu.be/x3UtKUvlDdI) 1470 | 1471 | The `git-cherry-pick` command may be unfamiliar to everyone :confused: 1472 | 1473 | It's okay, let's first look at the [official](https://git-scm.com/docs/git-cherry-pick) description: 1474 | 1475 | ```text 1476 | git-cherry-pick - Apply the changes introduced by some existing commits 1477 | ``` 1478 | 1479 | Still :question: :question: :question: after reading the official description? 1480 | 1481 | It's okay, I'll assume a scenario (after understanding it, you will understand the purpose of `git-cherry-pick`). 1482 | 1483 | Suppose the log of the master branch is as shown in the picture below: 1484 | 1485 | 1486 | 1487 | And there is a v1 branch with a log as shown in the picture below: 1488 | 1489 | 1490 | 1491 | Now I want to merge the commit `14dee93 - add d.py` from the v1 branch. 1492 | 1493 | (Because the commit `14dee93` is so great or for some reason only this commit is needed) 1494 | 1495 | In the above situation, it is very suitable to use `git-cherry-pick`. That is to say, I only want a few commits from other branches, not all of them. In other words, it is to pick commits from other branches to use. 1496 | 1497 | After understanding the suitable usage scenario, let's practice it :smirk: 1498 | 1499 | First, I want the commit `14dee93 - add d.py` from the v1 branch. 1500 | 1501 | So I first switch to the master branch, and then execute: 1502 | 1503 | ```cmd 1504 | git cherry-pick 14dee93 1505 | ``` 1506 | 1507 | If you want to pick many branches at once, you can also do it. Just separate them with spaces. 1508 | 1509 | ```cmd 1510 | git cherry-pick 14dee93 xxxxxx xxxxxx xxxxxx xxxxx 1511 | ``` 1512 | 1513 | If you want to pick a range of commits at once, you can use the following command: 1514 | 1515 | ```cmd 1516 | git cherry-pick A^..B 1517 | ``` 1518 | 1519 | (A and B represent your commit ids) 1520 | 1521 | If there are no conflicts, you will see the picture below: 1522 | 1523 | 1524 | 1525 | Let's look at the master's log again: 1526 | 1527 | 1528 | 1529 | You will find that we have successfully taken the commit `14dee93 - add d.py` from the v1 branch and used it. But now its commit id is `ab70429`. This is normal because it needs to be recalculated :smile: 1530 | 1531 | Actually, you will find that `git-cherry-pick` is not as difficult as you imagined :satisfied: 1532 | 1533 | When cherry-picking, it is inevitable to encounter conflicts. Here I will do another example of a conflict. 1534 | 1535 | Suppose the master's log is as follows: 1536 | 1537 | 1538 | 1539 | The log in the v1 branch is as follows. I want its commit `3a2f29a - add c.py and print world`. 1540 | 1541 | 1542 | 1543 | The log in the v2 branch is as follows. I want its commit `553587b - add f.py`. 1544 | 1545 | 1546 | 1547 | Next, we switch back to master and cherry-pick these two commits. 1548 | 1549 | At this time, you will find that it has a conflict :fearful: 1550 | 1551 | 1552 | 1553 | Use `git status` to check the status. Part A has already taught you how to resolve conflicts. 1554 | 1555 | 1556 | 1557 | First, we fix `c.py`, execute `git add c.py`, and then follow part A to execute `git cherry-pick --continue`. At this time, an editor window will pop up. 1558 | 1559 | 1560 | 1561 | After entering the commit message, enter `wq`, and you will see the picture below: 1562 | 1563 | 1564 | 1565 | Finally, look at the log again. 1566 | 1567 | 1568 | 1569 | We have successfully merged the commits we want into our master branch :kissing_smiling_eyes: 1570 | 1571 | To learn more about usage, you can refer to the official documentation [https://git-scm.com/docs/git-cherry-pick](https://git-scm.com/docs/git-cherry-pick). 1572 | 1573 | ## git revert 1574 | 1575 | Suppose my commit history is A1 -> A2 -> A3 -> A4 -> A5 -> A6 1576 | 1577 | Now I want to go back to the commit A4. At this time, I can use `git revert`!! 1578 | 1579 | First, revert A6 1580 | 1581 | ```cmd 1582 | git revert A6 1583 | ``` 1584 | 1585 | Then revert A5 1586 | 1587 | ```cmd 1588 | git revert A5 1589 | ``` 1590 | 1591 | If you look at the current commit history, it will look like this: 1592 | 1593 | A1 -> A2 -> A3 -> A4 -> A5 -> A6 -> A6_revert -> A5_revert 1594 | 1595 | At this time, your commit is actually at the position of A4. 1596 | 1597 | The advantage of using `git revert` is that you can keep the commit history. If you regret it again, you can revert back. 1598 | 1599 | If you want to revert the latest commit, just use HEAD 1600 | 1601 | ```cmd 1602 | git revert HEAD 1603 | ``` 1604 | 1605 | ## Resolving conflicts 1606 | 1607 | When merging, sometimes **conflicts** will be displayed. At this time, you must manually resolve the conflicts before submitting. 1608 | 1609 | Usually, the easiest way for me to encounter conflicts is when using the `pull` command. 1610 | 1611 | 1612 | 1613 | Look carefully at this picture. If you use the **pull** command, it will help you **auto-merge** (as shown in the picture, Auto-merging Hello.py). 1614 | 1615 | Then look at CONFLICT (content): Merge conflict in Hello.py, and it also says Automatic merge failed. 1616 | 1617 | This tells you that the Hello.py file has a conflict, and you must manually resolve the conflict. 1618 | 1619 | `git status` can tell us the conflicting files. 1620 | 1621 | 1622 | 1623 | Open the conflicting file and we will see that Git uses `<<<<<<<`, `=======`, `>>>>>>>` to mark the content of different branches. We modify it and then submit: 1624 | 1625 | 1626 | 1627 | Usually, we will manually modify the conflicts and then add a commit. 1628 | 1629 | ```cmd 1630 | git add Hello.py 1631 | git commit -m "conflict fixed" 1632 | ``` 1633 | 1634 | ### Suppose we want to abandon this merge today, what should we do? 1635 | 1636 | ```cmd 1637 | git merge --abort 1638 | ``` 1639 | 1640 | or 1641 | 1642 | ```cmd 1643 | git reset --hard HEAD 1644 | ``` 1645 | 1646 | You can cancel this merge and go back to the state before the merge. 1647 | 1648 | ## git stash command 1649 | 1650 | * [Youtube Tutorial - git stash command](https://youtu.be/CN065MNHtMY) 1651 | 1652 | Many times, we are developing a new feature or debugging, and suddenly a feature needs to be fixed urgently. 1653 | 1654 | But you don't want to commit the current situation because it's meaningless. The work is only half done. At this time, the useful command **stash** comes in handy. 1655 | 1656 | For example, suppose we have modified two files, A.py and B.py. 1657 | 1658 | 1659 | 1660 | And now, suddenly there is a bug that must be dealt with immediately. 1661 | 1662 | But, I haven't finished my work yet~~~~ 1663 | 1664 | At this time, you can use the following command: 1665 | 1666 | ```cmd 1667 | git stash 1668 | ``` 1669 | 1670 | 1671 | 1672 | If you want to be clearer about the reason for this stash, or what feature is being developed, you can use the following command: 1673 | 1674 | Example: 1675 | 1676 | ```cmd 1677 | git stash save "I am a comment" 1678 | ``` 1679 | 1680 | ```cmd 1681 | git stash save -u "feature" 1682 | ``` 1683 | 1684 | Parameter description: 1685 | 1686 | `-u` | `--include-untracked` 1687 | 1688 | `-a` | `--all` 1689 | 1690 | 1691 | 1692 | Next, you can use the `status` command, and you will find that it has become clean. 1693 | 1694 | 1695 | 1696 | And you can use the following command to view the things in the stash: 1697 | 1698 | ```cmd 1699 | git stash list 1700 | ``` 1701 | 1702 | 1703 | 1704 | Then you work hard to solve this bug. After committing, you can use the following command to get the stash back. This command will also delete the stash after getting it back. 1705 | 1706 | ```cmd 1707 | git stash pop 1708 | ``` 1709 | 1710 | Suppose you have many stashes today. You can specify them as follows (choose the usage you like): 1711 | 1712 | ```cmd 1713 | git stash pop 0 1714 | git stash pop stash@{0} 1715 | ``` 1716 | 1717 | 1718 | 1719 | You will find that the things just now are back~ 1720 | 1721 | If you want to get the stash back without deleting it, you can use the following command: 1722 | 1723 | ```cmd 1724 | git stash apply 1725 | ``` 1726 | 1727 | As shown in the picture below, you can find that the stash is not deleted after being retrieved. 1728 | 1729 | 1730 | 1731 | If you just want to delete the temporary storage, you can use the following command: 1732 | 1733 | ```cmd 1734 | git stash clear 1735 | ``` 1736 | 1737 | From the picture below, you can find that the things in the stash have been deleted by us. 1738 | 1739 | 1740 | 1741 | If you want to discard a specified stash, you can use (choose the usage you like): 1742 | 1743 | ```cmd 1744 | git stash drop 0 1745 | git stash drop stash@{0} 1746 | ``` 1747 | 1748 | ## git tag 1749 | 1750 | [Youtube Tutorial - git tag tutorial](https://youtu.be/azciLlpr3Gs) 1751 | 1752 | View tags 1753 | 1754 | ```cmd 1755 | git tag 1756 | ``` 1757 | 1758 | 1759 | 1760 | Specify keywords 1761 | 1762 | ```cmd 1763 | git tag -l "v1.*" 1764 | ``` 1765 | 1766 | `-l` `--list` 1767 | 1768 | `git tag` has lightweight tags and annotated tags. 1769 | 1770 | Lightweight tag 1771 | 1772 | If you want to create a lightweight tag, please do not specify `-a`, `-s` (GPG-signed), `-m`. 1773 | 1774 | ```cmd 1775 | git tag tag_name [commit_id] 1776 | ``` 1777 | 1778 | If you only use `git tag tag_name` without adding the commit id later, the tag will be automatically placed on the current commit id. 1779 | 1780 | Show comments 1781 | 1782 | ```cmd 1783 | git show v1.1-light 1784 | ``` 1785 | 1786 | Annotated tag 1787 | 1788 | ```cmd 1789 | git tag -a v1.1 -m "version 1.1" 1790 | ``` 1791 | 1792 | `-a` is the tag name `--annotate` 1793 | 1794 | `-m` represents the description (comment) of the tag 1795 | 1796 | Set a tag on a specified commit 1797 | 1798 | ```cmd 1799 | git tag -a v1.2 -m "version 1.1" [commit_id] 1800 | ``` 1801 | 1802 | Show comments 1803 | 1804 | ```cmd 1805 | git show v1.1 1806 | ``` 1807 | 1808 | The difference between a lightweight tag and an annotated tag is whether you can see more details. 1809 | 1810 | An annotated tag has more information. 1811 | 1812 | Lightweight tag is as follows: 1813 | 1814 | 1815 | 1816 | Annotated tag is as follows: 1817 | 1818 | 1819 | 1820 | When you execute `git push`, by default, it will not push the tag to the remote. 1821 | 1822 | You need to execute the following command to push the tag to the remote: 1823 | 1824 | ```cmd 1825 | git push origin [tagname] 1826 | ``` 1827 | 1828 | Push many tags at once (this will push all your tags that are not on the remote). 1829 | 1830 | ```cmd 1831 | git push origin --tags 1832 | ``` 1833 | 1834 | When others execute `git clone` or `git fetch`, they can get these tags. 1835 | 1836 | Remove local tag 1837 | 1838 | ```cmd 1839 | git tag -d [tagname] 1840 | ``` 1841 | 1842 | Delete remote tag 1843 | 1844 | ```cmd 1845 | git push --delete origin [tagname] 1846 | ``` 1847 | 1848 | ## git show 1849 | 1850 | Generally, I only use it to see what has been modified in this commit. 1851 | 1852 | ```cmd 1853 | git show 1854 | ``` 1855 | 1856 | 1857 | 1858 | ```cmd 1859 | git show [] […​] 1860 | ``` 1861 | 1862 | For other more detailed introductions, please refer to [https://git-scm.com/docs/git-show](https://git-scm.com/docs/git-show) 1863 | 1864 | ## git diff 1865 | 1866 | The following is the official description: 1867 | 1868 | ```text 1869 | Show changes between commits, commit and working tree, etc 1870 | ``` 1871 | 1872 | Here are a few examples: 1873 | 1874 | The file has not yet entered the staging area (Stage), that is, before executing `git add xxx`. 1875 | 1876 | You can see what modifications have been made. 1877 | 1878 | 1879 | 1880 | You can also see the differences between commits. 1881 | 1882 | 1883 | 1884 | For other more detailed introductions, please refer to [https://git-scm.com/docs/git-diff](https://git-scm.com/docs/git-diff) 1885 | 1886 | ## git diff-tree 1887 | 1888 | git-diff-tree - Compares the content and mode of blobs found via two tree objects 1889 | 1890 | Compare the differences between two blobs (commits). 1891 | 1892 | The documentation can be found at [git-diff-tree](https://git-scm.com/docs/git-diff-tree/en) 1893 | 1894 | Let's look at an example directly: 1895 | 1896 | ```cmd 1897 | git diff-tree -r --no-commit-id --name-status -a --diff-filter=ACDMRT > changes.txt 1898 | ``` 1899 | 1900 | `-r` means Recurse into sub-trees. 1901 | 1902 | `--no-commit-id` This flag suppressed the commit ID output. 1903 | 1904 | `--name-status` Show only the name(s) and status of each changed file. 1905 | 1906 | `--text` `-a` Treat all files as text. 1907 | 1908 | `--diff-filter=[(A|C|D|M|R|T|U|X|B)…​[*]]` 1909 | 1910 | Added (A), Copied (C), Deleted (D), Modified (M), Renamed (R), 1911 | 1912 | have their type (i.e. regular file, symlink, submodule, …​) 1913 | 1914 | changed (T), are Unmerged (U), are Unknown (X), 1915 | 1916 | or have had their pairing Broken (B). 1917 | 1918 | After execution, open `change.txt` and you will see the names of the different files. 1919 | 1920 | ```text 1921 | M addons/account/i18n/account.pot 1922 | M addons/account/i18n/ar.po 1923 | M addons/account/i18n/az.po 1924 | M addons/account/i18n/be.po 1925 | M addons/account/i18n/bg.po 1926 | M addons/account/i18n/ca.po 1927 | M addons/account/i18n/cs.po 1928 | ...... 1929 | ``` 1930 | 1931 | ## git archive 1932 | 1933 | Continuing the example above, if you want to package the different files between different commits (you don't want to export the whole package because it's too big, you just want to find the different files), you can use the `archive` command. The example is as follows: 1934 | 1935 | ```cmd 1936 | git archive --format=zip --output=files-diff.zip HEAD $(git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT ) 1937 | ``` 1938 | 1939 | The exported zip file will be the complete files that are different between these two commits. 1940 | 1941 | ## git grep 1942 | 1943 | The following is the official description: 1944 | 1945 | ```text 1946 | git-grep - Print lines matching a pattern 1947 | ``` 1948 | 1949 | Simply put, it can help you find files that match a pattern. For example, if I want to find files whose content contains the pattern "hello", I can execute the following command: 1950 | 1951 | ```cmd 1952 | git grep "hello" 1953 | ``` 1954 | 1955 | 1956 | 1957 | It will show which file and which piece of code uses that pattern. 1958 | 1959 | For other more detailed introductions, please refer to [https://git-scm.com/docs/git-grep](https://git-scm.com/docs/git-grep) 1960 | 1961 | ## git clean 1962 | 1963 | Delete untracked files. 1964 | 1965 | `git clean -n` 1966 | 1967 | `-n, --dry-run` Don’t actually remove anything, just show what would be done 1968 | 1969 | This command tells you which data will be deleted, but it will not actually delete it. 1970 | 1971 | Example is as follows: 1972 | 1973 | ```cmd 1974 | ❯ git status 1975 | On branch master 1976 | 1977 | No commits yet 1978 | 1979 | Untracked files: 1980 | (use "git add ..." to include in what will be committed) 1981 | test.py 1982 | 1983 | nothing added to commit but untracked files present (use "git add" to track) 1984 | 1985 | ❯ git clean -n 1986 | Would remove test.py 1987 | ``` 1988 | 1989 | If you execute the following command, it will actually delete it. 1990 | 1991 | `git clean -df` 1992 | 1993 | For detailed instructions, you can use `git clean --help` to view. 1994 | 1995 | Example is as follows: 1996 | 1997 | ```cmd 1998 | ❯ git status 1999 | On branch master 2000 | 2001 | No commits yet 2002 | 2003 | Untracked files: 2004 | (use "git add ..." to include in what will be committed) 2005 | test.py 2006 | 2007 | nothing added to commit but untracked files present (use "git add" to track) 2008 | 2009 | ❯ git clean -df 2010 | Removing test.py 2011 | 2012 | ❯ git status 2013 | On branch master 2014 | 2015 | No commits yet 2016 | 2017 | nothing to commit (create/copy files and use "git add" to track) 2018 | ``` 2019 | 2020 | Do you still remember the `git reset` command introduced earlier? Basically, it can be used together with `git clean`. 2021 | 2022 | `git clean` affects untracked files. 2023 | 2024 | `git reset` affects tracked files. 2025 | 2026 | Combining the above, you can go back to a clean state of a specified commit. 2027 | 2028 | ```cmd 2029 | git reset --hard HEAD 2030 | git clean -df 2031 | git status 2032 | ``` 2033 | 2034 | I suggest everyone to operate it yourself. 2035 | 2036 | ## git Submodule 2037 | 2038 | Since this content is a bit more, I wrote another article. 2039 | 2040 | * [Youtube Tutorial PART 1 - git Submodule tutorial - how to create submodule](https://youtu.be/IDMWLJCbCGo) 2041 | 2042 | * [Youtube Tutorial PART 2 - git Submodule tutorial - how to update submodule](https://youtu.be/ogZoZOVyAYI) 2043 | 2044 | * [Youtube Tutorial PART 3 - git Submodule tutorial - how to clone submodule](https://youtu.be/f5_O5Iu6pJo) 2045 | 2046 | * [Youtube Tutorial PART 4 - git Submodule tutorial - how to remove submodule](https://youtu.be/imndFN7AvFA) 2047 | 2048 | [git Submodule tutorial :memo:](https://github.com/twtrubiks/Git-Tutorials/blob/master/git_submodule_turorial.md) 2049 | 2050 | ## git Subtree 2051 | 2052 | Since this content is a bit more, I wrote another article. 2053 | 2054 | * [Youtube Tutorial PART 1 - git subtree tutorial - how to create subtree](https://youtu.be/kEvgK2gH_vg) 2055 | 2056 | * [Youtube Tutorial PART 2 - git subtree tutorial - how to push subtree](https://youtu.be/Df3zc1VOqN8) 2057 | 2058 | * [Youtube Tutorial PART 3 - git subtree tutorial - how to pull/create subtree](https://youtu.be/dE-D2yrD4ws) 2059 | 2060 | [git subtree tutorial :memo:](https://github.com/twtrubiks/Git-Tutorials/blob/master/git_subtree_turorial.md) 2061 | 2062 | ## Other git settings 2063 | 2064 | We have already set `user.name` and `user.email`, but there are many other things that can be set in Git. 2065 | 2066 | Sometimes, we have to put some files (folders) into the Git working directory, but we cannot commit them. 2067 | 2068 | For example, password settings or things generated by the compiler IDE. 2069 | 2070 | Every time `git status` shows red Untracked files, it's usually a bit annoying...... 2071 | 2072 | Git has also thought about this problem for us. Just create a special **.gitignore** file in the root directory of the Git working area. 2073 | 2074 | Then enter the names of the files (files) to be ignored, and Git will automatically ignore these files. 2075 | 2076 | Of course, you don't need to write the `.gitignore` file from scratch. GitHub has already prepared some files for us [gitignore](https://github.com/github/gitignore) 2077 | 2078 | The **.gitignore** file can be placed directly under the directory. 2079 | 2080 | 2081 | 2082 | ### .gitignore file format example 2083 | 2084 | 2085 | 2086 | ### .gitignore (Temporarily and Permanently) 2087 | 2088 | It is mainly divided into temporary and permanent ignore. 2089 | 2090 | * Temporarily ignore 2091 | 2092 | It is suitable for settings files. Sometimes when we are developing, we will have our own settings. 2093 | 2094 | But this setting is not necessarily needed by everyone. At this time, you can temporarily ignore the changes of this file. 2095 | 2096 | Temporarily ignore a file 2097 | 2098 | ```cmd 2099 | git update-index --skip-worktree 2100 | ``` 2101 | 2102 | Resume temporarily ignoring a file 2103 | 2104 | ```cmd 2105 | git update-index --no-skip-worktree 2106 | ``` 2107 | 2108 | * Permanently ignore 2109 | 2110 | Here is a supplementary scenario. Suppose the file has been committed to git today. 2111 | 2112 | But I want to add it to `.gitignore`. What should I do? :question: 2113 | 2114 | If you add the file to `.gitignore`, you will find that it is still not ignored :confused: 2115 | 2116 | 2117 | 2118 | At this time, the correct way is to execute the following command first. 2119 | 2120 | ```cmd 2121 | git rm --cached 2122 | ``` 2123 | 2124 | After execution, commit again (the file will not be deleted from the system, it is just to update the git index). 2125 | 2126 | 2127 | 2128 | At this time, you can try to update the content of the file again, and you will find that it has been successfully ignored :smile: 2129 | 2130 | ### git alias 2131 | 2132 | Sometimes I often make typos or can't remember the commands. 2133 | 2134 | If we type `git st` to mean `git status`, that would be great!!! 2135 | 2136 | So we can set it ourselves, so that in the future, typing **git st = git status** 2137 | As shown in the picture below, originally `git st` could not be used. After setting it, it can be used. 2138 | 2139 | ```cmd 2140 | git config --global alias.st status 2141 | ``` 2142 | 2143 | 2144 | 2145 | ```cmd 2146 | git config --global alias.br branch 2147 | ``` 2148 | 2149 | 2150 | 2151 | ```cmd 2152 | git config --global alias.ck checkout 2153 | ``` 2154 | 2155 | ```cmd 2156 | git config --global alias.sw switch 2157 | ``` 2158 | 2159 | ```cmd 2160 | git config --global alias.cm commit 2161 | ``` 2162 | 2163 | ```cmd 2164 | git config --global alias.lg "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative" 2165 | ``` 2166 | 2167 | Turn this long string into an alias, so that in the future, you only need to execute `git lg`. 2168 | 2169 | 2170 | 2171 | Someone might ask, where is this configuration file? 2172 | 2173 | Usually, it is under your user. For example, the user of this computer is HJ, so the configuration file will be under **C:\Users\HJ**. 2174 | 2175 | It is a **hidden file .gitconfig**. If you open it, the format is as follows. 2176 | 2177 | 2178 | 2179 | I wonder if everyone has noticed the `--global` parameter. It means global. If you execute it today: 2180 | 2181 | ```cmd 2182 | git config alias.stu status 2183 | ``` 2184 | 2185 | It means that it only takes effect in that directory. 2186 | 2187 | So what's the use of this? Imagine a scenario where you want to use a specific email to push under a specific folder, while for other folders, you still use the company's email. At this time, it is very suitable to use this method. 2188 | 2189 | For more information and details, you can use the following command to view: 2190 | 2191 | ```cmd 2192 | man git-config 2193 | ``` 2194 | 2195 | ### git update 2196 | 2197 | ```cmd 2198 | sudo add-apt-repository ppa:git-core/ppa 2199 | sudo apt-get update 2200 | sudo apt-get install git 2201 | ``` 2202 | 2203 | 2204 | 2205 | ## Use Git to push to multiple different remotes at once 2206 | 2207 | If GitHub goes down one day, does that mean you can't work? You might say you still have a local copy? 2208 | 2209 | But......more backups are definitely a good thing!! Here I will introduce how to push to multiple different remotes at once. 2210 | 2211 | Here we use [Bitbucket](https://bitbucket.org/product) as an example. 2212 | 2213 | First, use the command below to check: 2214 | 2215 | ```cmd 2216 | git remote -v 2217 | ``` 2218 | 2219 | 2220 | 2221 | For more instructions on the `git remote` command, you can refer to the official documentation [git-remote](https://git-scm.com/docs/git-remote). 2222 | 2223 | Next, we use the following command to add an `origin` remote: 2224 | 2225 | ```cmd 2226 | git remote set-url --add origin 2227 | ``` 2228 | 2229 | ```cmd 2230 | git remote set-url --add origin git@github.com:twtrubiks/test2.git 2231 | ``` 2232 | 2233 | 2234 | 2235 | Let's use `git remote -v` to check again. You will find that the remote we just added has been added. 2236 | 2237 | 2238 | 2239 | Finally, let's push again. 2240 | 2241 | 2242 | 2243 | Look carefully, isn't it pushing to multiple different remotes at once? Very convenient!! 2244 | 2245 | ***GitHub*** 2246 | 2247 | 2248 | 2249 | ***Bitbucket*** 2250 | 2251 | 2252 | 2253 | P.S. The configuration file is in the hidden file ".git" under the folder. There is a `config` file inside. 2254 | 2255 | 2256 | 2257 | Supplement a few `git remote` commands. It also supports `rename` and `remove`. 2258 | 2259 | The current remote is as follows: 2260 | 2261 | 2262 | 2263 | Let's rename the remote. The syntax is as follows: 2264 | 2265 | ```text 2266 | git remote rename 2267 | ``` 2268 | 2269 | ```text 2270 | git remote rename origin2 origin 2271 | ``` 2272 | 2273 | After execution, you will find that the remote has been successfully renamed to `origin`. 2274 | 2275 | 2276 | 2277 | Next, let's try `remove`. The syntax is as follows: 2278 | 2279 | ```text 2280 | git remote remove 2281 | ``` 2282 | 2283 | ```text 2284 | git remote remove origin 2285 | ``` 2286 | 2287 | Successfully deleted. Now the remote is empty. 2288 | 2289 | 2290 | 2291 | Next, let's try to add a remote. The command is as follows: 2292 | 2293 | ```text 2294 | git remote add [-t ] [-m ] [-f] [--[no-]tags] [--mirror=] 2295 | ``` 2296 | 2297 | ```cmd 2298 | git remote add origin git@github.com:blue-rubiks/t11.git 2299 | ``` 2300 | 2301 | 2302 | 2303 | If we want to modify the url of `origin`, we can use: 2304 | 2305 | ```cmd 2306 | git remote set-url origin git@blue.github.com:blue-rubiks/t11.git 2307 | ``` 2308 | 2309 | 2310 | 2311 | ## Multiple SSH Keys settings for different github account 2312 | 2313 | * [Youtube Tutorial - Multiple SSH Keys settings for different github account](https://youtu.be/gDxG-4tF7B8) 2314 | 2315 | [Multiple SSH Keys settings for different github account](https://github.com/twtrubiks/Git-Tutorials/blob/master/Multiple_SSH_Keys_settings.md) 2316 | 2317 | ## Git-Flow Basic Tutorial and Concepts 2318 | 2319 | * [Git-Flow Tutorials - youtube](https://youtu.be/zXlta66thZY) 2320 | 2321 | * [Git-Flow SmartGit Tutorials - youtube](https://youtu.be/ualXHytifbg) 2322 | 2323 | [Git-Flow Basic Tutorial and Concepts](https://github.com/twtrubiks/Git-Tutorials/tree/master/Git-Flow) 2324 | 2325 | ## PR (Pull Request) Tutorial 2326 | 2327 | * [Youtube Tutorial - github PR (Pull Request) tutorial](https://youtu.be/bXOdD-bKfkA) - [Article Quick Link](https://github.com/twtrubiks/Git-Tutorials/tree/master/pr-tutorial#github-pr-pull-request-%E6%95%99%E5%AD%B8) 2328 | 2329 | * [Youtube Tutorial - github CLI PR tutorial - gh](https://youtu.be/AD8X11lq3gQ) - [Article Quick Link](https://github.com/twtrubiks/Git-Tutorials/tree/master/pr-tutorial#github-cli-pr-%E6%95%99%E5%AD%B8) 2330 | 2331 | [PR (Pull Request) Tutorial](https://github.com/twtrubiks/Git-Tutorials/tree/master/pr-tutorial) 2332 | 2333 | ## Linux Notes 2334 | 2335 | * [Youtube Tutorial - Linux Tutorial - git ignore file mode (chmod) changes](https://youtu.be/QCh2k903Yak) 2336 | 2337 | Here I will talk about some problems you may encounter when using git on both windows and linux. 2338 | 2339 | First, execute the following command on linux: 2340 | 2341 | ```cmd 2342 | sudo chmod -R 777 folder 2343 | ``` 2344 | 2345 | git will default it as a change. How to ignore it? Please execute the following command: 2346 | 2347 | ```cmd 2348 | git config core.fileMode false 2349 | ``` 2350 | 2351 | You can also refer to this article [Git ignore file mode (chmod) changes](https://stackoverflow.com/questions/1580596/how-do-i-make-git-ignore-file-mode-chmod-changes) 2352 | 2353 | ### Formatting 2354 | 2355 | `core.autocrlf` 2356 | 2357 | Windows uses both Enter (Carriage Return, abbreviated as CR) and Line Feed (abbreviated as LF) to define a new line. 2358 | 2359 | While Mac and Linux only use one Line Feed (LF) character. 2360 | 2361 | So it will cause problems in cross-platform collaboration. 2362 | 2363 | You can set it like this on windows (meaning LF will be converted to CRLF): 2364 | 2365 | ```cmd 2366 | git config --global core.autocrlf true 2367 | ``` 2368 | 2369 | Linux or Mac system: 2370 | 2371 | ```cmd 2372 | git config --global core.autocrlf input 2373 | ``` 2374 | 2375 | With the above settings, CRLF will be preserved on Windows, and LF will be preserved on Mac and Linux and in the repo. 2376 | 2377 | If you want to understand more deeply, you can refer to [Formatting-core.autocrlf](https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration#Formatting-and-Whitespace). 2378 | 2379 | ### Modify editor 2380 | 2381 | ```cmd 2382 | git config --global core.editor "vim" 2383 | ``` 2384 | 2385 | ## Reference 2386 | 2387 | * [13 Git tips for Git's 13th birthday](https://opensource.com/article/18/4/git-tips) 2388 | 2389 | ## Donation 2390 | 2391 | The articles are all original after my own research and internalization. If it has helped you and you want to encourage me, you are welcome to buy me a cup of coffee :laughing: 2392 | 2393 | ECPAY (no membership registration required) 2394 | 2395 | ![alt tag](https://payment.ecpay.com.tw/Upload/QRCode/201906/QRCode_672351b8-5ab3-42dd-9c7c-c24c3e6a10a0.png) 2396 | 2397 | [Sponsor Payment](http://bit.ly/2F7Jrha) 2398 | 2399 | O'Pay (membership registration required) 2400 | 2401 | 2402 | 2403 | [Sponsor Payment](https://payment.opay.tw/Broadcaster/Donate/9E47FDEF85ABE383A0F5FC6A218606F8) 2404 | 2405 | ## Sponsor List 2406 | 2407 | [Sponsor List](https://github.com/twtrubiks/Thank-you-for-donate) 2408 | -------------------------------------------------------------------------------- /git_submodule_turorial.md: -------------------------------------------------------------------------------- 1 | [English Version](git_submodule_turorial_en.md) 2 | 3 | # git Submodule tutorial :memo: 4 | 5 | 手把手帶大家動手做 [Youtube Tutorial PART 1 - git Submodule tutorial - how to create submodule](https://youtu.be/IDMWLJCbCGo), 6 | 7 | git Submodule 的官方文件可參考 [git-submodule](https://git-scm.com/docs/git-submodule/)。 8 | 9 | 在開始介紹前,我們先來談談何時會使用到 git Submodule, 10 | 11 | 大家一定有遇過這種情境,就是 A repo 依賴 B repo,這邊你可能會問我, 12 | 13 | 阿為什麼不直接把兩個 repo 變成一個 repo 就好呢 :question: 14 | 15 | 這樣不是增加遊戲困難度 :question: 16 | 17 | 有時候,這個 B repo 可能是 github 上一個很多人維護超強的東西, 18 | 19 | 你不可能自己維護,更何況,不要重造輪子,我們要站上巨人的肩膀上 :kissing_smiling_eyes: 20 | 21 | 好,現在我們拉回來,當每次我們發現 B repo 有更新時,我們以前的做法 22 | 23 | 可能就要單獨把他 clone 下來,然後在複製進我們的 A repo 中, 24 | 25 | 但其實這樣真的太土炮,而且非常沒有效率 :expressionless: 26 | 27 | 那我們該怎麼辦呢 :question: 28 | 29 | 有沒有一種東西,可以把它整合,甚至我們執行一個指令,就可以把全部 30 | 31 | 我們依賴的 repo 都做檢查並且更新,如果是這樣的話,整個流程會更安全, 32 | 33 | 也更不會出錯 :relaxed: 34 | 35 | 答案是有的 :thumbsup: 36 | 37 | 那就是我們接下來要介紹的 git Submodule,如果你也有上述的使用情境, 38 | 39 | 那你就更需要學習 git Submodule 了 :flushed: 40 | 41 | 再更簡單一點的說明,你可以把它想成是一個很大的主 repo,然後裡面依賴 42 | 43 | 很多 A repo、B repo、C repo 之類,而這些 A repo,B repo,C repo,你就可以 44 | 45 | 把它想成是 Submodule,這樣是不是更清楚了呢 :grin: 46 | 47 | 既然了解了使用情境,那我們就來看看它該怎麼使用 :satisfied: 48 | 49 | ## how to create submodule 50 | 51 | 手把手帶大家動手做 [Youtube Tutorial PART 1 - git Submodule tutorial - how to create submodule](https://youtu.be/IDMWLJCbCGo), 52 | 53 | 先介紹一下 repo ,這邊有兩個 repo,分別為 main_project repo 以及 a_project repo, 54 | 55 | main_project repo 內容如下, 56 | 57 | ![alt tag](https://i.imgur.com/mLaKJyX.png) 58 | 59 | 接著,我們要建立 a_project 為 Submodule,建立 Submodule 的語法如下, 60 | 61 | ```cmd 62 | git submodule add [] [--] [] 63 | ``` 64 | 65 | 實際範例 66 | 67 | ```cmd 68 | git submodule add git@blue.github.com:blue-rubiks/a_project.git 69 | ``` 70 | 71 | ![alt tag](https://i.imgur.com/bexQ5Op.png) 72 | 73 | 這時候,你如果觀察資料夾,會發現多出一些東西 74 | 75 | ![alt tag](https://i.imgur.com/djYEPu9.png) 76 | 77 | 也可以用 `git status` 觀察, 78 | 79 | ![alt tag](https://i.imgur.com/I9jFYOb.png) 80 | 81 | 主要是多了 `.gitmodules` 以及 `a_project`, 82 | 83 | 可以看一下 `.gitmodules` 的內容,裡面就是 path 和 url 84 | 85 | ![alt tag](https://i.imgur.com/kk8qfTE.png) 86 | 87 | 雖然表面上是兩個改動,但實際上卻還有一個改動, 88 | 89 | 它存在 `main_project/.git/config` 中 90 | 91 | ![alt tag](https://i.imgur.com/mmyFYAk.png) 92 | 93 | 為什麼我要特別說這個,因為假如今天你想要移除 submodule, 94 | 95 | 你就必須到這裡移除和 submodule 相關的內容。 96 | 97 | 接著一些網路上的教學會說需要再執行以下兩個指令, 98 | 99 | ```cmd 100 | git submodule init 101 | git submodule update 102 | ``` 103 | 104 | 但我自己測試似乎是不需要的 ( 雖然執行了也不會有什麼影響 ), 105 | 106 | 所以就先記錄就好,不要理它 :smirk: 107 | 108 | 接著就一般 git 的操作後,push 就完成了 :smiley: 109 | 110 | ![alt tag](https://i.imgur.com/8c5ygMn.png) 111 | 112 | 可以使用 `git submodule status` 查看目前 submodule 的狀態 113 | 114 | ```cmd 115 | git submodule status 116 | ``` 117 | 118 | ![alt tag](https://i.imgur.com/44bW6Bs.png) 119 | 120 | push 之後,可以到 github 網頁上看, 121 | 122 | 會發現在 main_project repo 中的 submodule 有一個小圖示, 123 | 124 | 並且有一個 commit id, 125 | 126 | ![alt tag](https://i.imgur.com/K0Z5tAa.png) 127 | 128 | 而這個 commit id,就是 a_project repo 中的 commit id, 129 | 130 | ![alt tag](https://i.imgur.com/rcyWCGW.png) 131 | 132 | 學習完了如何建立 Submodule,接下來來看看如何更新 Submodule。 133 | 134 | ## how to update submodule 135 | 136 | 手把手帶大家動手做 [Youtube Tutorial PART 2 - git Submodule tutorial - how to update submodule](https://youtu.be/ogZoZOVyAYI), 137 | 138 | 假設 a_project repo 也是我們自己維護的,當我們對它更新時,會發生什麼事情 :question: 139 | 140 | 如果你進入 a_project repo 中,你會發現它其實就是一個我們常見的 git repo, 141 | 142 | 可以正常的 commit 以及 push, 143 | 144 | ![alt tag](https://i.imgur.com/1REbrbK.png) 145 | 146 | **只是你從外面 ( 也就是 main_project repo ) 看到的是像一種參照而已**, 147 | 148 | 上面這句話很重要,因為我們現在更新了 a_project repo ,可是 main_project 並不 149 | 150 | 知道它有任何更新,切回 main_project repo 中,你會發現確實有更動, 151 | 152 | ![alt tag](https://i.imgur.com/jlPgz8P.png) 153 | 154 | 所以這時候就必須要執行一般的 git 操作並且 push, 155 | 156 | ![alt tag](https://i.imgur.com/bM8Ow3O.png) 157 | 158 | 這樣才算是成功,最後這一步驟 **很重要**,有些人會忘記,如果你沒有加上這步驟, 159 | 160 | 你的 main_project repo 就不知道 a_project repo 有做修改,儘管它是參照的概念, 161 | 162 | 還是需要此步驟。 163 | 164 | 剛剛我們是自己改動 a_project repo,現在另一種情境,是別人改動 a_project repo, 165 | 166 | 那這時候,我們如何更新 Submodule 呢 :question: 167 | 168 | 剛剛說過如果我們進去 a_project repo 中,它就像一個正常的 git 一樣, 169 | 170 | 所以依照這個原則, 171 | 172 | 我只要進去 a_project repo 中,並切執行 `git pull` 即可,如下圖, 173 | 174 | ( 為了方便,我直接去 github 網頁端新增一個檔案,模擬有人更新 repo ) 175 | 176 | ![alt tag](https://i.imgur.com/M9noUKg.png) 177 | 178 | 但是,因為現在是只有一個 Submodule ,如果你有 N 個 Submodule 呢 ? 179 | 180 | 這樣我相信會非常麻煩,因為要一個一個更新 :sob: 181 | 182 | 所以,比較好的方法應該是使用以下指令一次更新, 183 | 184 | ``` cmd 185 | git submodule update --remote 186 | ``` 187 | 188 | 更多詳細的 update 可參考 [git-submodule-update](https://git-scm.com/docs/git-submodule#git-submodule-update--init--remote-N--no-fetch--no-recommend-shallow-f--force--checkout--rebase--merge--referenceltrepositorygt--depthltdepthgt--recursive--jobsltngt--ltpathgt82308203), 189 | 190 | `--remote` 的更多說明可以參考 [官方的說明](https://git-scm.com/docs/git-submodule#git-submodule---remote), 191 | 192 | ![alt tag](https://i.imgur.com/m2ICwPY.png) 193 | 194 | 可是這時候你如果切到 a_project repo 你會發現它竟然 **HEAD detached** 了 :confused: 195 | 196 | ![alt tag](https://i.imgur.com/JHMn26S.png) 197 | 198 | 其實這是正常的,因為默認的行為會將它 checkout 到最新的 commit id 上, 199 | 200 | 以這個範例 `HEAD detached at 066d0db` 來說, 201 | 202 | 就是執行了 `git checkout '066d0db'`,所以我們要修正它, 203 | 204 | 修正方法如下圖, 205 | 206 | ![alt tag](https://i.imgur.com/VfwxJW4.png) 207 | 208 | 簡單說就是要建立一個分支 ,然後再回到 master merge 該 ( `066d0db` ) 分支。 209 | 210 | 但這樣步驟真的有點多 :sweat_smile: 211 | 212 | 所以更簡單的方法,可以直接使用以下指令, 213 | 214 | ```cmd 215 | git submodule update --remote --merge 216 | ``` 217 | 218 | `--merge` 的 [官方說明](https://git-scm.com/docs/git-submodule#git-submodule-merge) 如下 219 | 220 | ```text 221 | the commit recorded in the superproject will be merged into the current branch in the submodule. 222 | ``` 223 | 224 | ![alt tag](https://i.imgur.com/0YmQoP4.png) 225 | 226 | 或 227 | 228 | ```cmd 229 | git submodule update --remote --rebase 230 | ``` 231 | 232 | `--rebase` 的 [官方說明](https://git-scm.com/docs/git-submodule#git-submodule-rebase) 如下 233 | 234 | ```text 235 | the current branch of the submodule will be rebased onto the commit recorded in the superproject. 236 | ``` 237 | 238 | ![alt tag](https://i.imgur.com/MHLvL8T.png) 239 | 240 | 這種作法更方便,也解決了 **HEAD detached** 的問題 :thumbsup: 241 | 242 | ## how to clone submodule 243 | 244 | 手把手帶大家動手做 [Youtube Tutorial PART 3 - git Submodule tutorial - how to clone submodule](https://youtu.be/f5_O5Iu6pJo), 245 | 246 | 這邊教大家如何 clone Submodule 的專案,執行一般的 clone 指令, 247 | 248 | ```cmd 249 | git clone git@github.com:blue-rubiks/main_project.git 250 | ``` 251 | 252 | 這時候我們看一下資料夾, 253 | 254 | ![alt tag](https://i.imgur.com/aiTpngo.png) 255 | 256 | 的確有將 a_project repo 一起 clone 下來,但不要高興的太早, 257 | 258 | 因為,你如果進去看這些資料夾查看,你會發現它的內容都是空的 :sob: 259 | 260 | ![alt tag](https://i.imgur.com/xZuY1tm.png) 261 | 262 | 這時候,我們要先 init submodule, 263 | 264 | ```cmd 265 | git submodule init 266 | ``` 267 | 268 | ![alt tag](https://i.imgur.com/bK6IzKW.png) 269 | 270 | 執行之後,它會將 `.gitmodules` 中的 url 寫入 `.git/config` 裡面, 271 | 272 | ![alt tag](https://i.imgur.com/vknhZHR.png) 273 | 274 | 這時候,submodule 都設定好了, 275 | 276 | 只需要再執行 update ( update 會依照 `.git/config` 裡面的設定更新 ), 277 | 278 | 指令如下, 279 | 280 | ```cmd 281 | git submodule update --recursive 282 | ``` 283 | 284 | 以下為 `--recursive` 官方說明 285 | 286 | ```text 287 | If --recursive is specified, this command will recurse into the registered submodules, and update any nested submodules within. 288 | ``` 289 | 290 | ![alt tag](https://i.imgur.com/SJFZW0U.png) 291 | 292 | 成功將 submodule 的內容 clone 下來了 :smile: 293 | 294 | ![alt tag](https://i.imgur.com/G9BXL77.png) 295 | 296 | 上面的兩段指令,也可以合併成一段, 297 | 298 | ```cmd 299 | git submodule update --init --recursive 300 | ``` 301 | 302 | 如果我們一開始就知道我們要 clone 的專案本身就有包含 submodule, 303 | 304 | 可以直接執行以下指令, 305 | 306 | ```cmd 307 | git clone --recurse-submodules git@github.com:blue-rubiks/main_project.git 308 | ``` 309 | 310 | 一次就完成全部的部分,包含 init 以及更新 submodule :satisfied: 311 | 312 | 詳細說明可參考 [git-clone---recurse-submodulesltpathspec](https://git-scm.com/docs/git-clone#git-clone---recurse-submodulesltpathspec), 313 | 314 | 但通常我們應該是不會一開始就知道這個專案有包含 submodule。 315 | 316 | ### how to remove submodule 317 | 318 | 手把手帶大家動手做 [Youtube Tutorial PART 4 - git Submodule tutorial - how to remove submodule](https://youtu.be/imndFN7AvFA), 319 | 320 | 要移除 submodule 的步驟比較多,這邊教大家如何移除, 321 | 322 | ```cmd 323 | git submodule deinit a_project 324 | ``` 325 | 326 | ![alt tag](https://i.imgur.com/YcUK6gU.png) 327 | 328 | 其實 deinit 就是刪除 `.git/config` 裡面的 submodule 相關設定, 329 | 330 | ![alt tag](https://i.imgur.com/Ek30GqW.png) 331 | 332 | 接著打開 `.gitmodules` 移除相關的設定,在這邊因為我們只有一個 submodule, 333 | 334 | 所以直接將 `.gitmodules` 刪除即可。 335 | 336 | ( 如果只要刪除特定的 submodule,就進去刪除相關的部分 ) 337 | 338 | 接著使用 git rm 指令,詳細用法可參考 [git-rm](https://git-scm.com/docs/git-rm), 339 | 340 | ```cmd 341 | git rm --cached a_project 342 | ``` 343 | 344 | ![alt tag](https://i.imgur.com/YVHYuDE.png) 345 | 346 | 如果有加上 `--cached` ,本地端的 submodule repo 會被保留,所以 347 | 348 | 如果沒有要保留,可以不加上 `--cached`,將會把本地端的 submodule repo 刪除。 349 | 350 | 接著再移除 `.git/modules/a_project`, 351 | 352 | ```cmd 353 | rm -rf .git/modules/a_project 354 | ``` 355 | 356 | 接著就 `git add` 以及 `git commit` 357 | 358 | ![alt tag](https://i.imgur.com/LHCpjtg.png) 359 | 360 | 最後在把空的 a_project 資料夾刪除 361 | 362 | ```cmd 363 | rm -rf a_project 364 | ``` 365 | 366 | 步驟雖然多了點,但其實不難 :smirk: 367 | 368 | ### Synchronizes Submodule 369 | 370 | 詳細可參考 [git-submodule-sync](https://git-scm.com/docs/git-submodule#git-submodule-sync--recursive--ltpathgt82308203) 371 | 372 | 以下為官方說明 373 | 374 | ```text 375 | Synchronizes submodules' remote URL configuration setting to the value specified in .gitmodules. 376 | ``` 377 | 378 | 有時候如果 Submodule 的 remote url 有變更的時候,就需要使用此指令更新。 379 | 380 | ## Donation 381 | 382 | 文章都是我自己研究內化後原創,如果有幫助到您,也想鼓勵我的話,歡迎請我喝一杯咖啡 :laughing: 383 | 384 | ![alt tag](https://i.imgur.com/LRct9xa.png) 385 | 386 | [贊助者付款](https://payment.opay.tw/Broadcaster/Donate/9E47FDEF85ABE383A0F5FC6A218606F8) 387 | 388 | ## License 389 | 390 | MIT license 391 | -------------------------------------------------------------------------------- /git_submodule_turorial_en.md: -------------------------------------------------------------------------------- 1 | # git Submodule tutorial :memo: 2 | 3 | [English Version](git_submodule_turorial_en.md) 4 | 5 | Step-by-step guide [Youtube Tutorial PART 1 - git Submodule tutorial - how to create submodule](https://youtu.be/IDMWLJCbCGo). 6 | 7 | The official documentation for git Submodule can be found at [git-submodule](https://git-scm.com/docs/git-submodule/). 8 | 9 | Before we start, let's talk about when to use git Submodule. 10 | 11 | Everyone must have encountered a situation where repo A depends on repo B. Here you might ask me, 12 | 13 | why not just combine the two repos into one? :question: 14 | 15 | Isn't that just making the game harder? :question: 16 | 17 | Sometimes, this B repo might be a super powerful thing on GitHub maintained by many people. 18 | 19 | You can't maintain it yourself, and what's more, don't reinvent the wheel, we should stand on the shoulders of giants :kissing_smiling_eyes: 20 | 21 | Okay, now let's get back to it. When we find that B repo has been updated, our previous method 22 | 23 | might be to clone it separately and then copy it into our A repo. 24 | 25 | But this is really too primitive and very inefficient :expressionless: 26 | 27 | So what should we do? :question: 28 | 29 | Is there something that can integrate it, or even a command we can run to check and update all 30 | 31 | the repos we depend on? If so, the whole process would be safer and less prone to errors :relaxed: 32 | 33 | The answer is yes :thumbsup: 34 | 35 | That's what we're going to introduce next, git Submodule. If you have the above usage scenario, 36 | 37 | then you need to learn git Submodule even more :flushed: 38 | 39 | To put it more simply, you can think of it as a large main repo that depends on 40 | 41 | many A repos, B repos, C repos, etc. And these A repos, B repos, C repos, you can 42 | 43 | think of them as Submodules. Isn't that clearer? :grin: 44 | 45 | Now that we understand the usage scenario, let's see how to use it :satisfied: 46 | 47 | ## how to create submodule 48 | 49 | Step-by-step guide [Youtube Tutorial PART 1 - git Submodule tutorial - how to create submodule](https://youtu.be/IDMWLJCbCGo). 50 | 51 | First, let me introduce the repos. There are two repos here, main_project repo and a_project repo. 52 | 53 | The content of main_project repo is as follows: 54 | 55 | 56 | 57 | Next, we want to create a_project as a Submodule. The syntax for creating a Submodule is as follows: 58 | 59 | ```cmd 60 | git submodule add [] [--] [] 61 | ``` 62 | 63 | Actual example: 64 | 65 | ```cmd 66 | git submodule add git@blue.github.com:blue-rubiks/a_project.git 67 | ``` 68 | 69 | 70 | 71 | At this point, if you look at the folder, you will find that some things have been added. 72 | 73 | 74 | 75 | You can also observe with `git status`. 76 | 77 | 78 | 79 | Mainly, `.gitmodules` and `a_project` have been added. 80 | 81 | You can look at the content of `.gitmodules`. It contains the path and url. 82 | 83 | 84 | 85 | Although it seems like two changes on the surface, there is actually another change. 86 | 87 | It exists in `main_project/.git/config`. 88 | 89 | 90 | 91 | Why do I specifically mention this? Because if you want to remove a submodule today, 92 | 93 | you must go here to remove the content related to the submodule. 94 | 95 | Next, some online tutorials will say that you need to execute the following two commands again: 96 | 97 | ```cmd 98 | git submodule init 99 | git submodule update 100 | ``` 101 | 102 | But I tested it myself and it seems unnecessary (although executing it won't have any effect). 103 | 104 | So just record it for now and ignore it :smirk: 105 | 106 | Then, after the general git operations, push and you're done :smiley: 107 | 108 | 109 | 110 | You can use `git submodule status` to view the current status of the submodule. 111 | 112 | ```cmd 113 | git submodule status 114 | ``` 115 | 116 | 117 | 118 | After pushing, you can go to the GitHub webpage to see. 119 | 120 | You will find that there is a small icon in the submodule in the main_project repo. 121 | 122 | And there is a commit id. 123 | 124 | 125 | 126 | And this commit id is the commit id in the a_project repo. 127 | 128 | 129 | 130 | After learning how to create a Submodule, let's see how to update a Submodule. 131 | 132 | ## how to update submodule 133 | 134 | Step-by-step guide [Youtube Tutorial PART 2 - git Submodule tutorial - how to update submodule](https://youtu.be/ogZoZOVyAYI). 135 | 136 | Suppose the a_project repo is also maintained by ourselves. What happens when we update it? :question: 137 | 138 | If you enter the a_project repo, you will find that it is actually a common git repo. 139 | 140 | You can commit and push normally. 141 | 142 | 143 | 144 | **It's just that from the outside (that is, the main_project repo), you see it as a kind of reference.** 145 | 146 | The above sentence is very important, because we have now updated the a_project repo, but main_project does not 147 | 148 | know that it has any updates. Switch back to the main_project repo, and you will find that there are indeed changes. 149 | 150 | 151 | 152 | So at this time, you must perform general git operations and push. 153 | 154 | 155 | 156 | This is considered successful. This last step is **very important**. Some people will forget it. If you don't add this step, 157 | 158 | your main_project repo will not know that the a_project repo has been modified. Although it is a reference concept, 159 | 160 | this step is still needed. 161 | 162 | Just now we modified the a_project repo ourselves. Now another scenario is that someone else modified the a_project repo. 163 | 164 | At this time, how do we update the Submodule? :question: 165 | 166 | As I said just now, if we go into the a_project repo, it's like a normal git. 167 | 168 | So according to this principle, 169 | 170 | I just need to go into the a_project repo and execute `git pull`, as shown below. 171 | 172 | (For convenience, I directly added a file on the GitHub webpage to simulate someone updating the repo) 173 | 174 | 175 | 176 | But, because there is only one Submodule now, what if you have N Submodules? 177 | 178 | I believe it will be very troublesome because you have to update them one by one :sob: 179 | 180 | So, a better way is to use the following command to update them all at once: 181 | 182 | ``` cmd 183 | git submodule update --remote 184 | ``` 185 | 186 | For more detailed updates, please refer to [git-submodule-update](https://git-scm.com/docs/git-submodule#git-submodule-update--init--remote-N--no-fetch--no-recommend-shallow-f--force--checkout--rebase--merge--referenceltrepositorygt--depthltdepthgt--recursive--jobsltngt--ltpathgt82308203). 187 | 188 | For more information on `--remote`, you can refer to the [official documentation](https://git-scm.com/docs/git-submodule#git-submodule---remote). 189 | 190 | 191 | 192 | But at this time, if you switch to the a_project repo, you will find that it is **HEAD detached** :confused: 193 | 194 | 195 | 196 | This is actually normal, because the default behavior will check it out to the latest commit id. 197 | 198 | Take this example `HEAD detached at 066d0db`. 199 | 200 | It is to execute `git checkout '066d0db'`, so we need to fix it. 201 | 202 | The correction method is as shown below: 203 | 204 | 205 | 206 | Simply put, you need to create a branch, and then go back to master and merge that (`066d0db`) branch. 207 | 208 | But this step is really a bit much :sweat_smile: 209 | 210 | So a simpler method is to directly use the following command: 211 | 212 | ```cmd 213 | git submodule update --remote --merge 214 | ``` 215 | 216 | The [official documentation](https://git-scm.com/docs/git-submodule#git-submodule-merge) for `--merge` is as follows: 217 | 218 | ```text 219 | the commit recorded in the superproject will be merged into the current branch in the submodule. 220 | ``` 221 | 222 | 223 | 224 | or 225 | 226 | ```cmd 227 | git submodule update --remote --rebase 228 | ``` 229 | 230 | The [official documentation](https://git-scm.com/docs/git-submodule#git-submodule-rebase) for `--rebase` is as follows: 231 | 232 | ```text 233 | the current branch of the submodule will be rebased onto the commit recorded in the superproject. 234 | ``` 235 | 236 | 237 | 238 | This method is more convenient and also solves the **HEAD detached** problem :thumbsup: 239 | 240 | ## how to clone submodule 241 | 242 | Step-by-step guide [Youtube Tutorial PART 3 - git Submodule tutorial - how to clone submodule](https://youtu.be/f5_O5Iu6pJo). 243 | 244 | Here's how to clone a Submodule project. Execute the general clone command: 245 | 246 | ```cmd 247 | git clone git@github.com:blue-rubiks/main_project.git 248 | ``` 249 | 250 | At this time, let's look at the folder. 251 | 252 | 253 | 254 | It did clone the a_project repo together, but don't be too happy too soon. 255 | 256 | Because, if you go in and look at these folders, you will find that their contents are empty :sob: 257 | 258 | 259 | 260 | At this time, we need to init the submodule first. 261 | 262 | ```cmd 263 | git submodule init 264 | ``` 265 | 266 | 267 | 268 | After execution, it will write the url in `.gitmodules` into `.git/config`. 269 | 270 | 271 | 272 | At this time, the submodule is all set up. 273 | 274 | You just need to execute update again (update will update according to the settings in `.git/config`). 275 | 276 | The command is as follows: 277 | 278 | ```cmd 279 | git submodule update --recursive 280 | ``` 281 | 282 | The following is the official documentation for `--recursive`: 283 | 284 | ```text 285 | If --recursive is specified, this command will recurse into the registered submodules, and update any nested submodules within. 286 | ``` 287 | 288 | 289 | 290 | Successfully cloned the content of the submodule :smile: 291 | 292 | 293 | 294 | The above two commands can also be combined into one: 295 | 296 | ```cmd 297 | git submodule update --init --recursive 298 | ``` 299 | 300 | If we know from the beginning that the project we want to clone contains submodules, 301 | 302 | we can directly execute the following command: 303 | 304 | ```cmd 305 | git clone --recurse-submodules git@github.com:blue-rubiks/main_project.git 306 | ``` 307 | 308 | Complete all parts at once, including init and updating submodules :satisfied: 309 | 310 | For detailed instructions, please refer to [git-clone---recurse-submodulesltpathspec](https://git-scm.com/docs/git-clone#git-clone---recurse-submodulesltpathspec). 311 | 312 | But usually we probably won't know from the beginning that this project contains submodules. 313 | 314 | ### how to remove submodule 315 | 316 | Step-by-step guide [Youtube Tutorial PART 4 - git Submodule tutorial - how to remove submodule](https://youtu.be/imndFN7AvFA). 317 | 318 | The steps to remove a submodule are a bit more. Here's how to remove it: 319 | 320 | ```cmd 321 | git submodule deinit a_project 322 | ``` 323 | 324 | 325 | 326 | In fact, `deinit` is to delete the submodule related settings in `.git/config`. 327 | 328 | 329 | 330 | Next, open `.gitmodules` and remove the related settings. Here, because we only have one submodule, 331 | 332 | we can just delete `.gitmodules`. 333 | 334 | (If you only want to delete a specific submodule, go in and delete the relevant parts) 335 | 336 | Next, use the `git rm` command. For detailed usage, please refer to [git-rm](https://git-scm.com/docs/git-rm). 337 | 338 | ```cmd 339 | git rm --cached a_project 340 | ``` 341 | 342 | 343 | 344 | If `--cached` is added, the local submodule repo will be preserved. So 345 | 346 | if you don't want to preserve it, you can omit `--cached`, which will delete the local submodule repo. 347 | 348 | Next, remove `.git/modules/a_project`. 349 | 350 | ```cmd 351 | rm -rf .git/modules/a_project 352 | ``` 353 | 354 | Then `git add` and `git commit`. 355 | 356 | 357 | 358 | Finally, delete the empty a_project folder. 359 | 360 | ```cmd 361 | rm -rf a_project 362 | ``` 363 | 364 | The steps are a bit more, but it's not difficult :smirk: 365 | 366 | ### Synchronizes Submodule 367 | 368 | For details, please refer to [git-submodule-sync](https://git-scm.com/docs/git-submodule#git-submodule-sync--recursive--ltpathgt82308203) 369 | 370 | The following is the official documentation: 371 | 372 | ```text 373 | Synchronizes submodules' remote URL configuration setting to the value specified in .gitmodules. 374 | ``` 375 | 376 | Sometimes if the remote url of the Submodule has changed, you need to use this command to update it. 377 | 378 | ## Donation 379 | 380 | The articles are all original after my own research and internalization. If it has helped you and you want to encourage me, you are welcome to buy me a cup of coffee :laughing: 381 | 382 | 383 | 384 | [Sponsor Payment](https://payment.opay.tw/Broadcaster/Donate/9E47FDEF85ABE383A0F5FC6A218606F8) 385 | 386 | ## License 387 | 388 | MIT license 389 | -------------------------------------------------------------------------------- /git_subtree_turorial.md: -------------------------------------------------------------------------------- 1 | [English Version](git_subtree_turorial_en.md) 2 | 3 | # git subtree tutorial :memo: 4 | 5 | 手把手帶大家動手做 [Youtube Tutorial PART 1 - git subtree tutorial - how to create subtree](https://youtu.be/kEvgK2gH_vg) 6 | 7 | 還記得之前和大家介紹過 [git Submodule tutorial 📝](https://github.com/twtrubiks/Git-Tutorials/blob/master/git_submodule_turorial.md) 這個指令嗎 :question: 8 | 9 | 忘記得趕快去複習一下吧 :smirk: 10 | 11 | 今天我要再來介紹一個類似功能的指令,名稱叫做 **git-subtree**,如果你用 git-subtree 和 git submodule 12 | 13 | 去 google,你會發現蠻多人拿這兩個來比較的,然後都說什麼 git-subtree 比較推薦之類的,但我這邊先 14 | 15 | 不下結論,結論在最後面,我先帶大家來了解他怎麼使用。 16 | 17 | ## 簡介 18 | 19 | 我先用兩句話來描述 git submodule 和 git subtree 的差異, 20 | 21 | 22 | ***git submodule 是 link 的概念,而 git subtree 則是 copy的概念*** 23 | 24 | ( 如果你看不懂,建議先去閱讀之前我介紹的 Submodule 的 [文章](https://github.com/twtrubiks/Git-Tutorials/blob/master/git_submodule_turorial.md) 吧 ) 25 | 26 | 27 | git subtree 的指令沒有很多,主要指令如下, 28 | 29 | ```text 30 | 'git subtree' add -P 31 | 'git subtree' pull -P 32 | 'git subtree' push -P 33 | 'git subtree' merge -P 34 | 'git subtree' split -P [OPTIONS] [] 35 | ``` 36 | 37 | 更多詳細內容可參考 [git-subtree](https://github.com/apenwarr/git-subtree/blob/master/git-subtree.txt) 文件。 38 | 39 | ## 教學 40 | 41 | ### how to create git subtree 42 | 43 | 先介紹一下 repo ,這邊有兩個 repo,分別為 main_project_subtree 44 | repo 以及 a_project_subtree repo, 45 | 46 | 先把 main_project_subtree repo clone 下來, 47 | 48 | ```cmd 49 | git clone git@github.com:blue-rubiks/main_project_subtree.git 50 | ``` 51 | 52 | main_project_subtree repo log 如下, 53 | 54 | ![alt tag](https://i.imgur.com/I6i93rr.png) 55 | 56 | 57 | 接下來,要加入 subtree ( 也就是 a_project_subtree ),使用以下指令 58 | 59 | ```cmd 60 | git subtree add --prefix=a_project_subtree --squash git@github.com:blue-rubiks/a_project_subtree.git master 61 | ``` 62 | 63 | or 64 | 65 | ```cmd 66 | git subtree add -P a_project_subtree --squash git@github.com:blue-rubiks/a_project_subtree.git master 67 | ``` 68 | 69 | `--prefix` option [文件](https://github.com/apenwarr/git-subtree/blob/master/git-subtree.txt) 說明如下, 70 | 71 | ```text 72 | -P :: 73 | --prefix=:: 74 | Specify the path in the repository to the subtree you 75 | want to manipulate. This option is mandatory 76 | for all commands. 77 | ``` 78 | 79 | 簡單說,prefix 就是指定一個目錄。 80 | 81 | `--squash` option [文件](https://github.com/apenwarr/git-subtree/blob/master/git-subtree.txt) 說明如下, 82 | 83 | ```text 84 | With '--squash', imports only a single commit from the subproject, rather than its entire history. 85 | ``` 86 | 87 | 也就是說,如果沒有使用 `--squash`,會將 a_project_subtree 的 log ( 歷史訊息 ) 全部顯示出來 88 | 89 | ( 請有 log 會變很長又有點亂的心理準備 :scream: ), 90 | 91 | 但通常我們不太需要顯示全部的 log ( 尤其是依賴的 repo ),所以記得加上 `--squash` option, 92 | 93 | 這樣的話,就會將全部的 log 合成一個 log 94 | 95 | ( 其實是兩個,因為還會有一個 merge 的 log :smiley: ) 96 | 97 | ![alt tag](https://i.imgur.com/aUSe38E.png) 98 | 99 | main_project_subtree repo log 會變成如下,會多兩個 log, 100 | 101 | ![alt tag](https://i.imgur.com/NS0aB4B.png) 102 | 103 | 如果你覺得以上指令實在太長了,可以拆成兩步驟,先執行, 104 | 105 | ```cmd 106 | git remote add a_project_subtree git@github.com:blue-rubiks/a_project_subtree.git 107 | ``` 108 | 109 | 更多詳細資料可參考 [git-remote](https://git-scm.com/docs/git-remote)。 110 | 111 | 然後執行 112 | 113 | ```cmd 114 | git subtree add -P a_project_subtree --squash a_project_subtree master 115 | ``` 116 | 117 | 其實就是將 remote url 變成 a_project_subtree 而已 :thumbsup: 118 | 119 | 120 | ### how to push git subtree 121 | 122 | [Youtube Tutorial PART 2 - git subtree tutorial - how to push subtree](https://youtu.be/Df3zc1VOqN8) 123 | 124 | 現在的 main_project_subtree 基本上已經將 a_project_subtree 整個 copy 過來了,就算現在 125 | 126 | a_project_subtree remote repo 被刪除也沒關係 ( 因為是 copy 過來的 ),而且 developer 甚至 127 | 128 | 不需要知道有 a_project_subtree 的存在,直接使用一般的 git 操作即可,也就是說,現在 129 | 130 | a_project_subtree 就像是在 main_project_subtree 裡面的一個目錄一樣。 131 | 132 | 如果你修改了 a_project_subtree ,並且執行 git push,在 main_project_subtree 中可以看到 133 | 134 | 資料被修改,但是在 a_project_subtree 中會看不到修改,因為還沒有 push 到 a_project_subtree, 135 | 136 | 那該如何 push 到 a_project_subtree 上呢 :question: 137 | 138 | 可以使用以下指令 139 | 140 | ```cmd 141 | git subtree push -P a_project_subtree a_project_subtree master 142 | ``` 143 | 144 | 或是 145 | 146 | ```cmd 147 | git subtree push -P a_project_subtree git@github.com:blue-rubiks/a_project_subtree.git master 148 | ``` 149 | 150 | update a_project_subtree, 151 | 152 | ![alt tag](https://i.imgur.com/QsyZEcK.png) 153 | 154 | 接著 push 改變到 a_project_subtree repo, 155 | 156 | ![alt tag](https://i.imgur.com/F8SJ2pn.png) 157 | 158 | 注意 `4/4 (2)` 這個,他每次都會重新 ( 重源頭 ) 計算,所以如果你一直增加 a_project_subtree 的 commit, 159 | 160 | 每次 push 到 a_project_subtree 的速度會越來越慢,該如何解決 :question: 161 | 162 | 後面會介紹 :smirk: 163 | 164 | ![alt tag](https://i.imgur.com/PnpAmqU.png) 165 | 166 | 如果你要 push 改變到 main_project_subtree,直接執行一般的 `git push` 操作就行了, 167 | 168 | ![alt tag](https://i.imgur.com/2T9Bn13.png) 169 | 170 | 到這邊我們休息一下 :relaxed: 171 | 172 | 在簡介的時候我們說過 **git submodule 是 link 的概念,而 git subtree 則是 copy 的概念**,所以如果你 173 | 174 | 仔細看 main_project_subtree 以及 a_project_subtree 的 log,你會發現他們是不一樣的 ( 因為 git subtree 175 | 176 | 有自己的策略 ),而之前介紹的 git submodule 則會有相同的 commit id 以及 submodule 的 commit link。 177 | 178 | ### how to pull git subtree 179 | 180 | [Youtube Tutorial PART 3 - git subtree tutorial - how to pull/create subtree](https://youtu.be/dE-D2yrD4ws) 181 | 182 | 如果今天我們要更新 a_project_subtree repo 可以使用以下指令, 183 | 184 | ```cmd 185 | git subtree pull -P a_project_subtree a_project_subtree master 186 | ``` 187 | 188 | 或是 189 | 190 | ```cmd 191 | git subtree pull -P a_project_subtree git@github.com:blue-rubiks/a_project_subtree.git master 192 | ``` 193 | 194 | 執行後,應該會跳出 `fatal: refusing to merge unrelated histories` 失敗的訊息, 195 | 196 | ![alt tag](https://i.imgur.com/hcqBtrK.png) 197 | 198 | 會出現這個的原因是因為我們在前面有執行 `git subtree add -P a_project_subtree --squash ` 199 | 200 | 中的 `--squash`,所以 pull 的時候也必須加上,也就是如下, 201 | 202 | ![alt tag](https://i.imgur.com/AoOcXFa.png) 203 | 204 | 這樣才可以成功 pull :smile: 205 | 206 | ### git subtree split 207 | 208 | split 這個指令比較特別,在這邊我提一下,在前面的 [how to push git subtree](https://github.com/twtrubiks/Git-Tutorials/blob/master/git_subtree_turorial.md#how-to-push-git-subtree) 部分有提到 `4/4 (2)` 209 | 210 | 這個是他每次都會重新計算,所以如果你一直增加 commit,push 速度會越來越慢的這個問題, 211 | 212 | 而 split 可以解決這個問題,他會從一個最新的點開始計算,不用每次都重頭計算 :smile: 213 | 214 | 先來看一下 [文件](https://github.com/apenwarr/git-subtree/blob/master/git-subtree.txt),`--rejoin` option 215 | 216 | ```text 217 | If you do all your merges with '--squash', don't use 218 | '--rejoin' when you split, because you don't want the 219 | subproject's history to be part of your project anyway. 220 | ``` 221 | 222 | 我們在 `git subtree add` 以及 `git subtree pull` 都有使用 `--squash`,所以 223 | 224 | 如果再使用`--rejoin` option 的話,會導致 a_project_subtree 的 history log 都 225 | 226 | 被 clone 回來,所以我們必須再加上 `--ignore-joins option`,指令如下, 227 | 228 | ```cmd 229 | git subtree split --rejoin --prefix=a_project_subtree --ignore-joins 230 | ``` 231 | 232 | ![alt tag](https://i.imgur.com/owS1Nz7.png) 233 | 234 | 這樣就完成 split 了,當我們如果有改動 a_project_subtree,`4/4 (2)` 這個就會重最新的點開始計算, 235 | 236 | 不會每次都重頭計算,導致 push 速度越來越慢 :satisfied: 237 | 238 | 239 | ## 結論 240 | 241 | 介紹完了 subtree ,一定要來說說我對 subtree 以及 submodule 的看法 :laughing: 242 | 243 | 在 submodule 中執行 init 以及如果要移除掉 submodule 時,步驟都比較繁瑣, 244 | 245 | 相對 subtree 來說,subtree 簡單很多,因為對 developer 來說,他就像一個 246 | 247 | 目錄一樣,就算今天 subtree 的 remote repo 被刪除也不擔心 :smiley: 248 | 249 | 但是我相信大家一定也發現了,使用 subtree 的時候,我們如果要切換 branch 250 | 251 | 並沒有想像中的容易 ( 在 submodule 中,我們可以進到依賴的 repo 中,然後 252 | 253 | 像操作一般的 git 使用 `git checkout` 來切換 branch,但在 subtree 中卻無法 254 | 255 | 這樣使用,因為他是整個 copy 過來的,不像 submodule 那樣是 link 的概念。 ) 256 | 257 | 所以如果有切分支 ( 你依賴的 repo ),建議還是使用 submodule 會比較方便, 258 | 259 | 另外一個點是,我覺得使用 subtree 時,會讓 log 變得有點亂 :weary:,不像 260 | 261 | submodule 那樣清楚很多 :smile: 262 | 263 | 而且 subtree 的侵略性比較強 ( commit id 都會改變),大家在使用時,要多了解 264 | 265 | 一下,雖然 subtree 指令看似只有幾個,但是整體使用下來,還是很多細節要注意 :sweat_smile: 266 | 267 | 268 | ## Donation 269 | 270 | 文章都是我自己研究內化後原創,如果有幫助到您,也想鼓勵我的話,歡迎請我喝一杯咖啡 :laughing: 271 | 272 | ![alt tag](https://i.imgur.com/LRct9xa.png) 273 | 274 | [贊助者付款](https://payment.opay.tw/Broadcaster/Donate/9E47FDEF85ABE383A0F5FC6A218606F8) 275 | 276 | ## License 277 | 278 | MIT license 279 | -------------------------------------------------------------------------------- /git_subtree_turorial_en.md: -------------------------------------------------------------------------------- 1 | # git subtree tutorial :memo: 2 | 3 | [English Version](git_subtree_turorial_en.md) 4 | 5 | Step-by-step guide [Youtube Tutorial PART 1 - git subtree tutorial - how to create subtree](https://youtu.be/kEvgK2gH_vg) 6 | 7 | Do you remember the [git Submodule tutorial 📝](https://github.com/twtrubiks/Git-Tutorials/blob/master/git_submodule_turorial.md) command I introduced before? :question: 8 | 9 | If you've forgotten, go review it now :smirk: 10 | 11 | Today I'm going to introduce another command with a similar function, called **git-subtree**. If you google `git-subtree` and `git submodule`, you'll find that many people compare the two, and they all say things like `git-subtree` is more recommended. But I won't draw a conclusion here. The conclusion is at the end. I'll first show you how to use it. 12 | 13 | ## Introduction 14 | 15 | I'll first use two sentences to describe the difference between `git submodule` and `git subtree`. 16 | 17 | ***`git submodule` is a concept of a link, while `git subtree` is a concept of a copy*** 18 | 19 | (If you don't understand, I suggest you first read the [article](https://github.com/twtrubiks/Git-Tutorials/blob/master/git_submodule_turorial.md) I introduced about Submodule) 20 | 21 | There are not many commands for `git subtree`. The main commands are as follows: 22 | 23 | ```text 24 | 'git subtree' add -P 25 | 'git subtree' pull -P 26 | 'git subtree' push -P 27 | 'git subtree' merge -P 28 | 'git subtree' split -P [OPTIONS] [] 29 | ``` 30 | 31 | For more detailed content, please refer to the [git-subtree](https://github.com/apenwarr/git-subtree/blob/master/git-subtree.txt) documentation. 32 | 33 | ## Tutorial 34 | 35 | ### how to create git subtree 36 | 37 | First, let me introduce the repos. There are two repos here, `main_project_subtree` repo and `a_project_subtree` repo. 38 | 39 | First, clone the `main_project_subtree` repo. 40 | 41 | ```cmd 42 | git clone git@github.com:blue-rubiks/main_project_subtree.git 43 | ``` 44 | 45 | The log of `main_project_subtree` repo is as follows: 46 | 47 | 48 | 49 | Next, to add a subtree (which is `a_project_subtree`), use the following command: 50 | 51 | ```cmd 52 | git subtree add --prefix=a_project_subtree --squash git@github.com:blue-rubiks/a_project_subtree.git master 53 | ``` 54 | 55 | or 56 | 57 | ```cmd 58 | git subtree add -P a_project_subtree --squash git@github.com:blue-rubiks/a_project_subtree.git master 59 | ``` 60 | 61 | The `--prefix` option [documentation](https://github.com/apenwarr/git-subtree/blob/master/git-subtree.txt) is as follows: 62 | 63 | ```text 64 | -P :: 65 | --prefix=:: 66 | Specify the path in the repository to the subtree you 67 | want to manipulate. This option is mandatory 68 | for all commands. 69 | ``` 70 | 71 | Simply put, `prefix` is to specify a directory. 72 | 73 | The `--squash` option [documentation](https://github.com/apenwarr/git-subtree/blob/master/git-subtree.txt) is as follows: 74 | 75 | ```text 76 | With '--squash', imports only a single commit from the subproject, rather than its entire history. 77 | ``` 78 | 79 | That is to say, if you don't use `--squash`, the entire log (history message) of `a_project_subtree` will be displayed. 80 | 81 | (Please be prepared for the log to become very long and a bit messy :scream:). 82 | 83 | But usually we don't need to display the entire log (especially for dependent repos), so remember to add the `--squash` option. 84 | 85 | In this way, the entire log will be combined into one log. 86 | 87 | (Actually two, because there will be another merge log :smiley:) 88 | 89 | 90 | 91 | The log of `main_project_subtree` repo will become as follows, with two more logs. 92 | 93 | 94 | 95 | If you think the above command is too long, you can split it into two steps. First, execute: 96 | 97 | ```cmd 98 | git remote add a_project_subtree git@github.com:blue-rubiks/a_project_subtree.git 99 | ``` 100 | 101 | For more detailed information, please refer to [git-remote](https://git-scm.com/docs/git-remote). 102 | 103 | Then execute: 104 | 105 | ```cmd 106 | git subtree add -P a_project_subtree --squash a_project_subtree master 107 | ``` 108 | 109 | It's just changing the remote url to `a_project_subtree` :thumbsup: 110 | 111 | ### how to push git subtree 112 | 113 | [Youtube Tutorial PART 2 - git subtree tutorial - how to push subtree](https://youtu.be/Df3zc1VOqN8) 114 | 115 | Now `main_project_subtree` has basically copied the entire `a_project_subtree`. Even if the `a_project_subtree` remote repo is deleted now, it doesn't matter (because it was copied). And the developer doesn't even need to know that `a_project_subtree` exists. They can just use the general git operations. That is to say, now `a_project_subtree` is like a directory inside `main_project_subtree`. 116 | 117 | If you modify `a_project_subtree` and execute `git push`, you can see the data is modified in `main_project_subtree`, but you won't see the modification in `a_project_subtree` because it hasn't been pushed to `a_project_subtree` yet. 118 | 119 | So how to push to `a_project_subtree`? :question: 120 | 121 | You can use the following command: 122 | 123 | ```cmd 124 | git subtree push -P a_project_subtree a_project_subtree master 125 | ``` 126 | 127 | or 128 | 129 | ```cmd 130 | git subtree push -P a_project_subtree git@github.com:blue-rubiks/a_project_subtree.git master 131 | ``` 132 | 133 | update `a_project_subtree`, 134 | 135 | 136 | 137 | Then push the changes to the `a_project_subtree` repo. 138 | 139 | 140 | 141 | Note `4/4 (2)`. It will be recalculated every time (from the source). So if you keep adding commits to `a_project_subtree`, the speed of pushing to `a_project_subtree` will become slower and slower. How to solve this? :question: 142 | 143 | I will introduce it later :smirk: 144 | 145 | 146 | 147 | If you want to push changes to `main_project_subtree`, just execute the general `git push` operation. 148 | 149 | 150 | 151 | Let's take a break here :relaxed: 152 | 153 | In the introduction, we said that **`git submodule` is a concept of a link, while `git subtree` is a concept of a copy**. So if you look closely at the logs of `main_project_subtree` and `a_project_subtree`, you will find that they are different (because `git subtree` has its own strategy). And the `git submodule` introduced before will have the same commit id and submodule commit link. 154 | 155 | ### how to pull git subtree 156 | 157 | [Youtube Tutorial PART 3 - git subtree tutorial - how to pull/create subtree](https://youtu.be/dE-D2yrD4ws) 158 | 159 | If we want to update the `a_project_subtree` repo today, we can use the following command: 160 | 161 | ```cmd 162 | git subtree pull -P a_project_subtree a_project_subtree master 163 | ``` 164 | 165 | or 166 | 167 | ```cmd 168 | git subtree pull -P a_project_subtree git@github.com:blue-rubiks/a_project_subtree.git master 169 | ``` 170 | 171 | After execution, you should get a `fatal: refusing to merge unrelated histories` failure message. 172 | 173 | 174 | 175 | The reason for this is that we executed `git subtree add -P a_project_subtree --squash` with `--squash` before. So when pulling, we must also add it, as follows: 176 | 177 | 178 | 179 | This way you can pull successfully :smile: 180 | 181 | ### git subtree split 182 | 183 | The `split` command is a bit special. I'll mention it here. In the previous [how to push git subtree](https://github.com/twtrubiks/Git-Tutorials/blob/master/git_subtree_turorial.md#how-to-push-git-subtree) section, I mentioned `4/4 (2)`. This is because it recalculates every time, so if you keep adding commits, the push speed will become slower and slower. 184 | 185 | And `split` can solve this problem. It will start calculating from the latest point, instead of recalculating from the beginning every time :smile: 186 | 187 | Let's first look at the [documentation](https://github.com/apenwarr/git-subtree/blob/master/git-subtree.txt), the `--rejoin` option: 188 | 189 | ```text 190 | If you do all your merges with '--squash', don't use 191 | '--rejoin' when you split, because you don't want the 192 | subproject's history to be part of your project anyway. 193 | ``` 194 | 195 | We used `--squash` in `git subtree add` and `git subtree pull`. So if we use the `--rejoin` option again, it will cause the history log of `a_project_subtree` to be cloned back. So we must add the `--ignore-joins` option. The command is as follows: 196 | 197 | ```cmd 198 | git subtree split --rejoin --prefix=a_project_subtree --ignore-joins 199 | ``` 200 | 201 | 202 | 203 | This completes the split. When we have changes to `a_project_subtree`, `4/4 (2)` will start calculating from the latest point, and will not recalculate from the beginning every time, which causes the push speed to become slower and slower :satisfied: 204 | 205 | ## Conclusion 206 | 207 | After introducing `subtree`, I must talk about my views on `subtree` and `submodule` :laughing: 208 | 209 | In `submodule`, executing `init` and removing a submodule are more cumbersome. 210 | 211 | Compared to `subtree`, `subtree` is much simpler, because for a developer, it is like a directory. Even if the remote repo of the `subtree` is deleted today, there is no need to worry :smiley: 212 | 213 | But I believe everyone has also discovered that when using `subtree`, it is not as easy as imagined to switch branches (in `submodule`, we can go into the dependent repo and use `git checkout` to switch branches like operating a general git, but in `subtree`, we cannot use it this way because it is copied over, unlike `submodule` which is a link concept). 214 | 215 | So if you have to switch branches (of your dependent repo), it is recommended to use `submodule` which is more convenient. 216 | 217 | Another point is that I think using `subtree` will make the log a bit messy :weary:, unlike `submodule` which is much clearer :smile: 218 | 219 | And `subtree` is more aggressive (the commit id will change). When you use it, you need to understand it more. Although `subtree` seems to have only a few commands, there are still many details to pay attention to when using it as a whole :sweat_smile: 220 | 221 | ## Donation 222 | 223 | The articles are all original after my own research and internalization. If it has helped you and you want to encourage me, you are welcome to buy me a cup of coffee :laughing: 224 | 225 | 226 | 227 | [Sponsor Payment](https://payment.opay.tw/Broadcaster/Donate/9E47FDEF85ABE383A0F5FC6A218606F8) 228 | 229 | ## License 230 | 231 | MIT license 232 | -------------------------------------------------------------------------------- /pr-tutorial/README.md: -------------------------------------------------------------------------------- 1 | [English Version](README_en.md) 2 | 3 | # PR (Pull Request) 教學 4 | 5 | 先來說說甚麼時候可以使用 PR (Pull Request), 6 | 7 | * 情境一 8 | 9 | 你在網路上看到一個很棒的專案, 然後你很想要為這個專案盡一份心力, 這時候就你就可以透過 PR 這個機制, 10 | 11 | 通常你不會有這個專案的權限, 所以一定是 Fork 這個專案到自己的 repo 下修改 -> 修改完後推回自己的分支 12 | 13 | -> 再對對應的原專案發 PR(注意對方的以及自己的 branch ) -> 等作者測試覺得沒問題後合併進主分支 14 | 15 | -> 恭喜你對社群盡了一分心力 :smile: 16 | 17 | * 情境二 18 | 19 | 公司也可以使用 PR 的模式進行多人開發, 但通常在這個情況下你都會有專案的權限(和情境一不同), 所以你就 20 | 21 | 不需要 Fork, 直接開 branch 後對特定的 branch(可能是 develop) 發 PR 即可. 22 | 23 | 24 | **情境一和情境二基本上都是一樣的概念, 唯一差別就是是否有 repo 的權限, 如果沒有就需要 Fork.** 25 | 26 | 今天主要會透過 情境二 和大家簡單介紹 PR 的操作. 27 | 28 | * [Youtube Tutorial - github PR (Pull Request) 教學](https://youtu.be/bXOdD-bKfkA) - [文章快速連結](https://github.com/twtrubiks/Git-Tutorials/tree/master/pr-tutorial#github-pr-pull-request-%E6%95%99%E5%AD%B8) 29 | 30 | * [Youtube Tutorial - github CLI PR 教學](https://youtu.be/AD8X11lq3gQ) - [文章快速連結](https://github.com/twtrubiks/Git-Tutorials/tree/master/pr-tutorial#github-cli-pr-%E6%95%99%E5%AD%B8) 31 | 32 | ## github PR (Pull Request) 教學 33 | 34 | 使用 [Git-Flow 基本教學](https://github.com/twtrubiks/Git-Tutorials/tree/master/Git-Flow) 的概念來做教學, 35 | 36 | 為了簡單一點, 在這邊就只有 main(master), develop, feature 分支. 37 | 38 | 今天有個需求來了, 我首先先從 develop 開一個 feature 分支, 39 | 40 | 假設做完了需求, 就把 feature 分支 push 上去, 然後開始發 PR. 41 | 42 | 現在要對 develop 發 PR, 要發的分支是 feature. (如下圖) 43 | 44 | ![alt tag](https://i.imgur.com/XfTq0hc.png) 45 | 46 | 選好之後就可以按下 Create pull request, 47 | 48 | 接著你會看到專案下有一個 PR, 49 | 50 | ![alt tag](https://i.imgur.com/ad8BM6T.png) 51 | 52 | 這時候可能是技術主管(依照各公司的流程)看到這個 PR, 如果沒有甚麼問題, 53 | 54 | 可以選擇要使用哪種的 Merge pull request 的方式. 55 | 56 | ![alt tag](https://i.imgur.com/JX9pQDU.png) 57 | 58 | 有三種 Merge pull request 可以選擇, 59 | 60 | * Create a Merge Commit 61 | 62 | 基本上這個就是一般的 merge, 但我通常不喜歡這個, 因為會有無意義的 merge commit. 63 | 64 | * Squash and Merge 65 | 66 | 這個就是假如你有一個功能, 總共使用了 10 個 commit 才完成這個功能, 但是這些 commit 67 | 68 | 其實都沒有太重要的資訊, 這時候你可以選擇使用 Squash 的方式, 把這 10 個 commit 合併成 69 | 70 | 一個 commit. (其他的 commit 會變成你的 message). 71 | 72 | * Rebase and Merge 73 | 74 | 這個和 Create a Merge Commit 的差別就是他不會有無意義的 merge commit, 他有移花接木 75 | 76 | 的概念, 你的分支上有 3個 commit, 使用這方法合併後就是會多出 3個 commit. 77 | 78 | 至於要使用哪一種, 就看各公司的需求了 :smile: 79 | 80 | 決定好 Merge pull request 的方式後就可以直接按下了, 你會發現他顯示 Merged, 81 | 82 | 並且也可選擇是否刪除該分支以及 revert 這個 PR. 83 | 84 | ![alt tag](https://i.imgur.com/ZuJ2eh1.png) 85 | 86 | 基本上整個流程就是這樣, develop 向 main 發送 PR 也是一樣的概念. 87 | 88 | ## github CLI PR 教學 89 | 90 | 如果你有看前面的文章, 你就會覺得很麻煩 :expressionless: 91 | 92 | 因為每次都要手動去網頁上點, 工程師肯定要使用 CLI, 93 | 94 | 今天我就來教大家這個 [github CLI](https://cli.github.com/) :laughing: 95 | 96 | 連 github 的網頁都不需要開 :satisfied: 97 | 98 | 首先是安裝方法 99 | 100 | [https://github.com/cli/cli/blob/trunk/docs/install_linux.md](https://github.com/cli/cli/blob/trunk/docs/install_linux.md) 101 | 102 | 接著是登入的方法 [gh_auth_login](https://cli.github.com/manual/gh_auth_login) 103 | 104 | ```cmd 105 | gh auth login 106 | ``` 107 | 108 | 選擇你要的登入方式即可. 109 | 110 | 順便一提, github CLI 的文件寫的很好, 大家可以參考他的文件玩玩看 [GitHub CLI document](https://cli.github.com/manual/). 111 | 112 | 接下來, 就要來體驗 github CLI 的強大了 :satisfied: 113 | 114 | 現在有一個 `feature_cli` 分支, 當 push 分支之後, 115 | 116 | (溫馨提醒 :exclamation: :exclamation: 要發一個 PR, 這個 branch 一定要 push 到 remote) 117 | 118 | (也就是說, 你沒辦法去發送一個 PR 只存在 local 端但卻不存在 remote 端) 119 | 120 | (如果你只想要 PR 特定的 commit, 請使用 `git checkout -b [branch] [commit_id]` 再發送 PR) 121 | 122 | 就可以透過 github CLI 發 PR, 要向 develop 分支發 PR, 指令如下 123 | 124 | 文件可參考 [https://cli.github.com/manual/gh_pr_create](https://cli.github.com/manual/gh_pr_create) 125 | 126 | ```cmd 127 | gh pr create --base develop --head feature_cli 128 | ``` 129 | 130 | 如果你目前就在 `feature_cli` 分支底下, 也可以只輸入 131 | 132 | ```cmd 133 | gh pr create --base develop 134 | ``` 135 | 136 | 成功透過 github CLI 發送 PR 137 | 138 | ![alt tag](https://i.imgur.com/sZF3SuH.png) 139 | 140 | github 上面也確實有這個 PR 141 | 142 | ![alt tag](https://i.imgur.com/7atBIzY.png) 143 | 144 | 也可以搭配其中的指令 145 | 146 | ```cmd 147 | gh pr create --base dev -a @me -l bug -r twtrubiks 148 | ``` 149 | 150 | `-a` 代表指定這個 pr 給自己 151 | 152 | `-l` label 設定為 bug 153 | 154 | `-r` 設定 reviews 為 twtrubiks 155 | 156 | 除了發 PR 之外, 也可以透過 github CLI 接受 PR, 指令如下 157 | 158 | 文件可參考 [https://cli.github.com/manual/gh_pr_merge](https://cli.github.com/manual/gh_pr_merge) 159 | 160 | ```cmd 161 | gh pr merge -s 2 162 | ``` 163 | 164 | 參數說明, 165 | 166 | `-m` Create a Merge Commit. 167 | 168 | `-s` Squash and Merge. 169 | 170 | `-r` Rebase and Merge. 171 | 172 | 數字 2 則代表要合併的 PR 編號. 173 | 174 | ![alt tag](https://i.imgur.com/hedvtIi.png) 175 | 176 | github 上面確實已經合併這個 PR 177 | 178 | ![alt tag](https://i.imgur.com/h6akTEd.png) 179 | 180 | 也可以建立 issue [gh_issue_create](https://cli.github.com/manual/gh_issue_create) 181 | 182 | ```cmd 183 | gh issue create -a @me -l bug 184 | ``` 185 | 186 | `-a` 代表指定這個 issue 給自己 187 | 188 | `-l` label 設定為 bug 189 | 190 | 還有非常多的指令, 就不一一介紹給大家了, 大家請自行研究 :relaxed: 191 | 192 | 基本上, github CLI 功能是非常強大的 :smile: 193 | 194 | ### 其他 195 | 196 | gh 預設的 editor 是 nano, 以下指令為修改成 vim 197 | 198 | ```cmd 199 | gh config set editor vim 200 | ``` 201 | -------------------------------------------------------------------------------- /pr-tutorial/README_en.md: -------------------------------------------------------------------------------- 1 | # PR (Pull Request) Tutorial 2 | 3 | [English Version](README_en.md) 4 | 5 | First, let's talk about when you can use a PR (Pull Request). 6 | 7 | * Scenario 1 8 | 9 | You see a great project on the internet and you really want to contribute to it. At this time, you can use the PR mechanism. 10 | 11 | Usually, you won't have permission for this project, so you must Fork the project to your own repo for modification -> After modification, push it back to your own branch 12 | 13 | -> Then send a PR to the corresponding original project (pay attention to the other party's and your own branch) -> Wait for the author to test and feel that there is no problem before merging into the main branch 14 | 15 | -> Congratulations on contributing to the community :smile: 16 | 17 | * Scenario 2 18 | 19 | The company can also use the PR model for multi-person development, but usually in this case you will have project permissions (different from scenario 1), so you 20 | 21 | don't need to Fork. Just open a branch and send a PR to a specific branch (maybe develop). 22 | 23 | 24 | **Scenario 1 and Scenario 2 are basically the same concept. The only difference is whether you have repo permissions. If not, you need to Fork.** 25 | 26 | Today, I will mainly use Scenario 2 to briefly introduce the operation of PR. 27 | 28 | * [Youtube Tutorial - github PR (Pull Request) tutorial](https://youtu.be/bXOdD-bKfkA) - [Article Quick Link](https://github.com/twtrubiks/Git-Tutorials/tree/master/pr-tutorial#github-pr-pull-request-%E6%95%99%E5%AD%B8) 29 | 30 | * [Youtube Tutorial - github CLI PR tutorial](https://youtu.be/AD8X11lq3gQ) - [Article Quick Link](https://github.com/twtrubiks/Git-Tutorials/tree/master/pr-tutorial#github-cli-pr-%E6%95%99%E5%AD%B8) 31 | 32 | ## github PR (Pull Request) tutorial 33 | 34 | Using the concept of [Git-Flow Basic Tutorial](https://github.com/twtrubiks/Git-Tutorials/tree/master/Git-Flow) for teaching. 35 | 36 | To make it simpler, there are only main(master), develop, and feature branches here. 37 | 38 | Today, a requirement has come up. I will first open a feature branch from develop. 39 | 40 | Assuming the requirement is done, I will push the feature branch up and start sending a PR. 41 | 42 | Now I want to send a PR to develop. The branch to be sent is feature. (As shown below) 43 | 44 | 45 | 46 | After selecting, you can press Create pull request. 47 | 48 | Then you will see a PR under the project. 49 | 50 | 51 | 52 | At this time, it may be the technical lead (depending on the company's process) who sees this PR. If there are no problems, 53 | 54 | you can choose which type of Merge pull request to use. 55 | 56 | 57 | 58 | There are three types of Merge pull request to choose from: 59 | 60 | * Create a Merge Commit 61 | 62 | This is basically a normal merge, but I usually don't like it because there will be meaningless merge commits. 63 | 64 | * Squash and Merge 65 | 66 | This is for when you have a feature that took a total of 10 commits to complete, but these commits 67 | 68 | don't actually have too much important information. At this time, you can choose to use the Squash method to merge these 10 commits into 69 | 70 | one commit. (The other commits will become your message). 71 | 72 | * Rebase and Merge 73 | 74 | The difference between this and Create a Merge Commit is that it will not have meaningless merge commits. It has the concept of grafting. 75 | 76 | If you have 3 commits on your branch, after merging with this method, there will be 3 more commits. 77 | 78 | As for which one to use, it depends on the needs of each company :smile: 79 | 80 | After deciding on the Merge pull request method, you can press it directly. You will see that it shows Merged. 81 | 82 | And you can also choose whether to delete the branch and revert this PR. 83 | 84 | 85 | 86 | Basically, the whole process is like this. The concept of develop sending a PR to main is the same. 87 | 88 | ## github CLI PR tutorial 89 | 90 | If you have read the previous articles, you will feel that it is very troublesome :expressionless: 91 | 92 | Because you have to manually click on the webpage every time. Engineers must use the CLI. 93 | 94 | Today I will teach you this [github CLI](https://cli.github.com/) :laughing: 95 | 96 | You don't even need to open the GitHub webpage :satisfied: 97 | 98 | First is the installation method: 99 | 100 | [https://github.com/cli/cli/blob/trunk/docs/install_linux.md](https://github.com/cli/cli/blob/trunk/docs/install_linux.md) 101 | 102 | Next is the login method [gh_auth_login](https://cli.github.com/manual/gh_auth_login) 103 | 104 | ```cmd 105 | gh auth login 106 | ``` 107 | 108 | Just choose the login method you want. 109 | 110 | By the way, the documentation for github CLI is very well written. You can refer to its documentation and play with it [GitHub CLI document](https://cli.github.com/manual/). 111 | 112 | Next, let's experience the power of github CLI :satisfied: 113 | 114 | Now there is a `feature_cli` branch. After pushing the branch, 115 | 116 | (Friendly reminder :exclamation: :exclamation: To send a PR, this branch must be pushed to the remote) 117 | 118 | (That is to say, you cannot send a PR that only exists locally but not on the remote) 119 | 120 | (If you only want to PR a specific commit, please use `git checkout -b [branch] [commit_id]` and then send the PR) 121 | 122 | You can send a PR through github CLI. To send a PR to the develop branch, the command is as follows: 123 | 124 | The documentation can be found at [https://cli.github.com/manual/gh_pr_create](https://cli.github.com/manual/gh_pr_create) 125 | 126 | ```cmd 127 | gh pr create --base develop --head feature_cli 128 | ``` 129 | 130 | If you are currently on the `feature_cli` branch, you can also just enter: 131 | 132 | ```cmd 133 | gh pr create --base develop 134 | ``` 135 | 136 | Successfully sent a PR through github CLI. 137 | 138 | 139 | 140 | There is indeed this PR on GitHub. 141 | 142 | 143 | 144 | You can also use some of its commands: 145 | 146 | ```cmd 147 | gh pr create --base dev -a @me -l bug -r twtrubiks 148 | ``` 149 | 150 | `-a` means to assign this pr to yourself 151 | 152 | `-l` sets the label to bug 153 | 154 | `-r` sets the reviewer to twtrubiks 155 | 156 | In addition to sending PRs, you can also accept PRs through github CLI. The command is as follows: 157 | 158 | The documentation can be found at [https://cli.github.com/manual/gh_pr_merge](https://cli.github.com/manual/gh_pr_merge) 159 | 160 | ```cmd 161 | gh pr merge -s 2 162 | ``` 163 | 164 | Parameter description: 165 | 166 | `-m` Create a Merge Commit. 167 | 168 | `-s` Squash and Merge. 169 | 170 | `-r` Rebase and Merge. 171 | 172 | The number 2 represents the PR number to be merged. 173 | 174 | 175 | 176 | This PR has indeed been merged on GitHub. 177 | 178 | 179 | 180 | You can also create an issue [gh_issue_create](https://cli.github.com/manual/gh_issue_create) 181 | 182 | ```cmd 183 | gh issue create -a @me -l bug 184 | ``` 185 | 186 | `-a` means to assign this issue to yourself 187 | 188 | `-l` sets the label to bug 189 | 190 | There are many other commands, so I won't introduce them one by one. Please study them yourself :relaxed: 191 | 192 | Basically, the github CLI function is very powerful :smile: 193 | 194 | ### Others 195 | 196 | The default editor for `gh` is nano. The following command changes it to vim: 197 | 198 | ```cmd 199 | gh config set editor vim 200 | --------------------------------------------------------------------------------