3、创建仓库:git init,git clone,工作区、暂存区、仓库

今天咱们来聊聊仓库创建这件事。说白了,Git 仓库就是你版本控制的「大本营」。没有仓库,你那些代码就只是一堆散装文件,哪天手一抖删了,哭都来不及。

我记得刚带团队那会儿,有个新同事上来就git init,然后在根目录下猛写代码,结果项目越来越大,分支管理一团乱。后来我才意识到——仓库怎么创建,其实藏着不少学问。

3.1 两种创建方式:git init 与 git clone

Git 提供了两条路子让你拿到仓库:从头建一个,或者从别处克隆一个。适用场景完全不同,我列个表帮你区分清楚。

命令 场景 初始化状态 典型使用
git init 本地新建项目,还没有远程仓库 生成空的 .git 目录,无任何提交 你准备写新项目,想先搞个版本控制
git clone 从远程仓库复制已有项目到本地 自动拉取所有历史,并设置好远程跟踪分支 加入团队项目或拉取开源代码

3.1.1 git init 详解

在你项目根目录敲下这行命令,Git 就会在当前文件夹里创建一个叫 .git 的隐藏目录。这个目录里装着整个版本库的全部元数据——提交历史、分支、配置、钩子脚本等等。你所有的工作文件都在外面,彼此不打架。

cd my-project
git init
# 输出:Initialized empty Git repository in /path/to/my-project/.git/

如果你想让仓库的名字和文件夹名不同,或者想指定远程仓库别名,可以用 --initial-branch 参数(Git 2.28+)来设置默认分支名。我个人习惯用 main 而不是 master,避免一些不必要的政治正确争议。

git init --initial-branch=main

嗯,这里要注意:git init 只是在本地新建了一个空仓库,你还没有任何提交。要开始记录版本,你得先 git add 再 git commit。

3.1.2 git clone 详解

当你想参与别人的项目,或者从公司 GitLab 拉取代码时,用 git clone。它会自动帮你做几件事:

  • 创建与远程仓库同名的本地文件夹
  • 初始化本地仓库
  • 把远程所有分支、标签、提交历史都拉下来
  • 自动设置 origin 为远程别名
  • 将当前工作区切换到默认分支(通常是 main 或 master)
git clone https://github.com/example/some-repo.git
cd some-repo
git branch -a  # 查看所有分支,包括远程分支

我曾经遇到过一个问题:公司内网用 HTTPS 克隆超大的仓库时总是断连。后来改用 SSH 密钥认证,配合 --depth 1 只拉最新历史,速度快了一倍不止。你想想看,如果只是要看最新代码,没必要把整个项目几万次提交都拖下来吧?

git clone --depth 1 git@github.com:example/some-repo.git

这叫「浅克隆」,适合 CI 构建或临时查看。但如果你要参与开发并推代码,还是老老实实用完整克隆。

3.2 三个核心区域:工作区、暂存区、仓库

这是 Git 最基础但也最容易混淆的概念。很多人学 Git 甚至会把暂存区忽略掉,直接 git commit -a 一带而过。其实理解这三个区域,能帮你避开很多坑。

我画了张图,你看一眼就明白了。

Git 三区域工作流 工作区 Working Directory × 编辑文件 git add 暂存区 Staging Area × 准备提交 git commit 仓库 Repository × 历史记录 只有暂存区里的修改才能被 commit 提交到仓库 绕过暂存区:git commit -a 等价于先 git add 再 git commit

工作区就是你磁盘上能看到的所有文件。你在这里写代码、改代码、删文件。

暂存区(也叫索引区)是一个中间缓存。你 git add 之后,修改就会进入暂存区。为什么要有这一步?因为有时候你改了很多文件,但只想把其中一部分提交到一个版本里。比如你修了 bug 又顺手改了个配置,你想分开 commit。利用暂存区就能精确选择哪些改动进这一次提交。

仓库就是 .git 目录里的那个数据库,保存着所有已经提交的版本快照。当你 git commit,暂存区的内容会被打包成一个新的提交,永久记录到仓库里。

关键区别:

  • 工作区文件可以随时修改,Git 不管。
  • 暂存区是「即将被记录」的候选名单。
  • 仓库里的提交一旦生成,原则上不该被修改(除非你用到 git rebase 等重写历史的命令,但那是另一个故事)。

3.3 实操演示:从 init 到第一次提交

为了让你更直观地感受这三个区域,我带你走一遍完整流程。假设我们要创建一个叫 learn-git 的项目。

步骤 1:创建本地仓库

mkdir learn-git
cd learn-git
git init

执行后,你会发现一个新文件夹 .git。用 ls -la 可以看见,但一般不用手动碰它。

步骤 2:在工作区创建文件

echo "# Learn Git" > README.md
echo "print('hello')" > hello.py

此时工作区多了两个文件。但 Git 还没跟踪它们。你可以用 git status 看看状态。

git status
# 输出显示 Untracked files:
#   README.md
#   hello.py

步骤 3:将文件添加进暂存区

git add README.md hello.py
git status
# 输出显示 Changes to be committed:
#   new file:   README.md
#   new file:   hello.py

现在这两个文件已经躺在暂存区里了。你可以随时用 git add 继续添加更多修改,或者在 commit 前用 git reset HEAD 把它从暂存区移出来。

步骤 4:提交到仓库

git commit -m "Initialize project with README and hello script"

提交成功后,你会在仓库里留下第一个永久记录。用 git log 可以看到这次提交的唯一哈希值。

commit 1a2b3c4d5e6f7g8h9i0j...
Author: Your Name <you@example.com>
Date:   Mon Mar 31 14:00:00 2025 +0800

    Initialize project with README and hello script

3.4 避坑指南

我曾经犯过的错:

  • 直接在托管平台(如 GitHub)上创建仓库,然后本地 git init 再 push,结果忘记关联远程。 解决办法:要么先在远程建仓库然后 clone,要么本地 init 后用 git remote add origin 手动关联。更推荐先 clone 再开发。
  • 把大文件(如 .dll、.so、.pkl)一股脑 git add,导致仓库体积爆炸。 正确做法是写 .gitignore 排除编译产物。如果你已经提交了,得用 git rm --cached 把它们从跟踪中移除,再提交一次。
  • 用 git commit -a 太顺手,忘了检查暂存区内容。 有一次我提交了不该提交的临时调试代码,差点把秘钥也提交上去。从此我坚持先用 git add -p 交互式选块,确保每一条改动都是我想要的。

小技巧:

  • 查看当前暂存区与仓库最新提交的差异:git diff --cached
  • 查看工作区与暂存区的差异:git diff
  • 想丢弃工作区某个文件的修改(恢复到暂存区或最后一次提交的样子):git checkout -- <file>

3.5 本章小结

创建仓库有两种方式:git init 适合全新项目,git clone 适合从已有远程仓库出发。不管是哪种,背后都离不开工作区、暂存区、仓库这三个基本区域。

  • 工作区是你看到的实际文件。
  • 暂存区是 commit 前的「中转站」。
  • 仓库是保存所有版本历史的地方。

理解了这三个区域,你就能像玩积木一样灵活控制每次提交的颗粒度。记住,Git 不会对你工作区的修改做任何保护——只有当你主动 add + commit 后,版本才会被安全地存起来。

下次遇到奇怪的状态(比如 git status 显示 untracked、modified、staged 混在一起),就用这张图去对照,基本都能找到答案。