0%

git cherry-pick 用来把某一个提交,或者某几个指定提交,复制到当前分支上。

它比较适合下面这些场景:

  • 线上分支修了一个 bug,想把这次修复同步到开发分支
  • 某个功能分支里只有一两个提交有用,不想整条分支都合并进来
  • 想把别的分支上的某次提交单独拿过来,而不是执行 merge

一、先理解 cherry-pick 做了什么

假设现在有两个分支:

  • main
  • feature

feature 分支上有一个提交修复了接口错误,你现在在 main 分支,也想要这个修复。

这时可以直接把那次提交挑过来:

git checkout main
git cherry-pick 提交ID

执行后,Git 会把那次提交的改动应用到当前分支,并在当前分支生成一个新的提交。

要注意的是,这不是“移动”原提交,而是“复制一份改动内容”到当前分支,所以新生成的提交 ID 一般会和原来不同。

下面这张图可以直观看一下这个过程:

二、最常见的基本用法

先查看提交记录:

git log --oneline

假设你看到 feature 分支里有这样一条提交:

a1b2c3d 修复订单接口参数错误

切换到目标分支后执行:

git checkout main
git cherry-pick a1b2c3d

如果没有冲突,这次提交就会被应用到 main 分支。

再执行:

git log --oneline

通常就能看到当前分支多出了一条内容相同、但提交 ID 不同的新提交。

三、一次挑选多个提交

1. 逐个写出多个提交 ID

如果要挑多个不连续的提交,可以直接把提交 ID 都写上:

git cherry-pick a1b2c3d d4e5f6g h7i8j9k

Git 会按顺序依次应用这些提交。

2. 挑选一段连续提交

如果这些提交是连续的,可以用范围:

git cherry-pick 起始提交ID^..结束提交ID

例如:

git cherry-pick a1b2c3d^..d4e5f6g

这里的意思是,把从 a1b2c3dd4e5f6g 之间的提交都挑过来,并且包含起始提交。

如果写成:

git cherry-pick a1b2c3d..d4e5f6g

那通常不包含 a1b2c3d 这次提交,这一点要特别注意。

四、只应用改动,先不自动提交

有时候你想先把改动拿过来,再自己检查一下,或者和当前工作区里的其他修改一起提交,这时可以加 -n 参数:

git cherry-pick -n a1b2c3d

-n--no-commit 的简写。

它会把改动应用到当前工作区和暂存区,但不会自动生成提交。

这时你可以先查看状态:

git status

确认没问题后再自己提交:

git commit -m "引入订单接口修复"

五、发生冲突时怎么处理

cherry-pickmerge 一样,也可能出现冲突。

比如当前分支和目标提交都修改了同一段代码,执行后可能会看到冲突提示。

这时处理步骤通常是:

1. 查看冲突文件

git status

2. 手动修改冲突内容

把冲突标记处理掉,保留你真正想要的代码。

3. 标记冲突已解决

git add 冲突文件

4. 继续执行

git cherry-pick --continue

如果你处理中途发现这次挑选不要了,也可以取消:

git cherry-pick --abort

它会回到执行 cherry-pick 之前的状态。

六、和 merge 的区别

很多人第一次接触 git cherry-pick 时,容易把它和 merge 混在一起。

简单来说:

  • git merge 是把一个分支整体合并过来
  • git cherry-pick 是只拿指定提交过来

如果你只想同步一个 bug 修复、一个小功能、一次配置调整,那么 cherry-pick 往往更直接。

但如果你需要把一整条分支的历史完整合并,通常还是 merge 更合适。

七、常见使用场景

1. 把线上 hotfix 同步回开发分支

比如你在 main 分支紧急修了一个线上问题,但开发工作还在 dev 分支继续进行。

这时可以切到 dev,把 main 上那次修复挑过来:

git checkout dev
git cherry-pick 修复提交ID

这样就不需要把整个 main 分支重新合并一遍。

2. 只拿功能分支里的某一次提交

有些功能分支提交很多,里面可能既有实验代码,也有真正需要的修改。

如果当前只想引入其中某一个提交,cherry-pick 会比 merge 更省事。

3. 补漏同步

有时两个分支长期并行开发,某个重要提交漏同步了,这时也可以直接用 cherry-pick 补过去。

八、常见问题

1. cherry-pick 后为什么提交 ID 变了?

因为 Git 会在当前分支重新创建一个新提交。

虽然改动内容可能一样,但它已经是另一条提交记录了,所以提交 ID 通常不同。

