很多人的 Windows 机器上,右键菜单里同时躺着“Git Commit -> master”“SVN Commit”“Git Sync”“SVN Update”四个长得几乎一样的图标,点哪一个全凭记忆。这不是个例,而是把 Gitee 仓库、Git 同步、TortoiseGit 和 TortoiseSVN 这四件事混成一团之后的典型状态。我自己最早也是这么过来的:想着“反正都是版本控制工具,装上总没坏处”,结果第一次多人提交就撞上冲突没人敢解,第一次换电脑备份发现推上去的是半个月前的代码。这篇东西就是把这套组合拳拆开讲清楚——Gitee 上的仓库怎么建、本地代码怎么规范地推上去、两三个人怎么不打架地协作、一个人怎么把 Git 当版本化网盘做备份,以及两个 Tortoise 客户端里那些文档不会写、但一定会咬你一口的坑。不管你是刚接触 Git 的学生、要接手团队仓库的新人,还是只想给自己几年的代码留个后路的独立开发者,下面这些内容都能直接照着做。
1. 先分清三个东西的活儿:Gitee 仓库、Git 同步、两个 Tortoise 客户端
1.1 标题里为什么同时出现 TortoiseGit 和 TortoiseSVN
这两个软件的图标都是乌龟,名字只差三个字母,很多人以为其中一个是另一个的新版。完全不是。TortoiseGit 是 Git 的 Windows 图形外壳,它自己不具备版本控制能力,本质上是把git.exe的命令行参数翻译成右键菜单和对话框;TortoiseSVN 是 Subversion 的客户端,走的是另一套完全独立的版本控制模型。之所以在一个标题里并列出现,通常源于三种真实场景:一是老项目还在 SVN 上,新项目迁到 Gitee 的 Git 仓库,机器上两个都得留;二是 Gitee 对仓库提供了多种访问入口,有人图省事用 SVN 客户端去拉 Git 仓库;三是纯粹被“都是乌龟”误导装重了。
我的建议很直接:新开的项目,只装 TortoiseGit,不要碰 TortoiseSVN。Git 是分布式的,本地有完整历史,断网也能提交、能看日志、能回滚,这对个人备份场景几乎是刚需;SVN 是集中式的,提交必须连服务器,本地只有一个.svn目录记录基线。两者的心智模型差别太大,混着用最容易出的事故是:把一个目录同时用两种客户端打开,生成互相干扰的元数据目录,最后谁都不认谁。
提示:判断一个工作副本属于哪种,看根目录下的隐藏文件夹就够了。有
.git的是 Git 仓库,每个子目录里都有.svn的是 SVN 工作副本。两者同时存在,说明这个目录已经被污染了,别硬修,重新检出更省时间。
1.2 装机的正确顺序与依赖关系
TortoiseGit 的安装包不含 Git 本体,这是新人最容易卡住的地方。正确顺序是:先装 Git for Windows,再装 TortoiseGit,最后装中文语言包。
Git for Windows 安装过程中有一个关键选项——“Adjusting your PATH environment”,默认是“Git from the command line and also from 3rd-party software”,保持默认不要改。这个选项会把git.exe注册进系统 PATH,TortoiseGit 安装后才能自动找到它。我见过有人为了“保持环境干净”选了最保守的那一项,结果装完 TortoiseGit,右键菜单全在,但每次操作都弹“git.exe not found”,排查半天以为是权限问题。
TortoiseGit 安装时会有一步让你填写用户名和邮箱,可以跳过,装完在设置里统一改。语言包必须和 TortoiseGit 主程序的版本号对应,32 位、64 位也不能错配,装错了的表现是界面一半中文一半英文,或者设置对话框直接打不开。
装完之后立刻做一件事:右键任意空白处 → TortoiseGit → Settings → Git,把 Name 和 Email 填上。这里的 Email 必须和 Gitee 账号绑定的邮箱一致,否则提交记录里的作者信息无法和账号关联,仓库的贡献图是空的,别人也搞不清这次提交是谁做的。这个设置是全局的,改一次对所有本地仓库生效。
1.3 一个必须提前拍板的决定:HTTPS 还是 SSH
推代码到 Gitee 有两条路,选错了后面每次操作都要多敲一遍密码。
| 对比项 | HTTPS | SSH |
|---|---|---|
| 地址形式 | https://gitee.com/用户名/仓库名.git | git@gitee.com:用户名/仓库名.git |
| 认证方式 | 账号 + 密码/访问令牌 | 密钥对 |
| 首次配置成本 | 低,直接克隆 | 需要生成密钥并在平台登记公钥 |
| 长期使用体验 | 凭据失效后反复弹窗 | 配好之后基本无感 |
| 网络环境适应性 | 一般较稳 | 部分受限网络下握手会慢 |
| 团队机器公用场景 | 不推荐,凭据会串 | 相对可控 |
单人长期使用,我倾向 SSH;临时在别人机器上拉个代码,HTTPS 更省事。
SSH 这条路有一个TortoiseGit 特有的坑:它默认使用自带的TortoisePlink.exe做 SSH 连接,而TortoisePlink只认 PuTTY 格式的.ppk私钥,不认 OpenSSH 生成的id_rsa。所以用ssh-keygen生成的密钥,在 TortoiseGit 里会报“Unable to open connection / Disconnected: No supported authentication methods available”。
两个解法,任选其一:
方案一,改 TortoiseGit 用的 SSH 客户端。Settings → Network → SSH client,把路径指向 Git 自带的ssh.exe,通常在C:\Program Files\Git\usr\bin\ssh.exe。
方案二,用 PuTTYgen 把id_rsa转成.ppk,然后在 Settings → Git → Remote 里给 origin 指定这个 ppk 文件。
# 生成密钥对,一路回车即可 ssh-keygen -t ed25519 -C "your_mail@example.com" # 查看公钥内容,整段复制到平台的 SSH 公钥设置页 cat ~/.ssh/id_ed25519.pub # 测试连通性,看到 success 之类的字样就算通了 ssh -T git@gitee.com公钥是给别人看的,私钥(不带.pub后缀的那个文件)绝对不能外传,也不能提交进仓库。
2. 从零把一个本地目录推上 Gitee:建仓、初始化、首次提交
2.1 仓库创建时的四个勾选项决定了后面省多少事
在 Gitee 上点“新建仓库”,表单里除了仓库名还有几个选项,草率勾选会在后面还债。
开源还是私有。个人备份代码、公司内部工具,一律选私有。私有仓库对外不可见,成员需要手动邀请。开源仓库任何人都能看、能提 Issue,适合真的想公开的东西。改这个属性在仓库设置里可以调,但一旦公开过,历史记录就被爬走了,撤销不回来。
是否初始化仓库。这里有个判断题:本地已经有代码目录了,就不要勾“使用 Readme 文件初始化这个仓库”。勾了会在远端生成一个初始提交,你本地推上去的时候两边历史不相关,会被拒绝,然后就得先git pull --allow-unrelated-histories再处理合并,多一堆事。本地已有代码,就老老实实建一个空仓库。
默认分支名。新仓库现在普遍用main,但很多本地 Git 默认还是master。这两个名字不一样,第一次推送时会出现“远端有 main,本地推的是 master”的分裂局面,仓库里莫名其妙多出一个分支。要么在本地执行git branch -M main改名,要么在建仓时就把默认分支设成master,统一最重要。
.gitignore 模板和许可证。建仓时可以直接选模板,比如 Visual Studio、Python、Node,能省掉手写。许可证对私有备份仓库没意义,对开源项目很重要,别乱选。
2.2 本地目录“接管”的两种做法与取舍
把已有代码变成一个 Git 仓库,有两种路径。
第一种,先有本地再有远端,我个人更常用。在代码目录空白处右键 → TortoiseGit → “Git Create repository here”,勾上“Make it Bare”以外的默认项,目录里会多出一个.git文件夹。此时这个目录就是仓库根,右键菜单会出现“Git Commit -> master”。提交完再在 TortoiseGit 里添加远端地址、推送。
第二种,先克隆远端再搬代码。右键 → Git Clone,填远端地址,选一个空目录。克隆完把老代码拷进去,注意别把旧目录里的.git(如果有)一起拷过去,两个.git嵌套会让整个仓库行为诡异。
两种做法结果一样,区别在于:克隆法会顺带把远端的初始文件(比如自动生成的 Readme、License)带到本地,减少一次合并;本地建仓法更适合一个已经跑了很久、文件结构复杂的目录,因为它不会往你的目录里塞额外文件。
仓库根的选择这条要单独说:一个 Git 仓库最好只对应一个项目。我见过有人把D:\工作整个设成仓库根,下面塞了三个项目、两个素材包、一堆安装包,结果git status一跑出来几千个未跟踪文件,提交的时候手一抖全推上去,仓库体积直接爆掉。
2.3 .gitignore 写不好,后面全是体力活
.gitignore是仓库根目录下的一个纯文本文件,作用是在提交时忽略匹配到的路径。它写不写,直接决定了你每次提交是“勾三个文件”还是“在一千个文件里翻找”。
写之前先搞清楚两件事:编译产物和依赖目录不用进仓库,因为它们能重新生成;本地配置和含密码的文件不能进仓库,因为泄露了很麻烦。
下面这份是通用起点,按语言栈裁剪:
# 编译输出 bin/ obj/ dist/ build/ target/ # 依赖 node_modules/ vendor/ .venv/ __pycache__/ # IDE 与本地配置 .vs/ .idea/ *.user *.suo *.local # 环境与密钥 .env *.pem secrets.json # 系统垃圾 Thumbs.db .DS_Store # 日志与临时文件 *.log *.tmp一个必须强调的点:.gitignore只对未被跟踪的文件生效。如果某个文件已经被提交过一次,后来才把它写进.gitignore,Git 会继续跟踪它。正确做法是先把它从索引里移除,再提交。
# 从版本控制中移除但不删除本地文件 git rm -r --cached node_modules git rm --cached .env # 然后提交这次变更 git commit -m "chore: 停止跟踪依赖目录与本地配置"TortoiseGit 也有对应操作:右键文件 → TortoiseGit → “Delete (keep local)”,效果等同,但要注意对话框提示,别手快点成普通删除。
2.4 首次推送在 TortoiseGit 里的完整点击路径
假设本地目录已经初始化并完成过一次提交,现在要推上去。
第一步,绑定远端。右键 → TortoiseGit → Settings → Git → Remote,Remote 填origin(这是约定名),URL 填仓库地址,Putty Key 视你用的是 SSH 还是 HTTPS 而定。填完点 Add/Set 保存。
第二步,推送。右键 → TortoiseGit → Push,界面里确认 Local 分支是master或main,Remote 分支留空表示同名,点 OK。第一次推 HTTPS 会弹登录框,输入账号密码;如果平台已启用令牌机制,密码位置要填访问令牌而不是登录密码,这一点经常把人卡死——反复正确输入密码却一直提示认证失败,八成是这里。
第三步,验证。刷新仓库页面,文件都在,提交信息也正确,就看提交作者头像有没有亮起来。没亮通常是邮箱没对上,回第 1.2 节改全局配置,后续提交会恢复正常,历史提交改起来麻烦,早期发现早期处理。
推送失败最常见的三种提示和处理:
| 提示关键字 | 真实原因 | 处理方式 |
|---|---|---|
rejected / non-fast-forward | 远端有你本地没有的提交 | 先 Pull(推荐 rebase 模式)再 Push |
Authentication failed | 密码位置该填访问令牌 | 清掉本地凭据后重新输入 |
Could not read from remote | 远端地址或 SSH 配置错 | 检查 Remote URL 与 SSH client 设置 |
unrelated histories | 两边历史毫无关联 | 合并时允许无关历史,或重建仓库 |
3. 多人开发:分支怎么分、冲突怎么解、谁有权合
3.1 三个人以上就该放弃“人人推 main”
两个人还能靠喊一嗓子协调,三个人以上直接推主干就是灾难片。我刚接触协作那会儿,团队四个人共用一个master,每天的日常是:A 推完 B 推不上去,B 强行拉取合并出一个满是冲突的提交,C 的改动被覆盖,D 的代码回滚时连带删了别人的文件。三天之后没人敢 push。
稍微正经一点的做法是主干保护 + 功能分支:
main(或master):只放可运行、可发布的代码,禁止直接推送feat/功能名:每个需求一条分支,从main拉出,做完合回去fix/问题名:修 bug 用,命名带上 Issue 编号更好追溯hotfix/xxx:线上紧急问题,从当前发布版本拉出
分支命名统一之后,看提交历史一眼就知道谁在干什么。TortoiseGit 里创建分支:右键 → TortoiseGit → “Create Branch”,填分支名,勾上“Switch to new branch”会顺便切过去。切换分支:右键 → “Switch/Checkout”,选目标分支。
合回主干时不要用本地直接 Merge 再推。走合并请求(Pull Request)流程,让另一个人看一眼代码再合。评审这件事在小团队里常被嫌麻烦,但它的真实价值不在“抓 bug”,而在于让至少两个人知道这段代码改了什么,将来出问题时有人能接手。
3.2 TortoiseGit 的冲突合并界面实操
冲突是绕不过去的。它的本质很简单:同一个文件的同一块区域,两个人改了不一样的内容,工具没法替你判断该留哪个。
先记住一条铁律:动手解冲突之前,先确认自己的工作区是干净的。有未提交的改动去拉代码,冲突会叠加在未提交内容上,最后连哪些是自己的都分不清。TortoiseGit 里可以看“Check for modifications”,把该提交的提交、该丢弃的丢弃。
冲突发生的典型现场:你点 Pull,弹出一个列表,显示若干文件 Conflict。此时这些文件里被塞进了<<<<<<<、=======、>>>>>>>三组标记。
右键冲突文件 → TortoiseGit → “Edit conflicts”,会打开三栏合并工具:
- 左栏Mine:你本地的版本
- 中间Base:两人修改之前的共同祖先
- 右栏Theirs:远端来的版本
- 下方:合并结果
操作上就三个动作:选中某一行点“Use this text block”取左边或右边;两边都想要就依次取;两边都不要就手删。全部处理完保存,回到资源管理器右键 → “Resolve”,勾选这个文件标记为已解决,然后提交。提交信息里写清楚“合并 xxx 分支,解决 xxx 文件冲突”,将来排查时这个信息很值钱。
对于配置文件这类“两边都加了新条目”的冲突,别偷懒直接选一边。正确做法是把两边的条目手工拼起来,然后本地跑一遍确认能启动,再提交。
提示:合并过程中如果彻底乱了,右键 → TortoiseGit → “Revert”,可以把文件恢复到冲突发生前的状态,重新来一遍。这是唯一一个可以无脑按的“后悔键”,前提是你还没点提交。
3.3 保护分支与合并请求的实际约束力
平台上的“保护分支”设置不是装饰。它能把“禁止直接推送主干”这条口头约定变成硬约束。配置项一般是:谁能推送、谁能合并、是否需要通过评审、是否需要流水线通过。
我的实际配置习惯:
- 主干:只允许管理员合并,禁止直接 push,合并后自动删除源分支
- 开发分支:允许开发者推送,允许强制推送(自己的分支,改历史无所谓)
- 发布标签:只允许管理员创建
“合并后自动删除源分支”这个开关特别值得打开。不打开的话,一年之后仓库里躺着两百条feat/xxx分支,谁也不知道哪条还有用。
合并请求的描述不要写“update”“修改”这种废话。写三件事:改了什么、为什么改、怎么验证。这三行将来在git log里翻出来,比任何文档都管用。
3.4 权限角色与“误删分支”的补救
仓库成员一般分几档角色,管理员能改设置能删仓库,开发者能推代码能提合并请求,观察者只能看。给权限的原则是最小可用:实习生进来先给观察者,等他熟悉流程再升开发者;离职当天移除成员,不要靠“他应该不会再登了”这种侥幸。
真删错了分支怎么办?分两种情况。
删的是本地分支:右键 → TortoiseGit → “Show log”,顶部勾上“All Branches”,找到那条分支最后一次提交,右键 → “Create Branch at this version”,重新起个名字就回来了。
删的是远端分支:本质上是删掉了一个指针,提交对象还在服务器上(平台有回收策略,通常保留一段时间)。如果能记得最后一次提交的哈希值,在日志里找到它,重新建分支并推送即可。所以关闭合并请求时删分支是对的,删之前确保已经合并,这个顺序不能反。
还有一个更常见的事故:提交了不该提交的东西(密码、密钥、客户数据)。注意,光删除文件再提交一次是不够的,历史里还留着。正确姿势是立刻去平台把那个密钥作废重置,然后视情况清理历史。作废永远比清理重要,因为清理历史这件事,只要你已经推送过,就不能假设没人拉走过。
4. 个人代码备份:把 Git 当版本化网盘的那些门道
4.1 一次性仓库 vs 一项目一仓库
单人做备份,第一个要决定的是仓库粒度。
一项目一仓库:每个项目独立,提交历史清晰,clone 下来就是完整项目,符合 Git 的设计初衷。缺点是仓库数量多,几十个项目就要建几十个仓库,找起来费劲。
一个大仓库装所有小项目:建一个私有仓库,下面按目录分项目A/、项目B/、脚本/、笔记/。好处是只有一处要维护,备份脚本写一份就够。缺点是任何一次提交都会让所有目录的历史纠缠在一起,将来想把某个项目单独拎出去,得用git filter-repo这类工具拆,拆起来不算愉快。
我的实际做法是折中:长期的、有明确生命周期的项目,一项目一仓库;零碎的脚本、单文件工具、学习笔记,合并进一个“杂物仓库”。判断标准很简单——这个项目将来会不会想单独分享给别人?会,就独立建仓。
另外强烈建议:所有个人备份仓库一律设成私有。哪怕代码里没有敏感信息,很多零碎文件里也会夹带路径、账号、内部地址。私有仓库的代价几乎为零,公开的代价可能是不可逆的。
4.2 备份脚本:开机拉取 + 关机推送
靠手动点右键备份,坚持不过两周。人就是这样,写代码的时候想不起来提交,等想起来的时候已经改了几百行,一次提交变成一锅粥,提交信息也懒得写,最后索性不提交了。
让机器干这件事。Windows 下用任务计划程序配两个触发器:开机后延迟两分钟执行一次拉取(保证多台机器之间同步),关机前执行一次提交推送。
一个能直接用的批处理骨架:
@echo off setlocal set REPO=D:\code\backup-repo cd /d "%REPO%" git add -A rem 没有变更就跳过,避免产生空提交报错 git diff --cached --quiet if %errorlevel%==0 ( echo 没有需要备份的变更 goto :pull ) git commit -m "auto backup" if errorlevel 1 goto :end pushd "%REPO%" git push origin main popd :pull git pull --rebase origin main :end endlocal几个容易翻车的细节。
git commit -m里不要用%date%和%time%。Windows 中文环境下这两个变量展开后带斜杠、冒号、星期几的中文,提交信息被截断甚至命令解析出错。真想要时间戳,用 PowerShell 生成:
$stamp = Get-Date -Format "yyyy-MM-dd HH:mm" Set-Location D:\code\backup-repo git add -A git diff --cached --quiet if ($LASTEXITCODE -ne 0) { git commit -m "auto backup $stamp" git push origin main } git pull --rebase origin maingit pull --rebase里的--rebase很关键。多人或者多机场景下,默认的 merge 模式会造出一堆“Merge branch 'main' of ...”的垃圾提交,历史图变成毛线团。rebase 会把你的本地提交重新接到远端最新提交之后,历史保持一条直线。
多台机器同步的时候还有一个隐雷:同一份代码在两台机器上都被改过,且改的是同一个文件。自动脚本在这个场景下必然冲突,而没有人在旁边处理冲突。所以备份仓库里最好不要放会同时在两台机器上编辑的文件,或者干脆给每台机器一条分支(pc-work、home-laptop),各自推各自的,需要的时候再手工合。
4.3 大文件与仓库体积失控的处理
Git 存的是完整快照,对文本极其友好,对二进制文件极不友好。一个 50MB 的设计稿改十次,仓库就多出 500MB 的历史,而且这些历史删不掉——你可以删掉工作区的文件,但历史提交里的对象还在,git clone的时候全都要拉下来。
平台的仓库容量是有上限的,超出之后推不上去,表现是推送到 99% 然后失败,非常折磨人。
处理办法按优先级排:
第一,从源头拦住。在.gitignore里把*.zip、*.psd、*.mp4、*.iso、*.exe、*.dll、*.db这类扩展名直接忽略。真的需要版本管理的大文件,用 Git LFS(大文件存储),平台支持的话配置一下即可,它会把大文件替换成指针,实际内容存在单独的存储区。
第二,定期看仓库体积。平台的仓库设置页一般能看到大小统计。超过几百 MB 就该查一查是哪个目录惹的祸。
第三,已经撑爆了怎么救。两条路:一是用git filter-repo从历史里彻底剥离大文件目录,然后强制推送覆盖远端——这一步会影响所有协作者,必须在大家都知情的情况下做;二是放弃历史,重建仓库:新建一个空仓库,把当前工作区文件重新提交推上去,旧仓库归档。对个人备份来说,第二条路又快又安全,反正你要的是“现在的代码”,不是“三年前那次误操作的完整记录”。
# 查一下仓库里体积最大的几个对象,定位元凶 git rev-list --objects --all | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | sort -k3 -n -r | head -205. 图形化工具最容易咬人的几个地方:报错、乱码、图标打架
5.1 凭据错乱:改了密码之后一直推不上去
场景很典型:平台改了密码或者开了二次验证,本地推送一直失败,输入新密码也没用。原因是 Windows 凭据管理器里存着旧密码,TortoiseGit 优先用缓存的凭据,你的输入根本没有生效。
清理路径:控制面板 → 用户账户 → 凭据管理器 → Windows 凭据,找到git:https://gitee.com这一条(可能有多条),删掉。然后重新执行一次推送,会重新弹认证框。
如果平台启用了访问令牌机制,那个输入框里“密码”位置要填的是令牌,不是登录密码。令牌一般在平台的“设置 → 私人令牌”里生成,生成时勾选权限范围,生成后只显示一次,记得先存下来。很多人第一次被这个卡住,反复怀疑自己记错了密码。
5.2 中文文件名显示成八进制串
执行git status或者看 TortoiseGit 的变更列表,一堆\344\270\255\346\226\207.txt这样的东西,完全看不懂。这不是乱码,是 Git 默认把非 ASCII 文件名转义显示了。
一行配置解决:
git config --global core.quotepath false改完 TortoiseGit 需要重启一下资源管理器(任务管理器里重启explorer.exe),或者干脆注销重登,否则变更列表还是老样子。
真正的内容乱码是另一回事,通常发生在用记事本打开 UTF-8 文件、或者团队里有人用 GBK 编码保存文件的时候。根治办法是在仓库根目录放一个.gitattributes,统一声明编码和换行策略,让所有机器行为一致:
* text=auto *.bat text eol=crlf *.sh text eol=lf *.cs text working-tree-encoding=UTF-85.3 换行符 CRLF 造成的“全文件都改了”
这是跨平台协作最经典的坑,没有之一。现象是:你只改了三行代码,提交上去显示改了四千行;或者别人拉下来,整个文件全变红。
原因是换行符。Windows 默认用 CRLF(回车+换行),Unix 系用 LF。Git 在中间做转换,配置不对就会来回抖动,每次检出都把你的文件整体重写一遍。
三个配置项的价值:
| 配置 | 含义 | 适用场景 |
|---|---|---|
core.autocrlf=true | 提交时转 LF,检出时转 CRLF | Windows 单机或混合团队 |
core.autocrlf=input | 提交时转 LF,检出不变 | 主要在类 Unix 环境 |
core.autocrlf=false | 完全不做转换 | 全团队同平台且已有统一约定 |
Windows 上一般这样设:
git config --global core.autocrlf true但要清楚,autocrlf是本地配置,管不了别人。真正一劳永逸的办法是.gitattributes里的* text=auto,它随仓库走,所有人拉下来规则一致。两者配合使用,冲突会少很多。
已经抖动过的文件怎么恢复?如果只是工作区被改了但没提交,TortoiseGit 里右键 → Revert 就能恢复。如果已经提交了一次“全文件变更”,那就比较麻烦,得 revert 那个提交再重新改,别硬着头皮往下推。
5.4 TortoiseGit 和 TortoiseSVN 的图标覆盖互相抢占
装了这两个之后,你会发现文件夹上的绿勾红叹号变得时有时无,或者某个目录明明有修改却一直显示绿勾。原因在 Windows 的一个历史限制:资源管理器的图标覆盖处理器只支持有限数量(大约十五个),谁在注册表里排前面谁生效。
两个 Tortoise 各自注册了一组覆盖图标,加起来轻松超过上限,排在后面的直接被系统忽略。表现就是“状态不同步”。
调试方法:打开注册表编辑器,定位到HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers,下面会列出一堆名字。Windows 按名称的字母顺序加载,靠前的优先。
如果想让 TortoiseGit 优先,可以在它那几项的名字前面加一个空格(比如把TortoiseGit1改成TortoiseGit1),空格在 ASCII 里排在字母前面,它就跑到最前了。
注意:改注册表前先导出备份。改完需要重启资源管理器才生效。如果不想折腾注册表,更省事的做法是只留一个客户端——既然新项目都用 Git,就把 TortoiseSVN 卸载掉,图标覆盖的冲突自然消失。
另外还有一个性能相关的设置值得调:TortoiseGit → Settings → Icon Overlays → Status cache,默认是“Default”,在大仓库(几万个文件)下会让资源管理器卡顿。改成“Shell”能缓解,改成“None”则完全不显示图标但最流畅,适合那种只用它推代码、不靠图标判断状态的场景。
5.5 Revert 和 Reset 不是一回事,按错就丢代码
这两个词在中文界面里都容易被理解成“撤销”,实际后果天差地别。
Revert(还原):作用对象是工作区里尚未提交的修改。右键文件 → TortoiseGit → Revert,文件回到最后一次提交的状态,你刚才的改动没了。这个操作不可撤销,但影响范围仅限你手动选中的文件。
Reset(重置):作用对象是提交历史。在 Show log 窗口里右键某个历史提交 → “Reset 'main' to this...”,有三个模式:
- Soft:只移动分支指针,改动全部保留在暂存区
- Mixed:移动指针,改动保留在工作区但不暂存
- Hard:移动指针,工作区文件一并被覆盖,未提交的改动直接蒸发
Hard是最危险的那个,它不问你有没有未提交的东西。我见过最惨的一次是:同事想退回上一个版本看看效果,随手点了 Hard,两个小时没提交的改动全没了,因为他以为“和 Revert 差不多”。
保住一条命的知识点:Git 里误操作的提交,通常还能捞回来。git reflog会记录 HEAD 的每一次移动,包括 Reset 之前的位置。
# 查看最近的操作记录,找到 Reset 之前那个提交的哈希 git reflog # 回到那个提交 git reset --hard <哈希值>注意,reflog救的是已提交的内容。工作区里没提交就被 Hard 覆盖的文件,找不回来(除非 IDE 有本地历史)。所以真正的保命习惯是:动手做任何 Reset 之前,先 Commit 一次,哪怕提交信息写“临时存档”。有提交就有一切。
6. TortoiseSVN 在这个组合里的真实位置
6.1 什么情况下仓库会同时给 SVN 入口
不少代码托管平台为了照顾从 SVN 迁移过来的团队,会为 Git 仓库额外提供一个 SVN 兼容访问地址,让习惯了 TortoiseSVN 的人可以继续用检出、更新、提交这一套操作,后台再转换成 Git 提交。仓库页面上一般能在“克隆/下载”按钮里切换协议,看到形如svn://或带svn路径的地址。
这个能力适合过渡期:团队里一半人还没学会 Git,先用 SVN 客户端接入,慢慢迁。但它有几个明显的短板,值得提前知道:
- 分支和标签的语义对不上。Git 的分支是轻量指针,SVN 的分支是目录拷贝。通过兼容层访问时,分支通常映射成特定路径,操作方式和原生 SVN 不完全一样。
- 提交历史的展示会变形。合并提交、多父提交这类 Git 特有能力,在 SVN 客户端里看不出来。
- 部分高级操作不支持。变基、拣选提交、子模块这些,走 SVN 入口基本没法用。
我的判断标准:过渡期用可以,长期用不建议。既然远端是 Git 仓库,本地就用 Git 客户端,工具和模型对齐,少一层翻译就少一层坑。
6.2 SVN 与 Git 的工作模型差异
理解差异,才能理解为什么“两个客户端指向同一个目录”必然出事。
SVN 是集中式的,每个子目录下都有一个.svn文件夹,里面存着这个目录的基线信息。提交时必须连服务器,提交成功后版本号 +1,全局递增。优势是模型简单、权限可以精确到目录、大二进制文件处理相对友好;劣势是断网基本没法工作,分支操作笨重,合并靠人肉。
Git 是分布式的,整个仓库只有一个.git目录在根上,本地有完整历史。提交是本地行为,秒完成;推送才是联网动作。分支几乎零成本,所以才有“一个需求一条分支”这种用法。
这个差异直接带来一个实操差异:SVN 的“更新”会覆盖你本地未提交的改动(冲突时给标记),Git 的“拉取”在本地有未提交改动时通常直接拒绝。Git 拒绝其实是保护你,但新人容易理解成“工具坏了”。
6.3 不要把两种客户端指向同一个工作目录
最后强调一遍这条,因为它的破坏性是静默的。
一个目录被 TortoiseGit 检出(有.git),又被 TortoiseSVN 检出(有.svn),两个工具会各自维护一套状态。你在 Git 里提交了,SVN 那边还显示“已修改”;你在 SVN 里更新,Git 检测到一堆文件变更。更糟的是.gitignore和 SVN 的忽略规则互不相认,一方忽略的文件另一方照样跟踪,最后这个目录没法解释自己处于什么状态。
正确的隔离方式:不同项目用不同目录,物理上分开。真要在同一台机器上同时维护两类项目,就按盘符或顶层文件夹划分,比如D:\git-projects和D:\svn-projects,右键菜单点哪个心里有数。
如果已经踩进去了,别试图手工删.svn目录来“修复”。删了之后 SVN 客户端会认为整个工作副本损坏,Git 那边也可能因为残留文件产生奇怪的未跟踪项。最省时间的做法是:把有改动的文件拷出来备份,删掉整个目录,重新检出,再把文件放回去。
我个人在这套组合上折腾几年下来,最有价值的一条经验是:把“提交”这个动作的频率提上去,把“推送”这个动作交给脚本。人一旦开始纠结“这次改动够不够格提交”,就会拖延,而拖延是备份失效的第一步。提交信息写得不漂亮不要紧,auto backup 2024-05-11 21:30这种也比没有强。真正需要认真写信息的,只有合并到主干前的那一次。
另一个小心得是,给每个仓库的根目录放一个README.md,第一行写清楚这个仓库是干什么的、属于谁、什么情况下可以删。半年后你自己回头看,会感谢当时写了这行字的自己。