通俗易懂的 Git入门

235 阅读12分钟

本文已参与掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力

为什么要使用 Git

对于每一个开发人员而言,代码管理都是迫切需要解决的问题。一般来说,我们编写的代码都会经过反复地调整,很难一蹴而就的将代码交付,并且随着时间推移,代码量和代码变更次数都会越来越多,如果没有一种有效方案保证它们能被记录的,那会为之后的维护增加巨大的成本。

虽然可以使用一种低效的方案,在代码上编写大量的注释以注明业务、修改等信息,以及当代码需要修改时则拷贝一份副本,并且标明 v1v2 等来记录代码版本;或者当需要和团队分享时,则直接打包整个项目发给他,待他改完后再发回来,依此原则直到整个项目最终完成。

Git 能够高效完美的解决上面遇到的问题:

  • 当我需要修改一段代码,我能够查看代码修改日志,甚至找出哪些人修改过这段代码,从而轻易的了解代码上下文

  • 当我现在的逻辑出现了一个致命的 BUG, 但我无法确定是何时因而产生的,那么我可以根据以往的记录一条一条的往前找,直到找出最近没有出现 BUG 的代码,通过这次提交的修改,我可以很轻易地找出 BUG 地原因

  • 当我和团队成员同时修改一段代码时,我能知道我们修改的内容、以及哪些是有冲突的。

如何使用 Git

使用 Git 前需要先安装 Git 软件,点击右边的官网链接前往下载并安装 Git 最新版: Git - Downloads (git-scm.com),Git 的官网有最棒的学习教程,里面对每一个功能都有犹如字典般详细的解释,对今后想深入学习/查阅资料都有很大的帮助。

但是官网的内容犹如浩瀚大海,如果一头扎入很容易陷入迷茫方向,本文将尽可能以最短的路径完整的演示出 Git 的日常使用,希望能在入门路上带来一点帮助。

万事万物都要有一个开始

在下载并安装完 Git 后,打开终端(window 系统对应 CMD.exe)或者你喜爱的命令行软件如:item2、hyper 等,在命令行输入如下命令git --version,如果能看到对应的 git 版本则证明已经安装成功,如下:

~❯ git --version
git version 2.24.3 (Apple Git-128)

接下来我将以一个最简单的 Web 应用为例,在我的项目目录下只有简单的两个文件:index.htmlindex.css

image.png

然后,在命令行中将当前目录切换到我的项目目录下并且运行 git init 命令, 当你看到 Initialized empty Git repository in xxx 这提示(如下图),证明你已经成功的将这个项目初始化为了一个 Git 仓库,这就意味着 Git 会管理并且跟踪这个项目目录下的所有文件的变化

image.png

做一个仓库管理员

一般的物品存取逻辑都有以下几个流程:当一个物品需要被保存到仓库前,通常都需要先对其进行检查然后再存到仓库中,存到仓库后会给这个物品一个编码以及除了编码之外一般还有一些基础信息用来描述这个物品是什么。

git 的操作也是如此,在将编写完的代码提交到仓库前,需要通过 git add 命令将需要提交到仓库的代码暂存起来,当确保无误之后再通过 git commit 命令将变更存到仓库中,并且可以对存到仓库中的变更进行描述, git 会对这次变更生成一段 commitId

image.png

以上面的例子为例,当我修改完 index.html 这个文件后,我运行 git add index.html, 表示我已经将 index.html 文件加入到暂存区,接着通过 git status 我能看到暂存区有哪些文件,如:

❯ git add index.html
❯ git status                                              
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   index.html

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	style.css

在上面的例子中的:Changes to be committed 下面的 new file: index.html 意思是有一个新的文件准备提交到仓库中,在 Git 中,在 Changes to be committed 下通常称为暂存区,查看暂存区的修改内容需要通过 git diff --staged 命令。

在确保所有代码都没有问题之后,运行 git commit -m "修改描述" 将在暂存区的文件都提交到仓库, 接着可以通过 git log 就能查看仓库存保存的每一次修改的记录:

commit 8153f327ccb273a95401672af185634370bbcf69 (HEAD -> master)
Author: Giter <giter@giter.com>
Date:   Wed Aug 25 10:16:14 2099 +0800

    first commit

上面是运行 git log 命令后显示的 git 仓库中的一条记录。

  • 第一行 commit 后面跟的是 git 的 commitId 通过它可以准确的定位到这次修改
  • 第二行的 Author 记录的是谁提交的
  • 第三行显示的是提交到仓库的时间
  • 第四行显示对这次提交的描述

至此就完成了从编写代码到提交 git 仓库的基本步骤,之后如果再次编写完代码,则再次运行 git addgit commit 命令将编写完成代码提交到仓库,通过 git log 就能够看到在仓库中保存的这次变更的记录。

团队协作、共享代码

在以团队的形式开发项目的时候,面临的第一个问题是如何共享和编写代码,一种方案是采用云盘,所有人都在云端编写代码,但是这种情况缺乏管理和灵活性,当同写同一行的时候,可能会覆盖另一个人的,或者无法知道是谁删了一些重要的代码

在 Git 中还可以设置远程仓库,上面所提交的仓库我们称为本地仓库,因为它存在于本地硬盘中的,如果硬盘损坏,或者不小心删掉了本地仓库,那么会很难再找回仓库中保存的提交记录。一般你不用去买另外的设备用来专门作为远程仓库,一般有现成的可以用,如:github、gitee、gitlab 等,这里我以 gitee 为例,演示如何创建一个远程仓库,并且把本地仓库同步到远程仓库中。

假设你已经有了 gitee 的账号密码,如果没有可以前往 gitee.com 免费注册,登录成功之后点击右上角的 + 号,并且点击 新建仓库

image.png

接着输入远程仓库的信息,如仓库名等然后确认,在这个表单中有很多快捷的选项,如果没有必要则保持默认的就行。

image.png

创建完后你会进入如下界面

image.png

上面的三个黑色区域中的命令都是需要回到本地的命令行进行操作,你需要根据自己的需求运行对应的命令。

  • 第一个黑色区域的命令提示如果没有设置 git 的一些基础信息,可以运行下面的命令设置,设置后有助于 git 记录是谁提交的代码,如果你重来没有运行过下面的命令,至少应该运行一次。
git config --global user.name "用户名"  
git config --global user.email "邮箱地址"
  • 第二个区域表示如果本地没有创建仓库,则可以根据以下命令进行创建, 比如你能看到 git initgit addgit commit 这三个命令分别是初始化将文件加入到暂存区将暂存区的内容提交到本地仓库
mkdir git-lean
cd git-lean
git init
touch README.md
git add README.md
git commit -m "first commit"
git remote add origin git@gitee.com:xxx/git-lean.git
git push -u origin master
  • 第三个区域则是本地已经有仓库了,如果需要将本地仓库和远程仓库关联起来,则运行下列命令, 由于我本地的项目已经初始化了一个仓库,并且本地仓库中有一条记录,所以我该选择这种方案
cd 项目地址
git remote add origin git@gitee.com:xxx/git-lean.git
git push -u origin master

接下来先解剖这两条命令:

  • git remote add origin 仓库地址: 可以理解为给本地的 git 仓库添加一个远程仓库名叫 origin,并且远程仓库的地址是 xxx
  • git push -u origin master: 将本地仓库同步到 origin 这个远程仓库

当我在我本地终端运行完这两行命令后, 你能看到终端中有一个同步信息:

Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 12 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 405 bytes | 405.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: Powered by GITEE.COM [GNK-6.0]
To gitee.com:xxx/git-lean.git
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.

接着在回到 gitee 中,刷新刚刚的页面,你能看到页面中已经有了本地仓库中的文件,如我刚刚提交到本地仓库的 index.html 文件

image.png

Ok, 远程仓库已经创建好了,并且我把本地仓库的内容同步到了远程仓库中。