2. 可以对已经 merge 过的提交再执行 cherry-pick 吗?

可以,但要小心重复改动。

如果那部分代码其实已经在当前分支里了,再次执行可能会出现冲突,或者产生重复提交。

执行前最好先用 git loggit show 看清楚目标提交内容。

3. cherry-pickrebase 是不是一回事?

不是。

rebase 主要是变更提交基底、整理提交历史;
cherry-pick 主要是挑选指定提交并复制到当前分支。

两者都可能改写历史,但使用目的不一样。

九、小结

git cherry-pick 最核心的作用,就是把指定提交复制到当前分支。

最常用的几种写法:

git cherry-pick 提交ID
git cherry-pick 提交ID1 提交ID2
git cherry-pick 起始提交ID^..结束提交ID
git cherry-pick -n 提交ID
git cherry-pick --continue
git cherry-pick --abort

如果你只想拿某几个提交,而不想整条分支都合并进来,这个命令会非常方便。

参考文章

Ollama 默认会优先使用系统里可用的 GPU 来做推理。

如果你的 Ubuntu 机器装了 NVIDIA 显卡,并且 Ollama 已经能正常调用 CUDA,那么直接执行 ollama run 时,通常会优先走 GPU。

但有些场景下,我们反而希望它强制只用 CPU,比如:

  • GPU 驱动不稳定,跑模型时容易报错
  • 想把 GPU 留给别的任务
  • 只是做功能测试,不关心推理速度
  • 服务器上的 GPU 显存比较紧张

这篇文章记录一下,在 Ubuntu 下如何让 Ollama 强制使用 CPU 推理,而不是继续使用默认 GPU。

一、先说结论

如果你只是想让某个模型在 Ubuntu + NVIDIA 的环境里临时改成 CPU 推理,不一定要去改服务环境变量。

更直接的方式是把该模型的 num_gpu 设为 0,也就是不往 GPU 分配任何层。

这篇文章保留两类方式:

  • 通过 API 调用时传 num_gpu: 0
  • ollama run 交互界面里执行 /set parameter num_gpu 0
  • 通过 systemd 配置,让 Ollama 长期默认走 CPU

二、临时强制使用 CPU

1. 在 API 调用里指定 num_gpu: 0

如果你是通过 Ollama 的 HTTP API 来加载模型,可以直接在请求里加上:

curl localhost:11434/api/generate -d '{"model":"llama3:latest","options":{"num_gpu":0}}'

如果模型只是被加载进内存,常见返回会类似这样:

{"model":"llama3:latest","created_at":"2024-09-25T10:06:21.362308925Z","response":"","done":true,"done_reason":"load"}

这表示模型已经按这次参数要求被加载好了。

接着可以用:

ollama ps

查看当前处理器分配情况,例如:

NAME                             ID              SIZE    PROCESSOR  UNTIL
llama3:latest 365c0bd3c000 4.3 GB 100% CPU Forever
qwen2:7b-instruct-q4_K_M f10f702d139e 5.4 GB 100% GPU Forever

这里最关键的是 PROCESSOR 一列。如果显示 100% CPU,说明当前这个模型实例没有使用 GPU 层。

2. 在 CLI 里设置 num_gpu 0

如果你平时更常用 ollama run,也可以直接在交互界面里设置参数。

先启动模型:

ollama run llama3:latest

进入交互界面后执行:

>>> /set parameter num_gpu 0
Set parameter 'num_gpu' to '0'

然后再正常提问:

>>> hello
Hello! It's nice to meet you. Is there something I can help you with, or would you like to chat?

退出后同样可以检查:

ollama ps

如果输出类似这样:

NAME                             ID              SIZE    PROCESSOR  UNTIL
llama3:latest 365c0bd3c000 4.3 GB 100% CPU Forever
qwen2:7b-instruct-q4_K_M f10f702d139e 5.4 GB 100% GPU Forever

那就说明 llama3:latest 这一份实例已经切到了 CPU,而其他模型仍然可以继续走 GPU。

三、通过 systemd 长期固定走 CPU

如果你的目标不是“只让某个模型临时走 CPU”,而是希望这台 Ubuntu 机器上的 Ollama 服务长期默认不用 NVIDIA GPU,那么还是应该改 systemd 配置。

这种方式和前面的 num_gpu 0 不一样,它影响的是整个 ollama 服务的默认行为。

1. 编辑 Ollama 服务 override

执行:

sudo systemctl edit ollama

然后写入:

[Service]
Environment="CUDA_VISIBLE_DEVICES=-1"
Environment="OLLAMA_LLM_LIBRARY=cpu_avx2"

