Git

版本控制工具

版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。版本控制能方便查看更改历史,备份以及恢复以前的版本,保证多人协作不出问题。

Git对待数据的方式,更像是把数据看作是对小型文件系统的一组快照。每次提交更新或者在Git中保存项目状态时,它主要对当时的全部文件制作一个快照保存这个快照的索引。为了高效,如果文件没有修改,Git不再重新存储该文件,而只是保留一个链接指向之前存储的文件,Git对待数据更像是一个快照流


安装Git和TortoiseGit

TortoiseGit

TortoiseGit提供了Git的图形化操作界面,Git工作区的目录和文件的图标符加了标识版本控制状态的图像,可以非常直观地看到哪些文件被更改了需要提交。通过对右键菜单的扩展,可以非常方便操作Git版本库。

通过设置,选择Git提供的ssh客户端,这样在下载ssh协议的代码仓库时,命令行与图形界面都可以使用同一套公钥和密钥


Git基本配置

3种配置等级

  1. 系统配置:git config --system

所有用户都有效,存放在git的安装目录下:%Git%/etc/gitconfig

  1. 用户配置:git config --global

只对当前用户有效,存放在用户目录下:~/.gitconfig

  1. 仓库配置:git config --local

    只对当前项目有效,存放在项目目录下:.git/config


配置个人身份

1
2
git config --global user.name "Hunter"
git config --global user.email hspecial@163.com

这个配置信息会在Git仓库提交的修改信息中体现和Git服务器认证使用的密码或者公钥密码无关


文本换行符配置

这一节的内容仍然是给我造成困惑的部分,感觉从参考资料来看,跨平台的项目开发应该要自动转换换行符。但是在我目前有限的开发经验来看,似乎并没有对项目造成影响,怀疑可能是IDE完成了相应的工作。

默认的配置状态为:core.autocrlf=true,暂且不去作修改,待日后有了深刻的理解再来完善本节内容。


编码配置

  • 中文编码支持

    • git config --global gui.encoding utf-8

      图形化界面采用的编码

    • git config --global i18n.commitencoding utf-8

      git commit log存储时,采用的编码

    • git config --global i18n.logoutputencoding utf-8

      查看git log时,显示所采用的编码

  • 显示路径中的中文

    git config --global core.quotepath false


与服务器的认证配置

http/https协议认证

禁用SSL证书验证:git config http.sslverify false

  1. 当你连接到一个不受信任的远程仓库时,可能没有有效的 SSL 证书,或者证书已经过期。
  2. 当你在开发或测试环境中使用自签名的 SSL 证书时,可能需要禁用验证以避免错误。

SSH协议认证

使用公钥认证,无需输入密码,加密传输,操作便利又保证安全性。


配置过程
  1. 生成公钥

    在bash命令行中输入如下命令:

    1
    ssh-keygen -t rsa -C "hspecial@163.com"

    一路回车,就能在用户目录下~/.ssh中,生成公钥id_rsa.pub

  2. 添加公钥id_rsa.pub中的内容到代码平台


新增公钥

出于某些情况,你可能要在另外的平台或者另外的项目,使用不同的邮箱来生成公钥,以便和代码平台的账号相匹配

  1. 生成公钥

    提示Enter file in which to save the key(输入要保存密钥的文件,即id_rsa文件)”时,输入密钥文件的保存位置。为了和用户配置等级的密钥区分,我选择在默认公钥的目录下创建对应的平台或项目名的文件夹。输入保存密钥的文件绝对路径即可。

  2. 添加公钥id_rsa.pub中的内容到代码平台


Git基本命令

Git版本控制下的工程区域

  1. 工作区 working directory

日常工作的代码文件或者文档所在的文件夹。

  1. 暂存区 stage

一般存放在工程根目录.git/index文件中,因此也可以把暂存区叫做索引。

  1. 版本库 Repository

.git文件夹就是Git的版本库,也可以叫本地仓库

image-20240717003436883

Git版本控制下的三种文件状态

  1. 已修改 modified

修改了某个文件,但还没有提交保存

  1. 已暂存 staged

把已修改的文件放在下次提交时要保存的清单中。被git add的文件就是已暂存的状态。

  1. 已提交 committed

文件已经被安全地保存在本地数据库中


常用命令

