文章目录
常用
撤销(3种)
-
use “git reset HEAD …” to unstage:取消暂存(包括删除操作)的文件(一般在暂存区时候用)
- 由暂存区回到工作区后,执行checkout
当执行 "git reset HEAD" 命令时,暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响。
-
git checkout – …" to discard changes in working directory:撤消对文件的修改(一般在工作区用)
以前暂存过一次或者没暂存都行,修改文件后,使用该命令会丢弃上一次的文件更改(包括删除和增加),回到上一次的样子。
当执行 "git checkout ." 或者 "git checkout -- <file>" 命令时,会用暂存区全部或指定的文件替换工作区的文件。这个操作很危险,会清除工作区中未添加到暂存区的改动。 当执行 "git checkout HEAD ." 或者 "git checkout HEAD <file>" 命令时,会用 HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和以及工作区中的文件。这个命令也是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。
-
提交后撤销:
有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。 此时,可以运行带有 --amend 选 项的提交命令来重新提交: 这个命令会将暂存区中的文件提交。 如果自上次提交以来你还未做任何修改(例如,在上次提交后马上执行了 此命令), 那么快照会保持不变,而你所修改的只是提交信息。 文本编辑器启动后,可以看到之前的提交信息。 编辑后保存会覆盖原来的提交信息。 例如,你提交后发现忘记了暂存某些需要的修改,可以像下面这样操作: $ git commit -m 'initial commit' $ git add forgotten_file $ git commit --amend
查看日志(本地查看和远程查看)
-
查看日志
-
git log:不建议:多屏显示控制方式
空格向下翻页 b 向上翻页 q 退出
-
git log --oneline:和下面差不多
-
git log --pretty=oneline:按照行显示,id会显示全
-
git reflog(建议使用):显示head
-
git log --graph --pretty=oneline(查看分支使用)
-
git log --graph --pretty=oneline --abbrev-commit
-
git log origin/master 或者 git log remotes/origin/master
-
版本的前进后退
本质:
三种方式
-
基于索引值操作[推荐]
git reset --hard [局部索引值] git reset --hard a6ace91
-
使用^符号:只能后退
git reset --hard HEAD^ 注:一个^表示后退一步,n 个表示后退 n 步
-
使用~符号:只能后退
git reset --hard HEAD~n 注:表示后退 n 步
reset(重置)命令的三个参数对比
拿后退操作来举例,其实主要就是版本相不相等的问题
-
–soft 参数:
仅仅在本地库移动 HEAD 指针
-
–mixed 参数:
在本地库和暂存区移动 HEAD 指针
重置暂存区
- –hard 参数:慎重使用!
- 在本地库移动 HEAD 指针
- 重置暂存区
- 重置工作区
删除文件并找回
删除
- git rm 默认将操作文件会放到暂存区,可以回到工作区,也可以commit后确认删除(删除需要两步)
- git rm --cached:版本库(包括index区)已经删除了,但是工作区文件还在。
- git rm -rf:已经在版本库的文件必须强制删除,版本库和工作区都被删除了
- rm 删除,默认将删除操作放到工作区,add后,commit(三步)
删除后找回(两种)
和上面的删除不一样,这次是直接使用linux的rm
-
永久删除:这个文件在删除前已经commit到本地库中了,此时使用rm文件后,再次add然后commit这个文件,就永久删除了,三个状态中都没有了。
$ rm test2.txt xulihao@DESKTOP-66V1RL1 MINGW64 /d/Users/xulihao/Desktop/gitlianxi (master) $ git status On branch master Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) deleted: test2.txt no changes added to commit (use "git add" and/or "git commit -a") xulihao@DESKTOP-66V1RL1 MINGW64 /d/Users/xulihao/Desktop/gitlianxi (master) $ git add test2.txt xulihao@DESKTOP-66V1RL1 MINGW64 /d/Users/xulihao/Desktop/gitlianxi (master) $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) deleted: test2.txt xulihao@DESKTOP-66V1RL1 MINGW64 /d/Users/xulihao/Desktop/gitlianxi (master) $ git commit -m "delet test2" [master 5bfa721] delet test2 1 file changed, 2 deletions(-) delete mode 100644 test2.txt xulihao@DESKTOP-66V1RL1 MINGW64 /d/Users/xulihao/Desktop/gitlianxi (master)
使用以下命令找回
git reset --hard b91b08e
2.**添加到暂存区的文件删除:**这个文件在删除前已经commit到本地库中了,此时使用rm文件后,再次add这个文件,不commit,使用git reset --hard HEAD
总结
- 前提:删除前,文件存在时的状态提交到了本地库。
- 操作:git reset --hard [指针位置]
- 删除操作已经提交到本地库:指针位置指向历史记录
- 删除操作尚未提交到本地库:指针位置使用 HEAD
疑问:git rm和rm的区别?
👉https://www.cnblogs.com/kevingrace/p/5674444.html
比较文件的差异
- linux操作(比的是文件本身)
- diff a b
- diff -u a b(详细对比)
- git diff(比较是区中的文件)
- git diff [文件名]
- 将工作区中的文件和暂存区进行比较
- git diff [本地库中历史版本orHEAD] [文件名]
- 将工作区中的文件和本地库历史记录比较
- 不带文件名比较多个文件
- git diff --chched 文件名 commit后的sha1值
- 比较暂存区和对象区
重写提交信息
- git commit --amend -m ‘修正’
- 修改最近一次提交的信息
分支
什么是分支?
默认是master,也叫主干分支。
在版本控制过程中,使用多条线同时推进多个任务。
分别对应**hot_fix(修复bug的热修复分支) master(主干) feature_blue(主题小组) feature_game(游戏小组)**四个分支
- 以上四个分支齐头并进,提高开发效率。
分支的好处?
- 同时并行推进多个功能开发,提高开发效率
- 各个分支在开发过程中,如果某一个分支开发失败,不会对其他分支有任何影响。失败的分支删除重新开始即可。
分支操作
- 创建分支
git branch [分支名]
- 创建并切换
git checkout -b 分支名
- 查看分支
git branch -v
- 切换分支
git checkout [分支名]
- 删除分支
- 前提,不能自己删自己
- 假如a分支是由master分出来的,在a分支中进行文件的增删改,已经commit了,没有合并的情况下,此时两个分支文件不一样,也不能删除,建议合并后再删除分支
- 如上,也可以强制删除 删除未合并分支(此时两个分支里文件相同) git branch -D 分支
- 删除已合并分支 git branch -d 分支名
- 细节:
- 如果在分支a中进行了写操作,但此操作局限在工作区中和暂存区中进行,没有commit,在master中可以看到该操作
- 如果分支a进行了写操作,并commit,那么master中将不会看到该操作。
- 假如a分支是由master分出来的,在a分支中进行文件的增删改,没有add和commit,处于工作区的情况下,那么此时文件操作在两个分支其实是一样的,所以此时可以直接删除分支,不用合并。
- 总结,删除分支主要看分支的操作是在工作区还是commit了,然后看两个分支的文件和操作是否一致。
合并分支(两种)
通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,
这样,从分支历史上就可以看出分支信息。
- 强制禁用
禁用
git merge --no-ff -m "merge with no-ff" 分支名
查看log
git log --graph --pretty=oneline --abbrev-commit
- 不禁用
-
第一步:切换到接受修改的分支(被合并的分支,一般是master,增加新内容)上
git checkout [被合并分支名]
-
第二步:执行 merge 命令
git merge [有新内容分支名]
解决分支合并时产生的冲突
-
出现原因,多个分支修改的同一个文件的同一行,由于版本不一样,在合并的时候不知道听谁的。如果版本号一致的话,那么会出现下图状态。
**总结:**分支出现冲突的原因来自于合并后的版本号不一样。 -
解决方法
- 此时就不能使用单纯的git merge去自动合并了。
- 编辑冲突文件,协商解决。
- 把文件协商修改到双方满意位置,wq。
- git add 【冲突文件名】
- git commit -m**(注意:不带文件名)**
git远程操作
- 创建远程库地址别名
- git remote -v 查看当前所有远程地址别名
- git remote add [地址别名] [远程地址]
-
推送
- git push [地址别名] [分支名]
- 或者git push origin dev:dev2(和上面的不同,在推送的时候可以修改推送分支的名字)
- 或者git push origin HEAD:dev2
-
克隆
git clone 远程地址
效果
- 完整的把远程库下载到本地
- 创建origin远程库地址别名
- 初始化本地库
-
拉取
- pull=fetch+merge
- git fetch [远程库地址别名] [远程分支名]
- 在本地库下git checkout origin/分支名,检查
- 切换到master,git merge [远程库地址别名] [远程分支名]
- 或者可以直接git pull [远程库地址别名] [远程分支名]
- git fetch origin 远程master:refs/remotes/origin/hello(将远程master拉取到本地hello分支并创建hello分支)
-
解决push时候的冲突
- 如果不是基于 GitHub 远程库的最新版所做的修改,不能推送,必须先拉取。
- 拉取下来后如果进入冲突状态,则按照分支冲突解决操作解决即可。
SSH免密登录
- 进入当前用户的家目录
- cd ~
- 删除.ssh 目录
- rm -rvf .ssh
- 运行命令生成.ssh 密钥目录
- ssh-keygen -t rsa -C [github邮箱]
- 注意:这里 -C这个参数是大写的 C
- 进入.ssh 目录查看文件列表
- 查看 id_rsa.pub 文件内容
- 复制 id_rsa.pub 文件内容,登录 GitHub,点击用户头像→Settings→SSH and GPGkeys
- New SSH Key
- 输入复制的密钥信息
GitFlow工作流
分支种类
- 主干分支 master
- 主要负责管理正在运行的生产环境代码,永远保持与正在运行的生产环境完全一致。
- 开发分支 develop
- 主要负责管理正在开发过程中的代码,一般情况下应该是最新的代码
- bug 修理分支 hotfix
- 主要负责管理生产环境下出现的紧急修复的代码。 从主干分支分出,修理完毕并测试上线后,并回主干分支。并回后,视情况可以删除该分支。
- 准生产分支(预发布分支) release
- 较大的版本上线前,会从开发分支中分出准生产分支,进行最后阶段的集成测试。该版本上线后,会合并到主干分支。生产环境运行一段阶段较稳定后可以视情况删除。
- 功能分支 feature
- 为了不影响较短周期的开发工作,一般把中长期开发模块,会从开发分支中独立出来。 开发完成后会合并到开发分支。
gitflow工作流举例
git高级部分👇👇👇
git版本穿梭
- 具体见上面的前进后退操作
checkout的放弃与穿越操作(游离状态)
- 不管是文件已经被暂存还是在对象区,只要修改文件了,此时当前文件默认就回到工作区了,所以checkout的操作范围是在工作区。
- 和reset head的比较(工作区修改后添加在暂存区触发,将暂存的内容回退到工作区)
- 穿越游离操作
- git checkout 版本号(前5位)
- 修改后必须提交(add+commit)
- 创建分支的好时机
- 过去做的事情对未来没影响,所以建议把过去修改了,建议创建分支,也就是‘平行宇宙’
- 当上一步提交后,会默认产生一个sha1值,用这个sha1值git branch ‘分支名’ sha1值创建分支
临时保存现场stash与差异性diff比较以及tag标签
为什么用?
- 建议(规范):在功能未开发完毕之前,不要commit
- 规定(必定):在没有commit之前,不能checkout开辟分支,要么提交,要么stash。
- tip:在开发的时候,项目经理突然让切换另外一个分支开辟新功能,新功能的项目比较紧急,但是当前分支并没有开发完,但是未开发完的分支最好不要提交,可是当前分支的开发功能写了一半,如果不commit又不能切换分支,此时stash就派上用场了。
使用(栈的形式)
- git stash保存现场 | git statsh save ‘现场名’
- git stash list 查看保存现场
- 还原现场(默认还原最近一次)
- git stash pop(还原现场并且将原来保存的现场删除)
- git stash appli(还原现场,不删除保存的现场)
- git stash apply stash@{下标}(指定恢复某一次现场)
- 删除现场
- git stash drop stash@{下标}
细节
- 如果不同的分支,在同一个commit版本阶段,在commit之前,可以切换分支。
- 如果不同的分支,在不同的commit版本阶段,没有commit,就不能切换分支。
- 如果前一个暂存现场和后一个暂存现场修改的是同一个文件的同一行,在恢复暂存的时候,会出现冲突,解决冲突和分支解决冲突是一样的
tag标签(类似于软件的更新版本,和commit后的sha1值关联)
- 使用(提交之后打标签)
- git tag v1.0打标签 (简单标签,只存储当前commit后的sha1值)
- git tag -a v2.0 -m ‘tag说明’ (有注释的标签,存储信息其中包含了提交后的sha1值)
- 查看 git tag
- git show 标签名 查看标签各种信息
- 模糊查询 git tag -l ’v*‘
- 删除 git tag -d ‘标签名’
- 注意事项
- 标签操作和分支没关系,在master分支对标签的操作,任何分支都可以查看
- git blame ’文件名‘ 查看文件所有commit的sha1值以及是由谁写的,具体到每一行
diff见上述比较文件差异目录
- 见上面git diff操作
本地与远程冲突问题
- 详情见上面git远程操作
gitk与gui
- 两种图形化界面,但是建议使用git bash
本地与远程分支的关联操作
https://www.liaoxuefeng.com/wiki/896043488029600/900375748016320
https://blog.csdn.net/u012216131/article/details/84256384
分支有三种
本地分支,本地的远程分支(追踪分支,是一个桥梁),远程分支
本地分支通过追踪分支(remotes/origin/master)进而查看远程分支(/origin/master)
- git branch --set-upstream-to=origin/ master
- 设置本地分支master跟踪origin/远程分支
本地到远程(记得建立追踪关系)
-
git push -u origin master(下一次推送master的时候直接就可以使用git push)
-
git log origin/master 查看远程日志
-
查看 git remote -av
-
当已经向远程仓库push了master以后,然后再用本地分支的a分支向master合并时候,会出现以下错误。
fatal: refusing to merge unrelated histories,解决如下👇
-
git merge a --allow-unrelated-histories
-
然后add再commit
-
合并后向远程推送的时候又会出现问题。
-
上面给出的建议就是先拉取下来,然后再合并,然后push,参考分支冲突解决。
-
或者直接使用git pull origin master --allow-unrelated-histories 然后git push -u origin master
-
当master推送了以后,在推送a分支的时候直接使用git push就不行了,因为a分支还未关联,解决如下👇
- git push -u origin a
- 或者 git push --set-upstream origin a 本地有,远程没有
远程到本地(记得建立追踪关系)
- git pull拉取远程所有分支,此时拉取下来的分支处于追踪分支,接下来解决的问题就是追踪分支如何到本地分支?
- 解决①
- git checkout -b dev origin/dev 创建本地dev分支并切换,再和pull下来的追踪分支origin/dev关联
- 然后git branch -av查看,此时本地dev就和远程dev分支绑定起来了。
- 解决②
- git checkout -b dev --track origin/dev 把追踪分支和本地分支dev关联起来,也可以关联本地别的分支,但是不建议
- 或者直接 git checkout --track origin/dev,默认与本地分支dev关联,不能指定别的本地分支。
- 解决③
- git pull origin 远程dev1分支:dev2,在拉取的直接将远程的dev1分支关联到本地的dev2分支
- 上述在拉取的时候,如果本地没有的dev2分支,在拉取的时候就直接在本地创建了。
操作本地与github的分支与标签
- 删除远程dev分支
- git push origin :dev(注意:冒号前面是一个空格)
- git push origin --delete dev或者直接git push origin -d dev
- 本地没有dev分支,但是本地却感知远端dev分支,或者本地有dev分支,但是远程却已经删除dev分支了
- git remote prune origin --dry-run 检测
- git remote prune origin 清理无效的追踪分支
- tag标签(本地操作详细见上面tag标签目录)
- 向远程推送标签 git push origin v1.0
- 远程推送多个标签 git push origin --tags
- git push origin v1.0:v5.0 将本地1.0推送成远程5.0 不建议!!
- 获取远程标签
- git fetch origin tag v1.0 只拉取标签
- 删除远程标签
- git push origin :v1.0(冒号前面是空格
- 如果将远程标签删除,其他用户无法感知的,还需要pull一下,然后再删除本地标签。
git gc(压缩文件)
省略。。。。
bare与submodule
裸库:没有工作区的仓库,一般是远程仓库,存在于服务端。
- 在本地创建裸库 git init --bare
submodule:子模块
- 应用场景:在一个仓库中,引用另一个仓库的代码
- 首先准备A 和B两个仓库(远程和本地都有)
- 在本地A库中,执行git submodule add B的远程ssh地址
- 接着push A,在github上就可以看到B库了
子模块修改操作
- A中有B库,在B库修改文件后,Bpush之后 A无法直接感知(A的远程B和本地B没有改变),👉需要在A库中进入B库主动pull一下,此时A库的本地B库就可以看见修改了,但是远端的A库照样没有改变,需要在A库中再push一次(不进入A库的B库),这样在远端的A库中就可以看到改变了。
- 不建议采用上述方法,可以使用下面的简化方法
- 从上面👉部分开始,不进入A的B库中pull,直接在A库中git submodule foreach git pull,这是为了简化A库中有很多子库。此时本地A库中就能看见B的修改了,但是远程A还是感知不到,需要在A库在push一次,这样远程A库也就能看见修改了。
遗留问题
此时在另外一个C文件中,去直接clone远程的A库的话,会显示A的子库B为空文件,解决如下
- git clone 远程A的ssh地址 –recursive
recursive(递归的意思)
subtree
上面的submodule是单向操作的,也就是说在子模块中修改,父工程可以感知,但是在父工程修改,总模块不好感知,所以采用subtree可以双向感知。
subtree的基本使用
- 老样子,首先在远程建立A和B库,然后本地建立AB库,向远程推送。
- 将B库加入到A库中,git subtree add -P B B仓库的地址 B仓库的分支 --sqash
- 也可以在A仓库中先给B仓库的地址起一个别名然后 git subtree add -P B B仓库地址别名 B仓库的分支
- –sqash参数的作用
- 减少commit的次数,降低子工程提交的commit对父工程的干扰,但是有可能会产生冲突,如果加了sqash,以后都加,如果不加,那么以后都不加。
subtree进行父子工程的双向操作
子工程改变,让父工程感知。
- 在B库中修改文件,然后push,同上,A库的本地和远端包括A库的B库都无法感知。
- 在本地A库中,git subtree pull -P B B的远程地址 B的分支 将B模块的改变加入到A工程,此时本地的A库的B库就可以看到改变,但是远端A库的B库还是没啥改变。需要在本地A库中向远程A库再push一次,这样就可以看到了。
父工程改变,让子工程感知。
- 在父工程A库的B库中的修改文件,让子库B感知改变
- 首先,在父工程A库的B库中的修改文件,然后在A的B库中push,此时在远端的A库中的B库已经可以发现修改了,但是远端的B库并没有任何的改变,解决如下
- 在push 的时候,在A库中不进入B库,git subtree push -P B B仓库的地址 B仓库的分支
- 上面推送了两次,那么是否可以只推送一次呢?答案是:不可能。。。。必须推送两次。
引入子工程时加–sqash参数后出现的冲突问题解析
在分支合并时候引起冲突的原因是什么?
- 两个分支拥有共同祖先,但是由于修改的是一个文件的同一行,会引起冲突。
- 两个分支没有共同的祖先,那怕修改的同一个文件的不同行,也会引起冲突。
- 如果不同的分支来自同一个祖先,可能会冲突
- 如果不同的分支来自不同的祖先,10000%会冲突
- cherry-pick引起的不规范冲突
还有一种冲突是commit冲突,git rebase会遇到
为什么加–sqash会产生冲突呢?
我的理解就是加了–sqash后,会再commit一次,屏蔽掉了以前的commit值,此时远端的仓库和本地的。。。
。。。。算了算了,还是百度吧。
建议要么加,要么不加。
cherry-pick
使用场景:
假设有A和B两个分支,在B分支已经进行开发,并且已经提交了几次了,此时发现这个功能应该在A分支上开发,写错分支了。。。那么如何将B分支已经开发的功能转移到A呢,常规操作是merge,但是merge后,B分支的所有commit都过去和A合并了,可以我只想将开发过程中指定一次的commit合并过去,怎么办呢?
解决如下
- 将已提交的B开发功能转移到A,转移的过程叫cherry-pick,每次只能转移(复制)一次commit
- 首先切换到 A分支,然后git cherry-pick commit值
- 在A分支gitlog查看,内容一样,但是sha1值会变(内容复制,sha1值会变)
- 转移完之后,将dev的转移commit删除,如下操作
- git reset --head 转移之前的commit值
- 不想要这个分支,直接git branch -D dev
细节:cherry-pick时候不能跨结点,也就是说,不能直接拿最后一次提交的sha1值,还有就是只能一个一个拿
git rebase(变基操作)
为了解决cherry-pick只能一个一个移动,引入了git rebase变基操作
- rebase会更改历史,rebase后不像merge,rebase后只有一条线,gitlog查看,dev的commit从master的第一次commit开始。
- cherry-pick是在master中操作A分支,将A转移到master
- git rebase是在A分支操作,将A的根基转移到master
- 两者的操作点不一样
- 建议在本地操作 rebase,不要推送到github
- 建议不要在master上rebase
git rebase的冲突解决
-
vim add. 然后git rebase --continue
-
或者 忽略冲突(放弃rebase所在分支的修改,直接使用其他分支) git rebase --skip后然后再执行 git rebase
master
终止rebase,还原成rebase之前的场景
- git rebase --abort
git pull --rebase origin master
rebase的使用
- 有master和dev分支,在dev中操作,向master转移
- git rebase master,出现冲突,参考上面冲突解决方案。
- 最后切换到master,执行merge
git rebase和git merge和git cherry-pick的区别
https://www.cnblogs.com/ludashi/p/8213550.html
https://www.cnblogs.com/NaughtyCat/p/differ-with-rebase-merge-cherry-pick.html
在idea中使用gradle整合gretty进行web开发
.gitignore
- 通配符
- https://www.cnblogs.com/kevingrace/p/5690241.html
- 各种.gitignore文件
- https://github.com/github/gitignore
使用gretty进行remote测试
gitlab
待续