这里的含义是:

  • CUDA_VISIBLE_DEVICES=-1:让 Ollama 看不到 NVIDIA GPU
  • OLLAMA_LLM_LIBRARY=cpu_avx2:明确优先使用 CPU 的 AVX2 LLM 库

如果你的 CPU 不支持 AVX2,也可以把它改成:

Environment="OLLAMA_LLM_LIBRARY=cpu_avx"

再不行就退到:

Environment="OLLAMA_LLM_LIBRARY=cpu"

2. 重载并重启服务

sudo systemctl daemon-reload
sudo systemctl restart ollama
sudo systemctl status ollama

这样之后,后续由这个 ollama 服务加载的模型,默认都会优先走 CPU。

四、怎么确认现在真的没有走 GPU

1. 用 ollama psPROCESSOR

这篇文章里最推荐的确认方式就是:

ollama ps

只要看到对应模型是:

100% CPU

基本就能确认它这次没有走 GPU。

2. 推理时再结合 nvidia-smi 观察

如果你的机器本来有 NVIDIA GPU,也可以在模型运行时另开一个终端观察:

nvidia-smi

如果没有看到 ollama 相关进程占用显存,通常就说明这次推理没有走 NVIDIA GPU。

五、几个容易踩坑的地方

1. num_gpu 0 只对当前加载方式生效

不管你是 API 里传:

"options":{"num_gpu":0}

还是在 CLI 里执行:

/set parameter num_gpu 0

它们本质上都是针对当前这次模型加载参数生效,不是把整个 Ollama 服务永久改成 CPU-only。

2. 其他模型还是可以继续走 GPU

这也是前面两种 num_gpu 0 方式的一个优点。

比如你让 llama3:latest 走 CPU,并不代表另外一个已经加载的 qwen2:7b-instruct-q4_K_M 也会自动切到 CPU。

所以 ollama ps 里可能会同时出现:

  • 一个模型显示 100% CPU
  • 另一个模型显示 100% GPU

3. 如果改的是 systemd,它影响的是整个服务

如果你用的是:

sudo systemctl edit ollama

那它就不是“只影响某个模型实例”了,而是会影响这个 ollama 服务后续加载出来的模型。

所以这两类方案要分清:

  • num_gpu 0:适合临时、按模型控制
  • systemd override:适合长期、按服务控制

4. CPU 推理速度明显变慢

这属于正常现象。

GPU 本来就是 Ollama 在 Linux 上更常见的高性能推理方式,强制切到 CPU 后,速度通常会明显下降,尤其是大模型。

如果你只是临时排查问题,可以先用小一点的模型测试,例如:

ollama run qwen3:0.6b

或者:

ollama run gemma3:1b

六、小结

在 Ubuntu 下,如果你的场景是 NVIDIA 显卡环境下强制让 Ollama 走 CPU,可以分成两类:

  1. 临时按模型控制:API 传 options.num_gpu = 0,或者 CLI 执行 /set parameter num_gpu 0
  2. 长期按服务控制:用 systemd override 配置 CUDA_VISIBLE_DEVICES=-1

前者更细粒度,适合临时测试;
后者更彻底,适合把整台机器上的 Ollama 默认固定成 CPU 推理。

参考文章

在看 shell 脚本时,经常会看到这样一段写法:

command >/dev/null 2>&1

很多人第一次看到会觉得有点绕,尤其是 2>&1 这部分,不太容易一眼看明白。

这篇文章就专门整理一下它的含义,以及为什么顺序不能乱写。

一、这段写法到底是什么意思

command >/dev/null 2>&1

它表示:

  • 把标准输出重定向到 /dev/null
  • 再把标准错误重定向到标准输出当前所在的位置

最终效果就是:

正常输出和报错输出都会被丢弃,不在终端显示。

二、先理解 3 个标准通道

在 Unix/Linux 里,一个进程默认会带有 3 个标准通道,对应 3 个文件描述符:

0 = stdin
1 = stdout
2 = stderr

分别表示:

  • 0:标准输入,程序从哪里读数据
  • 1:标准输出,程序正常结果输出到哪里
  • 2:标准错误,程序报错信息输出到哪里

后面提到的 1>2>2>&1,本质上都是在操作这些文件描述符。

三、>/dev/null 是什么意思

command >/dev/null

它等价于:

command 1>/dev/null

意思是把标准输出重定向到 /dev/null

