版本控制

2018-07-10 15:47 更新

版本控制

版本管理涉及團(tuán)隊(duì)協(xié)作,產(chǎn)品質(zhì)量,和產(chǎn)品上線。使用版本控制工具可使我們自由的做的一些幾點(diǎn):

  • 回退到任意版本
  • 查看歷史版本
  • 對(duì)比兩個(gè)版本差異

版本控制系統(tǒng)

版本控制系統(tǒng)(Version Control System)是一種記錄若干文件修訂記錄的系統(tǒng),它可以幫助開(kāi)發(fā)者查閱或回檔至某個(gè)歷史版本。

  • 手動(dòng)版本控制
  • LVCS 本地
  • CVCS 集中式(例如 SVN)
  • DVCS 分布式(例如 Git)

手動(dòng)版本控制

無(wú)法有效找到需要版本和差異,污染工作目錄結(jié)構(gòu)。

Local VCS 本地式

于是出現(xiàn)了本地版本控制系統(tǒng) RCS(Reversion Control System),其利用本地版本數(shù)據(jù)庫(kù)存儲(chǔ)不斷出現(xiàn)的文件版本。 它可以迅速找出需求的版本和維持工作目錄結(jié)構(gòu)。其缺點(diǎn)是不支持協(xié)同開(kāi)發(fā),這也讓開(kāi)發(fā)者不將其選做通用的版本控制工具來(lái)使用。

CVCS 集中式

利用中央服務(wù)器來(lái)管理文件版本,但每一次操作都需要網(wǎng)絡(luò)請(qǐng)求,且具有致命的單點(diǎn)故障。 (既中央服務(wù)器故障可導(dǎo)致,無(wú)法協(xié)同工作或數(shù)據(jù)丟失)

DVCS 分布式

分布式指的是每一份本地倉(cāng)庫(kù)都是一個(gè)完整的項(xiàng)目歷史拷貝,即使一份倉(cāng)庫(kù)丟失或者損壞也可以從其他的倉(cāng)庫(kù)中獲取此項(xiàng)目的完整歷史記錄。 也因?yàn)樵谔砑有掳姹静恍枰W(wǎng)絡(luò),這可以使操作流程。

分支模型

如果多人并行在一條線上開(kāi)發(fā)會(huì)導(dǎo)致開(kāi)發(fā)困難并且難以定位錯(cuò)誤位置。

分支,就是從目標(biāo)倉(cāng)庫(kù)獲得一份項(xiàng)目拷貝,每條分支都具有和原倉(cāng)庫(kù)功能一樣的開(kāi)發(fā)線。

分支模型(Branching Model)或稱之為工作流(Workflow),它是一個(gè)圍繞項(xiàng)目 開(kāi)發(fā)、部署、測(cè)試等工作流的分支操作(創(chuàng)建及合并等)的規(guī)范集合。

產(chǎn)品級(jí)開(kāi)發(fā)分支模型

常駐分支

  • development,從 master 創(chuàng)建
  • production(master),默認(rèn)分支可用于發(fā)布的代碼

活動(dòng)分支

  • feature,從 development,創(chuàng)建其為特性分支
  • hotfix,從 master 創(chuàng)建,用于修復(fù) Bug
  • release,從 development 創(chuàng)建

環(huán)境

  • 開(kāi)發(fā)環(huán)境,使用測(cè)試開(kāi)發(fā)配置(數(shù)據(jù)庫(kù),緩存,元數(shù)據(jù)配置)
    • 使用提交到下一個(gè) release 的特性分支
  • 測(cè)試環(huán)境,使用測(cè)試配置(測(cè)試數(shù)據(jù)庫(kù))
    • 使用 release/development
  • 預(yù)發(fā)布環(huán)境,小范圍發(fā)布使用線上數(shù)據(jù)庫(kù)模擬真實(shí)環(huán)境
    • 使用 release
  • 生產(chǎn)環(huán)境,線上配置
    • 使用 master

Git

