git學習練習總資源連接: https://try.github.io/ (練習已通,有document)javascript
本沙盒遊戲教學:https://learngitbranching.js.org/?demo java
自由沙盒模擬網頁 : http://git-school.github.io/visualizing-git/git
好的譯文: https://github.com/geeeeeeeeek/git-recipes/wikigithub
什麼是git?
一個分佈式的源代碼庫。管理Linux內核源代碼。正則表達式
git已快照形式保存和處理內容,每個提交都是一次快照。git能夠在快照之間回滾。vim
一個節點表明一個commit.api
*表明當前分支的最後一次提交:HEAD緩存
master是主幹。安全
其餘名字是分支。 併發
git merge :用於合併分支的代碼。
git rebase : 線性合併分支:
git rebase [-i] [目標] [移動記錄]
git rebaes [目標] #省略[要移動的記錄],則爲當前分支的全部commit。
假如當前分支是bugFix:
- git rebase master. 這樣bugFix分支就至關於在master的基礎上新增的代碼了。
- git checkout master 回到master
- git rebase bugFix, master和bugFix的代碼都同樣了。
HEAD:
是一個對當前檢出記錄的符號引用 -- 也就是指向你正在其基礎上進行工做的提交記錄
它老是指向當前分支上最後一次的提交記錄。 大多數提交樹的git命令都是從改變HEAD的指向開始的。
⚠️,後面章節講的遠程分支 origin/master是例外
HEAD 一般是指向分支名的(如bugFix)。在你提交commit時,改變了分支的狀態,這一變化經過HEAD變得可見。
分離的HEAD:
讓它指向某個具體的提交記錄(hash值)而不是分支名。
git checkout <hash>
git checkout命令本質就是移動HEAD,到目標commit點, 而後更新工做目錄以匹配這個commit點。
由於這個操做會overwrite local changes,致使改變的文件丟失,因此Git強迫你先commit或stash工做目錄中的改變的文件。
⮀ git checkout master error: Your local changes to the following files would be overwritten by checkout: app/assets/javascripts/search.js Please commit your changes or stash them before you switch branches.
關於git stash (具體工做原理和所有的知識見連接文章)
會把還沒有加入stage的文件和statge中的文件保存(就是未commited的文件),以便在以後使用。
以後能夠revert them from your working copy.
如今能夠進入任何其餘操做,如建立新commits, 轉變分支,執行其餘git操做了。
⚠️stash是本地的。當你push的時候,stash不會被傳輸。
Re-applying your stashed changes:
$ git stash pop
另外使用git stash apply, 能夠reapply the changes的同時在stash中保留它們。這在爲多個分支應用時有用。
⚠️:默認Git不會stash 未tracked文件和ignored files。
相對引用
經過指定提交記錄hash值的方式在Git中移動不方便操做。
必須用到git log, 並且hash值很是長。
所以能夠只使用前幾個字符表明一個提交記錄 , 即「相對引用」。
^ 代表👆向上移動一個commit記錄。
~2 表明向上移動2個提交記錄,~5,表明移動5個提交記錄
使用git checkout HEAD^, 就表明向上移動一次。
強制修改分支位置--移動分支
git branch -f master HEAD~3
表明把master向上移動三個提交節點,即第3個father note
-f :表明--force, force creation, move/rename, deletion
撤銷變動
- git reset
- git revert
Command | Scope | Common use cases |
---|---|---|
git reset |
Commit-level | Discard commits in a private branch or throw away uncommited changes |
git reset |
File-level | Unstage a file |
git checkout |
Commit-level | Switch between branches or inspect old snapshots |
git checkout |
File-level | Discard changes in the working directory |
git revert |
Commit-level | Undo commits in a public branch |
git revert |
File-level | (N/A) |
Git Reset
原文:
takes a specified commit and resets the "three trees" to match
the state of the repository at that specified commit.
Three Trees
把分支回退指定個數的commit記錄,來實現撤銷改動。至關於使用時間機器回退到過去開發的階段。(撤銷的提交記錄還存在,只是未加入stage暫存區)
⚠️ 這條命令對團隊使用的遠程分支無效!
例子:
# 從當前工做目錄回退一個commit. git reset HEAD~ # 從當前工做目錄回退2個commit git reset HEAD~2
⚠️被回退的2個commit提交變成懸掛提交。下次Git執行垃圾回收時,這兩個提交會被刪除!
git reset能夠把stage中的文件拿出stage。git reset </filename>
⭠ autoquery± ⮀ git reset README.md Unstaged changes after reset: M README.md
三個模式選項
- --soft -stage緩存區和工做目錄都不會改變。
- --mixed默認選項。✅
- 緩衝區和你指定的提交同步a(在提交a後,加入緩存區的文件被拿出來了)
- 工做目錄不受影響(在提交a後對工做目錄中的文件進行的改變被保留!)。
- --hard -緩存區和工做目錄都被更新,以匹配指定的commit點a。
- 至關於回退到剛剛提交完a的狀態!
- 在提交a後的操做所有刪除,包括工做目錄中對文件的改變,至關於時光倒流
能夠認爲這個三個模式是對a git reset操做的做用域的定義!
通常使用默認的--mixed。
Git Revert (點擊見詳細)
專門用於撤銷遠程提交記錄。但本質上是新增一個commit記錄,但更改了code,去掉了以前那個commit記錄中的變動代碼。
c0-c1-c2(->c3)
git revert C1, 結果是新增了一個c3.
c3是c1的反轉操做,即c1中變化的代碼,在c3中被撤銷了。
例如, 你追蹤一個bug並發現它是在某個commit點a內增長的一個變量。你無需手動進入這個commit點,刪除這個變量,而後再committing一個新的snapshot,你直接使用git revert a命令自動爲你作上面的事情。
⚠️:若是你revert提交點c1, 可是c2中有對c1中變化的代碼的進一步修改,你不能使用git revert c1, 系統會提示你衝突,須要先搞定衝突代碼。
How it works
git revert命令用於撤銷一個倉庫的commit歷史中的某個變化。
其餘撤銷命令如git checkout 和 git reset,移動HEAD和branch ref pointers到一個指定的commit點。
Git revert也take a specified commit,可是,git revert不會移動ref pointers到這個commit點。
而是執行一個反轉操做,反轉那個commit的改變的代碼,並建立一個新的「revert commit」。
最後ref pointers 會更新,指向這個新的"revert commit", 讓這個commit成爲分支的端點tip。
選項
-e --edit (默認選項) 打開系統的編輯器,提示你編輯commit信息。
-n --no-commit (一個特別的選項)使用它,git revert不會建立新的commit, 只會在working directory反轉變化的代碼,並在緩存中加入反轉代碼後的文件。一句話理解:須要你手動提交!其餘沒變化。
和get reset的比較:
1:不會改變歷史記錄。
- 基於這個優勢git revert能夠在一個public branch上使用,
- 而git reset最好在我的的branch上進行操做。
2:git revert只改變單一的提交點。而git reset會從當前提交往回退(回到過去)。
能夠這麼理解:
- git revert是撤銷committed changes的工具
⚠️git revert也和git checkout相似,在revert操做期間會重寫工做目錄中的文件。因此它會要求你commit/stash changes。
File-level Operations
git reset和git checkout命令能夠接收一個file path做爲參數。這會強制把它們的操做限制到一個單一文件。
例子:
git reset HEAD~2 foo.py
當引用一個文件路徑時, git reset更新緩存區以匹配特定commit點的版本的指定文件。
👆的命令會取得在提早兩個提交點的版本的foo.py文件,並把它放入Staged files緩存區中。
可是工做區出現對應的文件的unstaged 狀態:
⭠ autoquery ⮀ git reset head~3 README.md Unstaged changes after reset: M README.md ⭠ autoquery± ⮀ git status On branch autoquery Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: README.md Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: README.md
個人理解:
- 執行git reset head~2 foo.py命令
- 取得的文件foo.py放入緩存區,但程序發現取出的文件和當前的foo.py文件的內容不一致,因此出現👆的狀況。
另外,若是我修改了上一個commit點的foo.py文件並放入緩存區。
而後又執行git reset head~2 foo.py。
程序發現取出的文件和當前修改的foo.py文件的內容不一致,會把當前修改的foo.py文件放入工做目錄區。
⚠️複雜的操做盡可能配合可視輔助工具Sourcetree
Git checkout head File命令
它會把file放入working directory工做目錄。
⚠️個人實際操做是使用此條命令後,取出的文件會放入到緩存區!個人理解:
- git checkout head File
- 程序自動把取出的文件執行git add命令,放入緩存區。
git checkout HEAD~2 foo.py
⚠️此時git checkout命令不會移動HEAD標籤,即你不會切換分支!
⚠️:若是把這個變化git commit就至關於直接執行一個git revert命令了!!
總體提交記錄
當開發人員說,我想把這個commit放到這裏,那個commit放到剛纔的提交的後面,就可使用:
將一些commit複製到當前位置HEAD下面的話,使用這個命令:
git cherry-pick <提交號>...
⚠️我的理解,是每一個commit應該是一個獨立的模塊。
⚠️,前提是你知道你想要的commit記錄的hash值,才行。若是不知道,往下看⬇️
交互式的rebase
git rebase -i HEAD~3
指帶參數 --interactive的rebase命令,簡寫-i
rebase會打開一個UI界面:
git 技巧 1: 本地stage提交
假如當前在bugFix分支(C4), 而master在C1, 但願只把C4合併到master上。
c1(master)-c2-c3-c4(bigFix*)
第一種辦法:
git checkout master#HEAD回到master
git cherry-pick C4#ok了
第二種辦法:
git rebase -i C1 #會打開UI界面,選擇C4,去掉C2, C3 ,肯定。
#這時buFix是當前分支它包含C4直接放到了C1下面。
git checkout master #回到master, 由於master的數據比較舊
git merge bugFix#合併分支。
git 技巧 2:
git commit --amend
修復最新提交的便捷方式。做用是:
將緩存的修改和以前的commit合併到一塊兒,生成一個新的提交併替換掉原來的提交。
這是從寫項目歷史的命令。
討論:
倉促的提交在你平常開發過程當中時常會發生。很容易就忘記了緩存一個文件或者弄錯了提交信息的格式。--amend
標記是修復這些小意外的便捷方式。
注意:⚠️
不要修復public commit! 永遠不要重設和其餘開發者共享的commit。
修復也同樣:永遠不要修復一個已經推送到公共倉庫的commit!
https://learngitbranching.js.org/?demo (點擊連接看演示, 而後選擇第四行第2個按鈕)
c1(master)—c2(newImage)—c3(caption*)
設計師須要在c2上調整圖片格式,如何作?
|再問:
設計師幹嗎不在C3上調整?
答:猜想C2提交記錄是針對圖片的設計,c3提交記錄是針對其餘設計。
假設:
git checkout newImage
git commit --amend
git checkout capiton
git merge newImage #這會致使新產生一條commit記錄。C4, 不符合線性的要求。
而使用:
# 調整C2, C3的位置,讓C2位於分支tip, 由於git commit --amend用於最新提交點。 git rebase -i C1 # 對C2進行修改 git commit --amend #再調整回原先的結構。 git rebase -i C1 #至關於,歷史commit記錄樹沒有發生變化。
#而後就能夠合併了
git checkout master
git checkout caption
⚠️,做者提示這可能會致使衝突:改用挑🍒,更快捷。cherry-pick
git checkout newImage
git commit --amend #這會出現一個分叉。
git checkout master
git cherry-pick C2' C3
標籤的做用:tag
給某個提交記錄一個標籤,相似⚓️。 用於重要版本的標記。
git tag V1 <hash>
若是不指定<hash>提交記錄,則標記到HEAD指向的位置,所以能夠寫兩條語法:
git checkout <hash>
git tag V1
Git Describe
用來找到最近的tag。幫助你在commit record的歷史中移動了屢次後找到方向。
git describe <ref>
<ref>是任何識別commit記錄的引用,不指定的話,則以當前HEAD位置爲準。
它的輸出結構:
<tag>_<numCommits>_g<hash>
解釋:
<tag>是離<ref>最近的標籤,
numCommits是表示和<ref>相差多少個commit記錄,
hash表示的是你所給定的<ref>所表示的提交記錄hash值的前幾位。
Pushing tags
默認,標籤不會自動推送上去。 --tags將你全部的本地標籤推送到遠程倉庫。
git push <remoteName> <TagName>
push all tags:
git push <remoteName> --tags
挑戰1:線性移動合併分支:git rebase (能夠看sandbox案例)
git rebase [-i] [目標] [要移動的記錄]
git rebaes [目標] #省略[要移動的記錄],則爲當前HEAD
挑戰2: 使用HEAD~和HEAD^2來移動HEAD的位置
相對引用的擴展:
^2表明第二個父引用記錄,能夠鏈式使用。
git checkout HEAD~^2~
表示上一個父記錄,而後再第二個父記錄,而後再上一個父記錄。
若是要在這裏創建一個新分支
git branch bugFix HEAD~^2~
挑戰3 ,git rebase 的再次使用。
HEAD也能夠作[目標]
git rebase [-i] [目標] [要移動的記錄]
remote repertory
簡單來講就是你的倉庫在其餘機器上的備份。
特色:
- 備份,恢復丟失數據
- 遠程讓代碼能夠社交化了!其餘人能夠爲你的代碼作貢獻。
git clone: 在本地建立一個遠程倉庫的拷貝
遠程跟蹤分支 remote-tracking branch
在本地倉庫多了一個名爲o/master的分支,這種類型的分支叫作 遠程跟蹤分支。
遠程跟蹤分支反應了遠程倉庫(在你上次和它通訊時)的狀態。
這有助於理解你本地的工做和公共工做的差異 -- 這是和別人分享工做成果最重要的一步。
特別的屬性:在你checkout時,自動進入分離的HEAD狀態。 緣由是Git要求,不能在遠程跟蹤分支上直接寫代碼,須要先在其餘地方寫好代碼後,更新到遠程倉庫對應的位置,遠程跟蹤分支纔會更新。
git checkout o/master;
git commit;
在有新的commit提交記錄後,o/master不會同步更新,會和HEAD分離。
⚠️,Head老是指向當前分支上最後一次的提交記錄,但這裏是例外。
o/master只有在遠程倉庫中對應的分支更新後,纔會更新。
格式:<remote name>/<branch name>
簡稱: o/
默認: remote name 是 origin
Git Fetch
從remote repertory得到數據。
當從遠程倉庫得到數據時, 遠程分支會自動更新,以反應最新的遠程倉庫。
git fetch會作的事情:
- 從遠程倉庫下載本地倉庫缺乏的commit記錄
- 更新遠程跟蹤分支 如 origin/feature_branch
- ⚠️不會更新你的master分支和其餘分支,也不會修改你磁盤上的文件。
- 所以,不是說git fetch後本地倉庫就和遠程倉庫同步了,這是❌的想法。
- git fetch只是單純的下載服務。
//取指定的分支或tags,下載全部須要的commits和文件 git fetch <repository> [<refspec>...] // 取全部remote branch git fetch --all [<options>]
// 試運行,就是一次彩排,看看會出現什麼結果
git fetch --dry-run
使用git fetch 同步遠程倉庫:
git fetch origin //會顯示咱們下載的branchs
//a1e8fb5..45e66a4 master -> origin/master
//a1e8fb5..9e8ab1c develop -> origin/develop
//* [new branch] some-feature -> origin/some-feature
若是想要查看上游master增長了什麼commit,能夠運行git log命令,並使用origin/master進行檢索:
git log --oneline master..origin/master
而後批准這些變化併合並它們到你的本地master分支:
git checkout master git log origin/master
//如今origin/master和master分支指向同一個commit點了,
//而且你和上游upstream 開發同步了。 git merge origin/master
Git Pull
下載下來後,把遠程分支合併到本地的方法:
- git cherry-pick o/master
- git rebase o/master
- git merge o/master
- 等等
git pull 就是直接一步完成2個命令。即git fetch和git merge的方便代碼。
等同於:git fetch 和git cherry-pick o/master
效果等同於:git fetch, git rebase master o/master, git rebase o/master master。但結構是線性的,由於使用git rebase是線性合併,o/master會指向新C3'
模擬團隊合做:下載。
- git fetch
- 在本地的master分支新增了commit
- git merge o/master, 合併遠程分支到本地的master。
Git Push
當本地倉庫被修改,須要執行git push操做來分享這個修改的代碼給team members:
git push <remote> <branch>
git push <remote> -all
若是不帶任何參數,會使用push.default的設置。他的默認值是正在使用的Git的版本,推送前最好檢查一下這個配置
偏離的困惑
假如週一你克隆了一個倉庫,而後開發某個新功能。到週五時,能夠提交到遠程倉庫了。可是,
這周你的同事寫了一堆代碼並修改了你還在使用的API。這些變更讓你的新開發的功能不可用。
但他已經提交推送到遠程倉庫了。你的工做變成了基於舊版本的代碼,已和遠程倉庫的最新的代碼不匹配了。
這時,Git不會容許你push,它會要求你先合併遠程最新的代碼,而後你才能分享你的工做。
這就是歷史偏移
須要:
- git fetch
- git rebae o/master#這裏可能你的新增功能,會失效,
- 你須要先修改代碼,而後commit。最後:
- git push
- git fetch#更新本地倉庫中的遠程分支
- git merge o/master#這會產生新的commit記錄。C4
- git push
--rebase選項:
git pull --rebase , 等同於用git rebase合併遠程分支,而默認是git merge
小結:工做流程: fetch, rebase/merge, push
強制push
Git 爲了防止你覆蓋中央倉庫的歷史,會拒絕你會致使非快速向前合併的推送請求。
--force
這個標記覆蓋了這個行爲,讓遠程倉庫的分支符合你的本地分支,刪除你上次 pull 以後可能的上游更改。
只有當你意識到你剛剛共享的提交不正確,並用 git commit --amend
或者交互式 rebase 修復以後,你才須要用到強制推送。
⚠️ 可是,你必須絕對肯定在你使用 --force
標記前你的同事們都沒有 pull 這些提交。
關於origin和它的周邊 --Git 遠程倉庫高級操做
推送push 主分支
大型項目,開發人員會在特性分支上工做,工做完成後只作一次集成。
但有些開發者只在master上push,pull。這樣master老是最新的,始終與遠程分支保持一致。
- 將特性分支集成到master上。
- push並更新遠程分支
爲何操做遠程分支不喜歡用merge, 見仁見智
- 喜歡乾淨的提交樹,用rebase
- 喜歡保留提交歷史的,用merge
遠程跟蹤分支:remote-tracking branches
Git 設置了master和o/master的關聯。
- pull時,commit記錄會被先下載到遠程分支,如:origin/master上,以後會再合併到master分支。
- push時,咱們把工做從master推到遠程倉庫中的master分支,同時更新遠程分支origin/master。
它們的關聯關係,是由"remote tracking" 屬性決定的。
master被設定爲跟蹤o/master。
當你克隆遠程倉庫時,Git會爲遠程倉庫的每一個分支都設定一個遠程分支,而後再在本地建立一個和遠程倉庫
中的分支同樣的本地分支。
克隆完成後,你會獲得本地分支,若是沒有就是空白。
這也解釋了在克隆時會看到下面的輸出:
local branch "master" set to track remote branch "o/master"
本地分支「master」設置跟蹤遠程分支 "o/master"
能夠指定"remote tracking" 屬性屬性
讓任意分支跟蹤o/master,而後該分支就會像master分支同樣獲得隱藏的push目的地和merge的目標。
這意味着你能夠在分支XXX上指向git push, 將工做推送到遠程倉庫的master分支上。
兩種設置方法:
- git checkout -b XXX o/master
- 若是已經有了XXX分支,則使用git branch -u o/master XXX
Git push的參數
默認Git經過當前checkout分支的屬性來肯定遠程倉庫和要push的目的地。
咱們也能夠明確指定push的參數:
git push <options> <remote-Repository-name> <place>
例子:
git push --set-upstream origin master:
把當前分支push, 並設置遠程倉庫origin爲upstream。
解釋:
這行代碼通常用在建立一個遠程連接並同步上傳數據:
⭠ master ⮀ git remote add origin https://github.com/xxxxxx/yyyyyy.git
//連接遠程倉庫origin.
⭠ master ⮀ git push -u origin master //Branch master set up to track remote branch master from origin. //本地分支master已經開始追蹤遠程倉庫origin的master分支!
<place> 詳細解釋:
能夠分解爲<localbranch-source>:<destination>
即本地分支source,提交到遠程的另外一個分支destination。
<source>能夠是任何commit記錄位置。
若是<destination>在遠程倉庫中並不存在,則會在遠程倉庫中新建這個分支。
例子: git push origin localbranch:remotebranch
強制執行一次non-fast-forward merge
git push origin --force
⚠️,只有絕對肯定你正在作的才這麼用!
--all選項:推送全部分支:
git push <remote> --all
把標籤也推送上去,默認tags是不推送的。
git push <remote> --tags
Amended force push
git commit --amend 用於更新提交點,
本質就是把commit原先的改變和更新的內容存入一個新的commit點,扔掉原來的commit點。
可是如此,git push 會致使失敗,由於Git會發現Amended的commit點和遠程的commit點的內容是分離的。
此時須要使用--force選項來push一個amended commit!
# make changes to a repo and git add git commit --amend
#此時若是直接git push 會報告錯誤❌:
# ! [rejected] master -> master (non-fast-forward)
#須要使用--force
git push --force origin master
注意⚠️沒有同步更新remote-tracking branch!
當本地master分支和origin/master在一塊兒時,執行一次修改並commit,再git push, 這時origin/master不能同步更新,須要再次執行一次同步命名。git push ,git fetch均可以.
再次提交一次:
#修改一些文件並提交 git commit -am 'd'
能夠發現origin/master是處於4ebc769,
若是使用
git fetch
origin/master會更新到最新commit點。
刪除遠程分支或者tag
實際是推送一個空的分支替換遠程一個分支,至關於刪除遠程的這個分支。
//查看本地和遠程的分支 git branch --all //刪除本地分支 git branch -D branch_name //刪除遠程分支 git push origin origin :branch_name
Git remote
如⬆️代碼,git remote add origin <url>, 建立了本地和遠程倉庫origin的連接!
本地./.git/config文件儲存了這條記錄信息:
[core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true ignorecase = true precomposeunicode = true [remote "origin"] url = https://github.com/chentianwei411/practice.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "master"] remote = origin merge = refs/heads/master config (END)
git push -u origin master命令,則添加了[branch "master"]這個記錄。
全部git remote的操做,都會記錄在./.git/config文件中!
//執行git remote remove origin命令: //會斷開本地和遠程的鏈接:
//全部對跟蹤遠程的分支的設置和對遠程的配置設置被移除!
//結果: [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true ignorecase = true precomposeunicode = true [branch "master"] config (END)
再次使用git remote add origin <url>, 便可再鏈接上遠程倉庫:見./.git/config文件增長的代碼:
[branch "master"] [remote "origin"] url = https://github.com/chentianwei411/practice fetch = +refs/heads/*:refs/remotes/origin/*
git remote get-url --all origin
列出全部的遠程鏈接URLs.
git remote show <name>
這條命令會給出詳細的關於一個遠程鏈接的配置信息.
先git remote -v命令,查看。而後用git remtoe show <name>:
- show origin,
- show upstream ,
- shou other_users_repo
* remote origin Fetch URL: https://github.com/chentianwei411/practice Push URL: https://github.com/chentianwei411/practice HEAD branch: master Remote branch: master tracked Local ref configured for 'git push': master pushes to master (up to date)
git remote prune [--dry-run] origin
Deletes all stale remote-tracking branches under <name>.
刪除全部過時的遠程跟蹤分支, 即origin/xxx。
⚠️origin/xxx是存在於本地倉庫的,用於接收從遠程倉庫pull下來的數據。而後再merge到xxx分支。
These stale branches have already been removed from the remote repository
referenced by <name>, but are still locally available in "remotes/<name>".
這些過時的分支已經從遠程倉庫origin移除了,可是在本地倉庫仍然能夠在remotes/origin中找到:
⭠ master ⮀ git branch --all hotfix * master try remotes/origin/hotfix remotes/origin/master
--dry-run, 列出什麼分支會被剪除掉prune!
⮀ git remote prune --dry-run origin Pruning origin URL: https://github.com/chentianwei411/practice * [would prune] origin/hotfix
⮀ git remote prune origin Pruning origin URL: https://github.com/chentianwei411/practice * [pruned] origin/hotfix
再次使用git branch -a命令查詢全部分支,會發現remotes/origin/hotfix分支已經被刪除!!
顯示你的remotes
git remote
git remote -v
//-v選項,是verbose的意思 // 會列出標記的倉庫名字和相關信息,合做的倉庫URL. origin git@bitbucket.com:origin_user/reponame.git (fetch) origin git@bitbucket.com:origin_user/reponame.git (push) upstream https://bitbucket.com/upstream_user/reponame.git (fetch) upstream https://bitbucket.com/upstream_user/reponame.git (push) other_users_repo https://bitbucket.com/other_users_repo/reponame (fetch) other_users_repo https://bitbucket.com/other_users_repo/reponame (push)
添加遠程倉庫
當你添加了一個遠程倉庫。 你就可使用倉庫名字origin做爲<url>的簡寫,在其餘git命令上使用了。
這是由於./.git/config中記錄了url的信息:
[remote "origin"] url = https://github.com/chentianwei411/practice fetch = +refs/heads/*:refs/remotes/origin/*
Git Fetch origin <remotebranch-source>
和git push正相反,Git會查找remotebranch的本地遠程分支,並下載到本地的遠程分支。
這樣不會弄壞你的本地同名分支。
也能夠另外指定遠程倉庫的分支下載到哪一個本地的分支<destination>,
這樣會直接下載到某個分支上(不是遠程分支),⚠️開發人員不多這麼作,有風險。
若是本地沒有一個bar分支:
git fetch origin XX:bar的結果是, 會在本地自動建立一個bar分支,用來存儲遠程倉庫的commit信息。
若是隻有git fetch:
Git會下載遠程倉庫中全部的提交記錄到各個遠程分支...
省去<source> 的特殊用法:
刪除遠程倉庫的分支:
git push origin :foo #推送一個空的source到遠程倉庫,若是遠程倉庫有foo分支,這個分支將被刪除。
git fetch origin :bar #下載一個遠程倉庫沒有的空分支給本地,本地建立一個bar分支。
Git Pull參數
git pull origin foo 至關於:
git fetch origin foo; git merge o/foo
⚠️git pull origin master 會先下載到o/master, 而後merge到當前checkout檢出位置。
因此,使用git pull要不熟練,要不就別用。
git pull也可使用<source>:<destination>:
如:git pull origin master:foo
- 若是本地沒有foo, 先在本地建立一個foo分支,
- 而後從遠程倉庫下載master分支中的提交記錄並合併到foo分支
- 而後再merge到當前的檢出分支checkout分支上。
git clean
將未跟蹤的文件從你的工做目錄working directory中移除。
未跟蹤文件是新增到在工做目錄但還沒有添加到用 git add添加到repo's index。
它只是提供了一條捷徑,由於用 git status
查看哪些文件還未跟蹤而後手動移除它們也很方便。
和通常的 rm
命令同樣,git clean
是沒法撤消的,因此在刪除未跟蹤的文件以前想清楚,你是否真的要這麼作。
git clean
命令常常和 git reset --hard
一塊兒使用。
⚠️記住,reset 隻影響被跟蹤的文件,因此還須要git clean來清理未被跟蹤的文件。這個兩個命令相結合,你就能夠將工做目錄回到以前特定提交時的狀態。
# 先測試一下比較好-n ,--dry-run
git clean --dry-run
# 移除當前目錄下未被跟蹤的文件。-f(強制)標記是必需的。
# 不會刪除未跟蹤的目錄directory和.gitignore中的文件。 git clean -f # 移除未跟蹤的文件,但限制在某個路徑下 git clean -f <path> # 測試移除未跟蹤的目錄directory git clean -dn
# 移除未跟蹤的目錄
git clean -d
⚠️請牢記,和 git reset
--hard 同樣, git clean
是僅有的幾個能夠永久刪除提交的命令之一,因此要當心使用
栗子:
# 編輯了一些文件 # 新增了一些文件 # 『糟糕』 # 將跟蹤的文件回滾回去, 新增文件從head, index, 工做目錄中刪除了!!修改的文件恢復到以前的代碼!! git reset --hard # 移除未跟蹤的文件,先測試-n, -dn
git clean -n git clean -df
改變舊的或者多個commits: git rebase
rebase改基。從一個分支移動到另外一個分支,即改基。
移動或聯合一系列的提交點到一個新的base commit。實際是建立了一系列新的提交點。
效果和目的:
⚠️不要用在public commit。
- 讓你修改你的歷史, 而且可交互的改基容許你如此作而不留下雜亂的痕跡。
- 讓你在修改錯誤和重新定義你的任務後,仍然保持了一個乾淨,線性linear的程序歷史。
在真實的場景:
- 在主分支發現bug.。一個功能分支的功能由此壞掉。
- 開發者檢查主分支歷史git log。 由於clean history,開發者能快速的找出project的歷史。
- 開發者使用git log仍是不能識別出bug是什麼時候插入的introduced。因此他執行了git bisect
- 由於git history很乾淨, git bisect有一個refined set of commits 來比較。開發者快速的找到了這個插入bug的commit點。
例子:
當你在一個功能分支上進行開發時,主分支發現一個bug。
新增一個hotfix分支,用於fix bug。 在搞定bug後, 把bug分支合併到master。
你想要在你的功能分支上使用最新版本的主分支,但你想要保持你的功能分支的歷史乾淨,即好似你一直在最新版的master上開發功能分支。
# 開始新的功能分支 git checkout -b new-feature master # 編輯文件 git commit -a -m "Start developing a feature" #在 feature 分支開發了一半的時候,咱們意識到項目中有一個安全漏洞:---- # 基於master分支建立一個快速修復分支 git checkout -b hotfix master # 編輯文件 git commit -a -m "Fix security hole" # 合併回master git checkout master git merge hotfix git branch -d hotfix #將 hotfix 分支並回以後 master,咱們有了一個分叉的項目歷史。---- git checkout new-feature git rebase master #它將 new-feature 分支移到了 master 分支的末端, #而後在master上進行標準的快速向前合併了: git checkout master git merge new-feature
Rebasing的一個經常使用方式是:把upstream的變化集成到你的本地倉庫。
移動整個功能分支⬆️
git rebase -i <base>
用 -i
標記運行 git rebase
開始交互式 rebase。交互式 rebase 給你在過程當中修改單個提交的機會,而不是盲目地將全部提交都移到新的基上。你能夠移除、分割提交,更改提交的順序。
討論
交互式 rebase 給你了控制項目歷史的徹底掌控。它給了開發人員很大的自由,由於他們能夠提交一個「混亂」的歷史而只需專一於寫代碼,而後回去恢復乾淨。
大多數開發者喜歡在併入主代碼庫以前用交互式 rebase 來完善他們的 feature 分支。他們能夠將不重要的提交合在一塊兒,刪除不須要的,確保全部東西在提交到「正式」的項目歷史前都是整齊的。對其餘人來講,這個功能的開發看上去是由一系列精心安排的提交組成的。
執行:git rebase -i master 後出現vim的交互界面:
- 若是刪除全部pick,而後:wq保存退出,至關於取消rebase 這條命令。提示:Nothing to do
- 若是直接:q退出,不作任何修改,至關於執行了git rebase master命令。
- 提示:Successfully rebased and updated
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
若是隻pick 37c8c61,第一行。則提示成功:
Successfully rebased and updated refs/heads/b2.
![](http://static.javashuo.com/static/loading.gif)
git checkout master git merger b2
⚠️另外2個pick就被分支b2扔掉了!!
若是知道這2個commit的id,就能夠進入它們:
git chekcout xxxx
獲得提示信息:You are in 'detached HEAD' state. 你處於分離的HEAD!
能夠把這2個提交點合併到b2, 再把b2合併到master。
Rebasing的其餘幾個有趣的命令:
- edit
- reword -能夠重寫提交信息message
- squash -把當前的commit合併到上一個commit, 並提示你重寫提交message
- fixup -和squash同樣,不會重寫提交message.
edit 9a29b81
如可使用命令edit, 代替pick,當:wq保存退出vim後,提示
⭠ b3 ⮀ git rebase -i master Stopped at bb28411... change yangcheng You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
➦ bb28411 ⮀
而後你能夠在這個提交點上作修改,而後
➦ bb28411± ⮀ git add . ➦ bb28411± ⮀ git commit --amend [detached HEAD 36f12d6] change yangcheng Date: Sun Nov 25 11:45:44 2018 +0800 1 file changed, 1 insertion(+), 1 deletion(-) ➦ 36f12d6 ⮀ git rebase --continue Successfully rebased and updated refs/heads/b3. ⭠ b3 ⮀
squash 9a29b81
合併後放入新增的一個commit中。 這是rebase的主要功能!!
緣由:
大量的細小的改動,每一個改動都是一個提交。致使倉庫的history看起來很亂。
許多這樣的commit並無實際地給你的倉庫history增長任何價值
它們弄亂了blame(新舊版本的對比), make bisects take longer and make the history hard to navigate.
⭠ b4 ⮀ git rebase -i master
#此時進入vim編輯器。重寫提交message,而後:wq
[detached HEAD fac0e93] change content and guangzhou Date: Sun Nov 25 12:42:45 2018 +0800 2 files changed, 3 insertions(+), 1 deletion(-) Successfully rebased and updated refs/heads/b4.
而後:
git checkout master git merge b4
git branch -d b4
還有另外一種使用squash的方式:
git merge --squash <commit> 能夠把一個分支合併爲一個commit, 而後執行
git commit -m 'squash ...'
最後刪除分支b3: git branch -D b3
安全網:git reflog
Git保持對分支的tip的追蹤!這種機制叫作reflog(reference logs), 引用日誌。
Git 用引用日誌這種機制來記錄分支頂端的更新和其餘commit引用。
它容許你回到那些不被任何分支或標籤引用的commits。在重寫歷史後,reflog包含了分支舊狀態的信息,有須要的話你能夠回到這個狀態。
每次當你的分支tip被任何緣由所更新(包括切換分支,pulling in new changes, 重寫歷史或者僅僅是增長新的commits), 一個新的entry將被增長到reflog。
reflog提供了一張安全網,全部的分支頂端的變化都會被記錄。
另外, reflog提供了到期日。默認設置expiration time是90天。
用法
git reflog # 是git reflog show HEAD的簡寫
顯示:
fac0e93 HEAD@{0}: merge b4: Fast-forward 6f70a12 HEAD@{1}: checkout: moving from b4 to master fac0e93 HEAD@{2}: rebase -i (finish): returning to refs/heads/b4 fac0e93 HEAD@{3}: rebase -i (squash): change content and guangzhou 0481ff2 HEAD@{4}: rebase -i (pick): change content 6f70a12 HEAD@{5}: rebase -i (start): checkout master 3bc3338 HEAD@{6}: checkout: moving from master to b4 。。。
#fac0e96,第一行是最新的一次relog。
還能夠用:
git reflog --relative-date
fac0e93 HEAD@{3 hours ago}: merge b4: Fast-forward
6f70a12 HEAD@{3 hours ago}: checkout: moving from b4 to master
fac0e93 HEAD@{4 hours ago}: rebase -i (finish): returning to refs/heads/b4
...
#顯示相對如今,每條記錄發生的時間!
默認,git reflog會輸出HEAD ref。但也能夠顯示其餘ref。
如其餘分支,tags, remotes, Git stash均可以被引用。
引用中的語法格式:name@{qualifier}
得到所有的reflog:
git reflog show --all
查看具體某一個分支的引用:
⭠ b5 ⮀ git reflog show b5 //顯示: 905b28c b5@{0}: commit: change bj fac0e93 b5@{1}: branch: Created from HEAD (END)
若是使用過git stash命令儲存了緩存區的文件, 而且還未取出,則能夠用git reflog命令查看記錄:
git reflog stash
//顯示
bd1f5f7 stash@{0}: WIP on b5: 905b28c change bj
另外, 可使用git diff: Show changes between commits, commit and working tree
//git diff stash@{0} otherbranch@{0}
⭠ b5 ⮀ git diff stash@{0} b5@{0}
//顯示 diff --git a/beijing.txt b/beijing.txt index 7719339..6815264 100644 --- a/beijing.txt +++ b/beijing.txt @@ -1,3 +1,4 @@ +123 hahaha hello hello this is a beautiful city! (END)
通常用不到,使用圖形編輯器sourcetree,就可直觀的看一個commit的變化。
但用代碼,能夠有更豐富的細節設置,如加上一個到期的時間:
git diff master@{0} master@{1.day.ago}
具體可見git diff --help
恢復丟失的commits
Git 從不真地丟失任何東西,繼續執行了歷史重寫操做,如rebasing, commit amending。
git log的搜索功能很強大,有豐富的設置能夠查看各類狀況。
例如:
分支b5有4個commit,master有1個commit超過b5
⭠ b5 ⮀ git rebase -i master //進入vi編輯器。把第4行的pick改爲squash, :wq。 //terminal: [detached HEAD 55ce6f9] change one Date: Sun Nov 25 17:06:31 2018 +0800 2 files changed, 3 deletions(-) Successfully rebased and updated refs/heads/b5.
結果附加到master上的commit只有3個,最後一個是合併的commit
git log --pretty=oneline //也只能看到新增的3行log 55ce6f9c6f60d80f9b042f5f8a99556333d5854a change one 23bd2a5ead2b5a19d6bcc8667fbd23c636135264 change sth c4e32fb82669aa16ac60e2b9f8a1bc488a366be5 change bj
彷佛b5分支的最後2個commit,因爲squash,致使沒法找到了!其實否則:
使用git reflog命令,便可看到最新的ref日誌⬇️:
55ce6f9 HEAD@{0}: rebase -i (finish): returning to refs/heads/b5 55ce6f9 HEAD@{1}: rebase -i (squash): change one 07e7fbf HEAD@{2}: rebase -i (pick): 1 23bd2a5 HEAD@{3}: rebase -i (pick): change sth c4e32fb HEAD@{4}: rebase -i (pick): change bj eb80dcf HEAD@{5}: rebase -i (start): checkout master 1c226ea HEAD@{6}: checkout: moving from master to b5
能夠看到從start到finish的所有細節:一部瞭然!!
- 4行綠色是4個commit點
- 第2行操做的方式是squash。
若是想要恢復到執行git rebase以前,可使用:
git reset HEAD@{6}
保存改變的5個命令:
-
git add
-
git commit
-
git diff (比較commit的不一樣,能夠用可視化工具:sourcetree, 或者上遠程倉庫,在網頁上看。)
-
git stash(上面已經介紹,把staged和未staged的文件儲存起來)
-
.gitignore :這裏就介紹它。
.gitignore
Git從copy的行爲上分類: 把文件分紅3種類別:
- tracked -一個文件以前被staged or commited
- untracked -一個從未被staged or commited的文件,通常是新建的文件。
- ignored -不加入tracked的文件。
包括:
- 獨立緩存/packages
- build output directories:如 /bin, /out, /target
- compiled code: .pyc, .class文件
- 在運行時產生的文件: .log, .lock, .tmp
- 隱藏的系統文件: .DS_store, Thumbs.db
- 我的的IDE配置文件
通常能夠在~/.gitignore文件內查看倉庫中被忽略的文件:
當你有新的文件須要被忽略,.gitignore文件必須手動編輯和提交。
Global Git ignore rules
你須要本身創建.gitignore, 並設置core.excludesFile屬性。
$ touch ~/.gitignore $ git config --global core.excludesFile ~/.gitignore
Shared .gitignore files in your repository
一般,Git ignore rules被定義在倉庫根目錄的.gitignore文件中。
但也能夠在你的倉庫中的不一樣的目錄中定義多個.gitignore文件。
然而最簡單的方法仍是在根目錄建立.gitignor文件,由於它自己也是被版本控制的,當你push,就能夠和團隊共享。
Personal Git igonore rules
你也能夠定義我的的ignore模式,這在特殊的.git/info/exclude文件中。
它不會被版本控制!因此你能夠作一些私事!
如何忽略一個以前commit過的文件?
從倉庫刪除這個文件,而後增長一個.gitignore rule.
使用 --cached選項和git rm
git-rm - Remove files from the working tree and from the index. --cached: unstage and remove paths only from the index. 但會保留在working directory!
例子:
$ echo debug.log >> .gitignore $ git rm --cached debug.log //提示:rm 'debug.log' $ git commit -m "Start ignoring debug.log"
[master 6d51c73] Start ignoring debug.log
1 file changed, 1 deletion(-)
delete mode 100644 debug.log
.gitignore自身是被追蹤的!,須要git add .gitignore
Committing an ignored file
能夠強制一個被忽略的文件被提交到倉庫,使用-f選項便可:
$ cat .gitignore *.log $ git add -f debug.log $ git commit -m "Force adding debug.log"
固然,這不是一個明顯的,可讓團隊成員瞭解的方法:改成:
$ echo !debug.log >> .gitignore $ cat .gitignore *.log !debug.log $ git add debug.log $ git commit -m "Adding debug.log"
*.log表示全部帶.log後綴的文件都會被忽略,可是!dubug.log表示這個文件不會被忽略!
Stashing an ignored file
使用git stash回你歷史的存儲本地的變化,並在以後用git stash pop取回。
可是默認git stash忽略.gitignore中的文件。
使用--all選項,能夠stash 忽略的和未跟蹤的文件。
具體git stash 說明文檔見:https://www.atlassian.com/git/tutorials/git-stash/#stashing-untracked-or-ignored
Debugging .gitignore files
若是你有複雜的.gitignore模式或者有多個.gitignore文件,那麼想要知道一個文件爲什麼被忽略,就比較麻煩。
你可使用git check-ignore -v <file-name>來看什麼模式讓這個文件被忽略
git rm
用於把一個文件從a Git repository中移除(working directory和index, 或者只從index中移除)。
某種程度上能夠認爲是git add命令的相反命令。
git rm命令能夠用於移除單獨的文件或一組文件集合。
它的主要功能是從Git index中移除跟蹤的文件。
另外它也能同時從staging index和 working directory移除文件。
注意git rm 不會移除branches。
選項--cached:
這個選項的用途是取消文件的tracking,但保留這個文件在working directory!
案例見上.gitignore
如何undo git rm?
git rm命令須要執行git commit來生效。
git rm將更新staging index 和 working directory,
⚠️這些變化不會立刻產生效果,只有當新的commit被建立,這些變化纔會被添加到commit history中。
這就意味着git rm能夠恢復。(符合git 做爲時光機的原則)執行git reset HEAD
⚠️git rm只能用在當前current branch的文件!
爲何使用git rm來代替rm
當一個被跟蹤的文件被rm命令執行,Git倉庫會識別這個標準的殼命令rm。
Git倉庫會更新working directory來反應這個移除。但它不會更新staging index。
因此須要額外的git add命令,把這個移除的改變添加到staging index。
git rm 是一個方便的shorcut。它將同時更新working directory and the staging index 。
例子:取消文件shanghai.txt的跟蹤,並刪除這個文件。
⭠ master ⮀ git rm shanghai.txt 提示:rm 'shanghai.txt'
⭠ master± ⮀ git status 提示: On branch master Your branch is ahead of 'origin/master' by 4 commits. (use "git push" to publish your local commits) Changes to be committed: (use "git reset HEAD <file>..." to unstage) deleted: shanghai.txt #幫你使用了git add命令。
⚠️sourcetree,可使用discard,取消一個文件的修改或刪除。就是恢復操做!
等同命令:
git checkout <file>
git checkout -- <file> #加上-- 防止文件名和branch名字重複致使❌。
Unstage a file:
git reset HEAD <file>
git rm --cached <file> #刪除staging保留working direotry中的文件。
小節:
git rm --cache <file>用於再也不對某個文件進行tracking。以前commi過的文件,再也不跟蹤!
git clean -df 刪除未跟蹤的文件和目錄
git reset --hard 回滾跟蹤的文件,即恢復文件到未修改的狀態並再也不跟蹤,新增的文件則從工做目錄刪除。
git fetch 命令是如何與remote branches 合做的?(深刻分析)
Behind the scenes,在幕後, ./.git/objects目錄中,Git存儲了全部的commits, 包括local和remote。
~/practice/.git/objects ⮀ ⭠ master ⮀ ls 00 0d 1b 24 38 40 51 60 70 7e 8b 9b a8 bb c7 d4 e7 fd 02 11 1c 27 3b 43 55 65 76 81 90 9d ab bd c8 de eb info 04 15 1f 2d 3c 49 59 68 77 84 92 a1 ad be ce e0 f0 pack
經過使用branch refs(就是commit點的🆔), GIT讓分支commits清楚的分開。
遠程分支的Refs, 儲存在./.git/refs/remotes/
//👇兩條命令均可以查看 git branch -r git branch --all
本地分支的Refs,儲存在./.git/refs/heads/
~/practice/.git/refs/heads ⮀ ⭠ master ⮀ ls hotfix master try
less try能夠看到:
ad64e76234f1dee8ea19618d4f5964edbc20bd36 try (END)
git log
針對commit點的查詢和搜索功能:
//在屏幕上顯示幾行 git log -n <limit> //單行顯示,用於全局的概覽, 內容包括id和messages git log --oneline
// 顯示每一個commit的文件改變的概覽 git log --stat //使用-p選項,看每一個提交點的patch。比--stat更詳細的每一個commit的區別 git log -p
經過對message進行匹配來搜索,<pattern>能夠是字符串和正則表達式:
git log --grep="<pattern>"
看一個特定文件的提交歷史:
git log <file>
顯示一個相似sourcetree的結構:
git log --graph --decorate --oneline
⚠️,選項能夠一塊兒使用。
git blame
- The high-level function of
git blame
is the display of author metadata attached to specific committed lines in a file. This is used to explore the history of specific code and answer questions about what, how, and why the code was added to a repository.
用於比較commit點的歷史代碼。信息還包括添加代碼的做者名字,解釋。
合做者能夠查看這些歷史代碼。
通常使用UGI網頁,如在線git上查看這些歷史代碼。
例子:example
git clone https://kevzettler@bitbucket.org/kevzettler/git-blame-example.git cd git-blame-example
//使用git log查看全部commit信息。
git log
git blame只用於單獨的文件的歷史commit點的比較。須要提供file-path來輸出信息。
//輸出幫助信息 git blame //輸出文件的commit歷史: 即每一個commit的代碼的增減。 git blame README.md