git 入门、reset的3种模式、回滚文件、还原文件、变基、merge

本文详细介绍了Git的工作流程,包括工作目录、索引区及历史区的概念,重点讲解了git reset的不同模式及其应用场景,同时提供了IntelliJ IDEA中的常用Git操作指南。

git基础知识

盗用网上的一张图,git有工作目录、索引区(也叫暂存区)、历史区,这3个区,一定要记在脑子里,基本上git所有的操作都是操作这3个区。新建一个文件并提交的一般操作是,

  1. 新建文件
  2. git add到索引区
  3. 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

也许你见过如上的合并,这通常发生在更新代码时,它是怎么发生的呢?发生场景

  1. 本地更改了代码commit了,但未push
  2. 其他人也更改这个分支commit了,并push了
  3. 你打算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”?

<think>好的,用户现在的问题是,如何使用Git将特定文件回滚到指定的commit,同时保持其他文件。之前我已经给出过三种方法,但用户现在再次提问,可能希望得到更详细的解释或者确认正确的方法。我需要结合用户提供的引用内容来调整回答,确保准确。 首先,回顾用户提供的引用内容。引用1提到了使用git revert和git reset回滚整个提交,但这会影响所有文件,不是用户想要的。引用2和3详细介绍了git reset的不同选项,但同样是针对整个提交,而不是单个文件。用户需要的是仅回退特定文件,所以这些方法可能不太适用。 用户之前的回答中提到了三种方法,其中方法1是使用git checkout来检出特定commit中的文件,这应该是最直接的方法。用户现在的问题可能对这种方法的具体步骤不太清楚,或者需要更多的注意事项。同时,用户提供的引用中提到的git reset --hard等命令可能会让用户混淆,因为那些是针对整个版本库的操作,而用户需要的是单个文件。 因此,我需要明确强调使用git checkout <commit> -- <file>的方法,这是Git官方推荐的方式。同时,用户可能担心覆盖当前工作区的修改,因此需要提醒他们在操作前备份文件或者确认是否需要提交更改。此外,用户可能关心如何找到正确的commit哈希值,所以需要详细说明如何通过git log来查找文件的历史记录。 另外,用户可能还想知道其他方法,比如使用git restore或者git revert的部分操作,但这些方法可能不如git checkout直接。需要确认这些方法是否可行,并指出它们的适用场景。例如,git restore是在较新的Git版本中引入的,可以作为git checkout的替代,但可能用户更熟悉checkout命令。 还要注意用户提到的保持其他文件,所以在操作过程中必须确保只针对特定文件进行操作。任何涉及提交的操作,如方法2中的git revert,可能需要更多的步骤来分离文件的修改,这可能会比较复杂,适合高级用户。 最后,需要总结不同方法的优缺点,帮助用户根据具体情况选择最合适的方法。同时,回答中的注意事项,如备份文件、避免强制推送等,都是关键点,需要再次强调以确保用户操作安全。</think>### Git精准回退单个文件到指定commit的完整指南 根据您的需求,以下是实现文件回滚3种专业方法(以`app.js`文件为例): --- #### **方法1:精准检出历史版本(推荐方案)** ```bash # 查看目标文件的提交历史 git log --follow -- app.js # 定位目标commit的哈希值(示例:d3f4g5h) git checkout d3f4g5h -- app.js # 查看文件差异确认回滚效果 git diff HEAD app.js # 提交git commit -m "回滚app.js到d3f4g5h版本" ``` *核心原理*:通过`git checkout`从历史提交中提取目标文件覆盖当前版本[^1] --- #### **方法2:反向补丁应用法** ```bash # 查找需要撤销的提交(示例:a2b3c4d) git show a2b3c4d -- app.js # 生成反向修改补丁 git revert -n a2b3c4d # 注意此处会撤销该提交的所有文件修改 # 重置不需要回滚文件 git checkout HEAD -- . # 恢复所有文件到当前状态 git checkout a2b3c4d~1 -- app.js # 单独提取前一个版本的app.js # 提交git commit -m "撤销a2b3c4d对app.js的修改" ``` *适用场景*:需要处理跨多个提交的复杂修改时[^2] --- #### **方法3:交互式重置法** ```bash # 创建临时分支 git checkout -b temp-reset # 重置到目标commit(示例:e4f5g6h) git reset --mixed e4f5g6h # 恢复其他文件到最新状态 git checkout HEAD@{1} -- . # 恢复所有文件 git checkout e4f5g6h -- app.js # 单独保留目标文件 # 提交并合并回主分支 git commit -m "精准回滚app.js" git checkout main git merge temp-reset ``` *优势*:可处理未提交的本地修改[^3] --- ### 关键操作对比 | 方法 | 操作复杂度 | 历史记录影响 | 推荐指数 | |--------|------------|--------------|----------| | 方法1 | ★☆☆ | 生成新提交 | ★★★★★ | | 方法2 | ★★★☆ | 生成新提交 | ★★★☆ | | 方法3 | ★★★★ | 生成新提交 | ★★☆ | --- #### **高级技巧** 1. 时间戳定位commit: ```bash git checkout $(git rev-list -n1 --before="2023-06-01" HEAD) -- app.js ``` 2. 批量回滚多个文件: ```bash git checkout d3f4g5h -- src/*.js config/*.json ``` 3. 安全验证步骤: ```bash # 显示目标文件的历史版本 git show d3f4g5h:app.js > app.js.bak # 对比差异 diff app.js app.js.bak ``` --- #### **操作注意事项** 1. 强制覆盖保护机制: ```bash git config --global alias.force-checkout &#39;!git stash && git checkout $1 -- $2 && git stash pop&#39; # 使用:git force-checkout d3f4g5h app.js ``` 2. 文件路径追踪验证: ```bash git ls-tree -r d3f4g5h --name-only | grep app.js ``` 3. 恢复误操作: ```bash git reflog show --all | grep "checkout: moving" ``` --- ### 典型错误解决方案 **问题现象**:`error: pathspec &#39;app.js&#39; did not match any file(s) known to git` - 解决方案: ```bash # 确认文件历史存在 git log --diff-filter=D -- app.js # 恢复被删除的文件 git checkout d3f4g5h^ -- app.js ``` --- **相关问题**: 1. 如何恢复Git中意外覆盖的文件修改? 2. 怎样比较两个commit之间特定文件的差异? 3. Git如何将某个文件的修改合并到其他分支?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值