工程准备

  • git init:本地目录下新建git项目仓库

  • git clone [URL]复制远端工程到本地

    如果所在的项目Git服务器已支持git-lfs对二进制文件进行了区别管理,那么复制工程的时候**务必使用git lfs clone [URL]**,否则克隆操作无法下载到工程中的二进制文件,工程内容不完整。


查看修改

  • git diff:查看工作区的修改内容(当前索引与上次提交/任意两个节点(分支)之间的差异)

    • git diff --cached:当前索引与上次提交之间的差异

    添加--name-status参数,可以只看文件列表

  • git status:查看工作区和暂存区的文件状态

    该命令能看到修改的git文件是否已被暂存,新增的文件是否纳入了git版本库的管理。

    • Untracked files:新建但未被跟踪的文件
    • Changes not staged for commit:修改但未被暂存的文件
    • Changes to be committed:已修改并已暂存的文件

文件修改后提交推送

  • git add新增文件到暂存区

    把文件添加到暂存区,是提交修改文件之前的必要操作。如果文件已经被git追踪,即曾经提交过,在Git的早期版本中,需要git add再提交;在较新的版本中,无需git add即可提交

  • git rm删除文件到暂存区

    执行git rm后,通过git status查看时,会有deleted: xxx的提醒。之后进行提交,对应的文件就不再受git工程的管理直接从硬盘中删除文件,然后对该文件执行git commit,git会自动将删除的文件从索引中移除,效果相同

  • git mv移动文件到暂存区

    相当于mv操作的基础上,用git将操作传入了暂存区。

  • git commit提交更改的文件

    将暂存区中的文件改动提交到本地的版本库,一般需要附带提交描述信息,所以常见的用法是:

    git commit file_name -m "commit message"

    如果要一次性提交所有在暂存区改动的文件到版本库,可以执行git commit -am "commit message"

  • git push:将本地版本库的分支推送到远端对应的分支

    常见的推送命令格式:git push origin branch_name:new_branch_name

    new_branch_name是推送成功后,在远端服务器上的分支名:new_branch_name可以不写,远端分支名就和本地分支名相同。


查看日志

  • git log:查看当前分支上的提交日志

    git log默认按提交时间的由近到远列出所有的历史提交日志,每个日志基本包含:

    1. 提交节点
    2. 作者信息
    3. 提交时间
    4. 提交说明
    • --name-status

      能列出涉及改动的具体文件


分支管理

  • git branch:列出所有本地分支

    如果想查看远端服务器上的分支,-r即可,返回的分支名带origin前缀,即表示在远端。如果想查看远端和本地的所有分支,-a即可。

  • 新建分支

    默认基于当前节点创建分支

    • git checkout -b new_branch_name 新建后会自动切换到新分支
    • git branch new_branch_name 新建后不会切换到新分支
  • 删除分支

    • git branch -d branch_name

    • git branch -D branch_name 大写的D表示强制删除

      当目标分支上包含未合并的改动,则需要通过强制删除来操作。

    • git branch -d -r branch_name 删除服务器上的远程分支

      删除后还需要推送到服务器git push origin : branch_name

  • 切换分支

    • git checkout branch_name

    • git checkout -f branch_name

      如果当前分支工作区存在修改未提交的文件,与目的分支上的内容冲突,会导致切换失败,则需要使用git checkout -f进行强制切换

    checkout的对象可以是分支,也可以是某个提交节点或者节点下的某个文件

  • 从远端更新分支

    • git pull origin remote_branch:local_branch

      从远端服务器中获取某个分支的更新,再**与本地指定的分支进行自动合并(merge)**,如果远程指定的分支与本地指定的分支相同,则可以直接执行git pull origin remote_branch

    • git fetch origin remote_branch:local_branch

      git pull不同,git fetch在获取到更新后,不会自动合并,而是留给用户一个操作空间,确认git fetch内容符合预期后,再决定是否手动合并节点


分支合并

合并目标分支内容到当前分支,以下两种方式都可以达到目的

  • git merge branch_name

    git会将指定的分支与当前分支进行比较,找出二者最近的一个共同节点base,之后将指定分支在base之后分离的节点合并到当前分支上实际上是分支之间,差异提交的节点的合并会保留每个分支的所有历史

    git merge可能会遇到冲突,此时使用

    缺点:每次合并会产生新的提交节点,导致分支线混乱;分支关系模糊,无法准确看到哪些是合并产生,哪些来自原始分支。

    image-20240716165732981
  • git rebase branch_name

    rebase可以避免merge的交织

    image-20240716170407006

