0


git的基本操作 + 分支管理

一、基本操作

1. 修改文件

Git比其他的版本管理器设计得更加优秀,因为

Git追踪并管理的是修改,而非文件

修改一个文件,不管你是添加一行,或者删除一行,还是添加了又删除了,甚至你创建了一个新文件,只要和源文件有所不同的,这些都算是一个修改。

现在我们来对ReadMe文件做一下修改:

修改前

image-20241009101849350

修改后

image-20241009101938258

目前仓库的ReadMe和工作区的ReadMe是不同的。使用

git status

命令可以查看文件是否被修改。

image-20241009102433596

很清楚,红色的字体就可以看出来,文件是被修改了的。工作区的ReadMe文件被修改了,还没有进行add和commit操作。

目前我们只看见了文件被修改了,但是不知道具体文件的哪一个位置被修改了,用一个命令可以查看具体修改内容的位置。

**

git diff [file]

**:显示工作区和暂存区文件的差异。

**

git diff HEAD -- [file]

**:显示暂存区和版本库文件的差异。

image-20241009103122132

image-20241009103405601

git add 之后,就没有看到上面 no changes added to commit (use “git add”

and/or “git commit -a”) 的消息了。接下来让我们继续 git commit 即可:

image-20241009103730079

这时所有的修改文件已经被提交到了版本库了。


2. 版本回退

如果有一天你发现你的工作出现了问题,需要回退到某个历史的版本重新开始,这时你就需要版本回退的功能了。

版本回退功能:**

git reset

**。这里的 “回退” 指的是文件的内容进行回退。

git reset

命令语法格式为: **

git reset [--soft | --mixed | --hard] [HEAD]

**

  • –soft: 回退版本库,而工作区和暂存区不变。
  • –mixed:回退暂存区和版本库,而工作区不变,注意这是默认选项
  • –hard:回退暂存区、版本库和工作区。
  • **HEAD说明**:◦ 可以直接写成 commit id,表示回退到指定的版本。◦ HEAD 表示当前版本。◦ HEAD^ 表示上一个版本。◦ HEAD^^ 表示上上一个版本。◦ 以此类推……
  • 也可以使用数字来表示:◦ HEAD0 表⽰当前版本◦ HEAD1 上⼀个版本◦ HEAD2 上上⼀个版本◦ 以此类推……
    工作区暂存区版本库选项不变不变回退–soft不变回退回退–mixed回退回退回退–hard

为了方便测试回退功能,我们写3个版本的ReadMe,都进行commit操作。

版本1

image-20241009112454070

版本2

image-20241009112859597

版本3

image-20241009113025711

最后提交的是版本3,某些原因,我们需要回退到版本2,重新开始工作,由于需要将工作区的ReadMe文件回退了,所以需要

--hard

选项。

这里的回退我们使用

commit id

来做,于是需要

git log --pretty=oneline

,来查看最近几次的 commit id。

image-20241009114009450

如我们所料,当前的ReadMe文件已经回退到了版本2,当前我们使用git log来查看一下提交日志,可以发现HEAD指向了版本2。

image-20241009114234631

到这里我们的回退工作就算是完成了,但是我后悔了,我想再回到版本3怎么办?我们可以继续使用git reset,但是我们目前git log打印日志,没法知道版本3的commit id。

Git提供了一个

git reflog

命令来补救一下,该命令用来记录本地的每一次命令。

image-20241009132946751

注意这里的黄色的内容是commit id的部分,通过这个id可以进行文件操作。

image-20241009133233401


值得注意的是,在实际的开发中,由于长时间的开发,导致commit id早就找不到了,某一天你先回退到version3版本,那如何操作呢?貌似是不可能了!!!

值得说的是,Git 的版本回退速度非常快,因为 Git 在内部有个指向当前分支(此处是master)的HEAD 指针,

refs/heads/master

文件里保存当前 master 分支的最新 commit id 。当我们在回退版本的时候,Git 仅仅是给 refs/heads/master 中存储⼀个特定的commit id,可以简单理解成如下⽰意图:

image-20241009134145869

