您现在的位置是:首页 >技术教程 >Git advanced高级操作网站首页技术教程
Git advanced高级操作
这篇文章是继Git概念介绍,常用命令与工作流程整理 配图_TranSad的博客-CSDN博客
之后的一些补充,学习总结一些额外Git操作中的比较常用的操作。所以这篇文章假设你已经有了前面的基础,我就直接说一些没有提到过的部分。
Detached HEAD
在Git中我们通常把HEAD当成是指向当前分支的指针,但是,如果你检出(checkout)了一个不是本地分支的引用(例如一个标签(tag)、一个远程分支、一个SHA-1 ID或者一个相对引用如main~3),那么你将进入一个特殊的状态,称为"分离头指针"状态。
在"分离头指针"状态下,HEAD不再指向一个分支,而是直接指向一个提交。我们可以把这个状态想象为一个"匿名分支"。在这个状态下,你可以正常地进行提交,但是这些提交不会更新任何的命名分支。这个状态对于在提交历史中跳转很有用。例如,你可以检出一个特定版本的代码,进行编译和安装,然后再切换回主分支。
如果你在"分离头指针"状态下做了提交,然后切换到其他分支(如main
),那么你在"分离头指针"状态下做的提交可能会丢失。如果你想要保存在"分离头指针"状态下做的提交,你应该在切换分支之前,创建一个新的分支来引用这些提交。你可以使用git checkout -b name
命令来创建并切换到一个新的分支。这个命令会创建一个新的分支,并把HEAD指针切换到这个新分支,这样你在"分离头指针"状态下做的提交就不会丢失了。
如果你直接checkout回main分支,那么记录就丢失了,因为在Git中,如果一个提交不被任何分支或标签引用,那么它就可能会在一段时间后被Git的垃圾回收机制删除。
Rebase
简短复习一下rebase的基础用法:如果你在你的feature分支上使用git rebase main,这个命令会将feature分支上的所有提交取出,然后在main分支的最新提交上重新应用这些提交。
而原来的提交如果不再被任何分支或标签引用,那么它们就可能会在一段时间后被Git的垃圾回收机制删除(也就是图中的odd commits部分)。因此,如果你需要保留原来的提交,你应该在rebase
之前,通过git checkout -b name命令
创建一个新的分支来引用(或者说是保存)这些提交,因为只要有分支引用它们,它们就不会被删除。
回到最初始的状态,如果你把命令用反了,即:git checkout main git rebase feature,那么你就会把main分支的commit放到你的feature分支后面:
此时如果你在进行多人开发,那么你的本地仓库就和远程仓库的main分支不同了,这就会带来一些列问题,因为别人的main分支还是在那,所以rebase千万别用错了顺序,其次多人开发还是用merge更合适。下面是一个使用merge的正常工作流程:
-
首先,在你的本地仓库中,从主分支(
main
)检出一个新的分支(例如,feature
),然后在这个新分支上进行开发。 -
当你的开发工作完成后,你可以切换回主分支,然后使用
git merge feature
命令将你的工作合并到主分支。这个操作会创建一个新的合并提交(merge commit)。 -
然后,你可以将主分支推送到远程仓库,然后在远程仓库中创建一个拉取请求(pull request)。这个拉取请求会显示你的更改,并请求项目的其他成员审查你的代码并将你的更改合并到项目的主分支。
现在我们再回到rebase,讲一下其他高级操作。
git rebase --interactive
git rebase --interactive是一个允许用户以交互方式对提交历史进行更为复杂操作的命令,包括压缩(squashing)、分割(splitting)、删除(deleting)提交等
首先来看一个简单的例子:当执行git rebase -i main
(在feature
分支上)时,Git会打开一个文本编辑器,列出所有将被移动(即应用到main
分支上)的提交,如:
pick 33d5b7a Message for commit #1
pick 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3
每个提交前的pick
表示该提交将被使用在此次rebase中。你可以通过调整这些行的顺序来改变提交的执行顺序。这些提交是从上到下执行的(在日志中看到的顺序是相反的),因此首先会将33d5b7a
添加到main
分支上。如果你在这里删除一行,那么相应的提交就会被丢弃。如果你删除了所有行,那么rebase操作将被中止。当你保存并关闭文件后,Git将根据你的指示执行rebase操作。
Squashing 压缩
"压缩"提交就是将一系列的提交合并成一个单独的提交。在合并功能分支到开发分支的时候,这可能非常有用。你可能有大量的小规模提交,当它们合并到开发分支时,可能会使其他开发者阅读起来感到困扰。下面是一个代码示例:假设第二个和第三个提交修复了第一个提交中的小问题,你可以使用squash
命令将它们合并成一个单独的提交。
pick 33d5b7a Message for commit #1
squash 9480b3d Message for commit #2
squash 5c67e61 Message for commit #3
当你选择squash
一个commit时,Git会将该commit的更改融入到前一个(即上面的)commit中。在你的示例中,9480b3d
和5c67e61
这两个commit的更改都会被合并到33d5b7a
这个commit中。这意味着所有这三个commit的更改都会被压缩到一个新的commit中。然后,Git会打开一个编辑器,让你为新的合并后的commit编写一个新的commit消息。这个消息通常默认为所有被压缩的commit消息的集合,但你可以根据需要进行编辑。最后,当你保存并关闭编辑器后,Git会完成rebase操作,你的commit历史中就会只有一个包含了所有更改的新commit,而原来的三个commit就不再存在了。
splitting 拆分
"拆分"提交就是将一个提交分解成多个单独的提交。如果一个提交中做了太多不同的更改,这可能会很有用。通过将那个提交拆分成一个或多个单独的提交,你的版本历史会变得更细粒度,更易于阅读和修改。以下是一个代码示例:假设你想将第二个提交分解成两个单独的提交,你可以使用edit
命令来拆分。
pick 33d5b7a Message for commit #1
edit 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3
Git将首先执行你的第一个提交(33d5b7a),然后应用第二个提交(9480b3d),这时发现第二个提交你使用了edit,git然后把你放在控制台中。在那里,你可以使用git reset HEAD~
对那个提交进行混合重置,这实际上撤销了那个提交,并将修改的文件未暂存。现在,你可以分别暂存和提交文件到几个提交中。比如:
$ git add file_for_first_commit
$ git commit -m ‘first commit message’
$ git add file_for_second_commit
$ git commit -m ‘second commit message’
$ git rebase --continue
当你处理完edit命令后,git会处理最后的那个提交(5c67e61)。
deleting 删除
"删除"提交就是在提交历史中去除一个提交。假设第三个提交有问题,你可以使用drop
命令删除它:
pick 33d5b7a Message for commit #1
pick 9480b3d Message for commit #2
drop 5c67e61 Message for commit #3
保存并关闭文件后,Git应用更改,并把你放回编辑器中以合并提交消息。保存后,Git将根据你的指示执行rebase。
需要注意的是,由于Git构建提交对象的方式,删除或更改一个提交将导致重写所有跟随其后的提交。你在仓库历史中回溯得越远,就需要重建越多的提交。如果在序列后面有很多依赖于你刚刚删除的提交的提交,这可能会导致大量的合并冲突。在上面的例子中我们就只删除了最后一个提交,所以就还好。
其他一些 git rebase --interactive commands:
- p, pick <commit> = use commit
- r, reword <commit> = use commit, but edit the commit message
- e, edit <commit> = use commit, but stop for amending
- s, squash <commit> = use commit, but meld into previous commit
- f, fixup <commit> = like "squash", but discard this commit's log message
- x, exec <command> = run command (the rest of the line) using shell
- b, break = stop here (continue rebase later with 'git rebase--continue')
- d, drop <commit> = remove commit
- l, label <label> = label current HEAD with a name
- t, reset <label> = reset HEAD to a label
- m, merge [-C <commit> | -c <commit>] <label> [# <oneline>] = Create a merge commit using the original merge commit's message (or the oneline, if no original merge commit was specified). Use -c <commit> to reword the commit message.
Git commit --amend
最后再补充个amend,amend关键字也很有用,大概就是如果你想更新你的最后一次提交,你可以使用git commit --amend -m “message for updated commit”
命令。这个命令会将你的修改加入到最后一次提交中,并更新提交信息。