---
tags: notes, git
---
# Git筆記
git博大精深,留一些學習上遇到的困惑,當作以後的小抄。
我覺得寫得很棒的教學: [Learn git in 30 days.](https://github.com/doggy8088/Learn-Git-in-30-days)
學習branch、merge的線上圖形遊戲: [Learn Git Branching](https://learngitbranching.js.org/index.html?locale=en_US)
Commit message該怎麼寫: [Commit convention](https://www.conventionalcommits.org/en/v1.0.0/)
## Commands
* `git init` Initialize a git repo at the current working directory.
* `add`
* `git add <file>` Add **new** or **modified** file into **Staged**.
* `git add -A` Add all changes(including **new**, **modified**, and deleted files).
* `git add --all` The same as above.
* `git add .` The same as above.
* `git add -u` Add all **modified** and deleted. Use this to avoid staging ignore files.

* `commit`
* `git commit` Transfer all **Staged** files into `git repo` and establish a new version.
* `git commit <file>` Commit a specific `<file>`. It could be either **Staged** or **Modified**.
* `git commit -a` This `add` all unstaged(**Modified**) files automatically. And then `commit`. **Untracked** files won't be `add` nor `commit`.
* `git commit -m "msg"` `msg` is a note for each commit shown in `git log`. A commit note is reuquried. Without `-m "msg"`, git opens an editor and ask for typing a message.
* `reset`
* `git reset <commit>` Unstage all **Staged** files, which means, reseting *index* to `<commit>`. And set *HEAD* to `<commit>`. If `<commit>` is empty, then resets *index* to *HEAD*.
The working tree are not affected.
* `git reset --hard <commit>` Unstage files and copy files from *HEAD* of *git repo* to the disk. In other words, reset *index* and working tree in the same time.
If the file in the working tree was modified, the change would be discarded.
* `branch`
* `git branch <branch name>` Create a new branch, staying at current branch(not changing *HEAD*).
* `git branch -v` Display all the banches, including `HEAD`.
* `checkout`
* `git checkout <branch/commit> <file>` Pull specific file from `<branch/commit>` to working tree and `index`, without changing `HEAD`.
* `git chekcout <branch/commit>` Switch *HEAD* to a specific `<branch/commit>` and pull all the files from `<branch/commit>` to working tree.
* `merge`
1. `git checkout master`
2. `git merge hotfix2`: Merge `hotfix2` into master.
* `mv`
* `git mv <file name> <new file name>` Rename the file/dir.
* `git mv <file name> <new path>` After changing the file path without git command, `git mv <the file> <path>` fails to execute.
* `remote`
* `git remote` Display names of remotes.
* `git remote -v` Display names of remotes and their destinations.
* `git remote show <remote name>` Display more details about a remote.
* `git remote add <remote name> <URL>` Add a remote.
* `git remote remove <remote name>` Remove a remote.
* `.gitignore`
Change of `.gitignore` takes effect immediately right after it is modified without staging or commiting it. `.gitignore` can also be ignored.
* `diff`
Show changes between commits, commit and working directory, etc.
| Instruction | Compare 1 | Compare 2 |
|:--------------------------------- |:--------------------- |:--------------- |
| `git diff` | Working directory | Current version |
| `git diff HEAD` | Working directory | Index |
| `git diff commit_id` | Working directory | Commit version |
| `git diff --cached/--staged HEAD` | Index | Current version |
| `git diff commit_1 commit_2` | Commit 1 | Commit 2 |
| `git diff HEAD^ HEAD` | Last version | Current version |
| `git diff <file>` | The file in directory | Current version |
Note: Working directory = Working tree = Unstaged.
## Concepts
### File Status Lifecycle
* There are 4 kinds of status of a file:
* **Untracked**: The file is totally new to *HEAD*.
* **Modified**: The file is modfied after it was *Committed*, which means the file in the disk is different from the file in *HEAD*. It wont be *Commited* unless it is *Staged.*
* **Staged**: The file to be committed. `git commit` copies/sends this kind of file into *git repo/HEAD*. A file in the *index* is called a *Staged* file.
* **Committed**: The content of the file in the disk is the same as the one in *HEAD*.
* ps: *HEAD* is the current working version. And it is usually the latest one in the *git repo*.
* Full file lifecycle:
```graphviz
digraph life_cycle{
fontsize="13";
labelloc="b"; //location of title, "b" for bottom
rankdir="LR" //node direction from left to right. RL, TB, BT also available
graph [fontname = "源樣明體"];
node [fontname = "consolas", fontsize=11];
edge [fontname = "consolas", color="#002a57", fontsize=12];
untak[label="Untracked", shape=box, color="#6e1430", fontcolor=white, style=filled]
unmod[label="Committed\n(unmodified)", shape=oval, color="#ffffa3", fontcolor=black, style=filled]
moded[label="Modified", shape=box, color="#6e1430", fontcolor=white, style=filled]
stag[label="Staged", shape=box, color="#009447", fontcolor=white, style=filled]
{rank=same stag unmod }
untak->stag[label="add <file>", weight="4"]
stag->untak[label=" rm --cached <file> ", weight="1"]
unmod->moded[label=" \n\n\n modified ", weight="2"]
moded->stag[label=" add <file>", weight="0"]
unmod->stag[headlabel="*rm --cached <file>", weight="0",headport=s,tailport=nw,labelangle=46,labeldistance=7.3]
stag->unmod[label=" commit", weight="1",headport=ne]
unmod->untak[label="\n\n\n\n rm --cached <file>", weight="0"]
moded->unmod[headlabel="commit -a", labeldistance=9, labelangle=18,headport=se,tailport=sw]
}
```
> *`git rm --cached <file>` where `<file>` is already committed and you want to remove it from the *HEAD*.
* Typical flow example: Step by step
```graphviz
digraph life_cycle{
fontsize="13";
labelloc="T"; //location of title, "b" for bottom
rankdir="LR" //node direction from left to right. RL, TB, BT also available
graph [fontname = "源樣明體"];
node [fontname = "consolas", fontsize=11];
edge [fontname = "consolas", color="#002a57", fontsize=12];
untak[label="Untracked", shape=box, color="#6e1430", fontcolor=white, style=filled]
unmod[label="Committed\n(unmodified)", shape=oval, color="#ffffa3", fontcolor=black, style=filled]
moded[label="Modified", shape=box, color="#6e1430", fontcolor=white, style=filled]
stag[label="Staged", shape=box, color="#009447", fontcolor=white, style=filled]
init[label="original\nfiles"]
{rank=same unmod stag}
{rank=same init untak}
init->untak[label=" 1$ git init\n "]
untak->stag[label="2$ git add .", weight="4"]
stag->untak[label=" rm --cached <file> ", weight="1", style="invis"]
unmod->moded[label=" 4$ modified file ", weight="2"]
moded->stag[label=" 5$ git add .", weight="0"]
stag->unmod[label="3$ git commit ", weight="0"]
unmod->untak[label="\n\n rm --cached <file>", weight="0", style="invis"]
unmod->unmod[label="*rm --cached <file>", weight="0",headport=sw,tailport=sp, style="invis"]
}
```
Typical Flow Example: (from $1~$2)

Typical Flow Example: (from $3~$5)

1. hi
2. hihi
3. hihihi
4. matjax
$hi$