Git先生的故事

Git先生是一位很出名的攝影專家,他的主要職責是用它強大的拍攝技術幫我們共享成果,共創(chuàng)未來。為此它準備了許許多多的工具來實現這樣的目標。下面我們就來看看Git先生的工作場所,和他為我們的一些痛點帶來了哪些解決方案吧。

初次見面

老板(用戶自己)新買了一塊地皮(創(chuàng)建了一個目錄),想聘請Git先生到此開設一個工作室來加快這個地皮的建設工作。老板用git init 招來了Git先生,Git先生在該目錄下生成一個.git目錄,用來作為自己的辦公室,辦公室用來記錄自己的工作日志和成果。

讓我們來從空中俯瞰下這塊新的地皮,和Git先生為它所設計的藍圖吧。

下面我們來解釋下,這幾個區(qū)域的作用:

Working Directory:Git先生的老板所買下的地皮,這個是實實在在物理層面的地皮,我們可以在上面種些花花草草,建點高樓大廈啥的。

Staging Area:Git先生攝影棚所在地,位置位于Git先生的辦公室。每當老板完成了某件名垂青史的偉事,他就會命令Git先生把自己這個階段所干的事情一五一十的搬到攝影棚拍照記錄下來。git add 就是把修改搬到攝影棚,git commit就是命令Git先生拍照,而拍完照后,攝影棚馬上會被打掃干凈。

Repository:Git先生辦公室的某個區(qū)域,專門用來存儲照片用的。

Remote:這是一塊云端區(qū)域,Git先生會在工作完一段時間后,就把自己的作品上傳上去。這樣做,一方面是用來保存自己的作品,以備意外發(fā)生,另一方面也是提供給其他有興趣的老板們一起做這個項目。

Go to work

一切準備妥當后,Git先生馬上就投入到了緊張的工作當中。老板首先就迫不及待的在地皮上上種了一朵花,然后馬上命令Git來拍照留念。

當然結果是失敗的,Git也很苦惱,自己已經把所有流程和老板說過一遍了,但老板還是會魯莽行事。然后Git先生又向老板耐心的解釋了一下針對Git目錄下某個修改的4種狀態(tài)。

Untracked/Tracked
Not Staged/Staged
比如你新建一個文件,它的狀態(tài)就是 Untracked 的,你不能對 Untracked 的文件進行任何Git操作,除了先使用git add 讓它先變?yōu)門racked 狀態(tài)。一個文件被Track后,以后的修改如果未用git add,那這個修改就叫Not Staged,需要add后,讓它變?yōu)镾taged才能進行Commit。

老板按照Git先生的說法又執(zhí)行了一遍,這次他成功了。Git又向老板說,你可以用git log來查看我已經拍過的照片。

老板學會這招后,又給這塊地皮創(chuàng)建了樹、草,并且也都分別讓Git先生拍了照片保存。

git log后我們看到了這三張照片,如果要查看詳情還可以使用git log -p。

導演注:Commit的id為對當前文件夾內容做SHA-1得來。

上點兒色吧

老板想把樹涂成紅色的,再給樹取個名字叫big tree。他記得Git先生告訴過他可以用git diff來查看自己所做的改動

看到了自己的修改后,老板滿意的點點頭,然后用git add .把它們都丟進了攝影棚。過后就出去忙其他事情了,回來后他發(fā)現自己忘記離開前做了啥事情了。此時他再用git diff查看,發(fā)現里面空空如也。老板憤怒的叫來了Git先生問他是咋回事。Git先生友善的解釋了原因。

git diff顯示了您當前修改和我辦公室中所記錄的最新一張照片之間的差異,但是您已經把這些改動都挪到我的攝影棚里了,git diff就沒法查看了,如果您想看我攝影棚里擺了哪些東西。你可以使用git diff --staged/cached哦”

老板按照Git先生所說,果然看到了他以前的修改記錄。

導演注:stage相關的命令一般都與Staging Area相關,git add 也可以寫成 git stage,這兩個命令是一樣的。

不過當老板看到他把樹設成了紅色,覺得不合理,想放棄這次修改。他要如何去做呢?

git reset <file> 把這個文件的修改從Staging Area中去除
git checkout -- <file> 放棄工作區(qū)文件的修改

導演注:這里使用 git checkout <file> 也行,之所以使用--,因為該命令與切換分支的命令一樣,萬一這個文件名和某個分支名重名,則git checkout <file>就變成切換分支了。

老板不禁感嘆,幸好自己沒有進行commit。Git先生告訴老板說,即使你commit了,也不用怕,我也有幾種解決方案。

一,放棄整張照片

git reset HEAD~1
HEAD表示指向最新那張照片的指針,~1表示要想起回退一張,此時我們有三種回退方式可選
--soft 表示只刪除照片,照片的修改恢復到Staging Area中
--mixed 不但刪除照片,也不恢復Staging Area中的狀態(tài)(不加選項時就為此中方法)
--hard 不但刪除照片,而且連工作區(qū)域的修改也被回退掉

