使用 Git 管理项目文件和代码
Git 安装和配置
Git 是一个免费、开源的分布式版本控制系统,用于跟踪文件和代码的修改历史,支持多人协作开发。
Git 由 Linus Torvalds 于 2005 年创建,最初用于管理 Linux 内核的代码。与 SVN 等集中式版本控制系统不同,Git 是分布式的,每个克隆出来的仓库都包含完整的项目历史,提交、查看历史、切换分支等大部分操作都能在本地完成。
使用 Git 的必要性
开发过程中,文件会被反复修改。使用 Git 可以:
- 保存完整历史:每次修改都有记录,可随时对比、回退。
- 支持多人协作:自动合并各自的改动,冲突时给出提示。
- 并行开发:新功能、缺陷修复、实验都可以放在独立分支上进行。
- 数据安全:每个克隆出来的仓库都是完整副本,可随时恢复整个项目。
- 对接主流平台:GitHub、GitLab、Gitee 等平台和各类 CI/CD 工具都以 Git 为基础。
- 个人项目:独自开发也能借助版本回退、差异对比管理项目,不必再靠复制多份文件区分版本。
使用 AI 更需要 Git
借助 AI 编写和修改代码时,工具会直接读写项目文件,一次改动可能跨越几十个文件,出错的影响也更大。版本管理的作用更明显:
- 审查 AI 的改动:用
git diff查看 AI 修改了什么,确认后再提交。 - 改错了随时回退:只要之前提交过,就能回退到改动前的版本。
- 尝试不同方案:让 AI 在多个分支上各写一版,留优弃劣,不影响主线代码。
- 让 AI 更容易理解项目:清晰规范的提交记录,能让 AI 给出更贴合上下文的建议。
- 记录 AI 协作情况:在提交信息中标注哪些改动由 AI 生成、哪些经过人工审核。
安装 Git(Windows)
在 Git 官网下载 Windows 安装包,双击安装即可:https://git-scm.com/download/win(项目全称 Git for Windows,也叫做 msysGit)。
其他安装方式:
- 自动安装:使用社区维护的 Git Chocolatey 包。
- GitHub Desktop:自带图形化和命令行版本的 Git,也支持 PowerShell。
运行 Git 前的配置
安装后需要配置一次,每台计算机只需一次软件升级时会保留,可以随时再次运行命令修改配置。Git 的配置变量存储在三个位置:
--system:/etc/gitconfig,系统全局配置,需要管理员权限才能修改。--global:~/.gitconfig,对当前用户的所有仓库生效。--local:.git/config,默认当前仓库的配置文件。
每一级会覆盖上一级的配置。在 Windows 上,用户级配置位于 C:\Users\<用户名>\.gitconfig,系统级配置在 Git 的安装目录下。
用户信息
第一次使用前必须设置用户名和邮箱,每次提交都需要一起提交:
$ git config --global user.name "名字"
$ git config --global user.email "邮箱"
--global 只要运行一次;如果想针对单个项目使用不同信息时,在项目目录下运行不带 --global 的同样命令。
查看配置
$ git config --list # 列出所有配置
$ git config user.name # 查看某一项配置
获取 Git 仓库
有两种方式:把本地目录初始化为仓库、克隆一个已有的仓库。
初始化本地仓库
进入项目目录后执行:
$ git init
这个命令创建一个 .git 子目录,包含仓库所需的全部文件。如果目录中已有文件,继续把它们纳入跟踪并提交:
$ git add *.c
$ git add LICENSE
$ git commit -m 'initial project version'
克隆现有仓库
$ git clone https://github.com/libgit2/libgit2
会在当前目录下创建同名目录并拉取全部历史数据。如果需要自定义目录名,在命令末尾加上新目录名:
$ git clone https://github.com/libgit2/libgit2 mylibgit
记录每次更新到仓库
工作目录下的文件分为两种状态:已跟踪(Git 可以管理)和未跟踪( Git 不能管理)。修改文件后,通过暂存提交保存到仓库。
查看文件状态
$ git status
输出内容很多,可以加 -s 使用紧凑格式:
$ git status -s
M README
MM Rakefile
A lib/git.rb
?? LICENSE.txt
左栏是暂存区状态,右栏是工作区状态;?? 表示未跟踪,A 表示新增,M 表示修改。
暂存文件
用 git add 开始跟踪新文件,或把改动放入暂存区:
$ git add README
$ git add CONTRIBUTING.md
git add 暂存的是执行命令时的那个版本;如果之后又改了文件,需要重新 git add 一次。
提交更新
$ git commit # 打开编辑器输入提交说明
$ git commit -m "说明文字" # 直接在命令中给出提交说明
提交前先用 git status 确认所需文件都已暂存。提交记录的是暂存区的快照。
跳过暂存直接提交
git commit 加上 -a 选项,会自动暂存所有已跟踪文件的修改并提交,跳过 git add:
$ git commit -a -m 'added new benchmarks'
查看改动
git diff:查看尚未暂存的改动。git diff --staged:查看已暂存的改动(与git diff --cached同义)。
$ git diff
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8ebb991..643e24f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -65,7 +65,8 @@
-merged in.
+merged in. Also, split your changes into comprehensive chunks if your patch is
+longer than a dozen lines.
+ 开头是新增内容,- 开头是删除内容。
忽略文件
不加入 Git 管理的文件,可以在项目根目录创建 .gitignore 文件排除:
# 忽略所有 .a 文件
*.a
# 但跟踪 lib.a
!lib.a
# 只忽略当前目录下的 TODO 文件
/TODO
# 忽略任何名为 build 的文件夹
build/
# 忽略 doc/ 及其子目录下的所有 .pdf 文件
doc/**/*.pdf
- 空行和
#开头的行会被忽略。 - 支持标准 glob 模式:
*匹配任意字符,?匹配单个字符,[abc]匹配方括号中的任一字符,**匹配任意中间目录。 - 以(/)开头防止递归;以(/)结尾只匹配目录;模式前加叹号(!)取反。
删除与重命名文件
$ git rm PROJECTS.md # 删除文件,并停止跟踪
$ git rm --cached README # 只停止跟踪,保留本地文件
$ git mv README.md README # 重命名文件
git mv 相当于 mv + git rm + git add 三条命令的组合。
查看提交历史
git log 按时间顺序列出所有提交,最近的排在最上面:
$ git log
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date: Mon Mar 17 21:52:11 2008 -0700
changed the version number
常用查看方式:
$ git log --oneline # 每个提交压缩成一行
$ git log -p -2 # 显示最近两次提交的具体改动
$ git log --stat # 显示每次提交的文件修改统计
$ git log --pretty=format:"%h - %an, %ar : %s" # 自定义输出格式
$ git log --since=2.weeks # 最近两周的提交
$ git log --author="Scott" --grep="fix" # 按作者 / 关键字筛选
$ git log -- README.md # 只看某个文件的历史
Table1. git log 的常用选项
| 选项 | 说明 |
|---|---|
-p | 按补丁格式显示每个提交的差异。 |
--stat | 显示每次提交的文件修改统计。 |
--oneline | 每个提交压缩为一行显示。 |
--graph | 以 ASCII 图形显示分支与合并历史。 |
--pretty | 使用 oneline、short、full、format 等格式显示。 |
Table2. 限制 git log 输出的选项
| 选项 | 说明 |
|---|---|
-<n> | 仅显示最近的 n 条提交。 |
--since, --after | 仅显示指定时间之后的提交。 |
--until, --before | 仅显示指定时间之前的提交。 |
--author | 仅显示指定作者的提交。 |
--grep | 仅显示提交说明中包含指定字符串的提交。 |
-S | 仅显示添加或删除内容匹配指定字符串的提交。 |
撤消操作
修补最后一次提交
漏加了文件,或者提交说明写错了:
$ git add forgotten_file
$ git commit --amend
用新的提交替换上一次提交,仓库历史中不会留下两次记录。
取消暂存的文件
$ git reset HEAD CONTRIBUTING.md
文件会回到“已修改但未暂存”状态。
撤消对文件的修改
$ git checkout -- CONTRIBUTING.md
把文件还原成上次提交时的样子。
git checkout -- <file>会丢弃该文件在本地的所有修改,无法恢复,确认后再执行。
分支操作
分支可以在独立的工作线上开发新功能,不影响主分支,完成后再合并回去。
创建与切换分支
首先,假设你正在项目上工作,master 分支上已经有一些提交。要处理问题追踪系统中的 #53 问题,先新建一个分支并切换过去:
$ git checkout -b iss53 # 新建分支并立即切换(相当于下面两条命令)
$ git branch iss53 # 只新建分支
$ git checkout iss53 # 只切换到已有分支
之后在 iss53 分支上继续开发并提交。切换分支时,如果工作目录里有未提交的修改,可能会被拒绝切换。
合并分支(快进)
假设此时接到一个紧急修复任务,先切回 master,新建 hotfix 分支处理问题,再合并回 master:
$ git checkout master
$ git checkout -b hotfix
$ vim index.html
$ git commit -a -m 'fixed the broken email address'
$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
index.html | 2 ++
1 file changed, 2 insertions(+)
hotfix 直接延续自 master,Git 只需把指针向前移动,这种情况叫快进(fast-forward)。合并完成后可以删除 hotfix:
$ git branch -d hotfix
合并分支(三方合并)
如果两个分支各自都有新提交(从更早的提交分叉开来),合并时 Git 会做一次三方合并,并自动创建一个合并提交:
$ git checkout master
$ git merge iss53
Merge made by the 'recursive' strategy.
index.html | 1 +
1 file changed, 1 insertion(+)
合并完成后,不再需要的分支可以删除:git branch -d iss53。
解决合并冲突
如果两个分支修改了同一文件的同一部分,Git 就无法自动合并,会给出冲突提示:
$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
用 git status 查看处于“未合并”状态的文件:
$ git status
On branch master
You have unmerged paths.
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: index.html
打开冲突文件,会看到类似下面的标记:
<<<<<<< HEAD:index.html
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
please contact us at support@github.com
</div>
>>>>>>> iss53:index.html
======= 上面是当前分支(HEAD)的内容,下面是 iss53 分支的内容。保留想要的部分(或自行整合),并删掉 <<<<<<<、=======、>>>>>>> 这些标记行。
解决完所有冲突后,用 git add 把文件标记为已解决(可以用 git status 确认),然后运行 git commit 完成合并提交。也可以使用图形化工具解决冲突:git mergetool。
分支管理
$ git branch # 列出所有分支,* 表示当前所在分支
$ git branch -v # 同时显示每个分支的最后一次提交
$ git branch --merged # 列出已合并到当前分支的分支
$ git branch --no-merged # 列出尚未合并到当前分支的分支
尚未合并的分支用 -d 删除会失败,确认不再需要时可以强制删除:
$ git branch -d testing # 未合并时会报错
$ git branch -D testing # 强制删除
--merged和--no-merged不加参数时比较的是当前分支;在命令后加上分支名,可以查看其它分支的合并情况,例如git branch --no-merged master。