作者:Wenhu
博客:http://bioinfostar.com/
本講為第一部分,介紹git的“足夠你用”命令;第二部分,還將介紹github的主要用途以及如何在R中使用git,部分內(nèi)容改編自廖雪峰的《Git教程》。
源代碼:https://github.com/mckf111/mckf111.github.io/tree/master/_posts
正統(tǒng)解釋:分布式版本控制系統(tǒng) (Distributed Version Control System),但這其實對理解和愛上git沒啥卵用,反而讓人覺得它很高深,望而卻步!
Git的靈魂在于其讓人欲罷不能的實用性!
先舉兩個場景,相信大多數(shù)人都碰到過:
寫論文:不管是博士、碩士論文還是雜志上發(fā)表的paper,相信誰都不可能一遍寫完就直接提交,你干你老板也不會干,不改個幾十遍,那是不會罷手的 (剛剛親身經(jīng)歷的真實案例。。。)!假設(shè),你今天寫了幾段,保存為dissertation.docx
,明天轉(zhuǎn)念一想,還是把其中一段改掉好,但是怕后面再需要,咋辦?那只能另存一個文件,比如revised-1.docx
,然后在dissertation.docx
中繼續(xù)寫,這樣,數(shù)次刪改之后,你的文件夾中會有相當(dāng)多的revised-n.docx
文件,如果過了兩周,你突然想起有一段還是改回去好,你能記得原版本在哪個revised
文件中么?是不是得一個一個打開來查看,暈不暈?這還沒算上,老板給你改的諸多版本,要把它們合并到自己的新版本上,真是要暈死!一般來說,對于咱們生物專業(yè)的童鞋,用word寫文章還是絕大多數(shù),有沒有覺得審閱模式很坑?(當(dāng)然碰到個年輕的老板,愿意接受latex那就沒毛病了)
擼代碼:生物信息離不開寫代碼,腳本也好,軟件包也好,都不可能一成不變,因為需求在變化,原先的代碼可能需要經(jīng)常改動;或者你的上游分析軟件或者導(dǎo)入、加載的軟件做了修改,你也不得不隨之改動;又或者你想到了一個新的功能,想加在自己或別人的軟件包中,但又擔(dān)心會影響其他的已有功能,咋辦?是不是還是像上面那樣,保存一堆堆的文件,然后把自己搞瘋?
如果,有那么個“天網(wǎng)”軟件,能一直監(jiān)控我對于文件夾中的文件所進行的一切操作,包括增、刪、改,而且能把每次的改動,像相機一樣拍下快照存起來,再順便起個唯一的名字,這樣可以隨意把文件夾回溯到任意一個時刻,就像時空穿梭一樣,那該有多美好?
這就是git系統(tǒng)的主要功能之一——版本控制,有關(guān)分布式的含義,后面再說!相信到這里,你們應(yīng)該有點想要了解它了吧:)
第一個例子中,說到了word文檔的git監(jiān)控,可能有人知道,word不是純文本文件,而git來源于linux系統(tǒng),對于純文本的監(jiān)控最為安全有效,那是不是就沒辦法了呢?其實有,在此給出兩個鏈接,大家有興趣可以閱讀一下,其實是把word(.docx)轉(zhuǎn)換成markdown格式的純文本:Using Microsoft Word with git 和 Simul——專為word做的版本控制系統(tǒng)。
Git是一個軟件,你首先得安裝它!下載地址:git
上面的網(wǎng)址里有明確的各種系統(tǒng)中的安裝指導(dǎo),這里只說下如何在windows中進行初始配置,也可以參考廖雪峰的《Git教程》。
安裝完之后,在開始菜單中找到Git Bash
,打開它,會出現(xiàn)命令行窗口,這是之后會一直用到的東西。
配置:
git config --global user.name "your_name"git config --global user.email "email@example.com"
在上面的指令中將your_name
改成你想使用的名稱,最好不要有空格,將email@example.com
換成你常用的郵箱地址。這樣,你的電腦上的版本庫就有了自己的名稱了,一般不會和其他電腦上的混淆。
git init
)假設(shè)你在一個名為myapp
的文件夾中寫軟件,你想對這個文件夾(在git中,我們一般稱之為庫,repository or repo)進行g(shù)it監(jiān)控,怎么操作?
cd your/own/path/to/myappgit init## Initialized empty Git repository in F:/myapp/.git/
會出現(xiàn)上面一行字,代表git已經(jīng)在監(jiān)控你的myapp
庫了,并且創(chuàng)建了一個隱藏文件夾.git
,沒事別碰它,這是git的命根子!
git status
)在git操作的過程中,我們需要時時掌握監(jiān)控狀態(tài),有沒有新的增改刪?和遠程庫有沒有同步?等等。
git status## On branch master## Initial commit## nothing to commit (create/copy files and use "git add" to track)
git add
和git commit
)當(dāng)你在進行任何改動時,git都會關(guān)注到,但它還沒有拍照,所以,你想進行時空穿梭還早了點。那如何拍照呢?按快門add
,打包存照commit
。注意:add
這一步也被成為stage
,標(biāo)志著你的改動被添加進了緩存區(qū),但還未進入監(jiān)控的版本庫中,屬于中間的一個stage!
touch readme.txt # create a new readme.txt filegit status## On branch master## Initial commit## Untracked files:## (use "git add <file>..." to include in what will be committed)## readme.txt## nothing added to commit but untracked files present (use "git add" to track)git add readme.txtgit commit -m 'add readme file'## [master (root-commit) 90b9f6a] add readme file## 1 file changed, 0 insertions(+), 0 deletions(-)## create mode 100644 readme.txt
commit后面的-m
參數(shù)是message的意思,給你每次打包存照加上一點信息,方便以后查找。另外,每個commit都有一個唯一的hash碼對應(yīng),保證不會互相混淆,也方便準(zhǔn)確的時空穿梭!
可以多次拍照(
add
),一次打包提交(commit
)git是對改動本身打包存照,而非對改動的文件存照,所以沒有
add
的改動是不會被后面的commit
保存下來的,一定記住監(jiān)控分兩步:add
+commit
!
git diff
)如果想對比一下這次的改動和上次有啥區(qū)別,git diff
(difference)能清晰展示你要的信息。為了讓命令行窗口漂亮點,我們先給git的指令上點顏色:
git config --global color.ui true
接著,我們給readme.txt
加點內(nèi)容,用你喜愛的編輯器打開,然后添加一行字:I want to learn git!
,保存,查看狀態(tài):
git status## On branch master## 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.txt## no changes added to commit (use "git add" and/or "git commit -a")
git告訴我們有改動Changes
沒有存照進入緩存區(qū)not staged
,如果過了一段時間,你再回來工作,忘了具體改了啥,不敢隨便提交,咋辦?
git diff readme.txt## diff --git a/readme.txt b/readme.txt## index e69de29..235024b 100644## --- a/readme.txt## +++ b/readme.txt## @@ -0,0 +1 @@## +I want to learn git!## \ No newline at end of file
改動一目了然!
git log
)當(dāng)你的myapp
存在了一段時間后,改來改去,會有很多次的commit
被保存在版本庫中,如何查看一下做了哪些改動呢?這樣,既可以史為鏡,也能為以后的“時光倒流”做準(zhǔn)備。
如果直接輸入git log
,輸出的結(jié)果會比較冗雜,不容易把握信息概要,所以一般要加上幾個參數(shù)(--pretty=oneline --abbrev-commit
,具體含義請對比不加參數(shù)時的輸出來理解)來美化輸出:
git log --pretty=oneline --abbrev-commit## 90b9f6a (HEAD -> master) add readme file
日志顯示,目前有一個commit
,90b9f6a
是它唯一的hash碼的前幾位(這是隨機分配的,所以你的電腦上的碼一般不會和我的一樣,這很正常),它的message是add readme file
,這是我們之前自己標(biāo)注的。另外,后面會說到,HEAD
是指向當(dāng)前版本的指針,目前就指在這個唯一的commit
上,master
是我們目前的版本分支,稱為主分支,也是必須有的一個分支!
git reset
)如果我們想回到之前的某個版本,那就要使出殺手锏git reset
了,為了方便演示,我們先做點準(zhǔn)備,首先提交之前的改動(即加上I want to learn git!
這行字):
git add readme.txtgit commit -m 'first change in readme'## [master ddafbba] first change in readme## 1 file changed, 1 insertion(+)git status## On branch master## nothing to commit, working tree clean
接下來,我們再在編輯器中添加第二行字Git is awesome!
,保存。
git diff readme.txt## diff --git a/readme.txt b/readme.txt## index 235024b..76d1c1b 100644## --- a/readme.txt## +++ b/readme.txt## @@ -1 +1,2 @@## -I want to learn git!## \ No newline at end of file## +I want to learn git!## +Git is awesome!## \ No newline at end of filegit add readme.txtgit commit -m 'second change in readme'## [master d63de44] second change in readme## 1 file changed, 2 insertions(+), 1 deletion(-)
這時,整個master分支已經(jīng)有了3個commit了,我們可以查看一下日志:
git log --pretty=oneline --abbrev-commit## d63de44 (HEAD -> master) second change in readme## ddafbba first change in readme## 90b9f6a add readme file
這時,我們發(fā)現(xiàn)了最近的提交有誤,需要回到上一個提交的狀態(tài)(hash為ddafbba
),怎么辦?
第一種方法,如果只需要回到最近幾次的版本,那么直接移動HEAD指針即可,回退一版:HEAD^
,回退兩版:HEAD^^
,依次類推;或者用數(shù)字表示:退N版:HEAD~N
。
git reset --hard HEAD^## HEAD is now at ddafbba first change in readme
此時,版本已經(jīng)倒流到倒數(shù)第二個commit提交后的狀態(tài),可以查看目錄確認一下。(--hard
,顧名思義,硬刪除,把之后所有的改動都刪掉)
第二種方法,如果想要回到很久之前的一個版本,寫^
或者~N
都不合實際,那么可以用commit id,即hash碼去回退,先用git log
去查看之前的commit,找到你想回退的那個版本id,然后reset。比如,上面的例子,也可以這樣操作,注意下方命令行尾的commit id是你想要退回的版本id,來自你電腦上運行g(shù)it log時顯示的值為準(zhǔn),每個人顯示的是不同的:
git reset --hard ddafbba## HEAD is now at ddafbba first change in readme
個人推薦用第二種方式,準(zhǔn)確定位,省去不必要的麻煩!
git reflog
+ git reset
)假設(shè)你把版本倒流了,老板不喜歡,讓你再改回來,咋辦?git一樣能輕松搞定,還是接上面的例子,我們想回到最新的版本second change in readme
,首先,得找到這個commit id,git reflog
可以記錄你的每一次commit,不論是HEAD之前還是之后,只要你commit過,它就記錄下來了,然后再reset即可指定版本的commit id,如second change(d63de44),你操作時ID會不一樣,注意修改。
git reflog## ddafbba (HEAD -> master) HEAD@{0}: reset: moving to HEAD^## d63de44 HEAD@{1}: commit: second change in readme## ddafbba (HEAD -> master) HEAD@{2}: commit: first change in readme## 90b9f6a HEAD@{3}: commit (initial): add readme filegit reset --hard d63de44## HEAD is now at d63de44 second change in readme
奇妙吧,之前消失的commit又回來了!
說到這里,我想簡要說一下git監(jiān)控的工作原理,我們的myapp
文件夾中除了.git
以外的地方都是被監(jiān)視區(qū)域,稱為工作區(qū)(working directory),也就是你進行各種操作的地方;剩下的.git
文件夾,則是git的版本庫(repo),記錄你在工作區(qū)所作的每次改動,其中又分為暫存區(qū)和分支兩塊。
監(jiān)視流程:你進行了某次改動后,git一開始僅僅只會察覺到,而只有當(dāng)你git add
時,此次改動會被添加進版本庫的暫存區(qū),當(dāng)你再git commit
時,暫存區(qū)的改動將會融入到分支(文中為master主分支)中,形成一個版本節(jié)點,與當(dāng)前工作區(qū)保持一致。
前面說完了git的一些基本玩法,接下來再補充一些,能讓你玩得更流暢。
git checkout
)add
)當(dāng)你在工作區(qū)做了修改之后,還沒有add
,發(fā)現(xiàn)不想要了,想恢復(fù)到之前的狀態(tài)。你當(dāng)然可以手動刪除本次的修改,但如果本次改了好幾處地方,你不太記得了,怎么辦?
我們先在編輯器里給readme.txt
加一行字:Hello git!
,保存。
git status## On branch master## 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.txt## no changes added to commit (use "git add" and/or "git commit -a")
輸出中提示可以用git checkout
指令,我們嘗試一下。
git checkout -- readme.txt
再看你的編輯器,這行修改自動消失了,回到了最近的版本狀態(tài)。如果想要撤銷很多文件中的未提交的修改,可以用通配符.
來指代所有文件,即git checkout -- .
。
其實,
git checkout
指令就是用版本庫里的當(dāng)前版本來覆蓋工作區(qū)的版本。
add
),但尚未打包(commit
)還是上面的例子,我們再把這行字:Hello git!
加上,保存,并git add
。
git add readme.txtgit status## On branch master## Changes to be committed:## (use "git reset HEAD <file>..." to unstage)## modified: readme.txt
同樣,輸出中又貼心的做了提示,可以用git reset
指令,我們試一下。
git reset HEAD readme.txt## Unstaged changes after reset:## M readme.txtgit status## On branch master## 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.txt## no changes added to commit (use "git add" and/or "git commit -a")
注意看,此時的狀態(tài)是不是和第1中情況,尚未拍照add
時一模一樣啦,此時你要做的和之前一樣:
git checkout -- readme.txt
那行字又神奇的消失了,有木有?
commit
)請參考前面的時光倒流
一節(jié),不再贅述。
push
到遠程庫你沒救了。。。
git rm
)這一次,你不是在文件中做改動了,你想直接刪掉整個文件,我們舉個例子,先在工作區(qū)加一個空文檔test.txt
,并提交。
touch test.txtgit add test.txtgit commit -m 'add test.txt'## [master d8a2099] add test.txt## 1 file changed, 0 insertions(+), 0 deletions(-)## create mode 100644 test.txt
然后,我們刪掉文件,再查看一下git狀態(tài)。
rm test.txtgit status## On branch master## Changes not staged for commit:## (use "git add/rm <file>..." to update what will be committed)## (use "git checkout -- <file>..." to discard changes in working directory)## deleted: test.txt## no changes added to commit (use "git add" and/or "git commit -a")
Git告訴我們,這一次的刪除操作還尚未提交,這里,我們要轉(zhuǎn)換一下思維,雖然說是提交,但你已經(jīng)刪除了整個文件,如果用add
就顯得沒道理了,故而輸出中提示了另一個指令remove——git rm
。最后,別忘了也要commit
哦!
git rm test.txt## rm 'test.txt'git commit -m 'remove test.txt'## [master 7dcb016] remove test.txt## 1 file changed, 0 insertions(+), 0 deletions(-)## delete mode 100644 test.txtgit log --pretty=oneline --abbrev-commit## 7dcb016 (HEAD -> master) remove test.txt## d8a2099 add test.txt## d63de44 second change in readme## ddafbba first change in readme## 90b9f6a add readme file
以上就是Git中的大部分常用指令,還有幾個會在下一講的Github介紹中講到。
廖雪峰的《Git教程》https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000
學(xué)R學(xué)初階-03-R-Git和Github-1 http://bioinfostar.com/2018/06/15/%E5%AD%A6R%E5%AD%A6%E5%88%9D%E9%98%B6-03-R-Git%E5%92%8CGithub-1/
聯(lián)系客服