做好版本控制,保存清晰的修改歷史,利於項目管理,有效對抗混亂。
一 Git介紹
Git是一個分佈式版本控制系統工具。
一開始Linux是可以免費使用付費的BitMover旗下的BitKeeper版本控制系統的,但是因爲Samba的開發者Andrew Tridgell嘗試破解BitKeeper協議,導致BitMover收回了BitKeeper的免費使用權。所以Linus大概花了10天用c寫了一個版本控制系統,並開始用它提交Linux核心代碼,這就是Git。
1 分佈式和集中式的區別
集中式版本控制系統依賴於中心服務器,版本庫保存在服務器上,需要修改的時候,先從服務器獲取最新的版本,修改完成後再推給服務器,必須要在有網的情況下才能使用。
分佈式版本控制系統則沒有必要的中心服務器,每個人的電腦上都有完整的版本庫,提交代碼無需服務器參與;兩個人對相同文件做的不同修改,也可以直接推送給對方。分佈式版本控制系統中的服務器,更多的工作是保存所有人的改動,方便大家同步。
所以分佈式和集中式最主要的區別是:是否能夠在各自的電腦上獨立操作而無需服務器參與。
2 Gitlab、Github、Gerrit的區別
Git:一個版本控制工具。
Github:一個只支持通過Git進行版本控制的在線代碼倉庫託管平臺。
Gitlab:一個使用Git進行代碼版本控制的倉庫管理系統軟件。和Github不同之處在於通過Gitlab可以自己搭建一個Git的私有服務器,並且有完備的web前端進行管理和權限控制。類似於可以自己搭建一個私有的Github。
Gerrit:一個開源的代碼審查軟件,主要優化代碼提交後,從開始review到合併前的多次代碼提交的流程。
二 Git整體框架
1 Git的三個工作區域
本地工作區域需要了解的概念:
- 工作目錄:就是我們看到的目錄
- 暫存區(Index/Stage):.git/index,存放commit要提交的內容
- Git倉庫:.git目錄,記錄所有的提交
常用命令和Git工作區域之間的關係:
三 Git基本操作
1 Git配置
Git作爲一個工具,自然就有一些配置,配置通過git config
命令進行。
比如,Git必須要設置的配置user.name
和user.email
,命令爲:
git config --global user.name Farmer
git config --global user.email [email protected]
Git的配置包括全局配置和倉庫級別的配置,如果沒有指定--global
,則必須在Git倉庫內執行,表示對當前倉庫進行配置。
user.name
、user.email
相當於配置的key,後面的參數就是value,如果命令中只指定key,則表示讀取當前配置中該key的值。
爲了提高效率,推薦使用git config --global alias.xx xxxx
爲常用的命令設置別稱。如下:
git config --global alias.co checkout
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.br branch
git config --global alias.hist 'log --pretty=format:"%h %ad | %s%d [%an]" --graph --date=short'
通常還會通過下面的命令修改Git默認使用的編輯器(比如修改爲vim):
git config --global core.editor vim
全局配置保存在用戶目錄
~/.gitconfig
,倉庫級別配置保存在倉庫目錄.git/config
2 Git倉庫創建
通過git init
即可初始化一個空的倉庫,此時沒有任何文件添加了版本控制。
或者也可以通過git clone
克隆一個現有的倉庫,然後在其基礎上修改。
當然也是可以清空克隆來的倉庫的所有提交記錄的。
3 查看倉庫狀態
使用命令git status
查看當前倉庫狀態。
任何時候(合併代碼遇到衝突的過程中、執行rebase命令的過程中)都可以通過此命令確定倉庫的狀態,並且在不同狀態下,按照當前狀態可以執行的操作有一系列的命令提示。
如下圖,在執行合併代碼的過程中,存在的一些命令提示:
在合併代碼時,可能會遇到衝突,此時git status
會提示我們要手動解決衝突,然後再執行後續操作,或者終止合併。
4 分支操作
一般來說,在工作中存在協同辦公,大家在各自的分支上進行改動,最後合併在master
分支上。
常用的命令如下:
操作 | 命令 |
---|---|
創建分支 | git branch <name> |
查看分支 | git branch |
查看本地、遠端所有分支 | git branch -a |
刪除分支 | git branch -D <name> |
刪除遠端分支 | git push origin --delete <name> |
切換分支 | git checkout <name> |
創建分支並切換 | git checkout -b <name> |
因爲checkout承擔了太多功能,所以在Git V2.23.0版本,Git新增了switch
命令分擔checkout
命令切換分支的功能,比如:
操作 | 命令 |
---|---|
切換分支 | git switch <name> |
切換到先前的分支 | git switch - |
創建分支並切換 | git switch -c <name> |
覆蓋創建分支並切換 | git switch -C <name> |
5 準備commit
當我們需要針對某個功能要對代碼做出改動時,我們先把所有需要改動的地方完成,可能包括以下這些改動:
- 文件改名
- 修改文件內容
- 刪除文件
- 新增文件
- 等等...
我們通過git add
指明在提交(commit)改動時需要包含哪些改動,這些改動會被保存到暫存區。
可以使用
git add <目錄>
來添加整個目錄,使用git add -u
添加所有已經在版本控制中的內容的改動。
在添加改動到暫存區後,我們可以繼續在工作區修改,可能存在下面這些操作:
- 某個文件不應該改動或者添加版本控制,那麼可以取消暫存:
git reset <file>
- 添加暫存後,在工作區又修改了某個文件,想從暫存區恢復:
git checkout -- <file>
- 某個文件不想修改了,想恢復到最新commit的狀態:
git checkout HEAD <file>
在Git V2.23.0版本中,Git還新增了restore
命令分擔了部分checkout
命令的功能,比如前面撤消改動的三個操作將對應的變成:
- 取消暫存:
git restore --stage/-S <file>
,等同於git restore --source=HEAD --stage/-S <file>
- 從暫存區恢復:
git restore <file>
(沒有指定--source
,也沒有使用--stage
,表明source爲暫存區) - 取消所有改動:
git restore --source=HEAD --staged --worktree
因此,建議升級Git使用restore命令,其參數含義更加明確。
6 提交commit
在前面充足的準備下,我們把所有需要提交的改動添加到了暫存區,那麼我們就可以開始執行提交了。
通過執行git commit
,然後就會打開默認的編輯器進行commit message的編寫。
在打開的編輯器內容中默認還會包含目前添加的改動內容,幫助我們確定commit message怎麼書寫,但是因爲這些內容都是"#"開頭的,所以並不會真的出現在最後的commit message中。
爲了良好的管理,編寫commit message最好遵守一定的規則,比如對改動進行分類,劃定範圍,語句使用統一的句式等等。
7 撤銷commit
當我們想要撤銷一個commit時,我們可以使用git reset
命令,命令格式爲:
git reset [<mode>] [<commit>]
mode有5個選擇,常用的是前三個,--mixed
是不指定mode時的默認參數:
-
--soft
:把撤銷的改動保留到暫存區,不改變工作區的內容; -
--mixed
:撤銷的改動不會保留到暫存區,也不改變工作區的內容; -
--hard
:撤銷的改動不會保留在暫存區,而是直接應用到工作區,相當於執行--soft
之後,再從暫存區checkout; -
--merge
:更多用在撤銷一個merge,這時會直接修改工作區,需慎用; -
--keep
:類似--hard
,但是如果工作區有了改動,那麼會中止reset。
對於
--hard
如果工作區有的文件改動和提交了的內容不一致,那麼工作區的改動將會被覆蓋;如果不確定怎麼操作,建議執行--soft
,再選擇要保留(checkout)或者丟棄(reset)哪些改動。
8 “修改”commit
雖然提交前做了充足的準備,但是總是可能出現一些意外,比如:
- 發現commit message出現了typo,顯得非常不合適,必須改過來;
- 有新增的文件沒有添加;
- copy別人的代碼忘記改author;
- 等等...
那麼此時可以把需要改動的地方修改完成,同樣通過git add
添加暫存,然後使用git commit --amend
進行提交,並重新修改commit message即可。
也可以直接執行
git commit --amend
去修改commit message。
其實這個操作並不是修改commit,而是撤銷原先的commit,並重新創建了一個新的commit,並且獲取了此前的commit message,相當於:
$ git reset --soft HEAD^
$ ... do something else to come up with the right tree ...
$ git commit -c ORIG_HEAD
9 管理tag
在比較重要的版本上,我們可能需要創建標籤,使用的命令是git tag
,常用的命令有下面這些:
操作 | 命令 |
---|---|
顯示tag | git tag |
顯示匹配tag | git tag -l "v1.0.*" |
查看tag信息 | git show <tag-name> |
添加輕量tag | git tag "v1.0.0" |
添加註解tag | git tag -a "v1.0.0" -m "tag message" |
指定commit添加tag | git tag -a "v1.0.0" -m "tag message" <commit-id> |
刪除tag | git tag -d "v1.0.0 |
刪除y遠端tag | git push origin :refs/tags/v1.0.0 |
推送tag | git push origin v1.0.0 |
推送所有tag | git push origin --tags |
打標籤的操作相對獨立;
-m
如果沒有指定,則會打開編輯器讓用戶輸入tag message。
10 拉取和推送改動
使用git pull
命令拉取改動,命令格式爲:
$ git pull <遠程主機名> <遠程分支名>:<本地分支名>
比如,要獲取遠程origin
主機上的remote_master
分支作爲本地的master
分支:
$ git pull origin remote_master:master
如果不指定本地分支名,則表示是當前分支;
如果本地分支和遠程分支有綁定關係,並且只綁定了一個遠程分支,則命令可簡化爲:
$ git pull
綁定遠端分支可以通過命令:
git branch --set-upstream master origin/remote_master
使用git push
命令推送改動。命令格式和git pull
類似,但是本地分支名和遠端分支名互換了位置,爲:
$ git push <遠程主機名> <本地分支名>:<遠程分支名>
比如將本地master
分支推送到遠端remote_master
分支,命令爲:
$ git push origin master:remote_master
和git pull
類似,如果本地分支和遠程分支有綁定關係,並且只綁定了一個遠程分支,則命令可簡化爲:
$ git push
Git並不複雜,可以本地建立一個倉庫多加練習,實際工作中保持合理的使用流程,還是很好用的。