作者:Wenhu
作者:Wenhu
博客:http://bioinfostar.com/
本讲为第一部分,介绍git的“足够你用”命令;第二部分,还将介绍github的主要用途以及如何在R中使用git,部分内容改编自廖雪峰的《Git教程》。
源代码:https://github.com/mckf111/mckf111.github.io/tree/master/_posts
正统解释:分布式版本控制系统 (Distributed Version Control System),但这其实对理解和爱上git没啥卵用,反而让人觉得它很高深,望而却步!
Git的灵魂在于其让人欲罢不能的实用性!
先举两个场景,相信大多数人都碰到过:
写论文:不管是博士、硕士论文还是杂志上发表的paper,相信谁都不可能一遍写完就直接提交,你干你老板也不会干,不改个几十遍,那是不会罢手的 (刚刚亲身经历的真实案例。。。)!假设,你今天写了几段,保存为dissertation.docx
,明天转念一想,还是把其中一段改掉好,但是怕后面再需要,咋办?那只能另存一个文件,比如revised-1.docx
,然后在dissertation.docx
中继续写,这样,数次删改之后,你的文件夹中会有相当多的revised-n.docx
文件,如果过了两周,你突然想起有一段还是改回去好,你能记得原版本在哪个revised
文件中么?是不是得一个一个打开来查看,晕不晕?这还没算上,老板给你改的诸多版本,要把它们合并到自己的新版本上,真是要晕死!一般来说,对于咱们生物专业的童鞋,用word写文章还是绝大多数,有没有觉得审阅模式很坑?(当然碰到个年轻的老板,愿意接受latex那就没毛病了)
撸代码:生物信息离不开写代码,脚本也好,软件包也好,都不可能一成不变,因为需求在变化,原先的代码可能需要经常改动;或者你的上游分析软件或者导入、加载的软件做了修改,你也不得不随之改动;又或者你想到了一个新的功能,想加在自己或别人的软件包中,但又担心会影响其他的已有功能,咋办?是不是还是像上面那样,保存一堆堆的文件,然后把自己搞疯?
如果,有那么个“天网”软件,能一直监控我对于文件夹中的文件所进行的一切操作,包括增、删、改,而且能把每次的改动,像相机一样拍下快照存起来,再顺便起个唯一的名字,这样可以随意把文件夹回溯到任意一个时刻,就像时空穿梭一样,那该有多美好?
这就是git系统的主要功能之一——版本控制,有关分布式的含义,后面再说!相信到这里,你们应该有点想要了解它了吧:)
第一个例子中,说到了word文档的git监控,可能有人知道,word不是纯文本文件,而git来源于linux系统,对于纯文本的监控最为安全有效,那是不是就没办法了呢?其实有,在此给出两个链接,大家有兴趣可以阅读一下,其实是把word(.docx)转换成markdown格式的纯文本:Using Microsoft Word with git 和 Simul——专为word做的版本控制系统。
Git是一个软件,你首先得安装它!下载地址:git
上面的网址里有明确的各种系统中的安装指导,这里只说下如何在windows中进行初始配置,也可以参考廖雪峰的《Git教程》。
安装完之后,在开始菜单中找到Git Bash
,打开它,会出现命令行窗口,这是之后会一直用到的东西。
配置:
git config --global user.name "your_name"
git config --global user.email "[email protected]"
在上面的指令中将your_name
改成你想使用的名称,最好不要有空格,将[email protected]
换成你常用的邮箱地址。这样,你的电脑上的版本库就有了自己的名称了,一般不会和其他电脑上的混淆。
git init
)cd your/own/path/to/myapp
git init
## Initialized empty Git repository in F:/myapp/.git/
假设你在一个名为myapp
的文件夹中写软件,你想对这个文件夹(在git中,我们一般称之为库,repository or repo)进行git监控,怎么操作?
会出现上面一行字,代表git已经在监控你的myapp
库了,并且创建了一个隐藏文件夹.git
,没事别碰它,这是git的命根子!
git status
)在git操作的过程中,我们需要时时掌握监控状态,有没有新的增改删?和远程库有没有同步?等等。
git status
## On branch master
## Initial commit
## nothing to commit (create/copy files and use "git add" to track)
git add
和git commit
)当你在进行任何改动时,git都会关注到,但它还没有拍照,所以,你想进行时空穿梭还早了点。那如何拍照呢?按快门add
,打包存照commit
。注意:add
这一步也被成为stage
,标志着你的改动被添加进了缓存区,但还未进入监控的版本库中,属于中间的一个stage!
touch readme.txt # create a new readme.txt file
git status
## On branch master
## Initial commit
## Untracked files:
## (use "git add ..." 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.txt
git 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
参数是message的意思,给你每次打包存照加上一点信息,方便以后查找。另外,每个commit都有一个唯一的hash码对应,保证不会互相混淆,也方便准确的时空穿梭!
可以多次拍照(
add
),一次打包提交(commit
)git是对改动本身打包存照,而非对改动的文件存照,所以没有
add
的改动是不会被后面的commit
保存下来的,一定记住监控分两步:add
+commit
!
git diff
)如果想对比一下这次的改动和上次有啥区别,git diff
(difference)能清晰展示你要的信息。为了让命令行窗口漂亮点,我们先给git的指令上点颜色:
git config --global color.ui true
接着,我们给readme.txt
加点内容,用你喜爱的编辑器打开,然后添加一行字:I want to learn git!
,保存,查看状态:
git status
## On branch master
## Changes not staged for commit:
## (use "git add ..." to update what will be committed)
## (use "git checkout -- ..." to discard changes in working directory)
## modified: readme.txt
## no changes added to commit (use "git add" and/or "git commit -a")
git告诉我们有改动Changes
没有存照进入缓存区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
)当你的myapp
存在了一段时间后,改来改去,会有很多次的commit
被保存在版本库中,如何查看一下做了哪些改动呢?这样,既可以史为镜,也能为以后的“时光倒流”做准备。
如果直接输入git log
,输出的结果会比较冗杂,不容易把握信息概要,所以一般要加上几个参数(--pretty=oneline --abbrev-commit
,具体含义请对比不加参数时的输出来理解)来美化输出:
git log --pretty=oneline --abbrev-commit
## 90b9f6a (HEAD -> master) add readme file
日志显示,目前有一个commit
,90b9f6a
是它唯一的hash码的前几位(这是随机分配的,所以你的电脑上的码一般不会和我的一样,这很正常),它的message是add readme file
,这是我们之前自己标注的。另外,后面会说到,HEAD
是指向当前版本的指针,目前就指在这个唯一的commit
上,master
是我们目前的版本分支,称为主分支,也是必须有的一个分支!
git reset
)如果我们想回到之前的某个版本,那就要使出杀手锏git reset
了,为了方便演示,我们先做点准备,首先提交之前的改动(即加上I want to learn git!
这行字):
git add readme.txt
git 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 file
git add readme.txt
git commit -m 'second change in readme'
## [master d63de44] second change in readme
## 1 file changed, 2 insertions(+), 1 deletion(-)
这时,整个master分支已经有了3个commit了,我们可以查看一下日志:
git log --pretty=oneline --abbrev-commit
## d63de44 (HEAD -> master) second change in readme
## ddafbba first change in readme
## 90b9f6a add readme file
这时,我们发现了最近的提交有误,需要回到上一个提交的状态(hash为ddafbba
),怎么办?
HEAD^
,回退两版:HEAD^^
,依次类推;或者用数字表示:退N版:HEAD~N
。git reset --hard HEAD^
## HEAD is now at ddafbba first change in readme
此时,版本已经倒流到倒数第二个commit提交后的状态,可以查看目录确认一下。(--hard
,顾名思义,硬删除,把之后所有的改动都删掉)
^
或者~N
都不合实际,那么可以用commit id,即hash码去回退,先用git log
去查看之前的commit,找到你想回退的那个版本id,然后reset。比如,上面的例子,也可以这样操作,注意下方命令行尾的commit id是你想要退回的版本id,来自你电脑上运行git log时显示的值为准,每个人显示的是不同的:git reset --hard ddafbba
## HEAD is now at ddafbba first change in readme
个人推荐用第二种方式,准确定位,省去不必要的麻烦!
git reflog
+ git reset
)假设你把版本倒流了,老板不喜欢,让你再改回来,咋办?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 file
git reset --hard d63de44
## HEAD is now at d63de44 second change in readme
奇妙吧,之前消失的commit又回来了!
说到这里,我想简要说一下git监控的工作原理,我们的myapp
文件夹中除了.git
以外的地方都是被监视区域,称为工作区(working directory),也就是你进行各种操作的地方;剩下的.git
文件夹,则是git的版本库(repo),记录你在工作区所作的每次改动,其中又分为暂存区和分支两块。
监视流程:你进行了某次改动后,git一开始仅仅只会察觉到,而只有当你git add
时,此次改动会被添加进版本库的暂存区,当你再git commit
时,暂存区的改动将会融入到分支(文中为master主分支)中,形成一个版本节点,与当前工作区保持一致。
前面说完了git的一些基本玩法,接下来再补充一些,能让你玩得更流畅。
git checkout
)add
)当你在工作区做了修改之后,还没有add
,发现不想要了,想恢复到之前的状态。你当然可以手动删除本次的修改,但如果本次改了好几处地方,你不太记得了,怎么办?
我们先在编辑器里给readme.txt
加一行字:Hello git!
,保存。
git status
## On branch master
## Changes not staged for commit:
## (use "git add ..." to update what will be committed)
## (use "git checkout -- ..." 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
再看你的编辑器,这行修改自动消失了,回到了最近的版本状态。如果想要撤销很多文件中的未提交的修改,可以用通配符.
来指代所有文件,即git checkout -- .
。
其实,
git checkout
指令就是用版本库里的当前版本来覆盖工作区的版本。
add
),但尚未打包(commit
)还是上面的例子,我们再把这行字:Hello git!
加上,保存,并git add
。
git add readme.txt
git status
## On branch master
## Changes to be committed:
## (use "git reset HEAD ..." to unstage)
## modified: readme.txt
同样,输出中又贴心的做了提示,可以用git reset
指令,我们试一下。
git reset HEAD readme.txt
## Unstaged changes after reset:
## M readme.txt
git status
## On branch master
## Changes not staged for commit:
## (use "git add ..." to update what will be committed)
## (use "git checkout -- ..." to discard changes in working directory)
## modified: readme.txt
## no changes added to commit (use "git add" and/or "git commit -a")
注意看,此时的状态是不是和第1中情况,尚未拍照add
时一模一样啦,此时你要做的和之前一样:
git checkout -- readme.txt
那行字又神奇的消失了,有木有?
commit
)请参考前面的时光倒流
一节,不再赘述。
push
到远程库你没救了。。。
git rm
)这一次,你不是在文件中做改动了,你想直接删掉整个文件,我们举个例子,先在工作区加一个空文档test.txt
,并提交。
touch test.txt
git add test.txt
git commit -m 'add test.txt'
## [master d8a2099] add test.txt
## 1 file changed, 0 insertions(+), 0 deletions(-)
## create mode 100644 test.txt
然后,我们删掉文件,再查看一下git状态。
rm test.txt
git status
## On branch master
## Changes not staged for commit:
## (use "git add/rm ..." to update what will be committed)
## (use "git checkout -- ..." to discard changes in working directory)
## deleted: test.txt
## no changes added to commit (use "git add" and/or "git commit -a")
Git告诉我们,这一次的删除操作还尚未提交,这里,我们要转换一下思维,虽然说是提交,但你已经删除了整个文件,如果用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.txt
git 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介绍中讲到。
为鼓励读者交流、快速解决科研困难,我们建立了“宏基因组”专业讨论群,目前己有国内外1700+ 一线科研人员加入。参与讨论,获得专业解答,欢迎分享此文至朋友圈,并扫码加主编好友带你入群,务必备注“姓名-单位-研究方向-职称/年级”。技术问题寻求帮助,首先阅读《如何优雅的提问》学习解决问题思路,仍末解决群内讨论,问题不私聊,帮助同行。
学习扩增子、宏基因组科研思路和分析实战,关注“宏基因组”
点击阅读原文,跳转最新文章目录阅读
https://mp.weixin.qq.com/s/5jQspEvH5_4Xmart22gjMA