Git操做手冊

這篇文章主要介紹Git分佈式版本管理與集中式管理的一些差別,總結下Git經常使用命令做爲往後的速查表,最後介紹Git進階的一些案例。
本文分爲如下幾個部分:php

  1. Git與SVN差別
  2. Git經常使用命令
  3. Git進階指南

Git與SVN差別

Git的第一個版本是Linux之父Linus Torvalds親手操刀設計和實現的,Git 基於 DAG 結構 (Directed Acyclic Graph),其運行起來至關的快,它已是如今的主流。git

Git 和 SVN 思想最大的差異有四個:github

  • 去中心化
  • 直接記錄快照,而非差別
  • 不同的分支概念
  • 三個文件狀態

去中心化算法

Git:是一個DVCS(分佈式版本管理系統),在技術層面上並不存在一個像中心倉庫這樣的東西 , 全部的數據都在本地,不存在誰是中心數據庫

圖中每一個開發者拉取(pull)並推送(push)到origin。但除了這種集中式的推送拉取關係,每一個開發者也可能會從其餘的開發者處拉取代碼的變動,從技術上講,這意味着Alice定義了一個名爲bob的Git的remote,它指向了Bob的軟件倉庫。反之亦然。編程

直接記錄快照,而非差別緩存

Git每個版本都是直接記錄快照,而非文件的差別。 下面兩個對比圖在網上是廣爲流傳你們應該熟悉:安全

SVN:服務器

Git:app

Git使用SHA-1算法計算數據的校驗和,經過文件的內容或目錄計算出SHA-1哈希值,做爲指紋字符串,每一個Version 都是一個快照。

不同的分支概念

Git的分支本質是一個指向提交快照的指針,是從某個提交快照往回看的歷史。當建立/切換分支的時候,只是變換了指針指向而已.而SVN建立一個分支, 是的的確確的複製了一份文件。

三個文件狀態

在Git中文件有三種狀態:

  • 已提交(committed):該文件被安全地保存在了本地數據庫
  • 已修改(modified):修改了某個文件,但尚未保存
  • 已暫存(staged):把已修改的文件放下下次保存的清單中

Git經常使用命令

建立

複製一個已建立的倉庫:

1 $ git clone ssh://user@domain.com/repo.git

建立一個新的本地倉庫:

1 $ git init

本地修改

顯示工做路徑下已修改的文件:

1 $ git status

顯示與上次提交版本文件的不一樣:

1 $ git diff

把當前全部修改添加到下次提交中:

1 $ git add

把對某個文件的修改添加到下次提交中:

1 $ git add -p <file>

提交本地的全部修改:

1 $ git commit -a

提交以前已標記的變化:

1 $ git commit

附加消息(註釋)提交:

1 $ git commit -m 'message here'

提交,並將提交時間設置爲以前的某個日期:

1 git commit --date="`date --date='n day ago'`" -am "Commit Message"

修改上次提交

請勿修改已發佈的提交記錄!

1 $ git commit --amend

把當前分支中未提交的修改移動到其餘分支

1 $ git stash
2 $ git checkout branch2
3 $ git stash pop

搜索

從當前目錄的全部文件中查找文本內容:

1 $ git grep "Hello"

在某一版本中搜索文本:

1 $ git grep "Hello" v2.5

提交歷史

從最新提交開始,顯示全部的提交記錄(顯示hash, 做者信息,提交的標題和時間):

1 $ git log

顯示全部提交(僅顯示提交的hash和message):

1 $ git log --oneline

顯示某個用戶的全部提交:

1 $ git log --author="username"

顯示某個文件的全部修改:

1 $ git log -p <file>

誰,在什麼時間,修改了文件的什麼內容:

1 $ git blame <file>

分支與標籤

列出全部的分支:

1 $ git branch

切換分支:

1 $ git checkout <branch>

建立並切換到新分支:

1 $ git checkout -b <branch>

基於當前分支建立新分支:

1 $ git branch <new-branch>

基於遠程分支建立新的可追溯的分支:

1 $ git branch --track <new-branch> <remote-branch>

刪除本地分支:

1 $ git branch -d <branch>

給當前版本打標籤:

1 $ git tag <tag-name>

更新與發佈

列出當前配置的遠程端:

