这部分我们将学习git的基本操作,学完本节我们能掌握 Git 世界里绝大部分日常操作。 你将学会如何初始化一个项目、追踪文件的爱恨情仇、暂存和提交你的绝妙想法。 我们还会带你领略如何优雅地忽略某些文件、如何轻松反悔、如何翻阅项目的历史长河,以及如何与远方的伙伴协同作战。

踏入 Git 世界的第一步,是拥有一个“Git 仓库”。这就像是为你的项目开启一个拥有时光机能力的“存档点”。你有两种方式来获得它。
第一种,是为你已有的项目开启“时光机”功能。想象一下,你正在本地电脑上写一部小说,或者维护一个充满了代码的文件夹。你决定从此刻开始,要记录下每一次的修改。这时,你只需要走进这个项目的文件夹,然后念出咒语:
git init这条命令会在你的项目里召唤一个名为 .git 的神秘隐藏文件夹。这个文件夹就是你的项目的“时光机”核心,它包含了所有必需的“零件”和“记录本”,准备好为你服务。
从这一刻起,你的项目就拥有了版本控制的能力,但别急,你的文件们还尚未被正式“登记在册”。
第二种方式,则是直接复制一个别人已经建好的“时光机项目”。比如,你想为某个开源项目贡献代码,或者想下载一份带有完整修改历史的学习资料。这时,你需要使用的咒语是 git clone。
git clone [项目的URL地址]这条命令非常强大。它不像普通下载那样只给你一份最新的文件。它会把那个项目从诞生之初到现在的整个历史记录,完完整整地复制到你的本地电脑。 这就像你不仅得到了游戏的最终版,还得到了从 1.0 版本开始的所有存档。如果服务器的硬盘坏了,理论上,任何一个克隆了这份仓库的“玩家”,都能将项目恢复到当初的状态。这正是 Git 分布式设计的魅力所在。
Git 就像一个精明的古董商,它有三个重要的“区域”来管理你的代码。让我们先通过一个图来理解这三个区域的关系:
现在让我们详细了解这三个区域以及它们之间的交互流程:
这是你的工作台,你所有的项目文件都平铺在这里。你可以在这里随意修改、添加、删除文件,就像你在工作台上打磨你的手工艺品一样。当你在这里修改文件时,Git 会检测到这些变化,但不会自动保存到版本历史中。
这是你的"精品展示待售区"。当你对一件作品(一个或多个文件)感到满意,觉得可以作为一个阶段性成果时,你就把它从工作台(工作区)拿到这个展示区(暂存区)。这个动作,就是 git add。暂存区让你可以精确控制哪些修改要包含在下一次提交中。
这是你的"仓库保险柜"。当展示区(暂存区)里的东西都准备好了,你决定将它们作为今天的"营业成果"永久封存。你大手一挥,执行 git commit,把暂存区里所有的东西打包,盖上时间戳和说明,锁进保险柜里。这个"包裹"就是一个版本快照。
git add 将满意的修改添加到暂存区git commit 将暂存区的内容永久保存到本地仓库这种设计的精妙之处在于,它给了你一个反悔和整理的机会。你可以先在工作台(工作区)上尽情挥洒创意,哪怕弄得一团糟也没关系。
只有当你觉得某一部分的修改是完整且有意义的,你才用 git add 把它“打包”送到暂存区。最后,当你确认暂存区里的所有内容可以构成一个有意义的版本时,再用 git commit 一次性提交。
这就像写文章,工作区是你的草稿纸,暂存区是你整理好、准备放进最终文稿的段落,而 git commit 则是将这些段落正式“定稿”的动作。
那么,如何知道你的文件们现在都处于什么状态呢?git status 命令就是你的“状态检查器”。它会清楚地告诉你,哪些文件还在工作台上修改过(modified),哪些是新来的客人(untracked),哪些已经打包好在暂存区等待提交(staged)。
git add 命令的作用,可以理解为“把这个文件的当前内容加入到下一次的提交计划中”。它是一个多面手,无论是新文件要被追踪,还是已修改的文件要被暂存,都由它来执行。
.gitignore 清单在你的项目里,总会有一些你不想让 Git “看到”的文件。比如,编译过程中产生的临时文件、编辑器生成的配置文件,或者包含敏感信息的日志文件。你肯定不希望把这些东西也存进你的“历史保险柜”里。
这时,你可以在项目根目录下创建一个名为 .gitignore 的文件。你可以把它想象成一张“忽略清单”。
把那些你希望 Git 无视的文件名或匹配规则写进去,Git 就会对它们视而不见。这样,你就不会在 git status 时看到它们,也不会不小心用 git add 把它们加进暂存区。
git diffgit status 只能告诉你哪些文件变了,但如果你想知道具体改了哪一行、哪一句话,就需要 git diff 这个“文本对比器”了。
git diff,它会比较你工作区和暂存区的区别,告诉你哪些修改还在工作台上,尚未被打包。git diff --staged,它会比较暂存区和你上一次提交的区别,让你在提交前最后检查一遍,这次的“营业成果”里到底包含了哪些改动。人非圣贤,孰能无过。在 Git 的世界里,犯错是常态,而撤销操作就是你的后悔药。
git commit),就发现提交信息写错了,或者漏掉了一个文件。别担心,git commit --amend 是你的救星。它能让你重新编辑上一次的提交信息,并且如果你在这之前 git add 了新文件,它会把这些文件一并加到上一次的提交里,生成一个新的“完美”提交来替换掉刚才那个有瑕疵的。git add 到了暂存区(精品展示区),想把它拿回到工作台(工作区)继续修改。git reset HEAD <文件名> 就是这个“取回”的动作。文件本身的内容不会有任何改变。git checkout -- <文件名> 可以帮你实现。但这是一个危险的操作,因为你的所有修改都会瞬间消失,就像从未存在过一样。请务务必确认你真的不再需要那些改动了。到目前为止,你的所有操作都是在自己的电脑上进行的。但 Git 最强大的地方在于协作。远程仓库就是你们团队的“中央服务器”,比如大家熟知的 GitHub。
你可以把远程仓库想象成一个项目的“云端大本营”。每个成员都可以从这个大本营 clone 一份完整的项目历史到自己的电脑上,然后独立工作。
git fetch [远程仓库名]:这个命令会去大本营“侦查”一下,看看有没有你本地没有的最新进展,然后把这些新数据下载到你的本地,但不会自动合并到你当前的工作里。这就像是“只看不动”,让你先了解情况。git pull [远程仓库名] [分支名]:这个命令更像是 fetch 和 merge 的组合技。它会去“侦查”并下载最新进展,然后立刻尝试将这些新内容合并到你当前的工作分支中。这是一种更直接的“更新并同步”操作。git push [远程仓库名] [分支名]:当你完成了一个阶段的工作,并且在本地 commit 之后,就可以用 push 命令,把你本地的这些“历史记录”上传到云端大本营,分享给所有团队成员。当你的项目开发到某个重要阶段,比如 v1.0 版本正式发布时,你希望在历史长河中为这个时刻打上一个清晰的“里程碑”。git tag 命令就是用来做这件事的。
你可以创建一个“轻量标签”,它就像一个不会移动的分支,仅仅指向某个特定的提交。或者创建一个“附注标签”,这更常用,因为你可以在里面添加作者、日期和一段描述信息,让这个里程碑更加正式。
git tag -a v1.0 -m "正式发布 1.0 版本"
创建完标签后,默认它只存在于你的本地。你需要用 git push origin --tags 把它推送到远程仓库,这样其他人才能看到。
git alias有些 Git 命令又长又难记,但你又需要经常使用。Git 允许你为这些命令设置“别名”,就像给朋友起外号一样。
比如,你觉得 git status 太长,可以这样设置:
git config --global alias.st status
设置完之后,你再输入 git st,效果就和 git status 完全一样了。这能极大地提高你的工作效率。
现在让我们通过一个具体的项目 git_demo 来演示我们的Git工作流程。假设我们正在开发一个简单的Web应用,包含HTML、CSS和JavaScript文件。
# 创建项目目录
mkdir git_demo
cd git_demo
# 初始化Git仓库
git init此时项目结构:
git_demo/
├── .git/ # Git仓库(隐藏文件夹)
└── (空项目)# 创建项目文件
echo "<!DOCTYPE html>" > index.html
echo "body { color: blue; }" > style.css
echo "console.log('Hello Git!');" > script.js项目结构变为:
git_demo/
├── .git/
├── index.html
├── style.css
└── script.jsgit status输出结果:
On branch main
Untracked files:
(use "git add <file>..." to include in what will be committed)
index.html
style.css
script.js
nothing added to commit but untracked files present (use "git add" and/or "git commit -a")# 添加所有文件
git add .
# 或者分别添加
# git add index.html
# git add style.css
# git add script.js文件状态变化:
git status输出结果:
On branch main
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: index.html
new file: style.css
new file: script.jsgit commit -m "Initial commit: 添加 HTML, CSS 和 JS 文件"提交后的状态:
# 修改HTML文件
echo "<!DOCTYPE html>
<html>
<head>
<title>Git Demo</title>
<link rel='stylesheet' href='style.css'>
</head>
<body>
<h1>Welcome to Git Demo</h1>
<script src='script.js'></script>
</body>
</html>" > index.html
# 修改CSS文件
echo "body {
font-family: Arial, sans-serif;
margin: 20px;
background-color: #f0f0f0;
}
h1 {
color: #333;
text-align: center;
}" > style.css# 查看工作区与暂存区的差异
git diff
# 输出示例:
# diff --git a/index.html b/index.html
# index 1234567..abcdefg 100644
# --- a/index.html
# +++ b/index.html
# @@ -1 +1,11 @@
# -<!DOCTYPE html>
# +<!DOCTYPE html>
# +<html>
# +<head>
# + <title>Git Demo</title>
# + <link rel='stylesheet' href='style.css'>
# +</head>
# +<body>
# + <h1>Welcome to Git Demo</h1>
# + <script src='script.js'></script>
# +</body>
# +</html># 只暂存HTML文件的修改
git add index.html
# 查看状态
git status状态显示:
On branch main
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: index.html
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: style.cssgit commit -m "提升 HTML 结构"# 创建.gitignore文件
echo "# 忽略编译生成的文件
*.log
*.tmp
*.cache
# 忽略编辑器配置文件
.vscode/
.idea/
*.swp
*.swo
# 忽略操作系统生成的文件
.DS_Store
Thumbs.db
# 忽略依赖目录
node_modules/
dist/" > .gitignoregit add .gitignore
git commit -m "添加 .gitignore 文件,忽略不必要的文件"git log --oneline输出结果:
abc123 (HEAD -> main) 添加 .gitignore 文件,忽略不必要的文件
def456 提升 HTML 结构
ghi789 初始提交:添加 HTML, CSS 和 JS 文件git log --graph --oneline --all* abc123 (HEAD -> main) 添加 .gitignore 文件,忽略不必要的文件
* def456 提升 HTML 结构
* ghi789 初始提交:添加 HTML, CSS 和 JS 文件# 假设我们不小心暂存了不想提交的文件
git add style.css
# 撤销暂存
git reset HEAD style.css# 撤销style.css的修改(危险操作!)
git checkout -- style.css# 修改文件
echo "/* 添加一些样式 */" >> style.css
git add style.css
# 修正提交
git commit --amend -m "Improve HTML structure and add basic styling"