Git 学习笔记

1. 分布式版本控制系统和集中式版本控制系统的区别

分布式版本控制系统(DVCS)和集中式版本控制系统(CVCS)是两种常见的版本控制系统,它们在数据存储、协作方式和容错性等方面存在显著的区别。

  1. 数据存储:

    • CVCS:CVCS 中的代码仓库是集中存储在中央服务器上的,开发者通过与中央服务器进行交互来获取和提交代码。
    • DVCS:DVCS 中的代码仓库是分布存储的,每个开发者都拥有完整的代码仓库副本。开发者可以在本地进行代码的提交、分支创建等操作。
  2. 协作方式:

    • CVCS:在 CVCS 中,开发者之间的协作通常是通过与中央服务器进行交互来共享代码和合并修改。
    • DVCS:在 DVCS 中,开发者之间可以直接通过本地的代码仓库进行协作,包括分支合并、代码分享和冲突解决等操作。
  3. 容错性:

    • CVCS:CVCS 的容错性较低,如果中央服务器发生故障或网络中断,开发者无法正常进行版本控制操作。
    • DVCS:DVCS 具有较高的容错性,即使中央服务器不可用,开发者仍可以在本地进行版本控制操作,并在服务器恢复后同步更改。
  4. 分支管理:

    • CVCS:CVCS 的分支管理相对较为复杂,通常需要与中央服务器进行交互,并且在合并分支时可能会出现冲突。
    • DVCS:DVCS 的分支管理更加灵活和简便,开发者可以在本地创建、切换和合并分支,与其他分支进行并行开发,减少冲突和合并问题。

2. 下载并安装 Git

  • 官网下载地址: https://git-scm.com/download/win

  • 安装完成后,打开 PowerShell 或 CMD 等终端,输入 git -v 命令查看已安装的版本并进行使用。或者直接打开桌面的 Git Bash 应用以进行使用。

3. 初始化配置

  • 配置用户名:

    1
    git config --global user.name "username" 

    此命令用于设置全局的 Git 用户名,指定提交代码时的身份信息。将 “username” 替换为你的实际用户名,如果用户名中包含空格,则需要使用双引号将其括起来。

  • 配置邮箱:

    1
    git config --global user.email "xxxx@gmail.com"

    此命令用于设置全局的 Git 邮箱地址。通过指定一个有效的邮箱地址,可以在提交代码时将该邮箱作为联系方式附加到每个提交中。将 “xxxx@gmail.com“ 替换为你的实际邮箱地址。

  • 记住密码:

    1
    git config --global credential.helper store

    此命令用于设置 Git 凭据助手为 “store”。”store” 凭据助手是默认的简单凭据助手,以明文形式将凭据存储在本地文件中。第一次输入凭据后,它会自动使用这些凭据进行认证,避免重复输入用户名和密码。

  • 查看配置:

    1
    git config --global --list

    这条命令用于查看当前全局的 Git 配置。它将显示已经设置的所有配置信息,包括用户名、邮箱、记住密码设置等。

4. 工作区域和文件状态

  • 工作区域

    1. 工作区:编辑的代码所在目录
    2. 暂存区:执行 git add 命令后,文件从工作区添加到暂存区
    3. 仓库:执行 git commit 命令后,文件从暂存区提交到仓库

    alt text

  • 文件状态

    1. 已跟踪(Tracked):已被纳入版本控制的文件。这些文件会存在于仓库的不同版本中。

    2. 未跟踪(Untracked):未被纳入版本控制的文件,仅存在于工作区中。

    3. 未修改(Unmodified):已跟踪文件,但自上次提交后未被修改过。

    4. 已修改(Modified):已跟踪文件,在工作区被修改过,但还未暂存到缓存区。

    5. 已暂存(Staged):已修改的跟踪文件,标记为将要包含在下次提交里。

    6. 已提交(Committed):最新版本已安全提交到本地仓库,但还未推送至远程仓库。

    跟踪文件的状态变化主要是:未修改 -> 已修改 -> 已暂存 -> 已提交。

    alt text

