@(版本控制)[git]
大四開(kāi)始使用git協(xié)同開(kāi)發(fā)井辜,到現(xiàn)在工作一年,從一開(kāi)始使用模模糊糊,每次遇到?jīng)_突都膽戰(zhàn)心驚咧最,到如今慢慢感受到這個(gè)分布式控制工具給開(kāi)發(fā)帶來(lái)的便利。越發(fā)覺(jué)得御雕,掌握工具矢沿、使用工具提高效率的重要性!
個(gè)人開(kāi)發(fā)環(huán)境 ubuntu 14.04
說(shuō)明:
- $ 表示終端執(zhí)行命令
-
命令注釋
- [] 表示可選
Git 是一個(gè)分布式版本控制工具
基本上,使用git工作的流程如下:
- 修改文件饮笛,在
工作目錄
中修改文件咨察。 - 暫存文件,將文件的快照放入
暫存區(qū)域
福青。 - 提交更新摄狱,找到暫存區(qū)域的文件脓诡,將快照永久性存儲(chǔ)到
Git 倉(cāng)庫(kù)目錄
。 - 推送到遠(yuǎn)程服務(wù)器
對(duì)應(yīng)以上流程媒役,git 的三個(gè)狀態(tài)關(guān)系如下
安裝配置 Git
安裝 git
命令行直接輸入git提示應(yīng)用沒(méi)有安裝的情況下
安裝git祝谚,[圖形化 gitk, 差異比較工具 meld]
$ sudo apt-get install git gitk meld
配置 git
- 在~目錄下添加一個(gè).gitignore酣衷,將需要忽略的對(duì)象添加進(jìn)來(lái)交惯,對(duì)當(dāng)前用戶所有倉(cāng)庫(kù)生效。
- 配置config穿仪,使用 --global 說(shuō)明對(duì)當(dāng)前用戶所有倉(cāng)庫(kù)生效(對(duì)特定倉(cāng)庫(kù)或者文件的配置席爽,可以使用
--local/ --file
)。
$ touch ~/.gitignore
$ echo "cscope*" > ~/.gitignore
$ echo "tag*" >> ~/.gitignore
$ echo "*.swp" >> ~/.gitignore
$ git config --global core.excludesfile ~/.gitignore
# 自報(bào)家門(mén)啊片,記得替換自己的名稱和郵箱...
$ git config --global user.name luchaodong
$ git config --global user.email lcdsdream@126.com
# 顯示配置
$ git config --list
說(shuō)明:
- /etc/gitconfig 文件:系統(tǒng)中對(duì)所有用戶都普遍適用的配置只锻。若使用 git config 時(shí)用
--system
選項(xiàng),讀寫(xiě)的就是這個(gè)文件紫谷。 - ~/.gitconfig 文件:用戶目錄下的配置文件只適用于該用戶齐饮。若使用 git config 時(shí)用
--global
選項(xiàng),讀寫(xiě)的就是這個(gè)文件笤昨。 - 當(dāng)前項(xiàng)目的 Git 目錄中的配置文件(也就是工作目錄中的 .git/config 文件):這里的配置僅僅針對(duì)當(dāng)前項(xiàng)目有效祖驱。若使用 git config 時(shí)用
--local
選項(xiàng),讀寫(xiě)的就是這個(gè)文件瞒窒。
每一個(gè)級(jí)別的配置都會(huì)覆蓋上層的相同配置捺僻,所以 .git/config 里的配置會(huì)覆蓋/etc/gitconfig 中的同名變量。
開(kāi)始使用 Git
版本庫(kù)創(chuàng)建
現(xiàn)有目錄使用git開(kāi)始版本管理
創(chuàng)建 .git 后將需要跟蹤的文件添加并提交根竿。
通過(guò).gitignore設(shè)定不想跟蹤的文件陵像,再執(zhí)行 git add --all, 把其他所有文件加入追蹤目錄寇壳。
$ git init
$ git add --all
$ git commit -m "initial project version"
# 取消追蹤某個(gè)文件
$ git rm --cached <file>
克隆現(xiàn)有倉(cāng)庫(kù)
獲取倉(cāng)庫(kù)鏈接,保存在本地 local_pySerial 目錄下
git clone git@github.com:lcdsdream/pySerial.git [local_pySerial]
.gitignore 例子
# no .a files
*.a
# but do track lib.a, even though you're ignoring .a files above
!lib.a
# only ignore the TODO file in the current directory, not subdir/TODO
/TODO
# ignore all files in the build/ directory
build/
# ignore doc/notes.txt, but not doc/server/arch.txt
doc/*.txt
# ignore all .pdf files in the doc/ directory
doc/**/*.pdf
修改追蹤
修改提交
# 開(kāi)始寫(xiě)啊寫(xiě)
$ git add file_a # 將修改過(guò)的內(nèi)容加入暫存區(qū)
$ git commit # 將暫存區(qū)的修改提交到本地倉(cāng)庫(kù)
$ git commit -a # 將暫存區(qū)和工作區(qū)的修改提交到本地目錄
# -m "提交說(shuō)明"
修改記錄查看
$ git status # 查看文件狀態(tài)
$ git log # 查看提交記錄
$ git diff # 查看工作區(qū)修改內(nèi)容
$ git diff --cached # 查看暫存區(qū)修改內(nèi)容
$ git diff HEAD # 查看當(dāng)前工作區(qū)和暫存區(qū)修改的內(nèi)容
$ git blame file # 查看每一行代碼誰(shuí)提交的
撤銷操作
撤銷修改
# 取消修改暫存
$ git add file_a # 將file_a 修改保存到暫存區(qū)后想取消妻怎,
# 執(zhí)行
$ git reset HEAD file_a # 將file_a的修改從暫存區(qū)推出
# 取消修改
# 想直接取消工作區(qū)文件的修改
# 執(zhí)行 壳炎, 慎重!
$ git checkout file_a # 撤銷工作目錄下的修改
# 重新提交
$ git commit
# 發(fā)現(xiàn)漏了一文件
$ git add forgotten_file
$ git commit --amend
撤銷錯(cuò)誤提交
# 回退到某次提交逼侦, 修改的內(nèi)容仍然保留在工作區(qū)(--mixed)
# 如此操作修改了git 的commit 和index匿辩,如果推到服務(wù)器的,有沖突!
$ git reset [--mixed] commit_SHA
# --soft
# 保留源碼,只回退到commit 信息到某個(gè)版本.不涉及index的回退,如果還需要提交,直接commit即可.
# --hard
# 回退榛丢,不保留修改2颉!N蕖<诓 选侨!
# 在原來(lái)錯(cuò)誤的提交基礎(chǔ)上,執(zhí)行反轉(zhuǎn)
# commit 一直保持向前然走,不會(huì)帶來(lái)沖突
# revert 時(shí)要求工作樹(shù)干凈
$ git revert commit_SHA
打標(biāo)簽
以示重要援制,打個(gè)標(biāo)簽
查看
$ git tag # 列出標(biāo)簽
$ git tag -l 'v1.8.5*' # 列出感興趣部分標(biāo)簽
打標(biāo)簽
Git 使用兩種主要類型的標(biāo)簽:輕量標(biāo)簽(lightweight)與附注標(biāo)簽(annotated)。
* 輕量標(biāo)簽很像一個(gè)不會(huì)改變的分支芍瑞,它只是一個(gè)特定提交的引用晨仑。
```bash
$ git tag v1.4-lw
# 提供標(biāo)簽名稱,無(wú)其他參數(shù)
```
* 附注標(biāo)簽是存儲(chǔ)在 Git 數(shù)據(jù)庫(kù)中的一個(gè)完整對(duì)象拆檬。
它們是可以被校驗(yàn)的洪己;其中包含打標(biāo)簽者的名字、電子郵件地址竟贯、日期時(shí)間答捕;還有一個(gè)標(biāo)簽信息;并且可以使用 GNU Privacy Guard (GPG)簽名與驗(yàn)證澄耍。
```bash
# -m "標(biāo)簽信息"噪珊, 否則會(huì)運(yùn)行編輯器要求輸入
$ git tag -a v1.4 -m "my version 1.4"
# 查看標(biāo)簽信息
$ git show v1.4
```
通常建議創(chuàng)建附注標(biāo)簽,這樣你可以擁有以上所有信息齐莲;但是如果你只是想用一個(gè)臨時(shí)的標(biāo)簽痢站,或者因?yàn)槟承┰虿幌胍4婺切┬畔ⅲp量標(biāo)簽也是可用的选酗。
$ git tag -a v1.2 commit_SHA # 給某個(gè)提交打標(biāo)簽
$ git push origin v1.4 # 把標(biāo)簽推到共享服務(wù)器
$ git push origin --tags # 推送多個(gè)標(biāo)簽
Git 分支
Git 通過(guò)一個(gè)名為 HEAD 的特殊指針指向當(dāng)前所在的本地分支(將 HEAD 想象為當(dāng)前分支的別名)阵难。
創(chuàng)建切換合并分支
$ git branch testing # 在當(dāng)前提交對(duì)象創(chuàng)建一個(gè)分支
$ git branch testing xx # 從XX(標(biāo)簽,SHA等創(chuàng)建分支)
$ git checkout testing # 切換到testing分支
$ git checkout -b testing # 創(chuàng)建分支并切換過(guò)去
# 查看分支
$ git branch # -a 查看所有分支芒填,包括遠(yuǎn)程
# -v 查看分支的最后提交
# 在新分支上進(jìn)行開(kāi)發(fā)并有一些新提交呜叫,測(cè)試后合并回主分支
$ git checkout master
$ git merge testing
# 刪除臨時(shí)分支
$ git branch -d testing # -D 強(qiáng)制刪除,如果沒(méi)有merge的情況下
分支切換殿衰,是HEAD指針指向?qū)ο蟮母淖儯?HEAD 指向當(dāng)前分支
多人協(xié)同開(kāi)發(fā)的情況朱庆,對(duì)同一個(gè)文件同一個(gè)部分進(jìn)行了修改
,導(dǎo)致git沒(méi)法干凈地進(jìn)行合并闷祥,這時(shí)候會(huì)產(chǎn)生沖突娱颊。
如下,合并的時(shí)候 tt.c 出現(xiàn)沖突
$ git merge testing
Auto-merging tt.c
CONFLICT (content): Merge conflict in tt.c
Automatic merge failed; fix conflicts and then commit the result.
$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: tt.c
no changes added to commit (use "git add" and/or "git commit -a")
使用編輯工具打開(kāi)沖突文件進(jìn)行手動(dòng)合并凯砍。每一個(gè)沖突的部分箱硕,git都會(huì)標(biāo)記出來(lái)
<<<<<<< HEAD:tt.c
printf("1");
printf("21");
=======
printf("1");
printf("11");
>>>>>>> testing:tt.c
如上,<<<<<<< HEAD:index.html
和
=======
的之間的內(nèi)容是當(dāng)前分支(這里是master)的內(nèi)容悟衩,而后面到
>>>>>>> testing:index.html
之間是testing分支的內(nèi)容剧罩。
解決完沖突后暫存(告知git沖突解決),最后執(zhí)行g(shù)it commit座泳,完成合并惠昔。
如何解決沖突幕与?
根據(jù)實(shí)際需要,保留HEAD或者新合并內(nèi)容舰罚,亦或者纽门,兩者的結(jié)合。這個(gè)需要根據(jù)實(shí)際情況判斷营罢。
git 分支合并
壓合合并
在A上進(jìn)行添加功能測(cè)試赏陵,有多次提交,最后完成合并到分支B饲漾,只需作為一個(gè)記錄提交
$ git checkout B
$ git meger --squash A
# 此時(shí)A中的所有修改會(huì)加入到B中蝙搔,但是沒(méi)有提交,保留在暫留區(qū)
$ git commit -m "xx"
# 統(tǒng)一提交
揀選合并
揀選 另外分支的某一個(gè)提交
$ git checkout B
$ git cherry-pick SHA
揀選多個(gè)提交
git cherry-pick -n SHA1
# 會(huì)把合并保存到暫存區(qū)
git cherry-pick -n SHA..
# 繼續(xù)合并后提交
git commit
# 不添加留言默認(rèn)使用最后一條考传,或者接下去編輯
Git 變基
昨天我從master檢出分支開(kāi)發(fā)新功能A, 今天master上有新功能加入吃型,我想A功能基于今天的master上開(kāi)發(fā),所以執(zhí)行變基僚楞。
變基就像重新播放一樣勤晚,把master今天的修改移到我昨天檢出的分支上。
一般泉褐,變基使提交歷史更加簡(jiǎn)潔赐写,減少分叉。
experiment 分支在C2的基礎(chǔ)上修改了C4膜赃,但是master今天修改了C3挺邀,希望experiment的C4切到C3上,通過(guò)變基實(shí)現(xiàn)跳座。
$ git checkout experiment
$ gti rebase master
之后將修改提交到master分支端铛,master分支執(zhí)行快進(jìn)合并
$ git checkout master
$ git merger experiment
同樣的提交記錄,對(duì)比直接merge疲眷,提交歷史更加好看禾蚕。
合并后有分叉
Git 與遠(yuǎn)程倉(cāng)庫(kù)
查看遠(yuǎn)程URL
$ git remote -v
origin git@github.com:lcdsdream/pySerial.git (fetch)
origin git@github.com:lcdsdream/pySerial.git (push)
增刪改遠(yuǎn)程庫(kù)
# 添加 url
$ git remote add new_fe git@github.com:lcdsdream/pySerial.git
# 重命名
$ git remote rename new_fe old_fe
# 刪除
$ git remote remove fe
獲取遠(yuǎn)程庫(kù)
# 獲取遠(yuǎn)程到本地的origin/xxx, 需要手動(dòng)合并到本地的xxx
$ git fetch
# 獲取遠(yuǎn)程到本地的origin/xxx, 自動(dòng)合并到本地的xxx
$ git pull
# 獲取遠(yuǎn)程某個(gè)分支
$ git checkout -b 本地分支名 遠(yuǎn)程分支名(比如origin/xx)
推送到遠(yuǎn)程倉(cāng)庫(kù)
推送master分支到遠(yuǎn)程倉(cāng)庫(kù)
$ git push origin master
刪除遠(yuǎn)程分支
# 方法1
$ git push origin --delete serverfix
# 方法2 推送空分支替代
$ git push origin :serverfix
ssh 訪問(wèn)遠(yuǎn)程倉(cāng)庫(kù)
通過(guò)ssh訪問(wèn)github等遠(yuǎn)程代碼庫(kù)。
# 查看是否已經(jīng)有秘鑰
$ ls -al ~/.ssh
# 生成ssh key
$ ssh-keygen -t rsa -C "your_email@example.com"
# 回車(chē)默認(rèn)即可狂丝。
# 添加key到ssh-agent
# 啟動(dòng)ssh-agent
$ ssh-agent -s
$ ssh-add ~/.ssh/id_rsa
# 若執(zhí)行ssh-add /path/to/xxx.pem是出現(xiàn)這個(gè)錯(cuò)誤:
# Could not open a connection to your authenticationagent
# 則先執(zhí)行如下命令即可:
$ ssh-agent bash
將公鑰加入到github中
測(cè)試
$ ssh -T git@github.com
# The authenticity of host 'github.com (207.97.227.239)' can't be established.# RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.# Are you sure you want to continue connecting (yes/no)?
$ yes
# Hi username! You've successfully authenticated, but GitHub does not# provide shell access.
測(cè)試通過(guò)夕膀!
儲(chǔ)藏
在一個(gè)分支開(kāi)發(fā),由于某些需求需要切換到另一個(gè)分支美侦,但是對(duì)于當(dāng)前的內(nèi)容還不想提交,需要用到儲(chǔ)藏功能魂奥。
$ git stash # 將工作區(qū)和暫存區(qū)修改儲(chǔ)藏菠剩, 入棧
$ git stash --keep-index # 將工作區(qū)內(nèi)容存儲(chǔ)
$ git stash list # 列出儲(chǔ)藏內(nèi)容
# 應(yīng)用最后一次儲(chǔ)藏的修改,(但是對(duì)應(yīng)還保留在棧中)
# 不指定stash@{}的話耻煤,默認(rèn)取出最后推進(jìn)內(nèi)容
# --index具壮, 恢復(fù)的時(shí)候原來(lái)暫存的修改會(huì)暫存
$ git stash apply stash@{x}
# 應(yīng)用最后一次儲(chǔ)藏的修改(對(duì)應(yīng)清除)准颓,出棧
$ git stash pop stash@{x}
$ git stash drop # 直接刪除儲(chǔ)藏的修改
# 儲(chǔ)藏后由于修改可能無(wú)法直接取出
# 新建分支,合并
$ git stash branch branch_name
Git patch
使用git format-patch生成所需要的patch:
- 當(dāng)前分支所有超前master的提交:
$ git format-patch -M master
- 某次提交以后的所有patch:
不包括該次提交棺妓。
$ git format-patch SHA
- 從根到指定提交的所有patch:
包括該次提交
$ git format-patch --root SHA
- 某兩次提交之間的所有patch:
包括指定的那兩次提交
git format-patch 365a..4e16 --365a和4e16分別對(duì)應(yīng)兩次提交的名稱
- 某次提交(含)之前的幾次提交:
比如-2攘已, 則該次提交和該提交的上一次
git format-patch –n SHA --n指patch數(shù)
- 單次提交即為:
git format-patch -1 SHA
git format-patch生成的補(bǔ)丁文件默認(rèn)從1開(kāi)始順序編號(hào),并使用對(duì)應(yīng)提交信息中的第一行作為文件名怜跑。
如果使用了 --numbered-files選項(xiàng)样勃,則文件名只有編號(hào),不包含提交信息性芬;
如當(dāng)所有patch輸出到一個(gè)文件峡眶;可指定-o path指定patch的存放目錄;
檢查
# 檢查patch文件
$ git apply --stat newpatch.patch
# 檢查能否應(yīng)用成功:
$ git apply --check newpatch.patch
#不行會(huì)報(bào)錯(cuò)
打補(bǔ)丁
$ git am --signoff < newpatch.patch
# (使用-s或--signoff選項(xiàng)植锉,可以commit信息中加入Signed-off-by信息)
應(yīng)用patch出現(xiàn)問(wèn)題:
一個(gè)典型的git am失敗辫樱,可能是這樣的:
$ git am < PATCH
Applying: PACTH DESCRIPTION
error: patch failed: file.c:137
error: file.c: patch does not apply
error: patch failed: Makefile:24
error: libavfilter/Makefile: patch does not apply
Patch failed at 0001 PATCH DESCRIPTION
When you have resolved this problem run "git am --resolved".
If you would prefer to skip this patch, instead run "git am --skip".
To restore the original branch and stop patching run "git am --abort".
處理
$ git apply PATCH --reject
# 打開(kāi)文件處理
# (譯注:根據(jù).rej文件手動(dòng)解決所有沖突)
$ git add file_xxx
$ git am --resolved
end
后續(xù)補(bǔ)充.....
補(bǔ)充-如何撤銷修改操作