1. 开始之前:先把 Git 项目的底层逻辑理清楚
我第一次带团队的时候,见过无数个从零起步的项目,最后都卡在同一个问题上:代码没有任何版本管理,改坏了就回不去,多人协作靠微信传压缩包,文件名后面挂一串 v1、v2、final、final2。 这种情况持续到项目第三周就会彻底失控,谁改了哪一行、为什么改、怎么回滚,全靠记忆。所以那些真正值得长期维护的项目,第一步永远是同一个动作:创建 Git 项目。
这篇内容不是 Git 命令大全,而是围绕"git项目创建"这一个动作,讲清楚从本地初始化、远程仓库对接、分支规范到问题排查的完整链路。适合刚入门的新手,也适合已经被 git 折腾过几回、想把自己工作流理顺的半熟手。看完之后,你可以直接照着一套标准流程,把一个项目从零搭到能多人协作的稳定状态。
我先把一个最重要的认知讲明白:git 项目创建不是一个命令,是一套约定。 git init 只是把当前目录变成仓库,真正让项目"立得住"的,是你在创建阶段就定下来的目录结构、忽略规则、分支策略、提交规范。这些东西没定清楚,后面每一条命令都会踩坑。
拿生活里的事情类比,Git 项目创建就像买房之后做水电改造。房子是你的本地目录,git 是水电管线,远程仓库是接出来的市政管网。你可以在毛坯房里住,但等到入住之后再凿墙埋线,成本至少翻三倍。同样,项目写了两万行代码再补 git init、补 .gitignore,要么漏掉一堆本该忽略的文件,要么历史记录里全是垃圾提交。
更关键的是,Git 项目创建阶段决定的、后续很难改的东西,恰恰是最容易被忽略的。比如分支命名约定、commit 风格、标签规则。这些不属于命令层面,属于团队契约层面。所以我建议每个项目开始之前,不管是一个人做还是几个人协作,都要花二十分钟把下面这套流程走完。
如果你现在手上还没有项目,我建议用某个模拟项目 X(一个简单的 Web 服务)跟着实操一遍。有真实代码在跑,比空记命令有效得多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备:装好 Git、配好身份,别让细节拖后腿
2.1 Git 安装和终端选择
这一步听起来简单,但不同平台差别还是不小。Linux 系的发行版多数自带或者一条包管理命令就能装上,Windows 上推荐走官方安装包,macOS 上如果你装了 Homebrew,一条命令也能解决。装完以后,先确认版本。
bash复制git --version
版本只要不是太老,比如 2.20 以上,日常功能都没问题。但我见过实际项目里有人还在用七八年前的版本,结果 git switch、git restore 这种命令用不了,只能用老式的 git checkout,协作时还容易因为新旧版默认行为不同而踩坑。所以我个人的建议是:装完顺手升级到当前稳定版,尤其是团队里有多个新人加入的时候,避免老命令与新习惯互相干扰。
终端的选择也会影响实际操作体验。Windows 下我强烈建议直接用 Windows Terminal 加 PowerShell,或者 Git Bash,尽量不要用系统自带的旧版 cmd。原因很简单,Git 输出的是 ANSI 彩色信息,旧终端渲染得很难看,而且路径粘贴之类的交互体验很差。macOS 和 Linux 用户直接用系统自带终端就可以,配置一下 zsh 别名,效率会高很多。
2.2 配置用户信息:谁提交的,要对得上号
Git 项目创建后第一笔提交前,有两个配置必须设置:user.name 和 user.email。这俩决定了提交记录上显示的作者。如果不设置,Git 会用系统默认值,最终提交记录里会出现一堆乱七八槽的机器名,跟踪责任的时候会让人头大。
有两种设置范围:全局(--global)和仓库级(--local)。个人电脑上建议设好全局,但有个容易踩的坑:如果你同时给多个公司或者多个平台贡献代码,全局邮箱可能把你的项目提交关联到错误的账户上。
有一种很实用的做法是,在主目录下手动创建一个 ~/.gitconfig 文件,然后在里面用条件包含,按不同目录自动切换身份。比如你本地代码都放在 ~/work/ 和 ~/personal/ 下面,就可以针对两个目录分别配置名字和邮箱。这样换项目不会忘改配置,提交归属也能保证正确。
还有一种常见操作:在某个仓库里发现提交者信息错了,想改当前仓库的配置。我通常直接用命令:
bash复制git config user.name "你的名字"
git config user.email "你的邮箱"
如果想验证一下当前仓库生效的配置到底是哪个,可以加 --show-origin 查看配置来源,这个参数非常适合排查"我明明改了全局邮箱,怎么提交还是旧邮箱"这类问题。
2.3 SSH 密钥:远程仓库连接的第一道关
配置 SSH 密钥,本质上是为了免密推送。如果你不想每次 push 都输入账号密码,同时又希望连接更安全,绝大多数远程平台都支持 SSH。生成密钥的常用命令如下:
bash复制ssh-keygen -t ed25519 -C "你的邮箱"
我一直推荐用 ed25519,密钥长度短、生成快、安全性也够用,而且现代平台的兼容性早就没问题了。如果你需要兼容一些老旧系统,退而求其次用 RSA 4096 也行,但没必要作为首选。
生成之后,会把私钥留在 ~/.ssh/id_ed25519,公钥是 ~/.ssh/id_ed25519.pub。把公钥内容贴到远程平台的安全设置里,然后本地测试连接:
bash复制ssh -T git@某个平台域名
第一次连接会提示确认指纹,输入 yes 就行。这个指纹是平台服务器的公钥,确认一次后记录在 ~/.ssh/known_hosts 里,以后不会再问。
这里有个很多人忽视的细节:多个平台或者多个账号共用一个私钥,其实是可以的,但推送时很容易搞混身份。 如果你有多个 Git 平台账号,最好在 ~/.ssh/config 里为不同 host 指定不同的私钥文件。比如:
bash复制Host work
HostName 某个平台域名
User git
IdentityFile ~/.ssh/id_ed25519_work
Host personal
HostName 另一个平台域名
User git
IdentityFile ~/.ssh/id_ed25519_personal
这样从哪个目录操作、推到哪个平台,都清晰可查。我见过太多人把私钥串用,最后自己都不知道提交归属到了哪个账号。
3. 本地仓库初始化:从 git init 到第一次提交
3.1 项目目录结构和 git init 的时机
很多人初始化 Git 项目的时候,直接在项目根目录敲 git init 就完事。这个做法本身没问题,但有一个前提:你得先想好哪些文件该进版本库,哪些不该进。
我在真实项目里见过一个高频错误:项目刚开始,开发人员图省事,在根目录执行 git init,然后把整个目录里的依赖包、临时文件、日志文件全部提交了。这会导致两个结果:第一,仓库体积迅速膨胀,每次克隆都痛苦;第二,将来的操作稍微涉及依赖目录,git 就变得非常慢。正确做法是,先创建项目目录,但不要急着提交所有文件,先把忽略规则写好再提交第一次。 关于忽略规则,下一小节细说。
另外还有一个实际场景值得注意:你的项目可能不是单一仓库,而是多个子项目并行,比如前端、后端、工具脚本。这种情况下,我建议每个子项目单独建仓库,不要放在一个大仓库里强行管理。除非你们刻意采用 monorepo 策略并且有配套工具,否则多个项目的提交节奏完全不同,混在一个仓库里会让历史变得难以阅读。
执行初始化的命令很简单:
bash复制mkdir 模拟项目X
cd 模拟项目X
git init
初始化成功之后,默认分支名可能是 master,也可能是 main,取决于你的 Git 版本和配置。现在主流实践已经切换到 main,因为这个词语义更中性,而且远程平台默认也都用它。如果你手里的仓库还显示 master,想换成 main,新仓库可以这样改:
bash复制git branch -m main
这条命令在还没有提交任何内容的时候执行最合适,因为一旦有提交、有分支引用,改名就会牵连出一堆追踪关系。所以我的建议是:在 git init 之后,立刻把默认分支名设定成 main,作为项目创建流程的标准动作。
3.2 .gitignore 的写法:不把垃圾文件交给仓库
.gitignore 是本地仓库创建阶段最重要的文件之一,它决定哪些文件被 Git 忽略,不进入版本控制。很多人一开始不理解它的价值,直到某天把密钥文件提交上去,或者把几 GB 的编译产物推到远程,才追悔莫及。
写 .gitignore 最直接的逻辑是:凡是能从别处还原、生成、下载的文件,一律忽略。 比如不同语言的依赖目录、构建产物、临时文件、日志文件。下面是一个简单的模板,我以模拟项目 X 的 Java 服务和前端资源目录为例:
gitignore复制# 依赖目录
node_modules/
target/
vendor/
# 构建产物
dist/
build/
out/
# 环境配置
.env
.env.local
# 日志
*.log
logs/
# IDE 和系统文件
.idea/
.vscode/
*.iml
.DS_Store
# 临时文件
*.tmp
*.cache
个人经验里,最容易漏掉的是 .env。你在本地调试必须的数据库密码、第三方密钥,一不小心就会搭着首次提交一起进仓库。Git 历史一旦包含敏感信息,就算后来删除,也还能在历史记录里翻出来,强制删除历史又是一套复杂操作。所以从项目创建第一天起,就把环境类文件列入忽略名单。
还有一个小技巧,如果你不确定某个文件是否被忽略,可以运行:
bash复制git check-ignore -v 某个文件路径
它会告诉你是哪条忽略规则匹配到了这个文件。这个命令比 git status 直观得多,排查"为什么这个文件没被提交"非常高效。
3.3 第一次提交:让基线清晰可见
初始化完成、忽略规则写好后,可以先把所有文件加入暂存区,然后提交。第一次提交是整个仓库的基线,它要把项目入口、说明文档、最小可运行代码都包含进去,但不需要一次提交无限多的功能。
建议先补一份 README,写清楚这个项目是干什么的、怎么启动、目录结构。README 是项目的第一份说明书,你不写,别人接手的时候就得靠猜。然后执行:
bash复制git add .
git commit -m "chore: 初始化项目,添加基础工程结构"
这里我用了带类型前缀的提交信息,属于常规的提交规范,后面会展开讲。回到第一次提交本身,有两点经验想分享:
第一,确认没有多余文件混进来再提交。执行 git add . 前,先用 git status 检查一遍,把不该进去的文件排除掉。如果已经 add 了一部分,可以用 git reset 把暂存区清掉重来,不用慌。
第二,第一次提交不建议空仓库加一个空文件就完事。至少要有 README 和项目骨架,最好有一句能够跑通的最小逻辑。这样后续的任何改动都有对照物,定位问题也容易。如果你后面想改提交信息,可以用:
bash复制git commit --amend -m "新提交信息"
但注意,这条命令会改写提交历史,只适合在提交还没推送的情况下用。推送到远程之后再用 amend,就会造成历史分叉,处理起来麻烦得很。
4. 远程仓库对接:创建、关联、推送的完整闭环
4.1 远程仓库的创建要点
本地仓库建好了,接下来要把它和远程仓库对接。远程仓库的作用有两个:一是异地备份,二是多人协作的枢纽。我们在平台上创建一个新项目仓库时,有几个选项要特别注意。
仓库可见性。 个人项目默认私有就好,团队项目按公司规范来。别因为手滑选了公开,把还没完善的代码暴露出去。是否自动添加 README 和许可证。 如果你刚开始用平台自带功能初始化仓库,它可能会顺带生成一个 README 和许可证文件,这意味着远程已经有了初始提交。后续你本地推送时,就需要先处理 "远端有历史、本地也有历史" 的分叉问题。
我个人的习惯是:远程仓库选择完全不生成任何文件,纯空仓创建。这样本地推上去就是唯一的初始历史,简单干净,不需要额外合并。如果你已经选了自动生成,那也问题不大,后面加一条 pull 合并的操作就能解决。
4.2 关联远程仓库:remote 的相关坑
本地仓库和远程仓库之间的连接,通过 git remote 管理。添加远程地址的命令:
bash复制git remote add origin git@某个平台域名:用户名/仓库名.git
origin 是这个远程仓库在本地的别名,约定俗成当作主远程。你可以继续添加其他远程,比如测试环境、镜像仓库等等,这都没问题。需要注意的是,如果你用的是不同平台的私有部署,远程地址的结构可能不一样,但要保证路径准确。 我见过最典型的报错就是地址里多了个空格、少了个斜杠,或者域名写错成旧地址。
查看当前的远程仓库配置,可以运行:
bash复制git remote -v
这个命令会列出所有远程地址的 fetch 和 push 地址。每次配完 remote,我都习惯跑一遍这个命令确认,别指望配置过程一定正确。
还有一个实际场景:仓库迁移。比如某个项目从旧平台迁到新平台,远程地址变了,常见做法是:
bash复制git remote set-url origin 新地址
如果旧地址已经不再使用,也可以删除再添加:
bash复制git remote remove origin
git remote add origin 新地址
两种方式选一种就行,我倾向于用 set-url,因为不会中断短暂的连接状态。
4.3 推送并建立追踪关系
远程关联好之后,第一次推送需要明确分支和远程的追踪关系。如果你本地分支是 main,远程还没有任何分支,使用:
bash复制git push -u origin main
这里的 -u 是 --set-upstream 的缩写,意思是为本地分支设置上游追踪分支。设置之后,后续提交完可以直接 git push 或者 git pull,不用再带参数。第一次推送是最容易出问题的环节,因为可能遇到远程已有文件、权限不足、网络不通等意外。我挑两个最常见的展开。
如果远程仓库在创建时自动生成了 README,推送时会收到 "failed to push some refs" 的提示,原因就是两端历史没有关联。解决办法是先拉取并合并:
bash复制git pull origin main --allow-unrelated-histories
这个参数的意思是允许两个不相关的历史进行合并。如果你本地和远程都是完全独立的初始提交,不加这个参数 Git 会拒绝合并。合并完成后会产生一次合并提交,然后再推送。这里顺便说一句:如果你对远程仓库没有任何提交权限,推送也会被拒绝,报错信息通常是权限不足。 这种问题不是命令能解决的,去找管理员开通权限即可。
第一次推送成功后,后续每次提交后执行 git push,Git 会直接推到 main 分支。如果多人协作,我会习惯先 git pull --rebase 再推送,目的是在别人提交了新内容时,先把自己的改动变基到最新提交之上,避免出现多余的合并记录。这个习惯会直接影响提交历史的整洁度,后面详细展开。
5. 分支策略与提交规范:多人协作的契约
5.1 分支模型怎么选
本地远程都打通了,一个 Git 项目创建工作就算完成了七成,剩下三成是团队协作层面的约定。很多人忽略这部分,结果就是所有人在 main 上直接提交,冲突不断、历史混乱、发布时只能祈祷。
最常见的分支模型有两种。一种是简单的主干开发模式:所有人都在 main 上提交,用 tag 标记发布版本。这种方式适合个人项目或者两三个人维护的小项目,简单直接,但需要每个人都自律,代码评审比较难做。
另一种是功能分支模式:main 保持稳定,所有新功能从 main 拉出特性分支,开发完成后通过合并请求回到 main。这种模式适合正式项目和团队协作,能保证 main 随时处于可发布状态,同时让评审流程有了落地空间。
我推荐大多数人从第二种模式开始,哪怕只有一个人开发也建议这么做。因为隔离分支带来的风险极低,而好处是每次合并都有明确的上下文。比如:
bash复制git checkout -b feature/用户登录
做一段时间后合并回 main:
bash复制git checkout main
git pull
git merge --no-ff feature/用户登录
--no-ff 的作用是保留特性分支的完整历史和合并节点,不用默认的快进合并。虽然会让历史多一条合并记录,但好处是能看出某个功能是在哪个节点合进来的。如果团队比较重视简洁线性历史,也可以用 rebase 方式合并,但成本是对协作规范要求更高。
5.2 提交信息规范:让别人看历史像读文档
提交信息是 Git 项目创建之后持续维护的一部分,它不像初始化那样一次搞定,但必须从一开始就定好规则。我这里非常推荐约定式提交风格,格式大概是这样:
code复制<type>: <description>
type 常用 feat(新功能)、fix(修复)、chore(构建与工具改动)、docs(文档)、refactor(重构)、test(测试)等。举个例子:
bash复制git commit -m "fix: 修复模拟项目X在空目录下启动报错的问题"
为什么推荐这个规范?因为 Git 历史最终会成为项目的一份自动更新的 Changelog。你按类型、按模块搜索提交记录的时候,一眼就能看到这个提交做了什么,不需要点开 diff 来猜。很多项目还集成自动化工具,根据提交信息自动生成版本号或者发布说明。如果提交信息写得乱七八糟,这套自动化的基础就没了。
我还有一个实用习惯:提交信息里的描述使用祈使句、动词开头。不用纠结语法,只要让读者明白"这一个提交做了什么改动"就行。例如 fix: 增加参数校验,避免空指针异常 就比 代码修正 清晰得多。
5.3 代码评审与合并请求流程
分支模型确定之后,代码评审就要挂在合并流程里。现在主流平台都有合并请求功能,本质上是把一个分支的改动申请合并到目标分支,并附带讨论、审查、自动化检查。对于 Git 项目创建来说,这一步是在仓库设置里配置的:比如是否要求至少一名成员评审通过才能合并、自动化测试是否必须通过。
我见过一些开发者的抵触情绪,觉得合并请求很麻烦,打断编码节奏。但换个角度想,代码评审不是为了挑刺,而是用最低成本的方式让第二双眼睛帮你发现潜在问题。 尤其是一个项目逐渐变大之后,任何一次失误都可能影响线上服务,评审的价值就是提前拦截这些问题。
实际操作上,我自己的习惯是在特性分支开发到一个相对完整的阶段后,先推送分支,然后在平台创建合并请求。描述里写清楚这次改了什么背景、怎么测试、影响范围是什么。如果你的团队有模板,就按模板来;没有模板,自己写这么一段也花不了几分钟。
合并策略这块,不同平台有不同的选项,常见的是合并提交、压缩合并、变基合并。我给新手团队的推荐是压缩合并:把特性分支的一堆小提交压缩成一个干净的提交合并回 main,历史看起来清爽。缺点是这个功能分支的细节提交记录会丢失,但大多数时候我们并不需要保留那些中间过程。
6. 常见问题与排查实录:那些新手一定会踩的坑
为了让你实操的时候少走弯路,我把真实项目里遇到最多的一批问题整理成速查表,每个问题都附上排查思路和处理方式。
| 问题现象 | 问题原因 | 处理方式 |
|---|---|---|
| push 时提示 non-fast-forward | 本地提交落后于远程提交 | 先 git pull --rebase,解决冲突后再 push |
| 提交后代码不显示作者 | 未设置 user.name / user.email | git config user.name "某开发者",然后把提交者信息补上 |
| commit 漏掉文件想补进上一个提交 | 没有 amend 的概念 | git add 漏掉的文件 然后 git commit --amend |
| 误提交了大文件、敏感文件 | 初期 .gitignore 没写全 | 从当前目录移出文件,提交删除记录,再改写历史清理旧记录 |
| 切换分支时本地修改被带到别的分支 | 工作区或暂存区有未处理改动 | git stash 暂存改动,切完分支后 git stash pop 恢复 |
| 删除远程分支误操作 | 分支名写错或没确认清楚 | 只要分支没被强制推送覆盖,通常在远程平台可以恢复 |
| 看到有一堆 Merge branch 的记录 | pull 时默认生成合并提交 | 改用 git pull --rebase,保持历史线性 |
| 仓库体积异常大 | 把依赖和构建产物提交进了历史 | 用工具重写历史并清理缓存,后续让所有人重新克隆 |
这些问题的共同点,是它们都源于对 Git 工作方式的误解。Git 里 checkout、reset、revert 的区别,很多人用过很多次还是分不清。我用一句话总结我自己的理解:checkout 是切换指针,reset 是移动指针并可能丢弃改动,revert 是生成一次新的反向提交来撤销。 三个命令应用场景不同,但都建立在"提交记录是一条时间线"这个认知之上。
还有关于冲突的处理,其实没有那么恐怖。合并产生冲突时,Git 会在冲突文件里标出两方的改动区块。你在编辑器里打开文件,看到的是一段段的 < <<<<<<< HEAD、=======、>>>>>>> 分支名 标记,手动选择保留哪边、合并成什么样子,然后执行:
bash复制git add 冲突文件
git commit
就可以了。很多人卡在冲突上是因为心理压力,觉得是不是自己操作出了问题。实际上冲突是 Git 的正常提示,说明它不知道你希望保留哪个版本,需要你告诉它。
7. 扎实的 Git 项目创建习惯,值得长期坚持
创建 Git 项目并不难,真正决定项目质量的是创建阶段是否把基础设施定好了。我个人这几年带项目,每个新仓库都走固定的流程:先写 README 和 .gitignore 再提交初始化代码,分支模型和提交规范提前定下来,SSH 配置一次到位,远程仓库用空仓起步。这套流程熟练之后,整套操作不到半小时,但给后面节省的时间远不止这些。
最后分享一个小技巧:每次在 Git 操作前,先 git status 看一眼当前状态。 很多人直接敲命令然后被各种报错吓到,其实 Git 的提示已经写明该怎么办了,只是没有 git status 做铺垫,你会漏掉很多上下文。这个习惯听起来很简单,真正坚持下来的人,几乎很少在版本管理上出大问题。
如果你手头正好有一个新项目要启动,别急着写功能,先把上面这套创建流程走一遍。半小时的投资,换后面几个月的顺心,这笔账怎么算都划算。
