← 返回博客

Git 常用操作

Git 日常开发速查,涵盖提交规范、分支、远程仓库、凭据、子模块、代理和历史整理。

#git#devtools

Git 常用操作

这是一份按主题整理的日常速查表。命令执行前,请先确认当前仓库、分支和远程地址;涉及 reset --hard、强制推送或删除操作时,务必先确认是否需要保留现有历史和本地修改。

提交规范

# 模板
 
module.type(scope): subject
- #id1 xxxx
- #id2 xxxx
Related: #id
 
# eg
 
module.fix(#id): xxxxx
module.feat(#id): xxxxx
 
# 说明
 
type:
    feat: 新功能
    fix: 修复问题
    docs: 文档更新
    style: 代码格式(不影响代码运行的变动)
    refactor: 代码重构(既不是修复bug也不是添加新功能的代码更改)
    perf: 性能优化
    opt: 广义的优化,不局限于性能,表示对代码或实现的改进
    enhance: 用于表示改进现有功能的实现,但功能本质未发生变化。适用于用户体验优化、功能的轻微改进
    improve: 一般用于强调对代码或功能的改进,适用于广义的优化和提升
    refine: 用于小范围的优化和精细化调整,适合代码优化但不涉及结构性变化。
    test: 添加测试或更新测试
    build: 构建系统或外部依赖项的更改(如webpack,npm)
    ci: 持续集成相关的变动
    chore: 日常事务/维护, 适用于非功能性修改,例如日志打印、工具配置、脚本更新等。
    revert: 回滚某次提交

常用命令

bash
# 删除本地分支
 
git branch -d master
 
# 删除远程分支
 
git push origin --delete master
 
# 移动目录
 
git mv
git mv plugins/uniplugin_wecall plugins/uni-ios-sdk/uniplugin_wecall

私人仓库认证

优先使用 SSH 密钥或操作系统提供的凭据管理器。不要把真实密码、Token 或授权信息直接写进远程 URL。

使用 SSH

将远程仓库地址改成 SSH 地址,并把公钥添加到代码托管平台:

bash
git remote set-url origin git@<host>:<owner>/<repo>.git

使用 Git Credential Manager

Git Credential Manager 会调用系统凭据存储,具体支持情况取决于操作系统和安装方式。配置前请先参考其官方文档。

临时缓存凭据

如果确实需要使用 Git 的缓存助手,可以只缓存有限时间:

bash
git config --global credential.helper 'cache --timeout=3600'

不推荐:明文保存凭据

下面的配置会把凭据保存到用户目录下的 ~/.git-credentials,通常不应在共享电脑或生产环境使用:

bash
git config --global credential.helper store

Gitea 合并选项一览

合并方式含义是否保留提交历史是否生成合并提交主干是否线性推荐场景
✅变基后创建合并提交先rebase再merge --no-ff✅ 是✅ 是❌ 否避免冲突、保留历史
✅创建合并提交普通merge,不 rebase✅ 是✅ 是❌ 否保留完整 PR 提交
✅变基后快进rebase再fast-forward✅ 是❌ 否✅ 是想要线性历史
✅创建压缩提交squash 所有 PR 提交成一个❌ 否❌ 否✅ 是主干清爽、PR小
✅仅快进merge --ff-only,不 rebase✅ 是(必须线性)❌ 否✅ 是用于自动更新分支
✅手动合并不自动合并,需自己操作⚠️ 取决于你⚠️ 取决于你⚠️ 取决于你高级场景、需要更多控制

Git LFS 安装

Git LFS 用于管理 Git 仓库中的大文件。Ubuntu/Debian 系统可以直接安装发行版提供的软件包:

bash
sudo apt update
sudo apt install git-lfs
git lfs install

如果发行版仓库中的版本不满足需求,再参考 Git LFS 官方安装文档 选择对应的安装方式。

fork项目更新后, 原仓库也有更新, 冲突情况下合并

Fork 仓库后,自己的分支有提交,而原仓库(upstream)有更新,在 Gitea 网页上尝试合并时遇到了冲突。你需要 手动同步原仓库的最新代码到你的 Fork,并解决冲突。按照以下步骤操作:

步骤 1:添加原仓库(upstream)

如果你之前没有添加 upstream,需要先添加它:

git remote add upstream <原仓库的URL>

可以检查是否添加成功:

git remote -v

应该会看到 origin(你的 Fork)和 upstream(原仓库)。

步骤 2:获取原仓库的最新代码

git fetch upstream

然后切换到你的 main 分支(或者你 Fork 时的默认分支,如 master):

git checkout main

再合并 upstream 的最新代码:

git merge upstream/main

如果有冲突,Git 会提示哪些文件冲突。

步骤 3:解决冲突

打开有冲突的文件,手动修改代码,使其符合需求。通常冲突的代码长这样:

diff
<<<<<<< HEAD
你的 Fork 仓库的代码
<mark>=</mark>==
原仓库(upstream)的最新代码
>>>>>>> upstream/main

你需要手动修改为正确的内容,并保存文件。

然后运行:

git add .
git commit -m "解决合并冲突"

步骤 4:推送到你的 Fork 仓库

git push origin main

这时你的 Fork 仓库已经同步了原仓库的最新代码,并且冲突已经解决。

步骤 5:在 Gitea 发起合并请求

现在可以回到 Gitea,在网页上尝试合并你的 Fork 到原仓库。如果没有新的冲突,应该可以顺利合并了。

如果你在分支上开发,而不是 main 直接改

如果你在 feature-branch 分支上开发,而 main 已经同步了 upstream/main,你还需要:

git checkout feature-branch
git merge main  # 把最新的 main 合并到你的分支
 
git push origin feature-branch

然后再在 Gitea 上合并。

这样,你的 Fork 就会和原仓库保持同步,同时保留你自己的修改!

如果还是有问题,可以发具体的错误信息,我帮你分析!😃

拆分大仓库

  • 在迁移时,git filter-repo 会保留子目录的历史记录,并删除其他目录的历史,这样在新仓库中只会保留你想要的子目录及其相关的提交历史。
  • 如果只需要迁移某些目录而不丢失其历史,确保在 filter-branch 或 filter-repo 过程中准确指定路径。
# 把几个目录拆分到新仓库
 
# 安装 git-filter-repo 工具
 
pip install git-filter-repo
 
# 克隆原仓库并进入
 
git clone --no-checkout <old-repository> temp-repo
cd temp-repo
 
# 过滤出多个子目录
 
git filter-repo --path <path-to-subdirectory1> --path <path-to-subdirectory2> --path <path-to-subdirectory3> --force
git filter-repo --path WeCall --path license/WeCall --force
 
# 添加远程仓库
 
git remote add origin <new-repository-url>
 
# 推送代码
 
git push -u origin master
 
cd ..
rm -rf temp-repo

git clone指定本地文件夹名

jsx
git clone xxx.git <dir_name>
git clone -b develop https://github.com/example/repo.git my_project
git clone -b self https://github.com/leiax00/NotionNext.git --config http.proxy=http://192.168.0.3:7890 notionNext

git丢弃本地修改, 同步服务器最新

bash
# 丢弃所有本地更改
 
git reset --hard
 
# 获取远程最新代码
 
git fetch --all
 
# 强制同步到远程分支:
 
git reset --hard origin/分支名称

git分隔符设置

bash
// 提交时转换为LF,拉取时转换为CRLF
git config --global core.autocrlf true
 
// 提交时转换为LF,拉取时不转换
git config --global core.autocrlf input
 
// 不转换
git config --global core.autocrlf false
 
# 建议设置成input; 或者所有都设置为false由开发者自行管理
 

git无法检测到文件变化

bash
# 1. 删除.git/index
 
rm .git/index
 
# 2. 重置本地trace
 
git reset

关闭权限和所有者信息检查

bash
git config --global core.fileMode true
git config --global core.ignoreStat false
 
# core.fileMode false 将会忽略文件的执行权限变更。
 
# core.ignoreStat true 将会忽略文件系统不支持的元数据信息(比如所有者信息)。
 
# 关闭后就无法显示文件变更了, 毫无意义
 

git创建远程仓库(服务端)

bash
# 创建一个用户
 
adduser git
 
# 生成ssh证书
 
ssh-keygen
 
# 初始化git仓库, 根路径在: /repo_dev/git-server, 仓库以 .git后缀结尾
 
git init --bare test.git
 
# 设置仓库所属git
 
chown -R git:git test.git
 
# 添加要拉取该仓库客户端的公钥, 在客户端上执行
 
ssh-copy-id -i ~/.ssh/id_rsa.pub git@10.1.0.3
 
# 禁用git用户ssh登录(为了安全性, 也可不设置), 该步骤之后客户端公钥添加则需要手动添加到 /home/git/.ssh/authorized_keys 文件中, 一行一个
 
# git:x:1001:1001:,,,:/home/git:/bin/bash
 
git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell
 
# 客户端拉取仓库
 
git clone git@10.1.0.3:/repo_dev/git-server/test.git
 
# 权限管理: Gitolite: https://github.com/sitaramc/gitolite
 
# 公钥管理: Gitosis: https://github.com/res0nat0r/gitosis
 

git log中文乱码

bash
git config --global i18n.commitencoding utf-8

git status中文文件名显示为转义字符

git status 查看时,中文路径显示为八进制转义(如 40-\345\267\245\344\275\234),原因是 Git 默认开启了路径转义(core.quotepath = true),会把非 ASCII 字符转义成八进制序列以避免乱码。

bash
# 关闭路径转义,让中文文件名正常显示
git config --global core.quotepath false

修改分支名

bash
git branch -m master main
git fetch origin
git branch -u origin/main main
git remote set-head origin -a

git修改远程地址

bash
git remote set-url origin git@<host>:<owner>/<repo>.git
git remote -v

Git 删除本地修改并同步远程

reset --hard 会丢弃当前分支的未提交修改。确认不需要保留本地改动后再执行。

bash
git fetch --all
git reset --hard origin/main

Git 不要关闭 SSL 校验

不建议使用 http.sslverify falsehttps.sslverify false。这会关闭 TLS 证书校验,可能让中间人攻击变得可行。

如果 Git 报证书错误,应修复 CA 证书、代理或系统时间,而不是永久关闭校验。排查完成后,确保没有留下错误配置:

bash
git config --global --unset http.sslverify
git config --global --unset https.sslverify

个人代码行数统计

bash
git log --author=dingwei --since=2022-09-26 --until=2022-10-20 --format='%aN' | \\
sort -u | while read name; do echo -en "$name\\t"; git log --author="$name" --pretty=tformat: --numstat | \\
awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\\n", add, subs, loc }' -; \\
done
 
# leiax00 added lines: 36726, removed lines: 20575, total lines: 16151
 

git切换远程分支

bash
git remote rm origin
git remote add origin git@github.com:<owner>/<repo>.git
git push --set-upstream origin main

Git 配置用户信息

bash
git config --global user.name "<your-name>"
git config --global user.email "<your-email>"
 
# 仅当前仓库生效
git config user.name "<repository-name>"
git config user.email "<repository-email>"

git子模块操作

bash
git submodule add <url> <path>   # 其中,url为子模块的路径,path为该子模块存储的目录路径。
 
# clonefu父项目后, 子项目更新方法
 
git submodule init
git submodule update
 
git submodule update --init --recursive
 
# 删除子模块
# 先从索引和工作树移除
git submodule deinit -f -- <path>
git rm -f <path>
 
# 如果仍有残留,再检查并删除 .git/modules/<path>
# 同时确认 .gitmodules 中已经移除了对应条目

git配置代理

bash
git clone https://github.com/vuetifyjs/vuetify.git --config http.proxy=http://127.0.0.1:10809
 
git config --global http.proxy 'socks5://127.0.0.1:10808'
git config --global https.proxy 'socks5://127.0.0.1:10808'
 
git config --global http.proxy http://10.1.0.3:7890
git config --global https.proxy https://127.0.0.1:10809
 
git config --global --unset http.proxy
git config --global --unset https.proxy
 
# 不写 --global 或者设置为 --local 则只配置当前项目的代理
 
# 或者修改单个项目的 .git/config
 
[core]
	repositoryformatversion = 0
	filemode = false
	bare = false
	logallrefupdates = true
	symlinks = false
	ignorecase = true
[remote "origin"]
	url = https://github.com/leiax00/exploding-universe.git
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
	remote = origin
	merge = refs/heads/main
[submodule "universe-view"]
	url = https://github.com/leiax00/universe-view
	active = true
[http]
	proxy = http://127.0.0.1:10809

git多远程仓库提交

ini
[remote "github"]
	url = git@github.com:<owner>/<repo>.git
	fetch = +refs/heads/*:refs/remotes/github/*
[remote "gitee"]
	url = git@gitee.com:<owner>/<repo>.git
	fetch = +refs/heads/*:refs/remotes/gitee/*
[remote "origin"]
	url = git@<code-host>:<owner>/<repo>.git
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
	remote = origin
	merge = refs/heads/main

GitHub SSH 连接失败

如果已经配置 SSH,但执行 git fetchgit clone 时仍然要求输入密码,先确认远程地址确实是 SSH 格式:

bash
git remote -v
ssh -T git@github.com
ssh -vT git@github.com

然后按顺序检查:

  1. SSH agent 是否加载了正确的密钥;
  2. 公钥是否已经添加到 GitHub;
  3. 当前网络、代理和 DNS 是否正常;
  4. ~/.ssh/config 是否配置了错误的 Host 或 IdentityFile。

不建议把某个固定 IP 长期写入 /etc/hosts。GitHub 的地址可能变化,错误的固定 IP 还会导致后续连接失败。

将当前分支多个提交记录合并(已push)

  1. 创建一个临时分支保存当前内容:
bash
git checkout -b temp-squash
  1. 回到你想保留一条提交的分支:
bash
git checkout main  # 或你的目标分支
 
  1. 重置到上一个你想保留的基础提交(如果是全新仓库,可以用初始提交或者空提交):
bash
git reset --soft <commit_hash>  # 或者 HEAD~N
 
  • --soft 保留修改在暂存区
  1. 创建一条新的提交:
bash
git commit -m "保留最终修改的一条提交"
  1. 强制推送到远程(覆盖原来的历史):
bash
git push -f origin main

⚠ 注意:强制推送会覆盖远程历史,其他克隆该仓库的人会受到影响。

git分支合并


方法1:保留完整提交记录

main 分支执行:

bash
git checkout main
git merge --no-ff feature/b
  • 会生成一个 merge commit,保留 feature/b 分支的所有提交历史。
  • 如果有冲突,需要手动解决再提交。

方法2:压缩提交(squash 合并)

这里有两种情况:

2.1 直接 squash 合并

main 分支执行:

bash
git checkout main
git merge --squash feature/b
git commit
  • 所有 feature/b 的改动会合成一个提交。
  • 不会生成 merge commit(看起来就像你在 main 上直接写了一个大提交)。

2.2 先在 feature 分支上 rebase 再合并

如果你希望 分支干净,但仍然要保留合并关系:

bash
git checkout feature/b
git rebase -i main   # 压缩掉 feature/b 上的提交,整理为一个或少数几个提交
 
# 然后切回 main
 
git checkout main
git merge --no-ff feature/b
  • 这样最终 main 上会有一个 merge commit,但 feature/b 的历史是已经压缩过的。

📌 小结:

  • git merge --no-ff → 保留所有提交,生成 merge commit
  • git merge --squash → 压缩成一个提交,不会生成 merge commit
  • git rebase -i + git merge --no-ff → 先压缩分支历史,再带着 merge commit 合并