git基础知识
盗用网上的一张图,git有工作目录、索引区(也叫暂存区)、历史区,这3个区,一定要记在脑子里,基本上git所有的操作都是操作这3个区。新建一个文件并提交的一般操作是,
- 新建文件
- git add到索引区
- git commit到历史区(添加
-a
参数会自动提交到索引区,相当于第2步+第3步)
git reset 有3种模式
git reset 有3种模式,
git reset [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
git reset –mixed
这是默认的重置方式,重置索引区,保留工作区。
比如,修改了一个文件后,会提示文件被修改了,并提示add提交到索引区或者restore放弃工作目录更改。
git status
On branch feature1
Your branch is up to date with 'origin/feature1'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: src/main/java/com/example/learnspringboot/LearnspringbootApplication.java
no changes added to commit (use "git add" and/or "git commit -a")
接下来提交到索引区,接着看下状态,会发现有提示,待commit,或者restore索引区。
PS D:\learnspringboot> git add src/main/java/com/example/learnspringboot/LearnspringbootApplication.java
PS D:\learnspringboot> git status
On branch feature1
Your branch is up to date with 'origin/feature1'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: src/main/java/com/example/learnspringboot/LearnspringbootApplication.java
然后reset(默认是mixed),会重置索引区保留工作目录,所以提示中有Unstaged changes after reset
,重置后与提交到索引区之前完全一样。
PS D:\learnspringboot> git reset
Unstaged changes after reset:
M src/main/java/com/example/learnspringboot/LearnspringbootApplication.java
PS D:\learnspringboot> git status
On branch feature1
Your branch is up to date with 'origin/feature1'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: src/main/java/com/example/learnspringboot/LearnspringbootApplication.java
no changes added to commit (use "git add" and/or "git commit -a")
git reset –soft
重置head指向commit,但索引区和工作区都保存,也就是说add后但未commit的和本地工作目录都会保留。这种情况适合,本地工作目录做了更改add或者没add,但现在想回滚到某个版本,并且不想丢弃这些更改,那么就用这种方式。
git reset –hard
重置head指向commit、重置索引区、工作区,如果本地做了变更,add或者没add,最后不想要了,那么就用这种方式。
总结
这3种模式用联想记忆法,比如soft是软的意思,是最软的,重置head指向commit,索引区、工作区都保留;mixed是混合的,那就是中等喽,所以重置索引区,保留工作区;hard是最硬的,重置索引区、工作区;可以看到是包含关系,har包含mixed,mixed包含soft。为什么叫soft?因为它重置的范围最小;为什么叫mixed,因为它是中等,所以重置soft的部分+索引区,为什么是hard?因为重置了mixed部分+工作区。
经常见的问题
- 新建了文件,但还没git add,想删除,怎么办?
- 这种情况,因为文件还未纳入索引区,所以直接在磁盘或者ide删除就可以了。
- 新建了文件,git add了,想彻底删除,怎么办?
- 根据上图所说,应该是用 git reset --hard或者git reset,重置索引区,然后再物理删除文件就可以了。
- 新建了文件,commit了,不想要这个文件了,怎么办?
- 直接删除,然后commit就可以了。
以上问题最关键的在于理解3个工作区(工作目录、索引区、历史区)理解了后,运用之妙存乎一心了。
intellij idea中git操作
还原文件
- intellij idea中,还原文件,通常是右击git-----Rollback(ctrl+alt+z),通过日志(下方,git----Console)中可以看到使用的命令
git -c core.quotepath=false -c log.showSignature=false checkout HEAD -- src/main/java/com/example/learnspringboot/LearnspringbootApplication.java
,它用的checkout,查官方文档
git checkout [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>]
--pathspec-from-file=<file> [--pathspec-file-nul]
Overwrite the contents of the files that match the pathspec. When the (most often a commit) is not given, overwrite working tree with the contents in the index. When the is given, overwrite both the index and the working tree with the contents at the .
这意思就是指定了commit就会用commit版本覆盖索引区和工作目录,如果没指定,则使用索引区覆盖工作区。
- 用索引区覆盖工作目录
git checkout filepath
- 某个commit节点覆盖索引区和工作目录
-
git checkout commit filepath
,比如git checkout HEAD filepath
更新
intellij idea中更新的快捷键是ctrl+T,其实它包含了两个动作,默认的是fetch+merge,可以通过File----settings----Git----Update进行更改,本人更新喜欢rebase,因为提交历史是一条线,更清晰。
提交(快捷键ctrl+k)
这个没啥好说的。
推送(快捷键ctrl+shift+k)
这个没啥好说的。
stash暂存
比如正在开发中,来了个紧急bug,需要赶紧修复,但当前功能未开发完,commit很可能造成编译错误或者功能错误,我们就想如果有个地方可以暂存下变更就好了,git stash (vt. 藏匿; 隐藏; 存放; 贮藏;)
就是做这个的。右击git-----stash changes,当想恢复的时候,右击git---- unstash changes–apply changes
git rebase vs merge
- 什么时候该用rebase?
- rebase就是变基,只要没提交到远程仓库或者提交到了远程仓库,但别人还未基于此开发就可以使用。援引原文:
如果你只对不会离开你电脑的提交执行变基,那就不会有事。 如果你对已经推送过的提交执行变基,但别人没有基于它的提交,那么也不会有事。
如果你对已经推送至公用仓库的提交上执行变基命令,并因此丢失了一些别人的开发所基于的提交, 那你就有大麻烦了,你的同事也会因此鄙视你。
- 举例
比如,有两个分支experiment分支、master分支,experiment从master中切出来的,分别在两个分支上做了变更,现在要把experiment合并到master上,使用merge通常是这样的,
rebase相当于以master为基础,然后重放在experiment分支上做的变更。
可以看到rebase后的master是一条直线,更加清晰,但是rebase有风险,在你们没弄清楚他的作用之前不要轻易使用。
Merge remote-tracking branch ‘origin/feature1’ into feature1
也许你见过如上的合并,这通常发生在更新代码时,它是怎么发生的呢?发生场景
- 本地更改了代码commit了,但未push
- 其他人也更改这个分支commit了,并push了
- 你打算push,push之前先更新了一下,就会出现上述情况。
这是因为分支分叉了,要合并,默认的fetch+merge就会产生上述情况,提交线不清晰,可以设置使用fetch+rebase来避免这个情况,设置方法同上边intellij idea 更新小节。
使用rebase踩过的坑
比如一个master分支,在此基础上,切出了feature1分支,因为feature1分支是多个同事都要开发所以push到了远程,开发过程中,为了保持feature1比较新,要时不时将master的提交合并到feature1上,我就rebase了master,然后intellij idea就提示要update和push可以做,如果update就会莫名其妙的很多冲突。
这是因为feature1已经变基了master,此时再update,相当于本地feature1分支又要reabse 远程feature1(我的update策略设置的rebase),已经rebase了master就没必要再rebase远程feature1了,所以正确的操作方式是变基master后,直接force push覆盖远程分支,因为是force push所以这里要格外小心,好在intellij idea的force push默认是Force-with-lease
,(lease vt. 租用,租借),就好像本地租借了远程分支,但不加锁,如果远程有更新则会被拒绝,可以再次选择强制推送,
基于以上原因所以git官方才推荐了使用变基的场景,只要没提交到远程仓库或者提交到了远程仓库,但别人还未基于此开发就可以使用。…
最佳实践
- 多个人基于同一分支开发时,更新,怎么操作?
- 设置git pull --rebase,设置方法同上边intellij idea 更新小节,如果不设置默认是git pull --merge
- 下游分支更新上游分支的最新提交
- 在下游分支上执行,git rebase 上游分支,如果下游分支有对应远程分支,则force push,注意跟同事协商好,同事全部都push了,由一个人rebase;如果没有对应远程分支,那就没不用这么麻烦了。
- 上游分支合并下游分支
- 先执行上一步,然后切换到上游分支执行,git merge 下游分支,这样的提交线是一条直线。
head
git的命令中经常会用到head,可以将head看做一个指针,head指本地的版本库,可以将head替换为远程库,比如origin/master,origin代表一个地址,master是分支。
origin
origin代表远程仓库的地址 ,在config文件中,如
也可以通过intellij idea查看,
右击git—manage remotes
参考
Why am I merging “remote-tracking branch ‘origin/develop’ into develop”?