使用 Git 和 GitHub 协作编程

写在开头

非常感谢学校的Lucy老师愿意给我这个机会来给校内感兴趣的同学介绍Git这款神奇的软件,本文算是一个讲座的文字稿,希望能够帮助到未能前来参与讲座的同学与互联网上热爱钻研的网友们。讲座内容将在发布后直接更新在此处。

开始前准备

安装Git

  • macOS

    使用终端执行git --version,在弹出的窗口中继续。

  • Windows

    访问Git官方网站下载安装包,安装过程中一路下一步即可,当然如果你知道你在干什么的话可以进行一些自定义。

  • Linux

    既然你都选择了Linux,那你应该知道怎么安装吧(笑

Git 基础配置

接下来是至关重要的一步。如果你没有把这一步做好,之后使用 Git 可能会遇到问题。

Git 在管理代码时会储存管理者的名字和邮箱,所以你要告诉 Git 你叫什么以及你的邮箱是什么。运行:

1
2
git config --global user.email "你的邮箱"
git config --global user.name "你的名字"

然后试试:

1
cat ~/.gitconfig

可以看到,Git 把你的设置存在了文件 ~/.gitconfig 里。

接下来我们要配置 Git 使用的文本编辑器。如果你使用 Windows,你可能已经在安装时配置了编辑器。如果还没有配置,输入:

1
git config --global core.editor "编辑器启动命令"

其中编辑器启动命令可以是:

  • VS Code:code --wait
  • Xcode:xed -w
  • Notepad++:notepad++`
  • TextEdit:``open -e -W -n`
  • Windows 记事本:notepad
  • PyCharm:PyCharm --wait

想了解 git config 的更多使用方法,运行 git config -h 或者 man git config

新建仓库

既然Git适用于管理一个仓库的,那我们首先应该先新建一个仓库。使用以下命令

1
2
mkdir my_repo
git init my_repo

如果你看到了已初始化空的 Git 仓库于 ${PWD}/my_repo/.git/则表示仓库初始化成功。

Git基本概念

Git将一个仓库划分为了四个部分:工作区、暂存区、本地库、和远程库。你的本地仓库由 git 维护的三棵“树”组成。第一个是你的 工作目录,它持有实际文件;第二个是 暂存区(Index),它像个缓存区域,临时保存你的改动;最后是 HEAD,它指向你最后一次提交的结果。

但是请注意,Git不会尝试暂存一个空的目录,毕竟那里什么也没有。

Git工作流

由于暂存区内的东西随时可能会被你或者其他大聪明修改,因此需要一个新的功能来处理这种问题。这个功能就是提交。提交是将暂存区内的内容永久保存到版本库中的过程。你可以把它看作是项目的一个快照。

Unix命令基础入门

虽然可以用一些GUI界面来操作Git,如:GtiHub DesktopSmartGit等。但是在有些时候无法准确的使用命令行表达你想要的内容,因此掌握命令行操作将非常重要。

文件夹/路径操作

<>:必填,[]:选填

  • cd <path>:Change Directory,切换到目标目录
  • ls [param] [path]:List,列出特定目录下的文件
    • -a:列出所有文件,包括隐藏
    • -l:列出文件权限与所有者

文件操作

  • cat:Concatenate,显示或连接多个文件
  • touch:新建一个文件
  • grep:Global Regular Expression,用于查找文件里符合条件的字符串或正则表达式,常与管道符|连用
    • -i:忽略大小写进行匹配
    • -v:反向查找,只打印不匹配的行
    • -n:显示匹配行的行号
    • -r:递归查找子目录中的文件
    • -l:只打印匹配的文件名
    • -c:只打印匹配的行数
  • vi:命令行文本编辑器
    • i :切换到输入模式,在光标当前位置开始输入文本。
    • x :删除当前光标所在处的字符。
    • : :切换到底线命令模式,以在最底一行输入命令。
    • a :进入插入模式,在光标下一个位置开始输入文本。
    • o:在当前行的下方插入一个新行,并进入插入模式。
    • O :在当前行的上方插入一个新行,并进入插入模式。
    • dd :剪切当前行。
    • yy :复制当前行。
    • p(小写) :粘贴剪贴板内容到光标下方。
    • P(大写):粘贴剪贴板内容到光标上方。
    • u :撤销上一次操作。
    • Ctrl + r :重做上一次撤销的操作。
    • :w :保存文件。
    • :q :退出 Vim 编辑器。
    • :q! :强制退出Vim 编辑器,不保存修改。
键盘键位图

查看当前状态

1
git status

以上的命令可以查看在你上次提交之后是否有对文件进行再次修改。

使用-s这个参数可以让输出变得简短

git status 命令会显示以下信息:

  • 当前分支的名称,以及当前分支与远程库中对应分支的关系(例如,是否与远程库同步)。
  • 工作区中的未暂存修改:列出工作区中已修改但尚未通过 git add 添加到暂存区的文件。
  • 工作区中的未跟踪文件:列出工作区中新建但尚未被纳入版本控制(即未添加到暂存区和本地库)的文件列表。

提交你的第一个文件

将文件从工作区提交到暂存区,使用git add命令

1
git add <filename>

或使用git add .命令将所有文件添加到暂存区

1
git add .

若不小心git add了某个文件,则使用以下命令来撤销添加

你需要至少有一个才可以使用以下命令

1
git restore --staged <file>

在添加到暂存区之后,我们需要将其提交至HEAD,使用git commit命令

1
git commit -m "提交信息"

提交信息是对本次提交的描述,可以是任意字符串,但是最好是有意义的。需要遵循以下格式:

Commit Message 规范

此部分来源于作者: 阿里云开发者​,原文链接: https://zhuanlan.zhihu.com/p/182553920

1
<type>(<scope>): <subject>

type(必须)

用于说明git commit的类别,只允许使用下面的标识。

  • feat:新功能(feature)。

  • fix/to:修复bug,可以是QA发现的BUG,也可以是研发自己发现的BUG。

  • fix:产生diff并自动修复此问题。适合于一次提交直接修复问题

  • to:只产生diff不自动修复此问题。适合于多次提交。最终修复问题提交时使用fix

  • docs:文档(documentation)。

  • style:格式(不影响代码运行的变动)。

  • refactor:重构(即不是新增功能,也不是修改bug的代码变动)。

  • perf:优化相关,比如提升性能、体验。

  • test:增加测试。

  • chore:构建过程或辅助工具的变动。

  • revert:回滚到上一个版本。

  • merge:代码合并。

  • sync:同步主线或分支的Bug。

subject(必须)

subject是commit目的的简短描述,不超过50个字符。建议使用中文(感觉中国人用中文描述问题能更清楚一些)。

结尾不加句号或其他标点符号。

根据以上规范git commit message将是如下的格式:

1
2
fix(DAO):用户查询缺少username属性 
feat(Controller):用户查询接口开发

查看提交历史

使用git log命令可以查看提交历史,可以看到提交的作者、时间、提交信息等

1
git log

输入以上命令将获得如下消息,消息内分别包含

  • Commit Hash:提交哈希值
  • 提交分支信息
  • Author:作者
  • Date:提交日期
  • Commit Message:提交信息
1
2
3
4
5
commit 4d864d94fa8efa4bac56d838c0d25af8f9076d99 (HEAD -> main, origin/main)
Author: lightumcc <jessiezhu@createchstudio.com>
Date: Wed Dec 4 10:50:57 2024 +0800

docs: commit & push

其中的分支信息有三个内容,分别是HEADmainorigin/main

  1. HEAD

    • HEAD 是 Git 当前所指向的 工作分支的最新提交,也称为当前分支的“游标“。
    • 通常指向当前分支的最新提交记录。
    • 如果你切换到一个分支或具体的提交,HEAD 会指向那个分支或提交。
  2. main

    • main 是一个 本地分支,指向本地库中某分支的最新提交记录。
    • 在这个例子中,main 是你当前工作分支的名称。
    • 如果有新的提交,本地的 main 分支会随着提交移动。
  3. origin/main

    • origin/main 是远程库中 main 分支的 追踪分支。
    • 表示上次从远程库拉取数据时,远程库的 main 分支所指向的提交记录。
    • 如果远程库的 main 分支有更新,而本地未执行 git pullorigin/main 和本地 main 分支可能会不同步。

当你想查看一个人的提交历史记录,可以在命令后面加上--author=<name>,若想查看哪些文件改变了,可以使--name-status

除了使用默认的参数之外,还可以添加一些其他的内容来让你的提交历史更加清晰

1
git log --pretty=oneline

或者你想通过 ASCII 艺术的树形结构来展示所有的分支, 每个分支都标示了他的名字和标签:

1
git log --graph --oneline --decorate --all

比较文件差异

git diff 是一个常用的 Git 命令,用于查看文件的修改内容。它显示了工作区、暂存区或两个提交之间的差异。用于在你提交并push到仓库前查看差异,确认更改。

  • git diff --cached:查看暂存区与最后一次提交之间的差异
  • git diff HEAD:查看工作区与最后一次提交之前的差异
  • git diff <commit hash 1> <commit hash 2> -- <filename>:比较两个提交之间的差异

如何回到过去

如果我们想从之前的某个提交中恢复一个文件应该怎么办呢?

使用如下的命令会让你从过去的某次提交中恢复工作树文件。

1
git restore -s <commit hash> <filename>

-s--source=的缩写

以下是一些常见用法:

  • git restore <filename>:从暂存区恢复到工作树
  • git restore --staged <filename>:从 HEAD 恢复到暂存区
  • git restore --staged --worktree <filename>:从 HEAD 恢复到暂存区和工作树
  • git restore --source=<commit hash> --staged <filename>:从该提交恢复到暂存区
  • git restore --source=<commit hash> --staged --worktree <filename>:从该提交恢复到暂存区和工作树

将你的屎山作品公之于众

在你完成提交并编写完成Commit Message之后,你就可以使用git push命令将你的作品推送至仓库,此处的main是你所设定的分支名称,可以替换成你想要的,但是注意,请不要添加空格。

1
git push origin main

如果你严格遵守了这篇文章的步骤,你现在将看到一个如下的报错:

1
2
3
4
致命错误:'origin' does not appear to be a git repository
致命错误:无法读取远程仓库。

请确认您有正确的访问权限并且仓库存在。

那是因为你还没有克隆现有仓库,并没有将此仓库连接到某个远程服务器,你可以使用如下命令添加:

1
git remote add origin <server>

如此你就能够将你的改动推送到所添加的服务器上去了。

若不小心设置错误,可使用git remote set-url origin <server>来修改远程仓库地址。

若你添加的origin是HTTPS,并且你的远程要求身份验证,则会在推送的时候提示输入登录凭证

以下用GitHub举例获取登录凭证

生成并使用 Personal Access Token (PAT)

  1. GitHub 不再支持直接使用密码进行身份验证,而是需要使用 PAT。

    • 登录 GitHub 账户。
    • 进入 Settings > Developer settings > Personal access tokens > Tokens (classic)。
    • 点击 Generate new token:
    • 选择所需的权限(例如 repo 用于操作私有仓库)。
    • 复制生成的 token(仅显示一次)。
  2. 通过 HTTPS 使用 Git 操作时,Git 会提示输入:

    • 用户名:输入你的 GitHub 用户名。
    • 密码:输入刚刚生成的 Personal Access Token。

一般来说,使用GitHub作为远程仓库是最好的选择,因为它提供了免费的私人仓库,而且还有很多其他的功能。我完全不推荐国内的Gitee,不仅仅是因为审查原因,还有因为其在提交时要求复杂的身份验证,对新手来说不太友好。

在网页创建完成之后,你将会在初始页面中看到你仓库的远程,如:https://github.com/lightumcc/test-lecture.git,你在将本地仓库链接到GitHub之后,再次运行git push命令,你就可以在GitHub上看到你的代码了。

如果你觉得每次登录比较麻烦,你可以使用 Git 凭证缓存。可以在一定时间内(例如 15 分钟,或者你指定的时间)缓存凭证。

1
2
git config --global credential.helper cache
git config --global credential.helper 'cache --timeout=3600'

如果你想使用长期存储,可以使用以下命令

1
git config --global credential.helper store

登录凭证文件将被保存在~/.git-credentials中,以明文形式存储,建议使用1Password等密码管理器进行代理登录

分支

分支的作用是用来在不影响别的同事工作的情况下进行独立的开发,在完成功能开发之后进行分支合并。合并的操作将在下次讲座中讲解。

分支

  • 创建新分支并签出
1
git checkout -b <branch name>
  • 切换分支
1
git checkout <branch name>
  • 查看分支
    • -r:查看远程分支
    • -a:查看所有分支
1
git branch
  • 删除本地分支:

    1
    git branch -d <branchname>
  • 强制重命名分支

    1
    git branch -M <new branchname>
  • 强制删除未合并的分支:

    1
    git branch -D <branchname>
  • 删除远程分支:

    1
    git push origin --delete <branchname>

拉取别人的仓库

在有些时候,我们希望将别人的仓库下载下来进行学习,或者对原始项目提交贡献。这时候我们就需要使用git clone命令。

1
git clone <remote url>

希望各位不要使用某些网页上的Download Zip这个功能,这会导致当你不小心改错别人代码的时候可能无法回档,并且失去原有的版本控制功能。

git clone命令包含以下几个常用参数:

--depth:指定克隆的深度,即只克隆最近的几次提交。

--branch:指定克隆的分支。

--single-branch:只克隆指定分支。

--mirror:克隆镜像仓库,即克隆所有分支。

获取最新代码

假设你所克隆的仓库或你组内的其他成员推送了新的代码,你需要将其获取到本地,则应该使用如下命令

1
git pull

这个先从远程仓库获取最新的提交记录,然后将这些提交记录合并到你当前的分支中。是fetchmerge命令的集合。

但是此时可能存在本地代码与远端代码冲突的问题,无法自动进行合并。这个操作我们将在下一周的课程里讲解。

为了快速的解决这个问题,我们可以使用

1
git stash

以上命令将临时的保存当前进度,使用以下命令可以对临时保存的内容进行操作

查看存储的进度

1
git stash list

应用最近一次存储的进度:

1
git stash apply

应用并删除最近一次存储的进度:

1
git stash pop

删除特定存储:

1
git stash drop stash@{n}

清空所有存储:

1
git stash clear

融合他人的智慧

除了历史记录之外,Git还有以他特点就是分支管理。在别人与你并行开发完成之后,我们需要将其他分支合并到主分支,以对功能进行联调或发布。使用以下命令进行合并操作:

1
git merge <branch>

在有些时候,Git无法自动合并分支,这种情况大多出现在多人对同一个文件的同一行进行了修改。

这时,就会产生冲突。冲突并不可怕,只需与你的团队成员打一架友善交流,即可完成这次合并。

Git 会在终端中明确指出哪些文件发生了冲突。可以使用以下命令查看冲突状态:

1
git status

打开冲突文件,会看到类似以下的标记:

1
2
3
4
5
<<<<<<< HEAD
// 当前分支的内容
=======
// 目标分支的内容
>>>>>>> branch-name

这表示两部分内容的冲突区域。你需要根据实际情况决定保留哪部分内容,或者手动合并两部分代码。在解决完冲突后,保存文件并通过提交至暂存区标记冲突已解决:

1
2
git add <file>
git commit

在团队协作中,频繁提交代码可能会让提交历史显得混乱。而 git rebase 提供了一种方法,通过将一个分支的提交应用到另一个分支上,来整理提交历史。例如,当你完成一个功能开发时,可以将你的分支变基到主分支,以使提交记录看起来更线性。

1
git rebase main

这会将当前分支的所有提交重新应用到 main 分支的最新提交上。需要注意的是,变基会改变提交历史,因此尽量不要对已经共享的分支(例如远程分支)使用变基。

除了使用git rebase之外,还有另外一种合并分支的方案:git squash,意味着将多个连续的 commits 压缩合并成一个 commit。

1
2
3
4
5
6
7
8
9
10
11
		 		 ┌───┐      ┌───┐     ┌───┐      ┌───┐
... │ A │◄─────┤ B │◄────┤ C │◄─────┤ D │
└───┘ └───┘ └───┘ └───┘

After Squashing commits B, C, and D:

┌───┐ ┌───┐
... │ A │◄─────┤ E │
└───┘ └───┘

( The commit E includes the changes in B, C, and D.)

谴责他人

当你想知道一段代码是由谁、在何时、为何修改时,git blame 是非常有用的工具。它会逐行显示每段代码的提交记录和提交者:

1
git blame <file>

执行后,你会看到每一行代码的提交哈希值、提交者姓名和时间。通过这种方式,可以快速定位某段代码的责任人或进一步了解其修改意图。

安全地回到过去

当某次提交引入了问题,但你不想破坏其他人的提交历史时,可以使用 git revert 来生成一个新的提交,撤销指定的更改:

1
git revert <commit>

这与 git reset 不同,因为它不会直接修改历史,而是创建一个新的提交,用于撤销指定的更改。因此,它适合在多人协作时使用,因为不会影响他人的代码。

git reflog:查看所有操作记录

做一个仓库侦探

git reflog是一种强大的工具,用于查看仓库中所有的操作记录。即使某些提交无法通过普通历史记录查看(例如被 git reset 删除),git reflog 仍然可以追踪到:

这在误删分支或提交时特别有用,你可以通过它找到提交的哈希值并恢复:

1
git checkout <commit>

通过 reflog,Git 的每一次动作都不会“真正消失”,为开发者提供了极大的安全感。

结语

第二节讲座的内容就到此结束了,希望各位同学可以就此开启自己的代码规范之旅。通过掌握这些命令,你可以更加自如地应对版本管理中的各种挑战,无论是整理历史、查找问题还是挽救错误,Git 的这些工具都能为你提供强大支持。下一节讲座将会继续讲解GitHub的入门用法。

下节讲座的内容将包含:

  • GitHub使用教程