git

git
gogongxtgit进阶学习资料:
git小技巧
git基础概念
commit branch HEAD
commit就是节点,可以理解为整个git就是通过一个个commit构建起来的,每个commit都会基于一个父节点。commit
通过哈希值唯一标识
branch会指向一个具体的节点
HEAD指向一个分支(间接指向了那个分支的commit节点),如果直接指向某个具体的commit,则处于detached状态,新的提交不属于任何分支,新提交可能丢失,所以需要及时转成新的分支
git reset和git checkout切换commit区别
理解这个,你首先需要理解git基础概念的知识,理解branch和HEAD的区别
git reset移动的是HEAD当前所在的分支,把当前分支移动到某个commit节点git checkout移动的是HEAD,进入detached HEAD状态 branch仍然指向原来的commit节点 git checkout使用前需要清除local changes,所以不用需要stash,不用担心类似reset —hard丢失内容
例如:
1
2
3
4
5
6
7
8
9
10
# 原来的状态
A - B - C1 - C2 - C3 (main -> C3, HEAD -> main -> C3)
# 如果是:git reset --hard C1
A - B - C1 (main -> C1, HEAD -> main -> C1)
# 如果是:git checkout C1
A - B - C1 - C2 - C3 (main -> C3)
\
D (HEAD -> D) <-- 新提交,但无分支引用git rebase和merge区别
参考:https://blog.csdn.net/u010698107/article/details/129000177
简单来说,就是在合并分支过程中:
- merge会保留分支更多的信息,但也会更乱一些,对应下图紫色箭头变化
- rebase会直接把新的提交移动到主分支顶部,更简洁一些,没有分支分叉
| 特性 | Rebase | Merge |
|---|---|---|
| 提交历史 | 线性、整洁 | 保留原始分支结构 |
| 工作原理 | 重新应用提交到新基础 | 创建新的合并提交 |
| 冲突处理 | 可能在每个提交时遇到冲突 | 一次性解决所有冲突 |
推荐使用rebase进行分支合并:
1
2
3
4
5
6
git checkout feature
git rebase master
# 如果rebase出现冲突
git add <file> # 标记冲突已解决
git rebase --continue # 继续 rebase
git rebase --abort # 取消rebase刚开始时可能会提示要设置pul.rebase ,我们设置成true
- 默认行为:
git pull=git fetch+git merge - 设置后:
git pull=git fetch+git rebase
1
git config --global pull.rebase truegit 处理历史记录 合并历史记录等
有两种方法,第一种是cherry pick,比如我们想处理abc分支的几条commit,
可以创建另一个分支,然后cherry pick这几个有需要的分支,然后再使用git rebase合并这几个cherry pick过来的内容
另一个就是直接git rebase 合并几个分支:
git rebase -i HEAD~N 比如: git rebase -i HEAD~3
然后把几个对应的commit前面改成s,也就是squash,合入到最新的分支
退出再退出就可以了
git 永久记住账号密码:(适用于http连接)
git config --global credential.helper store
git如果要使用http进行push,github已经不再支持输入账号密码进行连接,可以使用token
具体参考:https://blog.csdn.net/matafeiyanll/article/details/130091999
git已经拉取了一个分支,想把别的分支拉取下来,怎么做
单个分支直接:直接git checkout分支名就行了
拉取所有分支:
1
2
3for branch in $(git branch -r | grep -v '\->'); do git branch --track "${branch#origin/}" "$branch" done
git切换到指定的tag
1
2
3
4
5
6
7
8
git checkout <tag_name>
# 如果这时候tag在远程,不在本地,先拉下来
git fetch --tags
# 查看可以切的tag
git tag
git tag -l -n # 查看带注释的taggit修改最近一次提交的用户名和邮箱
有时候提交前忘了修改邮箱
可以先把本地邮箱改了,例如:
git config --local user.email "gongxiaotian@didiglobal.com" && git config --local user.name "gongxiaotian"
修改完成后,再重新amend提交
1
git commit --amend --reset-author --no-editgit拉取github的PR并cherrypick到自己的分支
git remote add upstream {git_addr}
先添加github对应仓库代码地址
git fetch origin pull/123/head:pr-123
拉取对应pr分支,把pr#123拉取到本地,命名为pr-123
git log pr-123 查看那个pr分支的提交记录
git cherry-pick -n <提交哈希>cherry-pick那个pr的commitgit cherry-pick -n <提交A的哈希>^..<提交D的哈希>如果有多个commityi一起cherry-pickgit cherry-pick —continue报错了修改后继续提交
git常用命令
git config
git配置总共分成三个地方
- /etc/gitconfig
(几乎不会使用),
git config —system - ~/.gitconfig
(常用),
git config —global - .git/config
(针对当前git项目仓库),
git config —local
最常用的还是第二个,例如 git config —global user.email
和 git config —global user.name
默认如果不加就是-local
使用git config --list列出当前的配置信息
其中优先级 3>2>1
git status
用来看当前的仓库代码状态
这个其实超级重要,用好了很厉害,它会给出很多git的使用提示
比如你已经把test.txt stage了,然后不小心rm了文件
这时候执行git
status,会看到提示可以git restore <file>可以恢复文件
.gitignore
<name>忽略匹配到的文件名或目录名(默认递归)#表示注释*.a忽略所有以.a结尾的文件(默认递归)!test.a不忽略test.a文件/<name>只忽略项目根目录下的内容,子目录仍然跟踪<name>/忽略目录下的所有文件,其实加不加/都一样/*/<name>忽略一级目录下的文件内容/**/<name>忽略任意级目录下的文件内容(包括0级,也就是ignore文件所在根目录)
其它细节:
- 使用
*的细节doc/*.txt忽略doc下的0级子目录以txt结尾的文件doc/*/*.txt忽略doc下的一级子目录下的以txt结尾的文件doc/**.txt忽略doc下的任意级子目录以txt结尾的文件
git branch/checkout
git branch <branchname> 创建一个新的分支
git checkout -b <branchname>
创建一个新的分支并切换过去
git checkout -b <branchname> <frombranch>
从一个分支frombranch创建一个新的分支并切换过去
git branch -d <branchname>
删除一个分支,如果有未merge的内容,会报错,强制删除使用-D
git branch -D <branchname> 强制删除一个分支
git push origin -d <branch-name> 删除远程分支
git checkout -
和linux的cd -一样,在最近的两个分支之间切换
git branch -m <old_branchname> <new_branchname>
将分支重命名
git branch -av 查看本地和远程的分支和最新的提交信息
git merge
git merge <branchname>
把branchname分支的更改合并到当前HEAD所在分支
merge 在可以的情况下默认会使用fast forward模式
fast forward不会创建新的commit记录,比如master也会和dev保持相同的commit信息
关于快进合并(fast forward)可以查看https://blog.csdn.net/weixin_42051619/article/details/107449425
git merge --no-ff <branchname>
不使用快进合并分支
不快进合并会额外产生一条commit记录,(记录了merge记录)
好处是可以保留分支信息
git reset
git reset —hard HEAD^
回退到之前版本,回退个数是^符号个数
git reset —hard HEAD~1
回退到之前版本,回退个数是~后跟的数字
git reset —hard <commitID>
回退到之前的指定版本
如果回退以后无法找到新的commit记录,可以使用git reflg
查看操作的历史记录,从中找到之前的commit记录,然后再reset退回去就好了
总的来说,三种reset针对不同区域的内容会做如下变化,不显示指定默认是mixed
| 模式 | HEAD 移动 | commit内容 | 暂存区 staged | 工作目录 unstaged | 安全性 |
|---|---|---|---|---|---|
--soft |
✅ | 给到staged |
保留 | 保留 | ⭐⭐⭐⭐ |
--mixed |
✅ | 给到unstaged |
给到unstaged |
保留 | ⭐⭐⭐ |
--hard |
✅ | 丢弃 |
丢弃 |
保留 | ⭐ |
git reflog
git log记录的是commit记录
git reflog记录的是操作记录
git stash
git stash [push] [-all] [-m “info”]
- 默认不加就是push
-all可以把untracked也放进来-m添加信息例如:git stash push -m "WIP: info"
git stash apply [--index] [<stash>]应用stash,apply应用后不会删掉对应的stash
—index可以让原本处于staged区域的继续在staged
git stash pop [--index] [<stash>]和apply基本一样,区别在于应用后会删掉对应的stash
git stash list 列出stash
git stash show [-p] [<stash>]
显示stash内容,-p可以详细展示
git stash drop [<stash>] 删除stash
git stash branch <branchname> [<stash>]创建新的分支并把stash应用于分支
git stash save和push基本相同,优先用push
git stash clear删除所有stash,谨慎操作
最常用的就是pull或者切分支之前先git stash,然后操作执行完后再git stash apply —index
git tag
git tag 列出所有标签
git tag v1.0 创建轻量化标签
git tag -a v2.0 -m “release” 创建带注释的标签
git tag -l “v*”
搜索标签,支持正则(和gitignore类似)
git show <tagname> 显示tag信息
git push origin <tagname> 推送某些指定的标签
git push origin —tags 推送所有标签
git tag -d <tagname> 删除本地标签
git push origin :refs/tags/<tagname>
删除远程标签,以下都是一样的
git push origin —delete tag <tagname>
git tag -d <tagname>
git fetch --tags 从远程拿到tag
git fetch origin --tags 从指定远程拿到tag
git blame
git blame <filename>
diff/ git diff
diff <srcfile> <descfile>
比较两个文件的差异
diff -u <srcfile> <desfile>
比较两个文件的差异,展示为git diff样式
git diff
查看当前内容和暂存区的内容的差异(有哪些更改没加到暂存区)暂存区是src,工作区是des
git diff HEAD
查看最新节点和当前工作区的差异(没有commit的差异)HEAD是src,工作区是des
git diff —cached
查看暂存区和HEAD的差异(添加了哪些到暂存区)HEAD是src,暂存区是des
diff格式解析:
1
2
3
4
5
6
7
8
9
10
--- test1.txt 2024-07-16 14:24:47.623616008 +0800 # - 代表src文件,后面是修改日期
+++ test2.txt 2024-07-16 15:55:21.253642765 +0800 # + 代表des文件,后面是修改日期
@@ -1,4 +1,6 @@ # -1,4代表src第一行开始选择4行,+1,6代表des第一行开始选择6行
-12312 # 下面的代表src需要删除/增加/不变可以变成des
+1212
dfa
f
+
+dfadfgit remote
git remote add origin <origin_url>
这个其实就是给远程链接取个名字叫做origin
git remote remove origin
git remote set-url origin <origin_url>
git remote -v
git alias
git config —local/global/system alias.<aliasname> name
就是把
例如 git config —global alias.br branch
就是把br替换为branch,下次就可以直接输入git br
git remote
git push —set-upstream origin develp
设置当前分支绑定到远程的develop分支
git checkout -b develop origin/develop这个可以用连接远程分支比如
git push的完整操作其实是:git push origin src:desorigin
表示远程的链接别名,src表示本地分支,origin表示远程分支
删除远程分支:推一个空的分支上去
git push origin :des
也可以: git push origin —delete des
git submodule
子模块适用于项目依赖别的项目的情况
git submodule add git@github.com:QuarkGluonPlasma/git-research-child.git child
添加子模块,并clone到目录child下
添加子模块后,会生成一个.gitmodule的文件,里面记录了所有的子模块链接和路径
子模块怎么更新:
- 进入到子模块目录然后git pull
- 更新所有的子模块,在主目录下执行
git clone 默认不会拉取子模块,需要我们手动拉取,有两种
- clone时就指定拉取子模块,
git clone <url> —recursive - 已经clone好了,然后
git submodule init更新,git submodule update —recursive拉取
git subtree
git remote add subtree-origin <url> 添加远程库
git subtree add —prefix=subtree subtree-origin master
把subtree-origin的master分支clone到subtree目录
git subtree pull —prefix=subtree subtree-origin master
更新pull subtree仓库
git cherry-pick
git cherry-pick <commitid>
将某一次提交应用于当前分支
| 场景 | 命令 |
|---|---|
| 应用单个 commit | git cherry-pick <commit-hash> |
| 应用连续多个commit | git cherry-pick <start-commit>^..<end-commit> |
例如: git cherry-pick abc1234^..def5678 | |
应用但不提交 |
git cherry-pick -n <commit-hash>
|
git worktree
git worktree add <path> <branch>
如果分支当前不存在 ,可以添加-b选项创建新分支
示例: git worktree add ../name-feature-1 feature-1
git worktree list 列出所有工作树
git worktree remove <path> 删掉指定工作数
git worktree remove --force <path>








