# git指令
遊戲學很快
https://learngitbranching.js.org/index.html?locale=zh_TW
https://study.163.com/course/courseLearn.htm?courseId=1004094014#/learn/video?lessonId=1047962281&courseId=1004094014
https://zlargon.gitbooks.io/git-tutorial/content/remote/sync.html
看懂下面再回來看 面試題
https://gitbook.tw/interview
13快速方法
https://www.youtube.com/watch?v=ecK3EnyGD8o
進階
https://www.youtube.com/watch?v=mnmYwRoSisg
高階
https://www.youtube.com/watch?v=duqBHik7nRo
## gitignore


## 解決cherry hash不同問題
git cherry -v 0.0.179 main | grep "^+"
找內容相同 但hash不同的
## git cherry

## git commit -am
解決不能處理新增文件
用git add -A
git commit
然後自己去設定簡化版
但用不太到
## 懶得打字,或常打錯字
雖然 Git 指令不長,但有時候就懶得打那麼多字(例如 checkout 指令就有 8 個字⺟),或是
有些指令就是常會打錯(例如 status 指令我就常打成 state )。
遇到這種狀況,我們可以在 Git 裡設定⼀些「縮寫」,然後就可以少打幾個字。只要在終端機
下執⾏:
```
$ git config --global alias.co checkout
$ git config --global alias.br branch
$ git config --global alias.st status
```
設定之後,輸入 git co 指令就跟 git checkout 會有⼀樣的效果, git st 就有 git status
的效果,⼀下就省了很多個鍵。
也可直接到
~/.gitconfig 裡修改:
[alias]
```
co = checkout
br = branch
st = status
l = log --oneline --graph
ls = log --graph --pretty=format:"%h <%an> %ar %s
```
## commit但有缺失 補上文件
git commit --amend --no-edit
可以看上面快速方法第二
## 取消剛剛push到遠端的
git revert
## pull遇到 還沒add
git stash
git pull origin master
git stash pop
## config
建議跟github一樣
```
git config --global user.name "用戶姓名"
git config --global user.email "用戶Email"
```
查看 `git config --list`
地址 `~/.gitconfig`
自訂用法時候
前面加上! 他會不知道你要用git

所以不用加

## git init 初始化
git init(新建數據庫):
此指令表示會建立一個新資料夾".git",以後就能作為紀錄版本使用
在開新資料夾時,務必要打上此指令,若不確定有沒有輸入過,可以重複輸入
輸入後會顯示
`Initialized empty Git repository in "專案路徑"/.git/`
## git add 只寫特別的
git add 一般來說用 . 或 all
.是舊的用法
但之後一樣
不過建議用 -p
可以看到add的項目
-p可以用到全部的指令
## git remote
會會
## git status 查看狀態
`git status`
只會顯示 工作區未提交 跟 提交的
想要查看commit到本地版本庫要用 `git log`
四種狀態
1: untracked 未被追蹤
(用add到 Staged)
2: Modifed 表示工作區修改但還未添加到暫存
(用 add到 Staged)
(用 commit -am 到Committed)(只有修改的才能用這個)(可一次多個)
3: Staged 表示工作區修改的文件到暫存但還沒提交到版本庫
(用 commit到 Committed)
4: Committed 表示數據被安全的存放在本地庫中
顯示在這裡(反白)

## git merge
https://gitbook.tw/chapters/branch/merge-branch.html
A 合併 B,跟 B 合併 A 有什麼不同?
這個問題在我一開始學 Git 的時候也曾經困擾過我好一陣子,到底誰合併誰有那麼重要嗎?這就要看你著眼的重點是什麼了
看文章
看從誰出去 , 然後屬於誰的子孫
a有兩分支 b.c 所以b.c很多commit只要版本比a高
我們到a 就能merge b or c
pull也是這道理
ex
我在master 用pull
所以會產生 origin/master
屬於master 又比他新
所以在master merge origin/master就可以了
## git結構
1 working directory 工作區
2 staging index 暫存區
3 git directory(Repository)版本庫
## git log 查看commit
`git log` 要退出檢視狀態直接按鍵盤上的Q鍵即可離開
基本都用 `git log --oneline` 方便看
查看提交的訊息
包含提交人eamil 時間
也可以用
`git reflog`
用git log --oneline --graph 命令可以查看分支提交历史
當 HEAD 有移動的時候(例如切換分支或是 reset 都會造成 HEAD 移動),Git 就會在 Reflog 裡記上一筆
進階
http://jamestw.logdown.com/posts/238719-advanced-git-log
### log查詢

**例如我只想找⼀位叫做 Sherly 的作者的 Commit:**
```
$ git log --oneline --author="Sherly"
930feb3 add pig
51d54ff add lion and tiger
```
只會查到這兩個。同時可以再⽤ | (中⽂「或者」的意思) 來查詢「Sherly 以及 Eddie 這兩
個⼈的 Commit 紀錄」:
`$ git log --oneline --author="Sherly\|Eddie"`
**我想要找 Commit 訊息裡⾯有在罵髒話的.**
使⽤ --grep 參數,可以從 Commit 訊息裡⾯搜尋符合字樣的內容:
`$ git log --oneline --grep="WTF`
**我要怎麼找到哪些 Commit 的檔案內容有提到 "Ruby"
這個字?**
`$ git log -S "Ruby"`
**早上 Commit 了什麼!**
在查歷史資料的時候,可以搭配 `--since` 跟 `--until` 參數查詢:
`$ git log --oneline --since="9am" --until="12am"`
這樣就可以找出「今天早上 9 點到 12 點之間所有的 Commit」。還可以再加⼀個 after :
`$ git log --oneline --since="9am" --until="12am" --after="2017-01"`
## remote
基本上remote對應一個repository
一個url
但基本上一個專案是一個repository
所以不太會用到
各人覺得拉
高手才會用到
自己用到的情況
本地先寫
遠端還沒開
要連到遠端才需要
## 檢視特定檔案的 Commit 紀錄

