Skip to main content

Git/GitHub 简明教程

· 25 min read

本文假定读者已经有一定的命令行使用基础,并且以最少量的文字进行介绍,如有不尽之处请参考Git 官方教程

简单的简介

Git 是一个分布式版本控制系统,版本控制系统简单说就是它可以记录文件的每一步改动并且能够让你非常方便的将多个人的文件内容合并到一起,分布式就是指每一份代码仓库都会保留完整的文件和历史记录。Git 最初由 Linus 创造,目前应用最广泛的领域就是代码开发。
此处请记住:git 保存的不是文件而是对文件的修改,如果后面有哪里不理解就请再看一遍这句话。

简单的安装介绍

  1. Windows 用户:可以直接从官网上下载安装包安装
  2. MacOS 用户:可以使用HomeBrew安装或下载应用商店的 Xcode 并打开'Prefrence'->'Download'->'Command Line Tool'即可
  3. Linux 用户:软件包的名字就叫 git,源代码官网也有,多的就不说了
  4. 类 ChromeOS 用户:在宿主安装请使用ChromeBrew,在 Linux 子系统安装请参考上一条
  5. Android 用户:请选择一款终端(比如 Termux)并按照使用说明安装软件包

安装之后请先检查命令行下输入git --version 检查是否有显示 git 的版本信息,如果没有请查找如何将 git 添加到命令行。

简单的初始化配置

git config --global user.name "你的名字"
git config --global user.email "你的邮箱地址"

这两个命令的作用是设置你自己的信息,这些信息将会用来注明文件的修改是由哪个人完成的。
接下来请自行创建一个空文件夹并 cd 到文件夹内(路径请不要包含中文,下文假定该文件夹完整路径为/users/auser/gitLearning),输入

git init

该命令会在当前文件见创建一个名为.git的隐藏文件夹,一个 git 仓库的所有信息都存储在这个文件夹中,文件夹内容由 git 自行控制请不要手动修改,如果需要删除仓库删除此文件夹即可。在后文叙述中我们将默认忽略这个文件夹的存在。
出于各方面的考虑,git 能够记录纯文本文件的具体内容和二进制文件的基本信息的改动,所以要想发挥 git 的作用,就要把文字存储到纯文本文件中,另外一般情况下请使用UTF-8 without BOM编码来保存纯文本文档。

二进制文件纯文本文件
.doc .docx.txt
.xls .xlsx.csv
.pdf.md

按照惯例我们在版本库内放入一个空白的名为 README.md 的文件,如果不熟悉这个扩展名请点击这里
目前文件夹结构为

root
- users
- otheruser
- auser
- gitLearning
- .git (后文将忽略此文件夹)
- README.md
git status

这是我们在建立仓库之后应该知道的第一个命令,特别是在 git 使用还不熟练的时候,这个命令在任何时候都可以为我们提供有用的信息
目前版本库中还没有记录这个新放入的 README.md 文件,所以我们执行

git add README.md

该命令不会有回显。在日常使用中一次更新一般会包含文件的增加、删除、修改,此时使用上面的命令逐个记录就太繁琐了,使用下面的命令可以直接记录所有改动

git add -A

上面的命令只是让 git 记录了修改,并没有正式提交到仓库中,即没有生成一个新的版本,要提交修改只需要执行下面命令

git commit -m "这里填写本次修改的主要内容"

这个操作将会使 git 生成一个新的版本(类似于软件开发中的版本,这里指 git 仓库版本)。原则上每次提交都要求添加版本说明,目的是方便以后查阅更新历史

简单的远程仓库同步

git clone 远程仓库地址

这个命令可以完整地复制网络上别人的仓库,即使是不使用 git 的 Linux 用户也会经常用到这个命令,在这个操作之后,仓库会被放在当前路径下,并且已经包含了名叫 origin 的远程仓库名,远程仓库名就是远程仓库地址的别名,如果是在本地自行创建的仓库不会包含远程仓库名,可以自行添加

