config
git config --global user.name "damu"
git config --global user.email damu@example.com # 配置的是你个人的用户名称和电子邮件地址。这两条配置很重要,每 次 Git 提交时都会引用这两条信息,说明是谁提交了更新,所以会随更新内容一 起被永久纳入历史记录。其中global表示对当前的电脑用户有效,--system表示对这台电脑的所有用户有效,什么都不写表示这里的配置仅仅针对当前项目有效。所以这些变量可以存放在三个不同的地方。
git config --list #检查已有的配置信息
git config --global --unset user.email #删除配置信息
log
git log --oneline --all --graph -n 10 # --all获取全部分支
remote
git remote -v # 查看当前配置的远程分支地址
git remote add <远程仓库别名> <remote-url> # 添加远程仓库
git remote remove <远程仓库别名> # 移除远程仓库
clone
git clone <repository> [newRepName] # 克隆仓库,newRepName是重命名的文件夹名称
# 选项
--single-branch # clone only one branch, HEAD or --branch
--branch <branchName> # 指定下载分支
--depth <depth> # 指定下载的commit数量 如 --depth 1
branch
git branch #查看全部本地分支
git branch -v #查看全部本地的最后一次提交
git branch -vv #查看全部本地的最后一次提交,并显示出本地分支跟踪的远程分支
git branch -r/--remote #查看远程分支(要拉取下来才能看到)
git branch -a/--all #查看全部本地分支及远程分支(要拉取下来才能看到)
git branch --merged #查看哪些分支已经合并到当前分支 在这个列表中分支名字前没有 * 号的分支通常可以使用 git branch -d 删除掉;
git branch --no-merged #查看所有包含未合并工作的分支 尝试使用 git branch -d 命令删除在这个列表中的分支时会失败。 如果真的想要删除分支并丢掉那些工作,可以使用 -D 选项强制删除它。
git branch <branch> #基于当前提交对象创建分支,不会自动切换到新分支中
git branch <branch> commithash #基于指定提交对象创建分支(版本穿梭,时光机)
git branch -d <branch> #删除空的分支,删除已经被合并的分支,(自己不能删除自己)
git branch -D <branch> #强制删除分支,(自己不能删除自己)
git branch -u <remote-name>/<remote-branch> [<branch>] #设置或修改 已有的本地分支跟踪一个已拉取下来的远程分支,省略<branch>则为当前分支建立跟踪关系
git branch --set-upstream-to <remote-name>/<remote-branch> <branch> #同上
git branch --set-upstream-to=<remote-name>/<remote-branch> <branch> #同上
git branch --unset-upstream [<branch>] #取消指定分支跟踪关系,省略<branch>则取消当前分支的跟踪关系
checkout
git checkout <branch> #切换分支
git checkout -b <branch #新建分支并切换到该分支上
git checkout -b <branch> <remote-name>/<remote-branch> # 新建一个本地分支并设置其跟踪关系,前提是已拉取了远程分支,可以通过git branch -a 查看本地和远程分支名称
git checkout --track <remote-name>/<remote-branch> # 新建一个本地分支并设置其跟踪关系,本地分支名和远程分支名一致
git checkout -- 文件名:# 撤销工作区的修改,危险的指令。你对那个文件做的任何修改都会消失,你只是拷贝了另一个文件来覆盖它。
commit
commit --amend
追加提交,可以在不增加一个新的commit的情况下,将新修噶的代码追加到前一次的commit中。 如果自上次提交以来你还未做任何修改,那么快照会保持不变,而你所修改的只是提交信息(也就是注释说明)。 另外也可以修改暂存区后,再执行这条命令。
在使用git commit -m --amend命令的时候,表面上没有生成新的提交,只是进行了内容的追加。但实际通过git reflog命令查看历史提交记录,我们可以看到,其实是新生成了一个comimit。
git commit的原理:
git reset --soft HEAD^: 回退一个版本,且工作区和暂存区中的内容不会退。git commit -c ORIG_HEAD:提交操作。在根据此时的暂存区生成一个新的提交对象,替代原来提交的位置。-c <commit>的全程是--reuse-message=<commit>:获取现有的提交对象,并在创建提交时重用现有的提价对象的日期信息。-C <coomit>类似,但-c会调用编辑器使用户可以进一步编辑。
git commit --amend
git commit --amend --no-edit # 使用选定的提交信息而无需启动编辑器(追加提交,且不修改message信息)
push
git push [<remote-name>] [<remote-branch-name>] # 将当前分支推送至指定的远程分支。若省略远程分支名,则表示将当前分支推送至跟踪的远程分支,如无跟踪关系,则报错。
git push <remote-name> HEAD # 推送当前分支到同名的远程分支,如果远程分支不存在则会创建
git push [-u,--set-upstream] <remote-name> <branch-name>:<remote-branch-name> # 将指定分支推送至指定的远程分支并设置跟踪关系,若远程分支不存在,则会创建远程分支。如果远程分支被省略,则表示将本地分支推送到与之存在追踪关系的远程分支,如果该远程分支不存在,则会创建同名的远程分支,并建立跟踪关系,
git push <remote-name> --delete <remote-branch> #删除远程分支
pull
git pull 其实就是 git fetch 和 git merge FETCH_HEAD 的简写。
git pull <远程主机名> <远程分支名>:<本地分支名>
git pull origin master:brantest # 将远程主机 origin 的 master 分支拉取过来,与本地的 brantest 分支合并。
git pull origin master # 将远程主机 origin 的 master 分支拉取过来,与本地的当前分支合并。
merge
Git合并那些事——认识几种Merge方法 合并的情况包括
- fast forward merge 要合并的两个分支没有分叉,滞后的分支只需要简单的将指针往前移即可,不涉及内容变更的比较。如果我们想在合并后保留来自被合并分支的提交历史,并显式标注出合并发生的位置,那就需要在执行合并时加上参数--no-ff
- Three-Way Merge 要合并的两个分支已经分叉,会对这两个分支以及离他们最近的公共祖先提交记录进行比较,然后进行三方合并。合并的过程会生成一个新的提交记录。比较的原理参考怎么理解Git里的 "three-way merge" ?
- squash merge 所谓Squash Merge,是指Git在做两个分支间的合并时,会把被合并分支(通常被称为topic分支)上的所有变更“压缩(squash)”成一个提交,追加到当前分支的后面,作为“合并提交”(merge commit)。从参与合并的文件变更上来说,Squash Merge和普通Merge并没有任何区别,效果完全一样。唯一的区别体现在提交历史上:正如我们前面提到的,对于普通的Merge而言,在当前分支上的合并提交通常会有两个parent;而Squash Merge却只有一个。 使用情况:如果在被合并分支上,完整的提交历史里包含了很多中间提交(intermediate commit),比如:改正一个小小的拼写错误可能也会成为一个独立的提交,而我们并不希望在合并时把这些细节都反应在当前分支的提交历史里。这时,我们就可以选择Squash Merge。
Git合并那些事——Merge策略(上) Git合并那些事——Merge策略(下) merge的策略
- Recursive策略 Git在对两个分支进行合并时所采用的默认策略,它只适用于两个分支之间的合并。因此,对于超过两个分支的合并,需要反复地进行两两合并,才能最终完成所有分支的合并(这也是Recursive名字的由来)。本质上,Recursive就是一种Three-Way Merge。它的特点在于,如果Git在寻找共同祖先时,在参与合并的两个分支上找到了不只一个满足条件的共同祖先,它会先对共同祖先进行合并,建立临时快照。然后,把临时产生的“虚拟祖先”作为合并依据,再对分支进行合并。 当发生冲突时自动选择或丢弃其中一个分支上的修改。比如,假设我们要把分支B合并到分支A(git merge B -Xours)。如果指定参数-Xours,则表明丢弃分支B上的修改,保留当前分支A上的内容;指定参数-Xtheirs则刚好相反。
- Resolve策略
- Ours策略 前面提到了,如果在使用Recursive策略时指定-Xours参数,那么当发生冲突时,Git会选择丢弃被合并分支的修改,而保留当前分支上的原有修改。这种情况只在有冲突时才会发生,如果没有冲突,Git还是会帮我们自动完成合并的。 与之不同的是,Ours策略无论有没有冲突发生,都会毫不犹豫的丢弃来自被合并分支的修改,完整保留当前分支上的修改。所以,对于Ours策略而言,实质上根本就没有做任何真正意义上的合并,或者说做的是假合并(fake merge)。不过,从提交历史上看,Git依然会创建一个新的合并提交(merge commit),并让它的parent分别指向参与合并的两个分支上的提交记录。
- Octopus策略 Git在对两个以上的分支进行合并时,会自动选择Octopus策略。它的主要特点在于,只会生成一个合并提交,从而最大限度地减少了因为合并对提交历史造成的“污染”。因为按照这种合并策略得到的提交历史形似章鱼,所以名字还是起的很形象的。
- Subtree策略
git merge --abort # 终止合并操作
git merge branchA -m 'xxx' # 将branchA分支合并到当前分支,-m指定合并的描述信息
git merge branchName --no-ff # --no-ff 表示关闭fast forward
git merge --squash dev # dev会被压缩成一个提交,追加到当前分支的后面
*branchA: git merge branchB -Xours # 将branchB合并到当前分支branchA上,发生冲突时,保留当前分支,舍弃branchB
*branchA: git merge -s ours branchB # -s指定合并策略,完整保留当前分支的修改,毫无保留丢弃branchB的修改
stash
git stash #将工作区和暂存区的修改存储到一个新的堆栈中,堆栈中的内容(stash)可以被所有分支访问,git stash不会存储untracked files和.gitignore 中定义的文件
git stash save -u "备注信息" # 存储时添加备注信息
git stash list # 查看存储
git stash show # 查看最近一条存储信息
git stash show -p # 查看最近一条存储信息详情
git stash show stash@{id} # 查看指定的存储信息
git stash apply stash@{2} # 如果不指定一个储藏,Git 认为指定的是最近的储藏
git stash pop # 应用储藏然后立即从栈上扔掉它
git stash drop stash@{id} # 删除指定的 stash
git stash drop # 删除最近一次的 stash
git stash clear # 删除所有的 stash
remote
git remote add <remote-name> <url> # 添加一个新的远程 Git 仓库,同时指定一个你可以轻松引用的简写
git remote –v # 显示远程仓库使用的 Git 别名与其对应的 URL
git remote show <remote-name> # 查看某一个远程仓库的更多信息
git remote rename <old-remote-name> <new-remote-name> # 重命名
git remote rm <remote-name> # 移除一个远程仓库
reset
reset用于回退版本,可以遗弃不再使用的提交。执行遗弃时,需要根据影响的范围而指定不同的参数,可以指定是否复原索引或工作树内容 --mixed(默认):默认的时候,只有暂存区变化 --hard参数:如果使用 --hard 参数,那么工作区也会变化 --soft:如果使用 --soft 参数,那么暂存区和工作区都不会变化
git reset --soft commitHash # 重置HEAD到指定的提交hash处
git reset --soft HEAD~ # 撤销了上一次 git commit 命令。 当运行 git commit 时,Git 会创建一个新的提交,并移动 HEAD 所指向的分支来使其指向该提交。 当你将它 reset 回 HEAD~(HEAD 的父结点)时,其实就是把该分支移动回原来的位置,而不会改变索引(暂存)和工作目录。
git reset [--mixed] commitHash # 重置HEAD,暂存区到指定的提交hash处
git reset [--mixed] HEAD~ # 动HEAD(带着分支一起移动)和暂存区。会撤销一上次 提交,还会 取消暂存 所有的东西。 于是,我们回滚到了git add 和 git commit 的命令执行之前。类似于撤销中的git reset HEAD 文件名。
git reset --hard commitHash # 移动HEAD(带着分支一起移动)到指定的提交hash处,更新暂存区和工作目录
git reset --hard HEAD~ # 你撤销了最后一次的提交、git add 和 git commit 命令以及工作目录中的所有工作。HEAD~等效于HEAD^,表示上一次提交。上上次提交可以记作HEAD^^或者HEAD~2。
revert
在当前提交后面,新增一次提交,抵消掉上一次提交导致的所有变化,不会改变过去的历史,主要是用于安全地取消过去发布的提交。
git revert <commitHash> # 撤销某次操作,此次操作之前和之后的 commit和history都会保留,并且把这次撤销,作为一次最新的提交
git revert HEAD # 撤销前一个版本
git revert HEAD^ # 撤销前前一次
restore
git restore <file> # 撤消工作区的修改返回到最近一次add(缓存区)的版本或者最近一次commit(当前版本库)的版本
git restore --staged <file> # 撤销在暂存区提交的文件,撤销上一次git add 操作
rebase
场景一:合并提交记录
rebase的作用简要概括为:可以对某一段线性提交历史进行编辑、删除、复制、粘贴;因此,合理使用rebase命令可以使我们的提交历史干净、简洁!
适用的场景:1. 个人开发分支,有多次commit提交,但没有push到远程仓库,可以使用该指令使分支更简洁。2. 个人开发分支,即使被推到了远程分支,只要还没有合并到主分支,仍然可以rebase后通过git push -f推到远程仓库。
git rebase -i [startpoint] [endpoint] # 其中-i的意思是--interactive,即弹出交互式的界面让用户编辑完成合并操作,[startpoint] [endpoint]则指定了一个编辑区间,如果不指定[endpoint],则该区间的终点默认是当前分支HEAD所指向的commit(注:该区间指定的是一个前开后闭的区间)。
git rebase --edit-todo # 如果异常退出了 vi 窗口,可执行该指令
git rebase --abort # 如果进入了rebasing状态,可以通过该指令终止rebase
执行一次rebase操作。
# 1. 执行一次rebase操作
git rebase -i HEAD~3
# 2. 执行上述命令后进入交互界面,将commit内容编辑如下,意思就是把第二次、第三次提交都合并到第一次提交上
pick d2cf1f9 fix: 第一次提交
s 47971f6 fix: 第二次提交
s fb28c8d fix: 第三次提交
# 3. wq保存退出后进入注释修改界面
# 4. 编辑完保存即可完成commit合并
如想撤销rebase操作,可通过 git reflog 后,查看提交记录,可以进行reset操作。
场景二:分支合并
-
我们先从 master 分支切出一个 dev 分支,进行开发: git:(master) git checkout -b feature1
-
这时候,你的同事完成了一次 hotfix,并合并入了 master 分支,此时 master 已经领先于你的 feature1 分支了:
-
恰巧,我们想要同步 master 分支的改动,首先想到了 merge,执行: git:(feature1) git merge master git:(feature1) git log 我们就会在记录里发现一些 merge 的信息,但是我们觉得这样污染了 commit 记录,想要保持一份干净的 commit,怎么办呢?这时候,git rebase 就派上用场了。
-
让我们来试试 git rebase ,先回退到同事 hotfix 后合并 master 的步骤:
-
使用 rebase 后来看看结果:
(*feature1) git rebase master 这里补充一点:rebase 做了什么操作呢? 首先,git 会把 feature1 分支里面的每个 commit 取消掉; 其次,把上面的操作临时保存成 patch 文件,存在 .git/rebase 目录下; 然后,把 feature1 分支更新到最新的 master 分支; 最后,把上面保存的 patch 文件应用到 feature1 分支上; 如果有冲突,解决冲突 git add . # 标记冲突已解决 git rebase --continue # 提交改变 git push [-f] # 推到远程分支,如果已经push过了需要-f强制推 -
从 commit 记录我们可以看出来,feature1 分支是基于 hotfix 合并后的 master ,自然而然的成为了最领先的分支,而且没有 merge 的 commit 记录,是不是感觉很舒服了。
-
在 rebase 的过程中,也许会出现冲突 conflict。在这种情况,git 会停止 rebase 并会让你去解决冲突。在解决完冲突后,用 git add 命令去更新这些内容。 注意,你无需执行 gitcommit,只要执行 git rebase --continue,这样 git 会继续应用余下的 patch 补丁文件。
-
在任何时候,我们都可以用git rebase —abort来终止 rebase 的行动,并且分支会回到 rebase 开始前的状态。
rm
git rm -f 文件名 # 删除暂存区和工作区的文件
git rm --cache 文件名 # 删除暂存区的文件
cherry-pick
git cherry-pick <commitHash> # 将指定的提交(commit)应用于当前分支
git cherry-pick <HashA> <HashB> # 一次性应用多个提交记录
git cherry-pick A..B # 一次性应用从 A 到 B 的所有提交(不包括A)
git cherry-pick A^..B # 一次性应用从 A 到 B 的所有提交(包括A)
场景
撤销修改
git checkout -- file # 丢弃工作区的修改,让这个文件回到最近一次git commit或git add时的状态
git reset HEAD readme.txt # 暂存区的修改撤销掉(unstage),重新放回工作区。git reset命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本。
修改commit的信息
# 修改最近提交的 commit 信息
$ git commit --amend --message="modify message" --author="xxx" # 仅修改 message 信息
# 修改指定提交的 message 信息
$ git rebase -i <commit id>
# 修改最近的多条提交的message信息
$ git rebase -i HEAD~3
撤销合并
当我们revert一次回滚操作的时候,需要显示给出-m(mainline)选择告诉git回滚具体哪一个mainline。因为一个merge提交记录来源于两个commit,所以需要指定回到哪一个。
git show <commitHash> # 查看commit的具体信息,可以看到该次merge提交的来源,有两个来源,注意看hash
git revert <commitHash> -m 1 # revertmerge操作,回到来源1,也可以选择回到来源2
参考
彻底搞懂 Git-Rebase git cherry-pick 教程 git revert回滚merge提交时报错(commit xxx is a merge but no -m option ) git中push -f是啥意思