image-20241009134158365

3. 撤销修改

3.1 情况一:对于工作区的代码,还没有add

当你对工作区中的文件写了几天的代码,但是你一直没有进行add操作,这时你觉得这几天写的代码不行,你需要重新写,因为你写了很多代码了,你也记不住写了哪些代码,当然也不可能一行一行的在源文件中去删除。

Git给我们提供了好的方式 – **

git checkout -- [file]

**,让文件回到最近一次add或commit的状态。该命令中的–是很重要的,千万不能少,这是啥意思呢?后面说。

示例:

image-20241009135301183

3.2 情况二:已经add,但没有commit

对于此状态下,我们可以使用git reset中的–mixed选项,回退暂存区,而工作区不变。

image-20241009140621989

image-20241009140731142

在工作区中的文件新增了三行内容,然后进行add操作。我们的目的是撤销add操作,使得暂存区回到上一次操作。

image-20241009141043311

可以发现,暂存区是干净的,而工作区有修改。

你可以再次使用 git checkout – ReadMe 命令,让工作区的文件回到上一次add的状态。

3.3 情况三:已经add,并且已经commit了

这个简单,就是我们之前提到的

git reset --hard HEAD^

回退到上一个版本。

不过!这是建立在你还没有将本地版本库提交到远程版本库(后续会说到),如果已经提交到了本地版本库,那么就无力回天了。

4. 删除文件

在git中删除文件也是一种修改操作。

在工作区使用rm可以删除文件吗?

image-20241009141941829

此时,git status会告诉你,哪些文件被删除了,但是这里的只是删除了工作区中的文件,版本库中的文件可没有删除掉。

走到这里:

①不小心删错了。

②确实要从版本库中删除该文件。

对于第一种情况,删错了,**

git checkout -- file1

** 就可以恢复了。

image-20241009142623149

对于第二种情况,显示是没有删除干净的,git中有一个命令提供给我们删除工作区和版本库中的文件:**

git rm [file]

**

并且执行commit:

image-20241009143109009

二、分支管理

1. 理解分支

在版本回退里,你已经知道,每次提交,Git都把它们串成⼀条时间线,这条时间线就可以理解为是⼀个分支。截止到目前,只有⼀条时间线,在Git,这个分支叫主分支,即 master 分支。

再来理解⼀下HEAD,HEAD 严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD 指向的就是当前分支。

image-20241009143954139

每次提交。master分支都会向前移动一步,随着你提交的越来越多,master分支也越来越长,但是HEAD是一直指向的master分支的。所以当前分支一直是master分支。

image-20241009144624326

2. 创建分支

Git支持我们创建和查看分支。

**

git branch

**:查看当前本地的所有分支。

image-20241009183816881

**

git branch dev

**:创建dev的分支。

image-20241009183855490

image-20241009184352584

很明显,当前有两个分支,并且两个分支都指向的同一个修改。但是HEAD还是执行master分支的。

下面的图就能清晰的看出它们的关系:

image-20241009184632854

3. 切换分支

当我们创建出了dev分支,需要转换到dev分支上进行开发,如何做到呢?

**

git checkout dev

可以切换分支。还有一个命令可以直接创建并切换分支,一步到位:

git checkout -b dev

**

image-20241009185238026

切换到了dev分支了之后,我们就可以在该分支下进行工作了。

hello linux
aaaaabbbbb
hello version1
hello version2
write aaa to new branch      #这是新增的内容

image-20241009185546549

现在,dev分支工作完成了,我们切换到master分支上去。

image-20241009190315621

切回到master分支上了之后,发现ReadMe上新增的内容不见了。赶紧切回到dev看看。

在这里插入图片描述

在dev分支上内容是还在的,为什么会出现这种情况呢?我们来看看dev和master的指向就知道了

image-20241009190716015

可以看到它们两个的指向是不一样的,到这里就理解了,我们是在dev分支上提交的,但是master分支上此刻的提交点并没有改变。

我们就可以得到下面的图:

image-20241009190921619

当切换到master分支上的时候,HEAD执行master,当然就看不到dev上的了。

