Git/GitHub的基本使用
Git/GitHub 常用操作
前言
很多初学者刚接触 Git 时,会把 Git 理解成“上传代码的工具”。
这种理解不完全错,但很容易导致后面遇到分支、冲突、远端同步、push 被拒绝、PR 合并、rebase、stash、reflog 等问题时完全不知道 Git 在做什么。
更准确地说:
Git 是本地代码版本数据库。
GitHub 是远端 Git 仓库 + Web 页面 + 协作平台。
Git 真正重要的不是某一条命令,而是它背后的模型:
工作区 working tree
↓ git add
暂存区 staging area / index
↓ git commit
本地仓库 local repository
↓ git push
远端仓库 remote repository,例如 GitHub
本文面向有一定 Linux C/C++ 基础、但 Git/GitHub 体系还不熟悉的开发者,目标不是罗列命令,而是通过一条完整学习路线,建立可以支撑日常开发的 Git/GitHub 工作流。
本文约定:
- Linux 环境以 Ubuntu 24.04 为例。
- Windows 环境以 PowerShell 为例。
- 远端仓库以 GitHub 为例。
- 项目以一个简单 C 语言项目
git_c_demo为练习载体。 - 命令或参数第一次出现时会解释其功能和用途,后续重复出现时不再重复解释。
一、安装 Git 与基础配置
在 Linux 上先检查 Git 是否已经安装:
git --version
git:调用 Git 程序。--version:查看当前安装的 Git 版本。
如果没有安装,可以执行:
sudo apt update
sudo apt install git -y
sudo:以管理员权限执行命令。apt:Ubuntu/Debian 系统的软件包管理工具。update:更新本地软件包索引。install:安装软件包。-y:自动回答 yes,避免安装过程中反复确认。
安装后配置用户名和邮箱:
git config --global user.name "你的名字"
git config --global user.email "你的邮箱"
git config:查看或修改 Git 配置。--global:设置当前用户级别的 Git 配置,对当前系统用户的所有仓库生效。user.name:提交记录中显示的作者名称。user.email:提交记录中显示的作者邮箱。
查看配置:
git config --global --list
--list:列出当前配置项。
注意:这里的用户名和邮箱会写进 commit 作者信息里,不等于 GitHub 登录账号,但建议邮箱与 GitHub 账号邮箱保持一致。
二、创建第一个本地 Git 仓库
先创建一个 C 项目目录:
mkdir git_c_demo
cd git_c_demo
mkdir:创建目录。cd:切换当前目录。
创建一个简单的 main.c:
cat > main.c <<'EOF'
#include <stdio.h>
int main(void)
{
printf("hello git\n");
return 0;
}
EOF
cat:在终端打印或写入文件内容。这里配合重定向创建文件。>:把输出写入指定文件,会覆盖原文件。<<'EOF' ... EOF:Here Document,用来一次性写入多行文本。
创建 Makefile:
cat > Makefile <<'EOF'
CC = gcc
CFLAGS = -Wall -Wextra -g
all: main
main: main.c
$(CC) $(CFLAGS) -o main main.c
clean:
rm -f main
EOF
测试编译:
make
./main
make:根据 Makefile 中的规则执行构建。./main:运行当前目录下的 main 可执行文件。./ 表示当前目录。
初始化 Git 仓库:
git init
git init:在当前目录创建一个新的 Git 仓库,本质是生成 .git 目录。
查看当前仓库状态:
git status
git status:查看当前分支、工作区、暂存区状态,包括未跟踪文件、已修改文件、待提交内容等。
这时你会看到 main.c 和 Makefile 是 untracked files。
untracked files 表示:
Git 看到了这些文件,
但还没有开始跟踪它们。
把文件加入暂存区:
git add main.c Makefile
git add:把工作区中的修改加入暂存区,等待下一次 commit。
提交到本地仓库:
git commit -m "初始化 C 项目"
git commit:把暂存区中的内容保存为一次提交。-m:message,直接在命令行指定提交说明。
查看提交历史:
git log --oneline
git log:查看提交历史。--oneline:每个 commit 只显示一行,便于快速查看。
到这里,最核心流程已经完成:
写代码
↓
git add
↓
git commit
三、理解 status、diff、log
日常开发中,下面三个命令使用频率非常高:
git status
git diff
git log
修改 main.c:
sed -i 's/hello git/hello git and github/' main.c
sed:Linux 下的文本处理工具。-i:直接修改文件内容。s/旧内容/新内容/:替换文本。
查看工作区差异:
git diff
git diff:查看工作区和暂存区之间的差异。简单理解就是:你改了什么,但还没 git add。
加入暂存区:
git add main.c
查看暂存区差异:
git diff --staged
--staged:查看暂存区和上一次提交之间的差异,也就是即将被 commit 的内容。
提交:
git commit -m "修改 hello 输出内容"
查看图形历史:
git log --oneline --graph --decorate --all
--graph:用 ASCII 图显示分支关系。--decorate:显示分支名、HEAD、tag 等引用信息。--all:显示所有本地分支和远端追踪分支的历史。
建议以后经常使用:
git log --oneline --graph --decorate --all
它能帮助你看清楚当前仓库的真实历史结构。
四、撤销错误操作:restore、reset、revert 的基本思想
Git 里“撤销”不是一个命令解决所有问题,而是要先判断你要撤销的是哪一层:
工作区修改?
暂存区修改?
已经 commit 的修改?
撤销工作区修改:
git restore main.c
git restore:恢复工作区文件内容。常用于撤销还没有加入暂存区的修改。
如果文件已经 git add 到暂存区,先撤出暂存区:
git restore --staged main.c
--staged:只把文件从暂存区移回工作区,不删除实际修改。
如果 commit 已经提交,而且希望用一个新的提交来抵消它,可以使用:
git revert <commit_hash>
git revert:创建一个新的 commit,用来反向撤销指定 commit 的修改。<commit_hash>:某次提交的编号。
revert 的特点是安全,因为它不删除历史,而是新增一条“反向修改”。
日常建议:
未 add:git restore
已 add 未 commit:git restore --staged
已 commit 且已共享:优先 git revert
五、分支:branch、switch、merge
分支是 Git 的核心能力之一。
查看当前分支:
git branch
git branch:查看、创建或删除本地分支。
创建并切换新分支:
git switch -c feature/demo
git switch:切换分支。-c:create,创建新分支并切换过去。feature/demo:分支名。
切换回 main:
git switch main
在 feature 分支提交后,可以合并回 main:
git merge feature/demo
git merge:把指定分支的修改合并到当前分支。
删除已合并分支:
git branch -d feature/demo
-d:delete,安全删除本地分支。如果 Git 认为该分支未合并,会拒绝删除。
分支的基本模型是:
main
|
A
\
B feature/demo
合并后可能变成:
A --- B main
或者产生 merge commit:
A ---- M main
\ /
B --
六、冲突:两个分支改了同一处代码怎么办?
冲突的本质是:
两个分支改了同一个位置,
Git 不知道最终应该保留哪一个版本。
例如:
<<<<<<< HEAD
printf("hello from main branch\n");
=======
printf("hello from feature branch\n");
>>>>>>> feature/demo
含义是:
<<<<<<< HEAD 到 =======
当前分支的内容。
======= 到 >>>>>>>
要合并进来的分支内容。
解决冲突的标准流程:
git status
cat main.c
nano main.c
git add main.c
git commit
nano:Linux 下常见的终端文本编辑器。
不带 -m 的 git commit:打开编辑器填写提交信息,常用于 merge commit。
解决冲突时必须删除:
<<<<<<<
=======
>>>>>>>
最终文件应该是干净、可编译的代码。
冲突不是错误,而是 Git 在告诉你:
我不能替你决定最终代码应该是什么,
需要你自己判断。
七、GitHub:远端仓库与 SSH 连接
GitHub 可以理解成:
GitHub = 远端 Git 仓库 + Web 页面 + 协作工具
它不仅能保存代码,还能支持:
README
Issues
Pull Request
Actions
Releases
Wiki
Linux 开发中推荐使用 SSH 连接 GitHub。
检查 SSH 目录:
ls -al ~/.ssh
ls:列出目录内容。-a:显示隐藏文件。-l:使用长格式显示文件详细信息。~/.ssh:当前用户的 SSH 配置目录。
生成 SSH key:
ssh-keygen -t ed25519 -C "你的邮箱"
ssh-keygen:生成 SSH 密钥。-t:指定密钥类型。ed25519:一种现代 SSH 密钥算法。-C:添加注释,通常写邮箱,方便识别密钥用途。
启动 ssh-agent:
eval "$(ssh-agent -s)"
eval:执行字符串中的命令。ssh-agent -s:启动 SSH agent,并输出可被 shell 执行的环境变量设置。
添加私钥:
ssh-add ~/.ssh/id_ed25519
ssh-add:把私钥加入 ssh-agent,避免每次连接都手动指定。
查看公钥:
cat ~/.ssh/id_ed25519.pub
只复制 .pub 公钥到 GitHub,不要复制没有 .pub 的私钥文件。
测试连接:
ssh -T git@github.com
ssh:通过 SSH 协议连接远程主机。-T:不分配远程终端,只测试认证连接。git@github.com:GitHub SSH 连接地址。
成功时通常会看到:
Hi 用户名! You've successfully authenticated...
八、本地仓库连接 GitHub:remote、push、origin/main
如果本地已经有仓库,GitHub 上创建了空仓库,可以添加远端:
git remote add origin git@github.com:用户名/git_c_demo.git
git remote:管理远端仓库。add:添加远端。origin:远端仓库别名,约定俗成常用这个名字。git@github.com:用户名/仓库名.git:SSH 格式的远端仓库地址。
查看远端配置:
git remote -v
-v:verbose,显示详细远端地址,包括 fetch 和 push 地址。
第一次推送 main:
git push -u origin main
git push:把本地提交推送到远端仓库。-u:建立 upstream 追踪关系。origin:远端仓库名。main:要推送的本地分支名。
执行后建立关系:
本地 main ←→ 远端 origin/main
这里要区分:
main:
本地分支。
origin/main:
本地记录的远端 origin 上 main 分支的状态。
它不是 GitHub 本身,而是本地保存的一份远端分支快照。
如果远端地址写错,可以修改:
git remote set-url origin git@github.com:用户名/git_c_demo.git
set-url:修改已有远端的 URL 地址。
九、clone:第二台电脑如何接入项目
如果 Windows 主机要接入同一个 GitHub 仓库,应使用:
git clone git@github.com:用户名/git_c_demo.git
git clone:复制一个已有 Git 仓库到本地,包括代码、.git 目录、提交历史、分支、远端配置等。
注意:
clone 不是下载源码。
clone 是复制整个 Git 仓库。
所以 clone 后:
不需要 git init。
不需要 git remote add origin。
因为 clone 已经自动完成了:
创建 .git
复制历史
设置 origin
检出默认分支 main
建立 upstream
Windows 上建议使用 PowerShell:
mkdir D:\GitStudy
cd D:\GitStudy
git clone git@github.com:用户名/git_c_demo.git
cd git_c_demo
git status
Windows 下查看文件:
dir
dir:PowerShell/CMD 下查看当前目录文件列表。
查看文件内容:
Get-Content main.c
Get-Content:PowerShell 下读取并打印文件内容。
打开文本文件:
notepad main.c
notepad:打开 Windows 记事本编辑文件。
十、双机协作:fetch 和 pull
典型环境是:
Ubuntu 24.04 虚拟机
│
│ git push
▼
GitHub
▲
│ git fetch / git pull
Windows 主机
一台机器 push 后,另一台机器不会自动变化。
在另一台机器上可以先执行:
git fetch
git fetch:从远端获取最新提交、分支、标签等信息,更新 origin/main,但不修改当前本地分支和工作区文件。
fetch 后查看历史:
git log --oneline --graph --decorate --all
你可能看到:
origin/main 已经前进
HEAD -> main 还停在旧提交
如果要真正同步当前分支:
git pull
git pull:从远端拉取并合并到当前分支。简单理解:
git pull = git fetch + git merge
更稳妥的习惯是:
git fetch
git log --oneline --graph --decorate --all
git diff main origin/main
git pull
git diff main origin/main:比较本地 main 和远端追踪分支 origin/main 的差异。
十一、push 被拒绝:non-fast-forward
双机协作或多人协作时,经常会遇到:
! [rejected] main -> main (fetch first)
error: failed to push some refs
Updates were rejected because the remote contains work that you do not have locally.
意思是:
远端有你本地没有的提交。
GitHub 拒绝你的 push,防止你覆盖远端历史。
安全处理流程:
git fetch
git log --oneline --graph --decorate --all
git pull
git push
如果 pull 过程中冲突,就按冲突流程解决:
git status
Get-Content main.c
notepad main.c
git add main.c
git commit
git push
不要一看到 push 被拒绝就执行:
git push -f
-f:force,强制推送。它可能覆盖远端已有提交,团队开发中非常危险。
十二、GitHub Pull Request 工作流
真实团队开发通常不直接往 main 推代码,而是:
main 保持稳定
feature 分支开发
Pull Request 合并
标准流程:
git switch main
git pull
git switch -c feature/pr-demo
notepad main.c
git diff
git add main.c
git commit -m "演示 Pull Request 工作流"
git push -u origin feature/pr-demo
然后在 GitHub 网页上:
Pull requests
↓
New pull request
↓
base: main
compare: feature/pr-demo
↓
Create pull request
术语解释:
Pull Request:
GitHub 上的合并请求,用来请求把某个分支的修改合并到目标分支。
base:
目标分支,通常是 main。
compare:
要合并进来的分支,通常是 feature 分支。
PR 页面重点看:
Conversation:
讨论区。
Commits:
这个 PR 包含哪些提交。
Files changed:
这个 PR 修改了哪些文件和哪些行。
Checks:
自动检查结果,例如测试、编译、代码扫描。
确认没问题后:
Merge pull request
Confirm merge
Delete branch
PR 合并发生在 GitHub 上,但本地不会自动更新,所以还要:
git switch main
git pull
git branch -d feature/pr-demo
十三、PR 后续修改与 Code Review
真实开发中,PR 很少一次就合并。常见流程是:
创建 PR
↓
review 提意见
↓
本地继续改
↓
commit + push 到同一个 feature 分支
↓
原 PR 自动更新
Code Review 是合并前的代码检查和讨论过程。常见动作包括:
Comment:
只发表评论。
Approve:
批准 PR。
Request changes:
请求修改,暂时不建议合并。
关键点:
PR 跟踪的是 compare 分支,不是某一次 commit。
所以如果你已经创建了 feature/review-demo 的 PR,后续只需要继续在这个分支上修改:
git branch
notepad main.c
git diff
git add main.c
git commit -m "根据 review 修改输出内容"
git push
GitHub 上原来的 PR 会自动更新,不需要重新创建 PR。
十四、stash:临时保存做到一半的工作
日常开发中经常会出现:
代码写到一半,还不能 commit,
但突然需要切回 main 处理别的任务。
这时使用 stash。
git stash push -m "临时保存当前修改"
git stash:管理临时保存的修改。push:把当前未提交修改压入 stash 栈。这里的 push 不是推送到 GitHub。-m:message,给这次 stash 添加说明。
查看 stash 列表:
git stash list
list:列出当前仓库中的 stash 记录。
恢复 stash:
git stash apply stash@{0}
apply:把指定 stash 的内容恢复到当前工作区,但保留 stash 记录。stash@{0}:最新的一条 stash 记录。
删除 stash:
git stash drop stash@{0}
drop:删除指定 stash 记录。
更快捷的方式:
git stash pop
pop:恢复最新 stash,并在恢复成功后删除这条 stash。
如果新建了未跟踪文件,也想一起 stash:
git stash push -u -m "保存包括新文件的修改"
-u:include untracked,把未跟踪文件也一起保存进 stash。
注意:
stash 是本地操作。
不会 push 到 GitHub。
Windows 的 stash,Ubuntu 看不到。
十五、rebase:让 feature 分支基于最新 main
当你从 main 创建 feature 分支后,别人又往 main 合并了新代码,你的 feature 分支就落后了。
这时有两种方式:
merge main 到 feature
rebase feature 到最新 main 后面
基础 rebase 用法:
git switch main
git pull
git switch feature/rebase-demo
git rebase main
git rebase:把当前分支上的提交重新应用到另一个分支后面。main:这里表示把当前 feature 分支的提交重新放到最新 main 后面。
rebase 前:
F feature
/
A --- M main
rebase 后:
A --- M --- F' feature
|
main
注意:F 会变成 F',commit hash 可能变化。
commit hash:每个 commit 的唯一编号。提交内容、父提交、作者信息或时间变化时,hash 通常也会变化。
如果 rebase 过程中冲突:
git status
notepad 冲突文件
git add 冲突文件
git rebase --continue
--continue:解决冲突并 git add 后,继续执行被暂停的 rebase。
如果状态混乱:
git rebase --abort
--abort:取消当前 rebase,恢复到 rebase 开始前的状态。
安全原则:
自己的本地 feature 分支可以 rebase。
main 分支不要随便 rebase。
已经共享给别人的分支不要随便 rebase。
十六、.gitignore:已经被跟踪的文件为什么 ignore 不掉?
.gitignore 的核心规则是:
.gitignore 只影响未跟踪文件。
如果一个文件已经被 git add / git commit 过,它已经是 tracked file。之后即使写进 .gitignore,Git 也会继续跟踪它。
判断文件是否被 Git 跟踪:
git ls-files local_config.txt
git ls-files:列出 Git 当前正在跟踪的文件。后面跟文件名时,用来判断该文件是否在跟踪列表中。
让 Git 停止跟踪文件,但保留本地文件:
git rm --cached local_config.txt
git rm:让 Git 删除某个文件。--cached:只从 Git 的索引/跟踪列表中移除,不删除工作区真实文件。
如果是目录:
git rm -r --cached build/
-r:recursive,递归处理目录中的所有文件。
查看被忽略文件:
git status --ignored
--ignored:让 git status 额外显示被 .gitignore 忽略的文件。
C/C++ 项目常见 .gitignore:
# object files
*.o
*.out
*.a
*.so
*.exe
# executable
main
# build directories
build/
cmake-build-*/
# debug/core/log
core
core.*
*.log
# editor
.vscode/
.idea/
*.swp
# local config
local_config.txt
通常应该提交:
.c / .h / .cpp / .hpp
Makefile
CMakeLists.txt
README.md
.gitignore
测试代码
必要脚本
通常不应该提交:
.o 文件
可执行文件
build 目录
core dump
日志文件
本地配置
IDE 个人配置
临时测试文件
十七、reflog:误操作后的救命记录
如果你 reset 错了,或者发现 commit “不见了”,先不要慌。
执行:
git reflog
git reflog:查看本地 HEAD 或分支引用的移动记录,常用于找回 reset、rebase、切分支等操作后“看起来丢失”的提交。
git log 和 git reflog 的区别:
git log:
查看当前分支能访问到的提交历史。
git reflog:
查看 HEAD 和分支指针最近移动过的位置。
模拟回退:
git reset --hard HEAD~1
git reset:移动当前分支指针,让当前分支回到指定 commit。--hard:让当前分支、暂存区、工作区都恢复到指定 commit,会丢弃未提交修改,危险。HEAD~1:当前 HEAD 的上一个提交。
如果 reflog 里看到:
abc1234 HEAD@{1}: commit: 添加 reflog 演示提交
可以恢复:
git reset --hard abc1234
abc1234:目标 commit hash,要换成你自己终端里看到的编号。
注意:
reflog 是本地记录。
不会 push 到 GitHub。
Windows 的 reflog,Ubuntu 看不到。
如果你只是演示分支,想强制删除:
git branch -D feature/reflog-demo
-D:强制删除本地分支,即使它没有合并到 main,也会删除。比 -d 危险。
十八、日常开发推荐流程
1. 开始一个新功能
git switch main
git pull
git switch -c feature/功能名
2. 开发并提交
git status
git diff
git add 具体文件
git diff --staged
git commit -m "清晰说明修改"
3. 推送 feature 分支
git push -u origin feature/功能名
4. GitHub 上创建 PR
Pull requests
↓
New pull request
↓
base: main
compare: feature/功能名
↓
Create pull request
5. 根据 review 修改
git diff
git add 具体文件
git commit -m "根据 review 修改代码"
git push
6. PR 合并后本地收尾
git switch main
git pull
git branch -d feature/功能名
7. 另一台机器同步
git switch main
git pull
git status
这就是日常开发最常用的 GitHub Flow。
十九、常见问题处理流程
1. push 被拒绝
git fetch
git log --oneline --graph --decorate --all
git pull
git push
不要直接 git push -f。
2. 发生冲突
git status
Get-Content 冲突文件
notepad 冲突文件
git add 冲突文件
git commit
整理文件时删除:
<<<<<<<
=======
>>>>>>>
3. 代码写到一半要切任务
git stash push -u -m "说明当前临时工作"
git switch main
git pull
恢复:
git switch feature/原来的分支
git stash list
git stash apply stash@{0}
git stash drop stash@{0}
4. feature 分支落后 main
git switch main
git pull
git switch feature/功能名
git rebase main
如果冲突:
git status
notepad 冲突文件
git add 冲突文件
git rebase --continue
5. .gitignore 不生效
git ls-files 文件名
git rm --cached 文件名
git add .gitignore
git commit -m "停止跟踪本地文件"
6. commit 看起来丢了
git status
git reflog
git reset --hard <commit_hash>
不确定时,不要继续乱操作,先保存 git status、git log、git reflog 的输出。
二十、危险命令清单
这些命令不是不能用,而是必须知道风险。
git reset --hard
危险点:
会丢弃未提交的工作区和暂存区修改。
git push -f
危险点:
会强制覆盖远端历史,可能删除别人已经 push 的提交。
git branch -D 分支名
危险点:
会强制删除本地分支,即使它没有合并。
git rm 文件名
危险点:
会从 Git 跟踪中删除文件,也会删除工作区真实文件。
相对安全版本是:
git rm --cached 文件名
它只停止跟踪,保留本地文件。
总结
完成上述全部内容已经足够满足日常开发。上述内容总结如下:
1. 创建仓库。
2. 提交代码。
3. 查看修改。
4. 创建分支。
5. 合并分支。
6. 解决冲突。
7. 推送到 GitHub。
8. 克隆仓库。
9. 双机同步。
10. 处理 push 被拒绝。
11. 使用 Pull Request。
12. 根据 Code Review 继续修改。
13. 使用 stash 临时保存。
14. 使用 rebase 让 feature 基于最新 main。
15. 正确处理 .gitignore。
16. 使用 reflog 找回误操作后的提交。
这些能力已经能支撑:
个人项目
课程项目
实验室代码管理
GitHub 开源项目基础协作
公司中普通 feature 分支开发
进阶部分
下面这些内容在本文中未曾涉及,读者若有需要可以等真实遇到场景再学:
cherry-pick
interactive rebase
squash
submodule
Git LFS
bisect
worktree
GitHub Actions
Git 内部对象 blob / tree / commit / tag
复杂 release 流程
对于刚接触 Git 的开发者,最重要的不是继续堆命令,而是把下面这条主线用熟:
main 拉新
feature 开发
commit 保存
push 远端
PR 合并
pull 同步
stash 临时切任务
rebase 跟上 main
reflog 处理误操作
.gitignore 管理无关文件
结语
Git 真正难的地方,不是记命令,而是理解每条命令在改变什么:
它改的是工作区?
它改的是暂存区?
它创建了 commit?
它移动了分支指针?
它同步了远端?
它重写了历史?
它只是本地操作,还是会影响 GitHub?
使用 Git 的最重要的习惯:
提交前看 git status。
提交前看 git diff。
不要提交编译产物和本地配置。
新功能从 feature 分支开始。
合并用 PR。
出错先看状态,不要乱用危险命令。
以上就是本文全部内容
更多推荐



所有评论(0)