什么是Git为什么要用Git等等这些相信看到该标题点进来的同学也不希望浪费时间再看一遍,那么直接进入主题,对于日常工作中常用的Git相关操作进行整理,一起看看吧
面试官:你常用的Git操作是什么?
候选人:git clone
面试官:还有吗?
候选人:没了,我是cv工程师
面试官:好好好这么玩是吧,你最好都能答上来
插播,更多文字总结·指南·实用工具·科技前沿动态第一时间更新在公粽号【啥都会一点的研究生
】
如何在Git中创建新分支
git branch <branch-name>
该命令会在当前的位置创建一个新分支,但并不会切换到这个新分支。如果希望切换到新创建的分支,可以使用以下命令
git checkout -b <branch-name>
或者,从Git 2.23版本后,可以使用以下命令来创建并切换到新分支
git switch -c <branch-name>
也正好可以回答“如何在Git中切换分支”
如何删除Git中的分支?
git branch -d <branch-name>
branch-name
是待删除的分支名称,该命令会删除本地分支,但如果分支有未合并的更改,Git会拒绝删除,并提醒先合并或解决冲突
如果确定要强制删除分支,包括未合并的更改,可以使用以下命令
git branch -D <branch-name>
注意,无法删除主分支、当前所在分支或非分支的内容
此外,如果想要删除远程仓库中的分支,可以使用
git push origin --delete <branch-name>
删除分支后commit会发生什么
本地分支
如果分支上的所有更改都已经合并到其他分支,那么分支的删除是安全的,本地分支上的commit历史将被移除
如果分支上有未合并的更改,删除分支时,Git 会阻止删除,并提醒先合并或处理这些更改。这是为了确保不会意外丢失未合并的工作
远程分支
远程分支的删除不会直接删除分支上的commit,而是在远程仓库中标记分支为已删除。这样其他协作者可以看到分支已被删除,但仍然可以在本地找到分支的commit历史,直到远程仓库执行了垃圾回收(garbage collection)来清理这些已删除的分支
什么是Git垃圾收集器?
Git 垃圾收集器(Garbage Collector)是一个负责清理不再被引用的 Git 对象的机制。在 Git 中,所有的数据都被存储为对象,包括提交(commits)、树对象(trees)、标签(tags)等。有时候,由于分支切换、分支删除等操作,一些对象可能变得不再可达,但仍然占用着存储空间
Git 垃圾收集器的作用是定期运行,查找那些不再被引用的对象,并将其从 Git 数据库中删除,以释放存储空间。垃圾收集器有助于保持 Git 仓库的健康状态,防止不必要的存储空间占用
在日常使用中,大部分情况下,开发者不需要手动触发 Git 垃圾收集器,因为 Git 会在执行一些操作时自动进行清理。然而,有时可能希望手动运行垃圾收集器,可以使用以下命令
git gc
git gc 和 git gc --auto 有什么区别?
git gc
手动触发 Git 垃圾收集器的方式。当运行git gc
时,Git 会执行一系列的清理操作,包括垃圾收集和优化存储等
git gc --auto
自动垃圾收集。在这种模式下,Git 会自动判断是否需要运行垃圾收集,如果需要则执行。通常Git 在一些操作(比如commit、merge等)之后会自动检查并执行垃圾收集,所以大部分时间不需要手动运行 git gc --auto
git gc --no-prune 的作用是什么?
git gc --no-prune
用于运行 Git 垃圾收集器但不执行实际的对象删除操作。在正常的 git gc
运行中,Git 会查找不再需要的对象,并将它们从存储中删除以释放磁盘空间。然而,使用 --no-prune
选项,Git 会执行垃圾收集的其他方面,但保留不再需要的对象而不进行删除
这个选项有时候可能会用于调试或特定的维护场景,允许查看垃圾收集器标记的对象,但不会真正删除它们。这样可以在不改变存储结构的情况下,查看 Git 认为哪些对象可以被清理
什么是 git merge?
git merge
是 Git 中用于合并不同分支的命令。将两个或多个分支的历史和更改集成到一个新的commit中的过程。合并操作通常用于将一个分支的变更合并到另一个分支,以确保这两个分支包含了相同的代码更改
git merge <branch-name>
将指定分支 中的更改合并到当前分支。在执行合并之前,通常需要确保当前分支是要合并的目标分支
git merge有哪些策略?
Fast-forward Merge
Fast-forward(快进)合并发生在当前分支上没有新的commit时,当试图将一个分支合并到另一个分支时,如果没有需要合并的新commit,Git 可以直接将目标分支指针移动到源分支的位置,而无需创建新的合并commit,举例说明更清晰
假设有两个分支,master
和 feature
,并且它们的commit历史如下
A---B---C master
\
D---E feature
在这个情况下,在 master
分支执行了 Fast-forward 合并
# 切换到 master 分支
git checkout master
# Fast-forward 合并 feature 分支
git merge feature
Git 将会简单地移动 master
指针到 feature
分支的最新commit E
上,形成一个直线式的commit历史
A---B---C
\
D---E master, feature
需要注意的是,Fast-forward 合并只能发生在当前分支没有新commit的情况下。如果有新的commit,Git 将执行普通的三方合并(three-way merge)来创建一个新的合并commit
Three-way Merge
Three-way merge(三方合并)通常用于解决分支之间存在冲突的情况。这种合并方式涉及三个版本的代码:两个分支的最新commit(共同祖先和当前分支的最新commit)以及它们的共同祖先,依旧搭配例子食用
任然还是master
和 feature
分支,commit历史如下
A---B---C master
\
D---E feature
执行合并命令
# 切换到 master 分支
git checkout master
# 合并 feature 分支
git merge feature
Three-way merge会干嘛?Git 会找到两个分支的最近共同祖先(commit B),以及它们各自的最新commit(commit C 和 commit E)
A---B---C
\ /
D---E master, feature
在这个情况下,Git 将会比较三个版本的代码(B、C、E),并尝试合并它们。如果没有冲突,Git 会自动创建一个新的合并commit,形成一个合并后的commit历史
A---B---C---F master
\ / /
D---E feature
如果合并过程中存在冲突,Git 将会标记,等待用户手动解决。用户解决冲突后,再执行 git merge --continue
来完成合并
讲完了两种merge策略,怎么人为指定哪个合并策略?
git merge --no-ff <branch-name>
--no-ff
选项用于强制创建一个新的合并commit,即使可以执行快速前进合并,这样可以保留每个分支的独立历史
要执行Fast-forward 合并,则
git merge --ff <branch-name>
合并提交(Merge Commit)和常规提交(Regular Commit)有什么区别?
Merge Commit
由 git merge
命令创建,用于合并分支,通常会产生一个新的合并节点,有两个或多个父commit
Regular Commit
由 git commit
命令创建,记录了在当前分支上的一次更改,产生一个普通节点,只有一个父commit
如何撤消 git commit ?
可以使用git reset
,git reset
命令允许将当前分支的 HEAD 指针移动到不同的位置,有三个主要的选项:--soft
、--mixed
和 --hard
,对应于不同的重置模式
git reset --soft
git reset --soft <commit>
回退 HEAD 指针到指定的commit,但保留所有的更改。即不会修改工作目录或暂存区,所有的更改都被标记为未commit的更改,可以直接重新commit
git reset --mixed
git reset --mixed <commit>
默认的reset模式。回退 HEAD 指针到指定的commit,并且重置暂存区,但保留工作目录中的更改。即未commit的更改会保留在工作目录,但不会被标记为暂存区的更改,需要重新add
并commit
git reset --hard
git reset --hard <commit>
最彻底的reset模式。回退 HEAD 指针到指定的commit,重置暂存区,并删除工作目录中未commit的更改,慎用这个玩意,因为它会永久性地删除未commit的更改
git reset 和 git revert 有什么区别?
git reset
用于将分支的 HEAD 指针和工作目录重置到指定的commit,可以选择是否保留未commit的更改,主要用于本地分支上的操作,慎用于已推送到远程仓库的分支,以免引起冲突
git reset --soft HEAD^ # 保留未commit的更改,将这些更改标记为暂存区的更改,不修改工作目录
git reset --mixed HEAD^ # 默认模式,将未commit的更改标记为未暂存区的更改,不修改工作目录
git reset --hard HEAD^ # 丢弃未commit的更改,重置暂存区和工作目录到指定的commit
git revert
创建新的commit,撤销指定commit及其之后的更改,而不修改commit历史,适用于已经推送到远程仓库的commit,避免修改历史引起问题
git revert HEAD # 撤销最后一次commit
git revert <commit-hash> # 撤销指定commit
Git 中的暂存是什么?
在Git中,“暂存”(Staging)指的是将工作目录中的修改或新文件添加到Git的索引中(也称为暂存区),以便随后commit这些更改。暂存的主要目的是允许选择性地commit文件而不是全部文件的修改,一般的步骤为
- 修改文件: 在工作目录中对文件进行修改
- 将修改添加到暂存区
git add 文件名
- commit到Git仓库
git commit -m "description"
通过使用暂存区,可以控制哪些修改被包含在下次commit中,从而更加灵活地管理项目的版本历史
git rebase 是什么?
git rebase
是 Git 中用于合并分支的一种方式,它与 git merge
类似,但有一些重要的区别
在使用 git rebase
时,通常会选择一个基础分支(base branch)和一个目标分支(target branch)。基础分支上的commit将被移至目标分支上,这个过程涉及到逐个应用commit,因此它会改写commit历史
git rebase <base-branch>
同样以例子来说明,假设我们有两个分支,main
和 feature
,它们的commit历史如下
A---B---C main
/
D---E---F---G feature
在这个例子中,我们希望将 feature
分支上的commit整合到 main
分支上
执行 git checkout main
切换到 main
分支,然后执行 git rebase feature
git checkout main
git rebase feature
这将会创建一个新的commit历史:
A'--B'--C' main
/
D---E---F---G feature
A’、B’ 和C’ 通过逐个应用 feature
分支上的commit得到,这是 git rebase
的基本工作方式
要注意的是,原来的E 、 F、G 并没有被修改,它们保留在历史中。新的commit A’、B’ 和 C’ 被创建,并在 main
分支上形成了一个更加线性的历史
没明白的可以参考
https://git-scm.com/docs/git-rebase
git merge
通过多次合并commit生成更全面、更易读的历史记录,如果两个分支有冲突,Git 会生成一个合并commit,需要手动解决冲突git rebase
通过更少的commit创建更清晰、线性的历史记录,在 rebase 过程中,如果有冲突,Git 会逐个应用commit并在每个冲突点停下,需要手动解决冲突,然后继续 rebase。该方式更容易引入风险,因为会修改commit历史,可能导致冲突或数据丢失。
git tag 作用?
git tag
用于给 Git 中的commit打上标签(tag),这些标签通常用于标识某个特殊的commit,比如软件版本发布。标签提供了一个稳定的引用,使得方便地回溯到某个特定的commit,常用的命令选项有
-a
:用于创建一个带注释的标签-m
:指定标签的注释信息-l
:列出已有的标签
git tag -a v1.0 -m "Version 1.0 release"
列出所有tag
git tag
列出匹配条件下的tag
git tag -l "v1.*"
git stash 是什么?
git stash
是一个用于保存当前工作目录和暂存区的临时状态的命令。允许在切换分支、应用补丁或执行其他操作之前,将当前的修改存储起来,以便稍后重新应用,非常非常实用,常见的使用场景如
- 保存当前工作目录和暂存区的状态
git stash save "Work in progress"
- 切换到其他分支进行操作
git checkout other-branch
- 在其他分支进行操作
# 在 other-branch 上进行一些操作
- 切回原始分支并恢复
stash
git checkout original-branch
git stash apply
或者,如果想同时删除 stash
,可以使用:
git stash pop
此外,还有一些其他常用命令
git stash list # 显示 Git 存储库中所有存储的列表,以及有关每个存储的一些信息
git stash branch <branch-name> # 将更改应用到不同的分支
git cherry-pick 有什么作用?
git cherry-pick
将指定的commit复制到当前分支,创建一个新的commit,但不会将整个分支合并过来。通常用于在不合并整个分支的情况下引入或应用特定的更改
git cherry-pick <commit-hash> # <commit-hash> 是要应用的commit的哈希值
git pull 和 git fetch 之间有什么区别?
git pull
和 git fetch
都是用于从远程仓库获取更新的 Git 命令,但区别为
git fetch
git fetch origin
- 从远程仓库获取更新的信息,但并不自动合并或更新本地工作目录, 只是把远程分支的引用和相关对象(commit、tree等)下载到本地,需要手动合并或者在需要的时候将远程分支的变更整合到本地分支上
git pull
git pull origin master
- 从远程仓库获取更新的信息,并尝试将本地工作目录自动合并到获取的更新中
git pull
实际上包含了git fetch
,比如在执行git fetch
之后,立即执行git merge
也可以将远程分支的更改合并到当前本地分支
面试官:好好好,你tm装13是吧
以上就是本期全部内容,整理总结不易,期待点赞在看,我是啥都生,下次再见