撤销最近的commit,但保留修改的文件
git reset --soft HEAD~1撤销最近的commit,不保留修改的文件
git reset --hard HEAD~1撤销后找回commit(git reset --hard HEAD~1后)
git reflog找回提交的hash,再git reset --hard
放弃未暂存的更改
# 单个文件
git restore <文件名>
# 全部
git restore .恢复放弃的未暂存更改
也可以试试ctrl+z
把新修改与上一次commit合并
不要用在已经push的commit上,不然下次push就要-f了
git commit --amend让已clone的文件夹成为submodule
先git rm --cached再git submodule add <repo> <local path>
创建和使用patch
git diff/git apply:- 用途:主要用于分享**尚未提交(uncommitted)**的更改。
- 特点:生成的 patch 文件只包含代码的差异(diff),不包含 commit 信息(如作者、提交信息等)。
git apply在应用补丁时,只是将更改放入你的工作目录或暂存区,不会自动创建 commit。
git diff > my_changes.patch # unstaged
git diff --cached > staged_changes.patch # staged
git diff HEAD > all_uncommitted_changes.patch # unstaged & staged
git diff <commit_id_1> <commit_id_2> > changes_between_commits.patch
git apply --check my_changes.patch
git apply my_changes.patch
git add ...
git commit ...git format-patch/git am:- 用途:主要用于分享**已经提交(committed)**的更改。
- 特点:生成的 patch 文件是 .mbox 格式,包含了完整的 commit 信息(作者、日期、提交信息、代码差异等)。
git am在应用补丁时,会尝试自动创建新的 commit,保留原始的提交信息。这是 Linux 内核等项目常用的邮件协作流程。
git format-patch -1 # 最新一个
git format-patch -3 # 最新3个
git format-patch -1 <commit_id>
git format-patch main # main比当前分支多的所有commit
git am --show-stats 0001-some-change.patch
git am 0001-some-change.patch
git am patches/*.patchbranch
删除
git checkout main # 切走
git branch -d my-feature # 删除,如未合并,会被阻止
git branch -D my-feature # 强制删除
git push origin --delete my-feature # 删除远程分支push到指定远程分支
git push origin my-feature # 同名
git push origin local_name:remote_name # 不同名开发分支从主分支同步最新提交
# 1. 切换到 main 分支
git checkout main
# 2. 从远程仓库拉取最新的提交
git pull origin main
# 3. 切换回你的开发分支
git checkout develop
# ---
# 4. 将 main 分支的更改合并(merge)到 develop 分支
git merge main
# ---
# 4. 将 develop 分支变基(rebase)到 main 分支上
git rebase main把分支a的某个commit应用到分支b上
# 记得先确保已经切到了要修改的分支
git cherry-pick <commit-hash>
# 如有冲突,解决后:
git cherry-pick --continue
# 解决不了:
git cherry-pick --abort如何选择merge还是rebase
- git merge:保留最真实、最完整的历史。它忠实地记录了每一次分支的集成事件。
- git rebase:创造一个更整洁、更线性的历史。它让历史记录看起来像是所有开发工作都是串行完成的,非常易于阅读。
何时选择 git merge (默认行为)
当你希望保留分支的完整历史,并且重视每一次集成的上下文时,应该使用 merge。
黄金法则:永远不要 rebase 已经推送到远程并且被多人使用的公共分支 (如 main, develop, release 分支)。
适用场景:
- 合并到公共分支: 当一个功能分支 (feature) 开发完成,需要合并回 main 或 develop 分支时,通常会使用 merge。这会在主干上创建一个合并提交,清晰地记录了“功能 X 在这个时间点被集成进来了”,保留了整个功能分支的开发上下文。
- 团队协作: 当多个人在同一个长期存在的分支上协作时,使用 merge 来同步彼此的更改可以避免很多混乱。
优点:
- 可追溯性强: 历史是真实的,不会被修改。你可以清楚地看到每个分支的来龙去脉,以及它们在何时何地交汇。
- 安全简单: 不会改变已有的提交历史,对于 Git 新手来说更安全,不易出错。
- 保留上下文: 合并提交本身就是一个有意义的记录,它封装了一个完整的功能或修复。
缺点:
- 历史记录混杂: 如果功能分支非常多且频繁,主分支的历史图谱会变得非常复杂和混乱,充满了大量的合并提交,难以阅读。
- “无意义”的合并提交: 类似于 “Merge branch ‘a’ into ‘b’” 这样的提交信息可能会充斥在 git log 中。
何时选择 git rebase
当你希望在合并前“清理”你的本地提交,并为主分支提供一个干净、线性的提交历史时,应该使用 rebase。
适用场景:
- 同步个人分支: 你在一个私有的功能分支上开发,此时公共的 main 分支有了新的提交。为了在你的分支上获得这些最新更新,使用 rebase 是最佳选择。这会把你的分支上的所有提交在最新的 main 分支上“重放”一遍。
- 命令:git pull —rebase origin main
- 整理本地提交: 在将你的功能分支推送到远程准备 Code Review 之前,可以使用交互式 rebase (git rebase -i) 来整理你的提交。你可以:
- squash: 将多个零碎的提交(如 “fix typo”, “wip”)合并成一个有意义的提交。
- reword: 修改提交信息,使其更清晰。
- reorder: 调整提交的顺序。
优点:
- 历史记录清晰: 最终合并到主分支后,提交历史是一条直线,没有分叉和合并,非常容易阅读和理解。
- 没有冗余的合并提交: 避免了 merge 带来的额外合并节点。
- 易于定位问题: 由于历史是线性的,使用 git bisect 等工具追溯引入问题的提交会变得非常容易。
缺点:
- 破坏了历史的真实性: Rebase 的本质是重写历史,它会创建新的提交(即使内容一样,hash 值也变了)。
- 潜在风险: 如果在共享分支上使用,会给其他协作者带来巨大的麻烦。他们需要复杂的修复操作才能同步你的“新历史”。
- 冲突处理更复杂: 如果 rebase 过程中有多个提交都与基底分支冲突,你需要一个一个地解决这些提交的冲突,而 merge 只需要解决一次总的冲突。
推荐的团队协作工作流 (混合使用)
大多数现代开发团队会采用一种结合了两者优点的混合策略:
- 分支内用 rebase 保持整洁:
- 在自己的本地或私有功能分支上开发时,定期使用 git pull —rebase origin main 来同步主分支的最新代码。
- 在准备提交合并请求 (Pull Request) 之前,使用 git rebase -i 来清理自己的提交记录,确保每一个 commit 都是有意义的、独立的单元。
- 分支间用 merge 记录历史:
- 当功能分支准备就绪,提交 Pull Request。
- 经过代码审查和自动化测试后,通过代码托管平台(如 GitHub, GitLab)的 “Merge” 按钮将该功能分支合并到 main 或 develop 分支。
- 通常会选择创建一个合并提交 (—no-ff),即使可以 Fast-Forward。这样做的好处是,它会在主干上明确地记录“功能 X 的开发工作在这里被完整地并入”,同时保留了功能分支内部的清晰线性历史。