Git
https://tom.preston-werner.com/2009/05/19/the-git-parable.html
Git基于SHA-1的对象存储机制会创建
其中3b是SHA1的第一个字节, 之所以为第一个字节创建单独目录, 是因为一些文件系统在单个目录处理大量文件的效率很差,
该方法可以均匀创建256路分区缓解此问题.
.git/objects/3b/18e12dba79e4c8300dd08e728b8dad
这样的文件路径.其中3b是SHA1的第一个字节, 之所以为第一个字节创建单独目录, 是因为一些文件系统在单个目录处理大量文件的效率很差,
该方法可以均匀创建256路分区缓解此问题.
HEAD
当前分支的最近提交ORIG_HEAD
合并(merge)和复位(reset)时, 会用 ORIG_HEAD 指向旧版本的 HEAD.FETCH_HEAD
最近抓取(fetch)分支的 HEAD, 该引用仅在抓取之后短暂有效.MERGE_HEAD
合并(merge)进行时, 指向合并的分支(ORIG_HEAD 是master, MERGE_HEAD 是branch).
C~
/ C~1
上一代提交(在只有一个父提交的情况下与 C^1
相同)C~~
/ C~1~1
/ C~2
向上两代提交(即 C~1
的 C~1
)C^
和 C^1
第一个父提交(合并的提交拥有多个父提交)C^^
和 C^1^1
和 C^2
第二个父提交(合并的提交拥有多个父提交)提交范围的本质是"图的可达性"
{commit1}..{commit2}
指从commit1到commit2的所有提交(不包含commit1, 语义上commit1应该是commit2的祖先)^{commit}
在范围内排除特定提交{branch1}..{branch2}
技术上和commit范围一样, 语义上表示在branch2却不在branch1上的所有提交...{commit}
即 HEAD..{commit}
的缩写{commit}.. 即{commit}..HEAD
的缩写{commit1}...{commit2}
取commit1和commit2的对称差(极少使用),即取
commit1可达但commit2不可达的祖先提交
+ commit2可达但commit1不可达的祖先提交
git config --global user.name "用户名"
git config --global user.email "电子邮箱"
配置文件为
~/.gitconfig
git config --global alias.show-graph 'log --graph --abbrev-commit --pretty=online'
该命令添加了show-graph别名
git clone --bare existing_repo bare_repo_path
克隆为裸仓库git clone --bare existing_repo
克隆裸仓库到当前目录git remote add github git://github.com/BlackGlory/something.git
添加远程仓库git fetch github
从上游获取最新代码git merge github/master
合并上游master代码至当前分支git fetch origin pull/{id}/head:{name}
获取某个PR的分支, 并命名这种push的缺点在于push完后不会自动更新local/master, 需要用fetch重新拉取local/master.
如果项目需要同步推送到多个上游, 说明项目很可能处于一种不良模式, 应尽可能避免使用到此技巧.
如果项目需要同步推送到多个上游, 说明项目很可能处于一种不良模式, 应尽可能避免使用到此技巧.
# 让push的时候push到origin(不可省略, 否则不会push到origin)git remote set-url --add --push origin $(git remote get-url origin) # 让push的时候push到localgit remote set-url --add --push origin $(git remote get-url local)
git fetch --all
git pull
是 git fetch
和 git merge
的组合命令.git pull --rebase
以rebase方式拉取, 本地commit会变基到最新的远程commit上.注: rebase时, 需要用
git rebase --continue
而不是 git commit
提交冲突第一行: 简单描述提交内容
第二行: 空行
第三方以后: 详细陈述提交内容
第二行: 空行
第三方以后: 详细陈述提交内容
中止提交: 提交信息留空并退出编辑器
git commit --amend
重新编辑上一次提交(如果使用 git add
, 则可以将新的修改并入此次提交)git commit --amend --no-edit
重新提交git branch {分支名}
创建分支git checkout {分支名}
切换分支git checkout -
切换到上一个分支git checkoub -b {分支名}
创建并切换分支git log --graph
以图表形式查看分支git branch -a
查看包括本地和远程仓库的所有分支git branch -m <oldname> <newname>
重命名本地分支git branch -m <newname>
重命名本地当前分支git merge-base {commit1} {commit2}
返回两个提交的共同祖先git stash
暂存当前修改git stash --include-untracked
暂存当前未提交的跟踪文件和未跟踪的文件(常用)git stash apply
恢复最后一次暂存的文件git stash pop
恢复最后一次暂存的文件并删除暂存(常用)git stash list
查看全部的暂存列表git stash drop {name}
删除暂存git stash clear
删除全部暂存不要在主分支等公共分支上使用rebase, 这会破坏历史.
git rebase {branch}
以变基的形式将分支并入当前分支git rebase -i {commit}
以修改历史的形式变基到 {commit}
, 例如从分支变基到最新的mastergit rebase -i HEAD~5
以修改历史的形式变基最后5次提交squash意味着此提交将合并到先前的提交中, 包括此提交的message
fixup意味着此提交将合并的先前的提交中, 但不包括此提交的message
fixup意味着此提交将合并的先前的提交中, 但不包括此提交的message
git commit --fixup HEAD
创建提交作为HEAD的fixup, 为autosquash作准备git commit --squash HEAD
创建提交作为HEAD的squash, 为autosquash作准备git rebase -i --autosquash {commit}
在交互式编辑器里, 将自动处理先前提交的fixup和squash为交互式rebase默认开启autosquash:
git config --global rebase.autosquash true
git merge --no-ff {分支名}
将对应分支合并至当前分支, 但需要手动填写合并提交信息git merge --no-commit --no-ff {分支名}
手动进行合并git merge --squash branch
将分支里所有commit对文件的修改添加到当前stage, 执行
git commit
以完成合并.最终, 这相当于"将分支里的commit压缩(squash)为一个commit进行合并", 该commit默认情况下会以squasher为作者, 所有被压缩的commit为父commit.
git show {commit}
查看commit包含的修改git show next:{filename}
查看filename在next分支上的样子git log {filename}
查看目录或文件的日志git log -p {filename}
查看包含diff的日志git log --follow {filename}
查看该文件的历史日志(不使用follow的情况下, 由于重命名等原因会不显示重命名之前的日志)
git log --oneline
单行显示日志git log --oneline --graph
单行显示日志并打印节点图git diff HEAD
将工作树与HEAD(最后一个提交)进行比较git clean -fd
删除本地所有未追踪文件和目录git reset --hard {commit}
将当前分支(HEAD)退回指定commit, 也用于撤销合并等操作git restore .
与上一条作用相同git clean -fd && git restore .
恢复至上一次提交(放弃提交内容)git reset --soft HEAD~
撤销上一次提交(保留改动内容)git checkout {commit} -- path/to/file
从指定提交中恢复指定文件git tag {tagName}
创建轻量级, 无注释(non-annotated)/轻量级标签.git tag --annotate {tagName}
创建有注释(annotated)的标签git tag -n99
查看所有标签及它们携带的消息(non-annotated标签会显示commit消息).使用annotated标签被认为是最佳实践,
因为这种标签会生成对象, 记录标签的创建者, 创建日期和额外的标签消息
(在实际使用中, 标签消息没什么实际用处, annotated标签主要功能主要是为了记录标签创建者和创建时间).
因为这种标签会生成对象, 记录标签的创建者, 创建日期和额外的标签消息
(在实际使用中, 标签消息没什么实际用处, annotated标签主要功能主要是为了记录标签创建者和创建时间).
由于这个原因, non-annotated标签被认为适合私人和临时性使用, 而annotated标签被用于发布.
两者的区别可以通过
而non-annotated标签需要额外加上
git describe
体现出来, annotated标签能够被直接查询,而non-annotated标签需要额外加上
--tags
flag.git push --follow-tags
在push的同时向远程发送 有注释 标签.git push origin :{tagName}
删除远程tag, 与下一条等同git push --delete origin {tagName}
删除远程tag, 与上一条等同git revert HEAD
创建一个反转上一个提交的提交git revert {commit}..HEAD
反转从特定提交开始至今的所有修改.查看git的历史提交缓存, 从中可以抢救出那些被误删的提交.
git reset HEAD@{index}
前往reflog列出的指定提交.cherry-pick可将一个或多个提交的 更改 抓取到当前分支上.
通常用于跨分支打补丁的场景, 当不想合并整个分支的时候, 使用cherry-pick.
通常用于跨分支打补丁的场景, 当不想合并整个分支的时候, 使用cherry-pick.
自Git 2.23加入的新命令, 用于切换分支.
功能与checkout基本一样, 专门分离出一个命令是因为checkout承载了太多的功能.
功能与checkout基本一样, 专门分离出一个命令是因为checkout承载了太多的功能.
Git 2.23加入的新命令.
git restore path/to/file
相当于
git checkout HEAD -- path/to/file
恢复指定提交的文件(缩写为
-s
)git restore --source commit path/to/file
和subtree不同, submodule将仓库作为引用加入到当前仓库中.
submodule会向仓库添加
.gitmodules
文件记录子模块仓库的元数据, 此文件应该被纳入版本控制.添加子模块:
git submodule add 子模块的仓库地址 子模块路径
克隆具有子模块的项目:
git clone --recurse-submodules 项目地址# 以上命令等价于以下步骤git clone 项目地址cd 项目地址git submodule update --init# 以上命令等价于以下步骤git clone 项目地址cd 项目地址git submodule init # 作用是将`.gitmodules`复制到`.git/config`里git submodule update
获取同时更新任何嵌套的子模块:
git submodule update --init --recursive
- 期望仓库是嵌套的(一个仓库里有另一个仓库), 而不是多个仓库合并成的仓库.
- 希望能更容易切换子模块的提交(子模块只跟踪提交而不是分支/标签).
- 希望直接在当前仓库里贡献代码到子模块的仓库.
- 子模块可能频繁的创建和删除.
- 子模块永远具有可访问性.
和submodule不同, subtree将仓库作为副本加入到当前仓库中.
默认情况下会包括仓库原有提交, 这很适合将polyrepo迁移至monorepo:
git subtree add --prefix=子树路径 子树仓库的仓库地址 子树仓库的提交(或分支)
使用--squash选项可以在不包含仓库原有提交的情况下, 添加仓库为子树:
git subtree add --prefix=子树路径 --squash 子树仓库的地址 子树仓库的提交(或分支)
从源仓库拉取提交:
注意, 由于子树对Git是透明的, 因此每次pull实际上都是直接对比远程仓库和本地仓库, 然后进行merge.
git subtree pull --prefix=子树路径 子树仓库的地址 子树仓库的提交(或分支)
注意, 由于子树对Git是透明的, 因此每次pull实际上都是直接对比远程仓库和本地仓库, 然后进行merge.
子树是来自社区的名为git-subtree的脚本, 而非Git的核心功能.
因此当一个项目被作为子树添加时, Git是不知道子树这个概念的, 它对于Git来说是透明的.
这也是为什么每次调用子树命令都需要使用额外的参数, 它其实更像是子模块的手动版本.
因此当一个项目被作为子树添加时, Git是不知道子树这个概念的, 它对于Git来说是透明的.
这也是为什么每次调用子树命令都需要使用额外的参数, 它其实更像是子模块的手动版本.
- 基本上不需要向外部项目反馈代码时
- 希望能够很方便地编辑引用的外部项目的内容
- 希望将两个项目的源代码彻底合并(即希望将外部项目废弃)
- 不需要其他使用者掌握额外的知识(子模块会引入很多复杂的新知识)
情况: 操作者对该案例的原始仓库A没有控制权, 对Pull Request的仓库B也没有控制权.
目标: 操作者需要修改他人PR里的分支, 发送新的PR给仓库B, 以便原始PR能够被成功合并仓库A.
目标: 操作者需要修改他人PR里的分支, 发送新的PR给仓库B, 以便原始PR能够被成功合并仓库A.
- 1.
git clone 仓库B的URL
克隆仓库B - 2.
git remote add target 仓库A的URL
将仓库A作为远程名称target加入到当前仓库 - 3.
git fetch --all
下载仓库A和仓库B的所有远程分支 - 4.
git checkout PR分支
签出仓库B里的PR分支 - 5.
git checkout -b patch
创建一个B - 6.
git merge target/master
合并来自仓库A的最新代码, 不能使用变基, 因为仓库B没有仓库A的最新代码
另一种方案是直接fork仓库A, 用cheery-pick的方式将仓库B的分支捡进来.
虽然这种方案不方便发送PR给仓库B, 但直接发送PR给仓库A在结果上是一样的,
尤其是对那些很久都没有合并的PR而言, 原来的提交者可能早就不关心这件事了.
虽然这种方案不方便发送PR给仓库B, 但直接发送PR给仓库A在结果上是一样的,
尤其是对那些很久都没有合并的PR而言, 原来的提交者可能早就不关心这件事了.
- 1.
git checkout -b someone-patch-1 master
为它人的PR创建本地branch并切换至此branch - 2.
git pull https://github.com/someone/repo.git patch-name
将他人仓库的patch-name分支下载(fetch)并合并(merge)进此分支
经过试验, 这里使用git fetch替换git pull的效果是一样的 - 3.
git checkout master
切换回主分支, 准备合并 - 4.
git merge --no-ff someone-patch-1
合并 - 5.
git push origin master
合并完成后上传
git tag | xargs git push --delete origin
清除所有远程taggit tag | xargs git tag -d
清除所有本地taggit grep <regexp> $(git rev-list --all)
搜索该仓库从创建至今的所有历史文本