4. 合并分支

为了解决上述问题,master上也能里面看到新的提交,我们就需要将dev分支合并到master分支上去。

合并的时候切,换到master分支下,使用命令:**

git merge dev

**

csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git checkout master
Switched to branch 'master'
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git merge dev
Updating f186ca8..ba1c065
Fast-forward
 ReadMe |1+1 file changed,1insertion(+)
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ cat ReadMe 
hello linux
aaaaabbbbb
hello version1
hello version2
write aaa to new branch

这时,在master分支下,也能看到新的提交了。

示意图就变成了这样:

image-20241009191546335

Fast-forward 代表“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。

当然,也不是每次合并都能 Fast-forward,我们后面会讲其他⽅式的合并。

5. 删除分支

当前dev分支合并完成后,对我们来说就没啥用了,于是我们就可以删除该分支了,注意,当前处于dev分支下,是不可以删除dev分支的。

删除分支命令:**

git brach -d dev 

**

image-20241009191931591

image-20241009192033413

因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master分支上工作效果是⼀样的,但过程更安全。

6. 合并冲突

在合并分支的时候,并不是我们想合并就能合并成功的,有一种情况,会有合并冲突出现,这时就需要我们自己手动来解决该问题。

举个例子

  • 创建一个新的分支dev1并切换过去:get checkout -b dev1,然后再dev1分支上,将ReadMe文件进行修改,然后add和commit操作。
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ cat ReadMe 
hello linux
aaaaabbbbb
hello version1
hello version2
write bbb to new branch    #在dev1分支上,将aaa改成了bbb
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git add ReadMe 
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git commit -m "modify ReadMe"[dev1 be7b371] modify ReadMe
 1 file changed,1insertion(+),1deletion(-)
  • 再切换到master分支,查看ReadMe文件,看到还是aaa内容,之前说过了,这也很正常。
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ cat ReadMe 
hello linux
aaaaabbbbb
hello version1
hello version2
write aaa to new branch   #aaa并没有改变
  • 但是现在在master分支上我们也进行修改ReadMe文件,并且进行add和commit操作。
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ cat ReadMe 
hello linux
aaaaabbbbb
hello version1
hello version2
write ccc to new branch   #在master分支上,将aaa修改为了ccc
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git add ReadMe 
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git commit -m "modify ReadMe"[master 40de75c] modify ReadMe
 1 file changed,1insertion(+),1deletion(-)

此时的结构图如示:

image-20241012160441238

  • 最后一步,将dev1分支合并到master分支上。

image-20241012160728247

执行git merge dev1合并命令的时候,出现了很明显的字样,合并冲突。为什么会出现这种情况呢?

因为两个分支修改了同一个文件,git不知道要管理哪个了。所以这时候,就需要我们手动解决冲突。

解决冲突:

当我们查看ReadMe文件,会看到这些内容:

image-20241012161434044

多了三行我们看不懂的内容,<<<<<<< HEAD、=======、>>>>>>> dev1。

解释

<<<<<<< HEAD和=======之间的内容是master分支修改的内容。

=======和>>>>>>> dev1之间的内容是dev1分支修改的内容。

其他的内容就是没有修改过的。

为了解决冲突,我们需要手动的删除多了的三行内容,选择你要留下来的内容。你可以将ccc和bbb内容都留下来,或者留一个等。

这里我选择将两个内容都留下来,修改文件内容后,最后还要进行add和commit操作,这样我们就完成了解决合并冲突了。

csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ cat ReadMe 
hello linux
aaaaabbbbb
hello version1
hello version2
write ccc to new branch
write bbb to new branch
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git add ReadMe 
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git commit -m "merge ReadMe"[master 1bd16de] merge ReadMe

此时的状态就变成了:

image-20241012162803832

其实git给我们提供了带参数的git log命令可以查看分支合并情况。

这里使用 **

git log --graph --pretty=oneline --abbrev-commit

**

image-20241012163153784

最后记得将dev1分支给删除掉。

image-20241012163323650

7. 分支管理策略