git remote add 自己命名的远程仓库名 远程仓库地址

添加后可以检查一下远程仓库列表

git remote -v

添加之后就可以进行仓库同步了,下面分别是从远程更新修改和将修改推送至远程

git pull 远程仓库名
git push 远程仓库名 master

其他有关远程仓库的操作看命令就可以明白用处

git remote show 远程仓库名
git remote rm 远程仓库名
git remote rename 远程仓库名 新的远程仓库名

简单的分支/版本管理

下面我们通过类似链表的方法来理解分支管理,在一定程度上缺乏严谨性

假定的文件结构

root
- users
- otheruser
- auser
- gitLearning
- .git
- README.md
- file1.txt
- file2.txt
- folder
- file3.txt

什么是分支/版本

可以理解为每个分支是一条链表,链表上的每个节点都是一个版本,每个链表都有它的分支名(由用户决定名称,默认第一个分支叫做 master),每个节点都有它自己的版本名(由 git 随机生成)。链表之间又有一些指针相连,没有孤立的链表,用户只能对链表尾部的节点进行删除、增加操作。

分清四个区域

工作区:即在gitLerarning文件夹中显示的文件,是被修改最频繁的区域
暂存区:由 git 管理,是已被 git 记录但并未创建新版本的修改
stash 区:用于保存某个时间点的工作区和暂存区
版本库:即已经被 git 记录并生成版本号的内容,版本库中的每个节点都是一个版本。

在不同的机器上同步 git 仓库时只会同步版本库。
工作区和暂存区可以理解为孤立的节点,stash 区可以理解为一个单链表,版本库可以理解为一个图

分清三种指针

分支指针:指向每个分支的最新版本
HEAD 指针:指向了当前分支的分支指针
标签指针:自定义指针,方便查找

被修改最频繁的工作区和暂存区

之所以说修改最频繁,是因为在某些情况下我们不仅会使用编辑器修改其中的文件,还会使用 git 对整个文件夹内容进行修改。
假设我们需要查看 dev 分支,就可以使用如下指令

git checkout dev

这个操作在逻辑上可以理解为清除工作区节点和暂存区节点的内容并将 dev 分支指针指向的节点内容复制到工作区节点,在实际操作上可以理解为删除gitLearning中的所有文件并按照仓库的记录重新创建文件。
前面提到,我们创建新的版本之前要使用git add命令,这个命令将会把指定的文件的修改从工作区更新至暂存区,如果需要撤销这个操作,可以使用下面的命令

git reset HEAD 文件名

而如果我们想进一步将工作区的修改也还原到未修改的状态可以使用

git checkout -- 文件名

请时刻注意修改工作区即修改你的文件,只要是没有保存在版本库中的修改都会丢失,git 不会对你的操作进行二次询问。
前面提到的git commit操作在逻辑上是将暂存区节点添加到当前分支的结尾,并重新创建一个空的暂存区节点。也就是说工作区的修改只有添加到暂存区中才能进一步添加到版本库。
相应的,如果你对自己在工作区和暂存区进行的修改全都不满意,使用下面的命令可以撤销所有修改即清空工作区和暂存区节点。

git reset --hard HEAD

这个命令的另一个用处就是查看历史版本:

git reset --hard HEAD^ (有几个^符号就是往前多少个版本)
git reset --hard HEAD~2 (数字是几就往前多少个版本)

上面的命令与使用git checkout命令修改当前分支有些相似,但是前者是在不同分支之间横向切换,这里的命令是在同一个分支的不同历史版本中纵向切换。
需要注意的是,执行这些命令都会清空重建工作区和暂存区节点,也就是说没有通过git commit保存的修改都将丢失。如果你想在不使用git commit的情况下保留工作区和暂存区的改动进行切换分支或版本的操作,就需要用到 stash 区了。

用于备份的 stash 区