Git 是一個(gè)免費(fèi)開(kāi)源的分布式版本控制系統(tǒng),它也一個(gè)基于內(nèi)容尋址的存儲(chǔ)系統(tǒng)。 Git 是由 Linux 的創(chuàng)造者 Linus Torvalds。

優(yōu)勢(shì)

  • 速度快,不依賴網(wǎng)絡(luò)
  • 完全分布式
  • 輕量級(jí)分支操作
  • Git 為行業(yè)標(biāo)準(zhǔn)版本控制供給
  • 活躍和成熟的社區(qū)

安裝

Mac OS X 下使用 brew install git 下載更新既可。

Linux Ubuntu 下可使用 apt-get install git 既可。

Git 介紹

Git 命令

常用 Git 命令,當(dāng)在命令行中鍵入 git 可以便可以在幫助信息中看到。

獲取幫助

git help <command> git <command> -h git <command> --help 還有 man git-<command> 均可查詢某個(gè)命令的幫助文檔。

基本操作

配置 Git 使用 git config 此為創(chuàng)建 Git 倉(cāng)庫(kù)前必須完成的配置。

git config --global user.name "Li Xinyang"
git config --global user.email "lixinyang1026@gmail.com"
  • --local 默認(rèn) 具有最高優(yōu)先級(jí) 只影響本倉(cāng)庫(kù) .git/config
  • --global 中級(jí)優(yōu)先級(jí) 影響到所有當(dāng)前用戶的倉(cāng)庫(kù) ~/.gitconfig
  • --system 最低優(yōu)先級(jí) 影響到全系統(tǒng)的倉(cāng)庫(kù) /etc/gitconfig

初始化倉(cāng)庫(kù)

初始化倉(cāng)庫(kù),使用 git status 可以查詢當(dāng)前倉(cāng)庫(kù)的狀態(tài)。 如在未初始化倉(cāng)庫(kù)時(shí)查詢狀態(tài)會(huì)報(bào)出錯(cuò)誤信息。

git init [path]
git init [path] --bare

在初始化倉(cāng)庫(kù)后會(huì)出現(xiàn)一個(gè)隱藏的目錄 .git 其中包括了所有的當(dāng)前倉(cāng)庫(kù)的版本信息和本地設(shè)置文件(.git/config)。

查詢狀態(tài)

git status 此命令可以幫助開(kāi)發(fā)者在下面三對(duì)關(guān)系中找出文件狀態(tài)的變化。

  • 未跟蹤 <--> 跟蹤
  • 工作目錄 <--> 暫存區(qū)
  • 暫存區(qū) <--> 最新提交

Git 中存在兩種狀態(tài)內(nèi)容狀態(tài)文件狀態(tài)。 倉(cāng)庫(kù)中的文件均可以在狀態(tài)和區(qū)域之間進(jìn)行轉(zhuǎn)換。

添加文件到暫存區(qū)(同時(shí)跟蹤文件)

git add [file]

上圖所示,我們將 README.md 文件從工作區(qū)提交至?xí)捍鎱^(qū), 并將文件狀態(tài)從未跟蹤改變成已跟蹤。

NOTE:批量增加當(dāng)前目錄下全部文件 git add .

忽略文件

.gitignore 可以在添加至倉(cāng)庫(kù)時(shí)忽略匹配的文件,但僅作用于未跟蹤的文件。

