版本控制
版本管理涉及團隊協作,產品質量,和產品上線。使用版本控制工具可使我們自由的做的一些幾點:
- 回退到任意版本
- 查看歷史版本
- 對比兩個版本差異
版本控制系統
版本控制系統(Version Control System)是一種記錄若干文件修訂記錄的系統,它可以幫助開發者查閱或回檔至某個歷史版本。
- 手動版本控制
- LVCS 本地
- CVCS 集中式(例如 SVN)
- DVCS 分布式(例如 Git)
手動版本控制
無法有效找到需要版本和差異,污染工作目錄結構。
Local VCS 本地式
于是出現了本地版本控制系統 RCS(Reversion Control System),其利用本地版本數據庫存儲不斷出現的文件版本。
它可以迅速找出需求的版本和維持工作目錄結構。其缺點是不支持協同開發,這也讓開發者不將其選做通用的版本控制工具來使用。
CVCS 集中式
利用中央服務器來管理文件版本,但每一次操作都需要網絡請求,且具有致命的單點故障。
(既中央服務器故障可導致,無法協同工作或數據丟失)
DVCS 分布式
分布式指的是每一份本地倉庫都是一個完整的項目歷史拷貝,即使一份倉庫丟失或者損壞也可以從其他的倉庫中獲取此項目的完整歷史記錄。
也因為在添加新版本不需要網絡,這可以使操作流程。
分支模型
如果多人并行在一條線上開發會導致開發困難并且難以定位錯誤位置。
分支,就是從目標倉庫獲得一份項目拷貝,每條分支都具有和原倉庫功能一樣的開發線。
分支模型(Branching Model)或稱之為工作流(Workflow),它是一個圍繞項目
開發、部署、測試等工作流的分支操作(創建及合并等)的規范集合。
產品級開發分支模型
常駐分支
- development,從 master 創建
- production(master),默認分支可用于發布的代碼
活動分支
- feature,從 development,創建其為特性分支
- hotfix,從 master 創建,用于修復 Bug
- release,從 development 創建
環境
- 開發環境,使用測試開發配置(數據庫,緩存,元數據配置)
- 使用提交到下一個 release 的特性分支
- 測試環境,使用測試配置(測試數據庫)
- 使用 release/development
- 預發布環境,小范圍發布使用線上數據庫模擬真實環境
- 使用 release
- 生產環境,線上配置
- 使用 master
Git
Git 是一個免費開源的分布式版本控制系統,它也一個基于內容尋址的存儲系統。
Git 是由 Linux 的創造者 Linus Torvalds。
優勢
- 速度快,不依賴網絡
- 完全分布式
- 輕量級分支操作
- Git 為行業標準版本控制供給
- 活躍和成熟的社區
安裝
Mac OS X 下使用 brew install git
下載更新既可。
Linux Ubuntu 下可使用 apt-get install git
既可。
Git 介紹
Git 命令
常用 Git 命令,當在命令行中鍵入 git
可以便可以在幫助信息中看到。
獲取幫助
git help <command>
git <command> -h
git <command> --help
還有
man git-<command>
均可查詢某個命令的幫助文檔。
基本操作
配置 Git 使用 git config
此為創建 Git 倉庫前必須完成的配置。
git config --global user.name "Li Xinyang"
git config --global user.email "lixinyang1026@gmail.com"
- --local 默認 具有最高優先級 只影響本倉庫
.git/config
- --global 中級優先級 影響到所有當前用戶的倉庫
~/.gitconfig
- --system 最低優先級 影響到全系統的倉庫
/etc/gitconfig
初始化倉庫
初始化倉庫,使用 git status
可以查詢當前倉庫的狀態。
如在未初始化倉庫時查詢狀態會報出錯誤信息。
git init [path]
git init [path] --bare
在初始化倉庫后會出現一個隱藏的目錄 .git
其中包括了所有的當前倉庫的版本信息和本地設置文件(.git/config
)。
查詢狀態
git status
此命令可以幫助開發者在下面三對關系中找出文件狀態的變化。
- 未跟蹤 <--> 跟蹤
- 工作目錄 <--> 暫存區
- 暫存區 <--> 最新提交
Git 中存在兩種狀態內容狀態和文件狀態。
倉庫中的文件均可以在狀態和區域之間進行轉換。
添加文件到暫存區(同時跟蹤文件)
git add [file]
上圖所示,我們將 README.md
文件從工作區提交至暫存區,
并將文件狀態從未跟蹤改變成已跟蹤。
NOTE:批量增加當前目錄下全部文件 git add .
忽略文件
.gitignore
可以在添加至倉庫時忽略匹配的文件,但僅作用于未跟蹤的文件。
NOTE:GitHub 為各個類型項目和操作系統提供了忽略文件模板,
可以在這里找到。
暫存區刪除文件
使用 git rm
可以做到從暫存區刪除文件,下面提供幾種常用的使用方法:
-
git rm --cached
僅存暫存區刪除 -
git rm
才暫存區和工作區目錄中刪除 -
git rm $(git ls-files --deleted)
刪除所有被跟蹤但在工作區已經被刪除的文件
NOTE:git-ls-files
- Show information about files in the index and the working tree
工作區與暫存區
不同的區域中可以存在文件的獨立版本,如下圖所示工作區和暫存區的文件為兩個不同的版本。
(之前上個例子中所創建的 DummyFile
文件已被刪除)
暫存區
我們可以把暫存區比作一個每類物品只能放置一次的購物車此外還具有下面的特質:
- 貨架和購物車可同時出現同種物品
- 貨架上的物品可以替換掉購物車的物品
- 可以刪除物品
- 提交購物車完成購買并生成購買記錄
其中
- 物品:文件
- 貨架:工作目錄
- 購物車:暫存區
- 購買:提交內容
提交版本記錄
git commit
可以根據暫存區的內容創建一個提交目錄。
NOTE:直接提交工作區的內容git commit -a -m 'message'
,工作中不建議這樣操作。
查詢提交歷史記錄
git log
可以用來顯示提交是記錄信息。
其中包括:
- 提交編號 SHA-1 編碼的 HASH 標示符
- git-config 配置的提交者信息
- 提交日期
- 提交描述信息
工作中可使用下面簡單的配置進行版本查詢
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
其中已經預先設置好了非常多常用別名。
顯示版本差異
git diff
用于顯示版本差異,下面是幾個常用的方法:
-
git diff
顯示工作目錄與暫存區的差異 -
git diff -cached [<reference>]
暫存區與某次提交的差異(默認為 HEAD) -
git diff <reference>
工作目錄和某次提交間的差異 -
git diff <reference> <reference>
查詢兩次提交直接的差別
撤銷工作區的修改
git checkout -- <file>...
可用于撤銷工作區的修改
(此方法會丟棄工作區修改且不可恢復),下面是一些常用方法:
-
git checkout -- <file>
將文件從暫存區復制到工作目錄
NOTE:使用 --
是為了避免路徑和引用(或提交 ID)同名發生的沖突。
撤銷暫存區內容
使用 git reset HEAD <file>...
可用于撤銷暫存區的修改,下面是一些常用操作:
-
git reset HEAD <file>
將文件內容存上次提交復制到暫存區。
撤銷全部修改
git checkout HEAD -- <file>
可以直接將內容從上次的提交復制到工作區。
命令總結
分支操作
git branch
使用 git branch
可以對倉庫分支進行增刪查改的操作,下面列舉了一下常用的操作方式:
-
git branch <branchname>
,創建指定分支 -
git branch -d <branchname>
,刪除指定分支 -
git branch -v
,顯示所有分支信息
一份分支的引用只是一個文本文件,里面只有一個 SHA 編碼。它保存于
.get/refs/heads/master
中。—— 鄭海波 網易工程師
git branch next
git commit -m 'message'
切換至目標分支
git checkout
它可以本枝上根據通過移動 HEAD(指向當前的提交) 檢測出版本,
也可用于切換分支。(其會把當前的工作目錄和暫存區移動到提出分支的版本)
常用命令有:
-
git checkout <branchname>
,使指針指向目標分支 -
git checkout -b <branchname>
,創建目標分支并切換分支 -
git checkout <reference>
,可以指向任何一個版本
git checkout next
NOTE:所有提交是更具 HEAD 向前進的,所以前后分支后則會跟著 Next 分支進行開發。
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 在分離狀態則因盡量避免在此狀態下進行提交,只做內容的查看。
完全回退
使用git reset
可以將當前分支回退到歷史中的某個版本,
下面為常用的三種方式(三種的區別是恢復的內容時候同時會恢復的工作區或暫存區):
-
git reset --mixed <commit>
默認方式,內容存入暫存區 -
git reset --soft <commit>
內容存入暫存區和工作區 -
git reset --hard <commit>
暫存區和工作區保留現有狀態
git reset --mixed e390b3
如果上一個命令如果使用 hard
git reset --hard e390b3
如果上一個命令如果使用 hard
get reset --soft e390b3
此方法暫存區和工作目錄不會發生任何變化僅僅只是 HEAD 指針發生了變化,
但原有的提交已經無指針指向成為無索引的提交其就有可能被回收。
查詢所有提交記錄
git reflog
會根據倉庫的提交順序按順序來排列,其中包括無索引的提交,
可以在這里使用 HASH 值來進行,但是無索引的提交可能會丟失。
使用捷徑
A^
表示 A
上的父提交,多個 ^
可表示以上的多個級別。
A~n
則表示在 A
之前的第 n 次提交。
reset 與 checkout 區別
兩種方法都有兩個作用范圍,一個是分支操作(commit 操作),
另一個是文件操作(file 操作)。
命令 | 范例 | 移動 HEAD/Branch | 注釋 |
---|---|---|---|
git reset [commit] |
git reset HEAD^ --soft |
是/是 | 完全回退到某個提交(之前所在的位置將失去索) |
git reset [file] |
git reset README.md |
否/否 | 恢復暫存區到某個提交狀態(不移動指針) |
git checkout [commit] |
git checkout master |
是/否 | 移動當前指針 HEAD 到某個提交(并復制內容到工作目錄) |
git checkout [file] |
git checkout -- README.md git checkout HEAD -- xx.log
|
否/否 | 恢復工作目錄到某個狀態 |
stash 的作用
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...
突然需要切換到其他分支,工作區和暫存區還有在當前分支沒完成的任務。那么 stash
就使用 .git
中的特殊區(Stash 區)來幫你解決這個問題(因為強切回丟失當前的工作區和暫存區的內容)。
stash
可以把當前工作區和暫存區的狀態以棧(Stack)的形式保存起來(每次保存都會推一個內容到 stash
棧中),并返回一個干凈的工作空間(工作區和暫存區)。
NOTE:stash pop = stash apply + stash drop
類似于 JavaScript 中的 pop
操作。
NOTE+:什么是棧?可以把棧想象成一摞盤子堆(一個疊一個)。具體關于堆棧的信息可以在這里找到。如果還看不懂,建議完成哈佛大學在線計算機入門課程 CS50
,可以在這里找到。
merge
使用 git merge
可用于合并分支。下面的例子是將 next
分支合并到 master
分支中去。
解決 merge 沖突
當一個文件被同時修改時(更多情況為同時修改相同的一行代碼時)則極有可能產生合并沖突。
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 所在的內容。
merge fast-forward
也并不是所有的合并操作都會造成合并從圖(merge confilic)。最簡單的一種合并是 fast-forward
僅僅只是變化 HEAD 指向的位置(不產生新的合并節點)。
如果需要生成新的合并節點可以使用 git merge next --no-ff
意思是合并但不使用 fast-forward
。
merge 不足
當參與的人閱讀分支越多其分支結構就越復雜和難以被理解。如何實現在任何狀態下的線性提交?
如需完成線性提交可以使用 git rebase
,其可以修剪提交歷史的基線。它會將不同分支的提交在所選節點上進行重演(重演并重新創造新節點)這里 HEAD/Branch 均會發生移動。
git rebase master
但有時并不需要將其他分支上的全部提交節點統統進行重演。則可以使用 git rebase --onto
來選擇需要重演的提交節點。
git rebase --onto master 5751363
NOTE:上面的紅色的節點,未被重建(被丟棄)。
rebase 與 merge 區別
rebase
會產生線性的提交歷史,merge
則會產生多個不同分支的合并節點。所以具體沒有好壞之分,可根據使用的需求來決定。
注意!不要在共有分支上使用 rebase
(例如 master
分支)這會導致其他開發者在進行拉取(Pull)時,必須進行合并且合并中包含重復的提交。
tag
不論是 Branch 還是 HEAD 它們均為動態指針,如果想定義一個靜止的標示則可以使用 git tag
,它將給發布的提交版本設置一個別名。在設置了標簽后就可以直接使用標簽名來代替它所指代的版本提交了。
git tag v0.1 e39d9b2
git checkout v0.1
遠程操作
遠程操作可以將本地倉庫推送至遠程倉庫服務器。Git 支持許多主流的通信協議,其中包括 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
。