很多人拿到一台 Mac,装完编辑器、配好输入法,第三件事就是把 Git 拾掇利索。但真动手时会发现,Mac 上的 Git 有点"三头六臂":系统自带一份、Homebrew 装一份、Xcode 命令行工具里还藏着一份,which -a git一敲能出来三行路径。再叠加 VSCode 的图形化操作界面,很多人就卡在"我到底在用哪个 Git""为什么源代码管理面板是空的""提交按钮点了没反应"这类问题上。这篇就按我自己的实际配置顺序,把mac电脑上git的安装与配置讲透,再把vscode的图形化操作界面从开关到日常提交流程一路走完。适合刚换 Mac 的开发者、习惯命令行但想试试图形界面的老手,以及被 SSH 密钥和多账号搞晕过的同学。全程按"先搞清家底、再连远程、最后上手操作"的顺序推进,每一步都说明为什么这么做。
1. 先把 Mac 上 Git 的"家底"摸清楚,再决定装不装
1.1 系统自带的那份 Git 是什么来路
在终端敲git --version,如果直接报一串版本号,说明系统里已经有 Git 了;如果弹出一个对话框让你安装命令行开发者工具,那就是没有。macOS 自带的 Git 并不是苹果单独打包给你的,它属于Xcode Command Line Tools的一部分,实际路径在/usr/bin/git。这个路径有个特点:它是个"壳",真正的可执行文件在/Library/Developer/CommandLineTools/usr/bin/git。
为什么要关心这个?因为它决定了你的 Git 版本号跟随系统更新走。比如 macOS 12 上自带的 Git 可能是 2.32 左右,而当时最新已经到了 2.36。日常 clone、commit、push 差别不大,但涉及到git restore --staged、git switch这些较新的子命令,或者git sparse-checkout完整功能时,版本差一截就会报git: 'switch' is not a git command。遇到这种报错不用怀疑人生,先看版本号。
如果你只是想快速用一下,装命令行工具就够了,一条命令搞定:
xcode-select --install提示:这一步会弹窗,点击"安装"后要等几分钟到十几分钟不等,取决于网速。中途别关窗口,装完再敲
git --version验证。
1.2 用 Homebrew 装一份"自己能控制"的 Git
我更推荐用 Homebrew 单独装一份,原因很实在:一是版本新,二是升级不用等系统大版本更新,三是卸载干净,不会和系统组件纠缠。
# 如果还没有 Homebrew,先装 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" # 安装最新版 Git brew install git # 升级 brew upgrade git装完之后关键问题是PATH 顺序。Apple Silicon 芯片的 Mac,Homebrew 装在/opt/homebrew,Git 在/opt/homebrew/bin/git;Intel 芯片在/usr/local/bin/git。你可以这样确认:
which -a git如果输出的第一行是/usr/bin/git,说明系统自带的那份排在前面,你敲git用的还是老版本。这时需要调整 shell 配置。macOS Catalina 之后默认 shell 是 zsh,所以要改~/.zshrc(不是~/.bash_profile,这个是新手最常踩的坑):
# 编辑 ~/.zshrc export PATH="/opt/homebrew/bin:$PATH" # 让配置生效 source ~/.zshrc # 再次确认 which git # 期望输出:/opt/homebrew/bin/git注意:Intel 机型要写
/usr/local/bin。不确定自己芯片型号,敲uname -m,arm64是 Apple Silicon,x86_64是 Intel。
1.3 第一次配置必须落到实处的几个字段
Git 装好后第一件事是"报户口",否则提交记录上会显示unknown,或者用本机用户名当作者名,以后想改都很麻烦。
git config --global user.name "你的名字或昵称" git config --global user.email "你的邮箱"这里有个细节值得展开:邮箱要和你托管平台账号绑定的邮箱一致,否则提交记录不会归到你名下,贡献图是灰的。如果你在同一台机器上要区分工作和个人身份,可以去掉--global,在具体仓库里单独配一遍。
除了身份,下面这几项我基本是"装机必配":
# 新仓库默认分支用 main,而不是 master git config --global init.defaultBranch main # 换行符处理,Mac 上用 input 最省心(详见第 6 节) git config --global core.autocrlf input # 中文文件名不转义显示 git config --global core.quotepath false # 中文文件名在 macOS 上是 NFD 编码,这个开关避免同名文件被识别成两个 git config --global core.precomposeunicode true # pull 时默认用合并而不是变基(新手先用 merge,理解后再换) git config --global pull.rebase false配完想知道每一项到底写在哪个文件里,用这条命令,比逐个翻文件高效得多:
git config --list --show-origin输出里会标出每一行来自~/.gitconfig、/opt/homebrew/etc/gitconfig还是某个仓库的.git/config。优先级是仓库级 > 全局级 > 系统级,同一个键被高层覆盖时低层不生效,这点在排查"我明明配了却不生效"时特别有用。
想直接改配置文件而不是敲命令,用git config --global --edit,会拉起默认编辑器打开~/.gitconfig,改完存盘即生效,适合批量调整。
2. 让 Mac 和远程仓库"对暗号":SSH 密钥与多身份并存
2.1 为什么我一直推荐用 SSH 而不是每次输账号密码
HTTPS 方式 clone 当然能用,但每次 push 都要输凭据(虽然可以用钥匙串记住),而且一旦平台调整了鉴权规则,老仓库就会突然推不上去。SSH 方式配好一次,后面所有仓库都省事,也能天然支持多账号。
现在的推荐算法是ed25519,比老式的 RSA 更短、更快,安全性也够:
ssh-keygen -t ed25519 -C "你的邮箱" -f ~/.ssh/id_ed25519执行过程会问你两件事:一是密钥保存路径(直接回车用默认),二是 passphrase(口令)。建议设一个口令,这样即使私钥文件泄露,别人也用不了。如果嫌每次输麻烦,下一步的 ssh-agent 会帮你记住。
生成后目录里会出现两个文件:
| 文件名 | 作用 | 能否外传 |
|---|---|---|
id_ed25519 | 私钥,相当于家门钥匙 | 绝对不能分享 |
id_ed25519.pub | 公钥,相当于门锁编号 | 需要上传到平台 |
把公钥内容复制出来:
pbcopy < ~/.ssh/id_ed25519.pubpbcopy是 macOS 特有命令,直接把内容塞进剪贴板,比cat出来手动选中方便,也不容易漏字符。然后到托管平台的 SSH Keys 设置页粘贴保存。
2.2 ssh-agent:让口令只输一次
macOS 上的 ssh-agent 需要手动把私钥加进去,并且可以借助系统钥匙串记住口令:
# 启动 agent(如果已在运行会提示) eval "$(ssh-agent -s)" # 添加私钥,并把口令存进钥匙串 ssh-add --apple-use-keychain ~/.ssh/id_ed25519--apple-use-keychain是 macOS 专有参数(老版本上是-K),加上它以后重启终端、重启电脑都不用再输口令。这个细节网上很多教程没写,导致有人抱怨"我明明加了 agent,重启就失效"。
如果想让终端一打开就自动加载,把下面这段写进~/.zshrc:
if [ -z "$SSH_AUTH_SOCK" ]; then eval "$(ssh-agent -s)" > /dev/null ssh-add --apple-use-keychain ~/.ssh/id_ed25519 2>/dev/null fi2.3 一台 Mac 挂两个账号:用 config 文件把身份分开
工作一个账号、个人一个账号,这在开发者里太常见了。麻烦点在于:默认情况下 SSH 只会用~/.ssh/id_ed25519去连所有主机,第二个账号就会认证失败。
解决办法是给不同的主机起别名。先给第二个账号生成一把新密钥:
ssh-keygen -t ed25519 -C "work@example.com" -f ~/.ssh/id_ed25519_work然后在~/.ssh/config里配置(没有这个文件就新建):
Host github-work HostName github.com User git IdentityFile ~/.ssh/id_ed25519_work IdentitiesOnly yes Host github-personal HostName github.com User git IdentityFile ~/.ssh/id_ed25519 IdentitiesOnly yes注意IdentityFile只写私钥路径,不要写成.pub,这是极高频的错误。IdentitiesOnly yes的作用是告诉 SSH 只用指定的这把钥匙,不要到处乱试,能避免"账号 A 的密钥去认证账号 B"的诡异情况。
配好之后,仓库地址要跟着改成别名形式:
git clone git@github-work:团队/仓库名.git如果仓库已经 clone 过了,改一下 remote 就行:
git remote set-url origin git@github-work:团队/仓库名.git验证连通性:
ssh -T git@github-work看到欢迎语(通常会告诉你认证成功,以及该账号的用户名)就说明通了。如果报Permission denied (publickey),按这个顺序排查:ls -l ~/.ssh看私钥权限是不是600、.ssh目录是不是700、config 里的 Host 别名和 remote 地址里的别名是否完全一致(差一个字母就不匹配)。
3. VSCode 里让 Git 面板"活过来"的几个开关
3.1 安装后的第一件事是装 code 命令
从官网下载 macOS 版是.zip,双击解压得到一个Visual Studio Code.app,拖进"应用程序"文件夹。这一步之后,有个容易被忽略的动作:把命令行工具装上。打开 VSCode,按Cmd + Shift + P唤出命令面板,输入shell command,选择Shell Command: Install 'code' command in PATH。
装完之后,你就可以在任意终端里这样用:
code . # 用 VSCode 打开当前目录 code ~/.zshrc # 直接编辑某个文件界面是英文的话,在扩展面板搜Chinese,安装官方那个简体中文语言包,重启后就是中文界面。这一步不影响任何功能,纯粹降低理解成本,新手建议装。
3.2 源代码管理面板是空的?先分清三种"空"
左侧活动栏那个分叉图标就是源代码管理面板。点开发现是空的,通常有三种原因,判断方法完全不同:
第一种,你没打开文件夹。VSCode 是"文件夹优先"的编辑器,只打开单个文件时,Git 功能不会启用。用文件 > 打开文件夹选一个目录,面板立刻就有反应。这是最常见的原因。
第二种,这个目录本身不是 Git 仓库。面板会显示"未初始化仓库",下面有个"初始化仓库"按钮,点了就相当于执行git init。这里有个细节:点完之后它会用当前全局的init.defaultBranch作为默认分支名,如果你的全局配置没改,默认还是master。
第三种,VSCode 找不到 Git。表现是面板整个消失,或者提示"未找到 Git"。这时按Cmd + ,打开设置,搜git.path,把 Homebrew 装的 Git 绝对路径填进去:
{ "git.path": "/opt/homebrew/bin/git" }用which git拿到实际路径再填,别凭记忆写。另外确认git.enabled是true,这个开关如果被某个配置模板关掉了,面板也不会出现。
3.3 我每次装完 VSCode 都会改的几项配置
打开设置 > 搜索 git,或者直接编辑settings.json(Cmd + Shift + P输入Open User Settings (JSON))。下面是我自己用了很久的一套,逐项说明理由:
{ "git.enabled": true, "git.path": "/opt/homebrew/bin/git", "git.autofetch": true, "git.autofetchPeriod": 180, "git.confirmSync": false, "git.enableSmartCommit": true, "git.smartCommitChanges": "tracked", "git.postCommitCommand": "push", "git.openDiffOnClick": true, "git.ignoreLegacyWarning": true }git.autofetch:后台定时去远程拉取最新引用,这样你在分支列表里能看到"本地落后远程 3 个提交",避免盲推。周期设 180 秒,太频繁会一直占网络。git.confirmSync:关掉"确定要同步吗"的弹窗。有人担心误操作,但在个人项目上这个弹窗纯粹是打断节奏。git.enableSmartCommit:当暂存区为空时,提交会自动把所有改动加入暂存。装了这个之后操作快很多,但改文件很多时容易把不想提交的内容一起带上,所以我配了smartCommitChanges: "tracked",只自动带上已被跟踪的文件,新文件仍需手动暂存,算是个折中。git.postCommitCommand: "push":提交完自动推送。这个开关看团队习惯,如果你们走 PR 流程、不允许直推主干,建议设成"none"。git.openDiffOnClick:点文件直接进 diff 视图,而不是打开原文件。习惯后效率提升明显。
提示:
settings.json里如果已有内容,注意 JSON 语法,多了个逗号整个文件都会失效,VSCode 会在编辑器里用红色波浪线提示。
4. 图形界面下的日常提交流程:从改代码到推上远程
4.1 行级暂存:把"顺手改的"和"本次要提交的"分开
图形界面最大的价值就在这里。假设你改了一个功能文件,同时顺手删了几行调试日志、调了一下缩进。命令行下要拆成两次提交,得用git add -p一路交互,回答一串 y/n。在 VSCode 里就直观得多:
打开源代码管理面板,点某个文件,进入 diff 视图。左边是原始内容,右边是当前内容。把鼠标悬停在某个改动块上,会出现几个小按钮,其中一个是"暂存此代码块"(Stage Change)。点一下,只有这一块被加进暂存区,其余留在工作区。如果想更细,可以选中具体几行,右键选择"暂存所选范围"。
这个能力配合git.enableSmartCommit要特别小心:如果暂存区已经有内容了,智能提交不会自动把其余改动带上,这是对的;但如果你刚提交完、暂存区空了,再点提交就会把所有改动一锅端。我的习惯是先手动暂存,确认暂存列表里只有预期文件,再写提交信息,不要依赖自动暂存。
4.2 提交信息的输入体验和提交模板
在面板顶部的输入框写提交信息,按Cmd + Enter提交。这里有两个提升效率的小技巧。
第一,如果你想写多行提交信息(标题 + 详细说明),按输入框右侧的展开图标,会变成一个多行文本框。团队如果用 Conventional Commits(feat:/fix:/docs:这种前缀),建议在仓库里放一个提交模板文件.gitmessage:
# <类型>: <简短描述> # 类型可选:feat / fix / docs / style / refactor / test / chore # # 详细说明(为什么改,而不是改了什么)然后配置:
git config --local commit.template .gitmessageVSCode 的提交框目前不会自动加载模板内容(命令行git commit不带-m时会加载),所以模板更多是给命令行兜底用。
第二,输入框支持范围暂存按钮。写提交信息时,输入框右上角会多出"暂存所有更改"和"暂存所有更改并提交"两个按钮。第二个按钮非常方便,但它是"全量暂存 + 提交",用之前务必看一遍改动列表。
4.3 分支切换、合并与冲突解决:图形界面真正省事的地方
左下角状态栏会显示当前分支名,点一下会弹出分支列表,可以切换、新建、从某个提交建分支。新建分支时 VSCode 会问"从哪里创建",默认是当前 HEAD,可以选其他分支或某个具体标签,这个选择器比记git switch -c 名字 起点直观。
冲突是新手最怕的场景,而图形界面对这块的提升最明显。合并或拉取产生冲突后,冲突文件在面板里会被标记为!,打开文件会看到这样的标记块:
<<<<<<< HEAD 当前分支的内容 ======= 对方分支的内容 >>>>>>> feature/xxx上方会出现一行操作链接:采用当前更改 / 采用传入更改 / 采用两者 / 比较更改。点"比较更改"会打开三向合并编辑器,左边是你的、右边是对方的、中间是合并结果,可以逐块接受,也可以手动编辑。
我的经验是:简单的"二选一"冲突用链接按钮就够了,复杂冲突一定进三向编辑器,因为中间结果区可以手动改,改完直接保存,比在纯文本里删<<<<<<<标记可靠得多。全部冲突解决后,把文件逐个暂存,VSCode 会提示"所有冲突已解决",这时才能提交。没暂存完就提交,Git 会拒绝——这是保护机制,不是 bug。
4.4 拉取和推送的时机:先拉后推,别硬推
面板底部的同步按钮(两个箭头转圈那个)在git.confirmSync: false时点一下就直接拉取 + 推送。听起来爽,但有个前提:本地和远程没有分叉。如果两边都有新提交,同步会触发一次合并,可能直接把冲突带进来。
我自己的习惯是拆开做:先用...菜单里的"拉取"(Pull),确认无冲突后再"推送"(Push)。分支后面带↑2表示本地领先 2 个提交,↓1表示落后 1 个。
关于 pull 的行为,前面全局配了pull.rebase false,即"拉取时合并"。这个选择对新手的意义是:合并会产生一个合并提交,历史是网状但信息完整;变基会把你的提交搬到对方提交之上,历史是一条直线但会重写提交哈希。个人项目、单人分支我倾向变基,多人共享分支我倾向合并。VSCode 在git.pullRebase为 true 时,拉取会走变基路径。改法:
git config --global pull.rebase true # 想用变基 git config --global pull.rebase false # 想用合并5. 图形界面搞不定的时候,回到终端才是正解
5.1 哪些操作我从来不指望图形界面
VSCode 的 Git 集成覆盖了日常八成操作,但有几类事它做起来别扭,甚至做不了:
- 交互式变基(
git rebase -i):压缩、重排、改写历史提交。命令行会拉起一个编辑器让你写pick/squash/reword,图形界面基本无能为力。 - 挑选提交(
git cherry-pick):面板里没有入口,得用命令。顺带一提,cherry-pick 后如果冲突,git cherry-pick --abort是唯一的退路,这个命令值得记牢。 - 找回误删的提交:见下一节。
- 仓库体检与清理:
git gc、git fsck、.git目录瘦身,这些必须命令行。
所以我把 VSCode 内置终端(Ctrl + `)当成标配。它默认打开的就是当前项目目录,Git 的图形界面和命令行共享同一个仓库状态,你在终端里的任何操作,面板会立刻刷新,两边混着用没有问题。
5.2 撤销这件事必须分清三个命令
这是我在带新人时发现最容易搞混的地方,做个表说清楚。
| 命令 | 影响范围 | 典型用途 | 是否安全 |
|---|---|---|---|
git restore <文件> | 只丢工作区改动 | 改乱了想还原 | 会丢改动,谨慎 |
git restore --staged <文件> | 只把它移出暂存区 | 误 add 了文件 | 内容仍在工作区 |
git revert <提交> | 生成一个反向提交 | 已推送的提交要撤销 | 安全,保留历史 |
git reset --hard <提交> | 移动分支指针并清空 | 本地未推送的提交撤销 | 会丢提交,靠 reflog 救 |
VSCode 面板里右键文件能看到"放弃更改"(对应git restore)和"取消暂存"(对应git restore --staged),这两个够用了。revert和reset我基本都在终端里做。
万一reset --hard用错了怎么办?别慌,只要提交过就不算丢。用:
git reflog它会列出 HEAD 移动的完整历史,包括刚才那次 reset。找到目标提交的哈希,git reset --hard <哈希>或git branch 救回来 <哈希>就能回来。默认保留 90 天(不可达对象 30 天),时间相当充裕。这个技巧救过我不止一次。
5.3 值得装的几个 Git 相关插件
VSCode 自带的 Git 面板够用,但下面几个装了之后体验会上一个台阶:
| 插件 | 主要作用 | 适合谁 |
|---|---|---|
| GitLens | 行内 blame、提交历史、文件历史对比 | 需要频繁追溯"这行谁改的" |
| Git Graph | 可视化提交图,支持在图上建分支、cherry-pick | 想看整体历史脉络 |
| Git History | 单文件历史、按行查看演变 | 排查某文件何时被改动 |
GitLens 的行内 blame 有个副作用:每行末尾会多出一段灰色的作者信息,有人觉得干扰阅读。可以在设置里关掉:
{ "gitlens.currentLine.enabled": false, "gitlens.hovers.currentLine.over": "line" }保留悬停查看、关掉常驻显示,是我用下来比较舒服的平衡点。Git Graph 特别提醒一句:它虽然能在图上直接操作分支和 cherry-pick,但这类操作本质是写历史,动手前先确认当前没有未提交的改动,否则容易把自己绕进去。
6. 几个我踩过、且身边人几乎都会踩的坑
6.1 换行符:一个改动导致整个文件"全变了"
Windows 用 CRLF,macOS 和 Linux 用 LF。如果团队里有人用 Windows,而你没配好换行符策略,可能出现"我只改了一行,diff 却显示整个文件都变了"。原因是 Git 把行尾差异也算进了改动。
全局配置里我们写了core.autocrlf input,含义是:提交时把 CRLF 转成 LF,检出时不转换。在 Mac 上这是比较合适的取值。它的对照关系是:
| 取值 | 提交时 | 检出时 | 适用平台 |
|---|---|---|---|
input | CRLF → LF | 不转换 | macOS / Linux |
true | CRLF → LF | LF → CRLF | Windows |
false | 不转换 | 不转换 | 全平台一致的项目 |
如果仓库里已经混进了 CRLF,光配autocrlf不够,还需要在仓库根目录加.gitattributes来"一锤定音":
* text=auto *.sh text eol=lf *.png binary* text=auto让 Git 自动判断文本文件并统一成 LF;*.sh text eol=lf强制脚本文件用 LF,否则在 Mac 上执行会报bad interpreter;*.png binary明确告诉 Git 别对二进制文件做任何转换,避免图片损坏。
6.2.DS_Store和那些不该进仓库的文件
macOS 的 Finder 会在访问过的目录里写入.DS_Store,记录图标位置这类信息。如果不忽略,它会被git add .一起提上去,然后每次打开文件夹都可能变,造成无意义的提交。
在项目根目录建.gitignore,写清楚:
.DS_Store **/.DS_Store node_modules/ dist/ .env *.log .idea/ .vscode/settings.json其中**/.DS_Store是递归匹配,只写.DS_Store在某些情况下匹配不到子目录。另外设个全局忽略文件更省心,省得每个仓库都写一遍:
# 建立全局忽略文件 echo ".DS_Store" >> ~/.gitignore_global git config --global core.excludesfile ~/.gitignore_global注意:
.vscode/settings.json是否忽略要看团队约定。如果团队希望统一格式化规则,反而应该把它提交上去。我个人的做法是:只忽略.vscode里和本机路径强相关的部分,比如git.path。
6.3 中文文件名显示成一串八进制
如果git status里中文文件名显示成"\346\226\207\344\273\266.md"这种样子,就是core.quotepath的默认行为在作怪。前面配的git config --global core.quotepath false就是解决它的。
这个问题在图形界面下表现略有不同:VSCode 一般显示正常,但你在终端里看到的和面板里看到的"不一样",容易误以为文件被改名了。统一配好就没事。
另外还有一个更隐蔽的坑:macOS 文件系统用 NFD 编码存中文,也就是"é"可能被存成"e + 组合符"。如果同一个中文名文件在 Linux 上提交、Mac 上检出,Git 可能认为这是两个不同文件,导致重复。core.precomposeunicode true就是把这个行为拉齐,中文项目建议必配。
6.4 权限变化也会触发"无改动却显示已修改"
macOS 是类 Unix 系统,Git 会记录文件的执行位(executable bit)。如果某个文件从 644 变成 755,即使内容一字未改,git status也会显示为已修改。常见的触发场景是把文件从 Windows 拷过来、或者从压缩包里解压出来。
判断方法:
git diff # 如果输出类似 old mode 100644 / new mode 100755,就是权限问题处理方式有两种:一是不想让 Git 管权限,直接关掉这个跟踪:
git config --global core.filemode false二是确实需要保留执行权限(比如脚本),那就正常提交这次权限变更。
这里还有个细节:core.filemode是仓库级配置,在 Mac 上 clone 的仓库默认就是true。上面用--global只是给以后新建的仓库设默认值,对已有仓库要进去单独配一次,或者用git config --local core.filemode false。
7. 我平时维护这套环境的一些小习惯
配置这东西,配一次能用很久,但有几个习惯让我少踩了不少坑,顺手记一下。
第一,把~/.gitconfig备份到自己的仓库里。换电脑时git clone下来软链过去就行,比重新回忆配了哪些项快得多。注意这个仓库必须是私有的,gitconfig里可能有邮箱和工作相关的别名配置。
第二,升级 Git 之后顺手跑一次git config --list --show-origin。Homebrew 升级偶尔会调整默认路径或配置文件位置,看一眼能确认自己那份配置还在生效,尤其是credential.helper这类容易被覆盖的项。
第三,VSCode 的 Git 面板和终端不要"两边同时动手"。它们共享同一份仓库状态,但界面的刷新有延迟。如果你在终端里git stash之后立刻在面板里点提交,可能点的是一个已经过时的文件列表。稳妥做法是操作完在终端敲git status确认状态,再回面板操作。
第四,钥匙串里如果存了旧凭据,会和新密钥打架。表现是 HTTPS 方式总是认证失败、或者一直用错账号。打开"钥匙串访问",搜索托管平台域名,把旧条目删掉即可。这个排查方向很多人想不到,白白折腾半天 SSH 配置。
第五,多账号场景下,判断"我现在用的是哪个身份"的最快方式是这个组合:
git config user.email # 当前仓库生效的邮箱 git remote -v # 当前 remote 用的主机别名两行对上就说明身份没错。提交前扫一眼,比提交完发现作者错了再改历史省事太多。