5. git reset 回退版本

  • git reset --soft <哈希值>^:回退到某个版本,代码还在暂存区

  • git reset --hard HEAD^:彻底回退到上一个版本

  • git reset --mixed HEAD~3:回退到前3个版本,代码在工作区

  • git reflog:查看所有分支的所有操作记录

  • git ls-files:查看暂存区文件

  • git log:查看仓库历史提交记录

6. 使用 git diff 查看差异

命令 比较区域
git diff 工作区 vs 暂存区
git diff head 工作区 + 暂存区 vs 版本库
git diff --cached / git diff --staged 暂存区 vs 版本库
git diff <commit_hash> <commit_hash> / git diff head~ head 比较提交之间的差异
git diff <branch_name> <branch_name> 比较分支之间的差异
  1. git diff: 比较工作区中的文件与暂存区之间的差异。将显示修改但尚未暂存的内容。

  2. git diff head: 比较工作区的文件与版本库最新提交(HEAD)之间的差异,包括已修改但尚未暂存的内容,以及已暂存但尚未提交的内容。

  3. git diff --cachedgit diff --staged: 两者是相同的命令,用于比较暂存区(也称为缓存区)与版本库最新提交之间的差异,包括已暂存但尚未提交的内容。

  4. git diff <commit_hash> <commit_hash>git diff HEAD~ HEAD: 两者都是用于比较两个提交之间的差异。第一个指定两个提交的哈希值,用于比较它们之间的差异。第二个使用相对引用来比较最新提交和其父提交之间的差异。HEAD~ 表示当前提交的父提交。

  5. git diff <branch_name> <branch_name>:比较两个分支之间的差异,将显示两个分支之间的差异,包括文件的添加、修改和删除等。

补充命令: git show

  • git show :显示指定提交的详细信息。 可以是提交的哈希值(全部或部分)或标签名。

  • git show ::显示指定提交中指定文件的变更内容。 是提交的标识符, 是文件的路径和名称。

git show 可以显示指定提交的详细信息,包括提交的作者、日期、提交说明等,并展示提交引入的文件变更。

7. 使用 git rm 删除文件

  1. rm file ; git add file: 先从工作区删除文件,然后再暂存删除的文件。(不推荐)

  2. git rm <file>: 把文件从工作区和暂存区同时删除,同时会被标记为待删除状态。

  3. git rm --cached <file>: 若不想将某文件提交到版本库中,可以使用此命令将其从暂存区中移除。

  4. git rm -r *: 递归删除某个目录下的所有子目录和文件。

注意: 以上操作的最后不要忘记提交

8. .gitignore 忽略文件

