阅读视图
Git指令及相关原理
Git 常见命令汇总
-
初始化
git init
: 初始化代码仓库,生成.git目录。在根目录下的 .gitconfig 中会包含配置信息,如用户email、用户名等 -
git config
git默认使用全局的用户和邮箱信息
# 设置 # 全局 git config --global user.name <user_name> git config --global user.email <user_email> # 当前 git config user.name <user_name> git config user.email <user_email> # 获取 # 全局 git config --global user.name git config --global user.email # 当前 git config user.name git config user.email # 解决每次提交和拉取代码都需要输入用户名和密码 git config --global credential.helper store
-
git add
将文件的更改添加到暂存区(Staging Area)。跟踪指定的文件,将其添加到 Git 的索引中。索引包含了将在下次提交中包含的文件列表。
-
Git 会计算文件的哈希值,生成文件的唯一标识符(SHA-1 值),更新Objects目录以及index文件。对于生成的objects中的文件可以查看详细信息。
git cat-file -t <filename> # 文件类型 git cat-file -s <filename> # 内容字节大小 git cat-file -p <filename> # 文件内容
-
新建一个同内容不同名的文件,可以发现.git下的文件目录没有变化,说明objects中保存的是文件类型和文件内容,不包括文件名。
echo "hello git">hello.txt
-
Git 用到的hash算法是SHA1算法
echo "hello git" | shasum # d6a96ae3b442218a91512b9e1c57b9578b487a0b
-
Git的计算方式
echo "blob 10\0hello git" | shasum # 这里就是objects中使用的hash算法
-
-
git commit
在objects目录下生成一个commit类型的对象,一个tree类型的对象。
-
树对象(Tree Object):树对象代表了该次提交时整个项目目录结构的快照。它记录了当前提交状态下的文件和目录的组织结构。树对象保存了每个文件或子目录的名称、类型(blob 或 tree)和相应的哈希值。
- 文件(blob):文件类型的树对象表示一个具体的文件,它包含文件名、文件模式(如文件权限)和文件内容的哈希值。文件内容的哈希值在 Git 中以 blob 对象的形式进行存储。
- 目录(tree):目录类型的树对象表示一个子目录,它包含子目录或文件的引用。每个子目录都对应一个树对象,而文件对应的是 blob 对象。
-
提交对象(Commit Object):提交对象代表了一次具体的提交操作。它包含了该次提交的作者、提交时间戳、父提交引用和提交消息等信息。
-
作者和时间戳:提交对象记录了该次提交的作者信息和提交时间。
-
父提交引用:除了第一次提交外,每个提交对象都包含一个或多个父提交的引用。这些引用指向了该次提交的直接前导提交。这样形成了提交历史的有向无环图(DAG)结构,记录了提交的衍生关系。
-
提交消息:提交对象包含了提交时输入的提交消息,用于描述该次提交的目的和变更内容。
-
-
-
git 分支管理
-
HEAD是一个特殊指针,始终指向当前分支的最新commit
-
分支操作
git branch //列出当前所有本地分支 git branch -r // 查看所有的远程分支 git branch -a // 查看所有分支 git branch <branch_name> // 创建分支,如过已经存在,报错 git branch -D <branch_name> // 删除分支,不能删除当前分支 git checkout <branch_name> // 切换分支,也可以直接checkout到特定的commit(detached Head) git branch -m <new-branch-name> // 分支重命名 git branch -v // 查看每个分支的最后一次提交信息 git branch -vv // 用于查看分支列表及其对应的追踪信息 git switch <branch> // 切换到已存在的分支 git switch -c <new-branch> // 创建并切换到新的分支,等价于branch + switch git switch -f <branch> // 强制切换分支(丢弃当前更改) git switch <commit/branch/tag> --detach // 切换到某个提交或标签:
假设删除了一个分支,可以通过detached Head去找回删除分支的commit
```
git checkout <commit_code>
git checkout -b <branch_name>
```
-
git diff
比较文件或提交之间差异,例如当前工作目录和最新提交之间的差异,或者两个提交之间的差异。
# 比较工作目录和最新的commit的差异 git diff # 比较指定文件在工作目录和最新提交之间的差异 git diff <file-name> # 比较两个提交之间的差异: git diff <commit1> <commit2> # 比较指定分支和当前分支之间的差异 git diff <branch-name> # 比较指定提交和当前工作目录之间的差异 git diff <commit> # 比较指定提交和当前工作目录之间的差异(包括未暂存的更改 git diff <branch1> <branch2> # 比较两个分支之间的差异 git diff <commit> -- <file-name> # 对比索引区和代码 仓库 git diff —cached
-
仓库管理
-
新建一个仓库
-
将远程仓库添加本地:
-
git remote add origin url
-
查看配置:
cat .git/config
,发现出现了remote origin相关信息 -
上传到远程仓库:
git push -u origin master
-
查看.git文件目录:出现了origin目录。
-
此时,查看heads和origin远程master和head对应的版本号一致。
cat ..git\refs\remotes\origin\master
cat ..git\refs\heads\master
-
-
-
压缩、解压
每次修改都会生成新的bolb对象,通过压缩可以提高传输效率。将object中的文件压缩,全部放入pack中
git gc // 压缩 git verify-pack -v ..git\objects\pack\pack-c992458971df8d681e44771882f19e49298fc75b.idx // 查看具体的压缩情况 git unpack-objects < .git/<pack_url> // 解压缩
-
垃圾回收
当我们将同一个文件的多次修改,分别add到索引区时,会产生3个对应的blob,在进行commit,会生成对应的tree和commit文件,但是前两次修改产生的blob就是垃圾对象。git gc会将一个blob、一个commit、一个tree压缩,但是多余的blob不会删除
git prune // 删除多余的对象
分支删除之后遗留的垃圾对象
git -c gc.reflogExpire=0 -c gc.reflogExpireUnreachable=0 \ -c gc.rerereresolved=0 -c gc.rerereunresolved=0 \ -c gc.pruneExpire=npw gc "$@"
-
git merge
-
fast forward:如果目标分支在源分支的前进历史中没有额外的提交,那么 Git 将执行快进合并。这种合并模式可以产生一个干净的、线性的提交历史。切换回master分支进行merge,生成一个ORIG_HEAD可以用于回滚。
git merge bugfix
-
3 way merge
-
工作原理:3-way merge比较目标分支、源分支和共同祖先之间的差异,并尝试自动合并这些更改。
-
优势:3-way merge能够自动处理没有冲突的更改,因此对于大多数情况下的合并是简单且有效的。
-
适用场景:3-way merge适用于多人协作的项目,特别是当多个人在同一个分支上工作并推送更改时,以及当存在冲突时需要手动解决。
-
分叉的merge会导致parent出现两个,如果c3、c4 修改了同一个文件会出现冲突。
冲突,会保存三个不同版本的blob,冲突解决commit之后会只剩一个。
-
-
Rebase merge(合并衍合):
- 工作原理:Rebase merge将源分支的每个提交重新应用到目标分支上,形成一系列新的提交。它不会创建合并提交,而是重演源分支的提交历史,并将它们放在目标分支的顶部。
- 优势:Rebase merge可以产生一个干净、线性的提交历史,不会产生合并提交。它使得提交历史更易于理解、回溯和维护。
- 适用场景:Rebase merge适用于个人分支或仅自己使用的分支上的合并。
-
-
Rebase
要实现fast-forward,必须让新建分支用于master分支最新的commit。要实现这个功能需要使用
git rebase
rebase存在的问题:
- 一般不会在master分支上rebase
- master的commit(EC5BE)可能和dev分支上的commit在rebase的时候存在冲突。
- 如果之前dev分支已经上传到仓库,协作人员拉在本地进行修改,这时再进行rebase会影响他人工作。
-
Tag
使用带message的tag会生成一个新的类型为tag的objects,且删除tag之后会残留垃圾。同时在refs/tags中会生成新的文件指向commit对象。
如果只是生成一个tag,不会生成对应的object,只会在refs/tags中会生成新的文件指向commit对象
git tag <tag_name> // 执行commit的hash git tag -a <tag_name> -m <tag message> git tag -a <tag_name> <commit value> git tag // list git tag -d <tag_name> -M <tag message>
-
本地分支和远程分支
远程分支的信息被压缩在packed-refs中,不实时
// 克隆远程代码 git clone <remote_url> // 同步远程分支 git fetch // 远程仓库的名字和url git remote -v // 联网检查远程分支和本地分支的一个连接关系 git remote show origin
-
git fetch
检查远程仓库的代码情况,更新本地分支,对于远程分支删除的情况是不被处理的。
git fetch —prune
: 先剪枝再同步fetch结束可以通过merge让本地分支和远程分支保持一致
情况1:
git clone url // someone push new commit to github git fetch git merge origin/master
情况2:
git clone url // someone push new commit to github // local master have a new commit git fetch git merge origin/master
-
git pull
FETCH_HEAD记录执行git fetch/ git pull分支的对应远程分支的最新的commit
ORIG_HEAD 记录git pull之前对应的commit
git pull // 默认远程仓库获取并合并更改,origin git pull <remote> // 从指定远程仓库获取并合并更改 git pull --rebase //获取并在合并前进行变基(rebase)操作 git pull --no-commit // 获取并忽略远程仓库的更改
-
git push
git push // 没有分支关联的时候会报错 git push origin newBranch // 推送 git push -u origin newBranch // 推送且将远程分支和本地分支相连 git push origin -d branch_name // 删除远程分支
-
类似指令对比
-
git fetch & git pull
1、git pull:从远程获取最新版本并merge到本地,会自动合并或修改当前的工作。相当于git fetch与git merge两条命令
2、git fetch :会将数据拉取到本地仓库 ,它并不会自动合并或修改当前的工作
3、在实际使用中,git fetch更安全一些,因为在merge前,我们可以查看更新情况,然后再决定是否合并
-
git pull & git clone
1、git clone是将整个工程复制下来所以,不需要本地是仓库(没有.git文件夹),第一次拉取项目不需要使用git init 初始化本地仓库
2、git pull需要使用git init初始化本地仓库,
3、git clone 可以直接切换远程分支,git pull需要切换到当前分支
4、git clone 可以直接指定远程分支推送,git pull需要关联远程仓库(git add origin 远程仓库地址)才能push
-
git switch & git checkout
-
git switch
命令提供更直观和一致的分支切换语义。它的主要用法是git switch <branch>
,用于切换到已存在的分支或创建并切换到新的分支(使用-c
参数)。git checkout
命令在早期版本的 Git 中,它的语义较为复杂且多样化。除了切换分支外,git checkout
还用于切换提交、还原文件等。 -
git switch
在切换分支时具有更高的安全性。如果存在未提交的更改或未保存的工作目录状态,git switch
会拒绝切换分支,以避免意外丢失或混淆更改。git checkout
在切换分支时较为宽松,它不会检查工作目录的状态,因此可能会在存在未提交的更改时执行切换,潜在地导致更改的丢失。
-
-
git log & git reflog
git log
用于查看提交历史记录,显示了当前分支上的所有提交。git reflog
用于查看引用日志,显示了当前仓库的引用(如分支、HEAD)的移动历史记录。git reflog
对于恢复误操作、找回丢失的提交等非常有用。 -
git rm & git reset & git restore & git revert
-
git rm
用于从 Git 仓库中删除文件或目录。git rm <file> // 删除已跟踪的文件,从 Git 仓库中删除指定的文件放入暂存区,下一次提交时从仓库中彻底删除。 git rm -r <directory> // 递归删除目录 git rm -f <file/directory> // 强制删除,即使它们处于修改或未跟踪状态 git rm --cached <file> // 删除 Git 仓库中跟踪的文件,但保留本地工作目录中的文件 git rm <pattern> // 使用通配符或正则表达式指定一个模式来删除多个文件
-
git reset
用于重置 Git 仓库状态的命令,它可以在不同的模式下修改 HEAD 和分支指针,以及更改索引和工作目录的内容。git reset [--hard] <commit> // 将当前分支的 HEAD、索引和工作目录全部重置为指定的 <commit>。丢弃当前的所有更改,慎用。 git reset [--mixed] <commit> // 将当前分支的索引重置为指定的 <commit>,但保留工作目录中的更改。这意味着您可以重新提交工作目录的更改。 git reset [--soft] <commit> // 将当前分支的 HEAD 指针移动到指定的 <commit>,但不更改索引和工作目录。这样可以重新提交以前的提交或将它们合并为单个提交。 git reset [--hard] HEAD@{<n>} // 使用引用(reflog)中的记录恢复到以前的提交状态
-
git restore
用于还原文件的修改,恢复到先前的状态。它可以撤销工作目录和索引中的更改,或恢复已删除的文件。git restore <file> // 恢复工作目录到最近一次提交的状态,丢弃在工作目录中的任何修改。 git restore --staged <file> // 将指定的文件恢复到最近一次提交的状态,同时将其从索引中移除。这可以撤销 git add 操作。 git restore --source=<commit> <file> // 恢复已删除的文件 git restore . // 使用 . 可以批量恢复整个工作目录中的所有文件,将它们恢复到最近一次提交的状态。
-
git revert <commit>
:使用指定的提交哈希<commit>
来创建一个新的提交,该提交撤销了指定提交的更改。这种回滚方法会保留原始提交历史,并在回滚提交后生成一个新的提交来撤销先前的更改。
-