NOTE:GitHub 為各個(gè)類型項(xiàng)目和操作系統(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

  • Show information about files in the index and the working tree

工作區(qū)與暫存區(qū)

不同的區(qū)域中可以存在文件的獨(dú)立版本,如下圖所示工作區(qū)和暫存區(qū)的文件為兩個(gè)不同的版本。 (之前上個(gè)例子中所創(chuàng)建的 DummyFile 文件已被刪除)

暫存區(qū)

我們可以把暫存區(qū)比作一個(gè)每類物品只能放置一次的購(gòu)物車(chē)此外還具有下面的特質(zhì):

  • 貨架和購(gòu)物車(chē)可同時(shí)出現(xiàn)同種物品
  • 貨架上的物品可以替換掉購(gòu)物車(chē)的物品
  • 可以刪除物品
  • 提交購(gòu)物車(chē)完成購(gòu)買(mǎi)并生成購(gòu)買(mǎi)記錄

其中

  • 物品:文件
  • 貨架:工作目錄
  • 購(gòu)物車(chē):暫存區(qū)
  • 購(gòu)買(mǎi):提交內(nèi)容

提交版本記錄

git commit 可以根據(jù)暫存區(qū)的內(nèi)容創(chuàng)建一個(gè)提交目錄。

NOTE:直接提交工作區(qū)的內(nèi)容git commit -a -m 'message',工作中不建議這樣操作。

查詢提交歷史記錄

git log 可以用來(lái)顯示提交是記錄信息。

其中包括:

  • 提交編號(hào) SHA-1 編碼的 HASH 標(biāo)示符
  • git-config 配置的提交者信息
  • 提交日期
  • 提交描述信息

工作中可使用下面簡(jiǎn)單的配置進(jìn)行版本查詢

git log --oneline

# 較長(zhǎng)的命令可以使用 alias 的方法簡(jiǎn)化
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 命令設(shè)置

配置 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)預(yù)先設(shè)置好了非常多常用別名。

顯示版本差異

git diff 用于顯示版本差異,下面是幾個(gè)常用的方法:

  • git diff 顯示工作目錄與暫存區(qū)的差異
  • git diff -cached [<reference>] 暫存區(qū)與某次提交的差異(默認(rèn)為 HEAD)
  • git diff <reference> 工作目錄和某次提交間的差異
  • git diff <reference> <reference> 查詢兩次提交直接的差別

撤銷工作區(qū)的修改

git checkout -- <file>... 可用于撤銷工作區(qū)的修改 (此方法會(huì)丟棄工作區(qū)修改且不可恢復(fù)),下面是一些常用方法:

  • git checkout -- <file> 將文件從暫存區(qū)復(fù)制到工作目錄

NOTE:使用 -- 是為了避免路徑和引用(或提交 ID)同名發(fā)生的沖突。

撤銷暫存區(qū)內(nèi)容

使用 git reset HEAD <file>... 可用于撤銷暫存區(qū)的修改,下面是一些常用操作:

  • git reset HEAD <file> 將文件內(nèi)容存上次提交復(fù)制到暫存區(qū)。

撤銷全部修改

git checkout HEAD -- <file> 可以直接將內(nèi)容從上次的提交復(fù)制到工作區(qū)。

命令總結(jié)

分支操作

git branch

使用 git branch 可以對(duì)倉(cāng)庫(kù)分支進(jìn)行增刪查改的操作,下面列舉了一下常用的操作方式:

  • git branch <branchname>,創(chuàng)建指定分支
  • git branch -d <branchname>,刪除指定分支
  • git branch -v,顯示所有分支信息

一份分支的引用只是一個(gè)文本文件,里面只有一個(gè) SHA 編碼。它保存于 .get/refs/heads/master中。

—— 鄭海波 網(wǎng)易工程師

git branch next

git commit -m 'message'

切換至目標(biāo)分支

git checkout 它可以本枝上根據(jù)通過(guò)移動(dòng) HEAD(指向當(dāng)前的提交) 檢測(cè)出版本, 也可用于切換分支。(其會(huì)把當(dāng)前的工作目錄和暫存區(qū)移動(dòng)到提出分支的版本) 常用命令有:

  • git checkout <branchname>,使指針指向目標(biāo)分支
  • git checkout -b <branchname>,創(chuàng)建目標(biāo)分支并切換分支
  • git checkout <reference>,可以指向任何一個(gè)版本
git checkout next

NOTE:所有提交是更具 HEAD 向前進(jìn)的,所以前后分支后則會(huì)跟著 Next 分支進(jìn)行開(kāi)發(fā)。

git commit -m 'message'

# -- 為短名與 cd 類似
git checkout --

# 或者使用
# git checkout master

git checkout -b Issue-26

NOTE:使用 git branch -v 可以列出全部分支,帶 * 表示當(dāng)前所屬分支(HEAD 指向分支)。

