教你打通Git的任督二脈
本文轉(zhuǎn)載自微信公眾號(hào)「狼王編程」,作者狼王。轉(zhuǎn)載本文請(qǐng)聯(lián)系狼王編程公眾號(hào)。
大家好,我是狼王,一個(gè)愛打球的程序員
這篇主要讓我們來學(xué)習(xí)一下Git,這個(gè)分布式版本控制系統(tǒng)
在日常工作中,經(jīng)常會(huì)用到Git操作。但是對(duì)于很多人來講,剛上來對(duì)Git很陌生,操作起來也很懵逼。本篇文章主要針對(duì)剛開始接觸Git的新人,理解Git的基本原理,掌握常用的一些命令。
關(guān)于版本控制
什么是版本控制?我真的需要嗎?版本控制是一種記錄若干文件內(nèi)容變化,以便將來查閱特定版本修訂情況的系統(tǒng)。
什么是分布式版本控制系統(tǒng)分布式版本控制系統(tǒng)( Distributed Version Control System,簡稱 DVCS )。
在這類系統(tǒng)中,像 Git,Mercurial,Bazaar 以及 Darcs 等,客戶端并不只提取最新版本的文件快照,而是把原始的代碼倉庫完整地鏡像下來。這么一來,任何一處協(xié)同工作用的服務(wù)器發(fā)生故障,事后都可以用任何一個(gè)鏡 像出來的本地倉庫恢復(fù)。因?yàn)槊恳淮蔚奶崛〔僮?,?shí)際上都是一次對(duì)代碼倉庫的完整備份。
更進(jìn)一步,許多這類系統(tǒng)都可以指定和若干不同的遠(yuǎn)端代碼倉庫進(jìn)行交互。借此,你就可以在同一個(gè)項(xiàng)目中,分別和不同工作小組的人相互協(xié)作。你可以根據(jù)需要設(shè)定不同的協(xié)作流程,比如層次模型式的工作流,而這在以前的集中式系統(tǒng)中是無法實(shí)現(xiàn)的。
一、Git工作流程
以上包括一些簡單而常用的命令,但是先不關(guān)心這些,先來了解下面這4個(gè)專有名詞。
- Workspace:工作區(qū)
- Index / Stage:暫存區(qū)
- Repository:倉庫區(qū)(或本地倉庫)
- Remote:遠(yuǎn)程倉庫
工作區(qū)指的是在PC中能看得到的創(chuàng)建的一個(gè)管理倉庫的目錄,比如我的test文件夾就是一個(gè)工作區(qū):如下圖:
暫存區(qū)
.git目錄下的index文件, 暫存區(qū)會(huì)記錄git add添加文件的相關(guān)信息(文件名、大小、timestamp...),不保存文件實(shí)體, 通過id指向每個(gè)文件實(shí)體??梢允褂胓it status查看暫存區(qū)的狀態(tài)。暫存區(qū)標(biāo)記了你當(dāng)前工作區(qū)中,哪些內(nèi)容是被git管理的。
當(dāng)你完成某個(gè)需求或功能后需要提交到遠(yuǎn)程倉庫,那么第一步就是通過git add先提交到暫存區(qū),被git管理。
本地倉庫
保存了對(duì)象被提交 過的各個(gè)版本,比起工作區(qū)和暫存區(qū)的內(nèi)容,它要更舊一些。
git commit后同步index的目錄樹到本地倉庫,方便從下一步通過git push同步本地倉庫與遠(yuǎn)程倉庫的同步。
遠(yuǎn)程倉庫
遠(yuǎn)程倉庫的內(nèi)容可能被分布在多個(gè)地點(diǎn)的處于協(xié)作關(guān)系的本地倉庫修改,因此它可能與本地倉庫同步,也可能不同步,但是它的內(nèi)容是最舊的。
小結(jié)
任何對(duì)象都是在工作區(qū)中誕生和被修改;
任何修改都是從進(jìn)入index區(qū)才開始被版本控制;
只有把修改提交到本地倉庫,該修改才能在倉庫中留下痕跡;
與協(xié)作者分享本地的修改,可以把它們push到遠(yuǎn)程倉庫來共享。
下面這幅圖更加直接闡述了四個(gè)區(qū)域之間的關(guān)系,可能有些命令不太清楚,沒關(guān)系,下部分會(huì)詳細(xì)介紹。
二、常用Git命令
網(wǎng)上找了個(gè)圖,別人整理的一張圖,很全很好,借來用下。下面詳細(xì)解釋一些常用命令。
HEAD
在掌握具體命令前,先理解下HEAD。
這要從git的分支說起,git 中的分支,其實(shí)本質(zhì)上僅僅是個(gè)指向 commit 對(duì)象的可變指針。git 是如何知道你當(dāng)前在哪個(gè)分支上工作的呢?其實(shí)答案也很簡單,它保存著一個(gè)名為 HEAD 的特別指針。在 git 中,它是一個(gè)指向你正在工作中的本地分支的指針,可以將 HEAD 想象為當(dāng)前分支的別名。
add
add相關(guān)命令很簡單,主要實(shí)現(xiàn)將工作區(qū)修改的內(nèi)容提交到暫存區(qū),交由git管理。
- git add . 添加當(dāng)前目錄的所有文件到暫存區(qū)
- git add dir 添加指定目錄到暫存區(qū),包括子目錄
- git add file 添加指定文件到暫存區(qū)
git add -A 和 git add . 和 git add -u 的區(qū)別
- git add -A : 把所有變化提交到索引庫,包含當(dāng)前git倉庫的所有目錄
- git add -u : 提交被修改(modified)和被刪除(deleted)文件,不包括新文件(new)
- git add . : 該操作與git 的版本有關(guān):
- -1.x 版本:提交新文件(new)和被修改(modified)文件,不包括被刪除(deleted)文件
- -2.x 版本:和git add -A一樣,提交所有變化
commit
git commit 主要是將暫存區(qū)里的改動(dòng)給提交到本地的版本庫。每次使用git commit 命令我們都會(huì)在本地版本庫生成一個(gè)40位的哈希值,這個(gè)哈希值也叫commit-id,commit-id在版本回退的時(shí)候是非常有用的,它相當(dāng)于一個(gè)快照,可以在未來的任何時(shí)候通過與git reset的組合命令回到這里.
git commit -m message 提交暫存區(qū)到本地倉庫,message代表說明信息
git commit file -m message 提交暫存區(qū)的指定文件到本地倉庫
git commit --amend -m message 使用一次新的commit,替代上一次提交
branch
涉及到協(xié)作,自然會(huì)涉及到分支,關(guān)于分支,大概有展示分支,切換分支,創(chuàng)建分支,刪除分支這四種操作。
- git branch 列出所有本地分支
- git branch -r 列出所有遠(yuǎn)程分支
- git branch -a 列出所有本地分支和遠(yuǎn)程分支
- git branch branch-name 新建一個(gè)分支,但依然停留在當(dāng)前分支
- git checkout -b branch-name 新建一個(gè)分支,并切換到該分支
- git branch --track branch remote-branch 新建一個(gè)分支,與指定的遠(yuǎn)程分支建立追蹤關(guān)系
- git checkout branch-name 切換到指定分支,并更新工作區(qū)
- git branch -d branch-name 刪除分支
- git push origin --delete branch-name 刪除遠(yuǎn)程分支
- 關(guān)于分支的操作雖然比較多,但都比較簡單好記。
merge
Git的git-merge是在Git中頻繁使用的一個(gè)命令,很多人都覺得git合并是一個(gè)非常麻煩的事情,一不小心就會(huì)遇到丟失代碼的問題,從而對(duì)git望而卻步。
merge命令把不同的分支合并起來。如上圖,在實(shí)際開放中,我們可能從master分支中切出一個(gè)分支,然后進(jìn)行開發(fā)完成需求,中間經(jīng)過R3,R4,R5的commit記錄,最后開發(fā)完成需要合入master中,這便用到了merge。
- git fetch remote merge之前先拉一下遠(yuǎn)程倉庫最新代碼
- git merge branch 合并指定分支到當(dāng)前分支 一般在merge之后,會(huì)出現(xiàn)conflict,需要針對(duì)沖突情況,手動(dòng)解除沖突。主要是因?yàn)閮蓚€(gè)用戶修改了同一文件的同一塊區(qū)域。
rebase
rebase又稱為衍合,是合并的另外一種選擇。
在開始階段,我們處于new分支上,執(zhí)行g(shù)it rebase dev,那么new分支上新的commit都在master分支上重演一遍,最后checkout切換回到new分支。這一點(diǎn)與merge是一樣的,合并前后所處的分支并沒有改變。git rebase dev,通俗的解釋就是new分支想站在dev的肩膀上繼續(xù)下去。rebase也需要手動(dòng)解決沖突。
rebase與merge的區(qū)別
現(xiàn)在我們有這樣的兩個(gè)分支,test和master,提交如下:
- D---E test
- /
- A---B---C---F master
在master執(zhí)行g(shù)it merge test,然后會(huì)得到如下結(jié)果:
- D--------E
- / \
- A---B---C---F----G test, master
在master執(zhí)行g(shù)it rebase test,然后得到如下結(jié)果:
- A---B---D---E---C'---F' test, master
可以看到,merge操作會(huì)生成一個(gè)新的節(jié)點(diǎn),之前的提交分開顯示。而rebase操作不會(huì)生成新的節(jié)點(diǎn),是將兩個(gè)分支融合成一個(gè)線性的提交。
如果你想要一個(gè)干凈的,沒有merge commit的線性歷史樹,那么你應(yīng)該選擇git rebase 如果你想保留完整的歷史記錄,并且想要避免重寫commit history的風(fēng)險(xiǎn),你應(yīng)該選擇使用git merge
reset
有時(shí)候,我們用Git的時(shí)候有可能commit提交代碼后,發(fā)現(xiàn)這一次commit的內(nèi)容是有錯(cuò)誤的,那么有兩種處理方法:
- 修改錯(cuò)誤內(nèi)容,再次commit一次
- 使用git reset 命令撤銷這一次錯(cuò)誤的commit
第一種方法比較直接,但會(huì)多次一次commit記錄。而我個(gè)人更傾向第二種方法,錯(cuò)誤的commit沒必要保留下來。
reset命令把當(dāng)前分支指向另一個(gè)位置,并且相應(yīng)的變動(dòng)工作區(qū)和暫存區(qū)。
- git reset —soft commit 只改變提交點(diǎn),暫存區(qū)和工作目錄的內(nèi)容都不改變
- git reset —mixed commit 改變提交點(diǎn),同時(shí)改變暫存區(qū)的內(nèi)容
- git reset —hard commit 暫存區(qū)、工作區(qū)的內(nèi)容都會(huì)被修改到與提交點(diǎn)完全一致的狀態(tài)
- git reset --hard HEAD 讓工作區(qū)回到上次提交時(shí)的狀態(tài)
revert
git revert用一個(gè)新提交來消除一個(gè)歷史提交所做的任何修改。
revert與reset的區(qū)別
- git revert是用一次新的commit來回滾之前的commit,git reset是直接刪除指定的commit。
- 在回滾這一操作上看,效果差不多。但是在日后繼續(xù)merge以前的老版本時(shí)有區(qū)別。因?yàn)間it revert是用一次逆向的commit“中和”之前的提交,因此日后合并老的branch時(shí),導(dǎo)致這部分改變不會(huì)再次出現(xiàn),減少?zèng)_突。但是git reset是之間把某些commit在某個(gè)branch上刪除,因而和老的branch再次merge時(shí),這些被回滾的commit應(yīng)該還會(huì)被引入,產(chǎn)生很多沖突。關(guān)于這一點(diǎn),不太理解的可以看這篇文章。
- git reset 是把HEAD向后移動(dòng)了一下,而git revert是HEAD繼續(xù)前進(jìn),只是新的commit的內(nèi)容和要revert的內(nèi)容正好相反,能夠抵消要被revert的內(nèi)容。
push
git push的一般形式為 git push <遠(yuǎn)程主機(jī)名> <本地分支名> <遠(yuǎn)程分支名> ,例如 git push origin master:refs/for/master ,即是將本地的master分支推送到遠(yuǎn)程主機(jī)origin上的對(duì)應(yīng)master分支, origin 是遠(yuǎn)程主機(jī)名
- git push remote branch 上傳本地指定分支到遠(yuǎn)程倉庫
- git push remote --force 強(qiáng)行推送當(dāng)前分支到遠(yuǎn)程倉庫,即使有沖突
- git push remote --all 推送所有分支到遠(yuǎn)程倉庫
其他命令
- git status 顯示有變更的文件
- git log 顯示當(dāng)前分支的版本歷史
- git diff 顯示暫存區(qū)和工作區(qū)的差異
- git diff HEAD 顯示工作區(qū)與當(dāng)前分支最新commit之間的差異
- git cherry-pick commit 選擇一個(gè)commit,合并進(jìn)當(dāng)前分支
以上就是關(guān)于Git的一些常用命令及詳細(xì)闡述,相信能對(duì)Git有一個(gè)初步的認(rèn)識(shí)。