git基础

基础知识

git如同一颗树,每个树上都可以有多个分支,每个分支上又可以有多个节点。

  1. 每个 commit 是你整个工作区所有文件的一个完整快照(snapshot), 但 Git 使用哈希指向机制让它看起来像是"增量"。

基础命令

git commit 提交一个新节点

git commit:在当前分支,创建一个新的提交记录(一个新的的节点)

git commit --amend

修改最近一次的commit提交,比如忘了添加文件,想要修改提交信息。不适合对已经push到远程仓库的分支执行这样的操作。

AI解释

git commit --amend 是 Git 中一个非常常用且强大的命令,它的作用是修改(或替换)当前分支的最后一次提交,而不是新建一个提交。简单来说:它让你"假装"上一次提交其实是你现在想要的样子,常用于"我刚刚提交错了,想赶紧改一下"这种场景。常见使用场景

  1. 忘记添加文件(最常见)

    git add forgotten-file.txt
    git commit --amend

这会把 forgotten-file.txt 加入到上一次提交中,而不是新建一个提交。

  1. 修改上一次的提交信息(比如写错了 typo、消息不清晰)

    复制代码
     git commit --amend

直接运行不加参数,会打开编辑器让你修改上一次的 commit message。或者一行改掉(推荐):

复制代码
    git commit --amend -m "新的正确的提交信息"
  1. 同时修改文件和提交信息

    复制代码
     git add modified-file.txt
     git commit --amend -m "修复 bug 并添加新功能"
  2. 只改文件,不改消息(消息保持不变)

    复制代码
     git add new-file.txt
     git commit --amend --no-edit

--no-edit 表示不修改提交消息,直接沿用上一次的。

它到底是怎么工作的?

  • Git 会把上一次的 commit 内容 + 你现在的暂存区(staging area)内容 + 新的 commit message,重新生成一个全新的 commit 对象。
  • 这个新 commit 会完全替换原来的最后一次提交(旧 commit 会被"抛弃",但还没推送的话还能找回来)。
  • HEAD 和分支指针会指向这个新的 commit。

重要注意事项(非常重要!)

  1. 已经推送到远程仓库的提交,尽量不要 amend!

    复制代码
     git commit --amend   # 改完后
     git push --force     # 或 git push --force-with-lease

因为你改了历史,其他合作者会出问题(他们的历史和你对不上)。 → 小项目、个人项目、还没别人 pull 的话,可以随便 amend + force push。 → 团队协作中,已推送的提交尽量用 git revert 或新建 commit 修复。

  1. 如果只是想加文件但不想改消息,最佳实践:

    复制代码
    git add .
    git commit --amend --no-edit
  2. amend 后查看效果

    复制代码
     git log -1          # 看最新的提交
     git reflog          # 可以看到旧的 commit 还在(万一想找回来)

总结一览表

需求 命令
漏加了文件 git add 文件 → git commit --amend --no-edit
提交消息写错了 git commit --amend -m "新消息"
既加文件又改消息 git add 文件 → git commit --amend -m "新消息"
只改消息,不碰文件 git commit --amend(会打开编辑器)
已 push 过又 amend 了 git push --force-with-lease(谨慎!)

一句话总结:

git commit --amend 就是"后悔药",让你能优雅地修改"刚刚那次提交",保持历史整洁。用得越多,你的历史就越干净,越专业!

