作者:全栈陈序员 系列:《Git版本控制从入门到精通》 适用人群:零基础到入门进阶 阅读时间:约 18 分钟
大家好
前两篇我们打牢了概念地基、装好了 Git 环境。本篇终于要亲手创建第一个 Git 仓库了!
我们会完成一个最完整、最真实的新手体验:
- 用
git init把一个普通目录变成 Git 仓库; - 深入剖析
.git目录里到底装了什么; - 跑通"创建文件 → add → commit → log → 修改 → 再提交"的完整闭环;
- 讲清
git init的常用选项; - 把一个已有项目纳入 Git 管理;
- 讨论
mastervsmain的历史与切换; - 介绍 README.md 和开源协议的写法与选择。
跟着敲一遍,你就真正"用上 Git"了。
一、两种创建仓库的方式
Git 创建仓库有两种主要方式:
| 方式 | 命令 | 场景 |
|---|---|---|
| 在现有目录初始化 | git init |
把本地已有项目纳入 Git 管理,或从零开始新建项目 |
| 克隆远程仓库 | git clone <url> |
已有远程仓库(如 GitHub 上的项目),想下载到本地 |
本篇重点讲 git init,因为它是理解"仓库是怎么来的"的基础。git clone 涉及远程操作,会在第19篇详细讲。
二、git init 详解
2.1 创建项目目录并初始化
我们从一个空目录开始:
# 创建项目目录
$ mkdir my-first-repo
# 进入目录
$ cd my-first-repo
# 初始化 Git 仓库
$ git init
Initialized empty Git repository in /Users/chensen/my-first-repo/.git/
输出告诉我们:在当前目录下创建了一个 .git 子目录,仓库已初始化。
2.2 查看目录结构
$ ls -la
total 0
drwxr-xr-x 3 chensen staff 96 Nov 2 10:00 .
drwxr-xr-x 20 chensen staff 640 Nov 2 10:00 ..
drwxr-xr-x 10 chensen staff 320 Nov 2 10:00 .git
注意:.git 是隐藏目录 (以点开头),普通 ls 看不到,需要 ls -la。这个目录就是 Git 仓库的"心脏",所有版本数据都存在这里。
2.3 .git 目录结构详解
$ ls -la .git
total 64
drwxr-xr-x 10 chensen staff 320 Nov 2 10:00 .
drwxr-xr-x 3 chensen staff 96 Nov 2 10:00 ..
-rw-r--r-- 1 chensen staff 138 Nov 2 10:00 HEAD
-rw-r--r-- 1 chensen staff 23 Nov 2 10:00 config
-rw-r--r-- 1 chensen staff 147 Nov 2 10:00 description
drwxr-xr-x 2 chensen staff 64 Nov 2 10:00 hooks/
-rw-r--r-- 1 chensen staff 4096 Nov 2 10:00 index # 暂存区文件(首次init后可能还没有)
drwxr-xr-x 2 chensen staff 64 Nov 2 10:00 info/
drwxr-xr-x 3 chensen staff 128 Nov 2 10:00 logs/
drwxr-xr-x 4 chensen staff 128 Nov 2 10:00 objects/
drwxr-xr-x 4 chensen staff 128 Nov 2 10:00 refs/
逐个说明:
| 文件/目录 | 作用 |
|---|---|
HEAD |
指向当前所在分支,内容如 ref: refs/heads/main |
config |
当前仓库的 local 级配置(仅对本仓库生效) |
description |
仓库描述(供 GitWeb 使用,一般不用管) |
hooks/ |
钩子脚本目录(pre-commit、post-commit 等),第27篇详讲 |
index |
暂存区的二进制文件,记录哪些文件将被提交 |
info/ |
全局排除文件(info/exclude,类似 .gitignore 但不提交) |
logs/ |
引用变更日志(git reflog 用,第24篇详讲) |
objects/ |
所有 Git 对象的存储(commit、tree、blob),是版本库核心 |
refs/ |
引用(分支、标签)的存储,refs/heads/ 存分支,refs/tags/ 存标签 |
关键认知:
.git目录就是仓库本身 。你可以把工作区的文件全删了,只要.git还在,所有历史都能恢复。- 千万不要手动改
.git里的东西(除非你确切知道在做什么),用 Git 命令操作。 .git不要提交到另一个 Git 仓库(不要嵌套仓库),否则会出问题。
2.4 查看 HEAD 内容
$ cat .git/HEAD
ref: refs/heads/main
说明当前在 main 分支(如果你的 Git 默认分支是 main)。如果还没提交过,refs/heads/main 这个文件还不存在,提交后才会创建。
2.5 查看 config 内容
$ cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
这是仓库级配置,刚 init 时只有一些默认项。你可以用 git config --local 添加仓库特有配置。
三、第一个完整工作流
现在我们跑通 Git 最核心的闭环:工作区 → 暂存区 → 版本库。
3.1 创建第一个文件
# 创建 README.md
$ echo "# My First Repo" > README.md
# 查看文件
$ cat README.md
# My First Repo
此时文件在工作区,但 Git 还不知道它。查看状态:
$ git status
On branch main
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
README.md
nothing added to commit but untracked files present (use "git add" to track)
Untracked files 表示 Git 发现了一个新文件 README.md,但还没追踪它。Git 友好地提示你用 git add 把它加入暂存区。
3.2 加入暂存区
$ git add README.md
再次查看状态:
$ git status
On branch main
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: README.md
文件状态从 Untracked 变成了 Changes to be committed(待提交),说明它已进入暂存区。
3.3 提交到版本库
$ git commit -m "初始提交:添加README"
[main (root-commit) 3e92b4a] 初始提交:添加README
1 file changed, 1 insertion(+)
create mode 100644 README.md
解读输出:
[main (root-commit) 3e92b4a]:在main分支创建了根提交 (root-commit,即第一个提交),提交哈希是3e92b4a(简写,完整是40位);1 file changed, 1 insertion(+):1个文件变更,1行新增;create mode 100644 README.md:新建了文件README.md,权限模式 100644。
3.4 查看提交历史
$ git log
commit 3e92b4a8e5f1c2d9b7a3f6e8d4c2b1a9f0e7d6c5 (HEAD -> main)
Author: 全栈陈序员 <chensen@example.com>
Date: Mon Nov 2 10:05:00 2026 +0800
初始提交:添加README
git log 显示所有提交历史。当前只有一个提交,包含:完整哈希、作者、日期、提交信息。
按
q退出 log 的分页浏览。
3.5 修改文件再提交
模拟日常开发:修改文件,再提交一次。
# 修改 README.md
$ echo "" >> README.md
$ echo "这是一个用于学习 Git 的示例项目。" >> README.md
# 查看内容
$ cat README.md
# My First Repo
这是一个用于学习 Git 的示例项目。
查看状态:
$ git status
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
no changes added to commit (use "git add" and/or "git commit -a")
modified 表示已追踪的文件被修改了,但还没暂存。
查看差异:
$ git diff
diff --git a/README.md b/README.md
index 6e0c4c5..b3d7e8a 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,3 @@
# My First Repo
+
+这是一个用于学习 Git 的示例项目。
git diff 显示工作区与暂存区的差异:新增了2行(1空行+1内容行)。
暂存并提交:
$ git add README.md
$ git commit -m "完善README说明"
[main d3f1a2b] 完善README说明
1 file changed, 2 insertions(+)
再次查看历史:
$ git log
commit d3f1a2b9c4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9 (HEAD -> main)
Author: 全栈陈序员 <chensen@example.com>
Date: Mon Nov 2 10:10:00 2026 +0800
完善README说明
commit 3e92b4a8e5f1c2d9b7a3f6e8d4c2b1a9f0e7d6c5
Author: 全栈陈序员 <chensen@example.com>
Date: Mon Nov 2 10:05:00 2026 +0800
初始提交:添加README
现在有两条提交记录,最新的在上。
3.6 美化日志输出
$ git log --oneline
d3f1a2b 完善README说明
3e92b4a 初始提交:添加README
$ git log --oneline --graph --all
* d3f1a2b 完善README说明
* 3e92b4a 初始提交:添加README
--oneline 每个提交一行,--graph 显示分支图,--all 显示所有分支。日常推荐用这个组合,或者用上一篇配的 git lg 别名。
四、git init 的常用选项
4.1 指定初始分支名:-b
$ mkdir repo2 && cd repo2
$ git init -b main
Initialized empty Git repository in /Users/chensen/repo2/.git/
-b main 让初始分支直接叫 main,省去后续改名麻烦。需要 Git 2.28+。
4.2 裸仓库:--bare
$ mkdir my-project.git && cd my-project.git
$ git init --bare
Initialized empty Git repository in /Users/chensen/my-project.git/
裸仓库没有工作区 ,只有 .git 目录的内容直接放在当前目录。用途是作为远程中央仓库(服务器上的共享仓库),开发者往这里 push/pull。
裸仓库的目录名习惯以
.git结尾,如my-project.git。后面讲远程仓库时会用到。
4.3 自定义模板:--template
$ git init --template=/path/to/my-template
用自定义模板初始化,模板里的 hooks、配置等会被复制到新仓库的 .git 中。适合企业统一初始化仓库规范。
4.4 分离对象目录:--separate-git-dir
$ git init --separate-git-dir=/path/to/external-git-storage
把 .git 的对象存储放到外部目录,工作区只留一个指针文件。适合大仓库或特殊存储需求。
五、将现有项目纳入Git管理
实战场景:你手上已经有一个项目目录,想给它加上版本控制。
# 假设已有项目
$ cd ~/projects/legacy-app
$ ls
src/ config/ README start.sh
# 初始化
$ git init
Reinitialized existing Git repository in /Users/chensen/projects/legacy-app/.git/
# 查看状态:所有文件都是 Untracked
$ git status
On branch main
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
README
config/
src/
start.sh
nothing added to commit but untracked files present (use "git add" to track)
重要:在 add 之前,先创建 .gitignore,避免把编译产物、日志、密钥等不该版本化的东西加进去:
# 创建 .gitignore
$ cat > .gitignore << 'EOF'
# 编译产物
*.class
target/
build/
# IDE 文件
.idea/
.vscode/
*.iml
# 日志
*.log
logs/
# 系统文件
.DS_Store
Thumbs.db
# 敏感配置
config/local.properties
.env
EOF
然后分批添加并提交:
# 添加所有文件
$ git add .
# 查看将要提交的内容(确认没有误加敏感文件)
$ git status
# 提交
$ git commit -m "chore: 将现有项目纳入Git管理"
铁律 :第一次
git add .前一定要检查git status,确认没有密钥、密码、大文件。一旦提交了敏感信息,即使后面删除,历史里仍然有,清除很麻烦(需git filter-branch或 BFG)。
六、初始分支名:master vs main
6.1 历史背景
Git 从诞生起,默认初始分支名一直是 master。这个词源自 BitKeeper 的 "master repository"(主仓库),本身并无种族含义。
2020年5月,Black Lives Matter 运动兴起,科技界开始反思术语中的种族敏感词。master / slave 这对术语(主从)被认为有奴隶制暗示。GitHub 在 2020年6月宣布:
- 新建仓库的默认分支名从
master改为main; - 旧仓库不强制改,但提供改名工具。
Git 官方从 2.28 起支持配置默认分支名,但为兼容性仍默认 master。GitHub/GitLab 平台默认已改为 main。
6.2 如何修改默认分支名
配置 Git 全局默认分支名(推荐新装环境做):
$ git config --global init.defaultBranch main
之后所有 git init 默认创建 main 分支。
已有仓库改名:
# 把 master 改名为 main
$ git branch -m master main
# 如果有远程,更新远程的默认分支
$ git push -u origin main
# 删除远程旧分支
$ git push origin --delete master
6.3 该用哪个?
- 新项目 :用
main,顺应社区约定,避免争议; - 老项目 :如果团队没意见,保持
master也没问题,不必为了改名而改名; - 跟随平台 :GitHub/GitLab 新建仓库默认
main,本地保持一致最省心。
本系列统一使用 main。
七、README.md 与 LICENSE
一个规范的仓库,根目录至少要有两个文件:README.md 和 LICENSE。
7.1 README.md 的作用与写法
README 是项目的"门面",是别人(包括未来的你)了解项目的第一入口。GitHub/GitLab 会在仓库主页自动渲染 README.md。
一个好的 README 应包含:
# 项目名称
一句话简介:这个项目是做什么的。
## 功能特性
- 特性1
- 特性2
- 特性3
## 快速开始
### 环境要求
- Node.js >= 18
- Python >= 3.10
### 安装
```bash
npm install
运行
npm start
使用示例
const lib = require('your-lib');
lib.doSomething();
项目结构
src/ 源代码
docs/ 文档
tests/ 测试
贡献指南
欢迎提 Issue 和 PR。提交前请阅读 CONTRIBUTING.md。
开源协议
MIT License - 详见 LICENSE
联系方式
作者:全栈陈序员 邮箱:chensen@example.com
**写好 README 的原则**:
1. **开头一句话说清是什么**,别让人猜;
2. **快速开始要真能跑通**,步骤清晰、命令准确;
3. **配示例代码**,比千言万语管用;
4. **保持更新**,过时的 README 比没有更糟。
### 7.2 开源协议选择
LICENSE 文件声明项目的开源协议,决定别人能怎么用你的代码。常见协议:
| 协议 | 关键限制 | 适用场景 |
|------|---------|---------|
| **MIT** | 几乎无限制,保留版权声明即可 | 最流行,适合大多数个人/开源项目 |
| **Apache 2.0** | MIT + 显式专利授权 + 修改需声明 | 适合涉及专利的企业级项目(如 Kubernetes) |
| **GPL v3** | 衍生作品必须也开源(传染性) | 适合希望强制开源的软件(如 Linux 内核) |
| **LGPL** | 修改库本身要开源,但可被闭源软件链接 | 适合库/框架 |
| **BSD 2/3-Clause** | 类似 MIT,措辞略有不同 | 学术项目常见 |
| **Mozilla (MPL)** | 修改的文件要开源,可混合闭源 | Firefox 等 |
| **Unlicense / CC0** | 放弃所有权利,完全公有 | 想完全无约束的小工具 |
**MIT 协议示例(LICENSE 文件):**
MIT License
Copyright (c) 2026 全栈陈序员
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**选择建议**:
- 个人项目、想最大化传播:**MIT**;
- 公司项目、有专利考量:**Apache 2.0**;
- 希望衍生品也必须开源:**GPL v3**;
- 不确定:先用 MIT,最宽松最省心。
> GitHub 在新建仓库时可直接选择协议并自动生成 LICENSE 文件。也可用 `choosealicense.com` 网站辅助决策。
---
## 八、常见问题FAQ
**Q1:`git init` 后,目录里除了 `.git` 什么都没有,是不是失败了?**
A:没有失败。`git init` 只创建 `.git` 目录,不会创建任何业务文件。你的工作区文件由你自己创建。
**Q2:能不能在一个仓库里再 `git init`?**
A:可以,会提示 `Reinitialized existing Git repository`,不会丢失已有数据。但不要在仓库里嵌套另一个仓库(子目录再 init),会混乱。需要子仓库请用 `git submodule`(第30篇)。
**Q3:误删了 `.git` 目录怎么办?**
A:`.git` 没了,所有历史就没了,无法恢复(除非有远程备份)。所以 `.git` 极其重要,但**不要把它加到 .gitignore**------它本来就不该被追踪,Git 会自动忽略它。
**Q4:`git init` 创建的仓库和 GitHub 上创建的仓库什么关系?**
A:本地 `git init` 创建的是纯本地仓库,没有远程。GitHub 上创建的是远程仓库。通常流程是:本地 init → 在 GitHub 建空仓库 → `git remote add origin <url>` → `git push -u origin main`。后面会详讲。
**Q5:为什么我 `git init` 后默认分支是 master 而不是 main?**
A:你的 Git 版本 < 2.28,或没配置 `init.defaultBranch`。执行 `git config --global init.defaultBranch main` 即可。
**Q6:能否把一个目录同时作为工作区和裸仓库?**
A:不行。裸仓库(`--bare`)没有工作区,专门用作远程共享仓库。工作区仓库和裸仓库是两种不同形态,不要混用。
**Q7:`git init` 在已有 `.git` 的目录执行会怎样?**
A:安全。Git 检测到已有 `.git`,只更新缺失的子目录和文件,不会覆盖你的提交历史。输出 `Reinitialized existing Git repository`。
---
## 九、最佳实践
1. **先建 `.gitignore` 再 add**:避免误提交编译产物、IDE 文件、密钥。
2. **初始分支用 `main`**:`git config --global init.defaultBranch main`,一次配置终身受益。
3. **第一个提交放 README + .gitignore + LICENSE**:让仓库一开始就规范。
4. **提交信息要有意义**:`初始提交`可以,但 `feat: 初始化项目结构` 更好。后面会讲提交信息规范。
5. **不要把 `.git` 提交到别的仓库**:避免嵌套。
6. **定期 `git status`**:随时知道当前状态,避免意外。
7. **敏感信息绝不提交**:密码、私钥、`.env` 一律进 `.gitignore`。一旦提交,即使删除,历史仍在,需用 BFG/filter-branch 清理,非常麻烦。
---
## 十、小结
本篇我们亲手创建了第一个 Git 仓库,跑通了完整闭环:
1. **两种建仓方式**:`git init`(本地初始化)和 `git clone`(克隆远程)。
2. **`git init`** 在当前目录创建 `.git` 子目录,仓库数据全在其中。
3. **`.git` 目录**包含 `HEAD`(当前分支)、`config`(仓库配置)、`index`(暂存区)、`objects`(对象存储)、`refs`(引用)等,是仓库的核心,勿手动改、勿丢失。
4. **完整工作流**:创建文件(工作区)→ `git add`(暂存区)→ `git commit`(版本库)→ `git log`(查看历史)→ 修改 → 再 add/commit。
5. **`git init` 选项**:`-b main` 指定初始分支,`--bare` 创建裸仓库(用于服务器),`--template` 自定义模板。
6. **现有项目纳入 Git**:init → 先建 `.gitignore` → `git add .` → 检查 status → commit。
7. **master vs main**:2020年后社区改用 `main`,用 `init.defaultBranch main` 配置默认,已有仓库用 `git branch -m master main` 改名。
8. **README.md** 是项目门面,要写清是什么、怎么用、怎么跑;**LICENSE** 声明协议,个人项目用 MIT 最省心。
你现在已经有了一个能提交、能查看历史的本地仓库。但"工作区、暂存区、版本库"这三个区域到底怎么流转?为什么要有暂存区?下一篇我们深入这个**理解 Git 的核心概念**。
> 下一篇:**《Git从入门到精通(四)------ Git三大区域:工作区、暂存区与版本库》**
---
*如果本文对你有帮助,欢迎点赞、收藏、关注。我是全栈陈序员,我们下篇见。*