Git简介¶
推荐使用:
基本概念及操作¶
git的备份节点(commit)在底层构成一张DAG,产生的新commit会指向执行命令前所在的commit(例如:merge节点有多个前继,指向的是merge前的commit)因此,每个仓库仅有一个汇点——inital commit。
分支、HEAD、Tag仅仅是指向它们的指针,指针变动(甚至删除)并不会改变它们指向的节点,而只会导致你无法通过上述的单向指向关系找到原来的节点,即产生了unreachable
的节点(类似memory leak)这些unreachable
节点及其相关文件会定期被git清理掉,也就是GC。
备份与还原¶
- add告诉git能管理哪些文件、记录哪些文件的变动(create/delete/modify)
- add会将修改先同步暂存区,暂存区可以理解为一个准备提交的commit
- commit就是确认暂存区的修改,命名并创建备份点
- push修改远程,改动历史时push要加-f
git log --oneline
查看commit链
# 更新已经追踪的文件,未追踪要add
# git add .
git commit -a
git push
# 前跳一次 reset默认的mixed就很好用
git reset HEAD^
# reset错了返回未来 用reflog查HEAD的移动记录
git reflog
git reset <commit>
# 仅恢复某个文件/目录
git checkout <commit> <path>
checkout¶
当使用git checkout命令时,Git会切换到指定的分支,但如果后面接的是文件名或路径,Git则不会切换分支,而是把文件从.git目录中复制一份覆盖到当前的工作目录。与reset –mixed相反,它不会修改暂存区,只会修改工作区。
# 移动HEAD
git checkout test
git checkout <commit>
# 不移动HEAD
git checkout . # 从暂存区覆盖工作区
git checkout HEAD~2 <path> # 拿两个版本前的文件覆盖
切换新任务¶
暂存区并不能存下你手边的任务!它不固定在原来所在的commit上。checkout切换前请任选:
- 直接commit
- 用stash
合并分支¶
分支合并的两种方式,所有合并操作都是别的commit合并入HEAD,且都不会对别的分支产生影响。
# 1.merge多个commit
# 注:pull底层就是把origin/x合并到x
git checkout main
git merge test new
# 解决冲突
git commit -a
# 2.rebase 把从lca开始的修改叠加到某一个commit上
git rebase test
# 解决冲突
git add .
git rebase --continue
其他¶
# author email
git config --global user.name "hyj0824"
git config --global user.email "2417179808@qq.com"
# commit图
git log --oneline --graph
# rm + git add:删除文件并stage
git rm *
# 取消文件追踪不删除文件 make it untracked
git rm --cached *
git restore --staged *
# reset HEAD^1后再做一次commit,也就是修改最新commit
git commit --amend -m "xxx"
git commit --amend --no-edit # 不修改message
git commit --allow-empty -m "a" # 空commit
# 记录变动并合并到最新commit
git commit -a --amend --no-edit
# 删除远端分支
git push origin :new
# cherry-pick 仅拉取特定的commit内容,而不是从lca开始的全部commit
git cherry-pick "sha1" "sha2"
# 立刻 gc
git fsck --unreachable
git reflog expire --all --expire=now
git gc --prune=now
# 路径显示中文
git config --global core.quotepath false
修改代理¶
git config --global http.proxy socks5://127.0.0.1:20170
git config --global https.proxy socks5://127.0.0.1:1080
git config --global http.proxy http://127.0.0.1:20171
git config --global https.proxy https://127.0.0.1:8080
git config --global --unset http.proxy
git config --global --unset https.proxy
git config -l --global
Rebase¶
合并可以用 git rebase <base commit>
,不仅仅可以合并分支,还可以合并commit。
- 确定公共祖先:当前commit与base commit的lca。
- 取出更改:取出当前commit从公共祖先开始的所有提交。
- 应用更改:将这些提交更改依次接续到base commit后方。
在 rebase
过程中,如果出现冲突,Git 会停止并允许你手动解决这些冲突。解决冲突后,你需要使用 git add
将更改加入暂存区,然后使用 git rebase --continue
命令继续 rebase
过程。
rebase
操作的好处是可以将项目的历史变得更加清晰,因为它避免了不必要的合并提交,使得项目的历史看起来像是一条直线。这对于查看项目的历史和维护项目非常有用。
更精细化的修改需要使用 git rebase -i
的交互式操作,它允许你指定从公共祖先开始的每个提交分别采取怎样的操作:
- pick:这是默认操作,即按照原来的顺序将lca链上的提交依次应用到base commit后方。
- reword:修改某个提交的消息,这会在应用该提交之前暂停变基过程,允许你编辑提交消息。
- squash:将提交合并到前一个提交中,并让你给合并后的提交提供一个新的提交消息。
- fixup:将提交合并到前一个提交中,沿用前一个提交的消息。
- edit:对某个提交进行修改、添加新的commit或修复错误。这会暂停变基过程,允许你进行所需的更改,然后继续变基。
- drop:这个选项用于删除某个提交。如果你不再需要某个提交,可以将其行的
pick
改为drop
。
用edit暂停,reset到HEAD^,一个一个add+commit。
squash和fixup不能用在rebase -i列表的首个commit,因为它前面就是要保留的base commit,无法与之合并成新commit
HEAD¶
HEAD是一个指标,指向某一个分支,通常可以把它当作“当前所在分支”来看待。(如:refs/heads/master)。分支也是个指针,指向分支上最新的commit。例如:HEAD -> refs/heads/master -> b431fa7
HEAD如果没有指向本地的分支,则称之为detached head(断头HEAD)。可能发生这个状态的原因有:
- 使用 Checkout 指令直接跳到某个 Commit,而不是 Branch。
- Rebase 的过程其实也是处于不断的 detached HEAD 状态。
- 切换到某个远端分支的时候。
断头HEAD约等于创建一个叫HEAD的临时分支,可以允许你任意checkout某个commit开始工作,提交新的commit与主干分叉。但要切换到其他分支或commit时,你可以任选:
创建一个新分支¶
不切回原分支,给这个临时分支一个名字:git switch -c <new-branch-name>
PS C:\Users\24171\Documents\GitHub\Playground> git checkout 41cb26f
Note: switching to '41cb26f'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
checkout回原来的branch,然后用:git branch <new-branch-name> <commit>
PS C:\Users\24171\Documents\GitHub\Playground> git commit -a
[detached HEAD c599bef] modified: note.md
1 file changed, 3 insertions(+), 6 deletions(-)
PS C:\Users\24171\Documents\GitHub\Playground> git checkout master
Warning: you are leaving 2 commits behind, not connected to
any of your branches:
c599bef modified: note.md
9a7602e new file: note.md
If you want to keep them by creating a new branch, this may be a good time
to do so with:
git branch <new-branch-name> c599bef
Switched to branch 'master'
不新建分支¶
checkout回原来的branch,使用提示中的sha1来merge/rebase。
.gitignore¶
以”#”号开头表示注释;
以斜杠“/”开头表示目录;
以星号“*”通配多个字符;
以问号“?”通配单个字符
以方括号“[]”包含单个字符的匹配列表;
以叹号“!”表示不忽略(跟踪)匹配到的文件或目录;
*.txt , *.xls # 表示过滤某种类型的文件
target/ # 表示过滤这个文件夹下的所有文件
/test/a.txt , /test/b.xls # 表示指定过滤某个文件下具体文件
!*.java , !/dir/test/ # !开头表示不过滤
*.[ab] # 支持通配符:过滤所有以.a或者.b为扩展名的文件
/test # 仅仅忽略项目根目录下的 test 文件,不包括 child/test等非根目录的test目录
已追踪的文件,必须先取消追踪,才能被.gitignore忽略。因为其仅影响git add找未追踪文件,而不是找文件修改。
Fun Facts¶
在空目录中随便放一个文件就行了。如果当前还没有文件可以放,或者不知道该放什么文件,通常可以放一个名为“.keep”或“.gitkeep”的空文件,让Git能“感应”到这个目录的存在
空的目录无法被add检测,因为git add只关心文件内容生成blob对象,git commit后才组织tree对象形成目录。
其实,Git并不是很在意空间的浪费,能够快速、有效率地操作才是Git关注的重点。
基本上commit时优先记录整个文件,而不是差异。
Git根据这串信息,把原本放在.git/objects中以SHA-1计算命名的目录及文件,一个一个地复原成原来的样子
跟主席树一样,死去的oi又开始攻击我。
就像是推送空的内容去更新在线的cat分支的内容,也算是变相地把该分支删除。
git push origin :cat
git push origin master:cat
A:B means push A as B