devStandard/docs/learning/e-git/3-git延伸.md
2025-03-29 14:35:49 +08:00

556 lines
15 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# git 延伸
## 生成压缩包
```bash
git archive
```
## 生成patch补丁
```sh
# 把暂存区的变化生成补丁
git diff --cached > staged_changes.patch
# 安装
git apply staged_changes.patch
```
```sh
# 某几次提交
git format-patch <起始提交哈希>..<结束提交哈希>
# 指定输出路径
git format-patch -o path/to/output/directory <起始提交哈希>..<结束提交哈希>
# 应用补丁
git am path/to/output/directory/*.patch
# apply 不会修改提交历史,也不会改变 HEAD 指针的位置。你需要手动提交应用后的更改
# am 适用于处理来自邮件列表或其他外部来源的完整提交系列的补丁文件. 会自动处理提交和合并工作
```
```sh
# 把stash 生成补丁
git stash show -p stash@{索引} > stash_patch.patch
```
## 下载github上的patch
直接在commit的url后面加上.patch
`patch p1 < xxx.patch`
## 常见分支管理
https://www.ruanyifeng.com/blog/2012/07/git.html
## git branch 编辑状态
命令行输入git branch发现进入编辑状态都要:wq非常不方便这样配置
```sh
git config --global core.pager ''
```
## 测试网络
````bash
ssh -T git@192.168.10.40
````
## 远程tag和本地不同
```bash
# 问题场景:
# 同事A在本地创建tagA并push同步到了远程->同事B在本地拉取了远程tagA(git fetch)->同事A工作需要将远程标签tagA删除->同事B用git fetch同步远端信息git tag后发现本地仍然记录有tagA
# 分析对于远程repository中已经删除了的tag即使使用git fetch --prune甚至"git fetch --tags"确保下载所有tags也不会让其在本地也将其删除的。而且似乎git目前也没有提供一个直接的命令和参数选项可以删除本地的在远程已经不存在的tag我目前是没找到有关这类tag问题的git命令~~,有知道的同学可以告知我下,互相进步)。
# 解决方法:
# 删除所有本地分支
git tag -l | xargs git tag -d
# 从远程拉取所有信息
git fetch origin --prune
```
## 非快速合并
* 使用非快速合并的优势
> 分支树更明显
>
> 版本回退时更明确
<img src="https://img.081024.xyz/分支树.jpg" style="zoom:50%;" />
设置使用`git merge --no-ff `合并分支
```bash
# even create extra merge commit when fast forward merge would be possible
git config --global merge.ff false
# 因为git pull 默认为 git fetch + git merge
# 全局设置了no-ff, 会导致git pull 多一次merge local记录
# 设置 pull 时用ff, 或者用rebase, 可以避免这种情况
# 推荐使用 git pull --rebase
# Disallows non ff merges on pull. Overrides merge.ff when pulling
# git config --global pull.ff only
```
## git pull --rebase
* git pull --rebase的优势
> 拉取远程分支到本地, 合并时把本地的提交放到最后, 保持提交曲线为直线, 易于理解
* 默认git pull 和 git pull --rebase的区别
1. 远程分支origin上其他开发人员提交了 `D`, 自己本地分支提交了 `E`
<img src="https://img.081024.xyz/20220104141037.png" alt="image.png" style="zoom:50%;" />
2. **git pull** : 把"origin"分支上的修改pull下来与本地提交合并merge成版本M但这样会形成图中的菱形让人很困惑。
<img src="https://img.081024.xyz/20220104141055.png" alt="image.png" style="zoom:50%;" />
3. **git pull --rebase** : 创建一个新的提交RR的文件内容和上面M的一样但将E提交废除当它不存在图中用虚线表示。rebase的好处是避免了菱形的产生保持提交曲线为直线让大家易于理解。
<img src="https://img.081024.xyz/20220104141104.png" alt="image.png" style="zoom:50%;" />
* 全局设置避免每次带参
```bash
# set up pull to rebase instead of merge
git config --global pull.rebase true
```
* 解决冲突和git pull不同
```bash
# 冲突
# 1. 手动解决冲突
# 2.
git add
# 3.
git rebase --continue
# 终止rebase, 分支会回到rebase开始前的状态
git rebase --abort
```
## windows和unix下的换行符
推荐统一用`LF`
* 方案1 增加 .gitattribute 文件
在 repo 目录下新建 `.gitattribute` 文件,内容为:
```
* text eol=lf
```
* 方案2 修改 Git 配置
```bash
# win系统
git config --global core.autocrlf false // true: 检出时转成crlf, 提交时转成lf
git config --global core.eol lf
# mac linu unix 系统
git config --global core.autocrlf input // 提交时把 CRLF 转换成 LF签出时不转换
git config --global core.safecrlf true // 不允许混用
```
如果只需要修改当前仓库,去掉 `--global`。
## MacOs批量转换crlf为lf
1. 安装
```bash
brew install dos2unix
```
```bash
find ./ -name "*" | xargs dos2unix
```
ps: 实际执行该命令后, 发现哈士奇报错, 没有执行权限, 后来发现是`.husky`下的两个sh文件没有执行权限, 按照stackoverflow上的答案, 使用`chmod +x`应该就可以了
```bash
-rwxr-xr-x 1 nuc staff 116B 2 17 18:12 commit-msg
```
## vim打开文件,每行多出`^M`
同样是换行的问题, 用windows编辑后换行被改了
```sh
vim 下用命令 %s/^M//g
^是用ctrl+v, M是用ctrl+m, 别直接复制
```
## 应用另一个仓库的某个commit到自己的仓库
https://stackoverflow.com/a/9507417/3054511
方法1:
```bash
git fetch <url> <branch> && git cherry-pick <sha>
# 举例
git fetch https://github.com.cnpmjs.org/vbenjs/vue-vben-admin.git main && git cherry-pick b5364fe5469a5c5f084bb80623081f0ff57663a9
```
方法2: 格式补丁
```bash
git --git-dir=../<some_other_repo>/.git \
format-patch -k -1 --stdout <commit SHA> | \
git am -3 -k
```
## 修改默认commit模板
```sh
vim ~/.gitmessage
===
# head: <type>(<scope>): <subject>
# - type: feat, fix, docs, style, refactor, test, build, ci, revert
# - scope: can be empty (eg. if the change is a global or difficult to assign to a single component)
# - subject: start with verb (such as 'change'), 50-character line
#
# body: 72-character wrapped. This should answer:
# * Why was this change necessary?
# * How does it address the problem?
# * Are there any side effects?
#
# footer:
# - Include a link to the ticket, if any.
# - BREAKING CHANGE
#
===
vim ~/.gitconfig
===
[commit]
template = ~/.gitmessage
===
```
## 全局使用commit模板
https://cz-git.qbb.sh/zh/cli/
```sh
npm install -g czg
vim ~/.commitlintrc.js
# 粘贴中英文对照模板
===
// .commitlintrc.js
/** @type {import('cz-git').UserConfig} */
module.exports = {
rules: {
// @see: https://commitlint.js.org/#/reference-rules
},
prompt: {
alias: { fd: 'docs: fix typos' },
messages: {
type: '选择你要提交的类型 :',
scope: '选择一个提交范围(可选):',
customScope: '请输入自定义的提交范围 :',
subject: '填写简短精炼的变更描述 :\n',
body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
footerPrefixesSelect: '选择关联issue前缀可选:',
customFooterPrefix: '输入自定义issue前缀 :',
footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
confirmCommit: '是否提交或修改commit ?'
},
types: [
{ value: 'feat', name: 'feat: ✨ 新增功能 | A new feature', emoji: ":sparkles:" },
{ value: 'fix', name: 'fix: 🐛 修复缺陷 | A bug fix', emoji: ":bug:" },
{ value: 'docs', name: 'docs: 📝 文档更新 | Documentation only changes', emoji: ":memo:" },
{ value: 'style', name: 'style: 🎨 代码格式 | Changes that do not affect the meaning of the code', emoji: ":art:" },
{ value: 'refactor', name: 'refactor: ♻️ 代码重构 | A code change that neither fixes a bug nor adds a feature', emoji: ":recycle:" },
{ value: 'perf', name: 'perf: ⚡️ 性能提升 | A code change that improves performance', emoji: ":zap:" },
{ value: 'test', name: 'test: ✅ 测试相关 | Adding missing tests or correcting existing tests', emoji: ":white_check_mark:" },
{ value: 'build', name: 'build: 📦️ 构建相关 | Changes that affect the build system or external dependencies', emoji: ":package:" },
{ value: 'ci', name: 'ci: 👷 持续集成 | Changes to our CI configuration files and scripts', emoji: ":construction_worker:" },
{ value: 'revert', name: 'revert: ⏪️ 回退代码 | Revert to a commit', emoji: ":rewind:" },
{ value: 'chore', name: 'chore: 🔨 其他修改 | Other changes that do not modify src or test files', emoji: ":hammer:" },
],
useEmoji: false,
emojiAlign: 'center',
useAI: false,
aiNumber: 1,
themeColorCode: '',
scopes: [],
allowCustomScopes: true,
allowEmptyScopes: true,
customScopesAlign: 'bottom',
customScopesAlias: 'custom',
emptyScopesAlias: 'empty',
upperCaseSubject: false,
markBreakingChangeMode: false,
allowBreakingChanges: ['feat', 'fix'],
breaklineNumber: 100,
breaklineChar: '|',
skipQuestions: [],
issuePrefixes: [
// 如果使用 gitee 作为开发管理
{ value: 'link', name: 'link: 链接 ISSUES 进行中' },
{ value: 'closed', name: 'closed: 标记 ISSUES 已完成' }
],
customIssuePrefixAlign: 'top',
emptyIssuePrefixAlias: 'skip',
customIssuePrefixAlias: 'custom',
allowCustomIssuePrefix: true,
allowEmptyIssuePrefix: true,
confirmColorize: true,
maxHeaderLength: Infinity,
maxSubjectLength: Infinity,
minSubjectLength: 0,
scopeOverrides: undefined,
defaultBody: '',
defaultIssues: '',
defaultScope: '',
defaultSubject: ''
}
}
===
# 使用
czg
# 带emoji
czg emoji
# 为了方便可以添加添加alias
```
## 清空远程分支上的所有提交记录
```sh
# 创建孤立分支,并切换到该分支:
git checkout --orphan latest_branch
# 2. 暂存所有文件:
git add -A
# 3. 提交所有更改:
git commit -am "First Commit"
# 4. 删除主分支 master
git branch -D master
# 5. 重命名当前分支为 master
git branch -m master
# 6. 强制推送本地分支:
git push -f origin master
```
## 用远程仓库替换本地仓库
```sh
git fetch -a
git reset --hard origin/master # master指想换的分支
git pull
```
## 已提交的记录更改负责人和邮箱
```sh
# 第一步n代表提交次数
git rebase -i HEAD~n
# 第二步
然后按`i`编辑,把`pick` 改成 `edit`,按'Esc'退出编辑,按`:wq`保存退出
# 第三步
git commit --amend --author="作者 <邮箱@xxxx.com>" --no-edit
# 第四步
git rebase --continue
# 重复第3和4
# 最后
git push --force
```
## 允许提交空目录
在该目录下新建`.gitkeep
## fork项目同步上游
```sh
git remote -vv
git remote add upstream <url>
git remote -vv
git fetch upstream
git merge upstream/main
git push
```
## fork后从上游分支新建分支
1. 网页不支持
2. 从上游仓库克隆一个本地仓库
```sh
# 1. 上游仓库
git remote add upstream https://github.com/原始仓库的用户名/原始仓库名.git
# 2.
git fetch upstream
# 3.
git checkout -b 新分支名 upstream/原始分支名
```
## github 提交部分pr
```sh
git remote add upstream <上游仓库地址>
git fetch upstream
git checkout -b patch-1 upstream/main
git checkout main
git log
# 把你需要pull request的提交ID记录下来,通常只记前七位就可以了
git checkout patch-1
git cherry-pick
git push origin patch-1
# 发起pull request
# 如果上游仓库接受了你的pull request你就可以把`patch-1`分支删除了
# 首先确保不在patch-1分支中。Git不支持在一个分支里删除自己
git checkout main
git branch -D patch-1
git push origin :patch-1
```
## 比较文件的历史版本
vscode `Git History Diff` 插件
为了方便输入commit id , 可以打开该文件的TIMELINE, 筛选git history
## workflow
### 自动更新子模块
```yaml
name: Update Submodules
on:
workflow_dispatch: # 允许手动触发
schedule:
- cron: '10 0 * * *' # 每天午夜执行
jobs:
update_submodule:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: true # 确保检出子模块
- name: Update submodule
run: git submodule update --remote --recursive
- name: Check if changes were made
id: check_changes
run: |
if [ -z "$(git status --porcelain)" ]; then
echo "No changes were made."
else
echo "Changes were made."
git config user.email "actions@github.com"
git config user.name "GitHub Actions - update submodules"
git add .
git commit -m 'Update submodule'
git push
fi
```
### 自动同步上游仓库
```yaml
# .github/workflows/sync.yml
name: Upstream Sync
permissions:
contents: write
on:
schedule:
- cron: "0 0 * * *" # every day
workflow_dispatch:
jobs:
sync_latest_from_upstream:
name: Sync latest commits from upstream repo
runs-on: ubuntu-latest
if: ${{ github.event.repository.fork }}
steps:
# Step 1: run a standard checkout action
- name: Checkout target repo
uses: actions/checkout@v3
# Step 2: run the sync action
- name: Sync upstream changes
id: sync
uses: aormsby/Fork-Sync-With-Upstream-action@v3.4
with:
upstream_sync_repo: hoyolife/stone
upstream_sync_branch: main
target_sync_branch: main
target_repo_token: ${{ secrets.GITHUB_TOKEN }} # automatically generated, no need to set
# Set test_mode true to run tests instead of the true action!!
test_mode: false
- name: Sync check
if: failure()
run: |
echo "[Error] 由于上游仓库的 workflow 文件变更,导致 GitHub 自动暂停了本次自动更新,你需要手动 Sync Fork 一次详细教程请查看https://github.com/Yidadaa/ChatGPT-Next-Web/blob/main/README_CN.md#打开自动更新"
echo "[Error] Due to a change in the workflow file of the upstream repository, GitHub has automatically suspended the scheduled automatic update. You need to manually sync your fork. Please refer to the detailed tutorial for instructions: https://github.com/Yidadaa/ChatGPT-Next-Web#enable-automatic-updates"
exit 1
```
## github cli
```sh
brew install gh
```
## github action
https://docs.github.com/zh/actions/learn-github-actions/understanding-github-actions
## gitignore
全局:
```sh
~/.gitignore_global
# 然后 在 ~/.gitconfig 引入
[core]
excludesfile = ~/.gitignore_global
```
* `*`: 匹配任意字符(不包括路径分隔符)。
* `**`: 匹配任意数量的目录, 包括多级。
* `?`: 匹配任意一个字符。
* `!`: 忽略规则
* `.gitkeep`: 强制追踪, 比如空目录