一、基本操作
1. 修改文件
Git比其他的版本管理器设计得更加优秀,因为
Git追踪并管理的是修改,而非文件
。
修改一个文件,不管你是添加一行,或者删除一行,还是添加了又删除了,甚至你创建了一个新文件,只要和源文件有所不同的,这些都算是一个修改。
现在我们来对ReadMe文件做一下修改:
修改前:
修改后:
目前仓库的ReadMe和工作区的ReadMe是不同的。使用
git status
命令可以查看文件是否被修改。
很清楚,红色的字体就可以看出来,文件是被修改了的。工作区的ReadMe文件被修改了,还没有进行add和commit操作。
目前我们只看见了文件被修改了,但是不知道具体文件的哪一个位置被修改了,用一个命令可以查看具体修改内容的位置。
**
git diff [file]
**:显示工作区和暂存区文件的差异。
**
git diff HEAD -- [file]
**:显示暂存区和版本库文件的差异。
git add 之后,就没有看到上面 no changes added to commit (use “git add”
and/or “git commit -a”) 的消息了。接下来让我们继续 git commit 即可:
这时所有的修改文件已经被提交到了版本库了。
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:
版本2:
版本3:
最后提交的是版本3,某些原因,我们需要回退到版本2,重新开始工作,由于需要将工作区的ReadMe文件回退了,所以需要
--hard
选项。
这里的回退我们使用
commit id
来做,于是需要
git log --pretty=oneline
,来查看最近几次的 commit id。
如我们所料,当前的ReadMe文件已经回退到了版本2,当前我们使用git log来查看一下提交日志,可以发现HEAD指向了版本2。
到这里我们的回退工作就算是完成了,但是我后悔了,我想再回到版本3怎么办?我们可以继续使用git reset,但是我们目前git log打印日志,没法知道版本3的commit id。
Git提供了一个
git reflog
命令来补救一下,该命令用来记录本地的每一次命令。
注意这里的黄色的内容是commit id的部分,通过这个id可以进行文件操作。
值得注意的是,在实际的开发中,由于长时间的开发,导致commit id早就找不到了,某一天你先回退到version3版本,那如何操作呢?貌似是不可能了!!!
值得说的是,Git 的版本回退速度非常快,因为 Git 在内部有个指向当前分支(此处是master)的HEAD 指针,
refs/heads/master
文件里保存当前 master 分支的最新 commit id 。当我们在回退版本的时候,Git 仅仅是给 refs/heads/master 中存储⼀个特定的commit id,可以简单理解成如下⽰意图:
3. 撤销修改
3.1 情况一:对于工作区的代码,还没有add
当你对工作区中的文件写了几天的代码,但是你一直没有进行add操作,这时你觉得这几天写的代码不行,你需要重新写,因为你写了很多代码了,你也记不住写了哪些代码,当然也不可能一行一行的在源文件中去删除。
Git给我们提供了好的方式 – **
git checkout -- [file]
**,让文件回到最近一次add或commit的状态。该命令中的–是很重要的,千万不能少,这是啥意思呢?后面说。
示例:
3.2 情况二:已经add,但没有commit
对于此状态下,我们可以使用git reset中的–mixed选项,回退暂存区,而工作区不变。
在工作区中的文件新增了三行内容,然后进行add操作。我们的目的是撤销add操作,使得暂存区回到上一次操作。
可以发现,暂存区是干净的,而工作区有修改。
你可以再次使用 git checkout – ReadMe 命令,让工作区的文件回到上一次add的状态。
3.3 情况三:已经add,并且已经commit了
这个简单,就是我们之前提到的
git reset --hard HEAD^
回退到上一个版本。
不过!这是建立在你还没有将本地版本库提交到远程版本库(后续会说到),如果已经提交到了本地版本库,那么就无力回天了。
4. 删除文件
在git中删除文件也是一种修改操作。
在工作区使用rm可以删除文件吗?
此时,git status会告诉你,哪些文件被删除了,但是这里的只是删除了工作区中的文件,版本库中的文件可没有删除掉。
走到这里:
①不小心删错了。
②确实要从版本库中删除该文件。
对于第一种情况,删错了,**
git checkout -- file1
** 就可以恢复了。
对于第二种情况,显示是没有删除干净的,git中有一个命令提供给我们删除工作区和版本库中的文件:**
git rm [file]
**
并且执行commit:
二、分支管理
1. 理解分支
在版本回退里,你已经知道,每次提交,Git都把它们串成⼀条时间线,这条时间线就可以理解为是⼀个分支。截止到目前,只有⼀条时间线,在Git,这个分支叫主分支,即 master 分支。
再来理解⼀下HEAD,HEAD 严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD 指向的就是当前分支。
每次提交。master分支都会向前移动一步,随着你提交的越来越多,master分支也越来越长,但是HEAD是一直指向的master分支的。所以当前分支一直是master分支。
2. 创建分支
Git支持我们创建和查看分支。
**
git branch
**:查看当前本地的所有分支。
**
git branch dev
**:创建dev的分支。
很明显,当前有两个分支,并且两个分支都指向的同一个修改。但是HEAD还是执行master分支的。
下面的图就能清晰的看出它们的关系:
3. 切换分支
当我们创建出了dev分支,需要转换到dev分支上进行开发,如何做到呢?
**
git checkout dev
可以切换分支。还有一个命令可以直接创建并切换分支,一步到位:
git checkout -b dev
**
切换到了dev分支了之后,我们就可以在该分支下进行工作了。
hello linux
aaaaabbbbb
hello version1
hello version2
write aaa to new branch #这是新增的内容
现在,dev分支工作完成了,我们切换到master分支上去。
切回到master分支上了之后,发现ReadMe上新增的内容不见了。赶紧切回到dev看看。
在dev分支上内容是还在的,为什么会出现这种情况呢?我们来看看dev和master的指向就知道了
可以看到它们两个的指向是不一样的,到这里就理解了,我们是在dev分支上提交的,但是master分支上此刻的提交点并没有改变。
我们就可以得到下面的图:
当切换到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分支下,也能看到新的提交了。
示意图就变成了这样:
Fast-forward 代表“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。
当然,也不是每次合并都能 Fast-forward,我们后面会讲其他⽅式的合并。
5. 删除分支
当前dev分支合并完成后,对我们来说就没啥用了,于是我们就可以删除该分支了,注意,当前处于dev分支下,是不可以删除dev分支的。
删除分支命令:**
git brach -d dev
**
因为创建、合并和删除分支非常快,所以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(-)
此时的结构图如示:
- 最后一步,将dev1分支合并到master分支上。
执行git merge dev1合并命令的时候,出现了很明显的字样,合并冲突。为什么会出现这种情况呢?
因为两个分支修改了同一个文件,git不知道要管理哪个了。所以这时候,就需要我们手动解决冲突。
解决冲突:
当我们查看ReadMe文件,会看到这些内容:
多了三行我们看不懂的内容,<<<<<<< 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
此时的状态就变成了:
其实git给我们提供了带参数的git log命令可以查看分支合并情况。
这里使用 **
git log --graph --pretty=oneline --abbrev-commit
**
最后记得将dev1分支给删除掉。
7. 分支管理策略
通常合并分支的时候,如果可能,Git会采用Fast forward模式,我们第一次合并分支的时候,就是该模式,会出现下面的情况。
在这种 Fast forward 模式下,删除分支后,查看分支历史时,会丢掉分支信息,看不出来最新提交到底是 merge 进来的还是正常提交的。
删除了dev分支后,就会出现下面的情况:
dev分支提交的信息就会消失,我们就看不出来最新的提交是master自己提交的,还是其他分支合并提交的。
在我们上述解决合并冲突的例子中看出该模式就不是Fast forward模式了。
即使你删除了dev分支,依然可以看到master的最新一次提交是其他分支合并得到的。
接下来我们可以实操一下,很简单,我们先创建并切换到dev2分支,在dev2分支上修改ReadMe文件,并进行add和commit操作,然后再切换到master分支上,将dev2分支合并到master上。只不过这里的合并命令和之前的不一样,需要添加选项 **
--no-ff
,完整的命令:
git merge --no-ff -m "merge with no-ff" dev2
**
禁用 Fast forward 模式后,会创建⼀个新的 commit ,所以加上 -m 参数,把描述写进去。
合并之后应当变成这样:
即使你删除了dev2分支后,依然知道这是分支合并提交的。
7.1 分支策略
在实际开发中,master分支是非常稳定的,仅仅用来发布新的版本,不能在上面进行开发。
我们开发都是在其他分支上进行开发,做完活之后,测试也通过了之后,再将这些分支合并到主分支上去。
所以,团队合作的分支就像这样:
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文件。
该stash文件就是存放的dev2工作的内容。为了完成dev2的工作,我们需要将stash中的内容恢复出来。
恢复命令:**
git stash pop
**,恢复内容的同时,也会将stash文件给删除了。
你也可以使用 **
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分支上显示,这也很正常,此时的状态图:
这个时候我们需要将dev2分支合并到master分支上去,但是这样又有一个问题,dev2和master的最新的提交是都修改了文件的,所以会有合并冲突,所以我们需要手动的解决冲突,但是,冲突的代码可能不止一行,可能成百上千行,我们手动的去解决冲突,可能会破坏master分支上ReadMe文件中的原本内容。
解决问题的好的建议是。最好在自己的分支上合并master分支,再让master分支去合并dev2分支。就像下面两个图所示:
这里具体例子就不举例了。
显示的结果如示:
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。
10. 小结
分支在实际中有什么用呢?假设你准备开发⼀个新功能,但是需要两周才能完成,第⼀周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别⼈不能干活了。如果等代码全部写完再⼀次提交,又存在丢失每天进度的巨大风险。
现在有了分支,就不用怕了。你创建了⼀个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再⼀次性合并到原来的分支上,这样,既安全,又不影响别人工作。并且 Git 无论创建、切换和删除分支,Git在1秒钟之内就能完成!无论你的版本库是1个文件还是1万个文件。
版权归原作者 我是果子哥 所有, 如有侵权,请联系我们删除。