git stash

这个命令将会把工作区和暂存区的内容压缩为一个节点添加到 stash 区内并且清空工作区和暂存区,我们将这种操作称为保存现场。stash 区可以保存多个现场,在恢复现场时我们需要先查找那个现场的名字

git stash list

这个命令将会给出 stash 区内保存的现场信息,然后我们就可以通过现场名进行恢复

git stash apply 现场名  (从stash中恢复现场)
git stash pop 现场名 (从stash中恢复现场并从stash中删除)
git stash drop 现场名 (仅删除现场)

上面的三条命令可以类比对栈的操作。

复杂又简单的分支

假设我们目前只有默认的 master 分支,我们可以通过下面的命令创建一个新的分支

git branch 分支名

新的分支基于当前分支建立,在逻辑上就是建立一个新的链表并创建一个指针从 master 的最新版本指向新链表的第一个节点。使用下面的命令可以检查仓库内的所有分支

git branch

我们使用git checkout切换到新分支,这里假定新分支名叫 dev。我们可以发现此时 dev 分支的文件和 master 分支完全相同,还记得前面提到的 git 版本的不是文件而是文件改动吗,由于新创建的 dev 分支没有保存任何新的改动,所以沿着指针向前追溯至 master 分支的第一个版本,然后根据途中记录的变动得出的文件和 master 最新版本是相同的。
另外使用命令参数可以一步创建并切换至新分支

git checkout -b 分支名

有分必有合,当我们需要将两个不同分支的改动合并到一起时,就需要用到下面的命令。当然,在合并之前请保证工作区和暂存区是空的。

git merge 分支名

这个命令将会使 git 尝试将指定的分支合并到当前所在的分支,之所以说是尝试,是因为两个分支各自独立记录改动,如果有某些内容在两个分支中进行了不同的改动,还需要由用户来解决冲突,使用git status可以查看当前的冲突状态,在有冲突的文件内有冲突的行会被 git 标记出两个分支中的该部分的内容,例如:

<<<<<<< HEAD:README.md (这里表示是当前分支的README文件)
PHP是世界上最好的语言
=======
C是世界上重要的语言
>>>>>>> dev:README.md (这里表示是dev分支的README文件)

你要做的就是将这些内容(此处是五行内容)全部删除并保留合并之后应该是什么内容,比如说:

C是世界上重要的语言

或者:

JAVA是世界上最好的语言

然后使用git add命令将此文件加入暂存区,git 就会认为此冲突已经被手动解决。 所有冲突都解决之后使用git commit即可使合并生效,注意这里已经不仅是创建新版本了,而是创建了一个由两个分支合并得到的版本。一般情况下,我们会在合并之后删除被合并的分支,但现在我们要退回合并之前,了解一下快速合并(Fast-forward)。
如果你在 master 分支中分出一个 dev 分支,并且在 dev 分支合并回 master 时 master 仍然没有任何变化,git 默认会将 master 分支指针直接指向 dev 分支指针的位置,这种操作的速度快、不需要建立新的版本、合并效果相同,但是坏处就是 master 分支接收了 dev 分支的所有提交记录,对于两个将要长期存在的分支来说这样的操作会让版本库显得很乱,但对于一个临时分支来说,这样操作既可以安全地修改 master 分支又不会留下临时分支的痕迹。总之,如果你不想执行快速合并,就用下面的命令

git merge --no-ff 分支名

合并的问题解决之后,如果你不再需要 dev 分支,就可以删除它了

git branch -d 分支名

储存历史版本是有意义的

git 生成的每个版本的版本号不是递增的而是一个长的随机序列,这样做正是由于 git 的分布式特性,因为你并不知道在其他人的电脑上生成了多少个新版本,并且提交次数多也无法代表内容更新,所以使用随机序列可以保证任何机器生成的任何一个版本都是互相独立的,不会造成有不同修改记录却有相同版本号的情况出现。这带来的问题就是对于人来说寻找某一个版本就变得十分麻烦,这时候我们就需要用到标签了。
首先使用git log或者其他方式找到你要打标签的版本号,然后执行