-p看詳細

## 等等,這⾏程式誰寫的

-L 特定行數

## 啊!不⼩⼼把檔案或⽬錄刪掉了…
使用checkout

## pull重點 Pull + Rebase
我們現在知道 Pull 其實等於 Fetch 加上 Merge,⽽在「另⼀種合併⽅式(使⽤ rebase)」章節
也曾介紹過使⽤ Rebase ⽅式來合併,在執⾏ git pull 指令的時候,也可以再加上 --rebase
參數,它在 Fetch 完成之後,便會使⽤ Rebase ⽅式進⾏合併:
`$ git pull --rebase`
這有什麼好處?在多⼈共同開發的時候,⼤家都各⾃在⾃⼰的分⽀進⾏ Commit,所以拉回來⽤
⼀般的⽅式合併的時候常會產⽣為了合併⽽產⽣的額外 Commit(詳情請參閱「合併分⽀」章
節)。為了合併⽽產⽣的這個 Commit 本⾝並沒有什麼問題,但如果你不想要這個額外的
Commit,可考慮使⽤ Rebase ⽅式來進⾏合併。
學技術長
快速合併沒差
慢的話要用rebsa
## push推不上去

怎麼解決?
解決⽅法算是有兩招
第⼀招:先拉再推
用在多人上面
因為你電腦裡的內容是比較舊的,所以你應該先拉⼀份線上版本的回來更新,然後再推⼀次:
```
$ git pull --rebase
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 3 (delta 1), pack-reused 0
Unpacking objects: 100% (3/3), done.
From https://github.com/eddiekao/dummy-git
37aaef6..bab4d89 master -> origin/master
First, rewinding head to replay your work on top of it...
Applying: update index
```
這裡加了 --rebase 參數是表⽰「內容抓下來之後請使⽤ Rebase ⽅式合併」,當然你想⽤⼀
般的合併⽅式也沒問題。合併如果沒發⽣衝突,接著應該就可以順利往上推了。
第⼆招:無視規則,總之就是聽我的!(誤)
自己分支隨便用
凡事總有先來後到,在上⾯的例⼦中,Sherly 先推上去的內容,後推的⼈就是應該拉⼀份下來
更新,不然照規定是推不上去的。不過這規則也是有例外,只要加上了 --force 或是 -f 參
數,它就會強迫硬推上去,把 Sherly 之前的內容蓋掉:
```
$ git push -f
Counting objects: 19, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (17/17), done.
Writing objects: 100% (19/19), 2.16 KiB | 738.00 KiB/s, done.
Total 19 (delta 6), reused 0 (delta 0)
remote: Resolving deltas: 100% (6/6), done.
To https://github.com/eddiekao/dummy-git.git
+ 6bf3967...c4ea775 master -> master (forced update)
```
## git add 把工作區添加到暫存區
`git add` 將工作區內容移到暫存區
ex `git add index.html ` (記得包含副檔名)
`git add .` 可將全部上傳
**-p 可以分段**
選擇要或不要的
然後他會問你這段要不要
y or n
如果這段妳還要拆不要下 y 跟 n
要下 s
## git stash 暫存
(只有對修改的有用 新增加 綠色的沒用)
前提是我们的代码没有进行 commit ,哪怕你执行了 add 也没关系,
我重新開機的話,存進 stash 中的修改還會在嗎?答案是肯定的,因為 stash 的紀錄是存在於 git 的紀錄中
存的時候
git stash save nama
name最好打看得懂的
**git stash pop**
如果要把更改叫回來的話,我們只要輸入git stash pop 這個指令就可以了!
由於 git stash 可以存不只一個的 stash record,所以pop的意思是彈出最上層的stash record。
**git stash list**

**刪除**
而刪除 stash 也是相當簡單,只要透過 git stash drop 就能刪掉第一筆 stash、git stash clear 刪除所有 stash
**注意!**
Untracked 狀態的檔案預設沒辦法被 Stash,需要額外使⽤ -u 參數。
1. 使⽤ pop 指令,可以把某個 Stash 拿出來並套⽤在⽬前的分⽀上。套⽤成功之後,那個
套⽤過的 Stash 就會被刪除。
2. 如果後⾯沒有指定要 pop 哪⼀個 Stash,會從編號最⼩的,也就是 stash@{0} 開始拿(也
就是最後疊上去的那次)。
## 【冷知識】怎麼樣把檔案真正的從 Git 裡移掉?
砍掉重練
就是把整個 .git ⽬錄砍掉,整理好之後再重建,但這招這個應該不是我們這邊要討論的重點。
使⽤ Rebase 或 filter-branch 指令來整理
如果 Commit 的數量⼩,使⽤ Rebase 應該就⾜以進⾏編輯、重整。在「【狀況題】不⼩⼼把
帳號密碼放在 Git 裡了,想把它刪掉…」章節也曾介紹過 filter-branch 指令,它可以⼤範圍
的對每個 Commit 執⾏某個指令,並於修改完成後會⾃動再重新 Commit。所以如果是要把檔案
從 Git 裡拔掉,⽤ filter-branch 指令應該是相對較⽅便的。
## 高級技巧 慢慢檢查錯誤 git bisect
看13個技巧那影片
## git commit 把暫存區的提交到本地庫
`$ git commit`
* 添加-a參數,就可以檢測出有修改的檔案(不包括新增的檔案),將其加入索引並提交。這些操作只要一個指令就可以完成了。
* 加上-m參數,就可以指令提交“提交訊息”。如果不添加-m參數,就會啟動修改提交訊息的編輯器。
`git commit --amend` 修改上次commit的 名字
撤銷上次的提交 重新提交這次的
這也可以把沒加入的add重新加到這commit裡面
https://git-scm.com/book/zh/v2/Git-%E5%9F%BA%E7%A1%80-%E6%92%A4%E6%B6%88%E6%93%8D%E4%BD%9C
例如,你提交后发现忘记了暂存某些需要的修改,可以像下面这样操作:
```
$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend
最终你只会有一个提交——第二次提交将代替第一次提交的结果
```

