git使用方法总结

本篇博客对 git 的基本原理,基本操作进行了总结。

1. git 基础

git 是 linux 之父 Linus 于 2005 年时编写的,当时主要用于 linux 的版本管理工作。

git 不同于 SVN 这种集中式的版本管理工具(版本库集中存放在中央服务器)必须联网才能工作(因为必须从服务器下载代码的最新版本),它采用分布式的形式,让每个开发者都保存有一个完整的版本库,可以在本地随时进行修改。只有需要进行代码合并的时候,才需要联网,来确认修改的代码部分。

1.1 git 安装

下载windows版的 git,全部默认设置安装。

然后打开 git bash,使用命令行配置用户名和邮箱:

1
2
3
# --global 参数表示本机上所有仓库都使用该配置
$ git config --global user.name "xx"
$ git config --global user.email "xx@xx.com"

具体各个系统的安装细节,参考 Ref-廖雪峰git教程

1.2 创建版本库

版本库又名仓库(Repository), 其实就是系统中的一个文件夹,用于存放将要使用 git 进行管理的代码、资源文件。

在系统中创建一个 git repo 的步骤如下:

  • 打开 git bash,命令行切换到需要创建repository 的目录(或者直接在需要创建 repo 的目录下 ‘右键’ -> git bash here
  • 仓库初始化工作,先构建一个空的 git 仓库,输入 git init

执行完毕 git init 之后,在当前文件夹下,就会生成一个 .git 的文件夹(默认为隐藏属性),这里面就存放了 git 用于记录版本的相关文件。

1.3 添加文件

完成创建后,可以向 git repo 中添加文件。步骤如下:

  • 添加需要的文件到该目录;
  • 添加文件到 git repo: git add xxx(document file name);
  • 提交文件到 git repo: git commit -m "describe the content of this commit" (-m 后面输入本次提交的说明)。

1.4 工作区和暂存区

注意到上面向仓库中添加文件分为了两步操作,首先是 add, 然后是 commit。这就涉及到 git 中 暂存区的概念。

在 git 中,有工作区 和暂存区之分:

  • 工作区:电脑中我们直接编辑的区域;
  • 暂存区(英文为 Stage, 或者叫 index),使用 git add 指令,就是将文件修改添加到了暂存区。

在 git 中,修改了但没有被添加到暂存区的修改内容 的状态称为 “untracked”。修改的内容可以多次 add 到 stage 中,最后统一地 commit 到当前的分支。

需要注意的是,git 中跟踪并管理的实际上是修改,而并非文件本身。所以,必须加修改 add 到 stage 中,然后 commit 才能生效。

2. 版本控制

2.1 状态查看

在进行版本管理之前,我们可以通过指令查看当前代码库的状态,使用 git status 指令可以获得当前代码库的状态,例如当前代码分支,哪些文件修改了还没 添加到暂存区,哪些文件被修改了已经被添加到了暂存区还没有提交(commit)。

如果想查看当前代码库中文档与之前版本的差异,可以使用 git diff <所修改文件的文件名>

2.2 撤销修改

丢弃还未add到stage 中的修改

当我们在工作区对文档进行了修改,如果该修改还没有 add 到 暂存区,可以直接使用 git checkout -- <file> 丢弃掉工作区的修改。命令中 -- 不能少,否则就变为了切换分支指令。

最新版的 git 中,使用 git status 查看当前状态,如果工作区有 还没有 add 的修改,则推荐使用 git restore <file>... 丢弃修改。

丢弃已经 add到 stage 但还没 commit 的修改

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

最新版的 git 中,使用 git status 查看当前状态,如果有 add 到 stage 还没有 commit 的修改,它推荐使用 git rm --cached <file>... 指令将修改 unstage。关于上述二者的区别,见参考

2.3 git 版本回退

在 git 中,每个代码版本 都会有一个版本号(commit id),在 log 中可以看到,是一个 SHA 计算出来的很大的以十六进制显示的数字(不使用递增数,目的是便于进行分布式版本控制)。

git log 指令可以查看当前仓库的历史提交(commit)记录(git log --pretty=online 可以简化输出内容)。

使用 git 进行版本回退操作,用指令 git reset --hard <目标版本> 。这里的目标版本可以使用版本号来定位(仅需要写 commit id 的前几位即可,好像至少7位);也可以 以当前版本为基准进行回退,在 git 中,当前版本用 HEAD 表示,上一个版本为 HEAD^, 上上个版本为 HEAD^^, 前面第100个版本表示为 HEAD~100

当然,如果回退版本后希望撤销回退,可以使用 git reflog 查看执行过的指令(注意这时候使用 git log 无法查询到回退之前的版本记录),找到执行回退操作前的版本号,用 git reset --hard <版本号> 指令跳转到上述版本。

2.4 删除文件

当你在本地删除了一个文件,通常有 误删除需要恢复 和 确实需要删除两种情况。

如果是误删除需要恢复,使用与上面 丢弃还未add到stage 中的修改 相同的指令即可。

如果确实需要删除,则需要首先 git rm <file>, 然后 git commit -m "description"

3. 远程仓库

以 GitHub 作为远程库为例。实现本地库与远程库的沟通与同步,需要以下操作。

3.1 SSH 配置

GitHub 支持 SSH加密传输,通过配置 SSH 可以方便的进行本地库和远程库之间的沟通。

具体的步骤如下:

1- 在本地创建 SSH key。打开 git bash,切换到~ 目录,查看是否存在 .ssh 文件夹,如果有,说明已经生成。否则,使用指令创建 SSH key:

1
$ ssh-keygen -t rsa -C "email@address"

创建过程中可以不设置密码,一路回车到底。完成后,可以看到用户主目录中有 .ssh 文件夹,其中有 id_rsaid_rsa.pub (id_rsa 为私钥,id_rsa.pub 为公钥)。

2- 登录 GitHub 账户,打开 account settings,添加本地主机的 SSH key,将 id_rsa.pub 复制添加进来。

3.2 添加远程库

完成 SSH 配置操作之后,就可以为本地代码添加一个位于 GitHub 上的远程仓库了。具体步骤如下:

1- 首先在 GitHub 上新建一个 Repo。

2- 为本地仓库添加远程库地址, 在本地仓库下运行,git remote add origin git@github.com:xxx/xxx.git,效果是添加了远程仓库并将其命名为了 origin。远程仓库的链接地址在 github 新建的 repo 中找到。

完成后,通过 git remote show <主机名> 查看主机信息。

3.3 本地库到远程库的互传

push 本地库到远程仓库

标准的 push 指令为 git push <远程主机名> <本地分支名> <远程分支名> , 可以将本地的特定分支推送到远程机的特定分支上。

在第一次推送时,由于远程仓库为空,推荐加上 -u 参数,即 git push -u origin master,这样 git 不仅会将本地 master 分支内容推送到远程新的 master 分支(省略 远程分支名,如果不存在关联分支,会自动在远程创建一个与本地库同名的分支),还会把本地 master 分支和远程的 master 分支关联起来。从而使得以后的推送和拉取指令可以进行简化。

不过最方便的新建项目的做法是先创建空的远程库,然后 pull 到本地,再进行开发,具体参考 “从远程仓库更新本地库”。

关联后,后续可以使用 省略远程分支 的写法: git push origin master, 将当前的 master 分支推送到远程库上与之存在关联关系的分支 (参考)。

从远程仓库更新本地库

对于第一次从远程库拉取代码,使用 git clone git@github.com:xxx/xxx/git, 可以直接从远程仓库拉取代码到当前文件夹,并且自动设置远程库为上述 clone 的地址。

Note: 从2020年10月1日起,所有 github 上的新仓库会默认命名为 “main” 而不是 “master”.

此外,如果本地已经有仓库,需要从远程仓库获取代码的更新,就涉及到两个仓库的合并问题,参看下章,分支管理。

4. 分支管理

在 git 中,一次次的提交根据时间先后构成了一条链,这条链就构成了一个分支。

git 中,HEAD 表示当前分支,在前面的介绍中,HEAD 都指向 master 分支。

在 git 中,分支的管理是通过指针指向的位置来实现的,因此 git 实现分支合并、切换等操作非常快。

4.1 创建分支

具体的创建分支的操作指令如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# command 1:创建并切换到 dev 分支,-b 参数表示创建并切换
$ git checkout -b dev

# 上述 command 1 相当于下面两条指令
# branch <分支名> :创建分支。
# checkout <分支名> : 切换到对应分支,即将 head 指向指定的分支。
$ git branch dev
$ git checkout dev

# 推荐!! 较新的创建并切换分支方式
$ git switch -c dev
# 切换到已有分支
$ git switch master

# 可以使用 branch 查看当前分支
$ git branch

# 将指定分支 合并到 当前分支上
$ git merge dev

# 删除分支
# git 鼓励使用分支完成某项任务,开发完成后,合并后再删掉分支,
# 这样的效果和在 master 分支上工作一样,但过程更加安全。
$ git branch -d dev

4.2 冲突解决

当两个分支 merge 时,如果不是严格的在同一条链上,进行 merge 的时候,可能有冲突。

如果两个需要 merge 的版本之间有冲突,git 会报 CONFLICT,如下:

1
2
3
4
$ git merge feature1
Auto-merging test branch.txt
CONFLICT (content): Merge conflict in test branch.txt
Automatic merge failed; fix conflicts and then commit the result.

此外,终端中当前的分支名会显示为 dev|MERGING

这时候,我们找到 conflict 的文件, 在上面例子中是 branch.txt。会发现冲突部分显示如下:

1
2
3
4
5
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1

可见,git 使用 <<<<<<<>>>>>>> 标记出不同分支的内容。

我们之间在冲突的文档中进行修改,保留我们认为正确的内容。然后在上面 dev|MERGING 分支下执行 git addgit commit -m "description" 命令。处理完毕后,会发现当前分支名变回为 dev 分支。

4.3 利用远程仓库合作开发

当在本地开发完成后,可以 git push origin <branch-name> 将本地修改了的代码推到远程。

然而,如果合作开发,由于其他人推送了他们的更新,远程库可能比本地库新,此时,可以 git pull 将远程库拉回本地进行合并。

如果合并有冲突,则解决冲突,并在本地提交。解决掉冲突后,再用 git push origin <branch-name> 完成到远程库的推送。

Note: 如果 git pull 提示 no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令 git branch --set-upstream-to <branch-name> origin/<branch-name>

补充:pull 和 fetch 命令。

git fetch <远程主机名>,将远程主机上的更新取回本地。

git pull 命令用于从远程库获取并下载内容并且将本地仓库与下载的远程内容进行合并。(git fetch + git merge)。

可以使用 git pull <remote> 指令将特定的远程库下载到本地并与当前分支进行合并。相当于 git fetch + git merge origin/