git checkout c4006ec

當(dāng) HEAD 指針與具體的分支分離時(shí),我們將其稱之為 detached head。 如果 HEAD 在分離狀態(tài)則因盡量避免在此狀態(tài)下進(jìn)行提交,只做內(nèi)容的查看。

完全回退

使用git reset可以將當(dāng)前分支回退到歷史中的某個(gè)版本, 下面為常用的三種方式(三種的區(qū)別是恢復(fù)的內(nèi)容時(shí)候同時(shí)會(huì)恢復(fù)的工作區(qū)或暫存區(qū)):

  • git reset --mixed <commit> 默認(rèn)方式,內(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

如果上一個(gè)命令如果使用 hard

git reset --hard e390b3

如果上一個(gè)命令如果使用 hard

get reset --soft e390b3

此方法暫存區(qū)和工作目錄不會(huì)發(fā)生任何變化僅僅只是 HEAD 指針發(fā)生了變化, 但原有的提交已經(jīng)無(wú)指針指向成為無(wú)索引的提交其就有可能被回收。

查詢所有提交記錄

git reflog 會(huì)根據(jù)倉(cāng)庫(kù)的提交順序按順序來(lái)排列,其中包括無(wú)索引的提交, 可以在這里使用 HASH 值來(lái)進(jìn)行,但是無(wú)索引的提交可能會(huì)丟失。

使用捷徑

A^ 表示 A上的父提交,多個(gè) ^ 可表示以上的多個(gè)級(jí)別。 A~n 則表示在 A 之前的第 n 次提交。

reset 與 checkout 區(qū)別

兩種方法都有兩個(gè)作用范圍,一個(gè)是分支操作(commit 操作), 另一個(gè)是文件操作(file 操作)。

命令范例移動(dòng) HEAD/Branch注釋
git reset [commit]git reset HEAD^ --soft是/是完全回退到某個(gè)提交(之前所在的位置將失去索)
git reset [file]git reset README.md否/否恢復(fù)暫存區(qū)到某個(gè)提交狀態(tài)(不移動(dòng)指針)
git checkout [commit]git checkout master是/否移動(dòng)當(dāng)前指針 HEAD 到某個(gè)提交(并復(fù)制內(nèi)容到工作目錄)
git checkout [file]git checkout -- README.md git checkout HEAD -- xx.log否/否恢復(fù)工作目錄到某個(gè)狀態(tài)
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...

突然需要切換到其他分支,工作區(qū)和暫存區(qū)還有在當(dāng)前分支沒(méi)完成的任務(wù)。那么 stash 就使用 .git 中的特殊區(qū)(Stash 區(qū))來(lái)幫你解決這個(gè)問(wèn)題(因?yàn)閺?qiáng)切回丟失當(dāng)前的工作區(qū)和暫存區(qū)的內(nèi)容)。

stash 可以把當(dāng)前工作區(qū)和暫存區(qū)的狀態(tài)以棧(Stack)的形式保存起來(lái)(每次保存都會(huì)推一個(gè)內(nèi)容到 stash 棧中),并返回一個(gè)干凈的工作空間(工作區(qū)和暫存區(qū))。

NOTE:stash pop = stash apply + stash drop 類似于 JavaScript 中的 pop 操作。

NOTE+:什么是棧?可以把棧想象成一摞盤(pán)子堆(一個(gè)疊一個(gè))。具體關(guān)于堆棧的信息可以在這里找到。如果還看不懂,建議完成哈佛大學(xué)在線計(jì)算機(jī)入門(mén)課程 CS50,可以在這里找到。

merge

使用 git merge 可用于合并分支。下面的例子是將 next 分支合并到 master 分支中去。

解決 merge 沖突