git tag 标签名 版本名

如果省略版本名的话就代表将标签打到当前所在的版本。其他几个命令就不需要详细解释了

git tag  (列出)
git tag -d 标签名 (删除)
git show 标签名 (查看)

有了标签我们就可以快速地查找一些重要的版本了。

git reset --hard 版本名
git show 版本名

<!--

#提交指定 tag git push origin myTag

#提交所有 tag

git push origin --tags


#列出所有远程分支 git branch -r

#列出所有本地分支和远程分支 git branch -a

#查看分支的追踪关系 git branch -vv

#本地分支与远程分支建立关联(git pull,git push 时可以不用指定,另一个更为简洁的方式是初次 push 时,加入-u 参数)

git branch --set-upstream-to=origin/dev_xxx dev_xxx



#下载远程仓库的所有变动 git fetch [remote]

#取回远程仓库的变化,并与本地分支合并 git fetch origin master git pull [remote][branch]

#将本地分支推送到远程仓库 git push origin dev:dev 意思是“上传我本地的 dev 分支到远程仓库中去,仍旧称它为 dev 分支” 或者 git push origin dev

#将本地分支推送到远程仓库并建立追踪关系

git push -u origin dev:dev


#查看某个文件修改内容 git diff test.txt

#查看提交记录(--pretty=oneline 参数可以单行显示) git log

#查看操作记录(如果回退版本之后,又想恢复到当前版本,可以通过此命令查到 commit_id,从而恢复) git reflog

#删除文件(记得 commit)

git rm xxx

-->

使用 GitHub 交友

GitHub 官方提供英文版的交互式使用教程,而且 GitHub 作为一个网站功能改进也比较频繁,所以下文会为你学习英文版教程提供一些帮助而不是给出中文版的教程

GitHub 常见专有名词的解释

英文词典义一些随意的解释
repositorie存储库,资源库,套件库这个词用于称呼 git 仓库
issue问题;发布,议题,争论点这个是 GitHub 一个非常重要的功能,它表示了开发者对仓库内一些问题的注解和解决计划、还有其他用户发现的一些问题等都会以 issue 的形式出现
watch看;观看;观察这个功能会让该仓库有新改动时通知你
star星星;明星;星牌在 GitHub 上的 star 有点赞的含义,也是对一个仓库最大的认可
clone克隆复制仓库,与下载不同的是,下载只包含最新的文件而克隆会同时下载完整的修改历史
fork餐叉,前叉,分叉这个操作会将 GitHub 上别人的仓库复制一份到你自己的账户下
pull request拉请求;拉拽请求我们可以为任何一个人修复仓库中的 bug,而修复好的仓库就需要使用这个功能来让拥有者接收你的修改
merge合并;融合;归并当一个 pull request 被确认好要接收时执行的操作就叫做 merge
README说明;自述文件;读我档案这个文件用于向其他人介绍你的仓库的主要功能、使用方法和已知 bug
CONTRIBUTING贡献的;捐献;促成的
license许可证;执照;授权虽然 GitHub 大部分仓库是公开的,但是使用它也需要遵循一定的许可要求,例如不能用于商业

使用 GitHub 官方教程进行学习

GitHub Lab提供了各种操作的交互式教程,每个教程都是一个 repo(仓库的简称),学习一个教程就需要将该 repo 克隆到你自己的仓库并在该仓库安装学习工具,学习工具在已经安装的仓库中拥有监视、修改文件的权力,所以如果已经有仓库的情况下请不要在所有 repo 安装学习工具。安装之后 GitHub 官方的机器人账号会通过向你的仓库提交包含操作教程的 isuue 或 pull request 来引导你完成课程任务。首先建议你完成一次新人任务