Git學習總結

Git Community Book

 

Git介紹

Git對象模型

對象名是SHA值

 

每個對象(object) 包括三個部分:類型,大小和內容。大小就是指內容的大小,內容取決於對象的類型,有四種類型的對象:"blob"、"tree"、 "commit" 和"tag"。

 

•“blob”用來存儲文件數據,通常是一個文件。

·•“tree”有點像一個目錄,它管理一些“tree”或是“blob”(就像文件和子目錄)

• 一個“commit”只指向一個"tree",它用來標記項目某一個特定時間點的狀態。它包括一些關於時間點的元數據,如時間戳、最近一次提交的作者、指向上次提交(commits)的指針等等。

• 一個“tag”是來標記某一個提交(commit) 的方法。

幾乎所有的Git功能都是使用這四個簡單的對象類型來完成的。它就像是在你本機的文件系統之上構建一個小的文件系統。

與SVN的區別

SVN是使用“增量文件系統”存儲每次提交之間的差異。Git是每次提交的文件的全部內容都會記錄下來。

Git目錄與工作目錄

$>tree -L 1

|-- HEAD # 這個git項目當前處在哪個分支裏

|-- config # 項目的配置信息,git config命令會改動它

|-- description # 項目的描述信息

|-- hooks/ # 系統默認鉤子腳本目錄

|-- index # 索引文件

|-- logs/ # 各個refs的歷史信息

|-- objects/ # Git本地倉庫的所有對象 (commits, trees, blobs, tags)

