社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Git

Git : 每一行命令都算数

程序员专栏 • 3 年前 • 368 次点击  
👇👇关注后回复 “进群” ,拉你进程序员交流群👇👇


作者丨sowhat1412

来源丨sowhat1412


git思维导图

Git工作区域

为了说明我们日常开发中执行的一系列Git命令的作用是什么,我们需要了解Git的工作区域的概念,几乎每一个常见的Git命令操作都可以通过工作区域来解释。

Git本地有四个工作区域:

  • 工作区(Working Directory):在git init之后的本地的文件目录下,也就是大家写代码的地方

  • 暂存区(Staged/Index):修改了代码之后,需要先将改动add到暂存区,表示将要提交的改动

  • 本地仓库(Local Repository):本地Git仓库,通俗讲就是本地隐藏文件.git目录下,存储着你的所有改动

  • 远程仓库(Remote Repository):远程Git仓库,理论上和本地仓库地位平等,但是主要是用于多个开发者之间pull/push代码的仓库。

四个工作区域

Git 文件状态

接下来我们来看下Git文件的状态:

Git 文件状态
  • UnTracked: 未跟踪,此文件在文件夹中,但并没有加入到git库,不参与版本控制。通过git add 状态变为Staged。

  • UnModify: 文件已经入库,未修改, 即版本库中的文件快照内容与文件夹中完全一致。这种类型的文件有两种去处,如果它被修改,而变为Modified。如果使用git rm移出版本库,则成为UnTracked文件。

  • Modified: 文件已修改,仅仅是修改,并没有进行其他的操作。这个文件也有两个去处,通过git add可进入暂存staged状态,使用git checkout 则丢弃修改过,返回到unmodify状态,这个git checkout即从库中取出文件,覆盖当前修改。

  • Staged: 暂存状态,执行git commit则将修改同步到库中,这时库中的文件和本地文件又变为一致,文件为UnModify状态。执行git reset 取消暂存,文件状态为Modified。

Git 基本命令

在了解Git工作区域、文件状态以及本地仓库的相关信息之后,相信大家对于日常使用的一些命令都有了更加深刻的理解。接下来,我们一起进行一个常用命令总结:

  • git clone:将远程仓库克隆到本地,也就是创建了一个本地仓库,会出现隐藏文件.git

  • git status:查看状态,可以看到哪些文件被修改、哪些是未跟踪的文件

  • git diff:可以看到当前工作区和暂存区staged中的文件diff

  • git add:将未跟踪(新增加)或者修改过的文件从工作区添加到暂存区staged中

  • git commit:将暂存区staged中的内容提交到本地Git仓库中

  • git push:将本地Git仓库中的内容提交到远程Git仓库中

并且如果是单人开发,自己玩的情况下,貌似这些命令就足够了。但是,在实际的开发当中,我们往往会面对更加复杂的场景,需要一些更为复杂的命令来处理,我们接着往下看。

git merge

在当前分支上执行git merge master可以将master的提交合并merge到当前分支,也就是更新本地分支。我们日常开发中,将本地代码推到远程仓库,建立Merge Request,然后点击Merge按钮其实就是在master分支上merge了开发分支。

merge
git fetch

git fetch可以将远程分支拉到本地:

git fetch 会将所有远程分支都拉到本地
git fetch origin sowhat1412  指定拉取远程origin仓库的sowhat1412分支到本地
git pull

当我们想要更新本地分支代码的时候,需要将远程开发分支或者远程master分支代码拉到本地,并且合并到当前开发分支上。所以git pull = git fetch + git merge
在当前开发分sowhat1412上,我们执行如下的命令:

git pull origin master 
表示将远程master分支拉到本地并且merge到当前分支sowhat1412上
git rebase

rebase会把你当前分支的 commit 放到公共分支的最后面,所以叫变基。就好像你从公共分支又重新拉出来这个分支一样。
举例:如果你从 master 拉了个feature分支出来,然后你提交了几个 commit,这个时候刚好有人把他开发的东西合并到 master 了,这个时候 master 就比你拉分支的时候多了几个 commit,如果这个时候你 rebase master 的话,就会把你当前的几个 commit,放到那个人 commit 的后面。

git rebeae

merge 会把公共分支和你当前的commit 合并在一起,形成一个新的 commit 提交

git merge
git cherry-pick

对于多分支的代码库,将代码从一个分支转移到另一个分支是常见需求。

这时分两种情况。一种情况是,你需要另一个分支的所有代码变动,那么就采用合并(git merge)。另一种情况是,你只需要部分代码变动(某几个提交),这时可以采用 Cherry pick。

git cherry-pick命令的作用,就是将指定的提交(commit)应用于其他分支。

$ git cherry-pick <commitHash>

上面命令就会将指定的提交commitHash,应用于当前分支。这会在当前分支产生一个新的提交,当然它们的哈希值会不一样。

举例来说,代码仓库有master和feature两个分支。

 a - b - c - d       Master
         \
           e - f - g Feature

现在将提交f应用到master分支。




    
# 切换到 master 分支
$ git checkout master

# Cherry pick 操作
$ git cherry-pick f

上面的操作完成以后,代码库就变成了下面的样子。

    a - b - c - d - f  Master
         \
           e - f - g   Feature

从上面可以看到,master分支的末尾增加了一个提交f。

git cherry-pick命令的参数,不一定是提交的哈希值,分支名也是可以的,表示转移该分支的最新提交。

$ git cherry-pick feature
上面代码表示将feature分支的最近一次提交,转移到当前分支。

Cherry pick 支持一次转移多个提交。

$ git cherry-pick <HashA> <HashB>

上面的命令将 A 和 B 两个提交应用到当前分支。这会在当前分支生成两个对应的新提交。

如果想要转移一系列的连续提交,可以使用下面的简便语法。

$ git cherry-pick A..B 

上面的命令可以转移从 A 到 B 的所有提交。它们必须按照正确的顺序放置:提交 A 必须早于提交 B,否则命令将失败,但不会报错。

注意,使用上面的命令,提交 A 将不会包含在 Cherry pick 中。如果要包含提交 A,可以使用下面的语法。

$ git cherry-pick A^..B 
git reset
  • git reset commitId

该命令执行之后,HEAD指针会移动到选中commitId上,并且之前的HEAD ->commitId之间的所有修改的内容会被置于工作区,需要重新add、commit。
  • git reset --soft commitId

该命令执行之后,HEAD指针会移动到选中commitId上,并且之前的HEAD ->commitId之间的所有修改的内容会被直接置于暂存区staged中,也就是后续只需要执行commit操作就OK了
  • git reset --hard commitId

这个--hard很好理解,就是在回滚HEAD指针的时候,很强硬的将所有HEAD ->commitId之间的改动内容“全部删除”!
为什么加引号?因为前边我们说了Git不会丢失任何你提交过的内容(只要你玩的溜),后续我们会分析原因。
git reflog
  1. 新建一个目录git-test,初始化一下仓库,做一次简单的提交

mkdir git-test  
cd git-test/  
git init  
echo 0 > test  
git add .  
git commit -am "init"  
  1. 假设这几个星期你做了三次重要代码的提交:

linux-geek:/home/tmp/git-test # echo "important code0" >> test  
linux-geek:/home/tmp/git-test # git commit -am "important code0"  
[master 03f85a1] important code0  
 1 files changed, 1 insertions(+), 0 deletions(-)  
linux-geek:/home/tmp/git-test # echo "important code1" >> test     
linux-geek:/home/tmp/git-test # git commit -am "important code1"  
[master 83547ba] important code1  
 1 files changed, 1 insertions(+), 0 deletions(-)  
linux-geek:/home/tmp/git-test # echo "important code2" >> test     
linux-geek:/home/tmp/git-test # git commit -am "important code2"  
[master b826ae5] important code2  
 1 files changed, 1 insertions(+), 0 deletions(-)   
linux-geek:/home/tmp/git-test # cat test  
0  
important code0  
important code1  
important code2  
  1. 就在这个时候,假设你头脑突然短路,用了git reset这条命令:

linux-geek:/home/tmp/git-test # git reset --hard HEAD~3  
HEAD is now at 2e71cf6 init 

再来看一下代码的情况:

linux-geek:/home/tmp/git-test # cat test

有没有什么办法补救呢?答案是肯定的!

  1. 用git reflog来看一下

linux-geek:/home/tmp/git-test # git reflog
2e71cf6 HEAD@{0}: HEAD~3: updating HEAD
b826ae5 HEAD@{1}: commit: important code2
83547ba HEAD@{2}: commit: important code1
03f85a1 HEAD@ {3}: commit: important code0
2e71cf6 HEAD@{4}: commit (initial): init  

其中,我们可以清楚地看到HEAD每次移动的情况以及哈希值,于是乎我们再采用git reset去试一下:

linux-geek:/home/tmp/git-test # git reset --hard HEAD@{1}
HEAD is now at b826ae5 important code2  
  1. 我们再来看一下代码的情况:

linux-geek:/home/tmp/git-test # cat test
0
important code0
important code1
important code2 
  1. 此时用 git log 或 git log --pretty=oneline 可以看到全部的历史记录又恢复回来了。

git revert

revert 是回滚某个 commit ,不是回滚到某个。git revert是用于反做某一个版本,以达到撤销该版本的修改的目的。比如,我们commit了三个版本(版本一、版本二、 版本三),突然发现版本二不行(如:有bug),想要撤销版本二,但又不想影响撤销版本三的提交,就可以用 git revert 命令来反做版本二,生成新的版本四,这个版本四里会保留版本三的东西,但撤销了版本二的东西。如下图所示:

在这里插入图片描述
  1. 反做,使用“git revert -n 版本号”命令。如下命令,我们反做版本号为8b89621的版本:

git revert -n 8b89621019c9adc6fc4d242cd41daeb13aeb9861
  1. 这里可能会出现冲突,那么需要手动修改冲突的文件。而且要git add 文件名来提交

git commit -m "revert add text.txt"

手动整理commit

有这样一个需求,代码量不多,但是因为你多次提交,会导致在建立Merge Request的时候,出现了几十个commitId,比如:“update”、“bug fix”、“fix again”等,不光看起来很丑,也会给大家一种感觉,这小伙到底行不行呀?一百行代码的开发量,提交了这么多次才搞定。(尴尬.jpg)
为了解决这样的问题,我们可以巧妙的利用git reset。比如当前的commit是这样的A-1-2-3-4-5-6-7-8,你的第一个提交是1,那么我们执行如下的命令:

git reset --soft A // 重置本地分支HEAD指针
git commit -m "XXX逻辑开发"
git push origin ywq_news_0702 -f // 提交到远程分支

-f是什么操作?force的含义,表示强行执行本次操作。当我们git reset之后,本地的HEAD指针指向的commitId会比远程origin对应的落后,直接push会被拒绝。通过-f命令可以强行将本地内容push到远程分支上(切记!如果是多人共同合作的开发分支或者远程master操作,千万不能加-f操作!!!)
经过git reset --soft之后,我们提的Merge Request里就是一个commitId了,发出来的CR会感觉倍儿有面儿。

Git stash 临时储藏

当我们在当前分支开发某个需求的时候,遇到了另一个需求的联调问题,需要切换到另一个分支上去解决问题。怎么办?
正常情况下,我们应该将当前分支工作区的内容add 、commit之后再切换分支。但是问题来了,当前需求开发了一半,我不想生成一次提交怎么办?
放心,这个时候我们的git stash命令可以帮助我们将当前工作区的内容储藏起来。然后切换其他分支,处理完问题之后,再切换到当前分支,执行git stash pop取出来就完事。




    
git stash list // 查看当前stash里边的内容
git stash // 将当前工作区内容储藏起来
git stash pop // 将stash中栈顶内容pop出来,当然也可以根据顺序直接取第n个

Git 恢复修改文件

对于恢复修改的文件,就是将文件从仓库中拉到本地工作区,即 仓库区 ----> 暂存区 ----> 工作区。

对于修改的文件有两种情况:

  • 只是修改了文件,没有任何 git 操作

只是修改了文件,没有任何 git 操作,直接一个命令就可回退:
$ git checkout -- aaa.txt # aaa.txt为文件名
  • 修改了文件,并提交到暂存区)

即编辑之后,git add 但没有 git commit -m ....
$ git log --oneline    # 可以省略
$ git reset HEAD    # 回退到当前版本
$ git checkout -- aaa.txt    # aaa.txt为文件名
  • 修改了文件,并提交到仓库区

即编辑之后,git add 和 git commit -m ....
$ git log --oneline    # 可以省略
$ git reset HEAD^    # 回退到上一个版本
$ git checkout -- aaa.txt    # aaa.txt为文件名

参考

  • Git十大硬图:https://zhuanlan.zhihu.com/p/132573100

  • Git分支规范:https://zhuanlan.zhihu.com/p/108385922

  • Git思维导图:https://www.jianshu.com/p/e2f553942317

-End-

最近有一些小伙伴,让我帮忙找一些 面试题 资料,于是我翻遍了收藏的 5T 资料后,汇总整理出来,可以说是程序员面试必备!所有资料都整理到网盘了,欢迎下载!

点击👆卡片,关注后回复【面试题】即可获取

在看点这里好文分享给更多人↓↓

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/122396
 
368 次点击