源代码管理
源代码如何进行管理,是一个工程性的考量。工作中接触到的项目使用的源码管理方式各异,了解它们能让我们更游刃有余。个人偏好纯粹依赖 Git 管理项目源码,下面汇总日常使用 Git 时踩过的坑,以及 SVN、Repo 这两类仍在使用的工具的要点。
Git
跨平台代码提交
在 Git 中遇到“文件看起来内容一样,但 Git 提示有差异”是非常常见的情况。这通常是因为文件内部发生了肉眼看不见的元数据或不可见字符的改变。
不希望 Git 追踪文件权限的修改(在 Windows 环境下通常不需要追踪),可以关闭文件权限检查:
git config core.filemode false
注意:建议只在当前项目下关闭,不要加
--global,以免影响需要追踪权限的脚本项目
通过配置 Git 或 .gitattributes 文件来统一团队的换行符规范。
- Windows 用户推荐配置:
git config --global core.autocrlf true(提交时自动转为 LF,检出时转为 CRLF) - Mac/Linux 用户推荐配置:
git config --global core.autocrlf input(提交时转为 LF,检出时不转换)
解决 Git 提交冲突问题
每次我在写完代码,要向服务器提交时,经常会忘记提交前确认自己或其他同时是否提交过,而直接执行提交。如果之前确实有人提交,而我又没有同步,那么就会出现如下报错:
amass@AmassPC:~/Projects/Lifestyle$ git push origin
To gitea.amass.fun:amass/Lifestyle.git
! [rejected] main -> main (fetch first)
error: failed to push some refs to 'gitea.amass.fun:amass/Lifestyle.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
如果这时再使用 git pull 合并,就会出现很恶心的情况。首先,要解决远程仓库和本地仓库的冲突,然后再次 commit 写提交记录,那么在 git log 里面就会出现如下记录:
commit 54869cf8fef83261fe08afe596cf9bac891f6ace
Merge: 2c711d6 6c345a7
Author: amass 168062547@qq.com
Date: Sun Sep 4 17:51:39 2022 +0800
Merge branch 'master' of gitee.com:amass9610/Younger
作为一个强迫症,这时不能忍受的。我想如果一个对代码审核有要求的公司,大概率也不会允许的吧。
今天再次碰到这种情况了,在以往我可能是选择从 Git 服务器在 clone 一个新的仓库,然后使用 Beyond Compare 工具进行比对,把我的修改一个一个文件的查找修改至新拉下来的仓库里面,最后再提交,不过这样真心浪费时间,步骤也很繁琐。
今天决心一定要弄清楚这种情况要怎么解决,一番搜索之后,原来 git reset 就可以解决这个问题。首先我们先查看本地参考的 log:
amass@AmassPC:~/Projects/Lifestyle$ git log
commit 571b09d87c86e0dab5ddfdccb14ea00b34192937 (HEAD -> main)
Author: amass 168062547@qq.com
Date: Sat Nov 11 14:20:33 2023 +0800
Add React Material package.
commit b769af4a5f6c2f2e2dd559eb81fe93faf102f732 (origin/main, origin/HEAD)
Author: amass 168062547@qq.com
Date: Sun Nov 5 19:30:53 2023 +0800
Optimize build script.
......
571b09d8 就是当前我们已经提交的 id,且正处于现在这个节点。b769af4a 则是上一次提交记录,也就是最近未发生冲突的节点,所以我们直接回退致改节点,但是不撤销已修改的文件即可:
git reset b769af4a5f6c2f2e2dd559eb81fe93faf102f732
输出如下:
amass@AmassPC:~/Projects/Lifestyle$ git reset b769af4a5f6c2f2e2dd559eb81fe93faf102f732
Unstaged changes after reset:
M docusaurus.config.js
M package.json
M src/pages/LoginPage.jsx
将特定文件回退至指定 commit
在平常工作中,也许是代码提交不规范,或种种原因,之前提交了临时代码,然后现在想把代码回退至之前某个历史提交:
git checkout <commit-hash> -- <file-path1> <file-path2>
回退远程提交
有时提交的代码不需要了,且不想有那些提交历史。可以使用强制推送,但是一定要 慎用,少用!!!
git log # 查找提交记录
git reset --hard <commit-hash> # 使用reset回退到该提交<commit-hash>
git push --force <remote_name> <branch_name> # 强制推送到远程仓库
分支合并
git checkout master # 切换到 master 分支
git pull origin master # 确保 master 分支已是最新的
git merge dev # 将 dev 分支合入 master 分支
# 可能会有冲突,解决冲突
# 使用 git commit 提交合并后的代码
git push origin master # 将合并后的更改推送到远程的 master 分支
git branch -d dev # 删除本地 dev 分支
git push origin --delete dev # 删除远程 dev 分支
中文乱码
在默认设置下,中文文件名在工作区状态输出,中文名不能正确显示,而是显示为八进制的字符编码。
PS E:\Lifestyle> git status
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: .gitea/workflows/deploy.yaml
modified: docusaurus.config.js
modified: "\346\210\221\347\232\204\345\215\232\345\256\242/2024-05-10-Git\344\275\277\347\224\201\227\256\351\242\230.md"
解决办法:将 git 配置文件 core.quotepath 项设置为 false。quotepath 表示引用路径,加上 --global 表示全局配置,在终端输入命令:
git config --global core.quotepath false
从某个 tag/commit 创建分支
我将 git 仓库切换到了某个 tag 下,如何在当前状态创建一个名为 dev 的分支?
# 确认当前处于目标 tag 对应的提交
git checkout <tag_name>
# 直接创建并切换到 dev 分支:
git checkout -b dev
SVN
拉取代码(将 http://172.16.0.252/svn/DanKiV5/branches/B023_BJ_ALG 拉取至 B023_BJ_ALG 目录下):
svn checkout http://172.16.0.252/svn/DanKiV5/branches/B023_BJ_ALG B023_BJ_ALG
查看工作区状态:
svn status
撤销工作区某个文件的修改
svn revert source/2.Software/src/netconfig/src/zNetStateThread.cpp
输出:
Reverted 'source/2.Software/src/netconfig/src/zNetStateThread.cpp'
查看某次提交修改的文件
svn diff --summarize -c r1015 # r1015 为某次提交号
输出:
M source/2.Software/tools/hi3516dv500/start-app.sh
M source/2.Software/config/DK-FSAN-HI3516DV500/conf.txt
回退本地代码至某次提交
用于在本地查看某次提交的代码。
svn update -r 1014 # 1015 为某次提交号
查看差异 vimdiff
创建 vimdiff.sh 文件:
#!/bin/sh
# 去掉前5个参数
shift 5
# 使用vimdiff比较
vimdiff "$@"
然后编辑 ~/.subversion/config 文件,在 [helpers] 找到 diff-cmd,取消注释并修改为:
[helpers]
# 这里需要使用绝对路径
diff-cmd = /home/amass/.subversion/vimdiff.sh
然后我们使用 svn diff 文件路径 就可以通过 vim 查看比对差异了。使用 :qall 退出 vimdiff。
git svn
使用 git-svn 将源 Subversion 库转换为本地 Git 存储库,后续的本地操作就完全基于 git,只有在需要跟远程 svn 交互时,才需要使用 git svn xxx 命令。
安装:
sudo apt install git-svn
从 SVN 仓库拉取代码:
git svn clone http://172.16.0.252/svn/DanKiV5/branches/B023_BJ_ALG
从 SVN 上获取最新代码:
git svn rebase
这个其实类似于 git 里面的
git pull --rebase。所以如果有发生冲突,同等于在 rebase 的时候发生冲突要处理的方式一样。
把 git 的 commit 推上 svn:
git svn dcommit
Repo
Repo 起源自 Android 项目,在 Android 这个开源项目中存在 700 个左右的 Git 仓库,当你下载 Android 源码时,如果手动同步这么多的 Git 仓库,必然很累又不可靠,所以即使没有 Repo,你也可能会想着通过一个脚本工具来同步 Android 的 Git 仓库,只不过现在 Google 帮你写了这个脚本,并且还加了很多其他的功能,你可以直接使用。
Repo 不是用于替代 Git,而是依赖于 Git 的工具,对仓库真正的操作还是通过 Git,只不过是通过 Repo 调用 Git 对每个仓库来进行操作。
Repo 的具体使用可以参考 Gitee 的 Repo 工具使用介绍 这篇文章。