这里有两个点:

  • > 表示输出重定向
  • /dev/null 是一个特殊设备文件,写进去的内容会被直接丢弃

所以这条命令的效果是:

  • 正常输出不再显示
  • 错误输出仍然会显示在终端

例如:

ls existing-file missing-file >/dev/null

这时 existing-file 的正常输出会被丢弃,但如果 missing-file 不存在,报错信息还是会显示出来。

四、2>&1 是什么意思

2>&1

它的含义是:

把标准错误重定向到文件描述符 1 当前所指向的位置。

拆开看会更清楚:

  • 2>:操作标准错误
  • &1:表示引用文件描述符 1

注意这里的 1 不是文件名,而是文件描述符编号。

也就是说:

command >/dev/null 2>&1

可以理解成:

  1. 先把标准输出丢到 /dev/null
  2. 再让标准错误也跟着标准输出走

所以最后标准输出和标准错误都会进入 /dev/null

五、为什么 1 前面要加 &

因为 shell 需要区分:

  • 你写的是文件描述符
  • 还是普通文件名

不加 &

command 2>1

这表示把标准错误写入一个名为 1 的文件。

加上 &

command 2>&1

这表示把标准错误重定向到文件描述符 1

所以 & 的作用很简单,就是告诉 shell:

后面的数字不是文件名,而是文件描述符。

六、为什么 1 可以省略

在 shell 里:

>file

默认就是:

1>file

因为如果 > 前面没有写编号,默认操作的就是标准输出。

所以:

>/dev/null

等价于:

1>/dev/null

七、顺序为什么很重要

很多人会把下面两种写法看成一样,其实它们不一样。

写法 A

command >/dev/null 2>&1

执行顺序是:

  1. 1 指向 /dev/null
  2. 2 指向 1 当前的位置,也就是 /dev/null

结果:

  • 标准输出被丢弃
  • 标准错误也被丢弃

写法 B

command 2>&1 >/dev/null

执行顺序是:

  1. 先让 2 指向 1 当前的位置,此时 1 还是终端
  2. 再把 1 重定向到 /dev/null

结果:

  • 标准输出被丢弃
  • 标准错误仍然输出到终端

所以这两条命令不是一回事:

command >/dev/null 2>&1
command 2>&1 >/dev/null

重点就在于 shell 会从左到右依次处理重定向。

八、常见用法

1. 只隐藏正常输出

command >/dev/null

2. 只隐藏错误输出

command 2>/dev/null

3. 同时隐藏正常输出和错误输出

command >/dev/null 2>&1

4. 正常输出和错误输出都写入同一个文件

command >all.log 2>&1

5. 正常输出写文件,错误仍然显示在终端

command >out.log

6. 错误写文件,正常输出仍然显示在终端

command 2>err.log

九、/dev/null 是什么

/dev/null 是 Unix/Linux 下的一个特殊设备文件,常被叫做“黑洞设备”。

它的特点很简单:

  • 写进去的内容会被丢弃
  • 从里面通常读不到有意义的内容

所以它特别适合拿来做静默执行,比如:

  • 不想看到命令的普通输出
  • 不想让脚本打印一堆无关信息
  • 想让某些命令安静执行

十、实际例子

比如在脚本里,你可能会看到:

source venv/bin/activate >/dev/null 2>&1

它表示:

  • 执行 venv/bin/activate
  • 不显示正常输出
  • 不显示错误输出

不过这里也要注意一件事:

如果激活失败,报错信息也会被隐藏掉。

所以在排查问题时,通常要先把这类重定向去掉,不然看不到真实报错。

十一、还有一种更简写的写法

bashzsh 里,经常还能看到:

command &>/dev/null

它通常等价于:

command >/dev/null 2>&1

不过如果考虑可读性和兼容性,很多时候还是传统写法更直观一些:

command >/dev/null 2>&1

十二、小结

>/dev/null 2>&1 本质上就是 shell 重定向语法。

它的含义可以概括成一句话:

先把标准输出重定向到 /dev/null,再把标准错误重定向到标准输出当前所在的位置,从而让正常输出和错误输出都被丢弃。

如果只想记住最关键的几条,可以直接记下面这个速记版:

0 = stdin
1 = stdout
2 = stderr
>/dev/null      # 等价于 1>/dev/null,隐藏标准输出
2>/dev/null # 隐藏标准错误
2>&1 # 让标准错误跟随标准输出
>/dev/null 2>&1 # 正常输出和错误输出都隐藏
2>1   # 写入名为 1 的文件
2>&1 # 重定向到文件描述符 1

参考文章