`-- refs/ # 標識你項目裏的每個分支指向了哪個提交(commit)。

 

工作目錄只用來臨時保存簽出(checkout) 文件的地方,你可以編輯工作目錄的文件直到下次提交(commit)爲止

 

Git索引是一個在你的工作目錄和項目倉庫間的暫存區(staging area). 有了它, 你可以把許多內容的修改一起提交(commit). 如果你創建了一個提交(commit), 那麼提交的是當前索引(index)裏的內容, 而不是工作目錄中的內容.

 

查看索引:git status 命令

第一步

Git安裝與初始化

$ git config --global user.name "Scott Chacon"

$ git config --global user.email "[email protected]"

基本用法

獲得一個Git倉庫

Clone一個倉庫

git clone git://git.kernel.org/pub/scm/git/git.git

git clone http://www.kernel.org/pub/scm/git/git.git

 

初始化一個新的倉庫

$ tar xzf project.tar.gz

$ cd project

$ git init

正常的工作流程

你可以使用 git diff 命令再加上 --cached 參數 ,看看哪些文件將被提交(commit)。

$ git diff --cached

(如果沒有--cached參數,git diff 會顯示當前你所有已做的但沒有加入到索引裏的修改.) 你也可以用git status命令來獲得當前項目的一個狀況

 

除了用git add 命令,還可以用

$ git commit -a

這會自動把所有內容被修改的文件(不包括新創建的文件)都添加到索引中,並且同時把它們提交。

分支與合併基礎

創建一個新的叫”experimental”的分支:

$ git branch experimental

得到當前倉庫中存在的所有分支列表:

$ git branch

切換到”experimental”分支:

$ git checkout experimental

通過下面的命令來合併“experimental”和“master”兩個分支:

$ git merge experimental

如有有衝突,輸入下面的命令就可以查看當前有哪些文件產生了衝突:

$ git diff

當你編輯了有衝突的文件,解決了衝突後就可以提交了:

$ git commit -a

提交(commit)了合併的內容後就可查看一下:

$ gitk

刪除掉“experimental”分支了:

$ git branch -d experimental

git branch -d只能刪除那些已經被當前分支的合併的分支. 如果你要強制刪除某個分支的話就用git branch –D

合併分支,把branchname分支合併到當前分支:

$ git merge branchname

 

撒銷一個合併

如果你覺得你合併後的狀態是一團亂麻,想把當前的修改都放棄,你可以用下面的命令回到合併之前的狀態:

$ git reset --hard HEAD

或者你已經把合併後的代碼提交,但還是想把它們撒銷:

$ git reset --hard ORIG_HEAD

查看歷史-GIT日誌

git log命令可以顯示所有的提交(commit)

$ git log v2.5.. # commits since (not reachable from) v2.5

$ git log test..master # commits reachable from master but not test

$ git log master..test # commits reachable from test but not master

$ git log master...test # commits reachable from either test or

# master, but not both

$ git log --since="2 weeks ago" # commits from the last 2 weeks

$ git log Makefile # commits that modify Makefile

$ git log fs/ # commits that modify any file under fs/

$ git log -S'foo()' # commits that add or remove any file data

# matching the string 'foo()'

$ git log --no-merges # dont show merge commits

當然你也可以組合上面的命令選項;下面的命令就是找出所有從"v2.5“開 始在fs目錄下的所有Makefile的修改

$ git log v2.5.. Makefile fs/

日誌統計

$ git log –stat

格式化日誌

$ git log --pretty=oneline

$ git log --pretty=short

可以用'--graph'選項來可視化你的提交圖(commit graph),就像下面這樣:

$ git log --pretty=format:'%h : %s' --graph

比較提交-GIT DIFF

你可以用 git diff 來比較項目中任意兩個版本的差異。

$ git diff master..test

上面這條命令只顯示兩個分支間的差異,如果你想找出‘master’,‘test’的共有父分支和'test'分支之間的差異,你用3個‘.'來取代前面的兩個'.' 。

$ git diff master...test

 

通常用git diff來找你當前工作目錄和上次提交與本地索引間的差異。

$ git diff

上面的命令會顯示在當前的工作目錄裏的,沒有 staged(添加到索引中),且在下次提交時不會被提交的修改。

如果你要看在下次提交時要提交的內容(staged,添加到索引中),你可以運行:

$ git diff --cached

上面的命令會顯示你當前的索引和上次提交間的差異;這些內容在不帶"-a"參數運行 "git commit"命令時就會被提交。

 

$ git diff HEAD

上面這條命令會顯示你工作目錄與上次提交時之間的所有差別,這條命令所顯示的 內容都會在執行"git commit -a"命令時被提交。

 

更多的比較選項

查看當前的工作目錄與另外一個分支的差別,你可以用下面的命令執行:

$ git diff test

這會顯示你當前工作目錄與另外一個叫'test'分支的差別。你也以加上路徑限定符,來只比較某一個文件或目錄。

$ git diff HEAD -- ./lib

上面這條命令會顯示你當前工作目錄下的lib目錄與上次提交之間的差別(或者更準確的說是在當前分支)。

如果不是查看每個文件的詳細差別,而是統計一下有哪些文件被改動,有多少行被改動,就可以使用‘--stat' 參數。

$>git diff --stat

分佈式的工作流程

git pull命令執行兩個操作: 它從遠程分支(remote branch)抓取修改的內容,然後把它合併進當前的分支。

公共Git倉庫

見《Git Community Book》

將修改推送到一個公共倉庫

用你本地的"master" 分支去更新遠程的"master"分支,執行下面的命令:

$ git push ssh://yourserver.com/~you/proj.git master:master

or just

或是:

$ git push ssh://yourserver.com/~you/proj.git master

Git標籤

輕量級標籤

我們可以用 git tag不帶任何參數創建一個標籤(tag)指定某個提交(commit):

$ git tag stable-1 1b2e1d63ff

前面這樣創建的是一個"輕量級標籤",這種分支通常是從來不移動的。

如果你想爲一個標籤(tag)添加註釋,或是爲它添加一個簽名(sign it cryptographically), 那麼我們就需要創建一個"標籤對象"

標籤對象

如果有 "-a", "-s" 或是 "-u " 中間的一個命令參數被指定,那麼就會創建 一個標籤對象,並且需要一個標籤消息(tagmessage). 如果沒有"-m " 或是 "-F " 這些參數,那麼就會啓動一個編輯器來讓用戶輸入標籤消息(tag message).

簽名的標籤

在你的 .git/config 或 ~.gitconfig裏配好key.

下面是示例:

[user]

signingkey = <gpg-key-id>

你也可以用命令行來配置:

$ git config (--global) user.signingkey <gpg-key-id>

現在你可以直接用"-s" 參數來創“簽名的標籤”。

$ git tag -s stable-1 1b2e1d63ff

如果沒有在配置文件中配GPG key,你可以用"-u“ 參數直接指定。

$ git tag -u <gpg-key-id> stable-1 1b2e1d63ff

中級技能

忽略某些文件

你可以在你的頂層工作目錄中添加一個叫".gitignore"的文件,來告訴Git系統要忽略掉哪些文件,下面是文件內容的示例:

# 以'#' 開始的行,被視爲註釋.

# 忽略掉所有文件名是 foo.txt 的文件.

foo.txt

# 忽略所有生成的 html 文件,

*.html

# foo.html是手工維護的,所以例外.

!foo.html

# 忽略所有.o 和 .a文件.

*.[oa]

你可以點這裏gitignore查看一下詳細的語法解釋. 你也可以把".gitignore" 這個文件放到工作樹(working tree)裏的其它目錄中,這就會在它和它的子目錄起忽略(ignore) 指定文件的作用。.gitignor文件同樣可以像其它文件一樣加到項目倉庫裏(直接用 git add .gitignore 和 git commit等命令), 這樣項目裏的其它開發者也能共享同一套忽略文件規則。

 

如果你想忽略規則只對特定的倉庫起作用,你可以把這些忽略規則寫到你的倉庫下 .git/info/exclude文件中,或是寫在Git配置變量core.excludesfile中指定的文件裏。有些Git命令也可在命令行參數中指定忽略規則,你可以在這裏:gitignore查看詳細的用法。

REBASE

假設你現在基於遠程分支"origin",創建一個叫"mywork"的分支。

$ git checkout -b mywork origin

現在我們在這個分支做一些修改,然後生成兩個提交(commit).

但是與此同時,有些人也在"origin"分支上做了一些修改並且做了提交了。這就意味着"origin"和"mywork"這兩個分支各自"前進"了,它們之間"分叉"了。

在這裏,你可以用"pull"命令把"origin"分支上的修改拉下來並且和你的修改合併; 結果看起來就像一個新的"合併的提交"(merge commit):

但是,如果你想讓"mywork"分支歷史看起來像沒有經過任何合併一樣,你也許可以用git rebase:

$ git checkout mywork

$ git rebase origin

這些命令會把你的"mywork"分支裏的每個提交(commit)取消掉,並且把它們臨時保存爲補丁(patch)(這些補丁放到".git/rebase"目錄中),然後把"mywork"分支更新 到最新的"origin"分支,最後把保存的這些補丁應用到"mywork"分支上。

當'mywork'分支更新之後,它會指向這些新創建的提交(commit),而那些老的提交會被丟棄。

若出現衝突,解決後:

$ git rebase --continue

在任何時候,你可以用--abort參數來終rebase的行動,並且"mywork" 分支會回到rebase始前的狀
$ git rebase --abort

儲藏

當你正在做一項複雜的工作時, 發現了一個和當前工作不相關但是又很討厭的bug. 你這時想先修復bug再做手頭的工作, 那麼就可以用 git stash 來保存當前的工作狀態, 等你修復完bug後,執行'反儲藏'(unstash)操作就可以回到之前的工作裏。

$ git stash "work in progress for foo feature"

上面這條命令會保存你的本地修改到儲藏(stash)中, 然後將你的工作目錄和索引裏的內容全部重置, 回到你當前所在分支的上次提交時的狀態.好了, 你現在就可以開始你的修復工作了.

... edit and test ...

$ git commit -a -m "blorpl: typofix"

當你修復完bug後, 你可以用git stash apply來回復到以前的工作狀態.

$ git stash apply

儲藏隊列

你也可多次使用'git stash'命令,每執行一次就會把針對當前修改的‘儲藏’(stash)添加到儲藏隊列中. 用'git stash list'命令可以查看你保存的'儲藏'(stashes):

$>git stash list

GIT樹名

不用40個字節長的SHA串來表示一個提交(commit)或是其它git對象

Sha短名

分支,Remote或標籤

你可以使用分支,remote或標籤名來代替SHA串名, 它們只是指向某個對象的指針

日期標識符

Git的引用日誌(Ref Log)可以讓你做一些‘相對'查詢操作:

master@{yesterday}

master@{1 month ago}

上面的第一條命令是:'master分支的昨天狀態(head)的縮寫‘. 注意: 即使在兩個有相同master分支指向的倉庫上執行這條命令, 但是如果這個兩個倉庫在不同機器上,那麼執行結果也很可能會不一樣

順序標識符

這種格式用來表達某點前面的第N個提交(ref).

master@{5}

上面的表達式代表着master前面的第5個提交(ref).

多個父對象

這能告訴你某個提交的第N個直接父提交(parent). 這種格式在合併提交(merge commits)時特別有用, 這樣就可以使提交對象(commit object)有多於一個直接父對象(direct parent).

譯者注:假設master是由a和b兩個分支合併的,那麼 master^1是指分支a, master^2就是指分支b.

master^2

波浪號

波浪號用來標識一個提交對象(commit object)的第N級嫡(祖)父對象(Nth grandparent). 例如:

master~2

就代表master所指向的提交對象的第一個父對象的第一個父對象(譯者:你可以理解成是嫡系爺爺:)). 它和下面的這個表達式是等價的:

master^^

你也可以把這些‘標識符'(spec)疊加起來,下面這個3個表達式都是指向同一個提交(commit):

master^^^^^^

master~3^~2

master~6

樹對象指針

如果大家對第一章Git對象模型還有印象的話, 就記得提交對象(commit object)是指向一個樹對象(tree object)的. 假如你要得到一個提交對象(commit object)指向的樹對象(tree object)的sha串名, 你就可以在‘樹名'的後面加上'{tree}'來得到它:

master^{tree}

二進制標識符

如果你要某個二次制對象(blob)的sha串名,你可以在'樹名'(treeish)後添加二次制對象(blob)對應的文件路徑來得到它.

master:/path/to/file

區間

最後,你可以用".."來指兩個提交(commit)之間的區間。下面的命令會給出你在"7b593b5" 和"51bea1"之間除了"7b593b5外"的所有提交(commit)(注意:51bea1是最近的提交).

7b593b5..51bea1

這會包括所有從7b593b開始的提交(commit). 譯者注:相當於7b593b..HEAD

7b593b..

追蹤分支

在Git中'追蹤分支'是用與聯繫本地分支和遠程分支的。如果你在'追蹤分支'(Tracking Branches)上執行推送(push)或拉取(pull)時,它會自動推送(push)或拉取(pull)到關聯的遠程分支上。

 

如果你經常要從遠程倉庫里拉取(pull)分支到本地,並且不想很麻煩的使用"git pull "這種格式; 那麼就應當使用'追蹤分支'(Tracking Branches)。

 

‘git clone‘命令會自動在本地建立一個'master'分支,它是'origin/master'的‘追蹤分支’. 而'origin/master'就是被克隆(clone)倉庫的'master'分支

你可以在使用'git branch'命令時加上'--track'參數, 來手動創建一個'追蹤分支'.

git branch --track experimental origin/experimental

當你運行下命令時:

$ git pull experimental

它會自動從'origin'抓取(fetch)內容,再把遠程的'origin/experimental'分支合併進(merge)本地的'experimental'分支.

當要把修改推送(push)到origin時, 它會將你本地的'experimental'分支中的修改推送到origin的'experimental'分支裏,而無需指定它(origin)

使用GIT GREP進行搜索

用git grep 命令查找Git庫裏面的某段文字是很方便的. 當然, 你也可以用unix下的'grep'命令進行搜索, 但是'git grep'命令能讓你不用簽出(checkout)歷史文件, 就能查找它們.

例如, 你要看 git.git 這個倉庫裏每個使用'xmmap'函數的地方, 你可以運行下面的命令:

$ git grep xmmap

如果你要顯示行號, 你可以添加'-n'選項:

$>git grep -n xmmap

如果我們想只顯示文件名, 我們可以使用'--name-onley'選項:

$>git grep --name-only xmmap

我們可以用'-c'選項,可以查看每個文件裏有多少行匹配內容(line matches):

$>git grep -c xmmap

現在, 如果我們要查找git倉庫裏某個特定版本里的內容, 我們可以像下面一樣在命令行末尾加上標籤名(tag reference):

$ git grep xmmap v1.5.0

我們也可以組合一些搜索條件, 下面的命令就是查找我們在倉庫的哪個地方定義了'SORT_DIRENT'.

$ git grep -e '#define' --and -e SORT_DIRENT

builtin-fsck.c:#define SORT_DIRENT 0

builtin-fsck.c:#define SORT_DIRENT 1

我不但可以進行“與"(both)條件搜索操作,也可以進行"或"(either)條件搜索操作.

$ git grep --all-match -e '#define' -e SORT_DIRENT

我們也可以查找出符合一個條件(term)且符合兩個條件(terms)之一的文件行。例如我們要找出名字中含有'PATH'或是'MAX'的常量定義:

$ git grep -e '#define' --and \( -e PATH -e MAX \)

GIT的撤消操作-重置,簽出 和 撤消

修復未提交文件中的錯誤(重置)

如果你現在的工作目錄(work tree)裏搞的一團亂麻, 但是你現在還沒有把它們提交; 你可以通過下面的命令, 讓工作目錄回到上次提交時的狀態(last committed state):

$ git reset --hard HEAD

這條件命令會把你工作目錄中所有未提交的內容清空(當然這不包括未置於版控制下的文件 untracked files). 從另一種角度來說, 這會讓"git diff" 和"git diff --cached"命令的顯示法都變爲空.

如果你只是要恢復一個文件,如"hello.rb", 你就要使用 git checkout

$ git checkout -- hello.rb

修復已提交文件中的錯誤

創建新提交來修復錯誤:

$ git revert HEAD

下面這條命令就是撤消“上上次”(next-to-last)的提交:

$ git revert HEAD^

修改提交來修復錯誤:

如果你剛剛做了某個提交(commit), 但是你又想馬上修改這個提交; git commit 現在支持一個叫--amend的參數,它能讓你修改剛纔的這個提交(HEAD commit). 這項機制能讓你在代碼發佈前,添加一些新的文件或是修改你的提交註釋(commit message).

如果你在老提交(older commit)裏發現一個錯誤, 但是現在還沒有發佈到代碼服務器上。你可以使用 git rebase命令的交互模式, "git rebase -i"會提示你在編輯中做相關的修改。這樣其實就是讓你在rebase的過程來修改提交

維護GIT

在大的倉庫中, git靠壓縮歷史信息來節約磁盤和內存空間:(比較耗時)

$ git gc

保持可靠性: git fsck 運行一些倉庫的一致性檢查, 如果有任何問題就會報告. 這項操作也有點耗時, 通常報的警告就是“懸空對象"(dangling objects).

$ git fsck

 

建立一個公共倉庫

假設你個人的倉庫在目錄 ~/proj. 我們先克隆一個新的“裸倉庫“,並且創建一個標誌文件告訴git-daemon這是個公共倉庫.

$ git clone --bare ~/proj proj.git

$ touch proj.git/git-daemon-export-ok

上面的命令創建了一個proj.git目錄, 這個目錄裏有一個“裸git倉庫" -- 即只有'.git'目錄裏的內容,沒有任何簽出(checkedout)的文件.

 

高級技能

創建新的空分支

在偶爾的情況下,你可能會想要保留那些與你的代碼沒有共同祖先的分支。例如在這些分支上保留生成的文檔或者其他一些東西。如果你需要創建一個不使用當前代碼庫作爲父提交的分支,你可以用如下的方法創建一個空分支:

git symbolic-ref HEAD refs/heads/newbranch

rm .git/index

git clean -fdx

<do work>

git add your files

git commit -m 'Initial commit'

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章