二,我們再產生一張想放棄的照片的反修改的照片

git revert <commit-id> 產生此commit的反修改,并提交
此處commit-id不必是最新一次,可以是任意處的。

第一種方案適用于,你的commit還未push到云端的場景,第二種,如果你的修改已經push到云端,那么為了尊重歷史記錄,最好就是生成一個方向修改來回退錯誤部分,讓其他人知道歷史。

導演注:HEAD指針記錄了正在操作的節(jié)點的commit id,每個分支都有屬于自己的HEAD指針,并且只有一個

撥弄你的指針

老板經過上次的事件,發(fā)現自己可能會因為一時沖動做出一些錯誤的決定。就問Git先生是否有辦法把自己所有操作行為都記錄下來,而且還允許自己撤銷任何一種錯誤的操作。Git先生向老板解釋說:所有對HEAD指針的操作都會被記錄下來。

可以用git reflog查看到老板的所有HEAD操作

最上面,我們可以看到是老板徹底回退了給樹添加名字和顏色修改,執(zhí)行了git reset --hard HEAD~1,而如果老板突然又后悔了,想恢復添加名字和顏色的修改。那么我們就可以通過執(zhí)行git reset --hard HEAD@{1}來把操作回退到HEAD@{1}時,也就是加入名字和顏色那次commit。

這里的reset也有三個選項,--soft,--mixed,--hard,因為這里執(zhí)行的是恢復操作,所以這三個選項在這里的作用也需要反過來理解,hard自不用說,就是完全恢復到某個操作時的狀態(tài),而soft表示,雖然把HEAD指針撥到了某個操作時的狀態(tài),但在staging area中會產生可以讓恢復后的狀態(tài)重新修改回來的修改,就像物質與反物質那樣。mixed同理。

以上可能難以理解,這里我們再舉個應用場景來說明下:
我們知道git revert每次只能回退某個commit,那我們如何同時revert掉多個commit呢。針對這種場景我們就可以進行以下操作

 //撥動HEAD指針到5add0e9
git reset --hard 5add0e9    
//恢復到以前的commit處信息,并且在staging area生成了中反修改
git reset --soft HEAD@{1}   // 注意此處用了--soft
git commit -m 'revert to 5add0e9' 

多干點事

老板:地皮準備好了, 我們既要種花,種草,還要蓋個樓房啥的,種花要花幾天功夫,種草好又得花幾天。這幾樣事咱能不能一起干呢?干完了,分別拍個照片再合一塊豈不美哉。

Git先生:老板英明,針對這種情況我也早有準備。

git branch <branch-name> 創(chuàng)建一個分支,不加<branch-name>則為列出當前所有分支
git branch -d <branch-name> 刪除分支  -D 為強制刪除分支
git checkout <branch-name> 切換到該分支下
git merge <branch-name> 合并<branch-name>分支內容到本分支下

針對老板說的這種情況,我們只需要創(chuàng)建如下分支。然后分別在flower里種花,grass中種草,building中蓋樓,最后在master分支中把完成的照片merge過來就行。
? GitTestRepo git:(master) git branch flower
? GitTestRepo git:(master) git branch grass
? GitTestRepo git:(master) git branch building
? GitTestRepo git:(master) git branch
building
flower
grass
* master

導演注:分支前加*號的為當前工作分支

合作共贏

Git先生:報告老板,您這塊地皮很大,要是只有我們開發(fā),那得花上很久時間,何不把它開放出去,讓其他老板們一起進來把這塊蛋糕做大呢。

老板:好主意,我們要怎么做呢?

Git先生:一切交給我,不過因為地皮開放出去后,涉及到多方共同開發(fā)。有些注意事項還希望老板能聽我說道說道,否則危害甚大。

老板:請講請講!

如我們第一張圖所示,我們可以利用git push來把自己所有的照片上傳到云端,讓其他人也可以參與進來開發(fā)。既然是云端,那么首先我們就需要指明下這個云端地皮的地址是哪里。

git remote add origin https://github.com/CPPAlien/GitTestRepo.git
//這里一般用origin,當然你可以換成其他任何名,你也可以添加多個remote地址
git remote -v 可以用來查看所有云端地址信息
git push -u <remote> <branch>
git push --set-upstream <remote> <branch>
用這兩個命令來指明某個分支所對應到的remote地址。
如果不指定,你在執(zhí)行git push時需要明確寫出remote和branch。

因為是多人合作,所以就有可能別人在云端先與你提交了一些修改,而此時就需要進行git pull操作,把別人的修改拉取下來合并到本地。但直接git pull行為是不太安全的,因為它會直接產生merge行為,可能會導致你本地數據錯亂。所以我們一般用git fetch,正確流程如下

git fetch origin master 
//獲取origin上的master分支,會在本地自動創(chuàng)建一個的origin/master的臨時分支。

git log -p master..origin/master 
//比較本地的master分支和遠端的master分支,看下差別。

