唠唠闲话

Git 是目前世界上最先进的分布式版本控制系统,可以用于敏捷高效地处理任何或小或大的项目。所谓“分布式”是因为它的代码库可以同时存在于多个计算机上,并且这些代码库之间可以相互同步和交换代码更新。每个开发者都可以在本地拥有一份完整的代码库,可以独立地进行代码修改和提交,然后将这些修改推送到共享的远程仓库中。这种去中心化的代码管理方式,使得团队成员可以更灵活地协作,不受网络限制,在断网情况下也能继续工作。

个人而言,主要用 Git 实现这些功能:

  • 备份,同步本地项目
  • 多设备管理项目
  • 文件历史版本(改错的后悔药)
  • 管理 GitHub 项目
  • 部署博客网站

本篇记录 Git 中经常用的操作和命令,方便查阅,至于 Git 的安装和工作原理网上有大把介绍,就没必要赘述了。

推荐网站:廖学锋的 Git 教程,最开始学习 Git 就在这个网站。
推荐书籍:高见龙的 《Git 从入门到精通》


基本操作

修改配置

  1. git commit 提交修改时,需要“自报家门”,也就是提交人的姓名和邮箱,命令如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 设置用户名和邮箱
    git config --global user.name "Your Name"
    git config --global user.email "email@example.com"

    # 查看用户名和邮箱
    git config --global user.name
    git config --global user.email

    # 取消用户名和邮箱
    git config --global --unset user.name
    git config --global --unset user.email
  2. 补充说明

    • --global 参数全局设置用户名和邮箱
    • 在 git 仓库中,不加 --global 或者改用 --local 参数,则给仓库单独设置用户名和邮箱
    • 用户名和邮箱的双引号 "" 可写可不写
  3. git 默认编译器为 vim,可更换编译器为 vscode

    1
    git config --global core.editor "code --wait"

    注意参数 --wait 表示等待文本的编辑关闭,否则每次立即返回空 commit,导致提交失败。

  4. 使用 git config --list 查看整体设置
    深度截图_google-chrome_20220126140635
    在 git 仓库下,使用 git config --list 可以查看更多信息
    20220126140909

  5. git 的全局配置为主目录的 .gitconfig 文件,可直接编辑修改

  6. 在 git 中,通过 alias.<缩写> 可设置命令缩写,比如单行显示历史

    1
    2
    3
    4
    5
    6
    # 完整输入 
    git log --pretty=oneline
    # 设置简写
    git config --global alias.pretty "log --pretty=oneline"
    # 简写输入
    git pretty

    深度截图_google-chrome_20220126142950

  7. 常用的几个命令缩写

    1
    2
    3
    4
    5
    6
    7
    # 撤销工作区的更改
    git config --global alias.discard "restore --staged ."
    # 修改最后一次提交
    git config --global alias.amend "commit --amend --no-edit"
    # 打印简写
    git config --global alias.pretty "log --pretty=oneline"
    git config --global alias.oneline "log --oneline"

新建,添加,提交

  1. 在目标文件夹下,输入命令,新建仓库

    1
    git init
  2. git add 将工作区的文件提交到暂存区,常见用法:

    1
    2
    3
    4
    5
    git add <文件路径> # 添加文件
    git add <文件夹路径> # 添加文件夹
    git add *.html # 使用正则表达式匹配和添加文件
    git add . # 添加当前目录的文件和改动
    git add --all # 添加所有文件和改动

    补充说明:

    • git add 只能添加仓库所在文件夹内的文件
    • git add -p <文件名> 可以只提交文件的部分内容
  3. git commit 将暂存区的文件提交到版本库,常见用法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 单行注释
    git commit -m "<注释>"
    # 使用文本编辑器编写注释,默认编辑器为 vim
    git commit
    # 提交空内容:常用于演示,实战项目不建议使用
    git commit --allow-empty -m "empty commit"
    # -a 参数,直接将暂存区的所有改动提交到版本库
    git commit -a -m "don't need git add"
    git commit -am "don't need git add"

    补充说明:

    • git 要求每次提交都必须填写修改信息,原则上,信息内容应尽可能简单,清楚
    • git commit -a 提交工作区时,只能添加已追踪的文件,新文件无法处理
  4. git status 查看当前工作区状态

