git-restore的效果,简单来说便是用于康复缓存区(别号叫做索引区或index)和作业区(working directory)的资源文件,使他们回滚到上一次提交到(add操作)缓存区或上一次commit的状况。在运用指令时能够指定不同的可选项,使康复内容和方位更加灵活多变。
运用概要
下面是运用概要,能够在看完运用方法后再回来看会更加清晰,能够先越过:
git restore [<options>] [--source=<tree>] [--staged] [--worktree] --pathspec-from-file=<file> [--pathspec-file-nul]
可选参数
运用简写时留意大小写。
–worktree(-W)和–staged(-S)
经过运用–worktree和–staged选项能够指定康复的方位是作业区(working tree)或缓存区。假如都不指定,那么默许复原的是当时的作业区,一般是HEAD指向的commit。
–staged
–staged能够让复原方位指向缓存区。
举个比方,假定项目里有两个文件hello.txt和world.txt,将两个文件的内容分别做如下修正:
hello.txt文件
hello.txt文件内容
world.txt文件
world.txt文件内容
然后运用git add hello.txt指令将hello.txt文件添加到缓存区中,而world.txt文件坚持在作业区中不变。能够运用git status指令检查文件状况:
如上图所示hello.txt现已是modified,阐明现已添加到缓存区中,而world.txt是unstaged,阐明依然坚持在作业区中。
假如此刻想撤销现已提交到缓存区的文件改动,则能够运用git restore –staged <文件途径> 来进行文件逆向操作,将文件从缓存区中移除也便是变成unstaged状况。尝试输入:
git restore --staged . // 回滚文件状况
git status // 检查文件状况
履行指令后:
留意看此刻hello.txt文件的状况变为了unstaged,和world.txt文件相同,阐明文件现已被成功复原了。
–worktree
–worktree能够让复原的方位指向作业区。
仍是以上面这个比方举例,假定项目里有两个文件hello.txt和world.txt,修正如下:
hello.txt文件
hello.txt文件内容
world.txt文件
world.txt文件内容
然后运用git add指令将hello.txt添加到缓存区中,作业区只剩下world.txt文件是unstaged。假如此刻想复原作业区,则能够运用–worktree让restore只复原作业区的内容,操作如下:
git restore .
// 等同于
git restore --worktree .
// 等同于
git restore -W .
履行指令之后,运用git status检查:
成果如上,能够看到此刻world.txt文件从作业区中被复原成了未做修正时的状况,而hello.txt依然在缓存区中坚持不变。
除了运用restore康复文件内容外,还能够运用它康复被误删去的文件。举个,将上述比方中的hello.txt文件删去,然后运用git status指令检查内容:
此刻的hello.txt文件状况是deleted,假如想康复文件则能够运用git restore指令:
git restore hello.txt
// 等同于
git restore --worktree hello.txt
// 等同于
git restore -W hello.txt
履行指令后,运用git status检查:
会提示作业区的内容,没有任何的改动,hello.txt文件也成功被康复了。
留意:–worktree和–staged是能够一同运用的,它们会把缓存区和作业区一同进行康复。
–source=<tree>(-s <tree>)
该选参的效果是行将康复的作业区从指定的作业树(working tree)中康复。这个参数–source的值能够是某个commit、branch或许tag。留意:假如–source没有被指定,一起–staged被指定了,文件会从HEAD指向的commit中进行康复,不然一旦–source被指定,会默许从缓存区(index)进行康复。
举个比方:
假定某个分支中有两个commit。第一个commit增加了a.js,第二个commit中增加了b.js然后删去掉了a.js。
此刻项目中只要b.js,可是在一段时间开发后发现现在又需求a.js文件了,咋办?难道需求再切回上一个commit中复制a.js文件吗?no! no! no! 这么愚笨的方法,身为高档摸鱼工程师怎样能把时间糟蹋在这上面,接下来运用restore来快速康复文件:
// 这儿运用第一个commit的hash
git restore --source=63e02b3c5bb9605f1e6c8dd6bc51b466a2699d74 a.js
// 相当于
git restore --source=63e02b3c5bb9605f1e6c8dd6bc51b466a2699d74 --worktree a.js
在履行完指令后,咱们发现a.js文件被成功的康复到了项目中,而且状况是Untracked file。因为–source被指定了所以应该从缓存区中读取a.js,而且–staged和–worktree都未被指定,所以会写入到当时的作业区上,相当新创建一个a.js文件,所以文件状况是Untracked file。
接着运用–staged选参试试看:
// 这儿运用第一个commit的hash
git restore --source=63e02b3c5bb9605f1e6c8dd6bc51b466a2699d74 --staged a.js
奇怪的是,在项目中并没有发现有a.js,难道是康复失败了吗?
运用git status a.js检查文件的状况,咱们发现a.js文件先是被添加然后又被删去了。猜测是因为只在缓存区中康复了文件,并没有在作业区中康复文件才会导致文件状况是被删去。
所以应该康复文件一起加上–staged和–worktree在缓存区和作业区中一起康复文件:
// 这儿运用第一个commit的hash
git restore --source=63e02b3c5bb9605f1e6c8dd6bc51b466a2699d74 --staged --worktree a.js
此刻的a.js文件的状况便是现已被写入缓存区和作业区中。
所以在运用–source时,最好一起指定–staged和–worktree。
–patch(-p)
运用该参数后会以一种交互式的方法来问询如何处理文件每一处的修正(hunk)。
假定项目根目录中有一个a.js文件,咱们来随意写点东西:
class Person {
constructor(name) {
this.name = name;
}
sayHi() {
console.log(this.name + ':say hi');
}
sayGoodBye() {
console.log(this.name + ':say goodbye');
}
}
const somebody = new Person('tom');
根目录还有一个b.js文件:
console.log('b.js');
将这两个文件commit到本地仓库中。
随后咱们对作业区的文件做两处改动,首先将a.js进行内容修正:
class Person {
constructor(name, age) { // 新增age参数
this.name = name;
this.age = age; // 新增代码
}
sayHi() {
console.log(this.name + ':say hi');
}
sayGoodBye() {
console.log(this.name + ':say goodbye');
}
}
const somebody = new Person('tom', 28); // 新增参数
然后直接将b.js文件删去。
这时候,假如只想康复文件中的部分代码,而不是一整个文件,怎样办?是的,那便是运用–patch选项配合restore:
git restore --patch
咱们看到控制台里边呈现了能够交互的界面,而且问询咱们是否要扔掉本次修正在当时作业树中。而且本次文件修正被分为了两个hunk,也便是分为了两段代码来让用户承认修正。这个指令和git diff所展现内容似乎是一模相同的。
git中的每一次修正都会被认为是一个hunk,依据特定的方法来进行分割。
咱们来试试运用git diff:
git diff假如不加任何参数会将一切文件的变动一次性全部输出,而运用git restore –patch会将有变动的文件一个一个问询来让咱们处理是否要回滚,也便是第一张图蓝字的部分。
咱们看到处理文件的选项有[y,n,q,a,d,j,J,g,/,s,e,?],这几个英文字母又代表什么意思呢?这时候就能够运用?符号在控制台打印出协助阐明,咱们先来看看对应的英文阐明:
y – discard this hunk from worktree
n – do not discard this hunk from worktree
q – quit; do not discard this hunk or any of the remaining ones
a – discard this hunk and all later hunks in the file
d – do not discard this hunk or any of the later hunks in the file
g – select a hunk to go to
/ – search for a hunk matching the given regex
j – leave this hunk undecided, see next undecided hunk
J – leave this hunk undecided, see next hunk
s – split the current hunk into smaller hunks
e – manually edit the current hunk
? – print help
这儿一个一个选项来试并检查成果:
1.(y)
输入y将会扔掉本次hunk在作业树中的修正,接着会问询第二个hunk的处理方法:
2.(n)
n的效果便是不扔掉本次修正。
咱们挑选n并回车,修正的代码将会被保存下来,操作结束后对a.js文件改动就结束了:
const Person {
constructor(name) { // age参数被删去了
this.name = name;
// this.age = age 被删去了
}
sayHi() {
console.log(this.name + ':say hi');
}
sayGoodBye() {
console.log(this.name + ':say goodbye');
}
}
const somebody = new Person('tom', 28); // 传入28的参数被保存了
3.(q)
假如想退出本次restore,不对本次的hunks做任何的restore,这时候能够直接输入q。
再次回到对a.js做第一次修正的时候:
class Person {
constructor(name, age) { // 新增代码
this.name = name;
this.age = age; // 新增代码
}
sayHi() {
console.log(this.name + ':say hi');
}
sayGoodBye() {
console.log(this.name + ':say goodbye');
}
}
const somebody = new Person('tom', 28); // 新增代码
然后运用git restore –patch指令,接着输入q扔掉本次的一切restore操作(包括扔掉后边删去b.js的restore):
4.(a)
运用a就相当于直接运用git restore不带任何参数,扔掉本次单个文件内一切的hunk做的代码修正。
一旦咱们输入a并回车后,文件就会变成上一次commit的状况:
class Person {
constructor(name) {
this.name = name;
}
sayHi() {
console.log(this.name + ':say hi');
}
sayGoodBye() {
console.log(this.name + ':say goodbye');
}
}
const somebody = new Person('tom');
这样就处理完了第一个a.js文件,接着会继续问询是否对b.js进行restore,依据自己的需求挑选对应的参数就能够。
5.(d)
运用d将会承受对本文件的一切hunk修正,然后问询对下一个文件的restore:
在输入d参数后,a.js文件保存了对该文件的一切hunk,然后继续问询咱们对b.js的hunk。
6.(g)
运用g指令就能不按顺序,指定想要更改哪个hunk:
在输入g后,呈现了2个选项而且问询要去哪个hunk,输入2并回车,直接跳转到了第二个hunk中:
7.(/)
运用/将会以正则表达式匹配hunk修正的内容,比方假如想直接跳转到第二个hunk里边,咱们能够直接输入28来匹配:
咱们看到此刻现已跳转到了第二个hunk,并问询咱们处理的方法。
8.(J)
运用J能够暂时越过当时hunk,去处理下一个hunk。
如上图,在处理第一个hunk时,咱们输入了J来暂时越过这个hunk的restore转而进入第二个hunk的处理。
9.(j)
j的效果和J很相似,只不过j是跳转到下一个之前越过的未处理的hunk。在上面J的比方中,运用了J越过了第一个hunk,此刻再运用K越过第二个hunk,返回上一个hunk,也能够运用k越过第二个hunk,返回上一个未处理的hunk。接着能够再输入j检查成果:
如上图,成功跳转到了下一个未处理的hunk。
10.(s)
运用s能够将hunk拆分红更细粒度的多个hunks。
如上图,将第一个hunk拆分红更细粒度的两个hunks。
11.(e)
运用e选项能够手动的修正当时的hunk:
如上图,能够用类似vim编辑器去手动的修正hunk。
–ours、–theirs
当文件存在抵触(运用merge或许rebase等操作会发生抵触)处于unmerged paths,能够运用–ours或许–theirs来将文件restore成其间的一个版别。
假如有一个项目,这个项目有一个master分支和feature分支,在master分支中有一个名称为a的commit记录,这个commit中添加了一个文件weeks.txt并写入内容Monday。在feature分支中,也有一个weeks.txt文件而且写入内容为Tuesday。接着将feature分支兼并到master上,此刻weeks.txt文件发生了抵触:
这时假如运用git status检查weeks.txt文件:
此刻weeks.txt文件的状况便是unmerged paths也便是处于抵触状况。能够运用git restore来挑选需求保存的文件版别。比方假如想保存feature中的weeks.txt版别,就能够运用–thiers代表要保存merge的版别。
git restore weeks.txt --theris
履行指令后weeks.txt文件内容变为Tuesday
假如此刻想再切回master版别的代码,能够再运用–ours参数切回master:
git restore weeks.txt --ours
这个指令跟git checkout weeks.txt –theris或–ours功用相同。
–merge(-m)
该参数能够将现已处理完成的有抵触的文件,从头复原成未处理的发生抵触的状况。
仍是以上述的weeks.txt文件为例,此刻假如咱们现已处理完抵触,就能够运用git add weeks.txt将文件标记为现已处理完抵触。
可是现在假如想从头回到weeks.txt抵触发生的时候该怎样办呢?运用–merge就能做到:
git restore weeks.txt --merge
运用该指令就能够回滚weeks.txt文件,从头回到master merge feature的状况。
–conflict=<style>
这个参数和–merge的效果十分相似,只不过它能让抵触以不同方法展现。<style>的默许值是”merge”,可装备的值有”diff3″和”zdiff3″。
默许值merge的效果和运用–merge效果相同,直接来看diff3的效果:
在master分支有一个weeks.txt文件内容为Monday。然后运用git switch -c创建一个新的分支feature,然后修正weeks.txt内容为:
Tuesday
Common Change
运用commit提交到本地仓库中并命名为b。这样在feature分支的commit途径为a->b。从头切回master分支修正weeks.txt内容为:
Wednesday
Common Change
然后提交本地仓库并命名为c,在master分支的commit途径就为a->c。接着将feature分至兼并到master中:
这样”Tuesday”和”Wednesday”就发生了抵触,能够运用上述说到git restore或git checkout处理抵触。随后假如想回滚weeks.txt文件从头回到兼并的状况,能够运用git restore –conflict=merge或git restore –merge。
可是运用这两个指令都不能知道修正master和feature之前的,也便是它们的一起先人-名称为”a”的提交记录的文件内容,这时候就能够运用–conflict=diff3。
git restore weeks.txt --conflict=diff3
运用指令后:
如上图,在文件中呈现三条比照路线。ours、一起的先人||||||| base和theirs。这样就能够清楚比照本地改了哪些内容和行将兼并的分支有哪些区别。
值得留意的是此刻的一起修正内容”Common Change”在ours和theirs中都存在了,它是一个无需手动处理的抵触,没必要在两个内容块中都展现出来,所以能够运用第二个参数–zdiff3把Common Change的内容提取出来:
如上图,此刻的Common Change被提取到了抵触代码的外面。
–ignore-unmerged
当运用git restore去复原存在抵触且文件是unmerged的状况,那么这个操作不会被中断。
假定项目中有两个分支,一个分支为master,在该分支中有一个weeks.txt文件,文件内容为”Monday Common Change”。
另一个分支为feature,在该分支有一个weeks.txt文件,文件内容为”Tuesday Common Change”。除了该文件外还有一个fruits.txt内容为”Apple”:
这时候将feature分支兼并到master中。
如上图,fruits.txt文件被add到了缓存区,weeks.txt文件发生了抵触需求处理。
假如这时候直接运用git restore .指令康复一切文件是会失败的,而且restore操作会被终止。
这时候就能够加上–ignore-unmerged来疏忽有抵触且没有处理的文件(抵触内容会被保存,文件会被标记为已处理):
git restore --ignore-umerged --staged .
如上图,当时restore操作成功的进行了复原,而且weeks.txt的抵触仍是存在,文件也被标记为已处理。
–overlay、–no-overlay
在运用overlay形式时,假如在复原方针中不存在该文件,那么在当时的复原方位也不会直接删去该文件。默许运用的是–no-overlay,也便是和上面的条件相反会删去不存在的文件。
假定项目中有一个master分支,分支有两个文件,第一个是weeks.tx内容是”Monday”,第二个文件是fruits.txt内容是”Apple”。
项目中还有另一个feature分支,分支有一个文件weeks.txt内容是”Tuesday”。
现在假如想让master分支复原feature分支的一切文件,能够运用git restore指令来复原:
git restore . --source feature
复原成果:
如上图,weeks.txt文件被成功复原成了feature分支中的内容,可是fruits.txt文件却被删去了。假如想一起保存fruits.txt文件不被删去就能够运用–overlay:
git restore . --source feature --overlay
复原成果:
这时候fruits.txt文件被成功的保存了下来,而且weeks.txt被复原成了feature中的文件内容。
<pathspec>
用于限制git restore的效果规模,详细能够参考这篇文章。
–ignore-skip-worktree-bits
在稀疏检出形式中,默许只会更新和<pathspec>匹配的条目以及$GIT_DIR/info/sparse-checkout中声明的条目。这个选项将会疏忽稀疏形式,无条件的康复和<pathspec>匹配的条目。
–recurse-submodules、–no-recurse-submodules
假如运用**–recurse-submodules**选项,在复原作业区内容的一起还会同步复原submodules中的内容。假如没有指定该参数或许运用–no-recurse-submodules,submodules将不会进行复原,就像是运用git-checkout指令相同,HEAD指针将会和submodules进行分离。
假定项目中有该如下结构:
如上图,项目中有一个submodule,在submodule里边有一个README.md文件且没有任何内容。接下来随意写点内容做一些更改:
此刻在README.md文件中增加了”project init”,假如这时候运用restore指令进行复原:
git restore .
项目中的submodules是不会有任何变化的:
在履行指令后,README.md文件仍是和修正后的姿态相同。假如想要进行复原submodule则能够运用–recurse-submodules选项:
git restore . --recurse-modules
履行指令后:
README.md文件成功变成了上一次commit的状况。
–pathspec-from-file=<file>
在上面指定restore的<pathspec>都是来自指令行参数,然而也能够指定它来自某一个文件。
假定项目中有如下结构:
a.txt、b.txt和c.txt的内容都是空的,然后在pathspec.txt中写入要复原的:
a.txt
b.txt
如上所示,方针是复原a.txt和b.txt,而c.txt不进行复原。多个运用换行符进行分隔,每一行都是一个独立的。
然后在a.txt、b.txt以及c.txt分别写入各自的文件名称字符串:
最后尝试运用resotre指令进行复原:
git restore --pathspec-from-file=pathspec.txt
履行指令后:
如图所示,a.txt和b.txt被成功复原了,可是c.txt仍是坚持修正后的姿态,这阐明–pathspect-from-file选项收效了。
–pathspec-file-nul
只要和–pathspec-from-file才有意义,指定运用NUL字符作为分隔符而不是换行符。