## 修改 commit 紀錄
修改 Commit 紀錄有好幾種⽅法:
* 把 .git ⽬錄整個刪除(誤)。
* 使⽤ git rebase 來修改歷史。
* 先把 Commit ⽤ git reset 拆掉,整理後再重新 Commit。
* 使⽤ --amend 參數來修改最後⼀次的 Commit。
**請記得,雖然這只是改訊息,不管如何它就是修改了⼀次的歷史,所以請儘量不要在已經 Push
出去之後再修改,否則可能會造成其它⼈的困擾。**
在 commit change 時,我相信有一定的機率會發生手速太快導致打錯字的情況發生,這時候可以透過 amend 與 rebase 兩種命令來達成。
`git commit --amend -m "Your message." `只能用來修改最新一筆的 commit 紀錄。
而 **git rebase** 則可以修改過去的所有 commit,它提供了一個相當友善的互動模式供使用者依照自己的需求回溯紀錄。
不過,很重要的一點是,git amend 與 git rebase 修改的前提都是你還沒把 local change 推向遠端的情況下,因為這兩個命令都是在修改既定的事實,如果你已經把更改推向遠端了,再執行這兩種指令,伺服器是會拒絕你的請求的,但如果你真的很想 push 的時候該怎麼辦呢?
很簡單 只要在 **git push**時加上 -f 參數就行了,-f 參數代表force push的意思。
`git push -f origin <your branch name>`
但是!在多人協同開發一個專案的時候,git push -f 是一個非常危險的指令,因為很可能會不小心把別人上傳的 code 整個覆蓋掉,通常只有在單人開發、非常有把握的時候,或者是每個功能都能獨立成個別的 feature branch ,且那條 feature branch 只有你在用的情形下,才會用 git push -f 來強制更新分支。
總而言之,就是要非常小心使用這個命令
## 【狀況題】追加檔案到最近⼀次的 Commit
* 使⽤ git reset 把最後⼀次的 Commit 拆掉,加入新檔案後再重新 Commit。
* 使⽤ --amend 參數進⾏ Commit。
amend用法

## git rebase
https://medium.com/starbugs/use-git-interactive-rebase-to-organize-commits-85e692b46dd
https://gitbook.tw/chapters/branch/merge-with-rebase.html
commit融合
https://gitbook.tw/chapters/rewrite-history/merge-multiple-commits-to-one-commit.html
https://zlargon.gitbooks.io/git-tutorial/content/branch/rebase.html
雖然也能合併,但比較容易出現衝突所以使用merge, rebase優勢是合併不會產生新的commit,所以可以配合cherry-pick
也能用來當merge 但比較少這樣用,只要用來重整理commit
commit太瑣碎,要重新提交,或修改commit用這個
rebase 命令並給予 -i 參數,可以改寫提交,替換,刪除,合併。
-i 編輯
**注意!
在互動模式的紀錄由上而下是從最舊到最新,跟 git log 指令所呈現的結果是相反的**
Ex

舉例來說` git rebase -i 4cdfdea94c9be⋯⋯ `的意思是回溯從現在到 4cdfd 這個 Commit,-i 參數是指要進入 Rebase 指令的「互動模式」。
下完命令後,我們便回到了 test1 所在的 commit。

pick 意思是「保留這次的 Commit,不做修改」
**退出**
-- about
vim 基本操作
i編譯
u還原
ctrl+r反向還原
Esc出來
:wq退出vim
跳出來可以複製之類的
* reword改名字
* edit手動更改 比較好 可以reword(主要在能拆commit)
dev
* spuash 跟他上一個融合 message有三行 但可以編輯改名字
* fixup 只留一個名字 完全不用更改名字
* exec跑指令 暫時跳過
* break 跳出迴圈 只有當
* drop 移除這個commit (會刪掉資料)
* label 當前head設定名字
* reset也用不到
* merge也用不到
**rebase也能當分支合併**
https://www.youtube.com/watch?v=KFNERUQQGuo
建議看18分鐘開始
一個參數情況
我在a 下merge b
會把自己移動到b下面 繼承b的感覺
兩個參數
我在a 下merge a b
會 a在上面b在下面
b繼承a
**注意注意**
如果兩個參數 是會上去找交點 然後比對沒有的copy過來

