1. Git多人協作開發流程圖
1.1 processOn默認的模板
1.2 改造之后
https://www.processon.com/view/link/64ccaf56a433c931b2f9428a
訪問密碼:512I
① 總流程圖
② feat分支(功能/需求 分支)流程
③ bugfix分支(緊急補丁分支)流程
④ 回滾
上線后有嚴重問題,短時間內無法修復,需要回滾 – 以下解決方案是個人想法
若需要全部回滾,則直接用上一個版本的快照重新發布,后續再按照【② feat分支(功能/需求 分支)流程】的步驟將最新修改逐步合并到各個分支,再上線
若本次上線了多個功能,而需要緊急回滾其中一個功能(都在feat-xxx)上,則處理流程與【③ bugfix分支(緊急補丁分支)流程】類似,如下圖
2. 簡略的基礎
1.管理工具sourceTree https://www.sourcetreeapp.com/ 省下了敲命令的麻煩,SourceTree本身還是通過Git命令來執行任何操作
2.設置用戶名、郵箱(可以針對全局,也可以針當前工程)
3.fetch、merge、pull的含義(pull = fetch + merge)
4.【commit id 版本號】執行命令時版本號沒必要寫全,前幾位就可以了。HEAD指向的版本就是當前版本
5.工作區和暫存區:工作區和暫存區 - 廖雪峰的官方網站。對于所有分支而言, 工作區和暫存區是公共的。
6.ReadMe文件
7.gitignore文件。把要忽略的文件名(或者路徑)填進去,Git就會自動忽略這些文件。
8.Git比其他版本控制系統設計得優秀的原因:Git跟蹤并管理的是修改,而非文件
3. 總結操作流程
3.1 創建項目
第一步:gitlab頁面創建空項目
此步驟 由管理者建項目
第二步方式A:將項目clone到本地
開發人員加入這個項目(權限),將空項目clone到本地,在該項目的基礎上直接進行開發。
git clone 項目url
無論使用ssh還是http,都要執行clone命令,IDEA中也有執行clone的界面操作
方式一:命令行
git clone http://項目克隆地址
方式二:IDEA
第二步方式B:本地根據模板創建項目,再關聯遠程
在本地根據模板創建項目,執行命令git init
初始化為git倉庫,再與遠程倉庫進行關聯,將模板生成的代碼push到遠程倉庫。
本地根據模板創建項目
此處勾選了【Create Git repository】,之后就不用使用命令行【git init】來初始化了
勾選一些需要的組件,此處演示僅勾選了Lombok。
此時模板為我們生成了一些文件,但此時,本地倉庫沒有關聯遠程倉庫。
但此時執行git branch -a是看不到master分支的,原因:
當 git init 初始化一個空的 Git 倉庫時,它不會自動創建指向第一次提交的主分支(即 master 分支)。
因此,當你第一次執行 git commit 命令提交代碼之后,master 分支才會被創建。此時再執行 git branch -a 就可以看到 master 分支了。
本地倉庫關聯遠程倉庫
建立本地倉庫和遠程倉庫關系
git remote add origin http://xxxxxxxxx.git
拉取遠程倉庫代碼
git pull origin master --allow-unrelated-histories
–allow-unrelated-histories,表示允許不相干的兩個倉庫合并,因為我們之前兩個倉庫是完全獨立的,所以直接是沒辦法pull下來,需要加上該參數才行
此時能看到遠程倉庫初始的commit添加了README.md文件。
提交模板生成的代碼

