Git 用 assume-unchanged 优雅管理配置文件

最近在使用 Jekyll 开发博客时,遇到了一个典型问题:Gemfile 和 Gemfile.lock 文件 在本地和生产环境需要不同的配置。

  • 🖥️ 本地环境:需要特定版本的 jekyll 插件
  • ☁️ 云端环境:需要兼容老版本的依赖

解决方案:git update-index --assume-unchanged

Git 提供了一个优雅的解决方案:assume-unchanged 标志。

什么是 assume-unchanged?

assume-unchanged 是 Git 的一个内部标志,它告诉 Git:

“假设这个文件没有变化,不要检查它的修改状态,也不要让我提交它。”

如何使用?

# 从暂存区移除Gemfile文件
git restore --staged ../Gemfile ../Gemfile.lock

# 告诉 Git 忽略特定文件的本地修改
git update-index --assume-unchanged Gemfile
git update-index --assume-unchanged Gemfile.lock

# 查看哪些文件被标记为 assume-unchanged
git ls-files -v | grep '^h'

# 提交其他文件
git commit -m "更新博客文章,忽略Gemfile本地修改"

# 查看状态确认
git status


# 需要更新配置文件时
# 先恢复跟踪(撤销忽略)
git update-index --no-assume-unchanged Gemfile
git update-index --no-assume-unchanged Gemfile.lock

# 修改并提交
git add Gemfile
git commit -m "更新Gemfile依赖"

# 重新标记为忽略
git update-index --assume-unchanged Gemfile
git update-index --assume-unchanged Gemfile.lock

与 .gitignore 的区别

很多人会混淆 assume-unchanged.gitignore,它们有本质区别:

特性 assume-unchanged .gitignore
用途 忽略已跟踪文件的修改 忽略未跟踪的文件
效果 文件仍在版本控制中,只是不检查修改 文件完全不被版本控制
适用场景 本地配置文件、环境变量文件 构建产物、日志文件、IDE配置
共享性 本地设置,不共享给他人 提交到仓库,团队成员共享

skip-worktree 的区别

Git 还有一个类似的标志:skip-worktree

# skip-worktree 的用法
git update-index --skip-worktree Gemfile

# 查看区别
git ls-files -v | grep '^S'  # skip-worktree 文件
git ls-files -v | grep '^h'  # assume-unchanged 文件

主要区别

  • assume-unchanged:性能优化,告诉Git文件不太可能改变
  • skip-worktree:功能标志,明确表示”不要更新我的工作树”

对于配置文件管理,skip-worktree 是更安全的选择,因为它能防止 Git 的各种操作覆盖你的本地文件。

注意事项和陷阱

  1. 分支切换问题: 当你在标记了 assume-unchanged 的分支之间切换时,如果文件有冲突,Git 可能会报错。

  2. 团队协作
    # 假设团队成员更新了仓库中的 Gemfile
    git pull
    # 由于本地文件被标记,更新可能不会应用到你的工作副本
    
  3. 忘记恢复
    # 创建一个脚本帮助管理
    cat > git-ignored-files.sh << 'EOF'
    #!/bin/bash
    echo "当前被忽略的文件:"
    git ls-files -v | grep -E "^[hs]"
    EOF
    chmod +x git-ignored-files.sh
    

更好的替代方案

对于配置文件管理,还有更健壮的方案:

  1. 使用模板文件
    # 仓库中保存模板
    _config.example.yml
    Gemfile.example
       
    # 本地复制并重命名
    cp _config.example.yml _config.yml
    cp Gemfile.example Gemfile
    
  2. 环境变量
    # _config.yml
    url: <%= ENV['JEKYLL_SITE_URL'] || 'http://localhost:4000' %>
    
  3. 多环境配置
    # 使用不同的配置文件
    jekyll build --config _config.yml,_config.local.yml
    

总结

git update-index --assume-unchanged 是一个强大的工具,特别适合管理那些需要在版本控制中保留,但又不想提交本地修改的文件。

最后,如果你经常忘记哪些文件被标记了,可以在 .gitconfig 中添加别名:

[alias]
    ignored = !git ls-files -v | grep \"^[hs]\"
    hide = update-index --assume-unchanged
    unhide = update-index --no-assume-unchanged

然后使用更简洁的命令:

git ignored      # 查看被忽略的文件
git hide Gemfile # 隐藏文件
git unhide Gemfile # 取消隐藏

自建 DNS over HTTPS(DoH)并在 macOS 配置使用 在 Mac 下搞定了 Jekyll 环境:一次踩坑全记录