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