git merge origin/master 
或 git rebase origin/master 
//如果差別是在自己的認知范圍,那么就進行合并操作,這樣本地的master分支就與云端保持一致了。

如果本地有未push的commit,則會產生Merge的commit行為。
Merge的過程中有可能因為多人對同一個文件的修改而造成沖突。

git mergetool
//打開merge工具,merge完后保存,然后手動提交merge后的結果。

完成上述操作后,就可以把自己本地的commit提交到云端了。

導演注:git mergegit rebase的區(qū)別,rebase是找到兩者共同的commit處,把它者的修改接上去,然后再把自己的修改接在它者的修改后面,不會產生merge行為??礆v史圖時也不會像merge那樣有分叉。

從以下執(zhí)行rebase后的提示,也可知二三
?  GitTest git:(master)  git rebase origin/master
First, rewinding head to replay your work on top of it...
Fast-forwarded master to origin/master.

Git先生的秘密

老板,你已經學會了很多使用上的技巧,是時候告訴你我的一個大秘密了。首先你可以查看下 .git/refs 中的內容,你會發(fā)現有一個 heads 文件夾和 tags 文件夾。heads 中存儲不同分支文件,文件里面是一個 SHA-1 值,就是記錄當前該分支在哪個 Commit
點上。

?  Test git:(master) find .git/refs
.git/refs
.git/refs/heads
.git/refs/heads/dev
.git/refs/heads/master
.git/refs/tags

另外 .git 目錄中還有一個 HEAD 文件,里面記錄了當前所在的工作節(jié)點,以下說明你當前在master分支上,然后又回通過 .git/refs/heads/master 里面記錄的 SHA-1 值來確定工作的節(jié)點。

?  Test git:(master) cat .git/HEAD
ref: refs/heads/master

所以我們切換分支,除了上面說的,還可以用以下這種姿勢

?  Test git:(master) git symbolic-ref HEAD refs/heads/dev
?  Test git:(dev) ?

然后我們也可以通過 update-ref 命令來更新這些 refs 。這里我們舉個應用點,如何reset 某個分支中的第一條 commit。如果直接像上面說的那樣使用reset命令,則會出現以下錯誤:

?  Test git:(master) git reset HEAD~1
fatal: ambiguous argument 'HEAD~1': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

但我們可以使用 update-ref 命令來直接操作該ref

Test git:(master) git update-ref -d HEAD

這樣就刪除了 HEAD 所制定分支文件。
注:此種做法你是無法使用 reflog 方式看到行為的,如果想要恢復,你需要使用丟失的 SHA-1 值

?  Test git:(dev) ? git update-ref refs/heads/dev b4d2e6a13c2e34195d472d889ab34000737d31d1

其他小技巧

git cherry-pick <commit-id>
老板如果想把其他分支上的一些照片揀過來使用,可以使用此命令。如果該照片與本分支無沖突,則直接會在本分支上加上一條commit,如果有沖突,則需要解決沖突后重新提交。

git stash/ git stash pop
如果老板當前有些工作沒有commit。但有些云端的commit或者其他分支的commit是自己后續(xù)開發(fā)所要依賴的,那就可以使用git stash把當前未提交的修改放入到緩存棧,等合并操作完成后,再用git stash pop,把修改再加回來,你可以用git stash list查看當前的緩存棧。

后記

Git和Mercurial 都是在2005年時出現,分別由Linus和Matt主導開發(fā)。而兩者的出現也源于一個共同的事件,2005年初BitKeeper宣布向開源社區(qū)收費。Mercurial在英語中有反復無常的意思,而Git也可以翻譯成無用之人,Matt直接說他取名Mercurial的用意就是諷刺BitKeeper的開發(fā)者。

作者簡介
彭濤(@彭濤me) 致力于讓技術變得易懂且有趣
個人博客:http://pengtao.me
簡書:http://www.itdecent.cn/u/f9246f41945e
GitHub:https://github.com/CPPAlien

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 1.git的安裝 1.1 在Windows上安裝Git msysgit是Windows版的Git,從https:/...
    落魂灬閱讀 12,821評論 4 54
  • 1. 安裝 Github 查看是否安裝git: $ git config --global user.name "...
    Albert_Sun閱讀 13,848評論 9 163
  • 上班忙,歸舍晚,滿室紛雜,渾似篷蒿亂。腰系圍裙拈布絹,輕挽羅衫,先把廚臺涮。 洗瑤盤,滌玉碗,箕帚除塵,再取污裳浣...
    高十一妹閱讀 512評論 3 12
  • 姜老師 1.倫理號碼,填喬主任 2.數據錄入,找陳鴻 王 病例開始收,血樣再說(等喬主任協調) 明天六月份前90份,三組
    湯云程閱讀 350評論 0 0
  • 在 ~/bin 目錄下建立了軟鏈接,bash 能夠調用而 zsh 卻不行: 參考 Sublime Text 2 -...
    公爵海恩庭斯閱讀 2,930評論 0 1

友情鏈接更多精彩內容