通常合并分支的时候,如果可能,Git会采用Fast forward模式,我们第一次合并分支的时候,就是该模式,会出现下面的情况。

image-20241012164854579

在这种 Fast forward 模式下,删除分支后,查看分支历史时,会丢掉分支信息,看不出来最新提交到底是 merge 进来的还是正常提交的

删除了dev分支后,就会出现下面的情况:

image-20241012165106951

dev分支提交的信息就会消失,我们就看不出来最新的提交是master自己提交的,还是其他分支合并提交的。

在我们上述解决合并冲突的例子中看出该模式就不是Fast forward模式了。

image-20241012165509933

即使你删除了dev分支,依然可以看到master的最新一次提交是其他分支合并得到的。

image-20241012165623244

接下来我们可以实操一下,很简单,我们先创建并切换到dev2分支,在dev2分支上修改ReadMe文件,并进行add和commit操作,然后再切换到master分支上,将dev2分支合并到master上。只不过这里的合并命令和之前的不一样,需要添加选项 **

--no-ff

,完整的命令:

git merge --no-ff -m "merge with no-ff" dev2

**

禁用 Fast forward 模式后,会创建⼀个新的 commit ,所以加上 -m 参数,把描述写进去。

合并之后应当变成这样:

image-20241012180126753

即使你删除了dev2分支后,依然知道这是分支合并提交的。

image-20241012180146500

7.1 分支策略

在实际开发中,master分支是非常稳定的,仅仅用来发布新的版本,不能在上面进行开发。

我们开发都是在其他分支上进行开发,做完活之后,测试也通过了之后,再将这些分支合并到主分支上去。

所以,团队合作的分支就像这样:

image-20241012181409493

8. bug分支

假如现在我们正在dev2分支上进行开发,突然master分支上出现了bug,需要解决。在Git中,每一个bug都可以单独开一个临时分支出来进行修复bug,修复后,合并该分支到master分支上去,然后将该临时分支删除掉,然后继续dev2的工作。

比如,当前dev2正在进行开发到一半了,还无法进行提交。

csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ cat ReadMe 
hello linux
aaaaabbbbb
hello version1
hello version2
write ccc to new branch
write bbb to new branch
fffffffffffffffffffffff
I am coding......           #这一行是dev2分支正在开发的内容
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git status 
On branch dev2
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:   ReadMe

no changes added to commit(use "git add"and/or"git commit -a")

此时出现了master分支上出现了bug,我们需要切换到master分支上去,创建出一个临时分支去修复bug。

但是此时dev2分支开发的内容还没有开发完,因为还没有提交,所以master分支上也是可以看到dev2上新开发的内容,但是我们是不需要master分支看到这个内容,所以我们需要将dev2新开发的内容,临时保存下来,当bug分支修复后,再恢复出来。

Git提供了 **

git stash

** 命令,可以将当前的工作区信息进行储藏,被储藏的内容可以在将来某个时间恢复出来。

csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git stash
Saved working directory and index state WIP on dev2: f6bcfbe merge with no-ff
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git status 
On branch dev2
nothing to commit, working tree clean

使用了git stash命令后,工作区就是干净的了,这时我们就可以放心切换到master分支上,创建临时分支修改bug了。

修复bug

csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git checkout master    #切换到master分支
Switched to branch 'master'
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git checkout -b fix_bug   #创建并切换到fix_bug分支
Switched to a new branch 'fix_bug'
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ vim ReadMe 
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ cat ReadMe   
hello linux
aaaaabbbbb
hello version1
hello version2
write ccc to new branch
write bbb to new branch
fffffffffffffffffffffff
abcdef              #这一行是修复的bug,忘记写了
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git add ReadMe 
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git commit -m "fix bug"[fix_bug a29955c] fix bug
 1 file changed,1insertion(+)

修复完成后,切换到master分支,并完成合并,删除fix_bug分支。

csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git checkout master 
Switched to branch 'master'
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git merge --no-ff 
fatal: No remote for the current branch.
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git merge --no-ff -m "merge fix_bug branch" fix_bug
Merge made by the 'ort' strategy.
 ReadMe |1+1 file changed,1insertion(+)