Ex 我在bugFix的情況下
git rebase bugFix c6
會去找這兩個的交點 c0
往下看 他沒有c4 c5 c6
所以被拉過來到bugFix下面
**離開**
是⼀般的合併,也許只要 git reset HEAD^ --hard ⼀⾏指令,拆掉這個合併的 Commit ⼤
家就會退回合併前的狀態。但是,從上⾯的結果可得知,Rebase 並沒有做出那個合併專⽤的
Commit,⽽是整串都串在⼀起了,就跟⼀般的 Commit 差不多。所以這時候如果執⾏ git
reset HEAD^ --hard ,只會拆掉最後⼀個 Commit,但並不會回到 Rebase 前的狀態。
**如果用git rabase調整commit**
會推不上 要用
git push -force-with-lease
這個比 git push --force好
**但要跟你用同分支的夥伴喔**
https://gitbook.tw/chapters/github/using-force-push.html
還原
翻⼀下現在的 Reflog:
```
$ git reflog
28a76dc (HEAD -> cat) HEAD@{0}: rebase finished: returning to refs/heads/cat
28a76dc (HEAD -> cat) HEAD@{1}: rebase: add cat 2
35bc96e HEAD@{2}: rebase: add cat 1
053fb21 (dog) HEAD@{3}: rebase: checkout dog
b174a5a HEAD@{4}: checkout: moving from master to cat
e12d8ef (master) HEAD@{5}: checkout: moving from new_cat to master
b174a5a HEAD@{6}: checkout: moving from master to new_cat
e12d8ef (master) HEAD@{7}: checkout: moving from new_cat to master
b174a5a HEAD@{8}: checkout: moving from master to new_cat
...[略]...
```
看得出來最新的幾次紀錄都是在做 Rebase,但我看到這⾏:
`b174a5a HEAD@{4}: checkout: moving from master to cat`
看起來這應該是在開始做 Rebase 前的最後動作,所以就是它了!我使⽤ reset 指令硬切回
去:
```
$ git reset b174a5a --hard
HEAD is now at b174a5a add cat 2
```
使⽤ ORIG_HEAD
在 Git 有另⼀個特別的紀錄點叫做 ORIG_HEAD ,這個 ORIG_HEAD 會記錄「危險操作」之前
HEAD 的位置。例如分⽀合併或是 Reset 之類的都算是所謂的「危險操作」。透過這個紀錄點
來取消這次 Rebase 相對的更簡單:
```
$ git rebase dog
First, rewinding head to replay your work on top of it...
Applying: add cat 1
Applying: add cat 2
```
成功重新計算 2 個 Commit 並接到 dog 分⽀上了,這時候可使⽤這個指令輕鬆的跳回 Rebase
前的狀態:
```
$ git reset ORIG_HEAD --hard
HEAD is now at b174a5a add cat 2
```
⼀切⼜都回來了!
## 那如果不是⽂字檔的衝突怎麼解?
上⾯的 index.html 因為是⽂字檔案,所以 Git 可以標記出發⽣衝突的點在哪些⾏,我們⽤⾁眼
都還能看得出來⼤概該怎麼解決,但如果是像圖片檔之類的⼆進位檔怎麼辦?例如在 cat 分⽀
跟 dog 分⽀,同時都加了⼀張叫做 cute_animal.jpg 的圖片,合併的時候出現衝突的訊息:
```
$ git merge dog
warning: Cannot merge binary files: cute_animal.jpg (HEAD vs. dog)
Auto-merging cute_animal.jpg
CONFLICT (add/add): Merge conflict in cute_animal.jpg
Automatic merge failed; fix conflicts and then commit the result.
```
這下糟了,要把兩邊的⼈⾺請過來,討論到底誰才是最可愛的動物。討論後決定貓才是這世上
最可愛的動物,所以決定要⽤ cat 分⽀的檔案:
`$ git checkout --ours cute_animal.jpg`
如果是要⽤對⽅( dog 分⽀),則是使⽤ --theirs 參數:
`$ git checkout --theirs cute_animal.jpg`
決定之後,就跟前⾯⼀樣,加到暫存區,準備 Commit,然後結束這⼀回合。
## 【狀況題】想要在某些 Commit 之間再加新的 Commit
偶爾你可能會想在某些 Commit 之間再增加⼀些其它的 Commit,假設⽬前的歷史紀錄如下:
```
$ git log --oneline
27f6ed6 (HEAD -> master) add dog 2
2bab3e7 add dog 1
ca40fc9 add 2 cats
1de2076 add cat 2
cd82f29 add cat 1
382a2a5 add database settings
bb0c9c2 init commit
```
在 `ca40fc9 (add 2 cats)` 跟 `2bab3e7 (add dog 1)`這兩個 Commit 之間,我想再增加兩個
Commit。這個使⽤的技巧其實跟上⼀章「【狀況題】把⼀個 Commit 拆解成多個 Commit」有
點像,都是先停在某個 Commit 之後再來做事。所以起⼿式⼀樣是這樣:
`$ git rebase -i bb0c9c2`
再次提醒⼤家,在 Rebase 狀態的 Commit 列表跟平常我們看的紀錄是反過來的,所以如果你
想在某兩個 Commit 之間再增加 Commit,要注意停下來的那個點是不是正確的點。例如我想加
在` add 2 cats 跟 add dog 1 之間`:
```
pick 382a2a5 add database settings
pick cd82f29 add cat 1
pick 1de2076 add cat 2
edit ca40fc9 add 2 cats
pick 2bab3e7 add dog 1
pick 27f6ed6 add dog 2
```
注意,是停在` add 2 cats` 喔,所以我把那個 Commit 改成 edit ,接著繼續執⾏ Rebase:
```
$ git rebase -i bb0c9c2
Stopped at ca40fc9... add 2 cats
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
```
接下來,我很快的加兩個 Commit:
```
$ touch bird1.html
$ git add bird1.html
$ git commit
-m "add bird 1"
[detached HEAD 549bd92] add bird 1
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 bird1.html
$ touch bird2.html
$ git add bird2.html
$ git commit -m "add bird 2"
[detached HEAD e13837e] add bird 2
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 bird2.html
```
如果眼尖的朋友可能會注意到在 Rebase 的過程中常出現「detached HEAD」字樣,如果忘記
它是什麼意思,可以參閱「【狀況題】我可以從過去的某個 Commit 再長⼀個新的分⽀出來
嗎?」章節說明。
加好 2 個 Commit 之後,就繼續剛剛中斷的 Rebase 吧:
`$ git rebase --continue`
Successfully rebased and updated refs/heads/master.
這樣就在指定的位置中間增加新的 Commit 了:
## 【狀況題】我可以從過去的某個 Commit 再長
⼀個新的分⽀出來嗎?
我想⼤家⼀定都有過「如果我在年輕的時候做了另⼀個選擇,現在⼀定會不⼀樣!」的想法。
⼈⽣,很多事情不能重來,但我們可以⽤ Git 來補⾜這個缺憾(咦?)。
回到過去,重新開始
假設我們現在的歷史紀錄是這樣的:
我想要從 add container 那個 Commit ( 657fce7 )再做出新的分⽀,⾸先,你得先回到那個
Commit 的狀態,這時候使⽤的 git checkout 指令:
```
$ git checkout 657fce7
Note: checking out '657fce7'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b <new-branch-name>
HEAD is now at 657fce7... add container
```
這段訊息跟你說,你正處於「detached HEAD」的狀態(請參閱「【冷知識】斷頭(detached
HEAD)是怎麼⼀回事?」章節說明)。你可以從現在這個 Commit 開新的分⽀出去,讓我們直
接使⽤ Checkout 指令的 -b 參數直接建立分⽀並⾃動切換過去:
```
$ git checkout -b bird
Switched to a new branch 'bird'
```
這樣就開好⼀個 bird 分⽀了
### ⼀⾏搞定
如果不想先⾶過去再開分⽀,也是可以直接⼀⾏搞定:
`$ git branch bird 657fce7`
意思就是「請幫我⽤在 657fce7 這個 Commit 上開⼀個叫做 bird 的分⽀」,其實更應該說
「請幫我在 657fce7 這個 Commit 上幫我貼上⼀張 bird 的分⽀貼紙」。同時,使⽤
Checkout 指令配合 -b 參數也可以有開分⽀的效果,⽽且還會直接切換過去:
```
$ git checkout -b bird 657fce7
Switched to a new branch 'bird'
```
## ORIG_HEAD 是什麼東⻄?
在 .git ⽬錄裡除了剛剛說的 HEAD 檔案之外,還有另⼀個叫做 ORIG_HEAD 的檔案,當你在做⼀
些比較「危險」的操作(例如像 merge 、 rebase 或 reset 之類的),Git 就會把 HEAD 的狀
態存放在這裡,讓你隨時可以跳回危險動作之前的狀態。
雖然 git reflog 指令也可以查到相關資訊,但 Reflog 的資料比較雜⼀點,這個 ORIG_HEAD 會
更⽅便的讓你找到最近⼀次危險動作之前的 SHA-1 值。舉例來說:
```
$ git log --oneline
b174a5a (HEAD -> cat) add cat 2
c68537b add cat 1
e12d8ef (master) add database.yml in config folder
85e7e30 add hello
657fce7 add container
abb4f43 update index page
cef6e40 create index page
cc797cd init commit
```
然後我故意使⽤ hard 模式執⾏ Reset 指令,倒退⼀步:
```
$ git reset HEAD^ --hard
HEAD is now at c68537b add cat 1
```
看⼀下 ORIG_HEAD 的內容:
```
$ cat .git/ORIG_HEAD
b174a5a95a75c963617409d2fdb514c210d86ba6
```
它記錄的這個內容,正是進⾏ reset 指令前的 SHA-1 值 b174a5a ,所以如果想要取消剛剛這次
reset :
```
$ git reset ORIG_HEAD --hard
HEAD is now at b174a5a add cat 2
```
直接使⽤ reset 指令到 ORIG_HEAD 就能退回 reset 之前的狀態了。
## 【狀況題】新增⽬錄?
我剛剛新增了⼀個 images ⽬錄,但卻發現這個⽬錄好像沒辦法被加到 Git 裡⾯?