二者的实现机制和对合并后节点造成的影响有很大差异,有着各自的风险

推荐操作:

  1. 次级分支rebase main。

    次级分支rebase可以让自己的提交一直处于最前面,保证提交时间的有序性。

  2. main merge次级分支。

    可以看到主分支的哪个特性是基于次级分支同步而来。

实际开发中,比如当前自己开发的分支为hunter_test,主分支为main。应当通过Rebase 'hunter_test' onto main(onto,把当前分支的基点移动到master分支的最新提交),同步最新的master分支代码。再将自己的修改提交并push到远程分支,向main分支发起merge request请求。main分支通过Merge 'hunter_test' into 'main'将特性分支的更改合入到main分支中


撤销操作

  • git reset commit_id

    可将工作区内容回退到历史提交节点。IDEA中选择目标分支的对应提交,右键Reset Current Branch to Here

    image-20240717010957062 image-20240717003727407
    • Soft模式:不改变工作目录和暂存区(git add后),将被回退的提交放入暂存区
    • Mixed模式:清空暂存区,将回退的变更和暂存区的内容都放入工作目录
    • Hard模式:回退至指定提交,清空暂存区和工作目录的变更。(清除最彻底的模式
    • Keep模式:保留本地的变更,清除被回退的提交记录。
  • git checkout .

    回退本地所有未提交的修改。这是一条有风险的命令,不给用户任何确认机会。它会取消本地工作区的修改,用暂存区的所有文件直接覆盖本地文件,达到回退内容的目的

    如果仅仅想回退某个文件的未提交改动,可以使用git checkout -filename;如果想将工作区回退到某个提交版本,可以使用git checkout commit_id


进阶操作

Patch

适用于生成和应用补丁文件,可以在不同的代码库之间传递和应用提交的更改内容。

git format-patch -l -o path

  • -l:最近一次提交

    也可指定数量,搭配提交记录号指定具体的提交。例如git format-patch -1 提交记录号 -o path

  • -o:补丁文件路径

IDEA中的操作方式:

  1. 选中需要的提交,右键选择Create Patch...

    image-20240716230323603
  2. 可以选择存到文件或剪切板中。

    image-20240716234102223
  3. 工具栏 - Git - Apply Patch

    image-20240716235446452

git cherry-pick

适用于选择性地将单个或多个提交应用到当前分支中,创建一个新的提交。

在当前分支,git cherry-pick 需要的提交记录号,就能将指定的提交添加到当前分支中。

image-20240716175021314

如果合并指定的提交到当前分支遇到冲突,3种处理方式如下:

  1. 解决所有冲突之后,通过git add/rm 更改内容进行标记,git cherry-pick --continue
  2. git cherry-pick --skip 跳过冲突的提交。
  3. git cherry-pick --abort,终止执行,并回到执行cherry-pick之前的状态

IDEA中的操作方式:

选中目标分支中的目标提交,**右键选择Cherry-Pick**就能将对应的提交应用到当前分支中。

image-20240716180532508

git stash

  • git stash命令将当前本地的所有变更暂存到一个栈中,然后就可以干净地切换分支。
  • git stash save '暂存一些修改' 可以添加注释。
  • git stash list 可以看到stash的所有记录。
  • git stash pop可以将最新的一次暂存的代码恢复到本地。

revert

代码回退。

IDEA中,选中目标提交,右键点击Revert Commits,就能进行代码回退,并且会产生新的对应的逆向操作提交

image-20240717001901137

生成新的逆向操作的提交的好处是,如果后续leader的想法改变,需要保留删除的代码,此时对revert的提交再revert一次就可以了

image-20240717002018794

解决代码合并冲突

通常在项目中,各种git操作(merge、rebase…)都可能引起代码冲突。在IDEA中中会弹出一个冲突提示框:

image-20240717010112329

有3种处理情况:

  1. 点击Merge...依次查看处理冲突,进行代码合并。

  2. 合并不顺利,后悔了不想合并代码,点击Close关闭该冲突提示框。此时右下角应该会有一个xxx stopped due to conflicts弹框。

    点击Abort就能终止执行,并回到执行引起代码冲突的git操作之前的状态。

    image-20240717010409227
  3. 代码已合并完成,在push之前后悔了。在当前代码分支,选择最新的提交记录,右键Reset Current Branch to Here,Hard模式(回退至指定提交,清空暂存区和工作目录的变更。(清除最彻底的模式)),就能清除合并记录。