git 基础

工作区:在本地磁盘上看到、正在编辑的文件,Git 不会自动记录这里的修改
暂存区:存放的是:下一次 commit 要包含的内容,git add 的文件会放入暂存区
本地仓库:git commit 后,文件会放入本地仓库

1
工作区  -- git add -->  暂存区  -- git commit -->  本地仓库

git 文件状态

Untracked:文件存在于工作区,Git 从未记录过它,不在暂存区,也不在仓库中
Tracked:文件 add 后,属于 Tracked
Unmodified:文件已被 Git 跟踪,工作区内容 == 最近一次 commit
Modified:文件已被跟踪,工作区内容 ≠ 暂存区
Staged:文件已放入暂存区

git stash

git stash 是 Git 中的一个命令,用来临时保存当前工作目录和暂存区的修改(即未提交的更改),以便可以切换到其他分支或执行其他操作,而不会丢失当前的工作进度。

main 分支上写了一些代码,还没提交,这时需要切到 dev 分支修一个 bug,但当前的修改又不能提交,这时可以:

1
2
3
git stash
git switch dev # 或 git checkout dev

修完 bug 之后回到原来的分支,并恢复之前的修改:

1
2
3
git switch main
git stash pop # 取出最近一次 stash 的内容

命令 作用
git stash 将当前修改保存到 stash 栈中,并恢复到干净状态
git stash list 查看所有的 stash 记录
git stash pop 弹出最近的 stash,并应用到当前工作区
git stash apply 应用某个 stash(不会删除 stash 记录)
git stash drop 删除某个 stash
git stash clear 清空所有 stash
  • stash 会保存:工作区和暂存区的修改。
  • 新增的未被 git add 的文件(即“未跟踪文件”),默认不会被 stash;要连这些一起保存可以加 u
1
2
git stash -u  # 包括 untracked 文件
git stash -a # 包括 untracked + 忽略文件

squash

把多个 Git 提交合并成一个提交(commit), 通过 git rebase 等操作实现的。
假设想把最近的 3 次提交合并成 1 次提交:

1
git rebase -i HEAD~3

这时 Git 会打开编辑器,显示如下内容:

1
2
3
pick 123abc First commit
pick 456def Second commit
pick 789ghi Third commit

把后面的改成 squashs

1
2
3
4
pick 123abc First commit
squash 456def Second commit
squash 789ghi Third commit

保存后,Git 会提示编辑新的提交信息(可以合并、重写)

原来是:

1
2
3
commit 789ghi Third commit
commit 456def Second commit
commit 123abc First commit

现在变成:

1
commit abcd999 Combine all three commits

git rebase

git rebase 就是把当前分支的提交,重新“搬到”另一个分支的最新状态之后。

操作 原理 提交历史 是否创建新 commit
merge 把两个分支合并在一起 产生“合并节点”,保留分支历史 创建一个合并 commit
rebase 把你的提交挪到目标分支之后 更线性,更干净 会重写历史(新 hash)

假设有如下分支结构:

1
2
3
4
A---B---C  (main)
\\
D---E---F (feature)

执行:

1
2
git checkout feature
git rebase main

会变成:

1
2
A---B---C---D'---E'---F'  (feature)

D E F 的内容没变,但它们的 hash 值变了, 所以是新提交(D’ E’ F’)

git fetch

git fetch 是 Git 中的一个命令,用来从远程仓库获取最新的提交记录,但不会自动合并到当前分支。

1
git fetch origin
  • 联系远程仓库 origin
  • 下载所有新的分支、标签、提交等
  • 把它们存储到本地的远程分支,如:origin/mainorigin/dev
  • 不会改变当前工作分支(比如 main)

假设本地在 main 分支上,远程的 origin/main 提前更新了两个提交:

1
2
远程:A---B---C---D  (origin/main)
本地:A---B---C (main)

执行:

1
git fetch origin

之后的本地 Git 仓库会知道远程有个新提交 D,但当前分支不会自动合并这个提交:

1
2
3
4
A---B---C  (main)
\\
D (origin/main)

git pull

实际上是两个操作的组合:

1
git fetch + git merge

git push

git push 中的 -u(或 --set-upstream)参数的作用是:

将当前本地分支与远程分支绑定(设置默认上游分支),以后可以直接用 git push 或 git pull,不需要每次都指定远程分支。

第一次创建并切换一个新分支:

1
git checkout -b feature/login

然后把这个分支推到远程:

1
git push -u origin feature/login

以后只需要:

1
2
git push   # 推送当前分支
git pull # 拉取当前分支

不用每次都写全命令。

amend

修改提交信息:

1
git commit --amend

忘了加文件?补上再 amend:

1
2
git add filename  # 把漏加的文件加入暂存区
git commit --amend

这样不会生成一个新提交,而是把上一次提交修改为包含新的内容。

1
git commit -m "Fix typo"

后来发现还有一个文件漏加了:

1
2
git add forgot.js
git commit --amend

现在 Fix typo 这个提交会变成包含两个文件的提交。

git commit --amend 会 重写提交历史,也就是修改了原来的 commit(commit hash 会改变)

git cherry-pick

假设在 feature-a 分支修了一个 bug,但现在也希望 main 分支也拥有这个修复,不想合并整个 feature-a,只想拿那一个修复 commit → 用 cherry-pick

feature 分支有一个提交 abc123
切回 main 分支:

1
2
git checkout main
git cherry-pick abc123

main 分支现在就拥有了 abc123 这个提交(内容一样,但 hash 不一样)

多个 commit:

1
git cherry-pick hash1 hash2 hash3

cherry-pick 会创建一个 全新的 commit,虽然内容一样,但 hash 会变