为什么要用Git
想象你正在写一篇论文:
- 论文_初稿.doc
- 论文_修改1.doc
- 论文_最终版.doc
- 论文_最终版_绝对不改.doc
时间一长,你不知道哪个才是最新版,也不敢删掉旧的。Git 就是帮你管理这些“版本”的工具,它会记录每次修改的内容、时间、作者,让你能随时回到过去的某个状态,也能和别人并行合作,最后合并到一起。
核心概念先有个印象:
- 仓库(Repository):一个被 Git 管理的项目目录,里面藏着完整的历史记录。
- 提交(Commit):一次“保存快照”的操作,就像游戏的存档点。
安装并配置Git
环境:WSL2,Ubuntu24.04
打开终端,先更新包列表,再安装 Git:
sudo apt update
sudo apt install git -y安装完成后,检查是否成功:
git --version你应该会看到类似 git version 2.43.0 的输出(版本号以实际为准)。
配置身份
Git 每次提交都会记录“谁做的”,所以要先告诉 Git 你的名字和邮箱。这个信息会随提交一起被永久记录在历史里。
在终端依次执行(把名字和邮箱换成你自己的,建议邮箱使用 GitHub 注册邮箱):
git config --global user.name "你的名字"
git config --global user.email "你的邮箱@example.com"--global 表示这个配置对你整个系统的当前用户生效,所有 Git 仓库都会默认使用这个身份。
验证一下配置是否生效:
git config --global --list你会看到类似输出:
user.name=你的名字
user.email=你的邮箱@example.com可选设置
为了让以后的输出更易读,建议打开命令行颜色高亮:
git config --global color.ui auto在 Ubuntu 终端中,这会让 diff、status 等命令的输出用不同颜色标记增、删、改,看起来一目了然。
创建仓库与第一次提交
创建第一个项目目录
首先,我们在主目录下建一个练习文件夹,叫 my-first-repo:
cd ~
mkdir my-first-repo
cd my-first-repo初始化仓库
让 Git 接管这个目录:
git init你会看到类似输出:
Initialized empty Git repository in /home/你的用户名/my-first-repo/.git/这行字告诉你:仓库诞生了。它会在这个目录下创建一个隐藏文件夹 .git,里面存着 Git 需要的所有历史数据。用 ls -a 可以看到它。
重要认知:
git init后,这个目录就变成了工作区(Working Directory),里面的.git文件夹就是仓库(Repository)。
Git 的三个区域
继续之前,必须先把这三个区域记在心里,这是 Git 的灵魂:
工作区(Working Directory)
- 就是你能看到的项目文件夹,你直接在这里增、删、改文件。
- Git 会监视这里的变化,但不会自动记录。
暂存区(Staging Area / Index)
- 你希望被记录的那些修改,需要先“添加”到这个区域。
- 可以理解为一张“准备提交的快照暂存清单”。
- 使用
git add会把文件从工作区送到暂存区。
仓库(Repository / Commit History)
.git文件夹里的东西,藏着所有历史提交。- 使用
git commit会把暂存区的内容永久保存为一个“提交”。
工作流程是:
工作区 ──(git add)──> 暂存区 ──(git commit)──> 仓库记住这个流程,后面你就能理解每一个命令在干什么。
创建第一个文件
在工作区创建一个文件 hello.txt:
echo "Hello, Git" > hello.txt现在用 git status 看一眼仓库状态:
git status输出会显示:
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
hello.txt
nothing added to commit but untracked files present (use "git add" to track)Git 告诉你:
- 当前在 master 分支(这是默认的主分支名)。
- 还没有任何提交(No commits yet)。
- hello.txt 是未跟踪文件(Untracked),工作区有个新文件,但 Git 还没管它。
把文件加入暂存区
让 Git 开始跟踪 hello.txt:
git add hello.txt再运行 git status 看看变化,现在输出像这样:
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: hello.txthello.txt 现在在暂存区 了,状态是 “new file”,准备被提交。
第一次提交
把暂存区的内容永久保存到仓库中:
git commit -m "第一次提交:添加 hello.txt"-m后面跟的是提交信息(commit message),用来描述这次做了什么修改。- 提交信息必须写,而且最好写清楚,这是以后看历史的你会感谢你的事。
执行后你会看到:
[master (root-commit) xxxxxxx] 第一次提交:添加 hello.txt
1 file changed, 1 insertion(+)
create mode 100644 hello.txt这意味着:
- 在
master分支上生成了第一个提交(root-commit 表示它是整个历史的根)。 - 一个文件改变了,添加了一行内容。
- 提交的哈希值(一串十六进制字符)已经生成,唯一标识这次提交。
查看历史
现在看看仓库里记录了什么:
git log输出类似:
commit a1b2c3d4e5f...(一串哈希值)
Author: 你的名字 <你的邮箱@example.com>
Date: Mon Apr 27 10:00:00 2026 +0800
第一次提交:添加 hello.txt你看到了:
- 提交的唯一哈希值(不用记全,前几位就能区分)
- 作者和日期信息,正是你第一章配置的身份
- 提交信息
按 q 键可以退出 log 的浏览模式。
巩固流程
趁热打铁,再做一轮完整的修改→暂存→提交。
- 修改文件:向
hello.txt追加一行
echo "Git is fun" >> hello.txt- 查看状态:
git status- 查看具体改动:
git diff能看到你添加了什么内容(以 + 开头的绿字)。
- 加入暂存区:
git add hello.txt- 提交:
git commit -m "添加第二行:Git is fun"- 再次查看历史:
git log现在会看到两次提交,从新到旧排列。最新的在上面。
深入暂存区与文件管理
接下来会教你后悔药怎么吃——修改错了怎么撤,提交反悔了怎么退,还会让你真正看透 git diff 的各种用法。先确认一下历史:
cd ~/my-first-repo
git log --oneline你应该能看到类似两条记录(哈希值会不同):
a1b2c3d 添加第二行:Git is fun
e5f6g7h 第一次提交:添加 hello.txtgit diff 的三种常用姿势
git diff 是“对比神器”,不同参数对比不同区域。
- 工作区 vs 暂存区(
git diff,无参数)
先制造一个工作区修改:
echo "第三行:learning diff" >> hello.txt现在修改只存在工作区,未暂存。运行:
git diff输出会显示什么?以 + 开头的绿字是你新增的内容,前面带有 @@ 的行是位置标记。这个命令对比的是工作区和暂存区(如果暂存区为空,就是和最新提交比)。 因为暂存区现在还是原来的版本,所以你能看到新增的“第三行”。
- 暂存区 vs 最新提交(
git diff --staged)
先把刚才的修改加入暂存区:
git add hello.txt现在再运行 git diff 会发现没有任何输出,因为工作区和暂存区内容一致了。但你想看 暂存区和最新提交相比有什么变化,就用:
git diff --staged(也可以用 git diff --cached,一个意思。) 这次你会看到刚才添加的 +第三行:learning diff,说明暂存区比当前提交多了这一行。
- 工作区 vs 最新提交(
git diff HEAD)
在工作区再追加一行(暂存区没变):
echo "第四行:will be unstaged" >> hello.txt现在工作区比暂存区多了一行,暂存区又比最新提交多了一行。直接比较工作区和最新提交:
git diff HEAD你会看到两条新增行:一条是之前暂存的“第三行”,另一条是刚才加的“第四行”。 HEAD 总是指向当前分支的最新提交,所以这个命令跳过了暂存区,直接拿工作区和最后一次 commit 比。
小总结:
git diff→ 工作区 vs 暂存区git diff --staged→ 暂存区 vs HEADgit diff HEAD→ 工作区 vs HEAD
撤销工作区的修改
假设你现在发现“第四行”写错了,想丢弃工作区的这个修改,恢复成暂存区(或最新提交)的样子。在丢弃之前,再看一眼状态确认:
git status你会看到 hello.txt 既有暂存的修改(绿色 "modified"),又有未暂存的修改(红色 "modified")。现在我们丢弃工作区的修改(即刚才加的那行“第四行”):
git checkout -- hello.txt或者新版本 Git 也可以使用等价命令 git restore hello.txt。执行后,查看文件内容:
cat hello.txt你会发现“第四行”消失了,文件内容回到了暂存区保存的版本(只有三行)。再用 git diff HEAD 看,现在只显示暂存区里的“第三行”,说明工作区已经恢复到了暂存区的状态。
注意:这个操作会永久丢失工作区的未提交修改,而且很难恢复(除非用 IDE 的本地历史)。请一定确认要丢弃再执行。
取消暂存
当前的暂存区里还保存着“第三行”的修改。如果你决定:先不把第三行放在这次提交里,但保留在工作区,该怎么操作?
我们需要把文件从暂存区移回工作区。
git reset HEAD hello.txt(新版本 Git 也可以用 git restore --staged hello.txt) 运行之后,git status 你会看到:
- “Changes to be committed” 里不再有
hello.txt - 但
hello.txt出现在 “Changes not staged for commit” 里,而且还是被修改过的状态
也就是说,文件从暂存区退回了工作区,修改本身没有丢失。
此时如果你又后悔了,可以再次 git add 把它暂存回去。如果你连这个修改都不想要了,可以用上一步的 git checkout -- hello.txt 彻底丢弃。
git reset 的三种模式
比撤销文件更彻底的,是撤销一次提交。我们先创建一个“临时”提交来演示。
先把刚才的第三行暂存并提交:
git add hello.txt
git commit -m "添加第三行:learning diff"现在 git log --oneline 会显示三个提交。
接下来,我们要“退回”到上一个提交。git reset 可以移动当前分支指针,有三种常用模式:
| 模式 | 效果 | 提交 | 暂存区 | 工作区 |
|---|---|---|---|---|
--soft | 只撤销提交,改动留在暂存区 | 回退 | 保留 | 保留 |
--mixed(默认) | 撤销提交和暂存,改动留在工作区 | 回退 | 清空 | 保留 |
--hard | 全部撤销,工作区也清掉 | 回退 | 清空 | 清除 |
1. --soft:提交后悔了,但想重新修改提交信息
执行(注意:把 HEAD~1 指向上一个提交):
git reset --soft HEAD~1git status 查看状态,你会看到 hello.txt 的修改仍然在暂存区(绿色,等待提交),但 git log 里“添加第三行”那个提交已经消失了。这就像刚 git add 完,还没来得及 commit。你可以修改提交信息,或添加其他文件后再提交。
2. --mixed(默认):提交错了,想把修改推回工作区重新编辑
先重新提交回去,以便演示:
git commit -m "第三行(临时)"现在再次回退,这次不给模式(默认就是 --mixed):
git reset HEAD~1再 git status,你会发现:
- 暂存区是空的
hello.txt的修改在工作区,“not staged”
这就是让你重新整理要提交的内容,再 add 并 commit。
3. --hard:彻底放弃,回到干净状态(危险)
我们故意创建一个新文件并提交,然后彻底丢弃它。
echo "临时垃圾文件" > junk.txt
git add junk.txt
git commit -m "添加一个不要的提交"现在回退:
git reset --hard HEAD~1结果:
- 用
git log看,那个提交消失了 junk.txt被彻底删除,工作区干干净净
这就是 --hard 的威力:它会让你的工作区和暂存区完全和回退到的提交一致,未提交的修改也会丢失。一般情况下,不要轻易用 --hard,除非你十分确定可以丢弃。
分支的世界
分支是 Git 真正强大的地方——你可以在一条独立的线上开发新功能,不影响主线的稳定,成熟后再合并回去。
想象你在玩一种可以存档的冒险游戏。主线剧情是 master(或现在叫 main),当你走到某个关卡,突然想试试“如果我先去拿隐藏道具会怎样”,你就可以在那个时间点创建一个分支,在里面自由探索。就算玩砸了,主剧情毫发无损;如果探索成功,就把分支的进展合并回主线。
在 Git 里,分支仅仅是一个指向某个提交的轻量级指针。创建分支几乎瞬间完成,完全不占什么空间。
默认的主分支名在较新的 Git 中是 main,但你的仓库可能还是 master(取决于版本和配置)。我们这里以 master 为例,如果你看到的是 main,命令中替换即可。
查看分支
先看看当前仓库有哪些分支:
git branch输出:
* master前面的 * 表示你当前所在的分支——master。
创建分支
假设我们想给 hello.txt 添加一个“作者署名”功能,但不想直接在主线上改。我们创建一个叫 feature/author 的新分支:
git branch feature/authorgit branch 再看一下分支列表:
feature/author
* master新分支出现了,但 * 还停留在 master 上。创建分支并不会自动切换过去。
切换分支
要进入新分支工作,需要切换过去。推荐使用较新的命令 git switch(Git 2.23+),语义更清晰:
git switch feature/author你也可以使用传统的 git checkout feature/author,效果一样。现在再用 git branch 查看分支,* 已经移到 feature/author 前面了,表示你正在该分支上。
也可以同时创建并切换,用:
git switch -c 分支名
# 或
git checkout -b 分支名在分支上工作和提交
现在你在 feature/author 分支上。让我们修改 hello.txt,在文件末尾添加一行署名:
echo "Author: GitLearner" >> hello.txtgit status 查看状态,暂存并提交:
git add hello.txt
git commit -m "在 feature/author 分支添加作者署名"用 git log --oneline 看一下这个分支的日志,你会看到,feature/author 比 master 多了一个新提交。切回 master 看看文件:
git switch master
cat hello.txt你会发现,master 上的 hello.txt 完全没有那句 “Author: GitLearner”。因为那个修改只存在于 feature/author 分支上。这就是分支隔离的魔力。
合并分支
现在你觉得“署名功能”已经开发好了,想把它合并回主分支。首先确保你当前在想要合并到的目标分支上(即主分支 master):
git switch master然后执行合并:
git merge feature/author你会看到类似输出:
Updating a1b2c3d..f6g7h8i
Fast-forward
hello.txt | 1 +
1 file changed, 1 insertion(+)关键词:Fast-forward(快进合并)。
为什么会快进?
因为 master 自从分出 feature/author 之后,自己没有产生任何新提交。feature/author 相当于在 master 的基础上直线往前走了几步。合并时,Git 只需要把 master 的指针直接“快进”到 feature/author 的最新提交即可,无需创建额外的“合并提交”。
此时的提交历史是一条直线:
第一次提交 → 添加第二行 → 添加第三行 → 在 feature/author 分支添加作者署名此时 master 和 feature/author 指向同一个提交。你可以用 git log --oneline 验证。再查看 hello.txt,署名已经出现了。
合并后分支清理
功能分支完成使命后,可以安全删除:
git branch -d feature/author查看分支列表,只剩 master。如果分支没有被合并,用 -d 会拒绝删除(防止误删),此时若确定不需要,可用 -D 强制删除。
理解“分叉”合并(非快进)
快进合并很干净,但现实里往往各个分支会同时推进,产生分叉历史。我们模拟一次。
- 创建并切换到新分支
feature/sign:
git switch -c feature/sign- 在这个分支上修改,在
hello.txt添加一行 “Sign: Best regards”:
echo "Sign: Best regards" >> hello.txt
git add hello.txt
git commit -m "在 feature/sign 分支添加签名"- 切回
master,在master上也做一个修改,比如新建一个文件readme.md:
git switch master
echo "This is a README" > readme.md
git add readme.md
git commit -m "在 master 添加 README"现在两个分支各自往前走了。看一下图景:master 和 feature/sign 在 “添加作者署名” 这个提交之后分叉了。查看图形化日志(也可以不加 --graph,但图形很直观):
git log --oneline --graph --all你会看到类似:
* (HEAD -> master) 在 master 添加 README
| * (feature/sign) 在 feature/sign 分支添加签名
|/
* 在 feature/author 分支添加作者署名
* 添加第三行:learning diff
...合并分叉:产生合并提交
现在把 feature/sign 合并回 master。 确保当前在 master:
git merge feature/sign这一次,因为两个分支各自有新提交,Git 无法直接快进。它会生成一个 合并提交(merge commit),会把两个分支的修改合并到一起。
你会进入一个提交信息编辑界面(通常是 nano 或 vim)。如果是 nano,直接 Ctrl+X 退出保存就行;如果是 vim,按 :wq 保存退出。Git 已经默认填好了 “Merge branch 'feature/sign'”。
合并完成后,再看看日志图:
git log --oneline --graph --all你会看到类似:
* (HEAD -> master) Merge branch 'feature/sign'
|\
| * (feature/sign) 在 feature/sign 分支添加签名
* | 在 master 添加 README
|/
* 在 feature/author 分支添加作者署名
...这就是典型的功能分支开发与合并。
合并冲特与解决
这是 Git 学习中第一次要你“当裁判”——两个分支改了同一个地方,Git 无法自动决定用哪个,你必须手动裁决。我们会先故意制造一个冲突,再一步步解决它。继续在 ~/my-first-repo 操作。先确认一下当前历史形态(你的哈希值会不同):
cd ~/my-first-repo
git log --oneline --graph --all确保你在 master 分支,并且工作区是干净的:
git switch master
git status(应该显示 nothing to commit, working tree clean)
冲突是如何发生的
冲突并非 Git 出错,而是两个分支修改了同一个文件的同一区域,Git 无法判断该保留哪个版本,于是把决定权交给你。
假设你和队友都发现 hello.txt 的第三行 “learning diff” 不够好,你们各自在分支里做了不同修改,现在要合并。
第一步:创建两个分支,各自修改同一行
- 创建并切换到
branch-A,修改第三行:
git switch -c branch-A简单起见,我们重写整个文件,确保第三行不同:
echo -e "Hello, Git!\nGit is fun!\nA's change: diff is powerful" > hello.txt(-e 让 \n 转义,但 Ubuntu 默认的 echo 可能不支持,可以改用 printf 或直接 cat <<EOF。这里写一个稳妥的方式用 heredoc):
cat > hello.txt << 'EOF'
Hello, Git!
Git is fun!
A's change: diff is powerful
Author: GitLearner
Sign: Best regards
EOF提交:
git add hello.txt
git commit -m "branch-A: 修改第三行"- 切回
master,创建branch-B,同样修改第三行,但内容不同:
git switch master
git switch -c branch-B修改文件:
cat > hello.txt << 'EOF'
Hello, Git!
Git is fun!
B's change: diff is easy
Author: GitLearner
Sign: Best regards
EOF提交:
git add hello.txt
git commit -m "branch-B: 修改第三行"现在两个分支在 “第三行” 产生了分歧。图形化看看:
git log --oneline --graph --all会看到从 master 分出两个叉。
合并冲突
我们尝试把 branch-B 合并到 branch-A。冲突通常发生在你要合并时,所以先切换到 接收合并的分支(这里选 branch-A):
git switch branch-A
git merge branch-B终端会立刻显示:
Auto-merging hello.txt
CONFLICT (content): Merge conflict in hello.txt
Automatic merge failed; fix conflicts and then commit the result.这就是冲突通知。意思是:hello.txt 文件里,两个分支对同一部分有不同修改,Git 无能为力,你来解决。
查看冲突状态
运行 git status,你会看到:
On branch branch-A
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: hello.txt“both modified” 告诉你哪些文件冲突了。此时 Git 处于 合并中间状态,hello.txt 里被写入了冲突标记。
手动解决冲突
打开 hello.txt 看看里面变成什么样了:
cat hello.txt内容类似:
Hello, Git!
Git is fun!
<<<<<<< HEAD
A's change: diff is powerful
=======
B's change: diff is easy
>>>>>>> branch-B
Author: GitLearner
Sign: Best regards解释:
<<<<<<< HEAD到=======之间:你当前所在分支(这里branch-A)的内容。=======到>>>>>>> branch-B之间:被合并进来的分支(branch-B)的内容。
这两个版本同时存在,你必须决定:保留 A 的、保留 B 的、或者组合成一个新版本。现在你当裁判:假设我们决定综合一下,改成 "Both: diff is powerful and easy"。用编辑器修改文件。我们直接用命令行方式重写文件:
cat > hello.txt << 'EOF'
Hello, Git!
Git is fun!
Both: diff is powerful and easy
Author: GitLearner
Sign: Best regards
EOF注意:必须把 <<<<<<<、=======、>>>>>>> 这些标记全部删掉,并留下最终想要的内容。
标记为已解决、完成合并提交
冲突解决完后,你需要告诉 Git:“这个文件我处理好了”。将修改后的文件加入暂存区:
git add hello.txt再查看状态:
git status输出会变成:
On branch branch-A
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)意思是所有冲突都解决了,但合并过程还没完成,你还需要一个提交来结束合并。现在提交:
git commit -m "合并 branch-B:综合第三行修改"这个提交会自动完成合并流程。完成后,git log --oneline --graph 会看到一个新的合并提交,将两个分支重新连在一起。
实用技巧
- 中止合并——
git merge --abort
如果你在解决冲突的过程中慌了,比如发现搞错了分支,想回到冲突前干净的状态,用:
git merge --abort这会让分支回到执行 git merge 之前的状态,所有冲突标记消失,工作区干净。非常安全。我们不妨现在就试试:先重置一下场景(重新制造冲突),再中止。(如果你已经解决了上一个冲突并提交了,那可以先撤销最后一次合并提交来回退,但不推荐初学者这样做。我们换个思路,用 branch-C 再做一次演示。)不过现在你在 branch-A 上已经完成了合并,直接创建一个新分支演示即可。创建 branch-C 从 master 出发(确保没有冲突历史):
git switch master
git switch -c branch-C
# 修改同一行造成冲突(简单复制之前操作)
cat > hello.txt << 'EOF'
Hello, Git!
Git is fun!
C's proposal: diff is magical
Author: GitLearner
Sign: Best regards
EOF
git add hello.txt
git commit -m "branch-C: 修改第三行"再创建 branch-D:
git switch master
git switch -c branch-D
cat > hello.txt << 'EOF'
Hello, Git!
Git is fun!
D's proposal: diff is straightforward
Author: GitLearner
Sign: Best regards
EOF
git add hello.txt
git commit -m "branch-D: 修改第三行"切回 branch-C 并合并 branch-D,触发冲突:
git switch branch-C
git merge branch-D # 冲突现在查看状态,然后尝试中止:
git merge --abort状态回到合并前,hello.txt 恢复为 C 的版本,冲突消失。非常干净。
- 使用可视化合并工具
在 Ubuntu 终端里,如果安装了 meld 或 vimdiff,你可以用 git mergetool 来解决冲突,但对初学者,手动编辑已经足够,而且能真正理解冲突结构。这里不深入。
远程仓库与 GitHub 协作
这一章你会打破“本地孤岛”,把代码上传到 GitHub,和别人共享、协作。我们会在 WSL2 的 Ubuntu 里配置 SSH 密钥,然后把之前那个 my-first-repo 推送到 GitHub。
准备工作:注册 GitHub 账号
如果你还没有 GitHub 账号,先去 GitHub 注册一个(免费),记住你的用户名和注册邮箱。
注意:这个邮箱最好和你第一章 git config 里设置的邮箱一致,这样 GitHub 才能把你的提交和你的账号关联起来。
配置 SSH 密钥
Git 和 GitHub 之间可以通过 HTTPS 或 SSH 通信,SSH 更安全且不用每次输密码。我们来配 SSH。
检查是否已有密钥,在终端输入:
ls -al ~/.ssh如果看到 id_ed25519 和 id_ed25519.pub 或 id_rsa 和 id_rsa.pub,说明已经有密钥,可以跳过下面的生成部分;如果没有,就先生成。
生成新的 SSH 密钥(推荐 Ed25519)
ssh-keygen -t ed25519 -C "你的邮箱@example.com"-t ed25519指定加密类型,更快更安全。-C后面是注释,一般填你的邮箱。
运行后会提示你选择存储位置,直接回车使用默认路径 ~/.ssh/id_ed25519。 接着提示输入 passphrase(密码短语),可以回车留空,也可以设一个额外的保护密码(你自己决定,留空更方便)。
启动 ssh-agent 并添加密钥
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519这一步让代理管理你的密钥,免去重复输入(如果设了 passphrase 只输一次)。
复制公钥
cat ~/.ssh/id_ed25519.pub把屏幕输出的那一长串内容(以 ssh-ed25519 开头,以邮箱结尾)完整复制下来。
添加到 GitHub
- 浏览器登录 GitHub,点击右上角头像 → Settings。
- 左侧选择 SSH and GPG keys。
- 点击绿色的 New SSH key。
- Title 随便写,比如 “WSL2 Ubuntu”。
- Key 里粘贴刚才复制的公钥。
- 点击 Add SSH key。
验证配置是否成功:
ssh -T git@github.com如果看到 “You’ve successfully authenticated, but GitHub does not provide shell access.” 就说明 SSH 成功了。
在 GitHub 上创建一个空仓库
- GitHub 首页右上角点击 + → New repository。
- Repository name:填
my-first-repo(和本地目录同名,便于理解)。 - Description 可选。
- 不要勾选 “Add a README file”、“Add .gitignore” 或 “Choose a license”。因为我们已有本地仓库,如果勾选会导致历史冲突,初学者暂时避开。
- 点击 Create repository。
创建后你会看到一个页面,中间有 “…or push an existing repository from the command line” 一栏,里面有几条命令,正是我们接下来要执行的。
关联远程仓库
回到终端,确保在 my-first-repo 目录且干净:
cd ~/my-first-repo
git status我们给 GitHub 上的那个远程仓库起个简短的名字,约定俗成叫 origin(可以理解为主要远程仓库的默认称呼)。
在 GitHub 新建仓库的页面,找到 SSH 地址,形如:git@github.com:你的用户名/my-first-repo.git
添加远程:
git remote add origin git@github.com:你的用户名/my-first-repo.git(把“你的用户名”换成你自己的 GitHub 用户名)
验证是否添加成功:
git remote -v输出应类似:
origin git@github.com:你的用户名/my-first-repo.git (fetch)
origin git@github.com:你的用户名/my-first-repo.git (push)说明你的本地仓库已经关联了一个远程仓库,名字叫 origin,既可以推送(push)也可以抓取(fetch)。
推送本地内容到 GitHub
把本地的 master 分支推送给远程的 master 分支:
git push -u origin master-u是--set-upstream的缩写,会把本地master和远程origin/master绑定,以后在这个分支只需要 git push 即可。- 因为你是第一次推送,GitHub 那边是空的,所以推送成功会显示类似:
Enumerating objects: 9, done.
...
To github.com:你的用户名/my-first-repo.git
* [new branch] master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.现在去 GitHub 刷新你的仓库页面,hello.txt、readme.md 和历史提交都出现了
从远程获取更新
想象你有一台电脑在办公室,一台在家,或者队友向远程仓库推送了新提交,你需要把这些更新拉取到本地。
模拟远程更新:我们先在 GitHub 上直接编辑文件。
- 在 GitHub 仓库页面,点击
readme.md文件。 - 点击右上角的铅笔图标(Edit this file)。
- 在文件里添加一行:
## 远程协作练习。 - 页面下方,“Commit changes” 部分,填写提交信息,比如 “通过 GitHub 网页更新 README”。
- 选择 “Commit directly to the master branch”。
- 点击 Commit changes。
现在远程仓库的 master 比你的本地多了一个提交。
拉取到本地
回到终端,确保你在本地 master 分支:
git switch master
git pull你会看到类似:
remote: Enumerating objects: ...
...
Updating a1b2c3d..f6g7h8i
Fast-forward
readme.md | 2 ++
1 file changed, 2 insertions(+)git pull 其实是 git fetch(获取远程数据)和 git merge(合并到当前分支)的组合。现在本地 readme.md 文件里已经有你通过网页添加的那句话了。
克隆一个仓库
如果你想在新地方完全复制一个远程仓库(比如你的另一台电脑,或者当你的本地完全乱掉想重新开始),用 git clone。
为了演示,我们回到用户主目录,克隆一份我们的远程仓库到一个叫 my-first-repo-clone 的目录:
cd ~
git clone git@github.com:你的用户名/my-first-repo.git my-first-repo-clone执行后,Git 会把远程仓库的所有历史、分支都下载下来,并自动给它起名为 origin。进入这个克隆目录看看:
cd ~
cd my-first-repo-clone
git log --oneline你会发现历史记录和原来一模一样。这就是“克隆”。
历史整理与工作流技巧
这一章你会掌握四个工作利器:临时储藏现场(stash)、整理提交历史(rebase)、精准摘取提交(cherry-pick)、标记重要版本(tag)。
先在 ~/my-first-repo 里准备干净的环境,并拉取最新状态(如果有远程更新):
cd ~/my-first-repo
git switch master
git pull
git status临时暂停
想象你正在 master 上改一个 Bug,才改了一半,突然被叫去处理紧急任务。但你不想提交这个半成品,又需要切换分支。
制造一个半成品工作区
修改 hello.txt,随便加一行(但不提交):
echo "WIP: stash demo line" >> hello.txt查看状态,工作区是脏的(modified)。现在突然要切到其他分支,Git 会阻止或让你带着修改切走(如果有冲突可能切不过去)。优雅的做法是把工作区“储藏”起来。
储藏修改
git stash执行后,用 git status 查看——工作区干净了!刚才的修改消失了。再用 cat hello.txt 确认,文件回到了上次提交的状态。储藏的内容被 Git 存到了一个临时区域,可以随时取出。
查看储藏列表
git stash list会看到类似:
stash@{0}: WIP on master: xxxxx 最近一次提交信息恢复储藏