至此,修复bug的任务已经完成了,我们切换到dev2分支,继续完成我们的任务。

csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git checkout dev2
Switched to branch 'dev2'
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git status 
On branch dev2
nothing to commit, working tree clean

工作区是干净的,之前的dev2的工作现场存到哪里去了?使用 **

tree .git

** 命令查看,可以看到新增了一个stash文件

image-20241014101757282

该stash文件就是存放的dev2工作的内容。为了完成dev2的工作,我们需要将stash中的内容恢复出来。

恢复命令:**

git stash pop

**,恢复内容的同时,也会将stash文件给删除了。

image-20241014102105305

你也可以使用 **

git stash apply

** 恢复,但是恢复后,stash内容并不删除,你需要使用 **

git stash drop

** 删除。

csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ cat ReadMe 
hello linux
aaaaabbbbb
hello version1
hello version2
write ccc to new branch
write bbb to new branch
fffffffffffffffffffffff
I am coding...... Done!    #已经完成开发了
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git add ReadMe 
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git commit -m "modify ReadMe"[dev2 1cf6c39] modify ReadMe
 1 file changed,1insertion(+)

我们可以看到修复bug的内容,并没有在dev2分支上显示,这也很正常,此时的状态图:

image-20241014102713652

这个时候我们需要将dev2分支合并到master分支上去,但是这样又有一个问题,dev2和master的最新的提交是都修改了文件的,所以会有合并冲突,所以我们需要手动的解决冲突,但是,冲突的代码可能不止一行,可能成百上千行,我们手动的去解决冲突,可能会破坏master分支上ReadMe文件中的原本内容。

image-20241014103223415

解决问题的好的建议是。最好在自己的分支上合并master分支,再让master分支去合并dev2分支。就像下面两个图所示:

image-20241014103425317

image-20241014103446547

这里具体例子就不举例了。

显示的结果如示:

image-20241014131050555

9. 删除临时分支

实际开发中,添加一个新的功能时,最好新建一个新的分支,我们把这种分支叫做 feature 分支。

可是,当我们在feature分支上开发到一半了,产品经理告诉我们,该功能取消了,那么你就需要删除该分支。

使用传统的 **

git branch -d

** 删除分支是不行的。

csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git checkout -b dev3
Switched to a new branch 'dev3'
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ vim ReadMe 
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ cat ReadMe 
hello linux
aaaaabbbbb
hello version1
hello version2
write ccc to new branch
write bbb to new branch
fffffffffffffffffffffff
abcdef
I am coding...... Done!
I am write new feature!   #这是新加的功能!
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git add .
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git commit -m "modify ReadMe for new features"[dev3 608acce] modify ReadMe fornew features
 1 file changed,1insertion(+)

这时突然该功能取消了,你需要切换到master分支上,然后删除dev3分支。

csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git checkout master
Switched to branch 'master'
csj@iZuf68h547yeko5rjy13cvZ:~/gitcode$ git branch -d dev3
error: The branch 'dev3' is not fully merged.
If you are sure you want to delete it, run 'git branch -D dev3'.

但是这样删除是不行的。提醒我们使用 git branch -D dev3。

image-20241014132323167

10. 小结

​ 分支在实际中有什么用呢?假设你准备开发⼀个新功能,但是需要两周才能完成,第⼀周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别⼈不能干活了。如果等代码全部写完再⼀次提交,又存在丢失每天进度的巨大风险。

​ 现在有了分支,就不用怕了。你创建了⼀个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再⼀次性合并到原来的分支上,这样,既安全,又不影响别人工作。并且 Git 无论创建、切换和删除分支,Git在1秒钟之内就能完成!无论你的版本库是1个文件还是1万个文件。

标签: git linux

本文转载自: https://blog.csdn.net/adcxhw/article/details/142916234
版权归原作者 我是果子哥 所有, 如有侵权,请联系我们删除。

“git的基本操作 + 分支管理”的评论:

还没有评论