總結第二步的方式AB
以A的步驟,手動把模板生成的代碼copy到當前項目路徑,再進行add、commit、push操作,效果與B一樣。
但B的作用主要是將已有的項目(含有一些git提交歷史記錄)推送到新的空倉庫。
3.2 創建環境分支
現有的master對應生產環境,
若有dev環境,就創建對應的dev分支,
若有uat環境,就創建對應的uat分支。
創建分支可使用命令行、IDEA界面、遠程倉庫管理頁面。
3.2.1方式一:在頁面創建,fetch到本地
此處下拉可以選擇tag(若有的話)。
使用IDEA中的fetch按鈕,相當于執行命令【git fetch origin】
可以在本地看到,遠程有dev分支。
3.2.2方式二:在本地創建,push到遠程倉庫
在最新master的位置創建分支,相當于執行命令【git checkout -b uat 版本號】
此時uat分支只在本地存在,遠程倉庫沒有。
所以我們push到遠程倉庫
此push操作相當于執行命令【git push origin】
可以看到遠程倉庫也有uat分支了
3.3 創建功能分支
與【3.2 創建環境分支】類似,此時開發人員要開發一個【功能/需求/任務】,步驟如下:
先git pull
,更新整個項目。
基于最新master(或者說是最新發布版本的tag)創建功能分支。功能分支命名舉例:feat-xxx
其中“xxx”可以是敏捷相關管理系統里的【需求編號/子需求編號/任務編號】,個人認為盡量以最小單位的任務編號命名,也可以是自定義的需求功能首字母縮寫,項目內部自己規定
在此功能分支上進行開發,及時push到遠程倉庫。
3.4 功能分支合并到dev分支
現狀:有任務編號【123】,已基于最新master(或者最新版本的tag)新建功能分支【feat-123】,并將所做修改push到【origin/feat-123】
方式一:開發者自己本地合并
每個開發者自己本地合并,在命令行、IDEA中就可以操作
① 【git pull】(此時用fetch也行)更新整個項目
【pull = fetch + merge】,git執行完fetch之后發現,當前feat-123分支,本地與遠程在同一個位置,就沒再進行將 origin/feat-123 merge到 本地feat-123 的操作
②切換到最新origin/dev分支,此時本地dev與遠程dev在同一個位置。
git checkout -B dev origin/dev
注意此處checkout的是origin/dev,實際上執行的命令是【git checkout -B dev origin/dev】,
-B參數 (force create branch):如果已經存在一個名為new_branch的分支,則使用該參數意味著強制創建分支。例如,git checkout -B new_branch表示新建一個名為new_branch的分支,如果該分支已經存在,則強制覆蓋該分支。
由于-B參數,此時我們本地dev分支會跟上最新origin/dev,與其處于同一個位置。
③將功能分支【feat-xxx】merge到dev分支
由于本次feat-123所處的commit與dev分支沒有分岔,此時這樣merge會使用Fast forward模式,不推薦
Fast forward模式導致:功能分支merge到dev分支時,dev分支直接跟了上來,沒有產生新的merge。
此時推薦這樣merge:
④將dev分支push到遠程origin/dev
方式一補充:個人不建議的方式
切換到dev分支,把遠程功能分支(origin/feat-xxx)pull到dev分支。Pull into ‘dev’ Using Merge
由于【pull = fetch + merge】,
此操作會先fetch,獲取所有遠程分支的位置,再把遠程功能分支merge到dev分支。
最后push dev分支
但我個人不建議,因為會出現這個情況:
當遠程dev領先于本地dev,此時將遠程功能分支(origin/feat-xxx)pull到dev分支,會導致【本地dev分支】與【遠程origin/dev】出現分叉,在push時會被駁回,需要多一個merge。
方式二:在頁面上提交合并請求
在頁面上提交merge請求,將功能分支合并到dev分支。
我們先回到【方式一】操作前的這個狀態,本地與遠程dev分支都處于【dc733298】這個commit節點。
下面演示如何在頁面上提交合并請求
【merge】按鈕需要有merge權限的賬戶才可以操作。
但是如果有沖突的話,還是要回到【3.4 功能分支合并到dev分支/方式一】,在開發工具中進行合并解決沖突更方便。
此時我們在本地pull(fetch也行),就能看到最新遠程dev分支了。
沖突
若方式二在頁面上功能分支【feat-xxx】合并到【dev】分支時提示沖突,
回到【3.4功能分支合并到dev分支/方式一】中使用本地merge(頁面上也能操作解決沖突,但不方便),切換到dev,將功能分支merge到dev,直接進行沖突解決。
3.5 功能分支合并到master
與3.4操作類似,只是【target branch】由dev變為master
3.6 上線后對最新master標記tag,表明本次版本
標記tag詳見【4.11 git tag】
4. Git操作
4.1 git checkout
可以切換分支。
創建分支:可以用命令git checkout -b feat-xxx
,可以在遠程倉庫頁面上直接創建再pull到本地
關于git checkout HEAD
,等同于IDEA中直接對某文件rollback
4.2 git status
IDEA中可以直接查看
紅色-未被git跟蹤
綠色-新增文件
藍色-修改
在IDEA中操作commit時會幫我們自動add
IDEA中還可以創建changelist把所做的改動加到不同的組里。
4.3 git log
① 命令行查看commit信息
git log --pretty=oneline
查看提交歷史
② 命令行查看提交歷史路線圖
git log --graph --pretty=oneline --abbrev-commit
③ gitlab頁面上查看提交歷史路線圖
項目/Repository/Graph
④ IDEA中查看提交歷史路線圖
標簽顏色含義
黃-HEAD,本項目只關聯一個遠程倉庫時,只會存在一個黃色標簽,表示當前處在哪個commit
綠-本地分支
紫-遠程分支,會顯示【origin/分支名】,origin是默認的遠程倉庫的名字
⑤ 從log中查找【某文件】里關于【string】這個字符串內容的增刪,可精確到具體的commit
git log -S <string> path/to/file
例:
git log -S 'xxxxxxxxx' -- src/main/java/com/xxxxxxxxx.java
4.4 git revert
1.回滾某個commit
git revert commitId
2.回滾某個merge請求,可以在頁面上操作,默認會創建一個新的revert-xxx分支,再合并到指定的分支上。
4.5 git reset
回退到上一個版本就是HEAD,上上一個版本就是HEAD,當然往上100個版本寫100個比較容易數不過來,可以寫成HEAD~100
Git的版本回退速度非常快,因為Git在內部有個指向當前版本的HEAD指針,當你回退版本的時候,Git僅僅是改變HEAD指向的commit,然后順便把工作區的文件更新了
4種模式,我常用Mixed和Hard(但是hard慎用,會清空本地所有未提交的修改)
git reset --soft commitId
IDEA中使用undo commit也可以達到同樣效果(回退到上一個commitId)
4.6 git reflog
用來記錄你的每一次命令
這樣就可以知道想找的版本號(commit id)
4.7 git rebase
① git rebase
命令把分叉的提交變成直線 – 不推薦使用,分支歷史不清晰,且如果使用了強制推送可能會影響別人。
② 合并兩個commit – 可以在自己的功能分支合并到dev分支前使用
相關命令筆記:
1.
git rebase -i HEAD~2 -- 參數-i是不需要合并的版本號(commit的hash值)
2.
進入編輯模式后將pick修改成fixup或者squash等關鍵字。
p,pick 不對該commit做任何處理
r,reward 保留該commit,但是修改提交信息
e,edit 保留該commit,但是rebase時會暫停,允許你修改這個commit
s,squash(保留應用,合并commit描述)保留該commit,但是會將當前commit與上一個commit合并
f,fixup(保留應用,丟棄commit描述)與squash相同,但不會保存當前commit的提交信息
x,exec 執行其他shell命令
d,drop 刪除該commit
注意不能將第一條也就是最近的一條commit給pickup或者squash,這樣它就找不到之前的commit進行合并,然后就會報出錯誤
3.
commit message的編輯界面,輸入commit描述信息如果遇到錯誤 執行取消命令
git rebase --abort -- 取消這次rebase
③ rebase onto 最新分支-- 可以在自己的功能分支合并到dev分支前使用,效果類似于“基于最新的master分支重新做一遍已經提交的代碼”
4.8 git cherry-pick
能復制一個特定的提交到當前分支
4.9 git remote
遠程庫的名字就是origin,這是Git默認的叫法,也可以改成別的,但是origin這個名字一看就知道是遠程庫。
查看本地倉庫與遠程倉庫的關聯詳情
git remote -v
可以同一個項目關聯多個遠程庫
https://www.liaoxuefeng.com/wiki/896043488029600/1163625339727712
切換遠程倉庫步驟參考:
git remote -v // 查看本地倉庫與遠程倉庫的關聯詳情
git remote rm [遠程倉庫名] // 解除與遠程倉庫的關聯
git remote add [遠程倉庫名] "[遠程倉庫地址]" // 將本地倉庫與遠程倉庫關聯
4.10 git stash
可以把當前工作現場“儲藏”起來,等以后恢復現場后繼續工作
git stash save 這是本地臨時存放起來的未提交的修改
取出時(unstash)
git stash apply stash@{0}
相關命令筆記:
git stash save "save message" // 執行存儲時,添加備注,方便查找,只有git stash 也要可以的,但查找時不方便識別。
git stash list // 查看stash了哪些存儲
git stash show // 顯示做了哪些改動,默認show第一個存儲,如果要顯示其他存貯,后面加stash@{$num}
git stash show -p // 顯示第一個存儲的改動,如果想顯示其他存存儲,命令:git stash show stash@{$num} -p
git stash apply // 應用某個存儲,但不會把存儲從存儲列表中刪除,默認使用第一個存儲,即stash@{0},可更改num
git stash pop // 應用某個存儲,把存儲從存儲列表中刪除,默認為第一個stash,即stash@{0}可更改num
git stash drop stash@{$num} // 丟棄stash@{$num}存儲,從列表中刪除這個存儲
git stash clear // 刪除所有緩存的stash
4.11 git tag
標簽
https://www.liaoxuefeng.com/wiki/896043488029600/900788941487552
IDEA中創建tag好像不支持添加說明
在頁面上或者用命令行操作,可以創建帶有說明的tag。git tag -a v1.4 -m 'version 1.4'
標簽總是和某個commit掛鉤。如果這個commit既出現在master分支,又出現在dev分支,那么在這兩個分支上都可以看到這個標簽
標簽是指向commit的死指針,分支是指向commit的活指針
5. 建議的習慣
5.1 commit信息
①commit信息是要寫明白的,講明白這次提交是做了哪些事。
之前某系統有人提交過二十幾個commit全寫的一樣的字,合并代碼遇到沖突時很難知道TA某次commit所做修改的目的。
②另外,如果改動較多,建議把不同的步驟做多個commit,不要都寫在一個commit里,
不便于別人查看(抄)你修改的代碼,自己回顧的時候也不方便
如果出問題,也不便于revert。雖然可以按照單個文件回退那次的修改,但若單個文件中做了多個任務的修改,還是需要查看代碼逐行確認。
下圖是,我某次commit的內容包含了四種不同內容,此刻我是反例。當然該項目未上線,處于快速開發的階段,對git使用沒有過多要求。
如果未來可能進行【復盤】來向別人分享寫過的優秀代碼,從每個commit對比文件修改前后的內容也是比較方便的。
③最后,個人習慣,建議每個commit都以【分支的尾綴】開頭,比如feat-123分支,我的commit信息會寫【123 修改某邏輯】。
還有的項目規定以類似這樣的形式開頭:feat[123]、feat-123、bugfix[123]、bugfix-123,可以自行規定。
5.2 一個分支不要被多個人使用
多個人都用同一個分支(甚至都用master)有弊端,就是沒有利用好git分支管理的優勢,就像SVN一樣在使用。
另外,共用同一個分支容易push失敗,以及pull時merge過來可能有沖突。因為該分支在遠程倉庫可能有別人push的內容,領先于你本地。
5.3 查看提交人
git中可以查看每一行是誰寫的、
git的user.name、user.email都是自己定義的,如果項目沒有明確規范要求,不建議用xxx-yyy(yyy是每個人都一樣的公共后綴),會顯示yyy,大家都是yyy的話,不直觀,得去歷史記錄里看具體的提交人、
5.4 commit和push之前確認一下自己的修改
commit和push之前確認一下自己的修改,以免提交了本不改提交的(比如修改特定的本地配置,是不是導入了包后來沒用到等等問題)
可以創建另一個changelist單獨管理暫時不需提交的修改。
5.5 不使用Fast forward模式
通常,合并分支時,如果可能,Git會用Fast forward模式,但這種模式下,刪除分支后,會丟掉分支信息。
如果要強制禁用Fast forward模式,Git就會在merge時生成一個新的commit,這樣,從分支歷史上就可以看出分支信息。
–no-ff
5.6 下班及時push代碼
每天下班push,相當于備份保護自己寫的代碼,沒下班時也可以及時push。硬件是不完全可靠的,多個備份多個保障,不要寫代碼寫好長時間(甚至以周以月為單位)一直留在自己本地不push。
哪怕沒寫完,有些地方編譯都不通過,也可以push,因為在自己的分支上且沒合并到其他分支,不影響別人。這種情況,第二天再打開繼續工作時有兩種處理方式:
-
reset(使用–soft模式,IDEA中可使用“undo commit”)回到commit前的狀態,繼續開發,再commit,再強制push
-
不reset,繼續commit最新的開發,再使用rebase合并兩個commit為一個
若在家加班換了電腦完成了一部分開發并push,第二天又用回辦公室的電腦,可以按如下步驟處理:
-
刪除本地功能分支feat-xxx
-
git pull
-
checkout 最新遠程feat-xxx分支
補充:強制Push(git push -f
)
5.7 定期刪除功能分支
有的項目要求功能分支永不刪除,
但我認為上線后功能分支留著沒用,若保留,日積月累,分支數量將無窮增長,翻找分支變得不方便。廖雪峰官網所介紹的流程也是要刪掉功能分支的。
且如果沒使用Fast forward模式,merge歷史記錄里也是能搜到這個分支名的,若像我的習慣在commit信息以【分支尾綴】開頭,更是能搜到了,所以沒必要保留已上線的功能分支。
也可以每年清理一次太舊的功能分支。
附
Git教程 - 廖雪峰的官方網站