## 【冷知識】斷頭(detached HEAD)是怎麼⼀
回事?
這個標題有點驚⼈,其實不是恐怖片的那種斷頭啦。在「【冷知識】HEAD 是什麼東⻄?」章
節曾經介紹過,HEAD 是⼀個「指向某⼀個分⽀的指標」,你可以把 HEAD 當做「⽬前所在的
分⽀」看待。
正常情况下,HEAD 會指向某⼀個分⽀,⽽分⽀會指向某⼀個 Commit。但 HEAD 偶爾會發⽣
「沒有指到某個分⽀」的情況,這個狀態的 HEAD 便稱之「detached HEAD」。
可能發⽣這個狀態的原因有:
```
1. 使⽤ Checkout 指令直接跳到某個 Commit,⽽那個 Commit 剛好⽬前沒有分⽀指著它。
2. Rebase 的過程其實也是處於不斷的 detached HEAD 狀態。
3. 切換到某個遠端分⽀的時候。
```
能幹嘛
沒能幹嘛就只是提醒你斷頭 這樣你切到別的地方不容易回來
其實現在斷頭就等於你回去某個commit 在長出來一樣
那你要幫他記住怎辦
在當前位置開分支就好
`git branch name `
或是明確的跟 Git 說幫你建立⼀個分⽀指向某個 Commit:
`$ git branch tiger b6d204e`
脫離超簡單 去任何一個分支就好
## Reset、Revert 跟 Rebase 指令有什麼差別?
在進入這個主題之前,先看⼀下 Revert 這個指令。
如果想要拆除已經完成的 Commit,在「【狀況題】剛才的 Commit 後悔了,想要拆掉重做…」
章節介紹過可使⽤ Reset 指令來處理,在前幾章也有介紹過使⽤ Rebase 來修改歷史紀錄,例
如把多個 Commit 併成⼀個、把⼀個拆成多個、刪除 Commit、調整 Commit 順序或是在指定的
Commit 之間再加入新的 Commit 等,接下來要介紹的 Revert 指令,也是另⼀可以⽤來「後
悔」的指令。

### 使⽤ Revert 指令

