这部分我们将学习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仓库(隐藏文件夹) └── (空项目)
|# 添加所有文件 git add . # 或者分别添加 # git add index.html # git add style.css # git add script.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;
|# 创建.gitignore文件 echo "# 忽略编译生成的文件 *.log *.tmp *.cache # 忽略编辑器配置文件 .vscode/ .idea/ *.swp *.swo # 忽略操作系统生成的文件 .DS_Store Thumbs.db # 忽略依赖目录 node_modules/ dist/" > .gitignore
|git log --oneline
输出结果:
|abc123 (HEAD -> main) 添加 .gitignore 文件,忽略不必要的文件 def456 提升 HTML 结构 ghi789 初始提交:添加 HTML, CSS 和 JS 文件
|# 假设我们不小心暂存了不想提交的文件 git add style.css # 撤销暂存 git reset HEAD style.css
|# 撤销style.css的修改(危险操作!) git checkout
|# 创建项目文件 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.js
|git 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
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.js
|git commit -m "Initial commit: 添加 HTML, CSS 和 JS 文件"
提交后的状态:
|# 查看工作区与暂存区的差异 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文件的修改 git add index.html # 查看状态 git status
状态显示:
|On branch main Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: index.html
|git commit -m "提升 HTML 结构"
|git add .gitignore git commit -m "添加 .gitignore 文件,忽略不必要的文件"
|git log --graph --oneline --all
|* abc123 (HEAD -> main) 添加 .gitignore 文件,忽略不必要的文件 * def456 提升 HTML 结构 * ghi789 初始提交:添加 HTML, CSS 和 JS 文件
|# 修改文件 echo "/* 添加一些样式 */" >> style.css git add style.css # 修正提交 git commit --amend -m "Improve HTML structure and add basic styling"