Wenhu Next Generation Bioinformatician

03-R,Git和Github(上)

2018-06-15
Wenhu
 

本讲为第一部分,介绍git的“足够你用”命令,部分内容改编自廖雪峰的《Git教程》欢迎转载,但请注明出处!

Git是啥

正统解释:分布式版本控制系统 (Distributed Version Control System),但这其实对理解和爱上git没啥卵用,反而让人觉得它很高深,望而却步!

Git的灵魂在于其让人欲罢不能的实用性

先举两个场景,相信大多数人都碰到过:

  1. 写论文:不管是博士、硕士论文还是杂志上发表的paper,相信谁都不可能一遍写完就直接提交,你干你老板也不会干,不改个几十遍,那是不会罢手的 (刚刚亲身经历的真实案例。。。)!假设,你今天写了几段,保存为dissertation.docx,明天转念一想,还是把其中一段改掉好,但是怕后面再需要,咋办?那只能另存一个文件,比如revised-1.docx,然后在dissertation.docx中继续写,这样,数次删改之后,你的文件夹中会有相当多的revised-n.docx文件,如果过了两周,你突然想起有一段还是改回去好,你能记得原版本在哪个revised文件中么?是不是得一个一个打开来查看,晕不晕?这还没算上,老板给你改的诸多版本,要把它们合并到自己的新版本上,真是要晕死!一般来说,对于咱们生物专业的童鞋,用word写文章还是绝大多数,有没有觉得审阅模式很坑?(当然碰到个年轻的老板,愿意接受latex那就没毛病了)

  2. 撸代码:生物信息离不开写代码,脚本也好,软件包也好,都不可能一成不变,因为需求在变化,原先的代码可能需要经常改动;或者你的上游分析软件或者导入、加载的软件做了修改,你也不得不随之改动;又或者你想到了一个新的功能,想加在自己或别人的软件包中,但又担心会影响其他的已有功能,咋办?是不是还是像上面那样,保存一堆堆的文件,然后把自己搞疯?

如果,有那么个“天网”软件,能一直监控我对于文件夹中的文件所进行的一切操作,包括增、删、改,而且能把每次的改动,像相机一样拍下快照存起来,再顺便起个唯一的名字,这样可以随意把文件夹回溯到任意一个时刻,就像时空穿梭一样,那该有多美好?

这就是git系统的主要功能之一——版本控制,有关分布式的含义,后面再说!相信到这里,你们应该有点想要了解它了吧:)

第一个例子中,说到了word文档的git监控,可能有人知道,word不是纯文本文件,而git来源于linux系统,对于纯文本的监控最为安全有效,那是不是就没办法了呢?其实有,在此给出两个链接,大家有兴趣可以阅读一下,其实是把word(.docx)转换成markdown格式的纯文本:Using Microsoft Word with gitSimul——专为word做的版本控制系统

安装git

Git是一个软件,你首先得安装它!下载地址:git

上面的网址里有明确的各种系统中的安装指导,这里只说下如何在windows中进行初始配置,也可以参考廖雪峰的《Git教程》

安装完之后,在开始菜单中找到Git Bash,打开它,会出现命令行窗口,这是之后会一直用到的东西。

配置

$ git config --global user.name "your_name"
$ git config --global user.email "email@example.com"

在上面的指令中将your_name改成你想使用的名称,最好不要有空格,将email@example.com换成你常用的邮箱地址。这样,你的电脑上的版本库就有了自己的名称了,一般不会和其他电脑上的混淆。

开始啦

创建版本库 (git init)

假设你在一个名为myapp的文件夹中写软件,你想对这个文件夹(在git中,我们一般称之为库,repository or repo)进行git监控,怎么操作?

cd your/own/path/to/myapp
git init
## Initialized empty Git repository in F:/myapp/.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 addgit 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 <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.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 <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没有存照进入缓存区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

日志显示,目前有一个commit90b9f6a是它唯一的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),怎么办?

  1. 第一种方法,如果只需要回到最近几次的版本,那么直接移动HEAD指针即可,回退一版:HEAD^,回退两版:HEAD^^,依次类推;或者用数字表示:退N版:HEAD~N
git reset --hard HEAD^
## HEAD is now at ddafbba first change in readme

此时,版本已经倒流到倒数第二个commit提交后的状态,可以查看目录确认一下。(--hard,顾名思义,硬删除,把之后所有的改动都删掉)

  1. 第二种方法,如果想要回到很久之前的一个版本,写^或者~N都不合实际,那么可以用commit id,即hash码去回退,先用git log去查看之前的commit,找到你想回退的那个版本id,然后reset。比如,上面的例子,也可以这样操作:
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即可。

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的工作原理

说到这里,我想简要说一下git监控的工作原理,我们的myapp文件夹中除了.git以外的地方都是被监视区域,称为工作区(working directory),也就是你进行各种操作的地方;剩下的.git文件夹,则是git的版本库(repo),记录你在工作区所作的每次改动,其中又分为暂存区分支两块。

监视流程:你进行了某次改动后,git一开始仅仅只会察觉到,而只有当你git add时,此次改动会被添加进版本库暂存区,当你再git commit时,暂存区的改动将会融入到分支(文中为master主分支)中,形成一个版本节点,与当前工作区保持一致。

继续玩

前面说完了git的一些基本玩法,接下来再补充一些,能让你玩得更流畅。

撤销修改(git checkout)

1. 修改尚未拍照(add

当你在工作区做了修改之后,还没有add,发现不想要了,想恢复到之前的状态。你当然可以手动删除本次的修改,但如果本次改了好几处地方,你不太记得了,怎么办?

我们先在编辑器里给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

再看你的编辑器,这行修改自动消失了,回到了最近的版本状态。如果想要撤销很多文件中的未提交的修改,可以用通配符.来指代所有文件,即git checkout -- .

其实,git checkout指令就是用版本库里的当前版本来覆盖工作区的版本。

2. 修改已经拍照(add),但尚未打包(commit

还是上面的例子,我们再把这行字:Hello git!加上,保存,并git add

git add readme.txt
git 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.txt

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")

注意看,此时的状态是不是和第1中情况,尚未拍照add时一模一样啦,此时你要做的和之前一样:

git checkout -- readme.txt

那行字又神奇的消失了,有木有?

3. 修改已打包提交(commit

请参考前面的时光倒流一节,不再赘述。

4. 修改已push到远程库

你没救了。。。

删除文件(git rm

这一次,你不是在文件中做改动了,你想直接删掉整个文件,我们举个例子,先在工作区加一个空文档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 <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告诉我们,这一次的删除操作还尚未提交,这里,我们要转换一下思维,虽然说是提交,但你已经删除了整个文件,如果用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介绍中讲到。

关注我的最新博文,请订阅my RSS ~~


Similar Posts

评论 / Comments