Git学习篇
Git是目前世界上最先进的分布式版本控制系统
CVS/SVN:集中式版本控制系统,版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。
GIT:分布式版本控制系统根本没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这样就不需要联网了,因为版本库就在自己的电脑上。
既然每个人电脑上都有一个完整的版本库,那多个人如何协作呢?你们俩之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。
分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库,某一个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。而集中式版本控制系统的中央服务器要是出了问题,所有人都没法干活了。
在实际使用分布式版本控制系统的时候,其实很少在两人之间的电脑上推送版本库的修改,因为可能你们俩不在一个局域网内,两台电脑互相访问不了,也可能今天你的同事病了,他的电脑压根没有开机。因此,分布式版本控制系统通常也有一台充当“中央服务器”的电脑,但这个服务器的作用仅仅是用来方便“交换”大家的修改,没有它大家也一样干活,只是交换修改不方便而已。
Git的优势不单是不必联网这么简单,后面我们还会看到Git极其强大的分支管理,把SVN等远远抛在了后面。
版本库/仓库【repository】
简单理解为目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,或者在将来某个时刻可以“还原”。
一、创建版本库
-
在本地创建一个空目录,并进入该目录,此处为GitLearning
-
使用git init命令把该目录变成git可管理的目录【通过ls -ah可以看到该目录下出现了一个隐藏文件.git】
-
git init命令以后,已经建好了git仓库,并且提示这是一个empty Git repository
-
最好在空目录下创建git仓库【选择一个已经有东西的目录也是可以的,但是不建议如此】
二、把文件添加到版本库
①跟踪文件情况
-
一切的版本控制系统,只能跟踪纯文本编写文件的改动【txt,doc,代码等文本文件】
**注意!!!!**
- Microsoft的Word格式是二进制格式,因此,版本控制系统是没法跟踪Word文件的改动的。
- 强烈建议使用标准的UTF-8编码,所有语言使用同一种编码,既没有冲突,又被所有平台所支持。
- 千万不要使用Windows自带的**记事本**编辑任何文本文件。
- 原因是Microsoft开发记事本的团队使用了一个非常弱智的行为来保存UTF-8编码的文件,他们自作聪明地在每个文件开头添加了0xefbbbf(十六进制)的字符。
- 会遇到很多不可思议的问题,比如,网页第一行可能会显示一个“?”,明明正确的程序一编译就报语法错误,等等,都是由记事本的弱智行为带来的。
- 建议下载Notepad++代替记事本,把Notepad++的默认编码设置为UTF-8 without BOM
-
而图片、视频这些二进制文件,虽然也能由版本控制系统管理,但没法跟踪文件的变化。只知道图片从100KB改成了120KB,但到底改了啥,版本控制系统不知道,也没法知道。
②添加某文件到版本库
-
放到GitLearning目录下【即放到你初始化好为仓库的目录下】
-
git add <文件名>文件名>
若文件名处为
.
时表示添加当前目录下所有文件 -
git commit -m”<本题提交的说明>"本题提交的说明>
-m
后面输入的是本次提交的说明,方便从历史记录里方便地找到改动记录git成功会提示,
n file changed,m insertions
:n个文件被改动,插入了m行内容 -
多次add,一次commit
$ git add file1.txt $ git add file2.txt file3.txt $ git commit -m "add 3 files."
③获取工作区动态
-
git status:让我们时刻掌握仓库当前的状态【文件修改,文件是否最新,文件删除等】
我们删除了README.md文件,添加了text.md文件,但是在上次git add+commit以后没有再添加提交过,因此当前版本库的状态status是:
Changes not staged for commit列出了还没有准备提交的修改
deleted: README.md 文件被删除了
untracked file:从来没有被添加过的文件【text.md】
-
git diff <文件名>:查看文件变更/修改的具体内容【查看工作区和版本库里面最新版本的区别】文件名>
-
重新git add以后,通过git status 查看状态
提示了当前仓库的状态和变更情况
-
git commit,提交,得到提交后的变更情况
-
此时再
git status
$ git status On branch master nothing to commit, working tree clean
即Git告诉我们当前没有需要提交的修改,而且,工作目录是干净(working tree clean)
④小结
- 要随时掌握工作区的状态,使用
git status
命令。 - 如果
git status
告诉你有文件被修改过,用git diff
可以查看修改内容。 - 在Git中被称为
commit
认为是一个快照。一旦把文件改乱了,或者误删了文件,还可以从最近的快照中恢复——即从一个commit
中恢复
版本回退
一、历史记录总览
git log
命令显示从最近到最远的提交日志- commit id(版本号)【SHA-1,用十六进制表示】
- 和SVN不一样,Git的
commit id
不是1,2,3……递增的数字 commit id
需要用这么一大串数字表示呢?因为Git是分布式的版本控制系统,若多人在同一个版本库里工作,如果大家都用1,2,3……作为版本号,那肯定就冲突了。
- 和SVN不一样,Git的
- 提交者author
- 提交日期date
- commit命令 -m给出的message信息
- commit id(版本号)【SHA-1,用十六进制表示】
- git log –pretty=oneline:历史记录简述信息
- commit id(版本号)
- -m给出的message信息
二、回退
-
Git版本:
- 在Git中,用
HEAD
表示当前版本【即此时的5130a45....
】 - 上一个版本就是
HEAD^
,上上一个版本就是HEAD^^
- 往上100个版本可写成
HEAD~100
- 在Git中,用
-
git reset –hard HEAD^:回退到上一个版本
- 原来的text.md文件
- 回滚到该Git仓库的上一个版本【HEAD^——这里指的是add text.md】
- text.md变成之前的状态
-
此时再看git log!!!!之前那个add some的版本没了!
-
怎么回去?
- 前提:能知道要退回版本的commit id
- 只要上面的命令行窗口还没有被关掉,或者你还记得那个commit id
- 你就可以顺着往上找啊找啊,找到那个
add some
的commit id
是5130a4...
,于是就可以指定回到未来的某个版本:
$ git reset --hard <回到commit id> HEAD is now at 83b0afe append GPL
-
版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。
-
确实回来了
-
如果命令行被你关了,又不记得commit id了,怎么办?
-
Git提供了一个命令
git reflog
用来记录你的每一次命令:
三、版本回退原理
-
Git的版本回退速度非常快,因为Git在内部有个指向当前版本的
HEAD
指针,当你回退版本的时候,Git仅仅是把HEAD指针从当前版本修改到回退的版本上。 -
对于某个仓库有三个版本按照时间近到远有append GPL,add distributed以及wrote a readme file。当前版本为append GPL,现在回退到add distributed。
-
Git仅仅是把HEAD从指向
append GPL
:
改为指向add distributed
:
- 然后顺便把工作区的文件更新了。所以你让
HEAD
指向哪个版本号,你就把当前版本定位在哪。
四、小结
HEAD
指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id
。- 穿梭前,用
git log
可以查看提交历史,以便确定要回退到哪个版本。 - 要重返未来,用
git reflog
查看命令历史,以便确定要回到未来的哪个版本。
工作区和暂存区
-
工作区:在电脑里能看到的目录
-
当git init以后,普通的电脑目录——工作区就变成了版本库【有一个隐藏目录
.git
】、-
Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支
master
,以及指向master
的一个指针叫HEAD
。 -
文件往Git版本库里添加的时候,是分两步执行的
-
git add
:把文件修改添加到暂存区【把要提交的所有修改放到暂存区(Stage)】- 可以认为暂存区就是暂存修改
-
git commit
:提交更改,实际上就是把暂存区的所有内容提交到当前分支【可以一次性把暂存区的所有修改提交到分支】- 只提交add到暂存区的修改,没有add的修改不会更新到当前分支
- 因为我们创建Git版本库时,Git自动为我们创建了唯一一个`master`分支,所以,现在,`git commit`就是往`master`分支上提交更改。
-
一旦提交后,如果你又没有对工作区做任何修改,那么工作区就是“干净”的
$ git status On branch master nothing to commit, working tree clean
-
Git跟踪并管理的是修改,而非文件。
-
撤销修改
一、工作区回退
-
在工作区修改了 readme.txt但是还没有提交到暂存区时
-
用
git status
查看一下当前状态$ git status On branch master Changes not staged for commit: //changes还没有stage到暂存区 (use "git add <file>..." to update what will be committed) //提示通过git add提交到暂存区 (use "git checkout -- <file>..." to discard changes in working directory)//提示如果要抛弃工作区的修改,通过git checkout -- <file> modified: readme.txt no changes added to commit (use "git add" and/or "git commit -a")
-
命令
git checkout -- readme.txt
:把readme.txt
文件在工作区的修改全部撤销- 如果readme.txt只是在工作区修改,还没有提交到暂存区。那么这条命令会使得
readme.txt
撤销修改,回到和版本库一模一样的状态【也就是上一次add/commit以后的状态,即没有这个修改以前的状态】;——最近一次是git commit - 如果`readme.txt`已经添加到暂存区后,又作了修改,现在,撤销修改就**回到添加到暂存区后的状态**。【也是刚刚这个工作区的修改被撤销,但是之前提交到暂存区的修改没有被撤销】——最近一次是git add
- 如果readme.txt只是在工作区修改,还没有提交到暂存区。那么这条命令会使得
-
总之,就是让这个文件回到最近工作区修改以前的状态【在最近一次git add或git commit之前的那个状态】
git checkout -- file
命令中的--
很重要,没有--
,就变成了“切换到另一个分支”的命令
二、暂存区回退
-
修改已经add到暂存区了,此时工作区没有别的修改时
$ git status On branch master Changes to be committed://change已暂存,当还没commit (use "git reset HEAD <file>..." to unstage) //通过git reset HEAD <file>,可以撤销暂存区的修改 modified: readme.txt
-
用命令
git reset HEAD <file>
:可以把暂存区的修改撤销掉(unstage),重新**放回工作区** -
git reset
命令既可以回退版本【加–hard 版本】,也可以把暂存区的修改回退到工作区【没有–hard,直接版本+file名称】 -
git reset HEAD <file>
以后,暂存区是干净的,但是修改放回工作区了,因此工作区是脏的,有修改的【参考一、工作区退回】$ 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
-
再使用
git checkout -- readme.txt
,那么将会把工作区的修改也撤销。 -
所以通过两部,可以撤销暂存区的修改:
$ git status On branch master nothing to **commit**, working tree clean
三、小结
场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file
。【这个命令实际是:用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。但是只能恢复文件到最新版本HEAD,除非你版本回退了,HEAD变了】
场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD <file>
,就回到了场景1,第二步按场景1操作。
场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本退回一节【git reset –hard 版本号/版本标识】,不过前提是没有推送到远程库。
文件删除
一、工作区删除
- 在文件管理器中把没用的文件删了
- 用
rm
命令删了 git status
命令:会提示哪些文件被删除了【给出当前工作区状态】
二、版本库删除
-
从版本库中删除该文件:用命令
git rm
删掉+git commit -m"<message>"
- git rm:先删除工作区里的文件【相当于手动删除加git add,而rm只是工作区删除没有加add】
- git commit:提交删除到版本库
- 先手动删除文件,然后使用
git rm <file>
和git add<file>
效果是一样的。
$ git rm test.txt //从版本库中删除文件 rm 'test.txt' $ git commit -m "remove test.txt" [master d46f35e] remove test.txt 1 file changed, 1 deletion(-) delete mode 100644 test.txt //提示已经删除
三、撤销删除
-
工作区删错,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:
$ git checkout -- test.txt
git checkout
其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。