GIT版本管理之旅(一)
http://blog.youkuaiyun.com/rickey17/article/details/70183467
本章继续上章的类型,介绍基于空项目的版本库操作,本教程基于Ubuntu
- 创建版本库
- 新增文件
- 修改文件
- 删除文件
- 版本回退
- 基本概念
- 管理以及撤销修改
- 添加到远程仓库
- 从远程仓库克隆
创建版本库
版本库又名仓库,英文名repository,可以理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史。
xq@xq-VPCEG17YC:~/git$ pwd
/home/xq/git
xq@xq-VPCEG17YC:~/git$ mkdir test
xq@xq-VPCEG17YC:~/git$ cd test/
xq@xq-VPCEG17YC:~/git/test$ git init
初始化空的 Git 仓库于 /home/xq/git/test/.git/
xq@xq-VPCEG17YC:~/git/test$ ls
xq@xq-VPCEG17YC:~/git/test$ ls -ah
. .. .git
首先新建一个test的文件夹(不一定都是空文件夹,只是托管的文件自己需要把握),在该目录下执行git init命令,该目录就会变成git管理的仓库。
目录下多了一个.git的目录,这个目录是Git来跟踪管理版本库的,不可随意修改。
新增文件
xq@xq-VPCEG17YC:~/git/test$ vi hello.txt
xq@xq-VPCEG17YC:~/git/test$ cat hello.txt
this is my first try.
xq@xq-VPCEG17YC:~/git/test$ git add hello.txt
xq@xq-VPCEG17YC:~/git/test$ git commit -m "add a file[hello.txt]"
[master (根提交) b10613a] add a file[hello.txt]
1 file changed, 1 insertion(+)
create mode 100644 hello.txt
新建一个hello.txt的文件
通过git add xxx的命令添加到本地仓库
通过git commit的命令提交本次修改,-m是本次提交的说明,可以输入任意内容
修改文件
xq@xq-VPCEG17YC:~/git/test$ vi hello.txt
xq@xq-VPCEG17YC:~/git/test$ cat hello.txt
this is my second try.
xq@xq-VPCEG17YC:~/git/test$ git status
位于分支 master
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git checkout -- <文件>..." 丢弃工作区的改动)
修改: hello.txt
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
xq@xq-VPCEG17YC:~/git/test$ git diff
diff --git a/hello.txt b/hello.txt
index df723d7..55ef308 100644
--- a/hello.txt
+++ b/hello.txt
@@ -1 +1 @@
-this is my first try.
+this is my second try.
修改hello.txt文件
通过git status仓库当前的状态
通过git diff比较差异
xq@xq-VPCEG17YC:~/git/test$ git add hello.txt
xq@xq-VPCEG17YC:~/git/test$ git status
位于分支 master
要提交的变更:
(使用 "git reset HEAD <文件>..." 以取消暂存)
修改: hello.txt
xq@xq-VPCEG17YC:~/git/test$ git commit -m "update hello.txt"
[master 9d94f3b] update hello.txt
1 file changed, 1 insertion(+), 1 deletion(-)
xq@xq-VPCEG17YC:~/git/test$ git status
位于分支 master
无文件要提交,干净的工作区
修改之后提交文件和新增时时一样的,通过git status查看各种情况下仓库的状态
删除文件
本地工作空间删除文件 rm xxx
Git知道你删除了文件,因此,工作区和版本库就不一致了,可用git status查看状态
如果时本地空间误删可用git checkout – xxx从版本库中检出到本地;如果是要从版本库中删除该文件,那就用命令git rm删掉,并且git commit
版本回退
我们再次修改hello.txt然后commit
之后通过git log查看修改记录
xq@xq-VPCEG17YC:~/git/test$ git log --pretty=oneline
774045528c64b1b541a045093836c1845f14a908 update again
9d94f3ba85cfac45b0e30010fca1265f15e9aaf7 update hello.txt
b10613a11dbae7c93d2cf49f654ade68ad675030 add a file[hello.txt]
日志中记录的hash值,上一章我们解释过
文件在保存到 Git 时,数据都要使用 SHA-1 算法进行内容的校验和(checksum)计算,得到一个 SHA-1 哈希值,作为指纹字符串。该字串由 40 个十六进制字符(0-9 及 a-f)组成,并将此结果作为数据的唯一标识和索引。通过唯一标志判断文件的完整性以及是否变更。所有保存在 Git 数据库中的东西都是用此哈希值来作索引的,而不是靠文件名。
后面记录的是我们每次commit时的说明
在Git中,用HEAD表示当前版本,也就是最新的提交,上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100。
以下对应hello.txt的三个版本
- HEAD –> 774045528c64b1b541a045093836c1845f14a908
- HEAD^ –> 9d94f3ba85cfac45b0e30010fca1265f15e9aaf7
- HEAD^^ –> b10613a11dbae7c93d2cf49f654ade68ad675030
我们将版本回退到上一个版本,并查看文件,发现确实已经回退。
此时查看日志,发现只剩下两个版本了。此时版本对应如下
- HEAD –> 9d94f3ba85cfac45b0e30010fca1265f15e9aaf7
- HEAD^ –> b10613a11dbae7c93d2cf49f654ade68ad675030
xq@xq-VPCEG17YC:~/git/test$ git reset --hard HEAD^
HEAD 现在位于 9d94f3b update hello.txt
xq@xq-VPCEG17YC:~/git/test$ cat hello.txt
this is my second try.
xq@xq-VPCEG17YC:~/git/test$ git log --pretty=oneline
9d94f3ba85cfac45b0e30010fca1265f15e9aaf7 update hello.txt
b10613a11dbae7c93d2cf49f654ade68ad675030 add a file[hello.txt]
当然通过git reset –hard 9d94f3ba85cf(hashId 前几位也行,系统会自动查找),也是可以回退的。
如果回退后,又希望恢复到第三个版本怎么办呢?git reflog会记录每一次命令,然后找到对应的hashId恢复就可以啦。
xq@xq-VPCEG17YC:~/git/test$ git reflog
9d94f3b HEAD@{0}: reset: moving to HEAD^
7740455 HEAD@{1}: commit: update again
9d94f3b HEAD@{2}: commit: update hello.txt
b10613a HEAD@{3}: commit (initial): add a file[hello.txt]
xq@xq-VPCEG17YC:~/git/test$ git reset --hard 7740455
HEAD 现在位于 7740455 update again
xq@xq-VPCEG17YC:~/git/test$ cat hello.txt
this is my third try.
基本概念
到了这里我们继续巩固上一章的内容,理论+实践+理论
- 工作区(Working Directory)
就是你在电脑里能看到的目录,比如我的test文件夹就是一个工作区 - 版本库(Repository)
工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。
Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。
前面讲了我们把文件往Git版本库里添加的时候,是分两步执行的:
第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;
第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。
因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。
你可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。
管理以及撤销修改
Git比其他版本控制系统设计得优秀,因为Git跟踪并管理的是修改,而非文件。
比如你新增了一行,这就是一个修改,删除了一行,也是一个修改,更改了某些字符,也是一个修改,删了一些又加了一些,也是一个修改,甚至创建一个新文件,也算一个修改。
我们修改hello.txt然后git add添加到暂存区,然后再次修改hello.txt,最后执行git commit命令,提交后,用git diff HEAD – hello.txt命令可以查看工作区和版本库里面最新版本的区别,发现并不一致。
原因是:Git管理的是修改,当你用git add命令后,在工作区的第一次修改被放入暂存区,准备提交,但是,在工作区的第二次修改并没有放入暂存区,所以,git commit只负责把暂存区的修改提交了,也就是第一次的修改被提交了,第二次的修改不会被提交。
xq@xq-VPCEG17YC:~/git/test$ cat hello.txt
this is my third try.
xq@xq-VPCEG17YC:~/git/test$ vi hello.txt
xq@xq-VPCEG17YC:~/git/test$ cat hello.txt
this is my fourth try.
xq@xq-VPCEG17YC:~/git/test$ git add hello.txt
xq@xq-VPCEG17YC:~/git/test$ vi hello.txt
xq@xq-VPCEG17YC:~/git/test$ cat hello.txt
this is my fifth try.
xq@xq-VPCEG17YC:~/git/test$ git commit -m "stage test"
[master 1a465a0] stage test
1 file changed, 1 insertion(+), 1 deletion(-)
xq@xq-VPCEG17YC:~/git/test$ cat hello.txt
this is my fifth try.
xq@xq-VPCEG17YC:~/git/test$ git status
位于分支 master
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git checkout -- <文件>..." 丢弃工作区的改动)
修改: hello.txt
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
xq@xq-VPCEG17YC:~/git/test$ git diff HEAD -- hello.txt
diff --git a/hello.txt b/hello.txt
index fd3f316..3d7585b 100644
--- a/hello.txt
+++ b/hello.txt
@@ -1 +1 @@
-this is my fourth try.
+this is my fifth try.
文件修改错误需要撤销怎么办
本地工作区撤销
1.文件还在工作区可以手动修改文件;
2.git checkout – file也可以丢弃工作区的修改(命令中的–很重要,没有–,就变成了“切换到另一个分支”的命令)一种是hello.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
一种是hello.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近一次git commit或git add时的状态。
已经add到暂存区撤销
用命令git reset HEAD file可以把暂存区的修改撤销掉(unstage),重新放回工作区git reset命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本
- 已经commit到版本库撤销
参考《版本回退》小节
xq@xq-VPCEG17YC:~/git/test$ git checkout -- hello.txt
xq@xq-VPCEG17YC:~/git/test$ cat hello.txt
this is my fourth try.
xq@xq-VPCEG17YC:~/git/test$ cat hello.txt
this is my fifth try.
xq@xq-VPCEG17YC:~/git/test$ git add hello.txt
xq@xq-VPCEG17YC:~/git/test$ vi hello.txt
xq@xq-VPCEG17YC:~/git/test$ cat hello.txt
this is my sixth try.
xq@xq-VPCEG17YC:~/git/test$ git reset HEAD hello.txt
重置后取消暂存的变更:
M hello.txt
添加到远程仓库
Git是分布式版本控制系统,同一个Git仓库,可以分布到不同的机器上。别的机器可以“克隆”这个原始版本库,而且每台机器的版本库其实都是一样的,并没有主次之分。
单机也是可以克隆多个版本库,但是毫无意义。我们将GitHub作为服务器的角色,提供7*24的服务,从GitHub仓库克隆一份到自己的电脑上,并且各自把各自的提交推送到服务器仓库里,也从服务器仓库中拉取别人的提交。
GitHub申请以及SSH秘钥设置见上一章
http://blog.youkuaiyun.com/rickey17/article/details/70183467
我们可以把一个已有的本地仓库与之关联,然后,把本地仓库的内容推送到GitHub仓库。
在GitHub上新建一个test的版本库
现在,我们根据GitHub的提示,在本地的test仓库下运行命令:
git remote add origin https://github.com/rickey17/test.git
请千万注意,把上面的rickey17替换成你自己的GitHub账户名,否则,你在本地关联的就是我的远程库,关联没有问题,但是你以后推送是推不上去的,因为你的SSH Key公钥不在我的账户列表中。
添加后,远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库。
下一步,就可以把本地库的所有内容推送到远程库上:
git push -u origin master
这样你就可以在GitHub上看见你的文件了。
把本地库的内容推送到远程,用git push命令,实际上是把当前分支master推送到远程。
由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
从现在起,只要本地作了提交,就可以通过命令:
git push origin master
把本地master分支的最新修改推送至GitHub,现在,你就拥有了真正的分布式版本库!
从远程仓库克隆
git clone https://github.com/rickey17/test.git
xq@xq-VPCEG17YC:~/git/test$ git status
位于分支 master
您的分支与上游分支 'origin/master' 一致。
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git checkout -- <文件>..." 丢弃工作区的改动)
修改: hello.txt
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
xq@xq-VPCEG17YC:~/git/test$ git add hello.txt
xq@xq-VPCEG17YC:~/git/test$ git commit -m "commit to GitHub"
[master 3742dca] commit to GitHub
1 file changed, 1 insertion(+), 1 deletion(-)
xq@xq-VPCEG17YC:~/git/test$ git push origin master
对象计数中: 3, 完成.
写入对象中: 100% (3/3), 272 bytes | 0 bytes/s, 完成.
Total 3 (delta 0), reused 0 (delta 0)
To git@github.com:rickey17/test.git
1a465a0..3742dca master -> master
xq@xq-VPCEG17YC:~/git/test$ git clone https://github.com/rickey17/test.git
正克隆到 'test'...
remote: Counting objects: 15, done.
remote: Compressing objects: 100% (5/5), done.
remote: Total 15 (delta 0), reused 15 (delta 0), pack-reused 0
展开对象中: 100% (15/15), 完成.
检查连接... 完成。