一些前言:
    版本控制除了git其实还有SVN等,但是git比较流行。
    SVN是集中式版本控制系统(所有信息在服务器,单点故障隐患),而git是分布式版本控制系统(用户有全部信息,安全隐患)。
    先说操作再补充一下理论知识(源于B站Git基本原理介绍(1)——代码仓库的初始化_哔哩哔哩_bilibili系列内容)。


一、IDEA中操作git:


本地编辑了一些文件,下一步想要提交,首先需要更新(pull),然后再提交(push),避免覆盖。
pull并不会覆盖本地修改的文件,会将远程仓库的更新合并到本地。
pull和push在左下角的git插件中右键当前分支就有显示。

1.pull操作
首先说pull,点击左上角版本控制,点击更新,就完成了pull操作。
但这里出现两个选项,说一下他们的区别:
    将传入更改合并到当前分支:
        实际上是 git pull --merge,会将远程分支的更新合并到当前分支,如果有冲突需要手动解决
    在传入更改上变基当前分支:
        实际上是 git pull --rebase,会将本地的提交放到远程分支的更新之后,形成一条线性的提交历史
例如:
    远程分支历史:A---B---C   (origin/main)
    本地分支历史:A---B---D   (main)
    如果是merge:
        A---B---C
             \   \
              D---M   (main)
    如果是rebase:
        A---B---C---D'   (main)
如果本地修改的文件和远程仓库的更新有冲突,rebase会暂停并提示解决冲突,解决后需要使用git rebase --continue。
如果希望保持提交历史的线性,可以选择rebase;如果希望保留分支的合并历史,可以选择merge。

2.push操作
一般的项目开发中,一般自己先开一个自己的分支,用作测试,等自己开发完成后,再合并到主分支。如果直接在生产环境的主分支上开发,bug了怎么办呢?
具体步骤:
    idea左下角的git插件中,右击main有一个新建分支,比如新建分支"myFirstBranch"
    然后右击该分支,选择 签出 把工作区切换到该分支
    (这样下一次push才会push到这一分支,假如工作区还在main,那改的还是main!)
    不断地开发,push,这一分支没bug了就可以合并到main了:切换工作区到main以后,右键自己的分支,选择合并
    合并以后右键main,选择push推送。

当开发自己分支时,如遇他人修改,此时的更新操作:
    a. 左上角的版本控制点击更新项目,
    b. git插件中右键主分支,点击更新,现在主分支更新到最新了
    c. 切换工作区到自己的分支,
    d. 右键主分支,选择合并到当前分支,即可完成更新。


二、一些基本理论:

Git有四个工作区域:
        ---------------------------
       |工作目录(Working Directory)|                平时存放项目代码的地方
        ---------------------------
        |                       ↑
    git add files           git checkout
        ↓                       |
        -------------------------
        |   暂存区(Stage/Index)  |                   用于保存即将提交的文件列表信息
        -------------------------
        |                       ↑
    git commit              git reset
        ↓                       |
        -------------------------
        |   本地仓库(History)    |                   HEAD所指向的仓库。是安全存放数据的位置,这里面有提交的所有版本的数据
        -------------------------
        |                       ↑
    git push                git pull
        ↓                       |
        --------------------------
       |远程仓库(Remote Repository)|                 托管代码的服务器
        --------------------------
* HEAD:指向当前分支的最新提交记录。

git管理的文件状态:未跟踪(untracked),未修改(unmodified),已修改(modified),已暂存(staged),已提交(committed)

.gitignore文件:该文件用于忽略上传,可以使用通配符。
    名称的前面有 / 表示忽略该目录下的文件或文件夹,后面有 / 表示忽略该目录及其下所有内容。
    比如/temp表示忽略当前目录下的temp文件夹,而temp/表示忽略所有temp文件夹。

    通常java项目忽略 .idea目录,所有的class文件,日志文件,锁文件,target目录等等:
        .idea/
        *.class
        *.log
        *.lock
        target/

三、一些更加细节的理论

a. 当 git init时,发生了什么?
    1. 创建了一个名为 .git 的隐藏目录
    2. 在 .git 目录下创建了一些子目录和文件,比如config、HEAD等
    3. 初始化了main分支
    4. 创建了一个空的暂存区(Index)