1 $ git remote -v

顯示遠程端的信息:

1 $ git remote show <remote>

添加新的遠程端:

1 $ git remote add <remote> <url>

下載遠程端版本,但不合併到HEAD中:

1 $ git fetch <remote>

下載遠程端版本,並自動與HEAD版本合併:

1 $ git remote pull <remote> <url>

將遠程端版本合併到本地版本中:

1 $ git pull origin master

將本地版本發佈到遠程端:

1 $ git push remote <remote> <branch>

刪除遠程端分支:

1 $ git push <remote> :<branch> (since Git v1.5.0)
2  3 $ git push <remote> --delete <branch> (since Git v1.7.0)

發佈標籤:

1 $ git push --tags

合併與重置

將分支合併到當前HEAD中:

1 $ git merge <branch>

將當前HEAD版本重置到分支中:
請勿重置已發佈的提交!

1 $ git rebase <branch>

退出重置:

1 $ git rebase --abort

解決衝突後繼續重置:

1 $ git rebase --continue

使用配置好的merge tool 解決衝突:

1 $ git mergetool

在編輯器中手動解決衝突後,標記文件爲已解決衝突

1 $ git add <resolved-file>
2 $ git rm <resolved-file>

撤銷

放棄工做目錄下的全部修改:

1 $ git reset --hard HEAD

移除緩存區的全部文件(i.e. 撤銷上次git add):

1 $ git reset HEAD

放棄某個文件的全部本地修改:

1 $ git checkout HEAD <file>

重置一個提交(經過建立一個大相徑庭的新提交)

1 $ git revert <commit>

將HEAD重置到指定的版本,並拋棄該版本以後的全部修改:

1 $ git reset --hard <commit>

將HEAD重置到上一次提交的版本,並將以後的修改標記爲未添加到緩存區的修改:

1 $ git reset <commit>

將HEAD重置到上一次提交的版本,並保留未提交的本地修改:

1 $ git reset --keep <commit>

Git進階指南

問:如何修改 origin 倉庫信息?

一、添加 origin 倉庫信息

1 $ git remote add origin <git倉庫地址>

二、查看 origin 倉庫信息

1 # 如下三種方式都可
2 $ git config get --remote.origin.url
3 $ git remote -v
4 $ git remote show origin

三、刪除 origin 倉庫信息

1 $ git remote rm origin

問:如何配置 git ssh keys ?

在本地生成 ssh 私鑰 / 公鑰 文件
將「公鑰」添加到 git 服務(github、gitlab、coding.net 等)網站後臺
測試 git ssh 鏈接是否成功
接下來以添加 github ssh keys 爲例,請注意替換 github 文件名。

:若是對密鑰機制不熟悉,建議不要指定 -f 參數,直接使用默認的 id_rsa 文件名。

 1 # 運行如下命令,一直回車,文件名可隨意指定
 2 $ ssh-keygen -t rsa -b 4096 -C "kaiye@macbook" -f ~/.ssh/github
 3 
 4 # 若是不是默認密鑰 id_rsa ,則須要如下命令註冊密鑰文件,-K 參數將密鑰存入 Mac Keychain
 5 $ ssh-add -K ~/.ssh/github
 6 
 7 # 將 pub 公鑰的內容粘貼到線上網站的後臺
 8 $ cat ~/.ssh/github.pub
 9 
10 # 測試 git ssh 是否鏈接成功
11 $ ssh -T git@github.com

問:如何撤銷修改?

修改包含四種狀況,需單獨區分。

一、新建的文件和目錄,且從未提交至版本庫

此類文件的狀態爲 Untracked files ,撤銷方法以下:

1 $ git clean -fd .

其中,. 表示當前目錄及全部子目錄中的文件,也能夠直接指定對應的文件路徑,如下其餘狀況相似。

二、提交過版本庫,但未提交至暫存區的文件(未執行 git add)

此類文件的狀態爲Changes not staged for commit,撤銷方法:

1 $ git checkout .

三、已提交至暫存區的文件

此類文件的狀態爲 Changes to be committed,撤銷方法:

1 $ git reset .

執行以後文件將會回到以上的 1 或者 2 狀態,可繼續按以上步驟執行撤銷,若 git reset 同時加上 –hard 參數,將會把修改過的文件也還原成版本庫中的版本。