如果想要取消最後這次的 Commit( add dog 2 ),可以這樣做:
```
$ git revert HEAD --no-edit
[master f2c3e8b] Revert "add dog 2"
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 dog2.html
```
加上後⾯的 --no-edit 參數,表⽰不編輯 Commit 訊息。
Commit 怎麼增加了?⽽且原來的那個 add dog 2 的 Commit 也還在?原來,Revert 的指令是
「再做⼀個新的 Commit,來取消你不要的 Commit」的概念,所以 Commit 數量才會增加。
### 那怎麼取消 Revert?
如果做出來的這個 Revert 你也不想要了,有幾個⽅式來處理它:
**再開⼀次 Revert**
Reset、Revert 跟 Rebase 指令有什麼差別?
218
是的,你可以再開⼀個新的 Revert,來 Revert 剛剛這個 Revert(有點饒⼝):
```
$ git revert HEAD --no-edit
[master e209455] Revert "Revert "add dog 2""
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 dog2.html
```
但剛剛被刪掉的 dog2.html ⼜復活了,但這樣 Commit ⼜變多了...
**直接使⽤ Reset**
如果是要「砍掉」這個 Revert,其實只要直接使⽤ Reset 指令就⾏了:
$ git reset HEAD^ --hard
這樣就可以回到 Revert 之前的狀態了。
## git checkout
git checkout 分⽀名稱 的時候,Git 會切換到指定的分⽀,但如果後⾯接的是檔
名或路徑,Git 則不會切換分⽀,⽽是把檔案從 .git ⽬錄裡拉⼀份到⽬前的⼯作⽬錄。
更精準的來說,這個指令會把暫存區(Staging Area)裡的內容或檔案,拿來覆蓋⼯作⽬錄
(Working Directory)的內容或檔案


我们知道 checkout 一般用作切换分支使用,比如切换到 develop 分支,可以执行:
git checkout develop
但是 checkout 不只用作切换分支,他可以用来切换tag,切换到某次commit,如:(跟reset不同是 reset會還原)
commit的要有SHA1值
```
git checkout v1.0
git checkout ffd9f2dd68f1eb21d36cee50dbdd504e95d9c8f7 #
```
后面的一长串是commit_id,是每次commit的SHA1值,可以根据 git log 看到。
除了有“切换”的意思,checkout 还有一个撤销的作用,举个例子,假设我们在一个分支开发一个小功能,刚写完一半,这时候需求变了,而且是大变化,之前写的代码完全用不了了,好在你刚写,甚至都没有 git add 进暂存区,这个时候很简单的一个操作就直接把原文件还原:
**git checkout a.md**
**这里稍微提下,checkout 命令只能撤销还没有 add 进暂存区的文件。**
### git checkout filename.副檔名 在沒add之前把版本回到上一個
跟這用途一樣

全部 `.`
`git checkout -- .`

解惑
未提交的更改復原
`git checkout filename`
已提交的復原(在bush才能)(反正這個當忘記了)
`git checkout -- filename`
https://stackoverflow.com/questions/6561142/difference-between-git-checkout-filename-and-git-checkout-filename
### git checkout -b 切換創建分支
```
git checkout -b 建立分支並切換過去
```
若要在新建的issue 1分支進行提交,需要切换到issue1分支。
需執行checkout命令以退出分支。
`$ git checkout <branch>`
切換到 issue1 分支。
```
$ git checkout issue1
Switched to branch 'issue1'
```
在 checkout 命令給定 -b 參數執行,可以同時建立分支和切換。
`$ git checkout -b <branch>`
## git reset
如果你是跟其他開發者遠端協作,盡量不要將已經推到遠端數據庫的版本,進行git reset後再推上去。
試想如果你的同事推了個版本到遠端數據庫,但你抓下來後用 reset 還原掉他的資料,又 push 更新上去,這樣他的資料就都不見了。
所以使用 git reset 的時機在於,你將遠端數據庫 clone 下來後,在本地自己新增的 commit 可以還原。而遠端數據庫既有的 commit ,就不要再試圖還原。
**Reset 這個英文單字的翻譯是「重新設定」,但事實上 Git 的 Reset 指令用中文來說比較像是「前往」或「變成」,也就是「go to」或「become」的概念**
https://gitbook.tw/chapters/using-git/restore-hard-reset-commit.html
https://gitbook.tw/chapters/using-git/reset-commit.html
如果上面跟下面一樣是同一份
好像reset的時候會一起刪除
### git reset HEAD filename
要先add才能用reset HEAD
(可以理解放棄當前變動回上一步)
(寫到一半覺得錯了 想回到開始前)
拉取最近提交的版本庫中的這個文件到暫存區,該操作不會引響工作區
取消剛剛的add (把暫存區的拉回工作區 資料不會不見)
感覺跟 git restore --staged 一樣
https://stackoverflow.com/questions/58003030/what-is-the-git-restore-command-and-what-is-the-difference-between-git-restor
**不想拆commit就這用**
### 返回某一個版本
git reset 版本號 檔案名字

接下來用status會出現 兩個更動
原因是拉回版本是到暫存
第一個是跟儲存庫不一樣
第二個是跟現在工作區的內容不一樣

所以接下來用 git ckeckout 放棄更動 變成暫存區一致

剩下就要把暫存跟儲存庫同步
git commit -m "名字"

盡量避免用hard,建議用預設或soft較為安全,且盡量避免在master使用 git reset。
微調用 `git commit --amend `大改用 `git reset ~HEAD`

## reset 錯誤誤導

