【Git】命令行操作
Git 命令行操作
-
默认当前远程版本库:
origin
,Git里面的origin就是一个名字,它是在你clone一个托管在Github上代码库时,git为你默认创建的指向这个远程代码库的标签。 -
默认开发分支:
master
-
默认当前分支:
HEAD
-
当前分支的父提交(上一版本):
HEAD^
-
版本别名:提交版本后的括号内为该分支的最新版本,可以作为版本别名进行reset
个人理解:我们使用git操作,其实就是将工作区指针到处移动而已。
- 工作区:指针
- 本地库:本地数组
- 远程库:远程数组
SEQUENCER SUBCOMMANDS 序列化子命令
--continue
:继续
Continue the operation in progress using the information in .git/sequencer. Can be used to continue after resolving conflicts in a failed cherry-pick or revert.
使用.git / sequencer中的信息继续进行操作。可用于在解决失败的挑选或恢复中的冲突后继续。
--skip
:略过
Skip the current commit and continue with the rest of the sequence.
跳过当前提交,继续完成序列的其余部分。
--quit
:退出
Forget about the current operation in progress. Can be used to clear the sequencer state after a failed cherry-pick or revert.
忘记当前正在进行的操作。可以用来清除序列状态在一个失败的挑选或恢复后。
--abort
:中止
Cancel the operation and return to the pre-sequence state.
取消操作并返回到预序列状态。
1 本地库初始化
git init
:初始化本地仓库
效果
注意:.git目录中存放的是本地库相关的子目录和文件,不要删除,也不要胡乱修改。
2 设置配置签名
形式:
用户名:tom
Email地址:goodMorning@atguigu.com
作用:区分不同开发人员的身份
辨析:这里设置的签名和登录远程库(代码托管中心)的账号、密码没有任何关系。
命令
- 项目级别/仓库级别:仅在当前本地库范围内有效
git config user.nametom_pro
git config user.emailgoodMorning_pro@atguigu.com
- 信息保存位置:./.git/config文件
- 系统用户级别:登录当前操作系统的用户范围
git config --global user.nametom_glb
git config --global goodMorning_pro@atguigu.com
- 信息保存位置:~/.gitconfig文件(~代表根目录)
- 级别优先级
- 就近原则:项目级别优先于系统用户级别,二者都有时采用项目级别的签名
- 如果只有系统用户级别的签名,就以系统用户级别的签名为准
- 二者都没有不允许
使用git config --list
进行查看配置列表
3 基本操作
3. 1 状态查看
git status
:查看工作区、暂存区状态
3. 2 添加
git add [filename]
:将工作区的“新建/修改”添加到暂存区
- filename:为欲添加的文件名,
*
代表当前目录的所有文件。
3. 3 提交
git commit -m "commit message" [filename]
:将暂存区的内容提交到本地库
个人理解:
- add:将我们的代码交给git监管
- commit:将我们的代码提交到本地库
- push:将我们的代码推送到远程库
3. 4 查看历史记录日志
git log
:查看历史记录,显示完整的历史记录信息
多屏显示控制方式:
- 空格:向下翻页
- b:向上翻页
- q:退出
git --grep xxx
:搜索日志
上面这种方法显示的历史记录可能不太好看:我们可以采用以下方法进行简化历史记录的显示:
git log --pretty=oneline
:我们可以使用此命令让历史记录放在一行上
git log --oneline
:缩短了哈希值的显示,只显示后面7位
上面的几种方法只能显示过去与现在
下面这个命令可以显示过去、现在和未来
git reflog
:这种方式可以看到我们所有的历史记录,包括回退前、回退后、过去、现在、将来。
HEAD@{移动到当前版本需要多少步}
git reflog --all
:可以查看更完整的所有记录
3. 5 前进后退
本质:使用git reflog
来显示所有历史记录
可以使用以下方法来移动HEAD指针
基于索引值操作[推荐]
git reset --hard [局部索引值]
git reset --hard a6ace91
使用^
符号:只能后退
-
git reset --hard HEAD^
:一个^
表示后退一步,n个表示后退n步
使用~
符号:只能后退
-
git reset --hard HEAD~n
:表示后退n步
回退后提交只能使用git push -f
强制提交,因为在他前面已经有其他新节点了,得强行覆盖掉。
3. 6 reset 命令的三个参数对比
原本本地库、暂存区、工作区三者版本一致:
--soft
参数
- 仅仅在本地库移动HEAD指针(本地库回退)
- 暂存区还是原样
- 工作区还是原样
这里可以看出,我们的本地库版本退后了一步,而其他的都没改变
--mixed
参数(默认)
- 在本地库移动HEAD指针(本地库回退)
- 重置暂存区(重置,即 后退版本)(暂存区回退)
- 工作区还是原样
--hard
参数(危险,会完全回到)
- 在本地库移动HEAD指针(本地库回退)
- 重置暂存区(暂存区回退)
- 重置工作区(工作区回退)
让单个文件回滚到指定版本
1.进入到文件所在文件目录,或者能找到文件的路径
查看文件的修改记录
git log fileName
2.回退到指定版本
git reset 版本号 fileName
3.提交到本地参考:git commit -m “提交的描述信息”
4.更新到工作目录
git checkout fileName
5.提交到远程仓库
git push origin master
这样指定的文件回退到指定版本了
3. 7 删除文件并找回
前提:删除前,文件存在时的状态提交到了本地库。
操作:git reset --hard [指针位置]
- 删除操作已经提交到本地库:指针位置指向历史记录
- 删除操作尚未提交到本地库:指针位置使用HEAD
还原(撤销)某个版本
git revert <commit id>
:git 会生成一个新的 commit,将指定的 commit 内容从当前分支上撤除。
在 Git 开发中通常会控制主干分支的质量,但有时还是会把错误的代码合入到远程主干。 虽然可以直接回滚远程分支, 但有时新的代码也已经合入,直接回滚后最近的提交都要重新操作。 那么有没有只移除某些 Commit 的方式呢?可以一次 revert操作来完成。
考虑这个例子,我们提交了 6 个版本,其中 3-4 包含了错误的代码需要被回滚掉。 同时希望不影响到后续的 5-6。
* 982d4f6 (HEAD -> master) version 6
* 54cc9dc version 5
* 551c408 version 4, harttle screwed it up again
* 7e345c9 version 3, harttle screwed it up
* f7742cd version 2
* 6c4db3f version 1
这种情况在团队协作的开发中会很常见:可能是流程或认为原因不小心合入了错误的代码, 也可能是合入一段时间后才发现存在问题。 总之已经存在后续提交,使得直接回滚不太现实。
下面的部分就开始介绍具体操作了,同时我们假设远程分支是受保护的(不允许 Force Push,大多情况都是这样,导致使用不了reset)。 思路是从产生一个新的 Commit 撤销之前的错误提交。
使用 git revert <commit>
可以撤销指定的提交, 要撤销一串提交可以用 git revert <commit1>..<commit2>
语法。 注意这是一个前开后闭区间,即不包括 commit1,但包括 commit2。(这个跟Java中的截取字符串一样,都是为了方便)
git revert -n f7742cd..551c408
# 这样也可以 git revert --no-commit f7742cd..551c408
git commit -a -m \'This reverts commit 7e345c9 and 551c408\'
其中 f7742cd 是 version 2,551c408 是 version 4,这样被移除的是 version 3 和 version 4。 注意 revert 命令会对每个撤销的 commit 进行一次提交,--no-commit
后可以最后一起手动提交。
git revert -n COMMIT
或git revert --no-commit COMMIT
:这就相当于在本节点恢复撤销以前的COMMIT,然后不自行提交。也就是说只改变了本地的工作区代码,并不改变节点提交版本号。
-n
是代表不自动提交,只是将回滚的代码添加到,暂存区(效果类似 git add),因为 revert 默认是回滚一个版本,如果自动提交,最后的代码会变成 verson1 – version2 – version3 – version4 – revert-to-version3 – revert-to-version2 会生成2个commit 记录,所以,一般都要使用-n
(也写作--no-commit
) 这个参数。注意:reset是回到以前版本的节点上,而revert -n还是在本节点,只是恢复撤销了。
-n, –no-commit
Usually the command automatically creates a sequence of commits. This flag applies the changes necessary to cherry-pick each named commit to your working tree and the index, without making any commit. In addition, when this option is used, your index does not have to match the HEAD commit. The cherry-pick is done against the beginning state of your index.
此时 Git 记录是这样的:
* 8fef80a (HEAD -> master) This reverts commit 7e345c9 and 551c408
* 982d4f6 version 6
* 54cc9dc version 5
* 551c408 version 4, harttle screwed it up again
* 7e345c9 version 3, harttle screwed it up
* f7742cd version 2
* 6c4db3f version 1
现在的 HEAD(8fef80a)就是我们想要的版本,把它 Push 到远程即可。
确认 diff
如果你像不确定是否符合预期,毕竟批量干掉了别人一堆 Commit,可以做一下 diff 来确认。 首先产生 version 4(551c408)与 version 6(982d4f6)的 diff,这些是我们想要保留的:
git diff 551c408..982d4f6
然后再产生 version 2(f7742cd)与当前状态(HEAD)的 diff:
git diff f7742cd..HEAD
如果 version 3, version 4 都被 version 6 撤销的话,上述两个 diff 为空。 可以人工确认一下,或者 grep 掉 description 之后做一次 diff。 下面介绍的另一种方法可以容易地确认 diff。
bd1b80cb 和0f2f7e4a 是错误的提交可以通过git revert bd1b80cb 0f2f7e4a 来进行单独提交的回滚
3. 8 比较文件差异
git diff [文件名]
:将工作区中的文件和暂存区进行比较
git diff [本地库中历史版本] [文件名]
:将工作区中的文件和本地库历史记录比较
git diff
:不带文件名比较多个文件
git diff HEAD^ HEAD
:比较上一版本和现在版本的区别,即 上一版本如何到达现在版本,HEAD^ → HEAD
git diff --name-only
:只输出有变动的文件名
4 分支管理
4. 1 什么是分支?
在版本控制过程中,使用多条线同时推进多个任务。
4. 2 分支的好处?
- 同时并行推进多个功能开发,提高开发效率
- 各个分支在开发过程中,如果某一个分支开发失败,不会对其他分支有任何影响。失败的分支删除重新开始即可。
4. 3 分支操作
git branch
:查看本地分支。
电脑B本地clone仓库默认只会clone下master分支,而其他电脑A推送的分支是不会默认同步下来的。
clone下来的远程仓库并不会将所有分支都clone下来。
git branch -a
:查看所有分支,包括本地分支与远程分支。
git branch [分支名] [commitId]
:创建分支。当我们运行了git branch xx 之后,它就会默认基于你当前所在的这个分支最后一次提交的文件,进行一个复制,作为这个新创建的 xx 分支的初始代码。
git branch -v
:(version)查看本地分支的版本情况
git branch -d [分支名]
:(delete)删除分支
注意:在删除分支的时候,一定要先退出要删除的分支,然后才能删除。
git checkout [分支名]
:切换分支
合并分支
- 第一步:切换到接受修改的分支(被合并,增加新内容)上:
git checkout [被合并分支名]
- 第二步:执行merge命令:
git merge [有新内容分支名]
- 第三步:删除?:
git branch -d [分支名]
查看分支:
注意:当前分支前面有个标记“*”。
创建分支:
切换分支:
合并分支:
现在先在dev分支下的readme文件中新增一行并提交本地
切换到master分支下观察readme文件
将dev分支的内容与master分支合并:
删除分支:
注意:在删除分支的时候,一定要先退出要删除的分支,然后才能删除。
合并所有分支之后,需要将master分支提交线上远程仓库中:
合并提交
对于多分支的代码库,将代码从一个分支转移到另一个分支是常见需求。
这时分两种情况。一种情况是,你需要另一个分支的所有代码变动,那么就采用合并(git merge)。另一种情况是,你只需要部分代码变动(某几个提交),这时可以采用 Cherry pick。
git将某分支的某次提交合并到另一分支
代码开发的时候,有时需要把某分支(比如develop分支)的某一次提交合并到另一分支(比如master分支),这就需要用到git cherry-pick命令。
-
首先,切换到develop分支,敲
git log
命令,查找需要合并的commit记录,比如commitID:7fcb3defff -
然后,切换到master分支,使用
git cherry-pick -n 7fcb3defff
命令,就把该条commit记录合并到了master分支,这只是在本地合并到了master分支-n
是代表不自动提交,只是将回滚的代码添加到,暂存区(效果类似 git add),因为 revert 默认是回滚一个版本,如果自动提交,最后的代码会变成 verson1 – version2 – version3 – version4 – revert-to-version3 – revert-to-version2 会生成2个commit 记录,所以,一般都要使用 -n(也写作 –no-commit) 这个参数。-n, –no-commit
Usually the command automatically creates a sequence of commits. This flag applies the changes necessary to cherry-pick each named commit to your working tree and the index, without making any commit. In addition, when this option is used, your index does not have to match the HEAD commit. The cherry-pick is done against the beginning state of your index. -
最后,
git push
提交到master远程(可以用命令也可以用工具的push操作),至此,就把develop分支的这条commit所涉及的更改合并到了master分支。
解决冲突
-
冲突的表现
- 冲突的解决
- 第一步:编辑文件,删除特殊符号
- 第二步:把文件修改到满意的程度,保存退出
- 第三步:
git add [文件名]
-
第四步:
git commit -m "日志信息"
注意:此时commit一定不能带具体文件名
临时存储
git stash
temporarily shelves (or stashes) changes you\’ve made to your working copy so you can work on something else, and then come back and re-apply them later on. Stashing is handy if you need to quickly switch context and work on something else, but you\’re mid-way through a code change and aren\’t quite ready to commit.
git stash
:能够将所有未提交的修改(工作区和暂存区)保存至堆栈中,用于后续恢复当前工作目录。
特别注意:保存的是修改,而不是一整个项目快照,所以你在A分支的修改,也可以同步到B分支,而不修改B分支的其他模块代码。
前提:必须是处于git下的文件,未add到git的文件无法使用。
-
git stash
:保存当前工作进度,将工作区和暂存区恢复到修改之前。 -
git stash save "message"
:作用同上,message为此次进度保存的说明。 -
git stash list
:显示保存的工作进度列表,编号越小代表保存进度的时间越近。 -
git stash pop [stash@{num}]
:恢复工作进度到工作区,此命令的stash@{num}是可选项,在多个工作进度中可以选择恢复,不带此项则默认恢复最近的一次进度相当于git stash pop stash@{0} -
git stash apply [stash@{num}]
:恢复工作进度到工作区且该工作进度可重复恢复,此命令的stash@{num}是可选项,在多个工作进度中可以选择恢复,不带此项则默认恢复最近的一次进度相当于git stash apply stash@{0} -
git stash drop [stash@{num}]
:删除一条保存的工作进度,此命令的stash@{num}是可选项,在多个工作进度中可以选择删除,不带此项则默认删除最近的一次进度相当于git stash drop stash@{0} -
git stash clear
:删除所有保存的工作进度。
检出
我们一般使用检出(checkout)都是检出其他的分支,也就是分支切换,其实检出还可以检出暂存区、本地库(如果暂存区没有)或远程库(如果本地库没有)文件到我们的工作区,来达到一个拉取、恢复工作区代码的作用。
重置修改
当我们修改了工作区的一些代码后,发现有些文件的修改我们并不需要,需要还原,我们就可以使用checkout来重置、放弃修改。
git checkout -- filename
:这里的--
是为了表明后面的字段名为文件名路径名,而不是分支名,可有可无。
其实原理也就是上面我们说的,检出本地库,恢复到没修改过的状态。
拉取单个文件
在master分支下拉取某个文件
git fetch
git checkout master -- path/to/file
回滚单个文件
git reset HEAD^ filename
git checkout -- filename
注意:这里我们的reset并不能
--hard
只能使用默认的--mixed
那么工作区的内容就不会改变,所以我们需要使用checkout来将我们的本地库数据拉取到工作区。
那么git reset
为什么不能通过路径进行硬/软重置呢?
Because there\’s no point (other commands provide that functionality already), and it reduces the potential for doing the wrong thing by accident.
因为没有意义(其他命令已经提供了这种功能) ,并且它减少了偶然做错事情的可能性。
A “hard reset” for a path is just done with git checkout HEAD — (checking out the existing version of the file).
对路径的“硬重置”只需使用 git checkout HEAD -- < path >
(检查文件的现有版本)即可完成。
A soft reset for a path doesn\’t make sense.
对路径进行软重置是没有意义的。
A mixed reset for a path is what git reset — does.
一个路径的混合重置就是 git reset -- < path >
所做的。
也就是说,我们可以使用git checkout HEAD -- < path >
来硬重置路径。
如果省略版本号,那么就默认为HEAD,这就是为什么我们能够重置我们的修改,放弃我们的修改。
二进制文件冲突解决
如果二进制文件发生冲突,不方便查看git插入的冲突标记, 解决比较棘手,通常最简单的解决方法是提前沟通好,相同修改的地方二选一.git checkout [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <pathspec>…
git checkout --ours[--theirs] [--] PATH
:这里的--
是为了表明后面的字段名为文件名路径名,而不是分支名,可有可无。
--ours
表示检出当前分支,即保存当前分支的改动,丢弃另外分支的改动.--theirs
表示检出另外分支, 即保存另外分支的改动,丢弃当前分支的改动.
举个栗子:
有分支A和B, 当前我们在分支A上, 需要把分支B合并到分支A, HashMap.c文件发生冲突了.git checkout --ours -- HashMap.c
表示冲突的地方采用A分支上的修改,丢弃B分支上的修改.git checkout --theirs -- HashMap.c
表示冲突的地方采用B分支上的修改,丢弃A分支上的修改.
解决完冲突后,就可以像往常一样 git add git commit了。
远程库
克隆
git clone [远程地址]
拉取
- pull=fetch+merge
-
git fetch [远程库地址别名] [远程分支名]
:把远程库的代码拉取到本地库。 -
git merge [远程分支名]
或git merge [远程库地址别名/远程分支名]
:把本地库的代码与工作区合并 git pull [远程库地址别名] [远程分支名]
推送
git push [别名] [分支名]
命令行常用操作步骤
情况一:创建自己的项目
1. 创建远程库
2. 创建远程库地址别名
注意:这里的remote地址以及别名,都只对当前git仓库生效,对于其他git仓库不生效,即 作用域仅在本git仓库中。
git remote -v 查看当前所有远程地址别名
git remote add [别名] [远程地址]
3. 推送
git push [别名] [分支名]
推送需要输入账号和密码:
情况二:克隆别人的项目
1. 克隆远程仓库(从无到有)
git clone [远程地址]
效果
- 完整的把远程库下载到本地
- 创建 origin 远程地址别名
- 初始化本地库
情况三:更新已有的项目(更新位于不同分支)
1. 拉取(从旧到新)
-
git pull
=git fetch
+git merge
git fetch [远程库地址别名] [远程分支名]
-
git merge [远程分支名]
或git merge [远程库地址别名/远程分支名]
git pull [远程库地址别名] [远程分支名]
要特别注意的是,自己写完了代码,没有提交推送之前不要pull,得先提交推送然后发生冲突了再pull,否则git一看没有提交修改,说明文件没有修改,那就直接pull过来了,可能覆盖了你的本地文件。
如果遇到了上述问题:请看情况九
那为什么要先commit,然后pull,然后再push,我pull了,岂不是把自己改的代码都给覆盖掉了嘛,因为远程没有我改的代码,我pull,岂不是覆盖了我本地的改动好的地方了?那我还怎么push?
答:这个先 commit 再 pull 最后再 push 的情况就是为了应对多人合并开发的情况,
commit 是为了告诉 git 我这次提交改了哪些东西,不然你只是改了但是 git 不知道你改了,也就无从判断比较;
pull是为了本地 commit 和远程commit 的对比记录,git 是按照文件的行数操作进行对比的,如果同时操作了某文件的同一行那么就会产生冲突,git 也会把这个冲突给标记出来,这个时候就需要先把和你冲突的那个人拉过来问问保留谁的代码,然后在 git add && git commit && git pull 这三连,再次 pull 一次是为了防止再你们协商的时候另一个人给又提交了一版东西,如果真发生了那流程重复一遍,通常没有冲突的时候就直接给你合并了,不会把你的代码给覆盖掉
出现代码覆盖或者丢失的情况:比如A B两人的代码pull 时候的版本都是1,A在本地提交了2,3并且推送到远程了,B 进行修改的时候没有commit 操作,他先自己写了东西,然后 git pull 这个时候 B 本地版本已经到3了,B 在本地版本3的时候改了 A 写过的代码,再进行了git commit && git push 那么在远程版本中就是4,而且 A 的代码被覆盖了,所以说所有人都要先 commit 再 pull,不然真的会覆盖代码的
情况四:更新已有的项目(更新位于同一分支)(当前分支被其他人更新)
在CR过程中,目标分支可能被其他人更新,导致CR版本不是最新,称为落后目标分支,这时需要对CR进行变基(rebase),相当于基于目标分支最新版重新patch了一个commit
1. 变基
rebase:变基,改变我们的基础节点。
使用方法:git rebase [分支名] [提交id]
,我们就可以将此分支的提交id变基过来,即 将本地工作区指针移动到该提交。
-
git pull --rebase
=git fetch
+git rebase
git fetch [远程库地址别名] [远程分支名]
-
git rebase [远程分支名]
或git rebase [远程库地址别名/远程分支名]
:把本地库的代码变基到工作区 git pull --rebase
理解:也就是说,我们先在基点origin(C)的基础上先更新其他人的修改(D),即 改变我们的基础节点(变基),我们在变基后的基点D的基础上进行增加我们的修改E,组合成为了新的节点(R)。
2. 遇到冲突被中断,处理冲突,继续变基
步骤:
Step1. 在本地仓库中, 更新并合并代码。
也就是说我们需要在pull过来的最新版的基础上继续补充我们的变动,即 变基为最新版本。
# 这里分开写是想进行分步讲解
git fetch origin # 将远程主机的最新内容拉到本地
git rebase origin/customer/ceb # 将本地资源与远程资源合并变基,后发生冲突,会标明所有冲突
即
git pull --rebase # 注意这里是拉取资源和变基两步,拉取资源无冲突,变基有冲突,所以我们后面解决了冲突之后要继续变基
Step2. 依据提示分别打开冲突的文件, 逐一修改冲突代码
Step3. 所有冲突都修改完毕后, 提交修改的代码。
也就是说我们需要在我们解决冲突进行修改的基础上继续补充我们的变动,即 变基为修改版本。
git add -u # 加入修改后的代码
git rebase --continue # 因为上一步rebase命令产生了冲突,终止了,所以这里解决冲突后继续执行rebase,查看是否还有冲突未解决,如果还有冲突未解决就得继续修改
git add -u
<–>git add –update
提交所有被删除和修改的文件到数据暂存区git add .
提交所有修改的和新建的数据暂存区git add -A
<–>git add –all
提交所有被删除、被替换、被修改和新增的文件到数据暂存区
Step4. 更新patch
git push origin HEAD:refs/for/customer/ceb # 在完全解决冲突之后才能推入远程
提示:
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
命令:
vim text.txt # 修改冲突的文件
git add text.txt
# 因为冲突已经修改完了,产生了一个新的本地节点,所以需要继续变基到新的冲突修改完的节点,相当于变基到新的节点把commit提交了
# 冲突修改前 → 提交
# 变基为 冲突修改前 → 冲突修改后 → 提交
git rebase --continue
git push origin HEAD:refs/for/master
理解:也就是说,我们先在基点origin(C)的基础上先更新其他人的修改(D),即 改变我们的基础节点(变基),我们在变基后的基点D的基础上进行增加我们的修改E,组合成为了新的节点(R)。
git merge和git rebase的区别
使用下面的关系区别这两个操作:
git pull = git fetch + git merge
git pull –rebase = git fetch + git rebase
现在来看看git merge和git rebase的区别。
假设有3次提交A,B,C。
我们在分支origin的基础上进行了修改,代码编写,即 在远程分支origin的基础上创建一个名为”mywork”的分支并提交了,同时有其他人在”origin”上做了一些修改并提交了。
(我们的本地代码编写分支为mywork(E),其他人的修改为D)
提交树节点介绍:
- C:基础节点,我们的代码编辑和别人的代码编辑修改,都在此节点开始完成,以此节点为基点;
- D:其他人对C版本的代码修改;
- E:我们当前对C版本的代码修改;
- M:其他人的修改与我们的修改进行合并(merge);
- R:先更新其他人的修改,再在其他人的基础上更新我们的修改(变基,即 改变了我们的基础节点)。
其实这个时候E不应该提交,因为提交后会发生冲突。如何解决这些冲突呢?有以下两种方法:
1、git merge
用git pull命令把”origin”分支上的修改pull下来与本地提交合并(merge)成版本M,但这样会形成图中的菱形,让人很困惑。
理解:也就是说,我们自己的修改代码编写,即 本地提交分支(E)与其他人的修改(D)进行了分支合并(merge),合并为了一个新的节点(M)
2、git rebase
创建一个新的提交R,R的文件内容和上面M的一样,但我们将E提交废除,当它不存在(图中用虚线表示)。由于这种删除,小李不应该push其他的repository.rebase的好处是避免了菱形的产生,保持提交曲线为直线,让大家易于理解。
理解:也就是说,我们先在基点origin(C)的基础上先更新其他人的修改(D),即 改变我们的基础节点(变基),我们在变基后的基点D的基础上进行增加我们的修改E,组合成为了新的节点(R)。
在rebase的过程中,有时也会有conflict,这时Git会停止rebase并让用户去解决冲突,解决完冲突后,用git add命令去更新这些内容,然后不用执行git-commit,直接执行git rebase –continue,这样git会继续apply余下的补丁。
在任何时候,都可以用git rebase –abort参数来终止rebase的行动,并且mywork分支会回到rebase开始前的状态。
情况五:发起CR(Code Review)
CR(Code Review)又称为代码评审,是指在软件开发过程中,对源代码的系统性检查。通常的目的是查找系统缺陷,保证软件总体质量和提高开发者自身水平。
某些公司可能会拥有自己的CR,也就是说,我们的代码提交时并不是直接提交到github,而是先提交到公司的CR,进行代码评审,然后评审通过了之后我们才合入github。
发起CR:每个公司发起CR的命令可能不一样,大家按照公司要求书写即可。git push origin HEAD:refs/for/master
- git push 肯定是推送
- origin : 是远程的库的名字
- HEAD: 是一个特别的指针,它是一个指向你正在工作的本地分支的指针,可以把它当做本地分支的别名,git这样就可以知道你工作在哪个分支
- refs/for :意义在于我们提交代码到服务器之后是需要经过code review 之后才能进行merge的
- refs/heads 不需要
情况六:代码已经提交了,但是发现有点问题,所以想增加patch
增加patch(打补丁):我们的代码此时还没有并入代码库,还存留在CR中,所以我们可以对此次提交增加patch。git commit --amend
:修正最后一次提交,传递此选项将修改最后一次提交。阶段性更改将被添加到先前的提交中,而不是创建一个新的提交。该命令将打开系统配置的文本编辑器,并提示更改先前指定的提交消息。
This option adds another level of functionality to the commit command. Passing this option will modify the last commit. Instead of creating a new commit, staged changes will be added to the previous commit. This command will open up the system\’s configured text editor and prompt to change the previously specified commit message.
git add cr.txt
git commit --amend
git push origin master:refs/for/master
为什么我们可以就某次提交来打补丁呢?
从上图可以看出,我们的提交有commit id
和change id
两种,而commit id会随着提交次数而变化,一次操作就会生成一个commit id,而change id会随着变化而变化,打补丁的时候,我们的change id不改变,只会改变commit id,所以会保证变化的一致性,保证我们可以打补丁。
情况七:想临时存储代码(分支错误,切换分支,代码还不想提交)
应用场景:
1 当正在dev分支上开发某个项目,这时项目中出现一个bug,需要紧急修复,但是正在开发的内容只是完成一半,还不想提交,这时可以用git stash命令将修改的内容保存至堆栈区,然后顺利切换到hotfix分支进行bug修复,修复完成后,再次切回到dev分支,从堆栈中恢复刚刚保存的内容。
2 由于疏忽,本应该在dev分支开发的内容,却在master上进行了开发,需要重新切回到dev分支上进行开发,可以用git stash将内容保存至堆栈中,切回到dev分支后,再次恢复内容即可。
总的来说,git stash命令的作用就是将目前还不想提交的但是已经修改的内容进行保存至堆栈中,后续可以在某个分支上恢复出堆栈中的内容。这也就是说,stash中的内容不仅仅可以恢复到原先开发的分支,也可以恢复到其他任意指定的分支上。git stash作用的范围包括工作区和暂存区中的内容,也就是说没有提交的内容都会保存至堆栈中。
git stash # 能够将所有未提交的修改(工作区和暂存区)保存至堆栈中,用于后续恢复当前工作目录。
git checkout xxx # 切换分支
git stash pop # 将当前stash中最近保存的内容弹出,并应用到当前分支对应的工作目录上。
情况八:冲突的产生与解决
案例:模拟产生冲突。
-
同事在下班之后修改了线上仓库的代码
注意:此时我本地仓库的内容与线上不一致的。
-
第二天上班的时候,我没有做
git pull
操作,而是直接修改了本地的对应文件的内容 -
需要在下班的时候将代码修改提交到线上仓库(
git push
)
提示我们要在再次push之前先git pull操作。
【解决冲突】
-
先
git pull
此时 git 已经将线上(push)
与本地仓库(commit)
的冲突合并到了对应的文件中。 -
打开冲突文件,解决冲突
解决方法:需要和同事(谁先提交的)进行商量,看代码如何保留,将改好的文件再次提交即可。 -
重新提交
线上效果:
新手上路小技巧:上班第一件事先 git pull,可以在一定程度上避免冲突的产生。
情况九:git更新以后本地改动代码不见了被覆盖了
git pull会把本地未提交修改覆盖,这是当然的,毕竟add只是存放在暂存区暂存着了,并没有持久保存,想要持久保存就得commit上传到本地库中。
个人理解:
- add:将我们的代码交给git监管
- commit:将我们的代码提交到本地库
- push:将我们的代码推送到远程库
一般我们git更新不会覆盖掉我们本地的代码,但是也不是绝对的,在一定条件下,会被覆盖,在这种我本地文件没有上传git,没法使用git版本控制,又被git上的版本干掉了我本地代码的情况下,我相信小伙伴都会想说已经mmp的,这里我们说下这个方法,主要是使用idea,webstrom的 localHistory 来找回我们的代码。
-
选中整个项目右击,你就能看到本地的历史记录
-
点击本地历史记录,你能看到所有本地的记录
为了避免此情况:可以写完代码先 commit 再 pull 最后再 push,养成习惯。
git可视化操作——git GUI
我们介绍完了git的命令行操作,可能会有些朋友觉得这种操作实在是太麻烦了,所以接下来我们介绍一种git的可视化操作。
详情可以查看:git GUI可视化操作