四、已提交至版本庫(執行了 git commit)

每次提交都會生成一個 hash 版本號,經過如下命令可查閱版本號並將其回滾:

1 $ git log
2 $ git reset <版本號>

若是須要「回滾至上一次提交」,可直接使用如下命令:

1 $ git reset head~1

執行以後,再按照 1 或者 2 狀態進行處理便可,若是回滾以後的代碼同時須要提交至 origin 倉庫(即回滾 origin 線上倉庫的代碼),須要使用 -f 強制提交參數,且當前用戶須要具有「強制提交的權限」。

五、若是回滾了以後又不想回滾了怎麼辦?

若是是以上的狀況 1 或者 2,只能歇屁了,由於修改沒入過版本庫,沒法回滾。

若是是狀況 4,回滾以後經過 git log 將看不到回滾以前的版本號,但可經過 git reflog 命令(全部使用過的版本號)找到回滾以前的版本號,而後 git reset <版本號> 。

問:遇到衝突了怎麼解決?

兩個分支進行合併時(一般是 git pull 時),可能會遇到衝突,同時被修改的文件會進入 Unmerged 狀態,須要解決衝突。

一、最快的辦法

大部分時候,「最快解決衝突」的辦法是:使用當前 HEAD 的版本(ours),或使用合併進來的分支版本(theirs)。

1 # 使用當前分支 HEAD 版本,一般是衝突源文件的 <<<<<<< 標記部分,======= 的上方
2 $ git checkout --ours <文件名>
3 
4 # 使用合併分支版本,一般是源衝突文件的 >>>>>>> 標記部分
5 $ git checkout --theirs <文件名>
6 
7 # 標記爲解決狀態加入暫存區
8 $ git add <文件名>

二、最通用的辦法

用編輯器打開衝突的源文件進行修改,可能會發生遺留,且體驗很差,一般須要藉助 git mergetool 命令。

在 Mac 系統下,運行 git mergetool <文件名> 能夠開啓配置的第三方工具進行 merge,默認的是 FileMerge 應用程序,還能夠配置成 Meld 或 kdiff3,體驗更佳。

三、最好的習慣

有三個好的習慣,能夠減小代碼的衝突:
在開始修改代碼前先 git pull 一下;
將業務代碼進行劃分,儘可能不要多我的在同一時間段修改同一文件;
經過Gitflow 工做流也能夠提高 git流程效率,減小發生衝突的可能性。

四、最複雜的狀況

若是你的項目週期比較長,還應該養成「按期 rebase 的習慣」,git pull –rebase 可讓分支的代碼和 origin 倉庫的代碼保持兼容,同時還不會破壞線上代碼的可靠性。

它的大概原理是,先將 origin 倉庫的代碼按 origin 的時間流在本地分支中提交,再將本地分支的修改記錄追加到 origin 分支上。若是發生衝突,則能夠即時的發現問題並解決,不然到項目上線時再解決衝突,可能會發生額外的風險。

rebase 大概的操做步驟以下:

 1 # 將當前分支的版本追加到從遠程 pull 回來的節點以後
 2 $ git pull --rebase
 3 
 4 # 若發生衝突,則按以上其餘方法進行解決,解決後繼續
 5 $ git rebase --continue
 6 
 7 # 直到全部衝突得以解決,待項目最後上線前再執行
 8 $ git push origin
 9 
10 # 若屢次提交修改了同一文件,可能須要直接跳事後續提交,按提示操做便可
11 $ git rebase --skip

問:如何在不提交修改的前提下,執行 pull / merge 等操做?

有些修改沒有徹底完成以前,可能不須要提交到版本庫,圡方法是將修改的文件 copy 到 git 倉庫以外的目錄臨時存放,pull / merge 操做完成以後,再 copy 回來。

這樣的作法一個是效率不高,另一個可能會遺漏潛在的衝突。此類需求最好是經過 git stash 命令來完成,它能夠將當前工做狀態(WIP,work in progress)臨時存放在 stash 隊列中,待操做完成後再從 stash 隊列中從新應用這些修改。