不管是⽤什麼模式進⾏ Reset,Commit 就
是 Commit,並不會因為你 Reset 它然後就消失了
## Git Revert
主要用來抵銷前面commit
雖然在你的 local branch 中使用 git reset 很方便,但是這種「改寫歷史」的方法對別人的 remote branch 是無效的哦!
為了取消修改並且把這個狀態分享給別人,我們需要使用 git revert。舉個例子
在我們要取消的 commit 後面居然多了一個新的 commit!這是因為新的 commit C2' 引入了修改——用來表示我們取消 C2 這個 commit 的修改。
多虧了 revert,現在可以把你的修改分享給別人啦
revert跟 reset不一樣喔
reset是往前幾步驟
但revert是對當前的HEAD做
因為是取消別的人commit
所以是對當前的
如果要取消最近的不用打那長串
git revert HEAD就好
## cherry-pick 複製commit
當你知道你要複製哪些 commit(而且你也知道他們所對應的 hash 值),那麼 git cherry-pick 很適合你。
但是如果你不知道你要的是哪些 commit 呢? 很幸運的是,git 也有考慮到這個問題喔!我們可以用互動式的 rebase 來做到
ex
git rebase -i HEAD~3
## 暫時儲存現狀的操作
暫時儲存現狀的操作
`$ git stash save`
可以省略 save。也可以在 save 之後加入欲顯示的訊息。
**Back To Top**
顯示暫存清單
`$ git stash list`
Back To Top
**恢復暫存的操作**
`$ git stash pop`
僅使用`"git stash pop"` 將可復原到最新的操作。指定stash ID (如:stash@{1} ),則可以復原特定的操作。
**Back To Top**
刪除暫存的操作
`$ git stash drop`
如果使用 "git stash drop",會刪除最新的操作。指定stash ID (如:stash@{1} ),則可以刪除特定的操作。
**Back To Top**
刪除所有暫存的操作
`$ git stash clear`
## git remove
兩種情況
1:未更動文件
git rm 直接能刪除
2:有更動文件
會出現兩種
2-1: git rm --cached file.name 保留工作區
2-2: git rm --cached file.name 全部刪除
## 請 Git 幫你改名
跟前⾯的 git rm ⼀樣,Git 也有提供類似的指令可以讓你少做⼀步:
`$ git mv hello.html world.html`
其實 Git 根本不在乎你的檔案叫什麼名字!
Git 是根據檔案的「內容」去算出那個 SHA-1 的值,所以 Git 不是很在乎你的檔案叫什麼名
字,只在乎檔案的內容是什麼。所以當你進⾏更改檔名的時候,Git 並沒有為此做出⼀個新的
Blob 物件,⽽僅是指向原來舊的那顆 Blob 物件。但因為檔名變了,所以會為此做出⼀顆新的
Tree 物件喔。
## git 分支
### 查看
git branch
### 查看詳細
git branch -vv
### 建立
git branch name
### 切換
git checkout name
(提醒 git checkout filename 是把檔案回復 不修改的樣子)
### 刪除
git branch -d name
(記得不能刪除當前分支)
### 修改名字
git branch -m 原本名字 後來名字
### 創建並切換
git checkout -b name
### 注意如果有位提交的 切換分支會抱錯
要把當前未提交的站存
使用
git stash
可用 git stash查看站存那些
如果有就
git stash pop 清單名稱

提醒 用 stash apply不會刪除 還要用 stash drop去刪除
### git branch -f
ex
git branch -f main HEAD~3
強制main branch 移到HEAD往上三面三個
### git branch -i develop(使用不了 不知道哪裡記錯了)
目前在分之
下
git branch -i develop
重新定義當前branch (跟當前的) (只有commit)
沒有commit會錯誤
會跑去devolop然後看develop跟我分之的差異
因為-i的關係可以編譯
用vim編譯的喔記住
### 刪除本地分支 遠端關閉的
https://www.youtube.com/watch?v=mnmYwRoSisg
用下面的 remote update先更新
用awk搜尋
找

然後加上 xargs 後面加上命令

### 刪除 remote 底下所有過時的分支
git romote update --prune

## git diff
git diff 比較工作區跟站存區
git diff --staged 比較站存跟版本庫
### 比較版本差異
git diff 版本號 版本號

### 比較分支差異
git diff 分支名稱

## git 遠程倉庫(建立流程)(更詳細能看婉節那篇)
### 基本流程

