Author:onceday date:2023年3月5日
满满长路有人对你微笑过嘛…
windows安装可参考文章:git简易配置_onceday_CSDN博客
參考文档:
每个提交对象有一个完整的40字符的SHA-1散列值,但是指定时并不需要填写完整的40个字符。
Git 十分智能,你只需要提供 SHA-1 的前几个字符就可以获得对应的那次提交, 当然你提供的 SHA-1 字符数量 不得少于 4 个,并且没有歧义——也就是说, 当前对象数据库中没有其它对象以这段 SHA-1 开头。
首先使用git log
打印提交日志,然后确定目标提交的SHA-1值:
ubuntu->requests:$ git log
commit 1e62a3ec18e19f85ddae03b4cbbdf0b4c62834c0 (HEAD -> main, origin/main, origin/HEAD)
Merge: c57f1f0c 7908ad3a
Author: Ian Stapleton Cordasco
Date: Fri Oct 21 11:37:47 2022 -0500
Merge pull request #6262 from winmorre/docs-update/#6260
JSONDecodeError included in the documentation
......
然后使用git show来展示:
ubuntu->requests:$ git show 1e62a3
commit 1e62a3ec18e19f85ddae03b4cbbdf0b4c62834c0 (HEAD -> main, origin/main, origin/HEAD)
Merge: c57f1f0c 7908ad3a
Author: Ian Stapleton Cordasco
Date: Fri Oct 21 11:37:47 2022 -0500
Merge pull request #6262 from winmorre/docs-update/#6260
JSONDecodeError included in the documentation
一般来说,如Linux内核这样庞大的项目,也仅需前12个字符就可以分辨,因此并不用输入完整的SHA-1字符串。
使用git log --abbrev-commit --pretty=online
可以打印简单而唯一的值。
对于分支的顶端提交,直接使用git show
即可查看顶端的提交信息。
通过git rev-parse
可以看当前Git的分支所处的提交对象(提交对象)。
可以使用git reflog
来查看引用日志。 Git 会在后台保存一个引用日志(reflog), 引用日志记录了最近几个月你的 HEAD 和分支引用 所指向的历史。
ubuntu->requests:$ git reflog
1e62a3ec (HEAD -> main, origin/main, origin/HEAD) HEAD@{0}: clone: from gitee.com:onceday/requests.git
然后可以使用@{n}
来引用reflog中输出的提交记录。
git show HEAD@{5}
也可以使用同样的语法来查看某个分支在一定时间前的位置,例如查看master分支在昨天的时候指向了哪个提交:
git show master@{yesterday}
也可以使用git log -g
来查看类似git log输出格式的引用日志信息。
^
可以用来查看上一个提交,即祖先提交,下面命令即查看HEAD
的父提交:
git show HEAD^ #cmd上可能需要使用HEAD^或者"HEAD^"
对于合并的提交可以用指定数字来查看第二个父提交:
git show HEAD^2
另外一种方法是使用波浪号~
,同样指向第一父提交,因此HEAD~
和HEAD^
是等价的,区别是HEAD~2
代表第一父提交的第一父提交,即祖父提交。下面两种是等价的,^
和~
也可嵌套使用:
git show HEAD~~
git show HEAD~2
使用双点语法,可以让Git选择在一个分支中,而不在另一个分支中的提交:
git log master..experiment
上面语法的含义就是指出在experiment
分支中而不在master
分支中的提交。
除了双点语法外,还支持多点语法,如^branch
和--not branch
指明不希望提交被包含其中的分支。
如下面的命令是等价的:
git log refA..refB
git log ^refA refB
git log refB --not refA
在此基础上,就可以指定多点查询了:
git log refA refB ^refC
上面即查看被refA
或refB
包含,但不被refC
包含的提交。
还存在一个三点语法,可以选择处被两个引用之一包含但又不被两者同时包含的提交。
git log --left-right master...experiment
--left-right
会显示出每个提交到底处于那一侧的分支。
使用git add -i
选项可以进入交互式暂存。具体详情请参考文档progit2
。
使用git stash
可以暂存正在开发的文件,从而能切换到新的分支上。
stash
储存会清理工作目录的脏状态,跟踪文件的修改和暂存的改动,然后将未完成的修改保存到一个栈上,然后可以在任何时候重新应用这些改动,包括不同的分支。
使用下面命令即可:
git stash
,可以将当前工作内容暂存。git stash list
,查看储存的内容。git stash apply
,应用最新储存的内容。git stash apply stash@{2}
,指定应用目标存储,可以使用名字代替。git stash drop {stash_name}
,移除指定的存储目标。git stash pop {stash_name}
,应用储存并且从栈上丢弃。stash
可以储存未跟踪文件:
git stash -u
,该选项会存储任何未跟踪文件,但不会包含明确忽略的文件。git stash -a
,包含所有文件,包括明确忽略的文件。使用git stash branch
,可以指定的分支名创建一个新分 支,检出贮藏工作时所在的提交,重新在那应用工作,然后在应用成功后丢弃贮藏
清理工作目录有一些常见的原因,比如说为了移除由合并或外部工具生成的东西, 或是为了运行一个干净的构建而移除之前构建的残留。 需要谨慎地使用这个命令,因为它被设计为从工作目录中移除未被追踪的文件。 如果改变主意了,也不 一定能找回来那些文件的内容。 一个更安全的选项是运行git stash --all
来移除每一样东西并存放在栈 中。
常用命令如下:
git clean
,去除冗余文件,清理工作目录,只会移除没有忽略的未跟踪文件。git clean -f -d
,移除工作目录中所有未追踪的文件和空的子目录。git clean --dry-run
或git clean -n
,只是输出需要移除的文件,但不会去真的移除文件。git clean -x
,移除那些明确被忽略的文件。git clean -i
,支持交互式操作。需要查看一个仓库代码的某个函数的变更历史,就需要使用搜索工具。
如git grep
,会查找工作目录。它的速度非常快,不仅仅可以可以搜索工作目录,还可以搜索任意的 Git 树。
git log
支持的某些搜索向可以查找某个内容对象是什么时候存在和引入的。
git log -S XXXX_MAX --oneline
,查看XXXX_MAX
常量在哪个提交新增和删除的。git log -S -G "reg" --oneline
,支持正则表达式搜索。使用git log -L :git_deflate_bound:zlib.c
,可以查看zlib.c
文件中git_deflate_bound
函数的每一次变更。
修改最后一次提交的提交信息,可以使用:
git commit --amend
上面这条命令会将最后一次的提交信息载入到编辑器中供你修改。 当保存并关闭编辑器后,编辑器会将更新后的提交信息写入新提交中,它会成为新的最后一次提交。
如果需要修改最后一次提交的实际内容,可按以下步骤:
git commit --amend
,以新的改进暂存文件来提交旧友的最后一次提交。修改提交会改变提交的SHA-1
校验和,因此推送后的提交,就最好不要再修改了。
使用git commit --amend --no-edit
可以跳过提交信息的编辑步骤。
通过使用git rebase -i
工具可以修改历史提交信息,详情可参考progit2
书籍。
如,git rebase -i HEAD~3
,将想要修改的最近一次提交的父提交作为参数传递给git rebase -i
命令,其等价于下面形式:
git rebase -i HEAD~3..HEAD
即将从HEAD~3
到HEAD
范围内的四个提交的信息进行改写,注意,最好不要对已推送到服务器的提交进行修改。
git rebase
会从旧到新的顺序依次重演这四次提交过程,因此便可以乘机修改每一次提交。
当修改了目标提交信息后,需要依次输入git commit --amend
和git rebase --continue
,前者用于更新本次提交信息,后面用于自动应用剩下的提交。对于每一个需要修改的地方,Git都会停止,并且重复这个过程。
变基工具需要执行一个脚本,下面是脚本中有用信息的描述:
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase
--continue')
# d, drop = remove commit
# l, label
这里介绍一下reset
和checkout
的区别,首先是三个存储区域:
HEAD
,上一次提交的快照,下一次提交的父节点。Index
,预期的下一次提交的快照。Working Directory
,沙盒。HEAD
是当前分支引用的指针,它总是指向该分支的最后一次提交,这表示HEAD将是下一次提交的父节点。
Index
是预期的下一次提交,即Git的暂存区。使用git ls-files -s
即可查看暂存区文件信息。
工作目录是当前HEAD
指向内容和Index
内容,以及其他内容的融合体,是一个沙盒,在这里可以随意修改内容。
对于一个干净的工作区,首先在工作目录修改文件内容,然后使用git add
暂存工作目录的修改文件,最后使用git commit
提交暂存区的内容到git存储库中,此时HEAD的也会移向最新的提交。
对于切换分支或者克隆,当check out一个分支时,会修改HEAD指向新的分支引用,将索引填充为该次提交的快照,然后将索引的内容复制到工作目录中。
对于reset
操作,首先移动HEAD的指向,reset将当前分支的HEAD指针移到目标提交上。。
对于git reset --soft
,做完上面一步后,便停下了,并不会改变索引和工作目录。
此时如果更新索引的内容,然后再git commit,其效果和git commit --amend
的效果类似。
对于git reset --mixed
,会在--soft
基础上进一步更改Index
的内容,即撤销后续的提交,取消暂存所有的东西,但此时工作目录中还存在内容。
对于git reset --hard
,会在--mixed
基础上进一步的修改工作目录中的内容,一旦覆盖那些未提交过的内容,那么将无法找回这些数据,因此该命令相当有危险性。
可以给reset
指定文件路径,这样会跳过移动HEAD
分支的步骤,直接让索引(暂存区)的内容看起来像HEAD
分支。
git reset file.txt
git reset --mixed HEAD file.txt
也可以从指定提交中拉取数据git reset eeaabb file.txt
。下面展示了reset
和checkout
命令对树的影响:
对于下面的提交历史,V2版本的文件只完成了部分工作,想把这个部分去掉,可以如下操作。
先运行git reset --soft HEAD~2
,这个时候HEAD指针移到eb43bf8
上,然后索引区的内容保持不变(即38eb946提交的内容),然后再git commit
,这个时候就会把file-a.txt v3
和file-b.txt v1
都提交上去,如下:
这样,就跳过了原来9e5e6a4
提交了,且工作目录和索引区的内容保持不变,在提交历史中,V2的内容无法直接查询到了。
这是一个隐藏功能,能让Git记住解决一个块冲突的方法,这样在下一次看到相同冲突时,Git可以自动的解决它。
详情可以参阅progit2
。
能显示任何文件中每行最后一次修改的提交记录。 所以,如果在代码中看到一个有 bug 的方法,可以使用git blame
标注这个文件,查看哪一次提交引入了这行。
git blame -L 69,82 Makefile
上述命令确定Makefile中每一行分别来自哪个提交和提交者,使用-L
选项将标注的输出限制为该文件的第69行到第82行。
bisect 命令会对你的提交历史进行二分查找来帮助尽快找到是哪一个提交引入了问题。
一般步骤如下:
git bisect start
,启动二分查找。git bisect bad/good
,告诉Git当前所在提交是否有问题。git bisect bad/good
,告诉Git当前所在提交是否有问题。依次迭代上面步骤后,就可以最终定位问题所在了。
在确定问题之后,需要使用git bisect reset
重置HEAD指针到最开始的位置。
git bundle
命令会将git push
命令所传输的所有内容打包成一个二进制文件,然后可以通过物理手段传给其他人,并解包到其他仓库中。
ubuntu->cc:$ git bundle create repo.bundle HEAD master
Enumerating objects: 31, done.
Counting objects: 100% (31/31), done.
Compressing objects: 100% (27/27), done.
Total 31 (delta 6), reused 12 (delta 2), pack-reused 0
repo.bundle
中包含所有重建该仓库master分支所需的数据。
HEAD
添加了一个分支应用,这样在别处clone时可以直接从这个二进制文件中克隆出一个目录。
使用git clone repo.bundle repo
即可在他处重建该仓库。
可以只打包指定区间的提交内容:git bundle create commits.bundle master ^9a466c5
对于这样只打包了部分内容的包,需要先检查这个文件是否是一个合法的Git包,是否拥有共同的祖先来导入。如下:
git bundle verify ../commits.bundle
如果缺失必要的提交信息,那么就无法进行导入。
使用git bundle list-heads ../commits.bundle
可以列出这些包里可以导入的分支。
还可以使用git fetch
和git pull
从包里导入提交。
git fetch ../commits.bundle master:other-master
可以只打包指定区间的提交内容:git bundle create commits.bundle master ^9a466c5
对于这样只打包了部分内容的包,需要先检查这个文件是否是一个合法的Git包,是否拥有共同的祖先来导入。如下:
git bundle verify ../commits.bundle