git入门以及在idea中使用git_idea 使用git
介绍
项目迭代的过程中,对于每一个版本的项目,我们需要使用版本控制器来管理他们,git就是一个版本管理器。
作用:
- 版本控制
- 多人协作
- 代码备份
git命令:
git config -l : 查看配置
git config --global --list : 只看本地配置
git config --system --list : 只看系统配置
git config --global user.name 配置一个用户名
git config --global user.email 配置一个邮箱
用户名和邮箱可以任意配置只作为身份的标识
所有的配置都在用户目录下的一个.gitconfig文件中:
结构
remote:远程仓库
本地版本库:基本是整个.git文件
暂存区:.git中的index文件
workspace:工作目录
分支原理
每做出一次commit操作,都会创建一个新的提交节点(不同版本),而分支是指向不同版本提交节点的指针。
HEAD是指向当前分支的指针,表明当前所在的本地分支是哪个分支,HEAD的移动会影响工作区的代码。
两种合并方式
无论是那种合并方式,目的都是保证代码的同步或者一致性,都是将目的分支的代码合并到当前分支。
git merge
快速模式(fast-forward):如果当前分支指针所在的提交到目标分支指针所在的提交是一条直线,直接将当前分支的指针移动到目标分支
非快速模式(non-fast-forward):将当前分支指针所在提交与目标分支所在提交进行合并,会产生一个merge commit:
例1:
例2:
此时,master-feat分支的提交记录包括:v1,v2,v3,v4,v5,v6(merge会保留目标分支的历史记录)。master分支的提交包括:v1,c4,v5
- git merge --ff (fast-forward) :以快速模式合并,不符合条件就以非快速模式合并,是git merge的默认参数。
- git merge --no-ff (non-fast-forward):以非快速模式合并
- git merge --ff–only (fast-forward-only):只以快速模式合并,不符合合并条件就提示失败。
git rebase
以对方为基底把对方的代码合并过来
通过改变基底的方式合并分支:
git rebase master
:以master为基底把对方的代码合并过来
这里会拿C与D依次与M比较,处理完冲突后产生新的C, , D, ,此时C与D会消失,此时分支呈线性。
rebase会修改历史记录(C,D变成了C, , D,)
常用命令
git init :在当前目录下新建一个本地仓库(成功会有隐藏的.git文件)
git clone [-b ] url :克隆远程仓库到本地(首先要登录 会建立起本地与远程的连接)
注意:
- 在远程建立的仓库 .git文件就在主目录下
- git clone 会把远程的主目录下的.git文件以及所有文件复制到当前的空文件夹
git add . //添加工作目录所有文件到本地暂存区
git commit -m ‘描述信息’ //从暂存区提交到本地仓库并添加文件描述信息,请务必写好描述
git pull //拉取远程的最新更改并让当前分支与之合并 == fetch + merge
git pull --rebase //拉取远程的最新更改并让当前分支与之合并 == fetch + rebase
git fetch // 默认情况下,git fetch取回最新更改到本地仓库,但是不会改变本地状态
git merge //将其他分支合并过来,会改变当前分支指向的commit,会更新工作目录,merge会保留另一个分支的历史记录
git rebase //将当前分支变基到目标分支,但是会导致当前历史的重写,因此在远程不使用这种方式合并代码,常用在个人分支
git cherry-pick //将其他分支的commit与当前分支的最新commit对比,并合并冲突(可能没有),在当前分支产生一个新的commit。(只合并pick的这次提交(几个文件而已))
git remote add //建立仓库与远程仓库的连接(一个本地可以连接多个远程仓库)
git push //将本地仓库当前分支的更新同步到远程分支,更新的单位是分支
- Merge: 适用于希望保留分支历史的场景,通常用于最终合并功能分支到主分支。
- Rebase: 适用于希望保持线性历史记录的情况,适合于经常需要同步最新变更的开发人员。
- Cherry-Pick: 适用于需要快速应用特定修复或功能到现有分支的情况。
git checkout //切换/签出到该分支。本质是移动HEAD指针,将head移动到这个分支名的指针。
git checkout -b //是以当前分支为起点创建新分支(即拥有当前分支的代码),同时切换/签出到新分支,本质是创建一个新分支,然后将HEAD移动到这个分支。
git checkout //让Head指针指向该版本,相当于切回该版本,此时头指针与分支指针分离,此时对于当前代码的任意修改与提交都不会影响当前分支,等价于在一个匿名的分支上操作,可通过git checkout -b
来保留该分支,如果不想保留,我们直接切回本分支即可(git checkout )
git log //查看提交的日志,日志中commit后跟的就是版本号
git reset --hard //跳回该版本注意日志的版本也会回退!,别最后回不去了
标签是用来保存代码的如果我们的代码开发到一定程度可以使用标签来记录代码版本(v1.0 v1.1)并把代码保存起来
git tag //创建标签
git push //将标签推送到远程
在idea中使用git
先在根目录下添加.gitignore文件后再提交推送。
# ignore these folderstarget/.idea/.settings/.vscode/bin/out/# ignore these files.classpath.project.settings.idea# filter databfile、sln file*.mdb*.ldb*.sln# class file*.com*.class*.dll*.exe*.o*.so# compression file*.7z*.dmg*.gz*.iso*.jar*.rar*.tar*.zip*.via*.tmp*.err*.log*.iml# OS generated files.DS_Store.DS_Store?._*.Spotlight-V100.TrashesIcon?ehthumbs.dbThumbs.db.factorypath.mvn/mvnw.cmdmvnw# Files or folders need to be retained# ...
拉取
提交:
推送:
Idea中Git界面下的操作(idea左下角):
历史提交记录:
远程与本地各个分支的都能看到,注意:这里的远程记录是我们通过git pull或者git fetch抓取回来的,不是实时的,需要手动更新。
右键一个历史提交,主要会使用这几个功能
复制修订号:
就是复制这个commmit节点的commit-hash
优选:
就是git cherry-pick
签出修订
就是checkout 签出到某一个版本
重置提交信息
分支重置:
即git reset,不推荐使用,因为会把回退到的提交之后的所有提交丢弃。
将一个分支重置到当前版本:
- 软:重置当前分支到以前的提交,会保留当前的文件更改以及暂存区状态。
- 混合:重置当前分支到以前的提交,不会更改当前工作目录,会重置暂存区以匹配该提交,但
- 硬:不仅重置分支到指定提交,还会重置暂存区和工作目录,使其与指定提交完全一致。
- 保留:重置当前分支到以前的提交,重置当前工作目录,不重置暂存区
常用:软、混合
还原提交:
即git revert,推荐使用,就是通过产生新commit的方式还原这次提交,举个例子
现在,我有一个master-feat3节点:
我现在要对它的文件作修改:
源代码是:
作一次修改:
提交:
然后对这个提交使用git revert还原提交:
结果会产生一个新的commit,默认为Revert “XXX”,新的commit的内容就是还原了666这次commit的内容:
撤销提交:
就是把本地最新的这一次提交给撤销了,本地改动不会受到影响,还是拿master-feat3这个分支演示:
现在的提交记录:
现在的源代码:
进行修改:
然后提交:
再对最新提交做撤销提交:
点击OK后:
最新的提交被撤销了,但是我们对于代码的修改还在:
其他使用,以一个具体的例子来展示:
clone
用idea打开
可以看到,此时本地只有一个master分支,远程remote有很多分支,注意此时的remote需要你经常取fetch最新的变更,不然的话,你的remote状态只会停在这一刻。(其实远程代码就在本地)
想要拉取最新的remote,点击这里:fetch all remotes
想要基于某一个分支开发,点击对应的分支,然后右键
可以看到这里有很多选择,接下来解释一下:
checkout:作用是签出到这个分支。在这里就是签出到all-search分支,但是由于local里没有这个分支,所以在这里基于origin/all-search,在local开一个同名的all-search分支。以后提交代码相当于直接向远程的all-search交代码。checkout也可以对于本地的分支使用。最后注意区分origin/xxx与xxx的区别,一个是remote的,一个是local的。
new branch from \'orgin/all-search\':作用是基于这个远程分支开一个新的分支,相当于从orgin/all-search开了一个新的枝,(在分支的基础上再建立分支),这里点击后,需要自定义新分支的名字。
结果如下:
checkout and rebase onto master:就是先执行checkout签出到所选分支,将选中的分支以master分支为基底,进行变基操作。说白了就是将我们选择的这个分支以当前的分支为基底合到当前的分支。
还是举例子:
现在基于master新建一个master-feat1,new branch from ‘master’:
然后在master-feat1上随便提交几次代码:
然后执行 checkout and rebase onto master-feat1:签出到master分支,然后以master-feat1为基底,把feat1的代码合到master:
此时以master-feat1为基底,将代码合到了master。
rebase current onto selected:意思是当前分支以selected为基底合并代码
现在master-feat1有三次提交:
master有两次:
在master分支,选feat1,执行 rebase master onto master-feat1,以feat1为基底把feat1的代码合过来:
此时master也有三次提交了:
merge selected into current:把选中的分支以merge的方式到当前分支
现在feat1有五次提交:
master也有五次:
在master分支,选中feat1分支,执行merge feat1 into master,把支线分支合到主线
结果:
常见开发问题
问题0:当你已经开发了一个项目,现在想把项目推到远程,但是你一开始忘记与远程连接,怎么办?
- 将远程克隆下来,把远程的.git文件移到根目录
- 提交并推送
问题1:你被分配到了2个独立的任务,在完成任务一的过程中由于某些因素暂停开发了(比如开发环境的某个服务器有问题,后天才能修好),你不得不去做任务二。
- 先将当前分支feat-1代码提交到本地仓库,git add . 和 git commit
- 在主开发分支(不一定是主分支,可能是dev或者release分支)上重新开一条新分支feat-2开发任务二
问题2:你正在开发任务二的时候,同事解决了一个本地开发的性能问题,且他的代码已经被 merge 进了主分支,如果你能用上同事优化过的那段代码,你的本地开发会大幅提高效率。此时你该怎么办?
- 先将当前分支代码提交到本地仓库,git add . 和 git commit
- 再使用git pull --rebase origin develop (把远程dev的更新拉到本地,并以rebase 的形式合并到本地的feat分支),reabse保持git commit 历史记录的整洁,避免引入一次额外的merge的commit
问题3:你在开发任务二的时候,已经写了非常多的代码,但是突然被通知中止任务二(假如这是个 新特性,产品经理决定短期时间不上了,但是将来可能还会继续开发 ),怎么合理地保存任务二的代码?
- 先 git add . 和 git commit 当前代码
- 正常提交到远程新分支上去,不合并到主分支就是了。等将来有需要,再切换到这个分支,继续开发
问题4:你的任务一开发结束了,发起 pull request / merge request 的时候在Github/Gitlab 看到 conflict (文件冲突)的提示,这是谁的责任?怎么解决?
- 原则上是,谁遇到冲突,谁解决冲突
问题5:你被分配了任务三,但是你忘记使用新分支,直接在任务一的分支上写了很多代码,部分代码甚至已经执行过 git add 了,怎么恰当地把这些代码移到一个新分支上?
- 先commit当前分支的代码,git log 当前分支的commit记录,找到对应commit的hash值
- 切换到你要移动的新分支上, git cherry-pick hash值,把对应commit的代码复制到当前分支,就像挑选樱桃一样,无需像merge一样把整个分支都合并过来
问题6:在开发任务中,你写了很多代码,有的 git add 过了,有的甚至 git commit 过了,后来发现有很大一部分代码是重复造轮子,于是决定删掉这些代码。怎么使用 git 优雅地撤销指定的代码 从而 保证在 git log message 中不留下痕迹?
- 先 git add . 和 git commit 当前代码
- 使用 git log 查看本地的 commit 历史,找到合适的回退点 —— 假设对应的 commit hash 是 hash-abc。
- 假设当前分支名称是 feat-x,使用 git branch -m feat-x-backup 重命名当前分支(新名字叫 feat-x-backup 即备份的意思)—— 这样我们就能完整地备份之前写过的代码,以备不时之需当后悔药。
- 使用 git checkout hash-abc 切到一个 commit hash 上,然后 git checkout -b feat-x 切出一个跟原本分支同名的分支。 (注意与reset的区别,只是回退到这个commit,不是直接重置到这个commit)
- 如果以前 feat-x 上过远端,使用 git push -f origin feat-x 强制覆盖远端代码(虽然通常认为这是危险操作,但是由于我们本地有 feat-x-backup 备份,就不用担心了)
声明
理解自己在干什么,基本流程是**在一条主线上开发,每人在主线上建立分支,编写代码,我们时不时需要将主线上最新的代码更新到本地,合并到当前分支,开发完了再提交代码。
对于分支重置的操作,能不用就不用!!!,后悔药是不存在的!!!,代码没了就真没机会了!!!
签出之前记得git stash保存代码,作用是将当前暂存区与工作区代码的状态保存起来,使IDEA中的工作目录恢复到上一次提交的的工作状态,除非你再次应用stash。