### 第一次建立倉庫 clone自己網路找 (最簡單)
github建立好 直接clone下來就能了
### 本地建立
基本上就建立reomote 然後
git psuh remote 分支就好
//沒有readme才要
**超級實用**
https://exfast.me/2016/05/git-instructions-instructions/
新建repository
本地目錄下,在命令列裡新建一個代碼倉庫(repository)
裡面只有一個README.md
命令如下:
`touch README.md`
`git init`
初始化repository
`git add README.md`
將README.md加入到緩存區
(可以用git add --a將所有改動提交到緩存(注意是兩個杠))(all)
`git commit -m "first commit"`
提交改變,並且附上提交資訊”first commit”
Push
`git remote add origin `https://github.com/XXX(username)/YYYY(projectname).git
加上一個remote的位址,名叫origin,位址是github上的地址(Create a new repo就會有)
因為Git是分散式的,所以可以有多個remote.
`git push -u origin master`
將本地內容push到github上的那個位址上去。
### 關於 -u
【冷知識】設定 upstream 是什麼意思
在前面進行 Push 的時候,有加入了一個 -u 參數,表示要設定 upstream 的意思。嗯…問題是,什麼是「upstream」?
upstream,中文翻譯成「上游」。不要被名詞嚇到了,這 upstream 的概念其實就只是另一個分支的名字而已。在 Git 裡,每個分支可以設定一個「上游」(但每個分支最多只能設定一個 upstream),它會指向並追蹤(track)某個分支。通常 upstream 會是遠端 Server 上的某個分支,但其實要設定在本地端的其它分支也可以。
如果有設定,當下次執行 git push 指令的時候,它就會用來當預設值。舉例來說:
`$ git push -u origin master`
就會把 origin/master 設定為本地 master 分支的 upstream,當下回執行 git push 指令而不加任何參數的時候,它就會猜你是要推往 origin 這個遠端節點,並且把 master 這個分支推上去。
反之,沒設定 upstream 的話,就必須在每次 Push 的時候都跟 Git 講清楚、說明白:
`$ git push origin master`
否則,光就執行 git push 指令而不帶其它參數的話,Git 會跟你埋怨說不知道該 Push 什麼分支以及要 Push 去哪裡:
`$ git push`
fatal: The current branch master has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin master
## git tag
可以幫commit 做標記
重點不會移動到HEAD
git tag V1 qdsqd
幫qdsqd這個commit做v1這標籤
## github資料夾不能點 代表裡面有git文件
刪除git文件後
改名
再改回來
## git remote 增加遠程倉庫
顯示你的遠端
使用 git remote 命令可以檢視你已經設定好的遠端版本庫, 它會列出每個遠端版本庫的「簡稱」。 如果你克隆(clone)了一個遠端版本庫,你至少看得到「origin」——它是 Git 給定的預設簡稱,用來代表被克隆的來源。
```
$ git clone https://github.com/schacon/ticgit
Cloning into 'ticgit'...
remote: Reusing existing pack: 1857, done.
remote: Total 1857 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (1857/1857), 374.35 KiB | 268.00 KiB/s, done.
Resolving deltas: 100% (772/772), done.
Checking connectivity... done.
$ cd ticgit
$ git remote
origin
```
你也可以指定 -v 選項來顯示 Git 用來讀寫遠端簡稱時所用的網址。
```
$ git remote -v
origin https://github.com/schacon/ticgit (fetch)
```
origin https://github.com/schacon/ticgit (push)
如果遠端版本庫不止一個,這個命令會將它們全部列出來。 例如,一個版本庫內連結了許多其它協作者的遠端版本庫,可能看起來就像這樣:
```
$ cd grit
$ git remote -v
bakkdoor https://github.com/bakkdoor/grit (fetch)
bakkdoor https://github.com/bakkdoor/grit (push)
cho45 https://github.com/cho45/grit (fetch)
cho45 https://github.com/cho45/grit (push)
defunkt https://github.com/defunkt/grit (fetch)
defunkt https://github.com/defunkt/grit (push)
koke git://github.com/koke/grit.git (fetch)
koke git://github.com/koke/grit.git (push)
origin git@github.com:mojombo/grit.git (fetch)
origin git@github.com:mojombo/grit.git (push)
```
## 本地推到遠端
。⾸先,需要設定⼀個端節的節點,例如:
`$ git remote add origin git@github.com:kaochenlong/practice-git.git`
說明:
1. git remote 指令,顧名思義,主要是跟遠端有關的操作。
2. add 指令是指要加入⼀個遠端的節點。
3. 在這裡的 origin 是⼀個「代名詞」,指的是後⾯那串 GitHub 伺服器的位置。
在慣例上,遠端的節點預設會使⽤ origin 這個名字。如果是從 Server 上 clone 下來的話,它
的預設的遠端節點就會叫 origin 。(關於 Clone 的使⽤,請參閱「從伺服器上取得
Repository」章節說明)
不過別擔⼼,這只是個慣例,不⽤這名字或是之後想要改也都可以,如果想改叫七龍珠
dragonball :
```$ git remote add dragonball git@github.com:kaochenlong/practice-git.git```
總之,它就是只是指向某個位置的代名詞罷了。設定好遠端節點後,接下來,就是要把東⻄推
上去了:
```
$ git push -u origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 228 bytes | 228.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To github.com:kaochenlong/practice-git.git
* [new branch] master -> master
Branch master set up to track remote branch master from origin.
```
這個簡單的 Push 指令其實做了幾件事:
1. 把 master 這個分⽀的內容,推向 origin 這個位置。
2. 在 origin 那個遠端 Server 上,如果 master 不存在,就建立⼀個叫做 master 的同名分
⽀。
3. 但如果本來 Server 上就存在 master 分⽀,便會移動 Server 上 master 分⽀的位置,使
它指到⽬前最新的進度上。
4. 設定 upstream,就是那個 -u 參數做的好事,這個稍候說明。
如果你能理解上⾯這個指令的意思,你就可以再做⼀些變化。例如你的遠端節點叫做
dragonball ,⽽且你想把 cat 分⽀推上去:
`$ git push dragonball cat`
這樣就會把 cat 分⽀推上 dragonball 這個遠端節點所代表的位置,並且在上⾯建立⼀個名為
cat 同名分⽀(或更新進度)。
## 【冷知識】設定 upstream 是什麼意思
在前⾯進⾏ Push 的時候,有加入了⼀個 -u 參數,表⽰要設定 upstream 的意思。嗯...問題
是,什麼是「upstream」?
upstream,中⽂翻譯成「上游」。不要被名詞嚇到了,這 upstream 的概念其實就只是另⼀個
分⽀的名字⽽已。在 Git 裡,每個分⽀可以設定⼀個「上游」(但每個分⽀最多只能設定⼀個
upstream),它會指向並追蹤(track)某個分⽀。通常 upstream 會是遠端 Server 上的某個分
⽀,但其實要設定在本地端的其它分⽀也可以。
如果有設定,當下次執⾏ git push 指令的時候,它就會⽤來當預設值。舉例來說:
`$ git push -u origin master`
就會把 origin/master 設定為本地 master 分⽀的 upstream,當下回執⾏ git push 指令⽽不
加任何參數的時候,它就會猜你是要推往 origin 這個遠端節點,並且把 master 這個分⽀推
上去。
反之,沒設定 upstream 的話,就必須在每次 Push 的時候都跟 Git 講清楚、說明⽩:
`$ git push origin master`
否則,光就執⾏ git push 指令⽽不帶其它參數的話,Git 會跟你埋怨說不知道該 Push 什麼分
⽀以及要 Push 去哪裡:
```
$ git push
fatal: The current branch master has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin master
```
###### tags: `git`