* 当前.git的config内容是本地配置(比如用户名、用户邮箱),全局配置在用户目录下的gitconfig文件中。
* commit的时候优先用本地配置。


b. git add 时,发生了什么?
    1. 将工作区中要add的文件的当前状态复制到暂存区
    2. 创建一个新的blob对象在 .git/objects 目录下,存储 文件内容、类型 ,以SHA-1哈希值命名(比如1c/bfc3...十六进制),
       根据blob可以找到文件内容(命令 git cat-file -p <sha1>)。
        如果两个文件完全一样,对应的blob对象也是一样的。
        sha1哈希值是怎么计算的? ---实际计算的是 "blob len\0文件内容" 的SHA-1哈希值,len是文件内容的字节长度。
            Hash算法通过散列算法将任意长度的输入转换为固定长度的输出,hash过程不可逆。
            常见的有MD5 (128bit)、SHA-1 (160bit)、SHA-256 (256bit)。前两种不安全,已被破解。
    3. 那么文件名信息存储在哪里呢?在.git/index索引中。可以用git ls-files -s查看索引内容。
       索引中存储了:文件权限 + blob对象 + + 文件名。
* 在修改文件时,会创建新的blob,而旧的blob也会存在,所以git可以保存文件的历史版本。


b. git commit 时,发生了什么?
    1. commit后会返回一个哈希值,可知他生成了一个 commit对象 ,依旧存储在 .git/objects
        查看这个哈希值,包含tree、作者信息、时间戳、父commit 对象。
            继续查看tree对象,内容是 这次commit的文件的 索引。
    2. 在.git/refs/heads/目录下生成head引用,内容是这次的commit 对象。
        .git/HEAD文件指向当前分支的引用,比如 refs/heads/main,如果checkout到其他分支,HEAD会更新为指向该分支的引用。
    3. 在.git/logs/refs/heads/目录下生成日志文件,记录了每次commit的历史。
        可以用git reflog查看这些日志,包含每次commit的哈希值、作者信息、时间戳以及操作类型(commit、merge等)。
* 如果commit包含了文件夹,那么这次commit生成的tree 会包含另一个tree,存储新文件夹下文件的索引(文件名 + 对应的blob对象)。

示意图:
    commit1 对象←-parent--commit2 对象 ←-parent--commit3 对象
        ↓                   ↓                        ↓
    tree1 对象           tree2 对象               tree3 对象 → file1/file2Modified
        |                   |                       ↓
    -----------         -----------               子tree
    ↓         ↓         |         ↓                 ↓
  file1     file2       |     file2Modified      file3(blob)
  (blob)    (blob)      |         (blob)
    ↑--------------------    
c. git push 时,发生了什么?
    1. 将本地仓库中的commit对象发送到远程仓库
    2. 更新远程仓库的引用(refs),使其指向最新的提交对象
    3. 远程仓库接收并存储这些提交对象


d. 什么是hook?
    Git hooks是一些脚本,可以在Git特定事件发生时自动执行。
    这些脚本存放在.git/hooks目录下,默认有一些示例脚本,可以根据需要启用或编写自己的脚本。
    常见的钩子包括:
        pre-commit:在提交之前运行,可以用于代码检查、格式化等
        post-commit:在提交之后运行,可以用于发送通知、更新文档等
        pre-push:在推送之前运行,可以用于运行测试、验证代码等


四、Linux下git的常用命令
1. git init:初始化一个新的Git仓库
2. git clone <repository>:克隆一个远程仓库到本地
3. git status:查看当前工作区和暂存区的状态
4. git add <file>:将文件添加到暂存区
5. git commit -m "message":提交暂存区的更改到本地仓库
6. git pull:从远程仓库拉取最新的更改并合并到当前分支
7. git push:将本地仓库的更改推送到远程仓库
8. git branch:列出所有分支
9. git checkout <branch>:切换到指定分支
10. git merge <branch>:将指定分支合并到当前分支

11. git cat-file -p <sha1>:查看指定对象的内容;git cat-file -t <sha1>:查看对象类型
12. git ls-files -s:查看索引中的文件信息

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