Git
Git是一個分布式版本控制系統(Distributed Version Control System - DVCS)
版本控制系統(Version Control System - VCS)
核心:版本控制,主動提交,中央倉庫三個要素
中央式版本控制系統(Centralized VCS)
中央倉庫的作用:保存版本歷史,同步團隊代碼
分布式版本控制系統(Distributed VCS/DVCS)
分布式VCS除了中央倉庫之外,還有本地倉庫。
一.工作模型:
1.首先代碼提交到本地倉庫;
2.在服務器創建一個中央倉庫,把代碼從本地倉庫推送到服務器的中央倉庫;
3.其他同事把中央倉庫的內容克隆到本地,擁有了各自的本地倉庫;
4.在功能開發中,一個人會把它的每一步改動提交到本地倉庫,再從本地倉庫推送到中央倉庫;
5.另外兩個人就可以選擇把這些提交同步到自己的機器上,并把它們和自己的本地代碼合并。
二.優點:
大多數的操作可以在本地進行,所以速度更快,而且由于無需聯網;由于可以提交到本地,所以你可以分步提交代碼,把代碼提交做得更細,而不是一個提交包含很多代碼,難以 review 也難以回溯;
三.缺點:
由于每一個機器都有完整的本地倉庫,所以初次獲取項目(Git 術語:clone)的時候會比較耗時;本地占用的存儲比中央式 VCS 要高。
Git基本使用
下載地址:Git下載
- 在GitHub通過New Repository來新建遠程倉庫,在Create a new
repository的時候,填寫Repository name,Add.gitignore:Android,最后點擊Create repository完成創建;創建完成后,點擊右邊Clone or download,把倉庫的clone地址復制到剪貼板; - 從GitHub把中央倉庫clone到本地(使用命令:
git clone
)
git clone 復制的地址
注:.git 目錄就是你的本地倉庫(Local Repository),所有的版本信息都會存在這里,而.git所在的根目錄,稱為Git的工作目錄(Working Directory),它保存了你當前從倉庫中checkout的內容。
在項目的目錄下輸入:git log
查看提交歷史 - 代碼提交(先用
git add 文件名
把文件添加到暫存區,再用git commit
提交)
在這個過程中,可以使用git status
來隨時查看工作目錄的狀態
image.png
注:每個文件有"changed/unstaged"(已修改),"staged"(已修改并暫存),"commited"(已提交)三種狀態,以及一種特殊狀態"untracked"(未跟蹤)
用add指令來讓Git開始跟蹤:git add eZhouXingV5-Android
再次輸入git status
提交的方式是用commit指令:
git commit
在初始狀態下,你是在命令模式,不能編輯這個文件,你需要按一下 "i"(小寫)來切換到插入模式,然后就可以輸入你的提交信息了;
在輸入完成后別按回車,而是要按 ESC 鍵返回到命令模式,然后連續輸入兩個大寫的 "Z"(用 Shift 鍵或 Capslock 鍵都可以),就保存并退出了;
- 提交一次或多次之后,把本地提交push到中央倉庫(
git push
)
可以使用git push
來把你的本地提交發布(即上傳到中央倉庫)
團隊工作的基本工作模型
模擬同事的本地倉庫:
git clone 地址 文件名
從遠程倉庫更新內容使用: git pull
流程:同事commit代碼到他的本地,并push到GitHub中央倉庫;你把GitHub的新提交通過pull指令來取到你的本地;
push沖突:由于 GitHub 的遠端倉庫上含有本地倉庫沒有的內容,所以這次 push 被拒絕了;
解決方式:先用 pull 把遠端倉庫上的新內容取回到本地和本地合并,然后再把合并后的本地倉庫向遠端倉庫推送;
多人合作的基本工作模型:
- 寫完所有的 commit 后,不用考慮中央倉庫是否有新的提交,直接 push 就好;
- 如果 push 失敗,就用 pull 把本地倉庫的提交和中央倉庫的提交進行合并,然后再 push 一次;
進階 1:HEAD、master 與 branch
HEAD
當前commit的引用,它指的就是當前工作目錄所對應的commit;它具有唯一性,每個倉庫中只有一個 HEAD。在每次提交時它都會自動向前移動到最新的 commit 。
branch分支
HEAD 除了直接指向 commit,也可以通過指向某個 branch 來間接指向 commit。當 HEAD 指向一個 branch 時,commit 發生時,HEAD 會帶著它所指向的 branch 一起移動。
master 默認branch
主branch/主分支
- 新建的倉庫中的第一個 commit 會被 master 自動指向;
- 在 git clone 時,會自動 checkout 出 master。
branch的創建,切換和刪除
- 創建branch:
git branch 名稱
- 切換branch:
git checkout 名稱
可以用git checkout -b 名稱
創建后自動切換,用指定的名稱創建 branch 后,再直接切換過去; - 刪除branch:
git branch -d 名稱
HEAD 指向的 branch 不能刪除。如果要刪除 HEAD 指向的 branch,需要先用 checkout 把 HEAD 指向其他地方。
進階 2:push 的本質
- push 是把當前的分支上傳到遠程倉庫,并把這個 branch 的路徑上的所有 commits 也一并上傳;
- push 的時候,如果當前分支是一個本地創建的分支,需要指定遠程倉庫名和分支名,用
git push origin branch_name
的格式,而不能只用git push
;或者可以通過 git config 修改 push.default 來改變 push 時的行為邏輯。 - push 的時候之后上傳當前分支,并不會上傳 HEAD;遠程倉庫的 HEAD 是永遠指向默認分支(即 master)的;
進階 3:merge:合并 commits
- merge含義:從兩個 commit「分叉」的位置起,把目標 commit 的內容應用到當前 commit(HEAD 所指向的 commit),并生成一個新的 commit;執行
git merge branch1
- merge的使用場景:
單獨開發的branch用完了以后,合并回原先的branch;
git pull的內部自動操作
pull的內部操作:pull的實際操作其實是把遠端倉庫的內容用fetch取下來之后,用merge來合并;
merge在合并的時候,是有一定的自動合并能力的,如果一個分支改了A文件,另一個分支改了B文件,那么合并后就是既改A也改B;如果兩個分支都改了同一個文件,但一個改的是第 1 行,另一個改的是第 2 行,那么合并后就是第 1 行和第 2 行都改,也是自動完成。
-
merge的三種特殊情況
3.1 原因:當前分支和目標分支修改了同一部分內容,Git無法確定應該怎樣合并;
應對方法:解決沖突后手動commit;
image.png
Git 添加了三行符號 <<< === >>>
3.2 HEAD領先于目標commit: Git什么也不做,空操作;
3.3 HEAD落后于目標commit:fast-forward;
進階 4:Feature Branching:最流行的工作流
簡介:
- 任何新的功能(feature)或 bug 修復全都新建一個 branch 來寫;
- branch 寫完后,合并到 master,然后刪掉這個 branch;
git checkout -b books;//創建一個新的branch叫做books
git push origin books;//把代碼push到中央倉庫
git pull;//同事從中央倉庫拉下代碼
git checkout books;//切換到分支books
// 合并到master
git checkout master;
git pull;//merge之前pull一下,讓master更新到和遠程倉庫同步
git merge books;//合并books分支
// 合并后的結果push到了中央倉庫,并刪掉了books這個branch
git push;
git branch -d books;
git push origin -d books// 用-d參數把遠程倉庫的branch也刪了
進階 5:關于 add
add把改動的內容放進暫存區
1. add后面加個點'.':全部暫存
git add .
直接把工作目錄下的所有改動全部放進暫存區;
2. add添加的是文件改動,而不是文件名
進階 6:看看我都改了什么
git log
可以查看歷史記錄
1. 查看歷史中的多個commit: log
查看詳細改動:git log -p
;
查看大致改動: git log --stat
2. 查看具體某個commit: show
要看最新 commit ,直接輸入 git show
;要看指定 commit ,輸入 git show commit的引用或SHA-1
;
如果還要指定文件,在 git show 的最后加上文件名
3. 查看未提交的內容:diff
查看暫存區和上一條 commit 的區別:git diff --staged(或 --cached)
查看工作目錄和暫存區的區別:git diff 不加選項參數
查看工作目錄和上一條 commit 的區別:git diff HEAD
高級 1:不喜歡 merge 的分叉?用 rebase 吧
rebase——在新位置重新提交,重寫設置基礎點,改變commit序列的基礎點
使用方式:git rebase 目標基礎點
git checkout branch1
git rebase master
// 在rebase之后,記得切回master再merge一下,把master移動最新的commit:
git checkout master
git merge branch1
高級 2:剛剛提交的代碼,發現寫錯了怎么辦?
用 commit --amend 可以修復當前提交的錯誤: git commit --amend
,commit --amend 并不是直接修改原 commit 的內容,而是生成一條新的 commit。
git add 笑聲.txt
git commit --amend
高級 3:寫錯的不是最新的提交,而是倒數第二個?
commit --amend 可以修復最新 commit 的錯誤,但如果是倒數第二個 commit 寫錯了,怎么辦?
rebase -i:交互式 rebase
開啟交互式 rebase 過程: git rebase -i HEAD^^
把當前 commit ( HEAD 所指向的 commit) rebase 到 HEAD 之前 2 個的 commit 上;
交互式 rebase 最常用的場景是修改寫錯的 commit
- 使用方式是
git rebase -i 目標commit;
- 在編輯界面中指定需要操作的 commits 以及操作類型;
- 操作完成之后用
git rebase --continue
來繼續 rebase 過程。
高級 4:比錯還錯,想直接丟棄剛寫的提交?
reset --hard 丟棄最新的提交
git reset --hard HEAD^
:HEAD^ 表示你要恢復到哪個 commit。因為你要撤銷最新的一個 commit,所以你需要恢復到它的父 commit ,也就是 HEAD^。那么在這行之后,你的最新一條就被撤銷了
高級 5:想丟棄的也不是最新的提交?
用交互式 rebase 撤銷提交
交互式 rebase 可以用來修改某些舊的 commits。其實除了修改提交,它還可以用于撤銷提交;
你想撤銷倒數第二條 commit,那么可以使用 rebase -i:git rebase -i HEAD^^
用 rebase --onto 撤銷提交
在 rebase 命令中直接剔除想撤銷的 commits
高級 6:代碼已經 push 上去了才發現寫錯?
1. 出錯的內容在你自己的 branch
如果你在本地對已有的commit做了修改,這時你再push就會失敗,因為中央倉庫包含本地沒有的commits,這時選擇強行push:
git push origin branch1 -f
:忽略沖突,強制push
2. 出錯的內容已經合并到 master
增加一個新的提交,把之前的提交的內容抹掉;
不要強制 push,而要用 revert 把寫錯的 commit 撤銷;git revert HEAD^
高級 7:reset 的本質——不止可以撤銷提交
reset 的本質:移動 HEAD 以及它所指向的 branch
reset --hard HEAD^
它把 HEAD 和它所指向的 branch 一起移動到了當前 commit 的父 commit 上,從而起到了「撤銷」的效果;
把 HEAD 和 branch 移動到其他的任何地方:git reset --hard branch2
reset --hard:重置工作目錄
重置位置的同時,清空工作目錄的所有改動;
reset --soft:保留工作目錄
r重置位置的同時,保留工作目錄和暫存區的內容,并把重置 HEAD 的位置所導致的新的文件差異放進暫存區;
reset 不加參數:保留工作目錄,并清空暫存區
--mixed(默認):重置位置的同時,保留工作目錄的內容,并清空暫存區。
高級 8:checkout 的本質
git checkout branch名
: checkout 的本質是簽出指定的 commit,所以你不止可以切換 branch,也可以直接指定 commit 作為參數,來把 HEAD 移動到指定的 commit。
checkout 和 reset 的不同
reset 在移動 HEAD 時會帶著它所指向的 branch 一起移動,而 checkout 不會。當你用 checkout 指向其他地方的時候,HEAD 和 它所指向的 branch 就自動脫離了。
高級 9:緊急情況:「立即給我打個包,現在馬上!」
stash:臨時存放工作目錄的改動
在 Git 中,stash 指令可以幫你把工作目錄的內容全部放在你本地的一個獨立的地方,它不會被提交,也不會被刪除,你把東西放起來之后就可以去做你的臨時工作了,做完以后再來取走,就可以繼續之前手頭的事了。git stash
打完包切回你的分支: git stash pop
高級 10:branch 刪過了才想起來有用?
branch 用完就刪是好習慣
reflog :引用的 log
查看一下 HEAD 的移動歷史:git reflog
查看其他引用的 reflog
reflog 默認查看 HEAD 的移動歷史,除此之外,也可以手動加上名稱來查看其他引用的移動歷史:git reflog master
額外說點:.gitignore——排除不想被管理的文件和目錄
.gitignore記錄了所有你希望被 Git 忽略的目錄和文件。