當(dāng)一個(gè)文件被同時(shí)修改時(shí)(更多情況為同時(shí)修改相同的一行代碼時(shí))則極有可能產(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' 來(lái)完成合并沖突解決并提交一個(gè)新的版本。

NOTE:git cat-file -p HEAD 可用于顯示 git 中某個(gè)對(duì)象的具體信息。 NOTE+:<<<<<<<< HEAD 與 ========= 之間為 HEAD 所在的內(nèi)容。

merge fast-forward

也并不是所有的合并操作都會(huì)造成合并從圖(merge confilic)。最簡(jiǎn)單的一種合并是 fast-forward 僅僅只是變化 HEAD 指向的位置(不產(chǎn)生新的合并節(jié)點(diǎn))。

如果需要生成新的合并節(jié)點(diǎn)可以使用 git merge next --no-ff 意思是合并但不使用 fast-forward。

merge 不足

當(dāng)參與的人閱讀分支越多其分支結(jié)構(gòu)就越復(fù)雜和難以被理解。如何實(shí)現(xiàn)在任何狀態(tài)下的線性提交?

如需完成線性提交可以使用 git rebase,其可以修剪提交歷史的基線。它會(huì)將不同分支的提交在所選節(jié)點(diǎn)上進(jìn)行重演(重演并重新創(chuàng)造新節(jié)點(diǎn))這里 HEAD/Branch 均會(huì)發(fā)生移動(dòng)。

git rebase master

但有時(shí)并不需要將其他分支上的全部提交節(jié)點(diǎn)統(tǒng)統(tǒng)進(jìn)行重演。則可以使用 git rebase --onto 來(lái)選擇需要重演的提交節(jié)點(diǎn)。

git rebase --onto master 5751363

NOTE:上面的紅色的節(jié)點(diǎn),未被重建(被丟棄)。

rebase 與 merge 區(qū)別

rebase 會(huì)產(chǎn)生線性的提交歷史,merge 則會(huì)產(chǎn)生多個(gè)不同分支的合并節(jié)點(diǎn)。所以具體沒(méi)有好壞之分,可根據(jù)使用的需求來(lái)決定。

注意!不要在共有分支上使用 rebase(例如 master 分支)這會(huì)導(dǎo)致其他開(kāi)發(fā)者在進(jìn)行拉?。≒ull)時(shí),必須進(jìn)行合并且合并中包含重復(fù)的提交。

tag

不論是 Branch 還是 HEAD 它們均為動(dòng)態(tài)指針,如果想定義一個(gè)靜止的標(biāo)示則可以使用 git tag ,它將給發(fā)布的提交版本設(shè)置一個(gè)別名。在設(shè)置了標(biāo)簽后就可以直接使用標(biāo)簽名來(lái)代替它所指代的版本提交了。

git tag v0.1 e39d9b2

git checkout v0.1

遠(yuǎn)程操作

遠(yuǎn)程操作可以將本地倉(cāng)庫(kù)推送至遠(yuǎn)程倉(cāng)庫(kù)服務(wù)器。Git 支持許多主流的通信協(xié)議,其中包括 LocalHTTP、SSH、還有Git。服務(wù)器只應(yīng)該是作為同步之用(被動(dòng)接受既可)。

初始化一個(gè)本地的遠(yuǎn)程服務(wù)器

git init ~/git-server --bare

推送

git push 可以將當(dāng)期的全部版本提交提交推送至遠(yuǎn)程倉(cāng)庫(kù),其完成了提交歷史的完全不復(fù)制并同時(shí)移動(dòng)復(fù)制版本的 HEAD 與 Branch。

添加遠(yuǎn)程倉(cāng)庫(kù)別名

git remote 可用于添加遠(yuǎn)程倉(cāng)庫(kù)的別名。

# 更改倉(cāng)庫(kù) url 地址
git remote set-url origin 'https://github.com/li-xinyang/FEND_Note.git'

遠(yuǎn)程 push 沖突

可以先使用 git fetch + git merge 來(lái)解決沖突的問(wèn)題。git pull 就等同于 fetch 與 merge 的合并。

克隆遠(yuǎn)程倉(cāng)庫(kù)

使用 git clone 可以克隆遠(yuǎn)程倉(cāng)庫(kù),并將克隆地址默認(rèn)設(shè)為 origin。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)