查看历史信息

  1. 直接输入 git log 显示提交历史的详细信息

    1
    git log

    20220126145556

  2. 三种版本历史的简洁显示方式,可配合 git config alias 设置缩写

    1
    2
    3
    git log --pretty=oneline
    git log --oneline
    git log --oneline --graph

    深度截图_google-chrome_20220126145845

  3. git 许多操作需要指定版本,两种常用方式分别是 commit-idHEADcommit-id 就是图中的橙色字体,一串 SHA1 码。指定某个版本,只需输入 commit-id 的前几位数字(至少4位),并确保不出现歧义。HEAD 用法如下:

    • HEAD 表示当前最新版本
    • HEAD^ 表示前一版本,多个 ^ 代表前几个
    • HEAD~n 表示前 n 个版本,n 取 0 代表最新版本。
  4. 技巧1-根据提交信息检索特定 commit

    1
    2
    3
    4
    5
    6
    # 检索作者
    git log --oneline --author="rex"
    # 使用转义 \| 表示或运算
    git log --oneline --author="rex\|wang"
    # 检索关键字
    git log --oneline --grep="1.txt"

    深度截图_选择区域_20220126151333

  5. 技巧2-根据提交时间或文本内容,检索特定 commit

    1
    2
    3
    4
    5
    6
    7
    # 版本历史中检索涉及关键字内容的版本
    git log -S "txt change"
    # 查找某一时段的 commit
    # 查找今天某一时段的改动
    git log --oneline --since="9am" --until="12am"
    # 查找某天开始,每天某一时段的改动
    git log --oneline --since="9am" --until="12am" --after="2022-01-01"
  6. 技巧3-检索设计特定文件的 commit,追加参数 -p 可以查看具体修改

    1
    git log --oneline <文件名>

    深度截图_选择区域_20220126165142

  7. “这行代码是谁写的!” 用 git blame <文件名> 查看某个文件的某一行代码是谁提交的,比如
    20220126165819
    前 5 行由两个 commit-id 修改,去掉参数 --oneline 可以看具体提交用户是谁。

删除,变更文件

  1. 删除文件,并将修改添加到暂存区

    1
    2
    3
    4
    git rm <文件>
    # 相当于
    rm <待删除文件>
    git add <删除的文件>
  2. 如果只是取消文件的追踪,可以用 --cached 参数,删除暂存区的文件,而保留工作区原先的文件

    1
    git rm <取消追踪的文件> --cached

    注:这个方法常用于删除被 .gitignore 添加,却仍在暂存区的文件

  3. 移动并将修改添加到暂存区

    1
    2
    3
    4
    git mv <file1> <file2>
    # 相当于
    mv file1 file2
    git add file1 file2

amend 修改最后一次提交

下边介绍 --amend 参数的方法。

  1. 场景一:当 commit 信息填写不当,需要改动时,有以下几种方法:

    • git reset 版本回退,再重新提交 commit
    • 使用 git rebase 命令(后边单独介绍)
    • 使用 --amend 参数,改动最后一次的 commit
  2. 举个例子,commit 时手滑写了 WTF,赶紧用 --amend 修改提交信息

    1
    2
    git log --oneline
    git commit --amend -m "Welcome To Facebook"

    20220126162537

  3. 场景二:刚刚完成一次 commit,但发现一些细节没有改好,又不想为这些细节重新发一次 commit,这是有几种方法:

    • git reset --soft/--mixed 版本回退,添加修改再重新提交
    • 使用 --amend 修改最后一次的 commit
  4. 举个例子

    1
    2
    3
    # < 文件改动 >
    # git add ... 添加文件到暂存区
    git commit --amend --no-edit # 提交修改

    最后的参数 --no-edit 表示无需修改提交信息,也可以用信息替代

gitignore

