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链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
checkout¶
当使用git checkout命令时,Git会切换到指定的分支,但如果后面接的是文件名或路径,Git则不会切换分支,而是把文件从.git目录中复制一份覆盖到当前的工作目录。与reset --mixed相反,它不会修改暂存区,只会修改工作区。
1 2 3 4 5 6 7 |
|
切换新任务¶
暂存区并不能存下你手边的任务!它不固定在原来所在的commit上。checkout切换前请任选:
- 直接commit
- 用stash
合并分支¶
分支合并的两种方式,所有合并操作都是别的commit合并入HEAD,且都不会对别的分支产生影响。
1 2 3 4 5 6 7 8 9 10 11 12 |
|
其他¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
修改代理¶
1 2 3 4 5 6 7 8 9 10 |
|
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>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
checkout回原来的branch,然后用:git branch <new-branch-name> <commit>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
不新建分支¶
checkout回原来的branch,使用提示中的sha1来merge/rebase。
.gitignore¶
以”#”号开头表示注释;
以斜杠“/”开头表示目录;
以星号“*”通配多个字符;
以问号“?”通配单个字符
以方括号“[]”包含单个字符的匹配列表;
以叹号“!”表示不忽略(跟踪)匹配到的文件或目录;
1 2 3 4 5 6 |
|
已追踪的文件,必须先取消追踪,才能被.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分支的内容,也算是变相地把该分支删除。
1 2 |
|
A:B means push A as B