556 lines
15 KiB
Markdown
Executable File
556 lines
15 KiB
Markdown
Executable File
# 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** : 创建一个新的提交R,R的文件内容和上面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`: 强制追踪, 比如空目录 |