git branch 节点

  1. git branch <分支名> :在当前分支,创建一个新分支。
    • git branch -f <要移动的分支名> <想将分支名移动到的位置HEAD~x>:将你指定的分支强行移到,HEAD上面的x个节点
      • -f(-force)强制
  2. git branch -u <远程分支> [本地分支]
  • -u--set-upstream-to 的简写,git branch --set-upstream-to=<远程分支> [本地分支]
  • <远程分支> :要设置的远程分支(如 origin/main
  • [本地分支]:可选参数,指定要设置的本地分支。如果省略,则使用当前分支
  • 选择本地分支要跟踪的远程分支

git checkout 移动头部

  1. git checkout <分支名>: 切换到相应分支/节点
    • git checkout <commit提交的哈希值> :切换到对应的提交节点上。

    • git checkout <HEAD^> :指向HEAD的上一个节点,有几个^,就先向上指多少节点。

    • git checkout <HEAD~x> :指向HEAD上面的x个节点

    • git checkout -b <新分支名> :是一个组合命令,它创建 一个新的分支,并立即切换 到这个新分支上。

      • -b 选项代表"create a new branch",即创建新分支。
      • git checkout 部分负责切换分支。
        这个命令相当于连续执行以下两个命令:
      1. git branch <新分支名> (创建分支)
      2. git checkout <新分支名> (切换分支)
    • git checkout -b <本地分支名> <远程仓库>/<远程分支名>

      • 创建一个本地分支
      • 选择其基于哪个远程分支创建
      • 将本地分支跟踪你指定的远程分支
      • 切换到该远程分支

git merge 合并

git merge <分支名>: 创建一个新的节点,将你指定的分支合并到当前分支。注意该节点有两个父节点。

下图中的C4是main分支合并bugFix分支后的新节点,属于main分支

git rebase 拜义父

  1. git rebase <分支名>:放弃自己原先的分支,将自己嫁接到其他分支,生成一个新的节点。我愿称为认贼作父。

    1. git rebase <基分支> <目标分支>
      • 基分支 :作为新的基准
      • 目标分支 :要被变基的分支
        bug修复分支原本是在C2分支上。
  2. git rebase -i <base_commit>:给你一个编辑器,让你决定自base_commit后的那些提交,有哪些提交,要按哪些顺序复制到当前分支下(从base_commit开始,重新排列,选择节点顺序。并将原先的分支,移到当前)。

git reset 斩断过去,新的开始

git reset <commmit的哈希值>:回到历史的某个节点,在那个历史节点后面commit都将被遗忘。

  • 三种参数:
命令 作用
git reset --soft <commit> 仅移动 HEAD,保留暂存区和工作区
git reset --mixed <commit>(默认) 移动 HEAD + 清空暂存区,工作区不变
git reset --hard <commit> 历史 + 暂存区 + 工作区都回到指定版本(危险)
  • 禁忌:
    • 团队开发中一般禁用reset
    • rest后再push上去,会破坏git历史。
  • 适用场景:
    • 还没 push,想:
      • 撤销刚提交的代码
      • 修改 commit 内容/顺序
      • 清理历史,让 commit 更干净

git revert 取出一份存档开始,但不会删除已有存档

git revert <commmit的哈希值>:在现有git树上,创建一个新的commit节点,这个节点跟你要回退的节点一样。但git树上的旧节点不会发生改变。

  • 适用场景:

  • push后:

  • 想回退到之前的某版本,切不破坏历史

  • 团队协作中,99%都应该使用revert

git cherry-pick 挑选你想要的提交

git cherry-pick <commit的哈希值>:将某些提交(节点)复制到当前分支下

  • 适用场景:

  • 将部分提交(比如对某个bug的修改)而不是整个分支移到当前分支下。

  • 从多人提交中挑特定 commit 合入

  • 基本用法

  • 把某个 commit 摘取到当前分支:git checkout target_branch git cherry-pick <commit-hash>。当前分支就会多一个新的 commit(内容和原 commit 相同,但 hash 不一样)。

  • 一次摘取多个连续的 commitgit cherry-pick A..B

  • 意思是将A到B的所有提交复制到当前。注意A必须比B的提交早,且改命令不包含A节点,如果A节点也需要进行复制,用如下命令。

  • git cherry-pick A^..B

  • 摘取多个不连续的提交:git cherry-pick A B C

git clone 克隆

git clone :创建一份远程仓库的拷贝。

git fetch 下载远程仓库新的提交

  1. git fetch:
    • 从远程仓库下载本地仓库中缺失的提交记录
    • 更新远程分支指针(如 o/main)
    • 其不会改变你当前的工作区,可以理解为只是下载了远程仓库新的更改,但并未合并。
  2. git fetch origin <分支名/远程节点>:
    • 会将本地仓库里,没有的远程仓库新的提交下载下来,但不会更新引用。
  3. git fetch origin <分支名/远程节点>:<本地分支>
    • 该命令很危险,很少使用
    • git fetch origin main:refs/remotes/origin/main # 标准例子
      • 左边 main → 远程仓库的引用(通常是分支)
      • 右边 refs/remotes/origin/main → 本地要更新的引用位置(几乎永远是远程跟踪分支)
      • git fetch 的冒号语法是用来指定"refspec"的,本质是更新本地仓库中的某个引用(ref),而不是合并到你的本地工作分支!
    • 会将本地仓库里,没有的远程仓库新的提交下载下来,直接更新/创建本地分支(不是合并)
      "refspec 更新" 或 "强制更新引用(force-update a ref)"
      它根本不是 merge,而是 直接把本地某个 ref(引用)的指针强行挪到远程下载下来的 commit 上。一句话概括区别:
操作 是 merge 吗? 会创建合并提交吗? 会保留两条历史线吗? 是否安全? 实际效果
git merge origin/main 通常会(非快进时) 是(两条线汇合) 非常安全 把两条历史真正"合并"
git fetch origin main:feature 不是 不会 不会 非常危险 把本地 feature 分支指针"硬搬家"到 origin/main 指向的 commit
复制代码
	`git fetch origin :<本地分支>`当<分支名/远程节点>或者source为空时,本地仓库如果没有该远程分支,那么本地分支将被创造

git pull 下载远程仓库新的提交+合并

  1. git pull:

    • 作用等于git fetch +git merge
    • 日常开发:先 git fetch,再手动 merge/rebase(专业选手做法)
    • 完全信任远程、只想快速同步:git pull --ff-only(最安全的一键方式)
    • 永远别干的事:本地改了一堆还没 commit,直接盲目 git pull

    git pull --rebase # 最常用,对当前分支
    git pull --rebase origin main # 明确指定远程和分支
    git pull --rebase --autostash # 工作区有修改时自动暂存,非常方便!
    git fetch && git rebase origin/main # 和上面完全等价,只是分两步写

git push 推送你的改变

  1. git push:

    1. 首次推送并与远程分支确认关联

    推送当前分支到远程,并建立跟踪关系
    git push -u origin 分支名

    示例:推送当前分支到远程的 feature-branch
    git push -u origin feature-branch


复制代码
2. 推送已有关联的分支,如果分支已经设置了上游分支:

简单推送
git push

或者明确指定
git push origin 分支名
  1. git push <远程仓库> <本地分支名>将指定的本地分支,推送到远程仓库相应的分支中,此时将不受HEAD的影响
  2. git push origin <source>:<destination>是将本地的source(可以是个分支名,也可以是节点,其可以采用相对引用)推到远程仓库origin的destinnation。
    • 如果要推送的远程的分支不存在,远程仓库将自动为你创造。
    • 当你的source为空时,git push origin :<destination>,如果你将空推到远程分支(该远程分支在远程仓库存在),那么该远程分支将被删除。

git tag 打标签

  1. git tag <标签名> <要打上标签的节点>
    • 给某个commit打上标签,比如"v.1.0.1"
    • commit的哈希值一长串并不好用,打上标签后更容易跳转,识别。
      Git Tag 主要有两种类型:
类型 命令 特点 常见用途
轻量标签 (Lightweight) git tag v1.0.0 只是一个指向 commit 的名字(指针) 临时标记、私人用
带注解标签 (Annotated) git tag -a v1.0.0 -m "正式发布版本" 包含创建者、日期、消息等元数据,推荐使用 正式发布版本(强烈推荐)
复制代码
# 1. 创建轻量标签(不推荐正式发布用)
git tag v1.0.0

# 2. 创建带注解标签(强烈推荐)
git tag -a v1.0.0 -m "正式发布 1.0.0 版本"

# 3. 给历史某个 commit 打标签(比如补打标签)
git tag -a v0.9.0 9fceb02 -m "补打 v0.9.0 标签"

# 4. 查看所有标签
git tag

# 5. 查看某个标签的详细信息
git show v1.0.0

# 6. 推送单个标签到远程(默认 git push 不会推送标签)
git push origin v1.0.0

# 7. 一次性推送所有标签
git push origin --tags

# 8. 删除本地标签
git tag -d v1.0.0

# 9. 删除远程标签
git push origin :refs/tags/v1.0.0
# 或者新语法(推荐)
git push --delete origin v1.0.0

# 10. 切换到某个标签(相当于 checkout 到那个版本)
git checkout v1.0.0
# 建议创建分支再开发,避免 detached HEAD 状态
git checkout -b hotfix-1.0.1 v1.0.0

git describe 查看标签

命令格式和输出

基本语法:

复制代码
git describe [参数] [某个commit]

如果不指定提交,默认使用当前 HEAD。

典型输出格式:
复制代码
<最近标签>-<提交数量>-g<提交哈希缩写>

各部分解析:

  • <最近标签>:从当前提交往前找,找到的最近一个标签(通常是附注标签)
  • <提交数量>:当前提交与该标签之间相差的提交次数
  • g:固定字母,只是分隔符(git 的缩写)
  • <提交哈希缩写>:当前提交的缩写哈希值(通常是前7位)

mian ^2 第二个爸爸

表示当前节点的上一个节点,2不表示向上两个节点,而表示该节点的第二个爸爸。一个节点有两个父节点时其会指向,^2将指向第二个父节点。

git可以指向分支,也可以指向节点。其通常是指向当前分支最新提交,当其指向其他节点时,被称为分离的HEAD。HEAD他的作用在我看来,是作为操作时的一个基准。

git的工作区,暂存区,本地仓库,远程仓库

其工作流程为

复制代码
工作区  --git add-->  暂存区  --git commit--> 本地仓库  --git push--> 远程仓库
  1. 工作区:当前你正在操作的那部分文件,git并不关心
  2. 暂存区:工作区和仓库的中间的地带。
    1. 你可以有选择的将修改提交到暂存区,而不是所有。对于不必要的修改,可以不加到暂存区中,只提交你所需要的修改。
  3. 本地仓库:你的本地git树
  4. 远程仓库:多人协作的远程git树

rebase与merge的区别

  1. rebase适合自己一个人用,其会修改树的历史。在团队协作中,不能对main进行rebase。rebase适合对自己开发,运营的分支使用。
  2. merge适合团队使用。
    1. 团队中也可以有选择地使用rebase,比如:
      • 在发起Pull Request前整理提交
      • 个人特性分支更新主分支代码时

(22 封私信 / 80 条消息) Git:图解 merge 和 rebase 的区别 - 知乎

git远程仓库

远程分支

  1. 一个只读指针,指向本地仓库上一次与远程仓库同步的节点,其只能通过git fetch和git push 发生改变。你平时开发应该基于它创建本地分支,而不是直接在它上面操作。
  2. 当远程仓库有其他的commit时,远程分支就和远程仓库最新的commit不同步了,需要通过git fetch来同步。
  3. 本地的HEAD并不默认指向远程分支,而是当前最新分支。
  4. 远程分支一般是用来看的,而不是用的。

远程分支的格式

命名规范 :远程分支有一个特殊的命名格式:<remote_name>/<branch_name>

  • <remote_name> :通常是 origin,这是你克隆仓库时 Git 自动为你创建的远程仓库的默认简称。你也可以添加其他远程仓库,并给它们起别的名字(如 upstream)。
  • <branch_name>:远程仓库上分支的实际名字。

在远程分支上commit会导致什么?

当你直接检出一个远程分支(如 git checkout origin/feature),Git会使仓库进入"分离的HEAD"(Detached HEAD)状态。这意味着HEAD(Git用来指向当前所在位置的内置指针)没有指向任何一个本地分支的末端,而是直接指向了一个特定的提交。

  • 在此状态下提交 :你是可以执行git commit操作的。然而,新的提交不会附加到任何分支上[]。这个提交会成为一个"悬空"的提交,在Git的版本图中是孤立的。

  • 潜在风险:当你切换回其他分支时,这个没有分支引用的"悬空"提交很可能在后续的垃圾回收中被Git清除,**导致你的工作内容丢失。因此,在这种状态下提交通常是不被推荐的,除非你很清楚自己在做什么(比如临时性的实验)。

🔧 正确的做法:创建并跟踪分支

绝大多数情况下,你的目的是在一个固定的本地分支上工作,并能够方便地将更改推送回远程分支。正确步骤如下:

  1. 获取远程更新 :首先,使用 git fetch origin 获取远程仓库的最新信息,包括所有分支的更新情况。这能让你看到远程是否存在新的分支或提交。
  2. 创建并切换本地分支 :使用 git checkout -b <本地分支名> origin/<远程分支名> 命令。例如,git checkout -b feature origin/feature。这个命令会:
    • 创建一个新的本地分支(例如 feature)。
    • 让这个本地分支自动"跟踪"(track)指定的远程分支(例如 origin/feature)。
    • 切换到新创建的本地分支。
      你也可以使用 git checkout -t origin/feature,它会创建一个与远程分支同名的本地分支并进行跟踪。
  3. 在本地分支上提交 :现在,你在新创建的本地分支上工作。此时执行 git commit,你的提交会安全地保存在这个本地分支上。
  4. 推送更改 :完成提交后,你可以使用 git push 将本地分支上的新提交推送到它所跟踪的远程分支。由于设置了跟踪关系,简单的 git push 就足够了。

如果你的本地分支已经存在并且设置了跟踪远程分支,那么直接切换到该本地分支(例如 git checkout feature)进行工作和提交即可

git fetch的常见工作流程

verilog 复制代码
git fetch origin          # 先把远程最新内容拿下来看看
git log HEAD..origin/main # 查看对方提交了什么(不会影响当前代码)
git diff HEAD origin/main # 或者对比文件差异
# 确认没事后再决定怎么融入本地
git merge origin/main     # 或者
git rebase origin/main

偏离的提交历史

  1. rebase方案

  2. merge方案

  3. git pull --rebase方案

锁定的Main(Locked Main)

对于需要代码审查的工程,如何进行提交?

对于需要代码审查的项目,你不能直接将commit push到主支,这将会被拒绝。

新建一个分支feature, 推送到远程服务器。然后重置你的main分支和远程服务器保持一致, 否则下次你pull并且他人的提交和你冲突的时候就会有问题.

本地有多个分支要合并push

  1. rebase方案

  2. merge方案

杂项/备注

只保留一个提交

假设你为了解决一个bug,创建了一个新的分支,为了进行调试,你往其中加入了一些打印调试信息的代码,并进行了一次提交。然后在此的提交的基础上,完成了真正改写bug的操作,并又进行了一次提交。这时你只想保留改写bug的那个操作到主支,你可以选择用git rebase -i <base_commit> ,又或者是git sherry-pick <commit的哈希值>。

符号

*星号表示当前分支

o 表示远程仓库orgin

相关推荐
_pass_1 小时前
Git 日记
git
乌恩大侠3 小时前
AI-RAN 在 Spark上部署 Sionna-RK
大数据·分布式·spark
MC丶科4 小时前
Spring Boot + Elasticsearch 实现全文搜索功能(商品搜索)!让搜索快如闪电
spring boot·后端·elasticsearch·软考高级·软考架构师
t***26594 小时前
Springboot中使用Elasticsearch(部署+使用+讲解 最完整)
spring boot·elasticsearch·jenkins
h***59334 小时前
使用Canal将MySQL数据同步到ES(Linux)
linux·mysql·elasticsearch
G皮T5 小时前
【ELasticsearch】索引字段设置 “index”: false 的作用
大数据·elasticsearch·搜索引擎·全文检索·索引·index·检索
摆烂且佛系7 小时前
win10 Git Bash安装make命令
git
程序员皮皮林7 小时前
Redis:大数据中如何抗住2000W的QPS
大数据·数据库·redis