初步浅析 Git 教程

简单入门 Git,提升 Git 基本功

Posted by 陈谭军 on Wednesday, August 1, 2018 | 阅读 |,阅读约 10 分钟

Git 概念

Git 是目前世界上最先进的分布式版本控制系统,版本控制系统主要分为:CVS(集中式的版本控制系统)、SVN(集中式版本控制系统)、BitKeeper(商用)、类似 Git 的 Mercurial 和 Bazaar 等。集中式的版本控制系统:中央服务器是一个版本库 一旦宕掉所有的版本失效。分布式版本控制系统:中央服务器是暂存提交的版本。

Git 安装

Debian 或 Ubuntu Linux:通过一条 sudo apt-get install git 就可以直接完成 Git 的安装,非常简单。 Windows:https://git-scm.com/downloads 下载相应的文件,然后按照提示进行 Git 安装。

Git 示例

使用 Git 创建文件并且提交文件,进入某个磁盘下创建文件 (以windows 为例)。

如上图在磁盘上创建了 GitTest 文件,右击该文件。

git config --global user.name "Your Name"
git config --global user.email "email@example.com"

注意 git config 命令的 –global 参数,该参数表示你这台机器上所有的 Git 仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和 Email 地址,创建 readme.txt 文件。 内容为:

Git is a version control system.
Git is free software.

第一步,用命令 git add 告诉 Git,把文件添加到仓库 git add readme.txt

第二步,用命令 git commit 告诉 Git,把文件提交到仓库 git commit -m "wrote a readme file"

Git log 查看历史提交信息

git log --pretty=oneline

回退到某个版本,HEAD^ 回退到上一个版本,HEAD^^ 回退到上上一个版本,HEAD~100 回退到往上一百个版本。

git reset --hard HEAD^
git reset --hard HEAD^^
git reset --hard HEAD~100

Git 提供了一个命令 git reflog 用来记录你的每一次命令,可以通过这个命令回退到未来的某个版本。

总结: HEAD 指向的版本就是当前版本,因此,Git 允许我们在版本的历史之间穿梭,使用命令 git reset --hard commit_id,穿梭前,用 git log 可以查看提交历史,以便确定要回退到哪个版本。要重返未来,用 git reflog 查看命令历史,以便确定要回到未来的哪个版本。

工作区和暂存区

Git 和其他版本控制系统如 SVN 的一个不同之处就是有暂存区。工作区就是电脑工作的目录,如上图的 GitTest 目录。工作区有一个隐藏目录,git 这个不算工作区,而是 Git 的版本库。

上文中的第一步是用 git add 把文件添加进去,实际上就是把文件修改添加到暂存区;第二步是用 git commit 提交更改,实际上就是把暂存区的所有内容提交到当前分支。

使用 git status 查看一下状态。

git add 命令实际上就是把要提交的所有修改放到暂存区(Stage);执行 git commit 就可以一次性把暂存区的所有修改提交到分支。

管理修改

Git 管理的是修改,当你用 git add 命令后,在工作区的第一次修改被放入暂存区,准备提交。但是,在工作区的第二次修改并没有放入暂存区,git commit 只负责把暂存区的修改提交了,也就是第一次的修改被提交了,第二次的修改不会被提交。

那怎么提交第二次修改呢?你可以继续 git addgit commit。也可以别着急提交第一次修改,先 git add 第二次修改,再 git commit 就相当于把两次修改合并后一块提交了。

撤销修改

命令 git checkout -- readme.txt 意思就是,把 readme.txt 文件在工作区的修改全部撤销。这里有两种情况:一种是 readme.txt 自修改后还没有被放到暂存区,撤销修改就回到和版本库一模一样的状态;一种是 readme.txt 已经添加到暂存区后,又作了修改,撤销修改就回到添加到暂存区后的状态。总之,就是让这个文件回到最近一次 git commitgit add 时的状态。

命令 git reset HEAD <file> 可以把暂存区的修改撤销掉(unstage),重新放回工作区。

  1. 场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令 git checkout -- file 即可。
  2. 场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令 git reset HEAD <file>,就回到了场景1,第二步按场景1操作即可。
  3. 场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考 git reset --hard

删除文件

