- 这是基于上篇Git项目常用命令的一些额外补充,如有错误,请大家指针
一、前备知识
1.1分布式vs集中式
1.1.1集中式版本控制系统(CVCS)
-
核心代码存放:所有代码和历史记录都存储在一个中央服务器上,开发者在本地只保存当前所需的部分代码副本。
-
优点
:
- 权限控制:集中式系统可以更好地管理和控制代码的访问权限,避免未经授权的更改。
- 简单的操作:初学者容易理解,因为它符合“一个中心”的概念。
-
缺点
:
- 单点故障:如果中央服务器出现问题,所有人都无法提交或获取代码,影响工作进度。
- 网络依赖:每次提交、更新代码都需要与中央服务器通信,网络状况直接影响工作效率。
1.1.2分布式版本控制系统(DVCS)
-
核心代码存放:每个开发者的本地仓库都是完整的代码库副本,包括项目的完整历史记录。以 Git 为代表的 DVCS 没有单一的中央服务器。
-
优点
:
- 无单点故障:每个开发者都有完整的代码库,即使某台机器故障,也不会丢失项目历史。
- 更快的操作:大多数操作(如提交、查看历史等)在本地进行,无需网络,速度更快。
- 灵活的协作方式:可以轻松创建分支和合并,更适合团队协作。
-
缺点
:
- 分布式管理:如果没有一个主仓库作为参考,项目的合并和版本管理会变得复杂。
1.2暂存区vs工作区vs栈区
-
图解(暂存区、工作区)
-
git stash
将修改放入栈区 -
git管理的是修改,而不是文件
- 通过
git add
将修改放入暂存区,commit将修改提交- 快照机制:每次提交(
commit
)时,Git 实际上会为项目中的所有文件创建一个快照,但它不会为没有变化的文件重新存储内容。Git 只会为发生更改的文件创建新的快照,并将指针指向这些修改。这样,Git 可以通过这些快照追踪整个项目的历史变化。
- 快照机制:每次提交(
- 文件 vs. 修改:虽然 Git 存储了文件的快照,但它更关注的是这些文件在各个时间点的状态以及状态之间的变化。也就是说,Git 的工作方式是通过记录一系列的修改来追溯项目的历史,而不是对每个文件逐个进行管理。
- 通过
1.3推送分支
- 分支各作用
master
分支是主分支,因此要时刻与远程同步;dev
分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;- bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;
- feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。
三、进阶(扩充)
3.1时光机穿梭
3.1.1版本回退
-
参看版本提交日志
git log
-
回退到上一个版本 <
--hard
会将HEAD
指针移动到上个版本的已提交状态,暂存区、工作区全部回退,而--soft
只移动HEAD
指针到指定的提交位置,保留更改在暂存区和工作区中,--mixed
(默认)移动HEAD
指针到指定的提交位置,并将更改从暂存区移到工作区。不删除工作区的更改。git reset --hard HEAD^
-
回退到指定版本
git reset --hard <id>
-
查看git
HEAD
的变动日志:记录的是对HEAD
的移动、分支的切换、提交(commit
)、合并(merge
)、重置(reset
)等操作的历史。git reflog
3.1.2管理修改
-
查看工作区和版本库最新版本的区别,常用分支名填入HEAD指针
git diff <分支名> -- <文件名>
3.1.3撤销修改
-
不会被日志记录
git checkout -- <file>
-
命令:
git checkout -- <file>
-
作用:撤销工作区的修改,将文件恢复到当前分支的最新提交(
HEAD
)的状态。 -
具体操作
- 工作区修改:如果你在工作区对文件进行了修改,并且这些修改还没有被暂存(即没有执行
git add
),git checkout -- <file>
将丢弃这些未暂存的修改。
- 工作区修改:如果你在工作区对文件进行了修改,并且这些修改还没有被暂存(即没有执行
-
恢复到
HEAD
状态:这个命令通过将工作区的文件内容替换为HEAD
指向的最新提交的内容来实现撤销。这是基于 Git 提交时保存的快照来操作的。git reset <branch> <file>
-
命令:
git reset <branch> <file>
-
作用:撤回暂存区的内容,将文件从缓存区(暂存区)移除,但不影响工作区。
-
具体操作
- 缓存区(暂存区):如果你已经用
git add
将文件添加到缓存区,但决定不将这些修改提交,git reset <branch> <file>
将把这些文件从缓存区中移除,使它们不再被包含在下次提交中。
- 缓存区(暂存区):如果你已经用
-
恢复到
HEAD
状态:这个命令将文件从缓存区中移除,并恢复到HEAD
指向的最新提交的状态。虽然工作区的内容不会被改变,缓存区的内容会恢复到指定提交的状态。- 撤销方式与快照机制
-
撤销操作:
git checkout --
和git reset
都不涉及直接生成新的提交 ID,因此这些操作不会在提交日志中记录版本号。- 这些操作的实现依赖于 Git 的快照机制,而不是版本号。
-
快照机制:
-
快照:每次提交都会创建一个新的快照,记录文件在该提交时的完整状态。Git 使用这些快照来跟踪文件的历史状态。
-
恢复状态
git checkout --
:通过将工作区文件恢复到最新提交的快照来撤销修改。这个快照来源于HEAD
指向的提交。
-
git reset
:通过将缓存区恢复到指定提交的快照来撤销暂存区的内容。这个快照来源于你指定的<branch>
或提交 ID。
-
-
撤销实现:
- 快照使用:撤销操作依赖于 Git 之前的提交快照。即使缓存区和工作区没有直接的 ID,每次提交的快照都保存了文件的状态。通过恢复这些快照,Git 能够实现撤销操作。
-
git restore
-
--source <commit>
: 指定要恢复的提交。如果不指定,默认为最新的提交(HEAD)。--staged
: 仅恢复暂存区内容,保持工作区内容不变。--worktree
: 恢复工作区的内容,保持暂存区内容不变(通常默认已启用)。
-
-
总结
-
git resotre
是较新的语法git checkout -- <file>
:撤销工作区的修改,将文件恢复到最新提交的状态。git reset <branch> <file>
:撤回暂存区的内容,将文件从缓存区中移除,恢复到指定提交的状态。- 撤销机制:这些操作依赖于 Git 的快照机制,通过快照来恢复文件的状态,而不是通过版本号。每次提交都会保存文件的快照,通过这些快照,Git 实现了有效的撤销功能。
3.1.4删除修改
git rm <file>
- 与删除的
git add <file>
,后面也要git commit
- 与删除的
3.2远程仓库
3.2.1建立本地仓库和远程仓库链接
-
git remote add (origin)<远程仓库自定义名称> <远程仓库连接>
-
推送代码
git pull origin main
-
删除远程库
$ git remote -v origin [email protected]:michaelliao/learn-git.git (fetch) origin [email protected]:michaelliao/learn-git.git (push) $ git remote rm origin
-
clone代码
git clone <链接>
-
查看远程仓库名称,加上
-v
显示的信息更加全面git remote -v
-
推送分支
git push origin <分支名>
3.3分支管理
3.3.1创建并合并分支
-
创建分支并合并
git checkout -b <分支名> // == git branch <分支名> git checkout <分支名> // 查看所有分支 git branch // 删除分支 git branch -d <分支名> // 强行删除 git branch -D <分支名> // 合并分支,合并该分支到当前分支 git merge <分支名>
-
切换分支
-
git switch
git switch -c <分支名>
==git checkout -b <分支名>
git switch <分支名>
==git checkout <分支名>
-
-
过程(这是本地合并的过程)
- 主分支与分支创建:
- 一开始,主分支(例如
main
或master
)指向某个提交(也就是时间线上的一个点)。 - 当你创建一个新分支(例如
feature-branch
)时,这个子分支会从主分支的当前位置(提交)上分叉,创建一个新的分支指针。
- 工作区和 HEAD:
HEAD
代表当前活动的分支,它指向你当前工作的分支的最新提交。- 在分支创建时,
HEAD
指向的是你当前检出的分支,即main
分支。
- 在分支上进行更改:
- 你在子分支上进行更改并提交,子分支的指针会向前移动,记录这些更改。
- 合并分支:
- 当你准备将子分支的更改合并到主分支时,首先需要切换到主分支(例如使用
git checkout main
)。 - 使用
git merge feature-branch
命令将feature-branch
的更改合并到main
分支。 - 合并操作会将子分支的更改整合到主分支的历史中。这通常会创建一个新的合并提交,记录这两个分支的历史。
- 合并的结果:
- 合并后,主分支(例如
main
)的指针会移动到新的合并提交,这个提交包含了子分支的更改。 - 子分支仍然存在,可以选择继续开发或删除。
3.3.2分支管理策略
-
默认是开启
Fast forward
模式的,可以强制禁用,这样merge的时候会生成一个新的commitgit merge <分支名> --no-ff
$ git log
commit 05adea23be1fa9d08617659535f5ccc0136bfc3a (HEAD -> master)
Merge: 2b7011b 0db3fe7
Author: chenhao [email protected]
Date: Mon Sep 16 20:29:24 2024 +0800Merge branch 'test'
commit 0db3fe7d74f7de862e97ef185cf9c4a9f16ab7f0 (test)
Author: chenhao [email protected]
Date: Mon Sep 16 20:29:12 2024 +0800test2
commit 2b7011bafa741f4b63e3f8310741392a351b720f
Author: chenhao [email protected]
Date: Mon Sep 16 20:26:37 2024 +0800test
-
不使用就像这样,合并分支时,加上
--no-ff
参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward
合并就看不出来曾经做过合并。 -
在正式开发中,master是要求稳定的,我们可以通过dev子分支来做测试,再在下面添加子分支一步步实现
3.3.3分离头指针问题
- 当你使用
git checkout <commit-id>
切换到某个特定的提交(而不是分支的最新提交)时,你就进入了“分离头指针”(detached HEAD)状态。- 这个时候推荐先将修改的内容
git stash
,然后再切换分支,把最新代码拉到当时那个分支,避免冲突,但最好还是用完的分支及时删除。
- 这个时候推荐先将修改的内容
3.3.4bug分支
-
将修改隐藏
git stash
-
查看隐藏列表
git stash list
-
指定版本stash pop
git stash apply stash@{0}
-
git stash apply
与git stash pop
的区别:git stash apply stash@{0}
:应用指定的stash
,但不会删除stash
列表中的该项。git stash pop
:应用最近的stash
,并从stash
列表中删除该项。
-
如果一个分支的修改与另一个分支相同,可以使用
cherry-pick
命令将指定的提交应用到当前分支。首先,获取想要的提交的commit-id
,然后在需要修改的分支上输入:git cherry-pick <commit-id>
3.3.5多人合作冲突
-
总结
- 从本地推送分支,使用
git push origin branch-name
,如果推送失败,先用git pull
抓取远程的新提交; - 在本地创建和远程分支对应的分支,使用
git checkout -b branch-name origin/branch-name
,本地和远程分支的名称最好一致; - 建立本地分支和远程分支的关联,使用
git branch --set-upstream branch-name origin/branch-name
; - 从远程抓取分支,使用
git pull
,如果有冲突,要先处理冲突。
- 从本地推送分支,使用
-
git rebase
-
若自己提交代码前,别人提交了,如果
git pull
后再提交,会导致,git log
混乱,可以通过git rebase <分支名>
来将分支移动到另一个分支的最新提交 -
原先
A—B—C (main)
D—E—F (feature)git checkout feature git rebase main
-
现在
A—B—C (main)
D’—E’—F’ (feature) -
注意不要随意改动别人的分支哦
-