版本管理涉及團隊協(xié)作,產(chǎn)品質(zhì)量,和產(chǎn)品上線。使用版本控制工具可使我們自由的做的一些幾點:
版本控制系統(tǒng)(Version Control System)是一種記錄若干文件修訂記錄的系統(tǒng),它可以幫助開發(fā)者查閱或回檔至某個歷史版本。
手動版本控制
無法有效找到需要版本和差異,污染工作目錄結(jié)構(gòu)。
Local VCS 本地式
于是出現(xiàn)了本地版本控制系統(tǒng) RCS(Reversion Control System),其利用本地版本數(shù)據(jù)庫存儲不斷出現(xiàn)的文件版本。 它可以迅速找出需求的版本和維持工作目錄結(jié)構(gòu)。其缺點是不支持協(xié)同開發(fā),這也讓開發(fā)者不將其選做通用的版本控制工具來使用。
CVCS 集中式
利用中央服務器來管理文件版本,但每一次操作都需要網(wǎng)絡請求,且具有致命的單點故障。 (既中央服務器故障可導致,無法協(xié)同工作或數(shù)據(jù)丟失)
DVCS 分布式
分布式指的是每一份本地倉庫都是一個完整的項目歷史拷貝,即使一份倉庫丟失或者損壞也可以從其他的倉庫中獲取此項目的完整歷史記錄。 也因為在添加新版本不需要網(wǎng)絡,這可以使操作流程。
如果多人并行在一條線上開發(fā)會導致開發(fā)困難并且難以定位錯誤位置。
分支,就是從目標倉庫獲得一份項目拷貝,每條分支都具有和原倉庫功能一樣的開發(fā)線。
分支模型(Branching Model)或稱之為工作流(Workflow),它是一個圍繞項目 開發(fā)、部署、測試等工作流的分支操作(創(chuàng)建及合并等)的規(guī)范集合。
常駐分支
活動分支
Git 是一個免費開源的分布式版本控制系統(tǒng),它也一個基于內(nèi)容尋址的存儲系統(tǒng)。 Git 是由 Linux 的創(chuàng)造者 Linus Torvalds。
優(yōu)勢
安裝
Mac OS X 下使用 brew install git
下載更新既可。
Linux Ubuntu 下可使用 apt-get install git
既可。
常用 Git 命令,當在命令行中鍵入 git
可以便可以在幫助信息中看到。
獲取幫助
git help <command>
git <command> -h
git <command> --help
還有 man git-<command>
均可查詢某個命令的幫助文檔。
基本操作
配置 Git 使用 git config
此為創(chuàng)建 Git 倉庫前必須完成的配置。
git config --global user.name "Li Xinyang"
git config --global user.email "lixinyang1026@gmail.com"
.git/config
~/.gitconfig
/etc/gitconfig
初始化倉庫
初始化倉庫,使用 git status
可以查詢當前倉庫的狀態(tài)。 如在未初始化倉庫時查詢狀態(tài)會報出錯誤信息。
git init [path]
git init [path] --bare
在初始化倉庫后會出現(xiàn)一個隱藏的目錄 .git
其中包括了所有的當前倉庫的版本信息和本地設置文件(.git/config
)。
查詢狀態(tài)
git status
此命令可以幫助開發(fā)者在下面三對關系中找出文件狀態(tài)的變化。
Git 中存在兩種狀態(tài)內(nèi)容狀態(tài)和文件狀態(tài)。 倉庫中的文件均可以在狀態(tài)和區(qū)域之間進行轉(zhuǎn)換。
添加文件到暫存區(qū)(同時跟蹤文件)
git add [file]
上圖所示,我們將 README.md
文件從工作區(qū)提交至暫存區(qū), 并將文件狀態(tài)從未跟蹤改變成已跟蹤。
NOTE:批量增加當前目錄下全部文件 git add .
忽略文件
.gitignore
可以在添加至倉庫時忽略匹配的文件,但僅作用于未跟蹤的文件。
NOTE:GitHub 為各個類型項目和操作系統(tǒng)提供了忽略文件模板, 可以在這里找到。
暫存區(qū)刪除文件
使用 git rm
可以做到從暫存區(qū)刪除文件,下面提供幾種常用的使用方法:
git rm --cached
僅存暫存區(qū)刪除git rm
才暫存區(qū)和工作區(qū)目錄中刪除git rm $(git ls-files --deleted)
刪除所有被跟蹤但在工作區(qū)已經(jīng)被刪除的文件NOTE:git-ls-files
工作區(qū)與暫存區(qū)
不同的區(qū)域中可以存在文件的獨立版本,如下圖所示工作區(qū)和暫存區(qū)的文件為兩個不同的版本。 (之前上個例子中所創(chuàng)建的 DummyFile
文件已被刪除)
暫存區(qū)
我們可以把暫存區(qū)比作一個每類物品只能放置一次的購物車此外還具有下面的特質(zhì):
其中
提交版本記錄
git commit
可以根據(jù)暫存區(qū)的內(nèi)容創(chuàng)建一個提交目錄。
NOTE:直接提交工作區(qū)的內(nèi)容git commit -a -m 'message'
,工作中不建議這樣操作。
查詢提交歷史記錄
git log
可以用來顯示提交是記錄信息。
其中包括:
工作中可使用下面簡單的配置進行版本查詢
git log --oneline
# 較長的命令可以使用 alias 的方法簡化
git log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
Git 中 alias 命令設置
配置 Git 中別名的方法 git config alias.shortname <fullcommand>
。
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
git lg
NOTE:如果你使用 Mac OS X 可以嘗試使用 Oh My Zsh 其中已經(jīng)預先設置好了非常多常用別名。
顯示版本差異
git diff
用于顯示版本差異,下面是幾個常用的方法:
git diff
顯示工作目錄與暫存區(qū)的差異git diff -cached [<reference>]
暫存區(qū)與某次提交的差異(默認為 HEAD)git diff <reference>
工作目錄和某次提交間的差異git diff <reference> <reference>
查詢兩次提交直接的差別撤銷工作區(qū)的修改
git checkout -- <file>...
可用于撤銷工作區(qū)的修改 (此方法會丟棄工作區(qū)修改且不可恢復),下面是一些常用方法:
git checkout -- <file>
將文件從暫存區(qū)復制到工作目錄NOTE:使用 --
是為了避免路徑和引用(或提交 ID)同名發(fā)生的沖突。
撤銷暫存區(qū)內(nèi)容
使用 git reset HEAD <file>...
可用于撤銷暫存區(qū)的修改,下面是一些常用操作:
git reset HEAD <file>
將文件內(nèi)容存上次提交復制到暫存區(qū)。撤銷全部修改
git checkout HEAD -- <file>
可以直接將內(nèi)容從上次的提交復制到工作區(qū)。
命令總結(jié)
git branch
使用 git branch
可以對倉庫分支進行增刪查改的操作,下面列舉了一下常用的操作方式:
git branch <branchname>
,創(chuàng)建指定分支git branch -d <branchname>
,刪除指定分支git branch -v
,顯示所有分支信息一份分支的引用只是一個文本文件,里面只有一個 SHA 編碼。它保存于
.get/refs/heads/master
中。—— 鄭海波 網(wǎng)易工程師
git branch next
git commit -m 'message'
切換至目標分支
git checkout
它可以本枝上根據(jù)通過移動 HEAD(指向當前的提交) 檢測出版本, 也可用于切換分支。(其會把當前的工作目錄和暫存區(qū)移動到提出分支的版本) 常用命令有:
git checkout <branchname>
,使指針指向目標分支git checkout -b <branchname>
,創(chuàng)建目標分支并切換分支git checkout <reference>
,可以指向任何一個版本git checkout next
NOTE:所有提交是更具 HEAD 向前進的,所以前后分支后則會跟著 Next 分支進行開發(fā)。
git commit -m 'message'
# -- 為短名與 cd 類似
git checkout --
# 或者使用
# git checkout master
git checkout -b Issue-26
NOTE:使用 git branch -v
可以列出全部分支,帶 *
表示當前所屬分支(HEAD 指向分支)。
git checkout c4006ec
當 HEAD 指針與具體的分支分離時,我們將其稱之為 detached head
。 如果 HEAD 在分離狀態(tài)則因盡量避免在此狀態(tài)下進行提交,只做內(nèi)容的查看。
完全回退
使用git reset
可以將當前分支回退到歷史中的某個版本, 下面為常用的三種方式(三種的區(qū)別是恢復的內(nèi)容時候同時會恢復的工作區(qū)或暫存區(qū)):
git reset --mixed <commit>
默認方式,內(nèi)容存入暫存區(qū)git reset --soft <commit>
內(nèi)容存入暫存區(qū)和工作區(qū)git reset --hard <commit>
暫存區(qū)和工作區(qū)保留現(xiàn)有狀態(tài)git reset --mixed e390b3
如果上一個命令如果使用 hard
git reset --hard e390b3
如果上一個命令如果使用 hard
get reset --soft e390b3
此方法暫存區(qū)和工作目錄不會發(fā)生任何變化僅僅只是 HEAD 指針發(fā)生了變化, 但原有的提交已經(jīng)無指針指向成為無索引的提交其就有可能被回收。
查詢所有提交記錄
git reflog
會根據(jù)倉庫的提交順序按順序來排列,其中包括無索引的提交, 可以在這里使用 HASH 值來進行,但是無索引的提交可能會丟失。
使用捷徑
A^
表示 A
上的父提交,多個 ^
可表示以上的多個級別。 A~n
則表示在 A
之前的第 n 次提交。
兩種方法都有兩個作用范圍,一個是分支操作(commit 操作), 另一個是文件操作(file 操作)。
命令 | 范例 | 移動 HEAD/Branch | 注釋 |
---|---|---|---|
git reset [commit] | git reset HEAD^ --soft | 是/是 | 完全回退到某個提交(之前所在的位置將失去索) |
git reset [file] | git reset README.md | 否/否 | 恢復暫存區(qū)到某個提交狀態(tài)(不移動指針) |
git checkout [commit] | git checkout master | 是/否 | 移動當前指針 HEAD 到某個提交(并復制內(nèi)容到工作目錄) |
git checkout [file] | git checkout -- README.md git checkout HEAD -- xx.log | 否/否 | 恢復工作目錄到某個狀態(tài) |
git checkout next
# error: Your local change to the following files whould be overwritten by checkout:
# README.md
# Please, commit your changes or stash them before you can switch branches.
# Aborting...
突然需要切換到其他分支,工作區(qū)和暫存區(qū)還有在當前分支沒完成的任務。那么 stash
就使用 .git
中的特殊區(qū)(Stash 區(qū))來幫你解決這個問題(因為強切回丟失當前的工作區(qū)和暫存區(qū)的內(nèi)容)。
stash
可以把當前工作區(qū)和暫存區(qū)的狀態(tài)以棧(Stack)的形式保存起來(每次保存都會推一個內(nèi)容到 stash
棧中),并返回一個干凈的工作空間(工作區(qū)和暫存區(qū))。
NOTE:stash pop = stash apply + stash drop
類似于 JavaScript 中的 pop
操作。
NOTE+:什么是棧?可以把棧想象成一摞盤子堆(一個疊一個)。具體關于堆棧的信息可以在這里找到。如果還看不懂,建議完成哈佛大學在線計算機入門課程 CS50
,可以在這里找到。
使用 git merge
可用于合并分支。下面的例子是將 next
分支合并到 master
分支中去。
當一個文件被同時修改時(更多情況為同時修改相同的一行代碼時)則極有可能產(chǎn)生合并沖突。
git merge next master
# Autom-merging README.md
# CONFLICT (content): Merge confilict in README.md
# Automatic merge failed; fix confilict and then commit the result
git status
# On branch master
# You have unmerged paths.
# (fix confilict and run 'git commit')
# ...
# both:modified: README.md
# no changes added to commit (use "git add" and/or "git commit -a")
在解決完合并沖突后可以使用 git add .
然后 git commit -m 'resove merge confilict'
來完成合并沖突解決并提交一個新的版本。
NOTE:git cat-file -p HEAD
可用于顯示 git
中某個對象的具體信息。 NOTE+:<<<<<<<< HEAD
與 =========
之間為 HEAD 所在的內(nèi)容。
也并不是所有的合并操作都會造成合并從圖(merge confilic)。最簡單的一種合并是 fast-forward
僅僅只是變化 HEAD 指向的位置(不產(chǎn)生新的合并節(jié)點)。
如果需要生成新的合并節(jié)點可以使用 git merge next --no-ff
意思是合并但不使用 fast-forward
。
當參與的人閱讀分支越多其分支結(jié)構(gòu)就越復雜和難以被理解。如何實現(xiàn)在任何狀態(tài)下的線性提交?
如需完成線性提交可以使用 git rebase
,其可以修剪提交歷史的基線。它會將不同分支的提交在所選節(jié)點上進行重演(重演并重新創(chuàng)造新節(jié)點)這里 HEAD/Branch 均會發(fā)生移動。
git rebase master
但有時并不需要將其他分支上的全部提交節(jié)點統(tǒng)統(tǒng)進行重演。則可以使用 git rebase --onto
來選擇需要重演的提交節(jié)點。
git rebase --onto master 5751363
NOTE:上面的紅色的節(jié)點,未被重建(被丟棄)。
rebase
會產(chǎn)生線性的提交歷史,merge
則會產(chǎn)生多個不同分支的合并節(jié)點。所以具體沒有好壞之分,可根據(jù)使用的需求來決定。
注意!不要在共有分支上使用 rebase
(例如 master
分支)這會導致其他開發(fā)者在進行拉?。≒ull)時,必須進行合并且合并中包含重復的提交。
不論是 Branch 還是 HEAD 它們均為動態(tài)指針,如果想定義一個靜止的標示則可以使用 git tag
,它將給發(fā)布的提交版本設置一個別名。在設置了標簽后就可以直接使用標簽名來代替它所指代的版本提交了。
git tag v0.1 e39d9b2
git checkout v0.1
遠程操作可以將本地倉庫推送至遠程倉庫服務器。Git 支持許多主流的通信協(xié)議,其中包括 Local
、HTTP
、SSH
、還有Git
。服務器只應該是作為同步之用(被動接受既可)。
初始化一個本地的遠程服務器
git init ~/git-server --bare
推送
git push
可以將當期的全部版本提交提交推送至遠程倉庫,其完成了提交歷史的完全不復制并同時移動復制版本的 HEAD 與 Branch。
添加遠程倉庫別名
git remote
可用于添加遠程倉庫的別名。
# 更改倉庫 url 地址
git remote set-url origin 'https://github.com/li-xinyang/FEND_Note.git'
遠程 push 沖突
可以先使用 git fetch
+ git merge
來解決沖突的問題。git pull
就等同于 fetch
與 merge
的合并。
克隆遠程倉庫
使用 git clone
可以克隆遠程倉庫,并將克隆地址默認設為 origin
。
更多建議: