Git的主要功能是代码管理,版本管理,分支管理。
在远程代码管理平台上,项目的代码被管理在一个远程代码仓库中,在本地,代码保存在一个本地代码仓库中。通过Git,本地对代码进行修改后,可以提交到远程代码仓库,当远程的代码被他人修改后,也可以拉取代码到本地。
一、工作区、暂存区和仓库区
将代码从远程代码仓库中拉到本地仓库后,本地仓库的代码与远程仓库的最新代码保持一致。这时候可以在本地仓库中对代码进行修改。修改代码之后,需要按步骤将代码提交并推到远程仓库,要理解这个过程,就要知道工作区、暂存区和仓库区。
当不得不回退/回滚代码时,对工作区、暂存区、仓库区的理解更是重要。
工作区、暂存区、仓库区是虚拟的概念,并不是物理上有三个独立的空间,其实相当于是代码提交的三个阶段。
工作区:开发人员增加、修改、删除代码/文件时,都是在工作区中进行的。工作区有两个状态很重要,修改前(拉取代码后没有做任何修改)和修改后(确定修改内容并保存)。
暂存区:是开发人员完成部分功能后将代码和文件暂存的区域。工作区的代码完成后,必须先经过暂存区才能提交。这个区域的代码会生成版本,不过提交后版本就没有了。
仓库区:是开发人员完成阶段性的功能后将代码提交的区域,这个区域的代码每次提交都有版本号(版本号很重要),可以用于查看版本和回退版本。暂存区的代码,必须要经过仓库区才能推到远程仓库。
二、工作区、暂存区、仓库区和远程仓库的关系
一开始从远程仓库中拉取最新代码,代码保存在本地仓库中,开发人员还没有对代码做任何的修改,所以代码处于工作区未修改状态。
对代码修改、增加、删除后,保存代码,此时代码仍然在工作区,现在处于修改后的状态。
使用 git add . 将修改后的代码添加到暂存区。
使用 git commit -m "提交信息" 将暂存区的代码提交到仓库区,每次提交都有对应的版本号。
使用 git push 将代码从本地仓库区推到远程代码仓库上。
每一个步骤都可以支持回退/回滚。
修改后的代码要回退到修改前,使用 git checkout file。新增加的文件要回到修改前,直接rm file。
暂存区、仓库区、远程仓库的代码回退/回滚都可以使用 git reset 来实现,根据版本号指定回退的版本,HEAD 或 commit-id。
git reset 有三个参数:
--hard 会将代码回退到修改前的状态,就相当于上次拉完代码的状态。所有增加、修改、删除的内容都不在了。(这个操作要慎重)
--mixed 会将代码回退到修改后的状态,可以再次对代码增加、修改、删除,保存,添加到暂存区,提交到仓库区。
--soft 会将代码回退到暂存区,可以继续往前回退,也可以重新提交到仓库区。
如果代码已经推到了远程仓库,reset 回滚后重新推代码时,一定要加 --force 或 -f 参数,才能将本地仓库的版本覆盖远程仓库的代码,避免代码冲突。(这个操作也要慎重,要与团队做好沟通,否则会把其他人的代码也覆盖了)
三、Git代码管理(提交和回退)
本文中,有一个叫 GitProject 的项目代码托管在 Github 上,在 Windows 本地已经配置好了本地代码仓库,这个项目是一个新建的项目,里面没有任何代码(只有一个 README 文件)。
1. 右键打开 Git Bash , 进入本地代码的目录中,为了方便练习 git 的使用,我先创建了一个 git.py 文件,写了几行代码,然后将代码推到了远程仓库,下面的步骤基于这次提交的代码来做修改。
编写代码可以使用任何您喜欢的方式,各种编辑器、IDE都可以,比如用vi写好后wq保存退出。不管用什么方式,代码都是处于工作区,都可以保存代码然后按步骤提交代码。
2. 确认分支和拉取代码
不管任何时候,在开始修改代码之前,都记得先做两步操作。
第一,先看当前处于哪个分支上,当前 GitProject 项目还只有一个分支 master ,所以用的是 master 。
在实际的工作中,代码的分支会非常多,大的项目活跃的分支可能10几个,还有很多不常用的分支。而且,实际工作中是不会直接在mster分支上修改和调试代码的,都是先在其他分支测试通过之后,才会合入到master分支。
所以第一步先看当前分支是不是自己要修改代码的分支,如果不是先切换,否则改了很多代码发现改错分支了,很麻烦。
# 查看当前分支 git branch # 查看所有分支 git branch -a # 切换分支 git checkout master # 如果分支是在远程仓库创建的,可以这样获取 git fetch origin
第二步,git pull 拉取当前最新的代码,如果不是最新的代码,修改代码后向远程仓库提交时,会造成代码冲突。
# 拉取最新代码 git pull
做好以上两步,基本可以避免大部分的代码回退问题和代码冲突问题。
确认好分支是最新代码后,在git.py中进行开发(增加一行代码),然后保存。
3. 修改代码和取消修改
在修改代码前,使用 git status查看当前的状态,代码提示的是 nothing to commit,修改并保存后,git.py 的名字变成了红色,当前 git.py 处于工作区已修改的状态。
如果现在保存的代码不想要了,可以用 git checkout file 使代码回到修改之前,所有修改的内容都不会保存,如果是一个新增加的文件,可以使用 rm file 直接删除文件,回到修改前。
# 查看当前代码状态 git status # 修改后回退到修改前 git checkout git.py
4. 查看修改内容
修改并保存代码后,可以使用 git diff 查看修改的内容。
# 查看修改的代码内容 git diff
5. add添加代码和回退
代码处于工作区修改后的状态,可以使用 git add file 或 git add . 将代码添加到暂存区。git add file 是添加指定的文件,
git add . 是将整个工作区中保存了的内容都添加到暂存区。
# 添加git.py到暂存区 git add git.py # 添加所有已修改代码到暂存区 git add .
代码添加到暂存区后,使用 git status 查看到当前的状态,git.py 的名字变成了绿色,说明当前代码处于暂存区。
如果不想继续提交代码,将代码回退到前面的状态,可以使用 git reset HEAD git.py 回退到工作区修改后的状态。
其中,git reset 是所有操作过程中都可以使用的回退命令,后面接要回退的文件名,不指定文件则回退所有的修改。
HEAD 表示当前的版本,当前版本可以省略,HEAD~ 或 HEAD^ 表示当前版本的上一个版本,后面可以接多个~或^,也可以接数字。如HEAD~~~表示当前版本往前数的第3个版本,HEAD~5表示当前版本往前数的第5个版本。
git reset 后还有三个参数, --soft回退到暂存区,--mixed回退到工作区修改后的状态,--mixed可以省略不写,--hard回退到修改之前的状态。
所以 git reset 、 git reset git.py 、 git reset HEAD git.py 和 git reset --mixed HEAD git.py 一样,都是将 git.py 回退到当前版本的修改后状态。
# 回退代码到工作区修改后 git reset --mixed HEAD git.py
6. commit提交代码和回退
代码处于暂存区时,可以使用 git commit -m "提交信息" 来将代码提交到仓库区。
如果代码处于工作区,也可以使用 git commit -am "提交信息" 将代码从工作区提交到仓库区,相当于 git add 和 git commit 命令合并成一条命令,两步操作合并了。
# 提交代码 git commit -m "add print**"
提交代码后,代码处于待 git push 的状态,现在 git status 已经不显示被修改的 git.py 了。
一旦 git commit ,代码就立即生成一个新的版本号,要回退就是上一个版本了。
如果代码已经 git cmmit 提交,想要回退,可以使用 git reset --soft HEAD~ 来将代码回退到暂存区。可以使用 git reset HEAD~ 或 git reset --mixed HEAD~ 回退到工作区修改之后, 可以使用 git reset --hard HEAD~ 回到工作区修改代码前。
# 回退代码到暂存区 git reset --soft HEAD~
7. push推代码到远程仓库
在本地仓库区的代码,使用 git push origin 分支名 将代码推到远程仓库中,也可以直接 git push ,但建议还是跟上分支名。
# 将代码推到远程仓库 git push origin master
8. 查看远程仓库
到远程仓库查看,现在的 GitProject 项目有三次提交(前两次分别是新建项目,创建git.py文件),第三次提交的信息为 add print**, 正是我们刚才提交过来的代码,说明代码已经被推到了远程仓库。
9. 回退提交到远程仓库的代码
如果代码已经推到了远程仓库,但是发现代码有问题,需要将代码回滚,还是可以使用 git reset 来回滚代码。
回退到的位置依然是根据版本和 --soft --mixed --hard 三个参数来定位,回退的命令与前面的相同。
10. 重新提交回退的代码
现在将代码回退到了暂存区,现在的状态是待提交状态。
也可以回退到工作区进行修改,重新提交代码,然后推代码到远程仓库。
现在远程仓库的代码是第三次提交后的代码,本地代码回退了第三次提交,相当于是第二次提交之后的状态。在第二次提交的基础上重新提交,次数还是第三次。
远程已经有了三次提交,又要推另一个第三次提交,这会造成冲突问题,无法推成功。所以,在推代码的时候要加上 --force 或 -f ,表示将本地回退后重新提交的代码覆盖远程仓库的提交。
# 提交并覆盖远程仓库 git push origin master --force
11. 重新提交后,远程仓库的提交次数还是3次,但是提交信息已经改变了。说明上次提交到远程仓库的代码已经被覆盖了。
上面所有的回退操作中,版本号都可以通过 HEAD 来确定版本的。
每次 commit 提交都会生成一个唯一的 commit-id,要找到指定的版本,也可以使用 git log 或 git reflog 来查看提交过的 commit-id ,然后通过 commit-id 来指定版本。
git reflog 可以查看所有分支的所有操作记录(包括commit和reset的操作),包括已经被删除的commit记录,git log 则不能察看已经删除了的commit记录。
回退命令:
# 回退到指定版本 git reset --hard commit-id
在工作中,更安全更常用的回退代码方式是 Revert ,Revert 操作会生成一个新的 commit-id ,然后将 Revert 操作提交,就回退了代码。如果回退掉的代码又要 Merged 进来,可以再次 Revert,又生成一个新的 commit-id,然后提交。
使用 Revert 进行的每一次操作,都会有对应的 commit-id,并且 Revert 不会对其他人的代码造成影响,所以更适用。