假设团队中有其他人也需要同时开发这个项目,那么他可以把远程仓库中所有的内容都拉下来,对应的命令有两个 git clone 以及 git pull

  • git clone 通常用于第一次将远程仓库的代码同步到本地
  • git pull 通常是在本地仓库已经和远程仓库关联之后,用于将远程仓库的记录同步到本地仓库中,所以它和 git push 是相反的,一个是将本地仓库同步到远程仓库,一个是将远程仓库同步回本地仓库

接下来假设另一个团队成员需要开发这个项目,首先他需要运行 git clone 将远程仓库克隆一份到本地

❯ git clone 仓库地址
Cloning into 'xxx'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.

接着假设他修改了 index.html 并且重新运行 git addgit commit 将修改提交到了远程仓库。

回到远程仓库的页面中,点击它刚刚的提交记录就能看到他这次修改的是什么内容

image.png

image.png

可以看到上图右侧的 + 号表示:上一次提交到仓库中的修改和这一次提交到仓库中的修改新增了的内容,因为上一次是我提交的,而这一次是团队成员提交的,所以我可以轻易知道其他人改了什么?

冲突

如果团队成员和我同时改了同一个位置难免就出现了冲突,这时 git 是无法分辨出该用谁的代码,需要用户自己决定。例如其他成员修改了 index.html 的第十行,此时我也修改了 index.html 的第十行,那么我们提交到本地仓库的时候并不会出现问题,但当提交到远程仓库的时候,则会出现异常。

演示文稿1.gif

发生冲突后你在代码中通常会遇到这样的成对出现的内容:<<<<<<< HEAD=======>>>>>>>

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>学习 Git333</title>
</head>
<body>
<<<<<<< HEAD
我也修改了这行
=======
  修改了一行
>>>>>>> 8d024ad2aae1835d99620e4a75c2c63500eba928
</body>
</html>

这时你需要决定应该使用 <<<<<<< HEAD======= 部分内容,还是使用 =======>>>>>>> 8d024ad2aae1835d99620e4a75c2c63500eba928 部分的内容,选择好之后把另外的一部分删除以及删除 <<<<<<< HEAD=======>>>>>>> 这三个标识,然后再重新执行 git addgit commit 提交到本地仓库,最后再同步到远程仓库。当然这样可能会导致你删掉其他人的代码,所以如有必要最好联系对应的人确认无误之后再确定使用谁的,或者两个人的代码都使用。

查找记录、回退、回退后再回退

最后还有一种比较棘手的场景,假设项目出现了一个未知的 BUG,但是在之前的某一刻它是正常的,并且无法立刻就排查出问题来,通常的方案是找出导致修改 bug 出现的那次提交,然后确定出什么原因出现的 BUG 后再回到最新的记录来修改并且提交。

  • 首先使用 git log 找出最近记录的 commitId

  • 如果此时有一些修改,为了避免混淆,最好使用 git stash -u 将现在正在修改的内容保存起来

  • 接着使用 git reset 切换到未出现 BUG 的提交,如: git reset 8153f327ccb273a95401672af185634370bbcf69

  • 当定位到未出现 BUG 的提交之后,还需要定位到最先出现 Bug 的那个提交,以便找出 BUG 是在哪个提交引起的,这需要反复操作, 如:

    1. git log 查看之前的提交是否有出现 BUG
    2. 使用 git reset 切换到这个提交,运行项目之后确定是否出现 BUG
    3. 如果还出现了 Bug 则使用 git reset 往前继续找,直到找到没有出现 BUG 的提交
    4. 如果没有出现 Bug,并且还无法确定出现 BUG 的那次提交,则需要回到最新的提交
    5. 此时的 git log 已经无法看到最新的那次提交了,需要使用 git reflog 查看最新的那次提交
    6. 使用 git reflog 找出最新的提交,然后重复第一条,并且缩小往前查找的范围,直到找出出现 BUG 的那次提交。

结语

当你熟悉 Git 的基础流程之后,可能除了命令行工具,还会使用其他的软件辅助操作,比如 sourceTree 或者其他的软件,但 git 终归只是一个工具,在更多时候代码的管理耗费的时间和思考会更多,最后希望本文能对正在苦于 git 入门的人有所帮助,对于文中有出错的地方,希望大家能够及时指出,避免我误导他人。