【VCS】【Git】Git快速入门
本篇博客意在让新手快速入门,达到Git日常使用需求。后续会另开几篇博客来探讨细节。
1 Git的安装
这部分网站上资料非常多,根据自己的系统查找资料并下载安装包安装即可。
2 初次使用Git之前的配置
2.1 配置用户名和邮箱
git config --global user.name "user_name"
git config --global user.email "email_address"
2.2 查看配置
git config --list
3 理论基础
3.1 为什么要掌握Git命令
可能有很多小伙伴习惯了Windows的操作模式,对命令行相对抵触。而且市面上确实有诸如TortoiseGit、SourceTree等Git图形化工具,使用体验也还尚可。但正所谓“Git的设计让使用者觉得自己比想象中的笨”,Git拥有太多强大的功能,而图形化工具中只封装了其中的一部分,还是命令行的功能全面。其次,很多老鸟回望来路,都认为Git的学习确实需要自底向上的过程。掌握了命令行,使用图形化工具如探囊取物。反之则不知其所以然,倘若后面遇到问题,就要抓瞎咯。
3.2 这么多差异版本,是如何保存的
3.2.1 SVN记录原理
SVN记录的是每一次版本变动的内容。换句话说,SVN只保存差异。
3.2.2 Git记录原理
Git记录的则是将每个版本独立保存。譬如说File1有5个版本,那就有5个对应的拷贝。这种方式看似更浪费空间,但在分支管理上带来了很多便利。
3.3 工作区、暂存区和Git仓库
3.3.1 工作区
就是我们存放代码的地方,看得见摸得着。
3.3.2 暂存区
实际上是一个文件,其中保存我们的改动。
3.3.3 Git仓库
最终存放我们所有版本数据的位置。HEAD指针指向的就是我们最新提交的版本。
3.4 Git的工作流程
- 在工作目录中增删、修改文件;
- 将需要进行版本管理的文件放入暂存区;
- 将暂存区的文件提交到Git仓库。
3.5 Git管理的文件状态
- 已修改(modified);
- 已暂存(staged);
- 已提交(committed)。
4 建仓、拉代码、添加到暂存区、提交
4.1 建仓
4.1.1 建立全新项目(空白目录)
在新建的空目录中执行以下命令,即可建仓:
git init
建仓之后,可以看到在空白目录中新增了一个.git目录。这个目录就是用来跟踪版本迭代的。
4.1.2 已有项目代码建仓
进入项目代码根目录后,执行git init即可。
4.2 从服务器拉代码
4.2.1 默认方式
git clone git@192.168.199.118:hisi/v35x_560.git # git clone 仓库地址
这种方式默认拉取远端的master分支
4.2.2 拉取指定分支
git clone -b dev git@192.168.199.118:hisi/v35x_560.git
我们可以通过增加-b branch_name选项来指定clone的分支
4.2.3 拉取所有分支
验证后补充
4.3 将文件添加到暂存区
新建一个Readme.txt,内含部分描述信息(这是在工作区发生的)。然后将其添加到暂存区:
git add Readme.txt
如果我们修改了很多文件,逐一add过于繁琐。此时可以通过如下命令,把工作区的修改一并加入暂存区:
git add -u
4.4 将暂存区中的修改提交到Git仓库
输入以下命令,将修改提交到Git仓库:
git commit -m "log"
我们还可以将略过添加暂存区的步骤,直接将文件提交到Git仓库:
git commit -am "log"
5 查看工作状态和历史提交
5.1 查看状态
我们可以使用以下命令来查看当前的状态:
git status
5.2 更新暂存区状态
某个文件已经加入暂存区,但我们又在工作区对其进行了修改,此时可以再次执行git add,更新暂存区中的状态。
5.3 还原工作区修改
如果我们在工作区修改了某个文件,但这些修改不想要了,那么可以执行以下命令,来还原工作区中的文件:
git checkout -- file_name
这里要注意,如果暂存区中有此文件,则会用暂存区的版本来覆盖工作区。如果暂存区中没有此文件,则会以Git仓库中的版本来覆盖工作区中的版本。如果想还原成Git仓库的版本,则可以先用git reset HEAD file_name将其从暂存区中移出,然后再使用git checkout – file_name,从而使工作区中的文件还原为Git仓库中的状态。
5.4 查看历史提交
我们可以使用git log来查看历史提交。commit ID是根据sha1算出来的。为什么不像SVN那样,从1开始排序?因为Git是分布式的,这样会引发冲突。
5.5 git log的常用选项
- –decorate:显示每个commit的引用(如:分支、tag等)
- –oneline:单行显示(简化版log);
- –graph:以图形化方式查看commit;
- –all:查看所有分支的commit。
6 回到过去
6.1 reset命令——用Git仓库中的版本覆盖暂存区
6.1.1 用Git仓库中HEAD所指版本覆盖暂存区(指定文件)
git reset HEAD file_name
6.1.2 用Git仓库中HEAD所指版本覆盖暂存区(所有文件)
git reset HEAD
6.1.3 如何使用Git仓库中的其他版本覆盖暂存区
首先,假设我们在工作区的所有修改都add并且commit了,可以用下图来表示当前状态:
接下来,使用reset命令来回滚快照:
git reset HEAD~ # ~表示HEAD所指版本的前一个版本,~~等以此类推。如果~太多不便表示,可以在~后加数字来指定
回滚之后的状态如下:
注意,reset命令实际包含了两个动作:
- 移动HEAD的指向,可以将其指向之前的快照(HEAD指针的位置发生了改变,所以看log时,看不到最后一次提交的信息。要用git reflog才能看到所有log)。
- 用HEAD移动后指向的快照覆盖暂存区。
6.2 reset命令的选项
6.2.1 --mixed
git reset --mixed HEAD~ # --mixed 是默认选项,不写也会自动加上
移动HEAD的指向,可以将其指向之前的快照。并用HEAD移动后指向的快照覆盖暂存区。
6.2.2 --soft
git reset --soft HEAD~
使用–soft选项,只移动HEAD的指向,使其指向之前的快照,但并不覆盖暂存区中的内容。
这种用法通常用于撤销错误的commit。
6.2.3 --hard
git reset --hard HEAD~6
移动HEAD的指向,使其指向之前的快照。并用HEAD移动后指向的快照来覆盖暂存区中的内容。此外,还会将暂存区中的文件还原到工作区(也是HEAD所指的Git仓库版本的状态)。
6.3 使用快照ID来执行reset
数HEAD毕竟麻烦,我们也可以使用快照ID来指定reset到哪个版本(不用全部ID,足够识别就行了)。
6.4 回滚个别文件
git reset commit_ID file_name
回滚个别文件时,HEAD指针就不移动了。
6.5 reset还可以往前滚
git reset commit_ID # 视情况使用 --hard
7 版本对比
7.1 比较工作区和暂存区的差异
git diff
7.2 比较两个历史快照
git diff commit_ID1 commit_ID2
7.3 比较工作区和Git仓库中的快照
git diff commit_ID # 快照 ID 用 HEAD 这种方式也可以
7.4 比较暂存区和Git仓库快照
git diff --cached/--staged [快照ID] # 如果不指定快照ID,则默认和仓库中最新commit比较
7.5 diff功能概览图
8 常用的小技巧
8.1 修改最后一次提交
情景一:代码已经commit到仓库,突然想起还有两个文件没有add。
情景二:代码已经commit到仓库,突然想起log写得不全面。
执行带–amend选项的commit命令,Git就会“更正”最近的一次提交。
git commit --amend # 在接下来的界面中可修改 log
git commit --amend -m "new log" # 直接提交新 log
8.2 删除文件
git rm file_name
此命令删除的只是工作区和暂存区的文件,也就是取消跟踪,下次提交时,不纳入版本控制。已经提交到Git仓库的文件是不会删除的,需要移动HEAD指针来切掉。
如果误删除,可以使用git checkout – file_name的方式来恢复。
如果某个文件在工作区和暂存区的内容不一致,git rm执行时会报错。我们可以用git rm -f file_name来操作,这会同时把工作区和暂存区的文件删除。
如果需要只删除暂存区的文件,保存工作区的文件,则可以执行git rm --cached file_name。
8.3 重命名文件
git mv old_name new_name
9 分支与分支管理
9.1 什么是分支?为什么要有分支?
9.2 创建分支
git branch branch_name
git checkout -b branch_name # 创建 branch_name 分支,并切换到此分支
9.3 切换分支
git checkout branch_name
9.4 查看分支
git branch -v # 查看本地分支
git branch -r # 查看远程分支
git branch -a # 查看本地分支和远程分支
9.5 合并分支
git merge branch_name # 将 branch_name 分支合并到当前分支
如果merge时提示conflict,则需要手动解决冲突。Git会在冲突的文件中加入一些提示。
9.6 删除分支
git branch -d branch_name
删除分支后再查看log,会发现虽然这些分支没有了,但在这些分支上提交的快照依然存在。
9.7 匿名分支
git checkout commit_ID
此时是分离头指针状态。由于我们使用了checkout命令,但并未创建新的分支,所以Git创建了一个匿名分支。既然是匿名分支,如果我们切换到其他分支,匿名分支中的所有操作都会被丢弃。所以我们可以用匿名分支来做些实验,反正没有什么影响。如果想保留匿名分支的操作,可以根据Git提示来操作,为其新建一个正式的分支。
10 将本地修改推到服务器端
前面说过,Git是分布式的VCS工具。我们本地有自己的仓库,服务器端有服务器端的仓库。为了确保其他人clone的代码中能包含我们的修改,就要把我们本地的修改推到服务器端。
我们通过git push命令,将本地修改推送到服务器端:
git push <远程主机名> <本地分支名>:<远程分支名>
通常用origin来表示远程主机名。
10.1 省略远程分支名
表示将本地分支推送到与之存在追踪关系的远程分支(通常同名)。如果该远程分支不存在,则会新建。
# origin: 远程主机名
# master: 本地分支名
git push origin master
10.2 省略本地分支名
表示删除远程分支。这一操作等同于推送一个空的本地分支到远程分支。
# origin : 远程主机名
# refs/for/master: 远程分支名
git push origin :dev
10.3 同时省略本地分支名和远程分支名
如果当前分支与远程分支存在追踪关系,则本地分支和远程分支都可以省略,将当前分支推送到origin主机的对应分支。
git push origin
10.4 同时省略远程主机名、本地分支名和远程分支名
如果当前分支只有一个远程分支,那么主机名都可以省略:
git push
10.5 push提示reject的处理方式
如果我们本地的代码不是服务器上最新版本,push代码时会被拒绝。此时我们要先更新本地代码,再push到服务器。
git reset --hard # 将本地代码恢复到最新commit状态
git pull --rebase # 将服务器更新合并到本地
11 更新本地代码
前文说到,Git是分布式的VCS,很多人都会向服务器端push修改。接下来看看,我们如何将服务器上的更新同步到本地。
11.1 本地只有 master 分支,且未做修改
此时的本地代码处于clean状态,所以可以直接使用git pull来更新。
11.2 本地只有master分支,且有修改
如果修改需要保留,要先将修改push到服务器端,然后再使用git pull来更新。
如果修改无需保留,可以使用git reset --hard将代码恢复到clean状态,然后再使用git pull来更新。
11.3 本地在自有分支上修改
由于我们在自己的分支(假定分支名为mybranch)上修改,master处于clean状态。此时要分四步处理:
- 切换到master分支:git checkout master。
- 更新master分支:git pull。
- 切换到mybranch分支:git checkout mybranch。
- 把master分支合并到mybranch分支:git merge master。