参考链接
CSDN:Git 中使用 .gitignore
CSDN:.gitignore 失效解决方法

  1. git 仓库中,.gitignore 指定的文件和文件夹会被 git add 自动忽略,但 -f 参数可以让 git add 添加被忽略的文件。
    使用说明:

    • <文件/文件夹> 忽略所有目录下的该文件或文件夹
    • ./<文件/文件夹> 忽略当前目录下的该文件名或文件夹
    • ! <文件/文件夹 ! 代表取消忽略
  2. 如果文件夹.gitignore 忽略,用 git add <path> -f 强制添加后,只有添加时刻的文件被 git 记录,之后文件夹内的其他文件变化都不会被记录。

  3. 注意,.gitignore 文件本身也可以设置被忽略,但使用场景很少。假设我需要在不同设备上,使用不同的 .gitignore,那么我在每个设备上,都添加 .gitignore,并忽略文件自身。这样一来相应文件可以设置忽略,而 .gitignore 也不会被共享。

  4. 使用命令 git clean -fX 清除被忽略的文件,其中参数 -f 指强制删除。


版本库相关

初学 Git 必须掌握的三个概念: 工作区缓存区版本库

查看改动

参考链接
简书:Git diff 比较两个版本文件之间的差异
博客园:git 还原文件到之前版本

假设某个文件修改后出 bug,我们需要将文件和以前版本对比,找出问题,这时用 git diff 来完成。语法如下,方括号 [] 内的参数可选,默认查看整个工作区。

1
2
3
4
5
6
7
8
9
10
11
# 工作区和暂存区的差异
git diff [<文件>]

# 查看工作区与指定版本的区别。
git diff <commit-id> [<文件>]

# 查看两个版本的区别
git diff <commit-id> <commit-id> [<文件>]

# --stat 参数查看简略区别
git diff <commit-id1> <commit-id2> --stat [<文件>]

图片示例

回退文件

文件不小心改错,要回退到改错前的版本,有两种方法

  1. git checkout 回退文件

    1
    2
    3
    git checkout <commit-id> <filename> # 将文件回退到 commit-id 的版本
    git checkout <filename> # 将文件回退到 HEAD 版本
    git checkout . # 将当前目录回退到 HEAD 版本

    注: git checkout 同时回退工作区和暂存区的文件,因而不需要再用 git add

  2. git resotre 回退文件

    1
    2
    git restore --staged <文件> # 暂存区文件回退 HEAD 状态
    git restore <文件> # 文件回退为 HEAD 状态,与 git checkout 等效

    添加参数 --staged 可以只回退暂存区的文件

回退工作区

git reset 版本回退,本质上是修改 HEAD 指向的位置。有三个可选参数 soft, hardmixed,不同参数对工作区和暂存区有不同影响,默认参数为 mixed,参考这里

三者区别如下:

  1. git reset --soft
    保留 工作区和暂存区 的内容,简单来说就是你的代码还在只是变成了未提交状态或未添加状态

  2. git reset --hard
    回退 工作区和暂存区 到指定的 commit 版本

  3. git reset --mixed
    保留 工作区 的内容,回退 暂存区 到指定的 commit 版本


git 分支

分支是 git 最重要的功能之一,特别是团队项目,而一些个人使用场景中,Git 分支也能发挥很大作用。

基本操作

1
2
3
4
5
6
7
8
git branch # 查看分支
git branch -v # 查看分支和相应 commit 简略信息
git branch -r # 查看远程版本库分支列表
git branch -a # 查看所有分支列表,包括本地和远程
git branch <分支名> # 新建分支
git branch -m <分支> <新分支名> # 修改分支名
git checkout <分支名> # 切换到分支
git checkout -b <分支名> # 切换到分支,不存在时创建分支

当远程存在某一分支,而本地未同步时,可以用 switch 命令,将在本地创建相应分支

1
git switch <分支名> 

常用命令的分支操作

1
2
3
git clone -b <dev> <github 地址> # 克隆远程仓库的指定分支 dev
git push origin master:<dev> # 推送到指定分支 dev
git push origin <dev> # 默认推送到与当前分支同名的云端分支

删除分支

参考博客园: Git删除分支/恢复分支

  1. 如果需要删除的分支不是当前正在打开的分支,使用 branch -d 直接删除

    1
    git branch -d <branch_name>
  2. 删除一个正打开,或未合并的分支,需使用 -D 选项

    1
    git branch -D <branch_name>
  3. 删除远端分支

    1
    git push origin --delete <分支名>

恢复被删除的分支

