一、Git介绍
Git 是一个 分布式版本控制系统,用于跟踪代码变更、管理代码历史、并支持多人协作开发。它由 Linus Torvalds 在 2005 年为 Linux 内核开发而创建,现在已经成为软件开发的标准工具。Git的关键概念如下:
|----------------------------|-------------------------------------------------|
| 概念 | 含义 |
| 仓库(Repository) | Git 存储代码的地方,可本地(Local Repo)或远程(Remote Repo) |
| 工作区(Working Directory) | 开发者正在编辑的代码所在目录 |
| 暂存区(Staging Area) | git add 后,文件进入暂存区,等待提交 |
| 提交(Commit) | git commit 把暂存区的文件保存到仓库 |
| 分支(Branch) | Git 允许多个分支同时开发,main 是默认主分支 |
| 远程仓库(Remote) | 代码托管于 GitHub/GitLab/Bitbucket 以支持团队协作 |
| 合并(Merge) | 把一个分支的修改合并到另一个分支 |
| 变基(Rebase) | 把分支历史整理,使提交历史更清晰 |
| 回滚(Revert/Reset) | 还原代码到之前的状态 |
二、Git操作指令
2.1 Gitlab使用
为方便协同开发,项目组需要进行代码协同管理,采用Gitlab进行管理。Gitlab既可以在本地端进行私有部署,也可以网络端申请免费账户,每个账户免费有50GB空间(The most-comprehensive AI-powered DevSecOps platform | GitLab)。
申请到Gitlab账户后,可以直接通过新建项目->建立空白项目,快速生成一个新项目。第一个使用时,添加计算机公钥到账户中:
生成 SSH Key: ssh-keygen -t rsa -b 4096 -C "your-email@example.com"
复制 SSH Key到gitlab: cat ~/.ssh/id_rsa.pub
如果之前添加过,需要先删除再生成:
rm -f ~/.ssh/id_rsa ~/.ssh/id_rsa.pub
2.2 Git典型操作指令
获取远端仓库 git clone
git clone 远端地址
查看本地分支 git branch
git branch
查看远端分支
git branch -r
git remote show origin #列出所有远端分支
git branch -vv #查看绑定远程分支情况
获取远端仓库信息 git fetch
git fetch #从远程仓库获取最新的分支和提交信息,但不会自动合并到本地分支
git fetch origin #同git fetch,默认为origin仓库
git fetch origin+分支 #更新指定分支
git fetch --all #所有远端仓库,包括origin
git fetch --prune #清理已删除的远端分支
新建分支 git checkout
git checkout -b new_feature #本地新建分支
git push -u origin new_feature #推送远端并关联
拉取远端指定分支
git checkout --track origin/feature-xyz
或者:
git fetch origin
git checkout -b feature-xyz origin/feature-xyz
删除分支
git branch -d feature-branch #删除本地分支
git branch -D feature-branch #未合并,强制删除本地分支
git push origin --delete feature-xyz # 删除远端分支
git fetch --prune #清理已删除的远端分支
设置不提交的文件 .gitignore
touch .gitignore # 生成.gitignore文件 忽略相应文件或文件夹
demo.txt
test/
*.log
如果发现文件已经上传远端,需要先删除:
git rm -r --cached build
查看分支状态 git status/log/diff
git status #输出远端分支与本地分支的commit情况
未追踪的文件(Untracked files):
这些文件存在于 工作目录,但 Git 并未跟踪它们(未 git add)。
已修改但未暂存(Changes not staged for commit):
这些文件 在工作目录中被修改了,但尚未 git add。
已暂存但未提交(Changes to be committed):
这些文件已被 git add,但尚未 git commit。
git log #提交记录
git log --oneline --graph --decorate --all #查看分支合并历史
git log branch_name...origin/branch_name #查看分支commit差异
git diff #分支差异
git diff #当前修改但未 git add 的代码差异
git diff --cached #git add 过但尚未 commit 的文件变更
git diff branch_name origin/branch_name # 比较本地分支和远程分支差异
git diff main feature-x #比较两个分支差异
git diff main feature-x -- path/to/file.js #比较两个分支某个文件差异
添加修改代码 git add
git add . #添加所有修改
git add file1.txt file2.txt #添加部分文件修改
提交commit git commit
git commit -m "描述本次更改的内容"
获取远端最新代码 git pull
git pull #以merge方式合入代码,会产生merge commit,代码log变乱
git pull --rebase # 以rebase方式合入代码,无merge commit,会改变本地commit id
提交到远端 git push
git push #推送当前分支到远端
git push origin main #把本地 main 分支推送到 origin/main
取消提交 git reset
git add 后取消:
git reset #取消但保存修改
git reset --hard #取消不保存修改
git commit 后取消:
git reset --soft HEAD~1: #取消commit,保留add
git reset --mixed HEAD~1: #取消commit 和 add
git reset --hard HEAD~1: #取消代码修改
git push后取消:
git revert HEAD #反撤销commit
git push origin main #返撤销推送
临时保存代码 git stash
git stash #用于切换分支和拉取远端代码时临时保存代码
git stash -u #未被跟踪的文件也会保存
代码合并 git merge/rebase
git merge:
git checkout main # 切换到 main 分支
git merge feature-x # 合并 feature-x 分支到 main
git merge有两种合并方式:
Fast-Forward Merge(不会产生 Merge Commit)
feature-x 只是 main 的直接后继:
main: A -- B
feature-x: A -- B -- C -- D
merge后不改变commit ID:
main: A -- B -- C -- D (直接"快进"到 D)
Non-Fast-Forward Merge(会产生 Merge Commit)
如果 main 和 feature-x 有不同的提交:
main: A -- B -- C
feature-x: A -- B -- D -- E
merge后不会改变commit ID,但会增加一个新的merge commit
main: A -- B -- C -- M (合并提交)
\-- D -- E
git rebase:
git checkout feature-x #切换到feature分支
git rebase main #将 feature-x 重新应用到 main
如果 main 和 feature-x 有不同的提交:
main: A -- B -- C
feature-x: A -- B -- D -- E
merge后不会改变commit ID,但会增加一个新的merge commit
* A -- B -- C -- M (main)
\ /
D -- E (feature-x)
rebase后不会增加merge commit,但是会改变commit ID
* A -- B -- C -- D' -- E' (feature-x)
部分commit合并cherry-pick
git checkout main
git cherry-pick <commit1> <commit2> # 只拣选部分提交
git push origin main
代码冲突解决
代码在合并后如果对相同文件同一行做了不同的修改,会发生冲突,冲突标记如下:
<<<<<<< HEAD
// 代码 A(远程分支的版本)
some_function(a);
=======
some_function(a, b); // 代码 B(你的改动)
>>>>>>> add parameter b
处理完成后:
git add .
git rebase --continue
如果暂时不修改 可以直接
git rebase --abort 恢复
三、Git典型实践路径
3.1 单人单分支开发
git stash -u # 如果有修改但还没有commit,先存储本地修改
git pull origin main # 拉取远程代码并同步,执行merge操作,如果单分支与rebase结果一致
或者
git pull --rebase origin main # 拉取远程代码并同步,执行rebase操作
或者
git pull #与git pull origin main
git stash pop # 恢复本地修改,继续编辑代码
git add . # 添加文件
git commit -m "Initial commit" # 提交
git push origin main # 推送到远程
3.2 多人多分支开发
git pull origin develop # 获取最新的 develop 分支
git checkout -b feature-x develop # 从 develop 创建 feature 分支
git add .
git commit -m "Implement feature X"
git push origin feature-x # 推送到远程
feature稳定后合并到develop
git checkout develop
git pull origin develop
git merge --no-ff feature-x # 使用 no-fast-forward 保留 merge 记录
git push origin develop
git branch -d feature-x # 删除本地分支
git push origin --delete feature-x # 删除远程分支
也可以到gitlab提merge request(MR),MR可以设置code review
多人协作开发时,不建议采用rebase进行代码同步,rebase有可能造成commit混乱,主要原因是rebase会修改commit ID。如下所示:Alice和Bob同时基于feature-branch分支进行开发,如果Alice进行变基并推送到main分支,导致DE两个commit ID发生变化为D' E',而Bob的分支还是DE,导致Bob推送代码时会产生冲突。
A - B - C (main)
\
D - E (origin/feature-branch) # Alice 和 Bob 同时在 feature-branch 开发
A - B - C (main)
\
D - E - F (Alice 的本地 feature-branch)
A - B - C (main)
\
D - E - G (Bob 的本地 feature-branch)
A - B - C (main)
\
D' - E' - F' (feature-branch) # Alice 变基后推送