本文档主要记录 git 日常高频使用命令。
本地仓库由 git 维护的三棵“树”组成。
第一个是工作目录,它持有实际文件;
第二个是暂存区(Index),它像个缓存区域,临时保存改动;
最后是 HEAD,它指向你最后一次提交的结果。
如下图所示,
新建
-
git init
,创建一个新的本地仓库。 -
git clone -b <远端分支名>
,从远端 url 拉取仓库拉取制定分支到本地 local-name 文件夹下,如果不加远端分支名,则为默认分支 master。
提交
-
git add
将 working dir 相应文件的更改提交到 index 中。... -
git add .
提交所有文件的更改。 -
git commit -m 'cmmit xx'
将 index 中的内容提交到本地仓库,HEAD 指向最新提交。 -
git commit -a -m 'commit xx'
将 working dir 所有更改一次性提交到本地仓库。 -
git commmit
作用同 3,但是会弹出窗口,支持更长的 commit 信息。 -
git push <上游> <上游分支>
代码提交到远端库。
commit 信息变更
-
git commit --amend
进入编辑器,修改上一次的 commit 信息。 -
git rebase -i
合并 commit。
举例说明,使用 git log
查看,有以下 4 个 commit,
commit C4
Date: Sun Jun 21 17:25:56 2020 +0800
add line11
commit C3
Date: Sun Jun 21 17:24:33 2020 +0800
add line10
commit C2
Date: Sun Jun 21 11:49:06 2020 +0800
add line1
commbine 1
commit C1
Date: Sun Jun 21 11:45:06 2020 +0800
add newfile
现在,我想要将 C2 和 C3 合并,那么找到最早的 commit C2 的上一个 C1(或者使用 C2^
),执行 git rebase -i C1
,会出现合并 commit 选择的页面,大概如下,
pick C2 add line1
pick C3 add line10
pick C4 add line11
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
根据命令说明,将要合并的 commit 前面的命令改成 f
或者 s
, 两者的区别的是 f
在编辑合并后 commit的信息时只会出现最后一条,而 s
都会保存,建议使用 s
。要合并 C2 和 C3 的话,那么选择时间早的 C2,所以把 C3 前的 pick
改为 s
,保存。
接着会出现编辑页,编辑合并后 commit 的提交信息,改好以后再次保存就可。
注意:要合并的 C2 和 C3 是连续的 commit。
撤销
-
git checkout --
撤销一个工作区文件(必须是已经被 track 的文件)的修改。
参数为.
时表示撤销所有修改。 -
git rm --cached
从 index 中删除一个文件,使之变为 Untracked files 状态。 -
git reset HEAD
从 index 中撤销一个更改。 -
git reset --soft
从本地仓库 HEAD 回滚到 index 区(未 commit 的状态)。 -
git reset --mixed
从本地仓库 HEAD 回滚到工作区(未 add 的状态)。 -
git reset --hard
从本地仓库 HEAD 做版本回退(工作区未更改,已提交)。 -
git revert --no-commit
撤销指定 cimmit 的代码提交。..
撤销从 commit1 到 commit2 的提交,区间前开后闭区间。--no-commit
表示可以最后一起手动 git commit 提交。
revert 其实是将要取消的提交记录按照相反的操作将他们抵消掉,然后重新生成一个提交记录,这样在提交记录的完整性上更好,不同于 reset 的直接抹掉。
有冲突时需要手动修复,add 后再次 continue。 -
git merge --abort
抛弃合并过程并且尝试重建合并前的状态。该命令仅仅在合并后导致冲突时才使用。
分支管理
-
git checkout -b
基于 base 分支创建一个新分支,并切到新分支。base 分支为空时,表示为当前分支,base 分支如果为远端分支,则要加上游分支名字,如origin/dev
。 -
git checkout
切到某分支。 -
git branch -a
查看所有分支。
代码 diff
-
git diff
比较 file 在两个版本之前的差异。
如果不加 filename,那么比较所有文件的差异,
如果只有一个 commitid,表示指定 commitid 与当前版本的差异。
如果没有 commitid,只有 filename,比较某个文件与 HEAD 中的差异。
如果后面什么参数也没有,表示与 HEAD 的所有差异。 -
git diff --staged/--cached
比较暂存区/工作区与上一次提交的差异。
删除文件/分支
-
git branch -d
删除本地分支。 -
git push origin --delete
删除远端分支。 -
git rm --cached
并进行提交,完成本地文件在远端的删除。
代码合并
-
git merge
在 feature 上合并 master(这可能出现冲突,需要自行解决),
如果参数只有一个 branch,则表示与当前分支合并。feature 分支每次需要合并上游更改时,它都将产生一个额外的合并提交。如果master 提交非常活跃,这可能会严重污染你的 feature 分支历史记录。
-
git rebase -i
交互式合并指定分支。
rebase 通过为原始分支中的每个提交创建全新的 commits 来 重写 项目历史记录。
rebase 的主要好处是可以获得更清晰的项目历史。
在提交拉取请求之前,通常使用交互式 rebase 清理代码通常是个好的办法,但是在别人使用的分支(公共分支)上进行 rebase,重写其历史记录将使 Git 和你的队友无法跟踪添加到该功能的任何后续提交,也会在别人拉取代码时产生冲突。
在你运行 git rebase
命令之前,总是问自己,还有其他人在用这个分支吗? 如果答案是肯定的,那就把你的手从键盘上移开,开始考虑采用非破坏性的方式进行改变(例如,git merge
命令)。否则,你可以随心所欲地重写历史记录。
-
git cherry-pick
从 commitid1(不包括)到 commitid2 的更改,应用到本分支,如果要包含 commitid1,则使用.. commitid1^
好的实践
永远不要在公共分支上使用 git rebase
。
在私人分支,rebase 修改个人本次功能的 commit,通过定期执行交互式 rebase,你可以确保功能中的每个提交都具有针对性和意义。然后 merge 到主分支上。
更多参考 《git rebase VS git merge? 更优雅的 git 合并方式值得拥有》
代码拉取
git pull <上游> <上游分支>
从上游拉取指定分支。
如果上游分支与本地分支存在交叉,就会出现 Merge branch 'xx' of xx into xxx 的自动 commit,所以最好在使用 git pull
命令时,后面加上 --rebase
选项。
如果拉取不产生冲突,会直接 rebase,不会产生分支合并操作,如果有冲突则需要手动 fix 后,自行合并。
注意:以上所有使用 commitid 的地方,也都可以使用 HEAD/ HEAD^/HEAD~3 这种方式。