Git 会自行负责分支的管理,所以当我们删除一个分支时,Git 只是删除了指向相关提交的指针,但该提交对象依然会留在版本库中。因此,如果我们知道删除分支时的散列值,就可以将某个删除的分支恢复过来。在已知提交的散列值的情况下恢复某个分支:

1
git branch <branch_name> <hash_val>

如果我们不知道想要恢复的分支的散列值,可以用 git reflog 命令将它找出来。例如:

  1. 删除分支
    pic
  2. 输入 git branch next HEAD@{1} 恢复分支。
    pic

注:git reflog 命令显示整个本地仓储的 commit,包括所有 branch 的 commit ,甚至包括已经撤销的 commit。只要 HEAD 发生了变化, 就会在 reflog 里面看得到。


Github

GitHub 是世界上最大的同性交友网站基于 Git 的一个代码托管网站。开发者可以将代码在 GitHub 上开源,可以浏览其它项目的代码,fork 到自己名下做修改,clone 回本地(没有访问权限的 private repo 除外)使用,也可以发起 pull request 向上游提交自己的修改。

本节整理 Github 中常见场景的 Git 使用方法,初次使用 Github 还要进行设置:GitHub | 多账户设置以及下载加速

注:基于 Git 的代码托管网站还有很多,比如 GitlabGitee(码云) 等等,但相比之下,Github 有更多的生态基础。引用 Coding 的一段精炼介绍:Github 发明了碉堡了的两个功能:ForkPull Request。这两个功能创造了整个 Github 生态系统,使得“基因”得以繁衍和进化,充满了生命力。基因通过 Fork 被复制,而 Pull Request 使得基因得以进化。好的基因会被大量的 Fork,从而实现了优胜劣汰。这一整套体系才是精华所在,说 Github 是代码仓库显然太肤浅了。

本地推送到云端

  1. 在远端新建仓库
  2. git remote 连接远端仓库
    1
    git remote add origin <git 仓库地址> # 添加远端仓库为分支 origin
  3. 将本地仓库推送到远端
    1
    git push -u origin master 

注: -u 参数设置默认推送的分支,后续推送本地修改只需要 git push

远端克隆到本地

  1. 复制远端仓库地址
  2. git clone 到本地,此时目录下多了相应 git 仓库
    1
    git clone <git 仓库地址> 
  3. 如果对克隆的仓库有修改权限,后续可用 git push 推送本地修改

修改连接的仓库

使用场景:

  1. 取消当前仓库的链接

    1
    2
    3
    git remote # 查看远端仓库的分支名
    git remote -v # 查看远端仓库链接
    git remote remove origin # 删除远端分支
  2. 连接新仓库,并推送本地内容

    1
    2
    git remote add origin <git 仓库地址> 
    git push -u origin master

其他事项

汇总 Git 使用过程中遇到的问题。

未跟踪文件

没有被 git 跟踪的文件,不受 git 命令影响。

例如被 .gitignore 忽略的文件,或者尚未被 git add 的文件。无论用 git reset 重置工作区,git checkout 切换分支,还是 git rm * -rf 清空文件,都不影响。

win 换行符

参考 CSDN

在 windows 平台下 git add 的时候经常会出现如下错误
20210831231958

问题原因:
git 在 windows 下,默认是 CRLF 作为换行符,git add 提交时,检查文本中有 LF 换行符( linux 系统里面的),则会警告。所以问题的解决很简单,让 git 忽略该检查即可。
解决方法:执行下边命令

1
git config --global core.autocrlf false

fetch 和 pull

git fetchgit pull 命令的区别:

  • git fetch: 从远程仓库中获取最新的代码更新,并将其保存到本地的版本库中。这些更新不会立即应用到你当前的分支中,而是需要手动合并(merge)到本地分支后才能看到更改。在这个过程中,远程仓库中的更改被下载到本地版本库的远程跟踪分支(如 origin/master),而不会影响工作区或暂存区。

  • git pull:从远程仓库获取最新的代码更新,并自动将其合并到你当前的分支中。它实际上是执行了 git fetchgit merge 的组合操作。具体来说,git pull 将远程仓库中的更改下载到本地版本库的远程跟踪分支,然后将这些更改与当前分支进行合并。如果当前分支有未提交的修改,那么 git pull 会先自动将这些修改提交到本地版本库中,再进行合并。