Git的進一步研究

參閱: http://mindlee.net/2012/04/27/learning-git/ 【注意文章中所有的-均應爲--】

1. git blame 熟悉之後,可以試着用git gui blame, 不過命令行參數有些不一樣

E.g $git gui blame --line=44 src\Test.java


git ls-files

打印所有版控的文件列表


git ls-tree branch

打印內容類似

$ git ls-tree HEAD
100644 blob 8dead1626e5854f476b5c6eaa2507150c11605e0    a.txt
100644 blob 3e757656cf36eca53338e520d134963a44f793f8    b.txt

git show [參考http://stackoverflow.com/questions/610208/how-to-retrieve-a-single-file-from-specific-revision-in-git]

git show $REV:$FILE  顯示$REV這個commit的$FILE文件的內容,注意$FILE必須使用full path


git cat-file 用於顯示object的內容,object通常包含blob, tree和commit

[參考:http://www-cs-students.stanford.edu/~blynn/gitmagic/intl/zh_cn/ch08.html]

-p 漂亮的打印object內容,可以爲blob(文件內容), tree(文件屬性,名稱大小,修改時間等)

--batch 從標準輸入獲取object,例如 echo e7eeb5f6fc8d1ee46002ed83e073f2a028d0a7e1 |git cat-file --batch


2. git reset 熟悉之後,學些使用

git reset 之soft, hard

git reset --hard HEAD~1 爲完全恢復,即抹掉這條指定commit之前所有的commit記錄

git reset --soft HEAD~1  只改變版本庫,不改變暫存區和工作區; 【爲取消指定這條commit之前的那條commit,還原到將修改保存到stage的狀態(綠色),用處不大,完全可以用commit --amend來替換】

git reset --mixed HEAD~1 改變版本庫和暫存區,不改變工作區; 【爲取消commit, 還原到未將修改保存到stage的狀態(紅色),即修改內容依舊存在】

git reset .                            將git add 命令更新到暫存區的內容撤出暫存區,++【注意不要和git checkout .弄混!!!】
                                       即用版本庫的HEAD重置暫存區;
git reset  filename                    將暫存區中filename撤出暫存區,


git clean: 用於清除未加入(或者不打算加入)版本控制的文件和目錄。參數-f用於文件,-d用於目錄+++

參考:http://www.dotblogs.com.tw/larrynung/archive/2012/12/20/85834.aspx


git reflog 用於查看歷史操作

git reset --hard HEAD@{1} 恢復到該操作,即reset之後的狀態和該HEAD@{1}的操作結果是一致的++【可用於從merge衝突中脫身】【可用於在reset之後找回源碼】
git reflog -5                          查看最近五次的操作記錄,默認是HEAD
git reflog show master –5              查看master最近五次的操作記錄

git diff 比較分支或commit差異

git diff HEAD~1 HEAD~2 -- a.txt 比較不同commit之間相同文件的內容+++++

git diff brach1 branch2 -- a.txt 比較不同分支之間相同文件的內容+++++



git checkout

git checkout .                         會用暫存區內容刷新工作區,相當於取消本地所有修改;++++

git checkout branch -- filename         用branch中的文件替換暫存區 + 工作區中相應的文件;++++
git checkout HEAD~1 -- welcome.txt      從歷史中恢復文件;++++++
與git diff配合使用,選擇正確版本的文件內容


git tag

git tag -a v1.2 9fceb02 -m "Message here"  給某個舊的commit打標籤

git push origin --tags 推送所有標籤到remote repo,必須要加'--tags',pull的時候同樣要加


3. git format-patch 熟悉之後,學習

git cherry-pick 詳解:http://yiyingloveart.blogspot.com/2013/04/git-cherry-pick.html

分支之間的commit不需要指定分支名稱,只需要提供commit的SHA1值

git cherry-pick certain_commit                從衆多的提交中挑選出一個提交應用到當前的工作分支中【理解爲將certain_commit重新應用到當前分支】

舉例:

alias.hist=log --pretty=format:"%h %ad | %s%d [%an]" --graph --date=short

$ git hist
* edc561a 2013-08-16 | hoho [Bing Wei]
* 7b6abea 2013-08-16 | commit 4 from b2 [Bing Wei]
* 4282054 2013-08-16 | commit 2 from b2 [Bing Wei]
* 56a7fdd 2013-08-16 | commit 3 from b1 (testbb, branch1) [Bing Wei]

$ git cherry-pick 4282054
[master e5b4d82] commit 2 from b2
 1 file changed, 1 insertion(+)

$ git hist
* e5b4d82 2013-08-16 | commit 2 from b2 (HEAD, master) [Bing Wei]
* edc561a 2013-08-16 | hoho [Bing Wei]
* 7b6abea 2013-08-16 | commit 4 from b2 [Bing Wei]
* 4282054 2013-08-16 | commit 2 from b2 [Bing Wei]
* 56a7fdd 2013-08-16 | commit 3 from b1 (testbb, branch1) [Bing Wei]

同樣可以從reflog的Sha1中恢復某commit.

參閱:http://blog.csdn.net/cn_chenfeng/article/details/7244615


4. git rebase的高級用法 參閱【http://blog.yorkxin.org/2011/07/29/git-rebase


git show 參閱http://www.open-open.com/lib/view/1328070367499


5. 很難用到的git bisect,感覺就是丫就是爲了證明什麼,這命令真的太少用到了。。。


Refer to: http://heikezhi.com/yuanyi/git-201-slightly-more-advanced


6. 對象原理

http://git-scm.com/book/zh/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-Git-%E5%AF%B9%E8%B1%A1

對象存儲
之前我提到當存儲數據內容時,同時會有一個文件頭被存儲起來。我們花些時間來看看 Git 是如何存儲對象的。你將看來如何通過 Ruby 腳本語言存儲一個 blob 對象 (這裏以字符串 "what is up, doc?" 爲例) 。使用 irb 命令進入 Ruby 交互式模式:

$ irb
>> content = "what is up, doc?"
=> "what is up, doc?"
Git 以對象類型爲起始內容構造一個文件頭,本例中是一個 blob。然後添加一個空格,接着是數據內容的長度,最後是一個空字節 (null byte):

>> header = "blob #{content.length}\0"
=> "blob 16\000"
Git 將文件頭與原始數據內容拼接起來,並計算拼接後的新內容的 SHA-1 校驗和。可以在 Ruby 中使用 require 語句導入 SHA1 digest 庫,然後調用 Digest::SHA1.hexdigest() 方法計算字符串的 SHA-1 值:

>> store = header + content
=> "blob 16\000what is up, doc?"
>> require 'digest/sha1'
=> true
>> sha1 = Digest::SHA1.hexdigest(store)
=> "bd9dbf5aae1a3862dd1526723246b20206e5fc37"
Git 用 zlib 對數據內容進行壓縮,在 Ruby 中可以用 zlib 庫來實現。首先需要導入該庫,然後用 Zlib::Deflate.deflate() 對數據進行壓縮:

>> require 'zlib'
=> true
>> zlib_content = Zlib::Deflate.deflate(store)
=> "x\234K\312\311OR04c(\317H,Q\310,V(-\320QH\311O\266\a\000_\034\a\235"
最後將用 zlib 壓縮後的內容寫入磁盤。需要指定保存對象的路徑 (SHA-1 值的頭兩個字符作爲子目錄名稱,剩餘 38 個字符作爲文件名保存至該子目錄中)。在 Ruby 中,如果子目錄不存在可以用 FileUtils.mkdir_p() 函數創建它。接着用 File.open 方法打開文件,並用 write() 方法將之前壓縮的內容寫入該文件:

>> path = '.git/objects/' + sha1[0,2] + '/' + sha1[2,38]
=> ".git/objects/bd/9dbf5aae1a3862dd1526723246b20206e5fc37"
>> require 'fileutils'
=> true
>> FileUtils.mkdir_p(File.dirname(path))
=> ".git/objects/bd"
>> File.open(path, 'w') { |f| f.write zlib_content }
=> 32
這就行了 ── 你已經創建了一個正確的 blob 對象。所有的 Git 對象都以這種方式存儲,惟一的區別是類型不同 ── 除了字符串 blob,文件頭起始內容還可以是 commit 或 tree 。不過雖然 blob 幾乎可以是任意內容,commit 和 tree 的數據卻是有固定格式的。

7. 查找刪除文件的commit 

http://www.vogella.com/articles/Git/article.html#retrievefiles_finddeletedfile

git log -1 -- [file path]

8. git submodule

引用一段《Git權威指南》的話: 項目的版本庫在某些情況蝦需要引用其他版本庫中的文件,例如公司積累了一套常用的函數庫,被多個項目調用,顯然這個函數庫的代碼不能直接放到某個項目的代碼中,而是要獨立爲一個代碼庫,那麼其他項目要調用公共函數庫該如何處理呢?分別把公共函數庫的文件拷貝到各自的項目中會造成冗餘,丟棄了公共函數庫的維護歷史,這顯然不是好的方法。

Best practise:

http://www.kafeitu.me/git/2012/03/27/git-submodule.html

http://josephjiang.com/entry.php?id=342

一些常見的問題和處理方法:

1. 在依賴的子項目中使用特定的分支或者TAG

ref: http://stackoverflow.com/questions/1777854/git-submodules-specify-a-branch-tag

操作: 

1.1 進入子項目的目錄

1.2 切換到簽出你需要使用的分支

1.3 返回父項目/主項目的根目錄

1.4 使用git add 和 git commit 保存修改

2. 撤銷對子項目分支的切換

ref: http://stackoverflow.com/questions/12192095/how-to-discard-change-to-git-submodule

操作:

2.1 進入父項目的根目錄,執行git submodule update

將簽出默認分支,從而撤銷掉對其的修改

PS: 如果想撤銷代碼的修改,則直接進入子項目,使用git checkout -- 進行撤銷操作

9. Git中的AutoCRLF與SafeCRLF換行符問題

CR回車 LF換行Windows/Dos CRLF \r\n
Linux/Unix LF \n
MacOS CR \r

#提交時轉換爲LF,檢出時轉換爲CRLF
git config --global core.autocrlf true   

http://www.cnblogs.com/flying_bat/p/3324769.html


10. Git push的默認行爲

http://stackoverflow.com/questions/948354/git-push-default-behavior

http://longair.net/blog/2011/02/27/an-asymmetry-between-git-pull-and-git-push/

設置push的默認行爲

git config --global push.default current

其他選項
nothing : 不進行push操作(除非指定repo) - 最安全
matching : Push所有branch名稱匹配的分支,意味着如果當前你有n條分支,並且如果他們都能在repo上被找到的話,會全部被推送 - 默認選項,但是不推薦使用,因爲非常容易造成將其他branch上未完成的commits推送到remote repo上
tracking : Push當前分支,不管它在多少個repo上能被找到(derpecated)
current : Push當前分支(待測試和tracking的區別)
simple: (Git 1.7.11新增) 優先推送到upstream,但是如果upstream上的分支名和當前的不一致,則拒絕推送,爲git安裝後默認使用的選項 - 推薦使用

PS: 我們可以使用git branch --set-upstream-to=[remote repo]/branch 來將我們本地的branch和遠程指定倉庫的分支的進行綁定,這樣的話,當使用push.default = simple選項時, 使用git push就會第一時間到我們設定的remote repo上去尋找是否存在同名本地branch,然後進行推送

PS2: 萬惡的upstream,一直以爲只是用來追蹤upstream的repo的,其實跟那個完全沒關係好麼!



11. 設置git pull --rebase 爲默認操作

默認git pull操作是 git fetch + git merge

但是對於需要經常提交pull request的我們來說,需要的是git pull --rebase, 所以下面的config可以幫助我們將rebase作爲默認操作,減少重複操作

參考: https://coderwall.com/p/yf5-0w

$git config branch.autosetuprebase always

這樣所有新創建的branch就會使用rebase當你使用git pull的時候

針對已經存在的branch,則需要以下設置

$git config branch.YOUR_BRANCH_NAME.rebase true

最後,如果你需要在git pull的時候使用merge,則使用

$git pull --no-rebase


12. 找到好東西了:) -  教你如何抹掉歷史記錄

先來看下git rebase --help的說明

We can get this using the following command:

git rebase --onto master next topic

Another example of --onto option is to rebase part of a branch. If we have the following situation:

                            H---I---J topicB
                           /
                  E---F---G  topicA
                 /
    A---B---C---D  master

then the command

git rebase --onto master topicA topicB

would result in:

                 H'--I'--J'  topicB
                /
                | E---F---G  topicA
                |/
    A---B---C---D  master

This is useful when topicB does not depend on topicA.

這裏可以分別把master, topicA, topicB設想爲3個commit,他們的順序應該是master是最老的,topicB是最新的

這裏如果topicB不依賴topicA,那麼我們可以用上面的命令將topicB這個commit直接接到topicA上面

現在用commit的實例來演示一下

設想你有如此的記錄

$git status

last-commit

to-be-removed-commit

old-commit

...

你需要將to-be-removed-commit從當前分支中抹掉,那麼你可以用如下命令來實現

git rebase --onto HEAD^^ HEAD^ HEAD

這裏面HEAD^^意味着倒數第三個commit, 即 old-commit, 別的以此類推

那麼git rebase --onto是做什麼的呢?

基於上面的演示,我們可以知道--onto可以將最後提供的commit或者分支直接接到第一個提供的commit或者分支上

當執行完這個命令之後,你會跳到一個新的臨時分支上,這時候就需要你手動checkout出來。

OK, 當你理解這些之後,那麼下面的例子你應該同樣可以理解了

A range of commits could also be removed with rebase. If we have the following situation:

    E---F---G---H---I---J  topicA

then the command

git rebase --onto topicA~5 topicA~3 topicA

would result in the removal of commits F and G:

    E---H'---I'---J'  topicA
注意HEAD是J, 而HEAD~1是I,以此類推

不過一定要注意:topicB不能依賴topicA,否則無法成功。


待研究 http://www.worldhello.net/git-quiz/exam02.html

rev-list??
git log ..maint
git show :0:./file > file-2
git describe --tags --always --dirty
git name-rev
git log origin/master..
git diff-tree origin/master..
git request-pull origin/master URL-of-your-repo
git diff --stat origin/master


13. 查看修改的內容

參考: http://stackoverflow.com/questions/1587846/how-do-i-show-the-changes-which-have-been-staged

git diff

顯示那些本地修改的,但尚未加入到緩存區(staged)的內容

git diff --cached

顯示那些本地修改的,並且加入到緩存區(staged)的內容

git diff HEAD

顯示所有的修改內容,包括加入或者未加入到緩存區(staged)的內容


發佈了26 篇原創文章 · 獲贊 3 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章