git整理
git特點:
1- 直接記錄快照,而非差異比較。以快照流的方式對待數據。對所有文件制作一個快照,保存這個快照的索引。如果文件沒有改動,git只是簡單保留一個鏈接指向之前存儲的文件。
2- 近乎所有操作都是本地執行,所以很快。例如svn如果斷網了就無法提交。
git三種狀態:
已提交(committed): 修改進入了本地數據庫中
已修改(modified): 修改了,但未保存到數據庫中
已暫存(staged): 將修改的文件快照放入暫存區等待提交
git三個區域概念(Working tree, staging area, and Git directory):
git倉庫目錄(.git):git用來存儲項目元數據和對象數據庫的地方
工作目錄: git倉庫的壓縮數據庫中提取出來的某個版本
暫存區: 是一個保存了待提交文件的信息,放在git倉庫目錄中
HEAD的概念:
HEAD就是指向當前分支的最后一個提交的指針
HEAD可以簡寫成@。 HEAD~N表示順著自己這一脈(主線,git log --graph的左邊)上溯N個提交。 HEAD^表示上溯一個提交(等效于HEAD^1,這里的1表示主線,2表示被合并的提交<假設存在,git log --graph的右分支>), HEAD^^^表示主線上溯3個提交(等效于HEAD^^^1)。
獲取幫助:
git help <verb> #例如git help config
git <verb> --help
man git-<verb>
獲取一個倉庫(repository):
途徑1: 克隆遠程倉庫。 執行git clone <remote_repo> [directory]會從服務器倉庫上拉取源碼文件的所有版本
途徑2: 初始化一個本地倉庫。在有源碼的目錄中執行git init命令初始化一個倉庫,在目錄下會生成.git倉庫目錄。此時沒有任何文件被跟蹤
基本流程:
1-修改文件
2- git add <pathspec>... # 跟蹤<pathspec>指定的目錄或文件,也就是將它們納入版本控制。同時這些文件也進入了暫存區,等待提交。
3- git commit -m "初始化工程" #提交到本地git倉庫(也就修改的快照進入.git倉庫目錄)
注意: git add 命令是個多功能命令。1-可以用它開始跟蹤新文件; 2-把已跟蹤的文件放到暫存區; 3-還能用于合并時把有沖突的文件標記為已解決狀態...等功能。
git commit是將暫存區的快照提交到git倉庫
所以,執行git add后,修改的快照進入了暫存區待commit,如果此時又修改了文件,那么修改還在工作區中,需要再次git add才能將新的修改放入暫存區。
配置:
git配置文件可以使用git config命令來讀寫:
1. /etc/gitconfig 作用于系統級 git config 加 --system選項時讀寫此文件
2. ~/.gitconfig 或 ~/.config/git/config 作用于當前用戶 git config 加 --global選項時讀寫此文件
3. 當前倉庫目錄下的.git/config 作用于當前倉庫目錄對應的倉庫 工作目錄是該倉庫,且git config 不加參數時,讀寫此文件
每一個級別覆蓋上一級別的配置,所以 .git/config 的配置變量會覆蓋 /etc/gitconfig 中的配置變量。
重要命令:
git config --list #列出當前配置
git config <key> # 查看某項配置,例如 git config core.editor
git config <key> <value> # 修改某項配置,例如: git config --global core.editor vim
遠程倉庫操作:
git clone -b master https://a.cn/x/y.git # 只克隆master分支(對于很大的倉庫或網速很慢時挺有用的)
git remote -v # 查看遠程分支, 加-v參數不但顯示遠程倉庫短名字,還顯示它的長名字。 origin是git clone給遠程倉庫默認生成的短名字,長名字就是那串長長的url:https://a.com/foo/bar
git remote show origin # 查看遠程倉庫origin的所有遠程分支,這條命令可以查看本地分支跟蹤的是哪個遠程分支。(設置跟蹤參考下面git push -u的說明)
git branch -r # 列出遠程倉庫的所有分支以及遠程倉庫的默認分支是哪個(例如origin/HEAD->origin/master)。 “git ls-remote [遠程倉庫短名字]” 也是類似功能的命令
git remote add <遠程短名字> <url> # 關聯遠程倉庫(遠程倉庫存在的情況下才執行這條命令)。一個本地倉庫可以關聯多個遠程倉庫
git fetch origin dev:Dev # 從遠程倉庫origin下載數據,指定遠程倉庫的dev下載到對應本地倉庫Dev。語法: git fetch <遠程短名> [遠程分支][:本地分支] 直接執行git fetch會從默認追蹤的遠程分支下載
git pull # 是git fetch 和git merge的組合,參數指定方式參考上面的git fetch,我通常不適用git pull , 而是git fetch,然后再git merge。
git push origin LB:rb # 推送本地分支LB到遠程倉庫origin的rb分支。如果遠程倉庫origin沒有rb分支,則會在遠程origin下新建一個rb分支。如果直接執行git push那么就將本地分支推送到默認的遠程倉庫。如果本地倉庫關聯多個遠程倉庫,可以直接執行git push fooRep來指定將本地分支的變更推送到遠程倉庫fooRep下。如果推送的時候,提示沒有對應的上游分支可以增加-u選項(--set-upstream選項的縮寫)來建立對遠程分支的跟蹤,一旦使用-u來跟蹤遠程分支了,后面直接git push,不用跟其他參數了。這里我們執行git remote show origin就會看到本地的LB追蹤了遠程倉庫origin的rb分支
git push --all origin # 不管本地是否存在對應的origin遠程分支,都會將本地的所有分支都推送到遠程主機.
git rename origin gitee_foo # 把遠程倉庫名origin重命名為gitee_foo
git remote rm gitee_foo # 刪除遠程倉庫,也可以: git push origin --delete dev //假設遠程倉庫短名是origin
注意: 一般而言,dev、debug之類的分支是不需要推送到遠程倉庫的,一般是把這些分支合并到master分支后,推送master到遠程倉庫
分支操作:
git branch #查看分支 -v 或 -vv會顯示詳細信息
git branch --merged #查看所有已合并的分支, 同理 --nomerged查看所有未合并的分支
git branch <branchName> #創建分支
git branch <brName> <commitID> #以某個commitID來創建分支,非常有用
git checkout <branchName> #切換分支, 加-b參數標示創建并切換分支
git checkout origin/br0 #對于遠程clone下來的倉庫origin,切換到它的br0分支
git checkout -b <local-br> <remote>/<br> #創建一個與遠程分支remote/br相對應的本地分支 #針對特定標簽建分支
git checkout -b <brName> <tag> #針對特定標簽建分支
git merge <branchName> #合并分支到當前分支。 通常合并分支時,git一般使用”Fast forward”模式,禁用FF模式,需要加參數--no-ff ( 非FF合并后的歷史有分支,能看出來曾經做過合并,而FF看不出來)
git branch –d <branchName> #刪除分支-D表示強制
git branch -u <remote>/<brName> # 設置已有的本地分支來跟蹤一個剛剛拉取下來的遠程分支。 -u的長選項格式是--set-upstream-to。 此時@{u}或@{upstream}是<remote>/<brName>的快捷方式。所以git merge @{u}相當于git merge <remote>/<brName>
git push origin --delete branch1 #刪除遠程分支, 也可以寫成 git push origin :branch1
注意:master分支應該是非常穩定的,一般不在上面干活。在dev分支上干活,然后將穩定了的dev分支合并到master分支。團隊協作是各個成員將自己分支不斷合并到dev分支。
工作進度隱藏:
git stash save "stash_comment" #保存完成一半的工作, 此時git status提示是干凈的。
git stash list #列出已經隱藏的git棧
git stash apply stash@{N} #恢復到stash N
git stash clear #清除緩存的stash
git stash pop #將stash從git棧中彈出
git drop stash@{N} #刪除某個stash
git stash show stash@{N} #查看某個stash
比較差異:
git diff [<path>...] #使用默認對比工具來比較
git difftool [<path>...] #使用第三方工具比較差異, 例如BeyondCompare。關于將beyondCompare用于git,參考另一篇文章
git diff/difftool [file] #對比工作區和暫存區的差別(git add之前的)
git diff/difftool --staged [file] #對比暫存區和上次commit的差別
git diff/difftool <commit-id> <commit-id> #比較兩個版本(用commit-id指定)的差異
git diff/difftool HEAD [file] #比較工作區和最后一次commit的差異
git diff/difftool master ReadMe.txt #比較當前分支和master分支的ReadMe.txt文件的差異
git log dev ^master # 比較dev分支有,而master分支沒有的提交
git log master..dev # dev比master多了哪些提交? ..可以理解為from->to
git log master...dev # 不知道誰的提交多,只是想比較有哪些提交差異.加--left-right 參數可以顯示提交所屬的分支是哪個(例如git log --left-right master...dev 的<箭頭表示master的)
git whatchanged <file> # 查看某個文件的變更的詳細提交記錄,簡單記錄可以使用git log --pretty=oneline <file>。 查看完后,可以git show <commit-id> [file]或者git difftool <commit-id1> <commit-id2> [file]來查看具體變更
git blame <file> # 顯示文件的每一行最后變更的commit id
撤銷修改 / 版本回退 :
git checkout [commit-id] -- ReadMe.txt proto.c #從暫存區(如果指定了commit-id的話,則從該次提交id中恢復)中恢復文件到工作區。也就是執行add之前反悔了。這里的"--"表示選項的結束,如果沒有任何選項,可以省略 "--"
git reset --soft <commit-id> # HEAD移動到 commit-id 指定的那個提交(不是針對單個文件或目錄,所以參數中不能跟路徑),也可以用HEAD~N的方式來指定重置到前N個版本。
git reset --hard <commit-id> #HEAD、暫存區和工作區都重置到commit-id指定的提交(不是針對單個文件或目錄,所以參數不帶路徑)。這個有點危險,如果誤操作,只能通過git reflog 查一下歷史操作的commit-id才能恢復
git reset --mixed <commit-id> [path] #HEAD、暫存區都重置到commit-id指定的提交,工作區不變, --mixed是默認選項,參數可以帶路徑(可以針對單個文件或目錄執行重置)
我測試的結果: --soft是只重置HEAD指針, 工作區和暫存區不變。 不能帶路徑。
--hard是HEAD、暫存區和工作區都重置(危險操作) 。不能帶路徑。
--mixed是重置HEAD和暫存區(默認參數)。 工作區不變。可以帶路徑,但是帶路徑會有warning
git revert <commit-id> #revert和reset的區別是,revert是將舊版本變成一個新的提交. 可以revert常規commit也可以revert分支合并的commit, 后者需要-m <1|2> 來指定保留哪個分支,1表示當前分支
例如下面revert合并的commit:
--A------B-----C------M----X----Y(revert)
\-----a-----b-------/
如果當前處于上面分支X狀態(此時上面分支有AaBbCX的提交), 如果Y處執行git revert -m 1 M的CommitID 則上面的分支只有ABCX的提交,(也就是丟棄了ab的提交)
如果Y處執行git revert -m 2 M的CommitID 則上面的分支只有AabX的提交(也就是丟棄了BC的提交). 注意這條命令是在Y處執行,而非上一條命令執行完后執行
revert注意的地方,可以到https://segmentfault.com/a/1190000012897697看例子
修改已完成的提交(commit后,可以多次修改該commit):
例如:
git add proto.c
git commit -m '修正proto.c的bug' #突然發現漏了proto.h和gen_id.c,忘了移除tmp.txt,于是執行:
git add proto.h gen_id.c
git rm tmp.txt
git commit --amend '修正proto.c和gen_id.c的bug' #此時git log --stat 只能看到修改后的提交
刪除文件:
git rm <file> ; git commit -m 'xxxx' #刪除工作區和暫存區文件,并且提交
git rm --cached <file> ; git commit -m 'xxx' #刪除暫存區文件,并提交。此時工作區文件還在,這是版本庫不再跟蹤它了。
查看提交歷史:
git log -3 #查看最近三次提交
git log --after/before='1999-09-09' #查看1999-09-09前/后的提交。 指定時間可以使用類似"1year"、“8days”之類的。 --after等同--since,--before等同于--until
git log --author/--committer=<name> #按作者/提交者查看,注意作者和提交者的區別
git log --grep=<pattern> #按正則搜索注釋
git log hello.c # 查看修改了hello.c的commit,查看針對某個路徑的commit,如果有很多選項時,可以使用 -- 標示選項結束: liru
git log --graph #形象顯示commit log
git log --stat #顯示commit 歷史涉及的文件
git show --stat [commitID] #顯示某個commit涉及的文件變更
git log --pretty=format:%h" "%an" "%s #顯示短hash、作者、注釋(空格分隔)。更多%號格式參考git lot help
git log --pretty=oneline #單行顯示commit歷史
git log -- server.c # 查看 server.c 的提交記錄
標簽:
git tag # 查看已有標簽
git tag -l 'v1.*' # -l是小寫的L,只列出 v1.* 的版本號, 批配處v1.0 v1.1 v1.2...
git tag v1.0 # 打一個輕量級標簽, 輕量級標簽是對某個提交的引用。不指明commit-id的情況下,默認引用HEAD
git tag -a v2.0 -m '這是v2.0附注標簽的說明' # 打上一個附注標簽,-a是的a是annoted的首字母。附注標簽是一個倉庫中的獨立對象。它有自身的校驗和信息,以及標簽說明。
git tag -s v2.1 -m '這是v2.0附注標簽的說明' # 打上一個GPG簽名的附注標簽。 -s是signed的首字母. 使用git show v2.1會看到GPG簽名
git show v2.0 # 查看標簽v2.0的詳情
git tag -v v2.1 # 驗證含有GPG簽名的標簽(keyring中需要有GPG公鑰)
git tag v1.2 df1796d00a50a5567fac0b7d689402942943e01d # 給df1796d00a50a5567fac0b7d689402942943e01d這個提交打輕量標簽
git tag -a v2.3 -m '忘記打標簽了' # 給df1796d00a50a5567fac0b7d689402942943e01d這個提交打附注標簽
git push origin v2.3 # 將標簽推送到遠程倉庫origin。(默認情況下git push是不會推送標簽的,所以需要專門推送標簽,標簽才能體現在遠程倉庫上)
git push origin --tags # 將所有標簽推送到遠程倉庫
------------------------------------ 注意力分割線 --------------------------------------
我做的小測試:
mkdir x; cd x
git init
git remote add ts2 https://gitee.com/xxx/test2
git fetch ts2 # 抓取的副本在本地的工作空間是看不到文件的。ps: 可以git fetch ts2 dev 只fetch dev分支
git checkout -b dev ts2/dev #此時就可以看到ts2/dev對應的的本地dev分支了。此時執行 ls -l 就能看到工作空間有文件了
上面的最后兩步可以這么玩:
git fetch ts2 dev:dev #fetch ts2的dev分支,并在本地創建dev分支
git checkout dev #切換到本地dev分支,此時就能看到遠程的ts2/dev分支在本地對應dev分支的工作空間文件了
git checkout -b dev2 #建立本地dev2分支
git push -u ts2 dev2 #把本地分支dev2 推送到遠程分支ts2上,結果在gitee服務器上看到了dev2分支
浙公網安備 33010602011771號