Git 中,删除也是一个修改操作,我们实战一下,先添加一个新文件 test.txt 到 Git 并且提交。一般情况下,你通常直接在文件管理器中把没用的文件删了,现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令 git rm 删掉,并且 git commit。另一种情况是删错了,因为版本库里还有,所以可以很轻松地把误删的文件恢复到最新版本:git checkout -- test.txtgit checkout 其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以一键还原。命令 git rm 用于删除一个文件,如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容。

添加远程库

你已经在本地创建了一个 Git 仓库后,又想在 GitHub 创建一个 Git 仓库,并且让这两个仓库进行远程同步。GitHub 上的仓库既可以作为备份,又可以让其他人通过该仓库来协作。

根据 GitHub 的提示,在本地的 git 仓库下运行命令。

git remote add origin git@github.com:账户名/learngit.git

把本地库的所有内容推送到远程库上。

git push -u origin master

由于远程库是空的,我们第一次推送 master 分支时,加上了 -u 参数,Git 不但会把本地的 master 分支内容推送到远程新的 master 分支,还会把本地的 master 分支和远程的 master 分支关联起来,在以后的推送或者拉取时就可以简化命令。从现在起,只要本地作了提交,就可以通过命令推送。

git push origin master

克隆远程库

用命令 git clone 克隆一个本地库,git clone git@github.com:账户名/gitskills.git

GitHub 给出的地址不止一个,还可以用 https://github.com/michaelliao/gitskills.git 这样的地址。实际上,Git 支持多种协议,默认的 git:// 使用 ssh,但也可以使用 https 等其他协议。(Git 支持多种协议,包括 https,但通过 ssh 支持的原生 git 协议速度最快,使用 https 除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放 http 端口的公司内部就无法使用 ssh协议而只能用 https)。

分支管理

Git 的分支是与众不同的,无论你的版本库是 1 个文件还是 1 万个文件,无论创建、切换和删除分支,Git 在 1 秒钟之内就能完成!!!

创建分支

合并分支

删除分支

分支管理的命令如下:git checkout -b dev 相等于 git branch dev 或者 git checkout dev。用 git branch 命令查看当前分支,git branch 命令会列出所有分支,当前分支前面会标一个*号。我们把 dev 分支的内容合并到 master 分支上。dev 分支的工作完成,我们就可以切换回 master 分支。

git checkout master

git merge 命令用于合并指定分支到当前分支。合并后,再查看 readme.txt 的内容,和 dev 分支的最新提交是完全一样的。

创建于合并分支管理,Git 鼓励使用分支。

查看分支:git branch
创建分支:git branch <name>
切换分支:git checkout <name>
创建+切换分支:git checkout -b <name>
合并某分支到当前分支:git merge <name>
删除分支:git branch -d <name>
......

解决冲突

git checkout -b feature1
git add readme.txt
git commit -m "AND simple"
git checkout master
git commit -m "& simple"

这种情况下,Git 无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突。

当 Git 无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。解决冲突就是把 Git 合并失败的文件手动编辑为我们希望的内容,再提交。使用 git log --graph 命令可以看到分支合并图。

分支管理策略

合并分支时,如果可能,Git 会用 Fast forward 模式,但这种模式下,删除分支后,会丢掉分支信息。如果强制禁用 Fast forward 模式,Git 就会在 merge 时生成一个新的 commit,这样从分支历史上就可以看出分支信息。

下面我们实战一下 --no-ff 方式的 git merge

git checkout -b dev
git add readme.txt 
git commit -m "add merge"
git checkout master
git merge --no-ff -m "merge with no-ff" dev
git log

Git 还提供了一个 stash 功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作。git checkout -b issue-101 修复代码的分支(仅在于紧急修复 BUG)。一种方式是用 git stash apply 恢复,但是恢复后,stash 内容并不删除,你需要用 git stash drop 来删除;另一种方式是用 git stash pop,恢复的同时把 stash 内容也删了。

git stash list 查看,就看不到任何 stash 内容。

当手头工作没有完成时,先把工作现场 git stash 一下,然后去修复 bug,修复后再 git stash pop,回到工作现场。开发一个新feature,最好新建一个分支;如果要丢弃一个没有被合并过的分支,可以通过 git branch -D <name> 强行删除。