.gitignore文件用于指定在版本控制系统中忽略哪些文件或目录。

  1. 文件格式:.gitignore 是一个文本文件,每行包含一个忽略规则。可以使用通配符和模式匹配来指定要忽略的文件和目录。

  2. 文件路径:忽略规则可以是文件路径、文件名、目录名或模式。可以使用相对路径或绝对路径来指定文件或目录。

  3. 通配符:可以使用通配符来匹配文件名或目录名。常用的通配符有 *(匹配任意字符),?(匹配单个字符)和 **(匹配多级目录)。

  4. 注释:可以在 .gitignore 文件中使用 # 符号来添加注释。注释行将被 Git 忽略。

  5. 文件匹配规则:.gitignore 文件中的每一行都代表一个文件或目录的匹配规则。如果规则以 / 开头,则表示相对于仓库根目录的路径。如果规则以 / 结尾,则表示目录。

  6. 目录匹配:如果在 .gitignore 文件中指定了一个目录名,Git 将忽略该目录及其所有内容。可以使用 / 前缀指定相对于根目录的目录。

  7. 递归匹配:可以使用 **/ 来递归匹配目录及其子目录。例如,**/*.log 将匹配所有目录下的 .log 文件。

  8. 特殊规则:.gitignore 文件中可以使用一些特殊规则,例如 ! 来否定匹配、/ 开头的规则表示根目录、以及以 / 结尾的规则表示目录。

注意,.gitignore 文件只适用于尚未添加到 Git 的文件。如果文件已经被 Git 跟踪并提交过,需要先使用 git rm 命令将其从仓库中移除。

9. SSH 配置和克隆Github仓库

  1. 检查是否已在本地生成SSH密钥对。如果没有,可以通过以下命令生成:

    1
    ssh-keygen -t rsa -b 4096
    • ssh-keygen 是生成 SSH 密钥对的命令。
    • -t rsa 选项指定要生成的密钥类型为 RSA。RSA 是一种非对称加密算法,常用于 SSH 密钥对的生成。
    • -b 4096 选项指定要生成的密钥的位数为 4096 位。较长的密钥长度提供更高的安全性,但也会增加计算开销。

    执行这个命令时,会生成一对密钥:私钥(private key)和公钥(public key)。私钥是存储在你的计算机上的机密文件,用于对加密数据进行解密和进行身份验证。公钥是存储在远程服务器上的公共文件,用于加密数据和验证你的身份。

    在执行命令后,将会被要求选择要保存密钥的文件路径和文件名,并且可以选择设置一个密码来保护私钥的访问。生成的密钥文件通常包括两个文件:

    • 私钥文件(默认为 id_rsa):存储在 ~/.ssh/ 目录下。
    • 公钥文件(默认为 id_rsa.pub):与私钥文件关联,并可用于将公钥添加到远程服务器上的授权列表中。

    生成密钥后,你可以将公钥添加到远程服务器上,以便在与远程服务器建立 SSH 连接时进行身份验证。

  2. 在GitHub账户设置中添加SSH公钥。复制id_rsa.pub文件内容,在GitHub的SSH Keys设置页面粘贴公钥。

  3. 获取GitHub仓库的SSH链接格式,一般是:

    1
    git@github.com:username/repo.git
  4. 通过SSH链接克隆仓库:

    1
    git clone git@github.com:username/repo.git
    • 使用SSH链接不需要在每次交互时输入用户名和密码,可以更方便地管理仓库。
    • 如果在克隆时出现“Permission denied”错误,需要检查SSH keys的设置是否正确,以及当前用户是否有权限访问该仓库。

补充:在使用 SSH 连接远程主机时,SSH 首先会尝试使用默认的密钥对,即 ~/.ssh/id_rsa(私钥)和 ~/.ssh/id_rsa.pub(公钥)。要指定本地仓库使用的密钥对,可以使用 SSH 配置文件来关联密钥对和远程主机。

  1. ~/.ssh/ 目录下创建或编辑一个名为 config 的文件(如果该文件不存在)。

  2. config 文件中,为每个远程主机指定密钥对。例如,假设你有两个远程主机:github.comgitee.com。可以像这样指定密钥对:

    1
    2
    3
    4
    5
    6
    7
    8
    Host github.com
    IdentityFile ~/.ssh/github_key

    Host gitee.com
    IdentityFile ~/.ssh/gitee_key

    Host gitlab.com
    IdentityFile ~/.ssh/gitlab_key
  3. 保存并关闭 config 文件。

配置完成后,Git 在连接到相应的远程主机时,将自动使用指定的密钥对进行身份验证。
需要注意的是,除了使用 SSH 配置文件,还可以使用其他方式指定使用的密钥对,例如在 git clone 命令中使用 -i 选项直接指定私钥文件的路径。详细的用法可以参考 Git 和 SSH 的文档以及相应的命令帮助信息。

10. 关联本地仓库和远程仓库

  1. 将本地仓库与远程仓库建立关联,使得可以通过 origin 别名访问远程仓库

    1
    git remote add origin git@github.com:lololowe/lll.git
    • git remote add 用于添加远程仓库的别名。
    • origin 是远程仓库的别名,你可以自定义别名。
    • git@github.com:lololowe/lll.git 是远程仓库的地址,指定了要关联的远程仓库的 URL。
  2. 将当前分支重命名为 main,通常用于将默认分支名从旧的命名(如 master)改为新的命名(如 main)。

    1
    git branch -M main
    • git branch 用于创建、列出或删除分支。
    • -M 是一个选项,用于重命名(move)分支。
    • main 是新的分支名,你可以根据需要指定不同的分支名。
  3. 将本地的 main 分支推送到名为 origin 的远程仓库,并将本地分支与远程分支进行关联。通过 -u 选项,设置了跟踪关系,以便后续的推送和拉取操作可以更加便捷。

    1
    git push -u origin main
    • git push 用于将本地仓库的更改推送到远程仓库。
    • -u 是一个选项,用于设置跟踪分支,将本地分支与远程分支进行关联。
    • origin 是远程仓库的别名。
    • main 是要推送的本地分支名。

补充:可使用git remote -vgit remote --verbose命令查看当前 Git 仓库中配置的所有远程仓库的别名和对应的 URL。通常,远程仓库的别名为 origin,但也可以有其他自定义的别名。

11. 分支的基本操作

  1. 显示所有本地分支的列表。当前分支前面会有一个 * 标记。

    1
    git branch
  2. 创建一个新的分支,名称为 <branch-name>

    1
    git branch <branch-name>
  3. 切换到指定的分支,名称为 <branch-name>。这是 Git 旧版本的命令,在 Git 2.23 版本后建议使用 git switch 命令。

    1
    git checkout <branch-name>
  4. 切换到指定的分支,名称为 <branch-name>。这是 Git 2.23 版本后引入的新命令,推荐使用。

    1
    git switch <branch-name>
  5. 将指定分支 <branch-name> 合并到当前分支。执行合并操作会将指定分支的提交合并到当前分支,并生成一个新的合并提交。

    1
    git merge <branch-name>
  6. 删除指定的本地分支 <branch-name>。注意,只能删除已经合并到其他分支的分支,否则会出现警告。

    1
    git branch -d <branch-name>
  7. 强制删除指定的本地分支 <branch-name>。不管分支是否已经合并,该命令都会执行删除操作。

    1
    git branch -D <branch-name>

补充命令: git log --graph --oneline --decorate --all

  • --graph: 以图形方式展示提交历史,以直观的方式显示分支和合并关系。

  • --oneline: 以简洁的单行格式显示每个提交的摘要信息,只显示提交的哈希值和提交消息的第一行。

  • --decorate: 在输出中展示分支和标签的名称,以便更清楚地识别每个提交所属的分支或标签。

  • --all: 显示所有分支的提交历史,包括本地分支和远程分支。

以图形化展示简洁的提交历史,包括所有分支的提交信息:

1
alias graph="git log --graph --oneline --decorate --all"

12. 解决合并冲突

  • 两个分支未修改同一个文件的同一处位置: Git 自动合并
  • 两个分支修改了同一个文件的同一处位置(修改内容不相同): 产生冲突

解决方法:

  1. 手动修改冲突文件,合并冲突内容
  2. 添加暂存区(git add <file>.)
  3. 提交修改(git commit -m <message>)

13. 回退和 rebase

Git中的Rebase和Merge都是用于合并分支,但是两者的区别在于:

  • Merge(合并)会生成一个新的合并提交。保留完整的分支历史和合并过程,易于追溯变化。

  • Rebase(变基)会把一个分支上的所有提交改建到另一个分支顶部,重写commit历史,看起来像一个线性的提交过程。

区别示例:

1
2
3
A - B - C         ⬅ 主分支  
\
D - E - F ⬅ 特性分支

Merge:

1
2
3
4
5
A - B - C ⬅ 主分支
\ \
D - E - F ⬅ 特性分支
\
G ⬅ 合并提交(Merge)

Rebase:

1
A - B - C - D - E - F ⬅ 主分支

何时使用Rebase:

  • 需要维持线性简洁的提交历史时。

  • 在多人协作前整理提交,避免不必要的合并提交。

  • 在推送至公共仓库前整理提交历史。

何时使用Merge:

  • 需要保留完整的分支历史和合并过程。

  • 在推送和合并到公共仓库的最终阶段。

  • 记录特性分支开发情况,便于代码审查和追溯变化。

总结:

  • Rebase重写历史,Merge保留历史。

  • 在私有分支可用Rebase简化历史。

  • 在公共仓库可用Merge保留完整过程。

根据场景选择恰当的合并方式。

alt text

参考课程

  1. https://www.bilibili.com/video/BV1HM411377j/