如下是 git stash 經常使用命令:

 1 # 查看 stash 隊列中已暫存了多少 WIP
 2 $ git stash list
 3 
 4 # 恢復上一次的 WIP 狀態,並從隊列中移除
 5 $ git stash pop
 6 
 7 # 添加當前 WIP,注意:未提交到版本庫的文件會自動忽略,只要不運行 git clean -fd . 就不會丟失
 8 $ git stash
 9 
10 # 恢復指定編號的 WIP,同時從隊列中移除
11 $ git stash pop stash@{num}
12 
13 # 恢復指定編號的 WIP,但不從隊列中移除
14 $ git stash apply stash@{num}

問:如何在 git log 中查看修改的文件列表?

默認的 git log 會顯示較全的信息,且不包含文件列表。使用 –name-status 能夠看到修改的文件列表,使用 –oneline 能夠將參數簡化成一行。

1 $ git log --name-status --oneline

每次手動加上參數很麻煩,能夠經過自定義快捷命令的方式來簡化操做:

1 $ git config --global alias.ls 'log --name-status --oneline --graph'

運行以上配置後,可經過 git ls 命令來實現「自定義 git log」效果,經過該方法也能夠建立 git st 、 git ci 等一系列命令,以便沿用 svn 命令行習慣。

1 $ git config --global alias.st 'status --porcelain'

更多 git log 參數,可經過 git help log 查看手冊。

若是是看上一次提交的版本日誌,直接運行 git show 便可。

此外,若是你的 Mac 安裝了zsh(參考《全新Mac安裝指南(編程篇),那麼能夠直接使用 gst、glog 等一系列快捷命令,詳情見此列表:Plugin:git 。

問:git submodule update 時出錯怎麼解決?

例如,在執行 git submodule update 時有如下錯誤信息:

fatal: reference is not a tree:
f869da471c5d8a185cd110bbe4842d6757b002f5
Unable to checkout
‘f869da471c5d8a185cd110bbe4842d6757b002f5’ in submodule
path ‘source/i18n-php-server’

在此例中,發生以上錯誤是由於 i18n-php-server 子倉庫在某電腦 A 的「本地」commit 了新的版本 「f869da471c5d8a185cd110bbe4842d6757b002f5」,且該次 commit 未 push origin。但其父級倉庫 i18n-www 中引用了該子倉庫的版本號,且將引用記錄 push origin,致使其餘客戶機沒法 update 。

解決方法,在電腦 A 上將 i18n-php-server 版本庫 push origin 後,在其餘客戶機上執行 git submodule update 。或者用以上提到的 git reset 方法,將子倉庫的引用版本號還原成 origin 上存在的最新版本號。

其餘問題

設置本地分支與遠程分支保持同步,在第一次 git push 的時候帶上 -u 參數便可

1 $ git push origin master -u 

支持中文目錄與文件名的顯示(git 默認將非 ASCII 編碼的目錄與文件名以八進制編碼展現)

1 $ git config core.quotepath off

經常使用的打 tag 操做,更多請查看《Git 基礎 - 打標籤》

 1 # 列出全部本地 tag
 2 $ git tag   
 3 
 4 # 本地新增一個 tag,推送至 origin 服務器
 5 $ git tag -a v1.0.0 -m 'tag description'
 6 $ git push origin v1.0.0
 7 
 8 # 刪除本地與 origin tag
 9 $ git tag -d v1.0.0
10 $ git push origin --delete v1.0.0

使用 git GUI 客戶端(如,SoureTree、Github Desktop)能極大的提高分支管理效率。分支合併操做一般只有兩種狀況:從 origin merge 到本地,使用 git pull 便可;從另一個本地分支 merge 到當前分支,使用 git merge <分支名>,如下是經常使用命令:

 1 # 新建分支 branch1,並切換過去
 2 $ git checkout -b branch1
 3 
 4 # 查看全部本地與遠程分支
 5 $ git branch -a
 6 
 7 # 修改完成後,切換回 master 分支,將 branch1 分支合併進來
 8 $ git checkout master
 9 $ git merge branch1
10 
11 # 刪除已完成合並的分支 branch1
12 $ git branch -d branch1

參考資料

  1. Pro Git 簡體中文版
  2. Git權威指南
  3. 命令行man手冊
相關文章
相關標籤/搜索