Git Basic
初始化git
在一个空目录下,初始化git
在目录中放一些文件,然后添加到本地git中
检查git状态
要提交到远程仓库,那么必须先commit一下
如果git commit message写错了,可以用 --amend 修改:
提交到远程仓库,如果没有配置远程地址,则会报错,解决办法请参考下面的步骤
如果不小心git add了,要删除add的文件,可以执行
使用 git stash 将未完成的工作进行暂存
使用 git cherry-pick 来选择将某个commit merge到当前分支。比如在 dev 分支下,有两个 test1 和 test2分支,然后在test1分支下做了一些修改,已经提交上去,但没有merge到dev分支。这会想要在test2分支下,也应用这个变更。那么可以先用 git log看一下test1提交的commit id,然后cherry-pick选择commit到test2分支
Git rebase 压缩commit
使用 git rebase -i 命令,可以将多个 commit,压缩成1个commit。比如此时git log里,看到有历史3个commit,想要压缩成一个,那么可以用 git rebase -i HEAD~3 或者 git rebase-i HEAD~~~ 的方式,进行选择。之后会进入交互式界面,在交互式界面,编辑这个commit文件,将其他两个前面的pick改成s,表示squash,然后074c959 和 d38c6aa 就会被压缩到 5f7f01e 里了。如果想要从历史中,删除这两个commit,那么可以将s改成drop。之后保存退出就可以了
使用 fixup 和 autosquash 自动压缩commit
虽然使用 git rebase -i 可以将多个commit压缩成一个,或者在压缩的时候,选择drop commit,但是需要手动操作,且commit message里默认是有其他commit的message,此时我们可以用 git fixup 结合 autosquash帮我们实现.
具体操作方法是:在本地先提交一个commit,然后记下commitid,此时后续的其他变动,git add 之后,都可以使用 git commit --fixup COMMITID 的方式进行提交,然后git log里,我们能看到commit message都是 !fixup开头。
接下来使用 git rebase -i --autosquash 来帮我们自动合并commit,不需要做任何额外操作,我们能看到,默认其他的commit就是fixup的,而不是squash,然后保存,git log就看到只有一个commit了。
.git 文件夹太大压缩
如果经常对仓库里的文件操作,尤其是对一些大文件,图片之类的操作,那么 .git 文件夹有可能会非常大,比如10多G,甚至更大,可以使用 git gc 命令对其进行压缩
Git rebase 进行merge
如果在 master分支下,直接执行 git merge test,则会将 test分支的commit 方到master分支,同时会产生一个新的merge的commit 但是如果在 test分支下,先执行 git fetch origin master,然后 git rebase origin/master (或者执行 git pull origin master --rebase),则表示将本地的test分支的commit 历史,更新成跟master完全一样的commit历史
解决冲突
在git pull origin master --rebase更新本地代码,遇到冲突的时候,git rebase --skip可以让本地代码和远程保持一致,但本地的更改将在分支上丢失.
如果想保留本地,则执行 git checkout --ours . 之后执行git add. 最后执行 git rebase --continue 就可以了
配置github的key
在github上创建一个repo,然后在账户设置,SSH and GPG keys里面,添加 ssh pub key 产生ssh public key方法,添加~/.ssh/id_rsa.pub文件内容
之后可以测试一下
添加remote origin:
然后进行push
默认情况下,git credential是对所有仓库都生效的,如果有多个git账号,想访问不同的git repo的时候,使用不同的git credential,那么可以启用 useHttpPath,但需要注意的是,一旦启用了,任何新仓库,都会请求credential.
将远程git同步到本地
git 管理多个github repo
在本地创建另外一个folder,在github上创建好repo
Git 多分支管理
Git 中文显示
默认情况下,git 是无法显示中文的,而是显示八进制的字符。可以通过下面的修改,使git中文可正常显示
Git 高级使用方法
使用 git add -p 对同一个文件分段进行上传管理。比如一个文件中,有多处修改,我们在这次执行commit的时候,指向提交部分的修改,不想提交这个文件中的所有修改,就可以使用 git add -p 的方式来将文件添加进去。需要注意的是:如果本次想加入到git中的修改和不想加入的修改是分开在2个不同地方的,那么输入 git add -p FILENAME 后,然后输入 s 就能进行split,之后可以选择需要添加的区块。但如果想添加的地方和不想添加的地方是在一个地方,那么就没有 s 子命令,需要输入 e 手动进行编辑 示例:
git 日志与回滚
git reset 的时候,如果是 --soft,那么撤销的其他commit的变更,都会放到 暂存区,通过git status能看到。如果git reset --hard,则不会保留到暂存区。同时 git reset --soft HEAD~2 这种方式,可以直接回退2个commit
重新提交
正如在 "git 日志与回滚" 片段里提到的,我们可以用 git reset --soft 的方式,对已经提交的commit 进行撤销,撤销之后,在git status里能看到在staged中,处于git add 的状态,此时可以用 git restore --staged . 的方式对git add 进行撤销,然后重新add要提交进去的文件,再进行git commit。
这种方式在对于将 a b c 三个文件都提交到仓库里,但此时后悔了,只想提交a文件,b和c不想提交,此时就可以用git reset --soft COMMIT_ID 或 git reset origin/master 或 git reset HEAD~1 的方式,回退到指定commit,并且保留现有更改,然后再重新git add进行提交
撤消回退
在 "git 日志与回滚"中,我们提到可以用 git reset --hard HEAD1 直接丢弃某一个commit,这个和 git rebase -i HEAD1 之后,选中commit 进行drop有什么区别呢?
答案是:git reset --hard 的操作是直接丢弃commit,虽然在 git reflog 里能看到,但git reflog是保留在本地的,会自动清理的,不是很安全,如果使用这种方法,git reflog看到commit hash之后,要使用 git checkout或者 git cherry-pick 的方式添加这个commit,也不是很方便。如果使用 git rebase -i 的方式对commit进行操作,那么操作之前的HEAD信息,会保留到 ORIG_HEAD 这个分支下,这是一个特殊的分支,git branch 是看不到的,但如果git rebase -i之后后悔了,可以用 git reset --hard ORIG_HEAD的方式,轻松的撤销 git rebase 的结果,回退到 rebase之前的状态
查看某一个文件历史
通过 git blame 可以看文件中所有行的历史,但只能看这一行最后一次是谁修改的。如果想要看一个文件的历史改动,可以用git log。此时能看到文件改动的所有历史
然后使用 git blame可以看某一个commit的时候这个文件内容
删除git历史中的一个文件
现在不推荐使用 filter-branch,建议使用 filter-repo。不过 filter-repo需要单独安装
如果想要从git 历史中,使用本地的某个文件,对其进行替换
其中 -- --all 指的是,对所有分支都进行修改,如果直接写 HEAD的话,则只对当前分支进行修改
github pr merge 的三种方式
merged pull request:
这种方式是直接将源分支的一个或多个commit,移动到目的分支,同时再创建一个merge commit
squash merge:
这种方式是将源分支的一个或多个commit,压缩成一个新的commit放到目的分支。注意此事因为目的分支并没有源分支的commit,所以如果源分支下次提交的时候,没有rebase 当前目的分支,那么会将之前变更在pr里继续带上。尤其是如果更改的文件和目的分支不一样,又没有rebase,就会提示冲突
rebase merge:
这种方式是将源分支的一个或多个commit,创建一个或多个commit,放到目的分支
pre-commit
如果我们本地仓库根目录里有 .pre-commit-config.yaml文件,那么在仓库根路径下执行 pre-commit install 命令,则会自动根据这个yaml文件,生成hooks脚本,放在 .git/hooks/ 文件夹下。但此时需要我们针对每一个仓库目录,在本地都执行这个 pre-commit install 命令。当然我们可以在 ~/.gitconfig 文件里,指定 templatedir 目录,然后将 hooks文件放到这个目录里,这样以后只要 git clone 一个仓库,就会自动将 templatedir 路径下的hooks文件拷贝过来了.
如果想要在 git 提交或合并PR前对代码进行一些检查,我们可以借助于 pre-commit 这个工具来做。首先需要安装一下 pre-commit 这个工具,然后在git项目的根目录,需要存在一个 .pre-commit-config.yaml 文件,将 pre-commit 的一些hook可以在这里定义,然后通过 github action pipeline,对pr 进行检查。
如果想要在本地提交到本地仓库之前,就通过pre-commit进行一些检查,而不依赖仓库的 .pre-commit-config.yaml 文件。此时我们可以通过修改 ~/.gitconfig 里init的templatedir,这样以后 git clone或者git init的时候,会将这个 templatedir 目录下的文件,都拷贝到 项目.git/ 路径下,示例配置
此时我们在 ~/.git-template/文件夹下,创建一个 hooks 文件夹,在里面创建 pre-commit 文件,执行 chmod +x pre-commit 将其设置为可执行文件。之后运行git clone的时候,就会将 hooks文件夹都拷贝到 项目的.git/路径下,而在本地执行 git commit 的时候,就会执行 pre-commit 这个脚本。
当然我们也可以设置 pre-push, post-update 等脚本,我们可以执行 pre-commit install 命令 ,就会在 ~/.git/hooks/ 文件夹下,产生一些 .sample 结尾的示例文件,可以参考这个文件。
如果想要在本地也执行 pre-commit,可以将这个脚本放到 /home/USERNAME/.git-template/hooks/pre-commit 文件里
Git Tag
git tag 一般比较常见的有:轻标签(lightweight tag)和注释标签(annotated tag)。如果使用 git cat-file -t TAG_NAME 观察,轻标签是一个commit,注释标签是一个tag。用git show TAG_NAME对比,轻标签只能看到commitid,注释标签能看到很多信息。
创建轻标签
创建注释标签
如果想列出一个commit上的tag,可以用 git describe 命令,如果不加 --tags,有可能列出来的是轻标签,而非最新的标签。
git repo搭建
完整 clone git 仓库
如果想要完整clone git 仓库,然后迁移到另外一个仓库里,那么我们再 clone的时候,要加上 --bare 参数
示例:将远程仓库完全clone到本地,并还原成一个可以操作的仓库
示例:将远程仓库clone到本地后,再push到远端另外一个仓库
Git Config
默认情况下,git 仓库将会用 ~/.gitconfig 配置文件里的配置,如果有多个不同的仓库,想要让部分仓库使用另外的邮箱和用户名,那么可以将这些仓库放在一个特定路径下,然后通过 includeIf 来包含这个路径,然后在这个路径下的repo,都会用另外一个配置文件。想要检查当前repo的git配置,可以直接在仓库路径下输入 git config user.email 或者 git config user.name (需要注意的是,git配置里尽量避免使用 ~,可能导致配置不生效)
如果有多个git 配置文件,那么可以用下面的命令来查看
Git 也有一个全局配置
Git Debug
在执行git 任何命令的时候,如果想要显示更详细的日志,可以在前面加上 GIT_TRCE=1 来显示
最后更新于