目錄html
若是你想精通Git,直接到 Git官網 把這本ProGit掌握已足以Pro Git
此文主要介紹一切開發中經常使用的git命令和一些配置技巧(諸如git別名配置,log打印技巧,版本回退以及分支管理等)。
後來我又寫了篇主要介紹Git變基的文章《開發中關於Git那些事(Git Rebasing)》,有興趣的能夠看看。git
Git與SVN相比而言,Git的好處天然不用多說,Git徹底分佈式文件管理系統,加上其簡單速度,能夠高效管理相似 Linux 內核同樣的超大規模項目。徹底分佈式的系統,讓你能夠在公交車上,火車上,家中,甚至在廁所均可以敲代碼。什麼時候何地你均可以敲代碼,甚至不須要網絡。好很差使,開不開心?換是SVN,SVN服務器掛了,所有人中止敲代碼,停下來八卦去吧。正則表達式
Git 和其餘版本控制系統的主要差異在於,Git 只關心文件數據的總體是否發生變化,而大多數其餘系統則只關心文件內容的具體差別。這類系統(CVS,Subversion,Perforce,Bazaar 等等)每次記錄有哪些文件做了更新,以及都更新了哪些行的什麼內容。如同下圖所示:shell
可是,Git 並不保存這些先後變化的差別數據。實際上,Git 更像是把變化的文件做快照後,記錄在一個微型的文件系統中。每次提交更新時,它會縱覽一遍全部文件的指紋信息並對文件做一快照,而後保存一個指向此次快 照的索引。爲提升性能,若文件沒有變化,Git 不會再次保存,而只對上次保存的快照做一鏈接。工做方式相似下圖:數據庫
對比能夠發現,Git高效也在情理之中。
如今再簡單介紹一下Git管理下文件的三種狀態。對於任何一個文件,只要在Git管理下,那麼該文件只有三種狀態:已修改(modified),已暫存(staged)和已提交 (committed)。
已修改(modified): 文件被修改,可是尚未提交保存(也就是沒有使用git add,此時使用git status顯示爲紅色)。
已暫存(staged): 已修改的文件放入下次要提交的清單中(使用了git add後的狀態,此時使用git status顯示爲綠色)。
已提交(committed): 該文件已經被安全地保存在本地數據庫中(使用了git commit後,此時使用git status已經不存在該文件的任何信息)。
具體能夠參考下面兩幅圖來理解。
安全
user和email,--global參數全局配置,固然你也能夠不加此參數,不一樣的項目用不一樣的用戶名和郵箱。
注意:必定要認真填寫郵箱,若是你項目在OSChina或則GitHub,郵箱應該和OSChina或則GitHub綁定的郵箱一致,否則以後就算你提交了代碼,是看不到本身的貢獻度的。ruby
git config --global user.name Super git config --global user.email 1342449****@163.com
不過以後也是能夠重寫已經提交的commit的用戶名和郵箱,可是通常狀況下不要這樣操做,不提倡,重寫命令:
此命令會重寫全部commit,謹慎操做服務器
git filter-branch -f --env-filter ' if [ "$GIT_AUTHOR_NAME" = "Super" ] then export GIT_AUTHOR_NAME="Super" export GIT_AUTHOR_EMAIL="xxx@gmail.com" fi '
此配置在開發中至關重要,尤爲是對於使用Terminal,習慣使用命令行的朋友,因爲git不支持tab自動補全,每次想要看下工做目錄 狀態都要git status,至關耗時。除非你能肯定你敲兩個字母比六個字母用時少。網絡
git config --global alias.st "status -s" git config --global alias.br "branch" git config --global alias.co "checkout" git config --global alias.ci "commit -m" git config --global alias.aci "commit -a -m" // 跳過使用暫緩區,直接將git add和git commit合併爲一條命令 git config --global alias.lg "log --color --pretty=format:'%Cred%h - %Cgreen%an %C(yellow)| %ad | %Creset%s' --graph" //自定義log
第一條:git status是開發中使用最多最頻繁的,至於-s 是簡潔輸入(Give the output in the short-format)
第二條:此條也使用頻繁,可是我在開發中直接使用第三條跳過。
第三條:配置git aci 由於這樣直接跳過使用暫存區域,對於已經跟蹤的文件,我不要再這次使用git add加入暫緩區,而後再git commit提交到本地數據庫,爲了方便省事,直接將兩條命令合併爲一條,使用git aci "提交說明" 便可。省不省事,用下天然知道。
第四條:這裏是我自定義的log信息,固然,一會看完本文你也能夠本身格式化成本身喜歡的log格式。能夠先看下效果,下圖所示:分佈式
之後直接使用"git lg"便可,很是方便,顯示簡潔明瞭。
補充,也能夠不加引號,好比這樣子
$ git config --global alias.co checkout $ git config --global alias.br branch $ git config --global alias.ci commit $ git config --global alias.st status
git clone https://gitee.com/xxx/Super.git(遠程地址) // 直接拉取遠程分支(默認master主分支) git clone -b dev https://gitee.com/xxx/Super.git(遠程地址) // 直接拉取遠程git dev分支(非主分支) git checkout -b dev origin/dev // 拉取遠程開發分支到本地而且切換到開發分支 git push dev origin/dev git branch new // 建立新分支 git push origin new // 將new分支推送到遠程(git push [remote-name] [branch-name]) git push origin :new // 刪除遠程分支,注意冒號位置 git branch -r -d origin/dev // 刪除遠程分支(同上一行命令,做用同樣) git brach -d new // 刪除本地分支,若是有沒有merge的信息,確保該分支的確不用merge,直接使用-D強行刪除 git merge dev // 將dev分支合併到當前分支 git checkout master // 切換到主分支 其餘再也不一一列出
關於實際開發中,獨立開發的話其實兩個分支徹底夠用,一個主分支(master),一個開發分支(develop)。多人的話就按功能模塊和人員來具體新建分支便可。因爲這邊由我一人負責整個項目,那麼分支的話,我就創建了一個develop分支,發佈版本時切換到master處理便可。平時都在develop分支開發,若是此時發現線上版本有bug,那麼只須要切換到master分支,修改bug,而後封版便可。處理好後切換到develop分支開發就能夠了。固然最好仍是把剛纔在master修改bug的代碼merge到當前的develop分支上。
還有一點使用技巧,也就是git stash
的靈活使用。當咱們正在當前分支編代碼,忽然某人中斷你的思路,提出某功能需修改且比較緊急,更煩人的是當前的代碼已經開始編寫,並且改動了好多文件,重要的是不想中途進行git commit提交,此時git stash就發揮做用了。
git stash // 暫時隱藏,以後就能夠正常切換分支了,當前的修改內容只是保存而且隱藏起來 git stash pop // 回覆到以前的工做狀態,默認pop的是最近的一條,這樣就能夠愉快的繼續編寫當時被中斷的代碼了
git stash能夠執行屢次,咱們可使用git stash list
命令來查看清單。固然還有好多命令,可使用git stash --help
來查看。
其實關於git stash
還有一個使用技巧,那就是若是當前修改了衆多文件,忽然又不想改了,想恢復到原來的樣子。那麼可能你會使用git checkout
命令來取消全部的更改,可是有種狀況並很差使,由於你除了修改文件,還添加了一些文件或則拖入了一些文件到工程目錄下,此時使用git checkout
而後再使用git status
查看的時候,會發現工做區多出了還沒有識別的文件(處於等待add的狀態)。在這個時候,使用git stash
是再好不過了,由於這樣將全部git控制下的文件,包括以前的目錄,所有還原到以前的狀態(也就是說git stash
把新增的文件也暫時隱藏起來)。若是想完全刪掉,那就再把全部stash的列表清空吧,直接git stash list
看下,而後執行git stash clear
。
還有一點,關於在Git服務器上刪除分支,本地使用git branch -a
依舊能夠看到被刪除的分支。比如你的代碼託管到了開源中國(OSChina),你經過網站,在線刪除分支,就會出現這種問題,稍微有點強迫症的天然受不了。此時能夠使用 git fetch -p
使fetch以後刪除沒有與遠程分支對應的本地分支。 固然也能夠經過查看遠程分支。使用命令git remote show origin
顯示以下:
YJTSuper:yjtim super$ git remote show origin * remote origin Fetch URL: git@git.oschina.net:lingsui/yjtim.git Push URL: git@git.oschina.net:lingsui/yjtim.git HEAD branch: master Remote branches: dev tracked im tracked master tracked proV2.3.0 tracked refs/remotes/origin/test stale (use 'git remote prune' to remove) Local branches configured for 'git pull': master merges with remote master show merges with remote master Local refs configured for 'git push': dev pushes to dev (up to date) im pushes to im (fast-forwardable) master pushes to master (up to date) proV2.3.0 pushes to proV2.3.0 (up to date)
咱們能夠看到分支origin/test 已通過期(stale),可使用命令git remote prune origin
一樣能夠處理。
若是想要切換遠程地址,千萬不要從新再初始化一個git代碼倉庫,使用git add從新添加。這樣的好處只有一點,全部歷史提交信息所有清空,再也不保留,git clone
時文件變小,理所固然,歷史記錄所有清空了麼。若是肯定不保留,也建議這樣作,關鍵時,誰寫的代碼更改了哪些東西很重要呀,仍是選擇保留吧。使用下面的方法,一行代碼搞定,而且保留了全部的commit記錄。
git remote -v // 查看遠程地址 git remote set-url origin https://git.oschina.net/HaiShengHuo/xxx.git // 更換遠程地址, 新建一個項目不添加任何文件 在本地直接push便可
方法一
舉例:
git diff 56fd41f2 56c8822c
git reflog
或則 git log --oneline
注意:這裏的 git reflog是包含全部分支操做(切換,刪除等)以及全部提交記錄,而git log僅僅是當前分支下的提交,不存在分支操做記錄。這一點在版本回退中應該特別注意,使用git reset時,儘可能使用git log.
首先要列出最近的提交記錄。
git log 56c8822c --name-status
找到對應版本號,執行後能夠看到本次提交以前的全部修改文件。
git diff HEAD@{3} HEAD@{4}
具體某次修改的內容(具體某次提交的內容和上次提交的內容進行比較)。
git diff 56fd41f2 56c8822c
固然也能夠這樣,使用版本號,效果同樣。
這樣假如以前修改過具體某些內容,之後還須要修改的話,找起來真的很方便。好比說某些bug很久以後才發現,又要回頭去修改,而修改總要找到對應的代碼進行修改吧。那麼經過這樣幾行命令就定位到具體文件和位置了,方便且節省時間。找代碼有技巧,可是"找"終究仍是很浪費時間。這也告訴咱們,commit 提交命令很重要,須要認真寫,不可爲了省事而亂寫。
方法二
直接使用git log 4e732dee -p
或則 git log -p 4e732dee
其中4e732dee爲版本哈希值,會顯示以前全部歷史commit更改的具體內容
方法三
git log --stat 4e732dee
或者git log 4e732dee --stat
查看簡潔文件變化,會顯示以前全部歷史commit更改的文件以及代碼增刪情況
查看本地修改的內容,還沒有進行commit
直接使用git diff --stat
git checkout . 清空全部更改 如下命令是咱們在項目中已經添加了.gitignore 可是中途忽然不想再跟蹤某文件 此時發現簡單的在.gitignore文件中添加要忽略的文件是不起效的,由於該文件已 經被track,咱們還須要將其狀態改成 未track(其實只需刪除暫緩區文件而後將操做體檢便可) git rm --cached Podfile.lock 將Podfile.lock從暫緩區刪除,再也不跟蹤
備份的話好一些,每次新的版本打一次tag,查看起來仍是挺方便。不過不用也行。我不愛使用這個。
git tag -a WeChat1.0 -m "version 1.0" :給版本打上標籤 git tag : 查看全部的標籤 git push origin WeChat1.0 : 將WeChat1.0 push 到默認分支
git reset --hard HEAD // 沒有提交的狀況下進行版本回退 git reset --hard HEAD^ // 回退到上一個版本 git reset --hard HEAD^^ // 回退到指定回退到某個版本 git reset --hard 版本號(至少前5位) // 回退到前幾個版本 git reset --hard~1 git revert c011eb3c20ba6fb38cc94fe5a8dda366a3990c61 // 注意該行命令 reset和revert有本質區別
注意:開發中通常託管代碼到遠程代碼倉庫,好比Github或則OSChina,加入本地已經使用git push到遠程代碼倉庫,在本地使用git reset
回退再push明顯是不可行的。由於git reset直接回退到歷史中的某個Hash值,可是使用git revert就不同了。git revert將做爲一次新提交(新的Hash值)來撤銷某次更改而不是歷史中的某個Hash值,此時再push到遠程代碼倉庫情理之中。固然你嫌麻煩也可使用git reset 後再使用git push -f
強制 push 到遠程代碼倉庫。
reset和revert區別:
• git reset 3c7bdf2 回到3c7bdf2,3c7bdf2以前的commit都會保留,3c7bdf2以後的修改都會被退回到暫存區。
• git revert 3c7bdf2 生成一個新的commit來撤銷3c7bdf2,這次提交以前的commit都會被保留。
能夠參考博客git revert和git reset的區別
想回顧下提交歷史,可使用 git log 命令,其中有個--pretty 參數能夠配置
git log --pretty=oneline git log --pretty=format:"%h - %an, %ar : %s" git log --pretty=format:"%h %s" --graph git log --since=2.weeks
本身能夠試一下,這裏附上參數說明,本身能夠隨意配置,固然log字體顏色也都是能夠修改的,再也不細述。
%H // 提交對象(commit)的完整哈希字串 %h 提交對象的簡短哈希字串 %T // 樹對象(tree)的完整哈希字串 %t // 樹對象的簡短哈希字串 %P // 父對象(parent)的完整哈希字串 %p 父對象的簡短哈希字串 %an // 做者(author)的名字 %ae // 做者的電子郵件地址 %ad // 做者修訂日期(能夠用 -date= 選項定製格式) %ar 做者修訂日期,按多久之前的方式顯示 %cn // 提交者(committer)的名字 %ce // 提交者的電子郵件地址 %cd // 提交日期 %cr // 提交日期,按多久之前的方式顯示 %s 提交說明
時間和提交者過濾
-(n) // 僅顯示最近的 n 條提交。 --since, --after // 僅顯示指定時間以後的提交。 --until, --before // 僅顯示指定時間以前的提交。 --author // 僅顯示指定做者相關的提交。 --committer // 僅顯示指定提交者相關提交。
你必定奇怪_做者(author)_和_提交者(committer)_之間究竟有何差異,其實做者指的是實際做出修改 的人,提交者指的是最後將此工做成果提交到倉庫的人。因此,當你爲某個項目發去補丁,而後某個核心成員將你的補丁併入項目時,你就是做者,而那個核心成員就是提交者。咱們會在第五章再詳細介紹二者之間的細緻差異。
文件 .gitignore 的格式規範以下:
• 全部空行或者以註釋符號 # 開頭的行都會被 Git 忽略。
• 可使用標準的 glob 模式匹配。
• 匹配模式最後跟反斜槓(/)說明要忽略的是目錄。
• 要忽略指定模式之外的文件或目錄,能夠在模式前加上驚歎號(!)取反。
所謂的 glob 模式是指 shell 所使用的簡化了的正則表達式。星號(*)匹配零個或多個任意字符;[abc] 匹配 任何一個列在方括號中的字符(這個例子要麼匹配一個 a,要麼匹配一個 b,要麼匹配一個 c);問號(?) 只匹配一個任意字符;若是在方括號中使用短劃線分隔兩個字符,表示全部在這兩個字符範圍內的均可以匹配 (好比 [0-9] 表示匹配全部 0 到 9 的數字)。
# 此爲註釋 – 將被 Git 忽略 *.[oa] Git 忽略全部以 .o 或 .a 結尾的文件。通常這類對象文件和存檔文件都是編譯過程當中出現 的,咱們用不着跟蹤它們的版本 *~ Git 忽略全部以波浪符(~)結尾的文件,許多文本編輯軟件 (好比 Emacs)都用這樣的文件名保存副本 *.a # 忽略全部 .a 結尾的文件 !lib.a # 但 lib.a 除外 /TODO # 僅僅忽略項目根目錄下的 TODO 文件,不包括 subdir/TODO build/ # 忽略 build/ 目錄下的全部文件 doc/*.txt # 會忽略 doc/notes.txt 但不包括 doc/server/arch.txt
項目剛啓動時(好比要把項目搭建在OSChina上),創建git時推薦方案是,先在OSChina上創建一個全新的項目,而後拿到連接後clone到本地,而後在Git工做目錄創建文件夾和文件, 開始一個新的項目。這樣本地和遠程已經創建好了鏈接,隨後直接git add .
,而後git commit -m "init"
,最後push到OSChina就好。
固然,有時候咱們直接在本地初始化了git, 而後已將初始化了項目,想要與遠程創建連接,也是能夠的。可是通常不這樣作,既然本地已經有了git代碼倉庫,那麼隨後再在OSChina上創建一個項目也同時生成了一個git代碼倉庫,,兩則創建鏈接會有些問題, 最直接的問題就是, 本地的會直接覆蓋掉OSChina上的倉庫. 不過肯定不要OSChina初始化文件也能夠這樣作。
操做步驟:
首先給本地添加遠程地址 git remote add origin https://git.oschina.net/xxx/xxx.git
而後 git push origin master -f
其中-f
爲強制推送遠程(直接push會被拒, 由於遠程已有一個代碼倉庫)
這樣就能夠了(遠程OSChina初始化文件已經被覆蓋)。
修改已經提交log信息:git commit --amend
( --amend: amend previous commit)