1.Git简介
git是目前世界上最先进的分布式版本控制系统(没有之一)。
-
什么是版本控制系统?
是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。 -
什么是分布式的版本控制系统?
先了解一下集中式版本控制系统,版本库是集中存放在中央服务器的,干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。集中式的弊端很明显,必须联网才能使用,如果在局域网内还好。
那分布式版本控制系统与集中式版本控制系统有何不同呢?首先,分布式版本控制系统没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这样,工作的时候,对网络的依赖就很低,因为版本库就在自己的电脑上。既然每个人电脑上都有一个完整的版本库,那多个人如何协作呢?比方说在自己电脑上改了文件A,同事也在他的电脑上改了文件A,这时,只需把各自的修改推送给对方,就可以互相看到对方的修改了。
2.Git创建版本库
配置用户名和邮箱
配置用户名和邮箱,一般在安装完成之后就可以配置。
git config --global user.name "Your Name"
git config --global user.email "email@example.com"
由于Git是分布式版本控制系统,所以每个机器都是一个版本库,使用的时候都要配置用户名和邮箱。
注意:git config命令的–global参数,用了这个参数,表示你这台机器上所有的Git仓库都会使用这个配置,也可以对某个仓库指定不同的用户名和Email地址。
创建版本库
- 什么是版本库?
版本库又名仓库(repository),可以理解为目录,这个目录里面的所有文件都会被Git管理,文件的修改、删除和新增Git都能跟踪,以便于追踪历史或者还原到某时刻。
git init
$ git init
执行完 git init就会创建一个只有主分支的空仓库,之后会发现当前路径多了一个.git的目录(如果没有找到,这个目录默认是隐藏的),这个目录就是git来跟踪管理版本库的,千万不要手动修改。
添加文件到Git仓库
我们在当前路径下创建一个readme.txt文件,内容如下:
Git is a version control system.
Git is free software.
git add
- 首先执行
$ git add readme.txt
执行完后没有任何显示就对了,Unix的哲学是“没有消息就是好消息”,说明添加成功。
git commit -m “提交说明”
- 第二步
$ git commit -m "wrote a readme file"
[master (root-commit) eaadf4e] wrote a readme file
1 file changed, 2 insertions(+)
create mode 100644 readme.txt
git commit命令参数- m后面输入的是本次提交的说明,git commit命令执行成功后会告诉你:
- 1 file changed ,1个文件被改动(新添加的readme.txt文件)
- 2 insertions:插入了两行内容(readme.txt有两行内容)
为什么git添加文件要add和commit两步呢?
因为commit可以一次提交很多文件,所以可以多次add不同文件之后再提交,比如:
$ git add file1.txt
$ git add file2.txt file3.txt
$ git commit -m "add 3 files."
3.版本回退
当我们对一个问题修改提交了很多次,版本控制中的git log可以查看提交历史
git log
git log命令显示从最近到最远的提交日志。
$ git log
commit 1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master)
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:06:15 2018 +0800
append GPL
commit e475afc93c209a690c39c13a46716e8fa000c366
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:03:36 2018 +0800
add distributed
commit eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 20:59:18 2018 +0800
wrote a readme file
如果嫌输出信息太多,可以加上参数–pretty=oneline
$ git log --pretty=oneline
1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master) append GPL
e475afc93c209a690c39c13a46716e8fa000c366 add distributed
eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0 wrote a readme file
我们当前版本是append GPL,如果想回退到上一个版本,也就是add distributed,就可以使用get reset命令
git reset
$ git reset --hard HEAD^
HEAD is now at e475afc add distributed
也可以继续执行上面命令再回退到上一个版本,也就是wrote a readme file。不过我们执行以下git log再看看版本库状态:
$ git log
commit e475afc93c209a690c39c13a46716e8fa000c366 (HEAD -> master)
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:03:36 2018 +0800
add distributed
commit eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 20:59:18 2018 +0800
wrote a readme file
发现最新版本append GPL已经没了,如何恢复,如果你的命令窗口还没关,就可以找到append GPL 的commit id是1094a…,于是就可以回到这个版本:
$ git reset --hard 1094a
HEAD is now at 83b0afe append GPL
版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。
Git版本切换速度非常快,因为Git内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git只是把HEAD从新指向了新的版本。
如果你关闭了窗口,找不到commit id,可以用git reflog查找你每一次执行的命令:
git reflog
$ git reflog
e475afc HEAD@{1}: reset: moving to HEAD^
1094adb (HEAD -> master) HEAD@{2}: commit: append GPL
e475afc HEAD@{3}: commit: add distributed
eaadf4e HEAD@{4}: commit (initial): wrote a readme file
也可以找到被替换的版本号。
4.工作区和暂存区
电脑中能看到的目录为工作区,工作区还有个隐藏目录.git,这个是Git的版本库,版本库里面有很多东西,其中最重要的就是stage(或者叫index)的暂存区,还有Git为我们创建的第一个分支master,一级指向第一个分支的指针叫HEAD
前面讲把文件往Git版本库添加的时候是分两步的:
- 第一步是git add把文件添加进去,实际上就是把文件修改添加到暂存区
- 第二步是git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。
创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。
我们在工作区新增一个LICENSE文本文件,先用git status查看一下状态:
git status
$ 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
Untracked files:
(use "git add <file>..." to include in what will be committed)
LICENSE
no changes added to commit (use "git add" and/or "git commit -a")
使用两次命令git add,把readme.txt和LICENSE都添加后,用git status再查看一下:
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: LICENSE
modified: readme.txt
现在,暂存区的状态就变成这样了:
然后,执行git commit就可以一次性把暂存区的所有修改提交到分支。
$ git commit -m "understand how stage works"
[master e43a48b] understand how stage works
2 files changed, 2 insertions(+)
create mode 100644 LICENSE
一旦提交后,如果你又没有对工作区做任何修改,那么工作区就是“干净”的:
$ git status
On branch master
nothing to commit, working tree clean
版本库变成了这样,暂存区就没有任何内容了:
git diff HEAD – readme.txt命令可以查看工作区和版本库里面最新版本的区别:
5.撤销修改
撤销工作区修改
git checkout –
git checkout – file可以丢弃工作区的修改:
$ git checkout -- readme.txt
命令git checkout – readme.txt意思就是,把readme.txt文件在工作区的修改全部撤销,这里有两种情况:
一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
一种是readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近一次git commit或git add时的状态。
注意:git checkout – file命令中的–很重要,没有–,就变成了“切换到另一个分支”的命令
撤销暂存区修改
已经修改文件了且执行了git add提交到暂存区,用命令git reset HEAD 可以把暂存区的修改撤销掉(unstage),重新放回工作区:
git reset HEAD
$ git reset HEAD readme.txt
Unstaged changes after reset:
M readme.txt
git reset命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本。
再用git status查看一下,现在暂存区是干净的。
撤销版本库的修改
参照第3节版本回退。
还记得Git是分布式版本控制系统吗?我们后面会讲到远程版本库,一旦你把stupid boss提交推送到远程版本库就不好处理。
删除文件
直接在文件资源管理器中删除文件,执行git status查看那些文件被删了:
$ 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 rm删掉,并且git commit文件就从版本库中删除了:
git rm
$ 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其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
注意:从来没有被添加到版本库就被删除的文件,是无法恢复的!
6.Git远程仓库
最早,肯定只有一台机器有一个原始版本库,此后,别的机器可以“克隆”这个原始版本库,而且每台机器的版本库其实都是一样的,并没有主次之分。
我们可以在Git上创建一个远程库,然后关联本地库再推送,需要添加SSH Key,这里不做赘述。
Git 并不像 SVN 那样有个中心服务器。目前我们使用到的 Git 命令都是在本地执行,如果你想通过 Git 分享你的代码或者与其他开发人员合作。 你就需要将数据放到一台其他开发人员能够连接的服务器上。
添加远程库
要添加一个新的远程仓库和github关联
git remote add origin url
$ git push -u origin url
origin是远程库的名字url是远程仓库url,这样本地的文件add且commit之后执行push就会推送到github上,其他同事就可以下载,
git push origin master
$ git push origin master
把本地master分支的最新修改推送至GitHub,现在,你就拥有了真正的分布式版本库。
从远程库克隆
和上一节的区别是,我们要从远程仓库获取文件,就要用到git clone
git clone url
$ git clone git@github.com:michaelliao/gitskills.git
就会克隆一个本地库。
如果想克隆到某个目录可以这样:
$ git clone git@github.com:michaelliao/gitskills.git D:\project
你也许还注意到,GitHub给出的地址不止一个,还可以用https://github.com/michaelliao/gitskills.git这样的地址。实际上,Git支持多种协议,默认的git://使用ssh,但也可以使用https等其他协议。
7.创建于合并分支
在之前我们没学远程仓库的时候,在本地提交Git都把它串成一条时间线,这条时间线就是一个分支。在Git里,这个分支叫主分支,即master分支。HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。
每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越来越长。
当我们创建新的分支,例如dev时,指向master相同的提交,当切换到dev分支时,HEAD就会指向dev。
Git创建一个分支很快,因为除了增加一个dev指针,改改HEAD的指向,工作区的文件都没有任何变化。不过,从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变。
假如我们在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并。
下面开始实战:
首先,我们创建dev分支,然后切换到dev分支:
git checkout -b dev
$ git checkout -b dev
Switched to a new branch 'dev'
git checkout命令加上-b参数表示创建并切换,相当于以下两条命令:
git branch dev
git checkout dev
$ git branch dev
$ git checkout dev
Switched to branch 'dev'
然后,用git branch命令查看当前分支:
$ git branch
* dev
master
git branch命令会列出所有分支,当前分支前面会标一个*号。
然后就可以在dev分支上提交,比如修改readme.txt,然后提交:
$ git add readme.txt
$ git commit -m "branch test"
[dev b17d20e] branch test
1 file changed, 1 insertion(+)
现在,dev分支的工作完成,我们就可以切换回master分支:
$ git checkout master
Switched to branch 'master'
切换回master分支后,再查看一个readme.txt文件,刚才添加的内容不见了!因为那个提交是在dev分支上,而master分支此刻的提交点并没有变:
现在,我们把dev分支的工作成果合并到master分支上:
git merge
$ git merge dev
Updating d46f35e..b17d20e
Fast-forward
readme.txt | 1 +
1 file changed, 1 insertion(+)
git merge命令用于合并指定分支到当前分支,这个时候dev分支和master分支就一模一样了。
合并完成后,就可以放心地删除dev分支了:
git branch -d dev
$ git branch -d dev
Deleted branch dev (was b17d20e).
删除后,查看branch,就只剩下master分支了:
$ git branch
* master
切换分支这个动作,用switch更科学。因此,最新版本的Git提供了新的git switch命令来切换分支:
创建并切换到新的dev分支,可以使用:
$ git switch -c dev
直接切换到已有的master分支,可以使用:$ git switch master
$ git switch master
8.配置别名
有没有经常敲错命令?比如git status?
如果敲git st就表示git status那就简单多了,当然这种偷懒的办法我们是极力赞成的。
我们只需要敲一行命令,告诉Git,以后st就表示status:
$ git config --global alias.st status
配置一个git last,让其显示最后一次提交信息:
$ git config --global alias.last 'log -1'
甚至还有人丧心病狂地把lg配置成了:
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
9.配置文件
配置Git的时候,加上–global是针对当前用户起作用的,如果不加,那只针对当前的仓库起作用。
Git的仓库的配置文件是.git/config
$ cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = git@github.com:michaelliao/learngit.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
[alias]
last = log -1
别名就在[alias]后面,要删除别名,直接把对应的行删掉即可。
而当前用户的Git配置文件放在用户主目录下的一个隐藏文件.gitconfig中:
$ cat .gitconfig
[alias]
co = checkout
ci = commit
br = branch
st = status
[user]
name = Your Name
email = your@email.com
配置别名也可以直接修改这个文件,如果改错了,可以删掉文件重新通过命令配置。
参考:
https://www.liaoxuefeng.com/wiki/896043488029600
https://www.runoob.com/git/git-tutorial.html