推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,Git 就会把该分支推送到远程库对应的远程分支上:git push origin master,如果要推送其他分支,比如 dev,则可以使用 git push origin dev

多人协作工作模式

首先,可以试图用 git push origin <branch-name> 推送自己的修改;如果推送失败,则因为远程分支比你的本地更新,需要先用 git pull 合并上游最新提交。如果合并有冲突,则解决冲突,并在本地提交;没有冲突或者解决掉冲突后,再用 git push origin <branch-name> 推送就能成功。如果 git pull 提示 no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令 git branch --set-upstream-to <branch-name> origin/<branch-name>。这就是多人协作的工作模式,一旦熟悉了,就非常简单。

总结使用:

查看远程库信息,使用 `git remote -v`。
本地新建的分支如果不推送到远程,对其他人就是不可见的。
从本地推送分支,使用 `git push origin branch-name`,如果推送失败,先用 `git pull` 抓取远程的新提交。 
在本地创建和远程分支对应的分支,使用 `git checkout -b branch-name origin/branch-name`,本地和远程分支的名称最好一致。
建立本地分支和远程分支的关联,使用 `git branch --set-upstream branch-name origin/branch-name`。
从远程抓取分支,使用 `git pull`,如果有冲突,要先处理冲突。

rebase

这就是 rebase 操作的特点,把分叉的提交历史“整理”成一条直线,看上去更直观。缺点是本地的分叉提交已经被修改过了。rebase 操作可以把本地未 push 的分叉提交历史整理成直线。rebase 的目的是使得我们在查看历史提交的变化时更容易,因为分叉的提交需要三方对比。

标签管理

git tag <name> 就可以打一个新标签。git tag 查看所有标签,对历史的提交进行标签活动,需要找到 commit id。如 git tag v0.9 f52c633git show <tagname> 查看标签信息,还可以创建带有说明的标签,用 -a 指定标签名,-m 指定说明文字。

命令 git tag <tagname> 用于新建一个标签,默认为 HEAD,也可以指定一个 commit id;命令 git tag -a <tagname> -m "blablabla" 可以指定标签信息;命令 git tag 可以查看所有标签。如果要推送某个标签到远程,使用命令 git push origin <tagname>。删除已经提交的版本tag,先本地删除 git tag -d v0.9,然后远程删除 git push origin :refs/tags/v0.9

总结:

git push origin <tagname> 可以推送一个本地标签
git push origin --tags 可以推送全部未推送过的本地标签
git tag -d <tagname> 可以删除一个本地标签
git push origin :refs/tags/<tagname>可以删除一个远程标签

Git 仓库

Github

在 GitHub 上,可以任意 Fork 开源仓库。自己拥有 Fork 后的仓库的读写权限,可以推送 pull request 给官方仓库来贡献代码。

码云

GitHub 相比,码云也提供免费的 Git仓库。此外,还集成了代码质量检测、项目演示等功能。对于团队协作开发,码云还提供了项目管理、代码托管、文档管理的服务,5 人以下小团队免费。使用码云和使用 GitHub 类似,我们在码云上注册账号并登录后,需要先上传自己的SSH公钥。

附录

常用工具

git config --global alias.st status  状态
git config --global alias.co checkout 切换主目录
git config --global alias.ci commit 提交代码
git config --global alias.br branch 查看分支
git config --global alias.unstage 'reset HEAD'
git reset HEAD file 可以把暂存区的修改撤销掉 unstage 重新放回工作区
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"
......

自定义 Git

某文件不需要提交,在 Git 工作区的根目录下创建一个特殊的 .gitignore 文件,然后把要忽略的文件名填进去,Git 就会自动忽略这些文件。 所有配置文件可以直接在线浏览 https://github.com/github/gitignore。忽略文件的原则是忽略操作系统自动生成的文件,比如缩略图等。 忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如 Java 编译产生的 .class 文件,忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。.gitignore 文件写完后,也要提交到 Git 中。

总结

通过详细的介绍和示例,教程详细解释了 Git 的基本概念、工作流程、常用命令和常见操作。通过学习教程,可以了解到 Git 的分支管理、合并冲突解决、远程仓库和团队协作等高级功能。总之,这个教程适合任何想要学习 Git 的人,无论是初学者还是有一定经验的开发人员。它是学习和运用 Git 进行版本管理的绝佳指南。