一、git status命令
- 作用:用于显示当前工作目录和暂存区的状态。
- 命令格式
git status
- 显示内容详解
On branch master
:当前所在的分支是master分支Untracked files
:尚【未跟踪】的文件,通常是新建的文件,此时可以使用git add file
将其添加到暂存区,同时跟踪。Changes not staged for commit
:已修改但未暂存的文件,就是工作目录修改了,但是没有放到暂存区中。- 如果准备提交,可以使用
git add file
将其加入暂存区,为后续提交做准备 - 如果要放弃修改,可以使用
git restore file
恢复到【上次提交的状态】
- 如果准备提交,可以使用
Changes to be committed
:已经在暂存区了,还没有提交。- 如果要提交文件,可以使用
git commit -m "提交文件"
- 如果要回到工作目录,可以使用
git restore --staged file
- 如果要提交文件,可以使用
nothing to commit, working tree clean
:与上次提交的记录一致。
二、git add命令
- 作用
- 添加新文件:将新创建的文件加入到Git的版本控制中
- 暂存文件修改:将已经修改的工作目录文件添加到暂存区
- 暂存删除的文件:工作目录某个文件已经删除,将这个【删除的文件】添加到暂存区
- 总的来说,就是把工作目录【修改的文件】添加到暂存区。此处的【修改的文件】可以理解为【新建的】、【修改过的】、【删除了的】
- 命令格式
git add file-path
- 可以添加单个文件
git add a.txt
- 可以添加多个文件
git add a.txt b.txt
- 可以添加当前目录所有文件
git add .
- 可以添加单个文件
- 常用选项
git add .
:将当前目录下的所有修改添加到暂存区- 等价于
git add -A
- 等价于
git add -p files
:交互方式选择部分文件添加到暂存区y
:暂存n
:不暂存q
:退出,后面的文件也不管了a
:暂存所有文件d
:跳过所有文件
三、git commit命令
-
作用
-
将暂存区的更改记录到本地仓库,形成一个新提交
-
创建版本快照:每个提交都会创建一个新的版本快照,同时会生成唯一的哈希值来标识此提交
-
-
命令格式
git commit -m "提交信息"
-m
:述这次提交的内容。
-
注意事项
- 使用
git commit
的前提是,该文件是【已跟踪】,如果是【未跟踪的】,不能被提交。 git commit -m "提交信息"
,后面的提交信息是必须的。git commit -m "提交信息" 文件
,后面的文件可选- 指定文件:将该文件提交
- 不指定文件:将暂存区的文件全部提交
- 使用
-
常用选项
-a
:将所有【已跟踪】文件提交,同时暂存的文件也会被提交
四、git log命令
-
基本语法
git commit -m "提交信息"
-m
:述这次提交的内容。
-
命令格式
git log
-
作用:查看提交历史记录。
-
显示内容:
commit a02200c4dc61b302003fcd71c8eb67a3acea83f7 (HEAD -> master) # 该提交的哈希值 Author: justloiter <ouyangxix@163.com> # 作者和邮箱 Date: Thu Jan 16 11:00:53 2025 +0800 # 提交的时间戳 发布了第一个版本 # 提交信息 commit fc2f8c3f68281f3ea7d5e22268209b4e34c87d67 Author: justloiter <ouyangxix@163.com> Date: Thu Jan 16 11:00:07 2025 +0800 创建了a.txt
-
常用选项
-
--oneline
:显示简短的哈希值和提交信息a02200c (HEAD -> master) 发布了第一个版本 fc2f8c3 创建了a.txt
-
--graph
:以图形方式显示分支和合并历史(注意最左边的线条)* commit a02200c4dc61b302003fcd71c8eb67a3acea83f7 (HEAD -> master) | Author: justloiter <ouyangxix@163.com> | Date: Thu Jan 16 11:00:53 2025 +0800 | | 发布了第一个版本 | * commit fc2f8c3f68281f3ea7d5e22268209b4e34c87d67 Author: justloiter <ouyangxix@163.com> Date: Thu Jan 16 11:00:07 2025 +0800 创建了a.txt
还可以结合
--oneline
使用。 -
--author=justloiter
,使用作者过滤 -
--since
以及--until
:使用时间过滤,例如git log --since="2025-01-01" --until="2025-02-01"
- 可以单独使用。
-
--stat
:显示每个提交的文件变更统计,包括修改的文件数,每个文件的增加和删除行数# git log --stat --oneline 87139a9 (HEAD -> master) 发布第二个版本 a.txt | 2 ++ # 代表增加了两行 1 file changed, 2 insertions(+) a02200c 发布了第一个版本 a.txt | 1 + # 代表增加了一行 1 file changed, 1 insertion(+) fc2f8c3 创建了a.txt a.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-)
-
--reverse
:倒序显示提交记录,即先显示最早提交的。
-
五、git reflog命令
-
作用:用于查看和操作Git的引用日志。
- 引用日志记录了HEAD、分支、标签等引用在不同时间点的变化。
- 即使已经被删除或无法通过普通方式访问的提交,也可以找回。
-
命令格式
git reflog [options] [ref]
ref
:需要查看的引用(例如HEAD或者某个分支)。缺省则是HEAD的options
:控制输出的选项
-
输出的内容
-
哈希值:每个提交的哈希值
-
索引(HEAD@{n}):历史的一个快照,n是该引用变动的相对时间(0表示当前的引用)。
-
操作类型:描述引用变动的操作。常见的操作:
commit
、checkout
、merge
、rebase
、reset
等 -
提交信息:提交时的注释信息
-
举例
$ git reflog master acf3dd5 (master) master@{0}: commit: 修复了第二个版本的bug b781f1a master@{1}: reset: moving to b781 b8a23d9 master@{2}: reset: moving to b8a2 b781f1a master@{3}: reset: moving to b781 b8a23d9 master@{4}: reset: moving to b8a2 b781f1a master@{5}: reset: moving to b781 b8a23d9 master@{6}: reset: moving to b8a2 b781f1a master@{7}: commit: 第二个版本 b8a23d9 master@{8}: commit: 第一个版本 9cc534f master@{9}: commit (initial): 创建了a.txt
-
-
-n number
:限制输出的数目
六、git reset命令
-
三种模式
-
--soft 版本号
:仅改变HEAD,不会修改【暂存区】和【工作目录】 -
--mixed 版本号
:改变HEAD和【暂存区】,不会修改【工作目录】 -
--hard 版本号
:全部改变 -
下面一个例子说明
-
初始化
初始有两个版本,目前在第二个版本
【工作区】和【暂存区】干净的 -
git rest --soft b8a2
到第一个版本去
可以看见,本地仓库已经只有两条了,也就是说,改变了HEAD
可以看见,暂存区还有内容,也就是说【工作区】和【暂存区】并没有被改变(内容还是版本二的) -
先回到版本二,然后再
git reset --mixed b8a2
到第一个版本去
可以看见,本地仓库只有两条提交记录,也就是说,改变了HEAD
可以看见,【暂存区】内容也没了,也就是说,改变了【暂存区】,但是【工作区】并没有被改变(内容还是版本二) -
先回到版本三,然后再
git reset --hard b8a2
到第一个版本去
可以看见,本地仓库只有两条记录,也就是说,改变了HEAD
可以看见,【工作区】和【暂存区】都是干净的,也就是说,被更改了。
-
-
总结:三者都会改变
HEAD
,--soft
不会改变【工作区】和【暂存区】,--mixed
会改变【暂存区】,但是不会改变【工作区】,--hard
都会改变。 -
如果不小心用了
--hard
,还有办法找回原来的代码吗?有的,兄弟有的。- 使用
git reflog
,找到对应的版本
- 再使用
git reset --hard b781
即可
- 使用
-
七、git branch命令
- 作用:分支相关的命令
git branch
:- 列出所有本地分支
git branch -r
- 列出远程分支
git branch -a
- 列出远程和本地分支
git branch -v
- 列出本地所有分支,并且显示每个分支的最新提交的简要信息
git branch -vv
- 上面的扩展,还会显示每个分支与远程跟踪分支的关联情况。
git branch 新分支名
- 创建一个新分支,指向HEAD
git branch -f 分支1 分支2
- 将分支1指向分支2,注意,该操作不会改变HEAD
git branch -d 分支名
- 删除分支
git branch -m 旧分支 新分支名
- 重命名分支
git branch -u 远程分支 【本地分支】
- 设置本地分支跟踪远程分支,如果此时已经在【本地分支】,那么可以省略
八、git checkout命令
git checkout 分支名
- 切换分支。(使HEAD指向该分支)
git checkout -b 新分支名 【版本号】
- 创建并切换到新分支。如果不指定版本号,那么就是HEAD
git checkout -b 本地分支 远程分支
- 创建一个本地分支,跟踪远程分支(这个分支是保存在本地的)。(同时HEAD会指向这个本地分支)
九、git merge命令
-
作用:合并不同的提交历史。
-
命令格式:
git merge branch
- 将当前分支与branch分支合并
-
合并的类型
-
快进合并(Fast-Forward Merge):如果当前分支是目标分支的祖先分支,那么当前分支会直接移动到目标分支,这种合并不会生成新的合并提交。
例子:
使用
git merge bug
可以看见,只是移动了分支,并没有新的提交生成。 -
三方合并(Three-Way Merge):当前分支不是目标分支的直接祖先。
例子
执行git merge dev
-
-
合并时发生冲突的解决
查看文件状态:
可以看见冲突了
手动解决冲突(这里直接使用vim打开文件)- 不能直接commit,要先
git add a.txt
,最后git commit -m "解决了冲突"
-
--no-ff
:禁止快进合并-
如果是快进合并,此时会强制生成合并,生成新纪录。看下面例子
main是dev的祖先,如果直接执行git merge dev
,那么main移动到dev就结束了,如果使用--no-ff
-
-
--only-ff
:仅允许快进合并
十、git rebase命令
- 作用:将一个分支的更改移动到另一个分支的操作。作用和
git merge
类似,但是结果不同,直接看例子
目前在bugFix
分支,执行git rebase main
注意C3并没有消失。其实就是拷贝一份C3,然后再合并C2的结果,变成了C3’ - 命令格式:
git rebase 目标分支 [branch]
- 目标分支:通常是父分支,如上图的C2
- branch:如果缺省,则是当前分支。
- 操作步骤(举例说明)
此时在main
分支,使用git rebase dev
-
- 首先会找到
main
和dev
共同的祖先,也就是C2,然后main
分支会直接把后续的节点直接移动到目标节点上去。如图,会把C5 <- C6直接与C4合并了。可以认为,直接把main
分支换了一个爹。
- 首先会找到
- rebase中文翻译为【变基】,非常恰当,因为就是把【爹给换了】。
十一、git remote命令
- 作用:管理远程仓库,主要用于添加、删除、查看远程仓库信息
- 命令格式:
git remote [command] [arguments]
command
:具体的操作命令,如add
,remove
,show
等arguments
:根据命令需要提供的参数。
git remote
- 列出所有已配置的远程仓库(仅显示仓库名)。
-v
:显示所有远程昂库的URL地址
git remote add name url
- 添加一个远程仓库,name是仓库的别名,通常是origin;url是仓库的地址
git remote remove name
- 删除指定仓库
git remote rename 旧仓库名 新仓库名
- 重命名远程仓库
git remote show name
- 显示指定仓库的详细信息
git remote set-url name 新url
- 修改指定仓库的url
十二、git fetch命令
- 作用:从远程仓库获取更新
- 基本语法:
git fetch [remote] [branch]
remote
:远程仓库branch
:远程分支,如果缺省,会更新所有远程分支
git fetch
到底更新了什么玩意?- 更新的就是本地的【远程跟踪分支】,也就是
origin/xxx
这种。
- 更新的就是本地的【远程跟踪分支】,也就是
git fetch 远程仓库
等价于git fetch
- 更新远程仓库的所有【远程跟踪分支】到本地
- 说人话!
- 假如本地有
origin/main
和origin/dev
,那么这些分支都会被更新
- 更新远程仓库的所有【远程跟踪分支】到本地
git fetch 远程仓库 远程分支
- 这个命令只会更新该【本地的远程跟踪分支】,即【origin/分支】。
- 说人话!
- 假设
git fetch origin dev
,那么只会从仓库拉取与origin/dev
相关的提交,同时,更新origin/dev
- 其他的分支不会管
- 这个命令只会更新该【本地的远程跟踪分支】,即【origin/分支】。
git fetch 远程仓库 远程分支:本地分支
- 指定远程分支,来更新指定的【本地分支】和【本地远程分支】(这里要和普通的git fetch做对比,普通的git fetch只会更新
origin/xx
)。 - 例如
git fetch origin dev:bug
:用远程仓库的dev分支,更新origin/dev
和bug
(后者是本地分支) - 如果本地分支不存在,那么会先创建本地分支。
- 有两个限制
- 不能在bug分支上
- 远程dev和
origin/bug
分支要同步
本地bug在C5,远程dev在C4。
- 这个命令一般不会使用的!!!!
- 指定远程分支,来更新指定的【本地分支】和【本地远程分支】(这里要和普通的git fetch做对比,普通的git fetch只会更新
git fetch 远程仓库 :本地分支
:fetch空到本地,会在本地创建一个新分支。- 执行
git fetch origin :bar
- 执行
十三、git pull命令
- 作用:从远程仓库获取最新的更改,并将这些更改合并到当前的本地分支
- 默认是
git fetch
+git merge
组合 - 注意,这里的
git merge
是当前的HEAD来合并的。看下面的例子
当前,HEAD在bar
分支,执行git pull origin main
- 上面这条命令可以分解成
git fetch origin main
,也就是更新本地的【远程跟踪分支】,即o/main
- 然后还执行
git merge o/main
,所以变成了下面这个,注意是bar
合并o/main
- 默认是
- 基本语法
git pull [remote] [branch]
remote
:远程仓库的名字。如果缺省,则默认从该分支配置的仓库拉取branch
:远程分支的名字,从该分支拉取更新,如果缺省了则会拉取当前分支【跟踪的远程分支】。(这里和git fetch做区别,git fetch缺省了会更新所有的分支)
--rebase
- 不使用
git merge
,而是git rebase
- 不使用
--no-rebase
- 不适用
rebase
- 不适用
--ff-only
- git仅执行快进合并,也就是没有冲突的合并
git pull 远程仓库 远程分支:本地分支
:拉取远程分支到本地分支,看下面例子
执行git pull origin main:foo
- 可以分解成
git fetch origin main:foo
,即从远程main分支,更新本地的foo分支,和本地的【远程跟踪分支】即o/main
因为本地没有foo分支,所以首先会新建foo分支,然后会更新foo
和o/main
- 然后还会执行
git merge foo
十三、git push命令
-
作用:将本地仓库的更改推送到远程仓库。将本地分支的commit上传到远程仓库,更新远程分支。
-
基本语法:
git push [remote] [branch]
remote
:远程仓库的名称,如果不指定,会推送到当前分支的远程仓库branch
:要推送的本地分支名称,如果不指定,则会推送当前分支- 如果本地分支,在远程仓库中不存在,那么会创建一个远程分支,本地分支跟踪这个远程分支。
-
git push
到底push了什么玩意儿?-
把本地的分支,推送到指定的仓库。
-
那我怎么知道远程仓库更新了什么东西?
-
远程仓库的【远程分支】,和本地分支【一模一样】了。
- 第一种情况:远程分支有的,本地分支都有,那么直接推送即可。
- 远程:1 -> 2 -> 3
- 本地:1 -> 2 -> 3 -> 4
- 推送后,远程:1 -> 2 -> 3 -> 4
- 第二种情况:远程分支有的,本地分支部分没有,此时要先拉取。
- 远程:
- 本地:
- 此时能够直接推吗?直接推,难道直接变成:C0 <- C1 <- C3 ? 那么C2怎么办?会丢失了。所以此时git会拒绝推送
- 所以一般是先
git pull
,即本地变成
然后再git push
- 为什么现在就可以推送了?因为C4是C3和C2的孩子节点,同时拥有C3和C2的代码,不会丢失。
- 第一种情况:远程分支有的,本地分支都有,那么直接推送即可。
-
一句话总结:把本地分支【拷贝】一份到远程分支。
-
远程分支要拷贝哪些内容?
-
本地分支有的内容,远程分支全部都要拷贝
本地分支main
跟踪远程分支o/main
,此时本地分支拥有哪些内容?- C4:C4又拥有C2和C3
- C3:拥有C1
- C2:拥有C1
- C1:拥有C0
下图是远程分支的内容:
可以看见远程分支没有C3和C4,所以,执行
git push
之后的结果是
可以看见,本地分支【拥有的东西】,远程分支也拥有了。
-
-
-
推送到指定的分支
git push 远程仓库 本地分支:远程分支
- 如果远程分支不存在,那么会新建这个远程分支(本地也会有【远程跟踪分支】,但是不会有这个本地分支)。
-
删除远程分支
git push 远程仓库 :远程分支
-
例子
本地分支只有main
,以及两个远程跟踪分支o/main
和o/foo
,远程仓库有两个分支,main
和foo
执行git push origin :foo
本地远程跟踪分支o/foo
和远程仓库分支foo
被删除了。
-
注:
- 文中可视化的项目地址:https://github.com/pcottle/learnGitBranching
- 可以直接网页打开使用:https://learngitbranching.js.org/?locale=zh_CN