git常用命令和操作
git常用命令如下,详细了解,点击跳转检索
文章目录
获取与创建项目
git init
你只需要简单地运行 git init 就可以将一个目录转变成一个 Git 仓库,这样你就可以开始对它进行版本管理了。
git clone
git clone 实际上是一个封装了其他几个命令的命令。 它创建了一个新目录,切换到新的目录,然后 git init 来初始化一个空的 Git 仓库, 然后为你指定的 URL 添加一个(默认名称为 origin 的)远程仓库(git remote add),再针对远程仓库执行 git fetch,最后通过 git checkout 将远程仓库的最新提交检出到本地的工作目录。
快照基础
git add
git add 命令将内容从工作目录添加到暂存区(或称为索引(index)区),以备下次提交。 当 git commit 命令执行时,默认情况下它只会检查暂存区域,因此 git add 是用来确定下一次提交时快照的样子的。
git status
git status 命令会显示工作区及暂存区域中不同状态的文件。 其中包含了已修改但未暂存,或已经暂存但没有提交的文件。 在一般的显示形式中,它会给你一些如何在这些暂存区之间移动文件的提示。
git diff
当需要查看任意两棵树的差异时,可以使用 git diff 命令。 此命令可以查看你工作环境与你的暂存区的差异(git diff 默认的做法),你暂存区域与你最后提交之间的差异(git diff --staged),或者比较两个提交记录的差异(git diff master branchB)。
git commit
git commit 命令将所有通过 git add 暂存的文件内容在数据库中创建一个持久的快照,然后将当前分支上的分支指针移到其之上。
git reset
git reset 命令主要用来根据你传递给动作的参数来执行撤销操作。 它可以移动 HEAD 指针并且可选的改变index 或者暂存区,如果你使用 --hard 参数的话你甚至可以改变工作区。 如果错误地为这个命令附加后面的参数,你可能会丢失你的工作,所以在使用前你要确定你已经完全理解了它。如果要回退到version1
version1─>version2─>version3
└─master
└─HEAD
那么HEAD会直接指向version1,之后的版本都不见了
version1
└─master
└─HEAD
git reset用于撤销未被提交到remote的改动,即撤销local的修改。
除了移动当前分支的HEAD,还可以更改workspace和index:
--soft
:修改HEAD,不修改index和workspace。--mixed
:修改HEAD和index,不修改workspace。默认行为。--hard
:修改HEAD、index、workspace。
此时使用git push时,会提示要pull远程代码,因此,需要使用git push -f强制推送到远程
git revert回退实现的原理和reset完全不同,后面会介绍。
分支与合并
git branch
git branch 命令实际上是某种程度上的分支管理工具。 它可以列出你所有的分支、创建新分支、删除分支及重命名分支。
分支创建
Git 是怎么创建新分支的呢? 很简单,它只是为你创建了一个可以移动的新的指针。 比如,创建一个 testing 分支, 你需要使用 git branch 命令:
$ git branch testing
这会在当前所在的提交对象上创建一个指针。
Git 又是怎么知道当前在哪一个分支上呢? 也很简单,它有一个名为 HEAD 的特殊指针。 请注意它和许多其它版本控制系统(如 Subversion 或 CVS)里的 HEAD 概念完全不同。 在 Git 中,它是一个指针,指向当前所在的本地分支(译注:将 HEAD 想象为当前分支的别名)。
git branch 命令仅仅 创建 一个新分支,并不会自动切换到新分支中去。
git checkout
git checkout 命令用来切换分支,或者检出内容到工作目录。
要切换到一个已存在的分支,你需要使用 git checkout 命令。 我们现在切换到新创建的 testing 分支去:
$ git checkout testing
这样 HEAD 就指向 testing 分支了。
运行一个带有 -b 参数的 git checkout 命令:
$ git checkout -b iss53
Switched to a new branch "iss53"
它是下面两条命令的简写:
$ git branch iss53
$ git checkout iss53
git checkout与git reset的区别
不带路径
运行 git checkout [branch] 与运行 git reset --hard [branch] 非常相似,它会更新所有三棵树使其看起来像 [branch],不过有两点重要的区别。
首先不同于 reset --hard,checkout 对工作目录是安全的,它会通过检查来确保不会将已更改的文件弄丢。 其实它还更聪明一些。它会在工作目录中先试着简单合并一下,这样所有 还未修改过的 文件都会被更新。 而 reset --hard 则会不做检查就全面地替换所有东西。
第二个重要的区别是 checkout 如何更新 HEAD。 reset 会移动 HEAD 分支的指向,而 checkout 只会移动 HEAD 自身来指向另一个分支。
例如,假设我们有 master 和 develop 分支,它们分别指向不同的提交;我们现在在 develop 上(所以 HEAD 指向它)。 如果我们运行 git reset master,那么 develop 自身现在会和 master 指向同一个提交。 而如果我们运行 git checkout master 的话,develop 不会移动,HEAD 自身会移动。 现在 HEAD 将会指向 master。
所以,虽然在这两种情况下我们都移动 HEAD 使其指向了提交 A,但 做法 是非常不同的。 reset 会移动 HEAD 分支的指向,而 checkout 则移动 HEAD 自身。
带路径
运行 checkout 的另一种方式就是指定一个文件路径,这会像 reset 一样不会移动 HEAD。 它就像 git reset [branch] file 那样用该次提交中的那个文件来更新索引,但是它也会覆盖工作目录中对应的文件。 它就像是 git reset --hard [branch] file(如果 reset 允许你这样运行的话), 这样对工作目录并不安全,它也不会移动 HEAD。
此外,同 git reset 和 git add 一样,checkout 也接受一个 --patch 选项,允许你根据选择一块一块地恢复文件内容。
git merge
git merge 工具用来合并一个或者多个分支到你已经检出的分支中。 然后它将当前分支指针移动到合并结果上。
我们首先在 新建分支 一节中介绍了 git merge 命令。 虽然它在本书的各种地方都有用到,但是 merge 命令只有几个变种,一般只是 git merge 带上一个你想合并进来的一个分支名称。
git stash
git stash 命令用来临时地保存一些还没有提交的工作,以便在分支上不需要提交未完成工作就可以清理工作目录。
项目分享与更新
git fetch
git fetch 命令与一个远程的仓库交互,并且将远程仓库中有但是在当前仓库的没有的所有信息拉取下来然后存储在你本地数据库中。
git pull
git pull 命令基本上就是 git fetch 和 git merge 命令的组合体,Git 从你指定的远程仓库中抓取内容,然后马上尝试将其合并进你所在的分支中。
git push
git push 命令用来与另一个仓库通信,计算你本地数据库与远程仓库的差异,然后将差异推送到另一个仓库中。 它需要有另一个仓库的写权限,因此这通常是需要验证的。
git remote
git remote 命令是一个是你远程仓库记录的管理工具。 它允许你将一个长的 URL 保存成一个简写的句柄,例如 origin ,这样你就可以不用每次都输入他们了。 你可以有多个这样的句柄,git remote 可以用来添加,修改,及删除它们。
补丁
Git 中的一些命令是以引入的变更即提交这样的概念为中心的,这样一系列的提交,就是一系列的补丁。 这些命令以这样的方式来管理你的分支。
git cherry-pick
当项目复杂的时候,有些功能可能还没完成,有些功能必须现在就得提交更新。 这种情况下,需要自己将代码写到自己的分支,然后使用Git的挑拣功能,挑拣到目标分支
合并单个commit
例:想要在b1分支合并进x功能,则首先在有x功能的分支执行git log找到该功能的提交记录以及对应的commit hash id,然后切换到b1分支,执行`cherry-pick;
git cherry-pick <hash>
合并多个commit
提取合并hash1到hash2之间提交的所有内容,包括hash2不包括hash1,即 (前开后闭的区间]
git cherry-pick <hash1>..<hash2>
提取合并hash1到hash2之间提交的所有内容,包括hash2和hash1,即 [闭区间]
git cherry-pick <hash1>^..<hash2>
示例:撤销过去某次未PUSH的merge
merge实际上要revert里面的commit,这个merge本身没法revert
修复引用
C1─>C2─>C5─>C6─>M(master,HEAD) └> C3─> C4┘(topic)
如果这个不想要的合并提交只存在于你的本地仓库中,最简单且最好的解决方案是移动分支到你想要它指向的地 方。 git reset --hard HEAD~ 这会重置分支指向所以它们看起来像这样 C1─>C2─>C5─>C6 [M delete](master,HEAD) └> C3─> C4(topic)
还原提交
C1─>C2─>C5─>C6─>M─>^M(master,HEAD) └> C3─> C4┘(topic)
新的提交 ^M 与 C6 有完全一样的内容,所以从这儿开始就像合并从未发生过,除了“现在还没合并”的提交依然在 HEAD 的历史中。 如果你尝试再次合并 topic 到 master Git 会感到困惑:
$ git merge topic Already up-to-date.
topic 中并没有东西不能从 master 中追踪到达。 更糟的是,如果你在 topic 中增加工作然后再次合并,Git只会引入被还原的合并之后 的修改。
以下是含有坏掉合并的历史 C1─>C2─>C5─>C6─>M─>^M─>C8(master,HEAD) └> C3 ─> C4 ─> C7┘(topic)
解决这个最好的方式是撤消还原原始的合并,因为现在你想要引入被还原出去的修改,然后创建一个新的合并提交:
C1─>C2─>C5─>C6─>M─>^M─>^^M─>C8(master,HEAD) └> C3─> C4┴───C7──────┘(topic)
在本例中,M 与 ^M 抵消了。 ^^M 事实上合并入了 C3 与 C4 的修改,C8 合并了 C7 的修改,所以现在 topic 已经完全被合并了。
进阶:Git中撤销中间的某次merge代码
先reset --hard到merge之前,然后把后续的cherry pick过来
https://www.jianshu.com/p/5f0528dea878
git rebase
这里要记住什么是待变基分支和基分支
-
feature:待变基分支、当前分支
-
master:基分支、目标分支
A─>B─>M(master)
└>C─>D(feature)
git checkout feature
git rebase master // 这两条命令等价于git rebase master feature
官方解释:当执行rebase操作时,git会从两个分支的共同祖先开始提取待变基分支上的修改,然后将待变基分支指向基分支的最新提交,最后将刚才提取的修改应用到基分支的最新提交的后面。
结合例子解释:当在feature分支上执行git rebase master时,git会从master和featuer的共同祖先B开始提取feature分支上的修改,也就是C和D两个提交,先提取到。然后将feature分支指向master分支的最新提交上,也就是M。最后把提取的C和D接到M后面,但这个过程是删除原来的C和D,生成新的C’和D’,他们的提交内容一样,但commit id不同,feature自然最后也是指向D’。
A-->B-->M-->C'-->D'(feature)
遇到冲突时,和merge不同,需要每个commit解决冲突
git pull --rebase
git pull = git fetch + git merge FETCH_HEAD
git pull --rebase = git fetch + git rebase FETCH_HEAD
git revert
git revert 命令本质上就是一个逆向的 git cherry-pick 操作。 它将你提交中的变更的以完全相反的方式的应用到一个新创建的提交中,本质上就是撤销或者倒转。
revert通过新建一个commit来撤销一次commit所做的修改,是一种安全的方式,并没有修改commit history。
Fast-Forwords
当前分支合并到另一分支时,如果没有分歧解决,就会直接移动文件指针。这个过程叫做fastforward。
举例来说,开发一直在master分支进行,但忽然有一个新的想法,于是新建了一个develop的分支,并在其上进行一系列提交,完成时,回到 master分支,此时,master分支在创建develop分支之后并未产生任何新的commit。此时的合并就叫fast forward。