赞
踩
Git 是一个很强大的分布式版本管理工具,最初由Linus Torvalds编写,主要用于 Linux 内核开发的版本控制工具。
学习Git首先要明白Git的分层结构,下面一张图基本就说明各层之间的关系。
Git的工作总共分四层,其中三层是在自己本地也就是前面说的git仓库,包括了工作目录,暂存区和本地仓库。
**工作区(Workspace)**就是我们执行命令git init时所在的地方,也就是我们执行一切文件操作的地方。随着修改,工作区文件在没有 add 到暂存区时候,工作区将和暂存区是不一致的。当执行add操作后,工作区和暂存区就是一致的了。
**暂存区(Index)**就是文件暂时存放的地方,所有暂时存放在暂存区中的文件将随着一个 commit 一起提交到Local Repository此时Local Repository里面文件将完全被暂存区所取代。暂存区是 git 架构设计中非常重要和难理解的一部分。
**本地仓库(Local Repository)**就是当前用户的更新代码后所存的地方,这点和其他版本控制系统不同,git 是分布式版本控制系统,是完全去中心化工作,你可以不用和中央服务器 (Remote Server) 进行通信,在本地即可进行全部离线操作,包括 log,history,commit,diff 等等。完成离线操作最核心是因为 git 有一个几乎和远程一样的本地仓库,所有本地离线操作都可以在本地完成,等需要的时候再和远程服务进行交互。
**远程仓库(Remote Repository)**也就是中心服务器,也就是我们做好工作之后推送到远程仓库,或者从远程仓库更新下来最新代码到我们的git仓库。
Git所存储的都是一系列的文件快照,然后git来跟踪这些文件快照,发现哪个文件快照有变化它就会提示你需要添加到暂存区或是提交到本地仓库来保证你的工作目录是干净的。因此只要明白他们之间逻辑关系,然后学习基本命令,我相信你很快就会上手Git。
关于Git更详细的介绍网上很多,这里不在赘述。
笔者是一个实用主义者,写这个文章的目的也就是为了让更多朋友尽快入门,尽可能学会各种命令,用于工作中。下面是笔者根据自己的工作经验,总结了一些常用Git命令。
git clone
git status
git add
git commit
git push
git pull
git log
git reset
git revert
git tag
git branch
git checkout
git merge
git stash
git clean
接下来就针对这些命令一一讲解。在讲解之前,我们需要搭建好Git服务器或者使用Github。关于Git服务器的搭建请参看笔者博客:
持续集成环境搭建Jenkins+Gitlab+Gerrit
Gitlab基本配置与使用
关于Github的基本使用请参看笔者博客:
Git和Github的使用
[PS]笔者本文将使用Gitlab作为远程服务器,当然也可使用Github,操作都是类似的。
笔者假设你已经创建一个远程仓库,笔者在服务器上新建一个项目。
[PS]值得注意的是,在接下来的操作之前,需要配置SSH密钥。笔者在前面的文章已经讲过了,就不在赘述了。
新建之后,在本地就可拉取远程代码了。
这里有两种方式:
笔者选择的是HTTP方式。
$ git clone < url >
这会在当前目录下创建一个名为 “demo” 的目录,并在这个目录下初始化一个 .git 文件夹, 从远程仓库拉取下所有数据放入 .git 文件夹,当然这个文件夹一般隐藏的,然后从中读取最新版本的文件的拷贝。
如果你进入到这个新建的demo文件夹,你会发现所有的项目文件已经在里面了,准备就绪等待后续的开发和使用。
如果你想在克隆远程仓库的时候,自定义本地仓库的名字,你可以通过额外的参数指定新的目录名:
$ git clone < url > < new name >
Git 支持多种数据传输协议。上面的例子使用的是 https:// 协议,不过你也可以使用 git:// 协议或者使用SSH 传输协议,比如 user@server:path/to/repo.git 。
另外还介绍一个常用的命令。
$ git pull
git pull 命令用于从远程获取代码并合并本地的版本。git pull 其实就是 git fetch 和 git merge FETCH_HEAD 的简写。当用户A将本地代码推送到远程仓库后,用户B就可以使用git pull命令拉取远程仓库最新的代码,保持本地和远程同步。
拉取远程仓库之后接下来就是修改代码,并提交代码,这里推荐VS Code编写代码,关于
关于如何使用VS Code进行嵌入式开发,请参看笔者博客:
Windows版:
https://bruceou.blog.csdn.net/article/details/119976818
https://bruceou.blog.csdn.net/article/details/119986310
https://bruceou.blog.csdn.net/article/details/120092498
Linux版:
https://bruceou.blog.csdn.net/article/details/120007156
https://bruceou.blog.csdn.net/article/details/120027933
https://bruceou.blog.csdn.net/article/details/120094588
接下来笔者将一个STM32的工程拷贝到刚才拉取的项目文件夹下。
然后打开VS Code,你会发现,VS code将会自动检测出代码差异,将有修改的代码与文件给你举出来。
值得注意的是,只要你的电脑安装了Git,在VS Code的终端就能找到,和使用右键打开Git是一样的。
前面笔者新增了一个文件夹,VS Code自动检查当前工程目录的状态,主要使用的是git status 命令查看哪些文件处于什么状态。
$ git status
可以看到,在状态报告中可以看到新增的stm32f103ze文件夹出现在 Untracked files 下面。 未跟踪的文件意味着 Git 在之前的快照(提交)中没有这些文件,也就告诉用户是否需要管理新增的这里文件,并给出了相应的参考指令。
笔者这里还需要说明下Git工作目录下的工作状态。
在Git工作目录下的每一个文件都不外乎这两种状态:已跟踪(Tracked)或未跟踪(Untracked)。 已跟踪的文件是指那些被纳入了版本控制的文件,在上一次快照中有它们的记录,在工作一段时间后,它们的状态可能是未修改(unmodified),已修改(modified)或已放入暂存区(staged)。简而言之,已跟踪的文件就是 Git 已经知道的文件。
工作目录中除已跟踪文件外的其它所有文件都属于未跟踪文件,它们既不存在于上次快照的记录中,也没有被放入暂存区。
初次克隆某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修改状态,因为 Git刚刚检出了它们,而你尚未编辑过它们。
编辑过某些文件之后,由于自上次提交后你对它们做了修改,Git 将它们标记为已修改文件。 在工作时,你可以选择性地将这些修改过的文件放入暂存区,然后提交所有已暂存的修改,如此反复。
好了,接下来就根据Git提示将文件添加到暂存区。
可用以下命令:
$ git add . //所有文件都更新到暂存区
或
$ git add --A[–all / --no-ignore-removal] //同上,可以使用–A, --all, --no-ignore-removal任意一个参数
当然也可之更新某个文件。
$ git add < file >
接下来在查看工作目录的文件状态。
可以看到所新增的文件都在 “Changes to be committed” 下面,就说明是已暂存状态。
这时如果修改main.c文件,我们看看文件的状态是什么。
文件main.c出现在 “Changes not staged for commit” 下面,说明已跟踪文件的内容发生了变化,但还没有放到暂存区。要暂存这次更新,需要运行 git add 命令,这里就不在演示了。
现在的暂存区已经准备就绪,可以提交了。
值得注意的是,在提交前,先用 git status 看下,你所需要的文件是不是都已暂存起来了,然后再运行提交命令。
$ git commit -m < commit info >
这样就将工作区的代码提交到了本地仓库,提交后它会告诉你,当前是在哪个分支(master)提交的,本次提交的完整 SHA-1 校验和是什么(463dc4f),以及在本次提交中,有多少文件修订过,多少行添加和删改过。
如果是使用VS Code,你还会发现代码管理器没有任何更改信息了。
前面将工作区的代码同步到了本地仓库。这里就可以使用git log来查看历史提交信息。
$ git log
也查看工作目录的文件状态。
可以看到所有的文件都已经提交到本地仓库,但是并没有更新到远程仓库,接下来就讲本地仓库的代码同步到远程仓库。
$ git push -u origin main
再来看看提交的历史信息。
细心的朋友发现和同步到远程仓库前的差别了吗?远程仓库的HEAD指针和本地仓库的HEAD指针都指向最新的提交,也就说明本地和远程仓库是同步的。
将本地仓库的代码同步到远程仓库后,可在服务器端查看更新的代码。
在查看历史提交信息,笔者再介绍一个非常有用的选项是 --pretty。 这个选项可以使用不同于默认格式的方式展示提交历史。 这个选项有一些内建的子选项供你使用。 比如 oneline 会将每个提交放在一行显示,在浏览大量的提交时非常有用。
$ git log --pretty=oneline
当然啦,还可使用GIt GUI查看,非常的直观。
当然啦,还有其他的GUI,可参看笔者博文:
我相信很多朋友在操作过程中都有可能想要撤消某些操作。
有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。此时,可以运行git commit --amend 命令来重新提交。
假设在操作之前的log信息如下:
然后在最新的提交记录上追加提交,就使用以下命令。
$ git commit --amend
这个命令会将暂存区中的文件重新提交到本地仓库。
就会提示是否修改commit信息,笔者这里不修改。
当你在修补最后的提交时,与其说是修复旧提交,倒不如说是完全用一个“新的提交”替换旧的提交,理解这一点非常重要。从效果上来说,就像是旧有的提交从未存在过一样,它并不会出现在仓库的历史中。
接下来我们在查看下log信息。
可以发现最新的commit信息被替换了,感觉旧有的提交从未存在过一样。
下面再介绍两个回退命令:reset和revert。
先介绍reset,reset的作用是修改HEAD的位置,即将HEAD指向的位置改变为之前存在的某个版本,当然的版本信息如下:
$ git reset --hard HEAD
表示回退到当前版本,HEAD指向当前版本。如果你修改了一些代码,想去除,就可以用上述命令一次性去除。
$ git reset --hard HEAD^
表示回退到上一个版本。
$ git reset --hard < commit >
该命令修改HEAD的位置,即将HEAD指向的位置改变为之前存在的某个版本,–hard参数将将重置HEAD返回到另外一个commit,重置index以便反映HEAD的变化,并且重置工作区也使得其完全匹配起来。也就是将工作区(Workspace)、暂存区(Index)和本地仓库(Local Repository)全部使用指定的commit替换,不会保留任何指定commit后的提交。
这是一个比较危险的动作,具有破坏性,数据因此可能会丢失!如果真是发生了数据丢失又希望找回来,那么只有使用:git reflog命令了。
$ git reset --mixed < commit >
–mixed是reset的默认参数,也就是当你不指定任何参数时的参数。它将重置HEAD到另外一个commit,并且重置Index以便和HEAD相匹配,但工作区不会被更改。所有该Branch上从HEAD(commit)到你重置到的那个commit之间的所有变更(未git add的操作),你可以接者写代码,之后再进行git add,git commit。也就是将暂存区和本地仓库全部使用指定的commit替换,保留工作区修改的代码。
$ git reset --soft < commit >
–soft参数Git重置HEAD到另外一个commit,HEAD位置改变,但工作区和暂存区都不会做任何变化,所有的在HEAD和你重置到的那个commit之间的所有变更集都放在Stage(index)区域中。也就是将本地仓库使用指定的commit替换而已。
下面演示下使用–soft参数。
回退到上一个版本后,可看到本地仓库的信息已经发生变化。
然后查看本地目录的文件状态。
上述信息表明工作区和暂存区的信息没有改变。
不管使用上述任何参数,HEAD指针将向指定回退的版本处,新的版本信息如下:
接下来将介绍另外一个命令revert。
与reset不同的是,reset是HEAD指针跳到目标版本,但将跳过的版本丢弃掉了。revert是复制了一个目标版本(某个想要回退到的历史版本)加在当前分支的最前端。
当前的版本信息如下:
接下来使用revert操作。
$ git revert 6c62842364b5ff1440915f0b2b039e2697c6114a
使用revert操作后,当前的版本信息如下:
使用revert操作后,当前的代码和c1c596c所对应代码是一样的。
值得注意的是,如果已经将本地仓库推送到远程,执行撤销操作之后,需要进行强推。
$ git push -f origin main
最后总结下reset和revert操作。
如果本地仓库已经推送到远端仓库,其他人可能已经提取代码并开始使用,则revert是一种更友好的方式,因为当使用reset 后,会导致一些分支再也看不见了,而对于revert,所有的版本信息都在,也便于问题的追溯。因此在使用Git时最好遵循以下原则:在本地存储库中进行这些类型的更改,reset、revert都没关系。但是,如果提交已经被推送到远程仓库,最好不要做影响commit历史纪录的更改,尽可能不用reset。
1.列出标签
在 Git 中列出已有的标签非常简单,只需要输入 git tag (可带上可选的 -l 选项 --list):
$ git tag
这个命令以字母顺序列出标签,但是并不是按照标签的时间先后顺序的。
你也可以按照特定的模式查找标签。 例如,Git 自身的源代码仓库包含标签的数量超过 500 个。 如果只对 1.0系列感兴趣,可以运行:
$ git tag -l “V1.0*”
2.创建标签
Git 支持两种标签:轻量标签(lightweight)与附注标签(annotated)。
轻量标签很像一个不会改变的分支——它只是某个特定提交的引用。
而附注标签是存储在 Git 数据库中的一个完整对象,它们是可以被校验的,其中包含打标签者的名字、电子邮件地址、日期时间, 此外还有一个标签信息,并且可以使用 GNU Privacy Guard (GPG)签名并验证。 通常会建议创建附注标签,这样你可以拥有以上所有信息。但是如果你只是想用一个临时的标签, 或者因为某些原因不想要保存这些信息,那么也可以用轻量标签。
附注标签
在 Git 中创建附注标签十分简单。 最简单的方式是当你在运行 tag 命令时指定 -a 选项:
$ git tag -a V1.0.0 -m “V1.0.0_20220227”
$ git tag
-m 选项指定了一条将会存储在标签中的信息。 如果没有为附注标签指定一条信息,Git 会启动编辑器要求你输入信息。
通过使用 git show 命令可以看到标签信息和与之对应的提交信息:
$ git show V1.0.0
输出显示了打标签者的信息、打标签的日期时间、附注信息,然后显示具体的提交信息。
轻量标签
另一种给提交打标签的方式是使用轻量标签。 轻量标签本质上是将提交校验和存储到一个文件中——没有保存任何其他信息。 创建轻量标签,不需要使用 -a、-s 或 -m 选项,只需要提供标签名字:
$ git tag V1.0.0-lw
$ git tag
这时,如果在标签上运行 git show,你不会看到额外的标签信息。 命令只会显示出提交信息:
$ git show V1.0.0-lw
3.后期打标签
你也可以对过去的提交打标签。 假设提交历史是这样的:
$ git log --pretty=oneline
现在,假设在V1.0.1时你忘记给项目打标签,你需要在命令的末尾指定提交的校验和(或部分校验和):
$ git tag -a V1.0.1 -m “V1.0.1_20220227” 47b7891
$ git show V1.0.1
可以看到你已经在那次提交上打上标签了。
4.推送标签
默认情况下,git push 命令并不会传送标签到远程仓库服务器上。 在创建完标签后你必须显式地推送标签到共享服务器上。 这个过程就像共享远程分支一样——你可以运行 git push origin 。
$ git push origin V1.0.0
如果想要一次性推送很多标签,也可以使用带有 --tags 选项的 git push 命令。 这将会把所有不在远程仓库服务器上的标签全部传送到那里。
$ git push origin –tags
现在,当其他人从仓库中克隆或拉取,他们也能得到你的那些标签。
【notes】git push 推送两种标签
使用 git push --tags 推送标签并不会区分轻量标签和附注标签, 没有简单的选项能够让你只选择推送一种标签。
5.删除标签
要删除掉你本地仓库上的标签,可以使用命令 git tag -d 。 例如,可以使用以下命令删除一个轻量标签:
$ git tag -d V1.0.0-lw
注意上述命令并不会从任何远程仓库中移除这个标签,你必须用 git push < remote > :refs/tags/< tagname > 来更新你的远程仓库:
第一种变体是 git push < remote > :refs/tags/< tagname > :
$ git push origin :refs/tags/V1.0.0-lw
上面这种操作的含义是,将冒号前面的空值推送到远程标签名,从而高效地删除它。
第二种更直观的删除远程标签的方式是:
$ git push origin --delete < tagname >
几乎每一种版本控制系统都以某种形式支持分支管理。使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续开发工作。
Git的一大特色就是可以灵活的建立分支,任意切换分支,因为有分支的存在,才构成了多工作流的特色。因为项目开发中,多人协作,分支很多,虽然各自在分支上互不干扰,但是我们总归需要把分支合并到一起,而且真实项目中涉及到很多问题,例如版本迭代,版本发布,Bug 修复等。
你可以在不同分支间不断地来回切换和工作,并在时机成熟时将它们合并起来。而所有这些工作,你需要的命令有branch、checkout 和 merge。
Git的分支其实本质上仅仅是指向提交对象的可变指针。Git 的默认分支名字是main(以前是master)。 在多次提交操作之后,你其实已经有一个指向最后那个提交对象的 main分支。 main分支会在每次提交时自动向前移动。
Git仓库都有 main分支,它并不是一个特殊分支,它和其他分支并没有区别,之所以每个仓库创建后都有一个main分支,那是因为git init命令之后创建它,大家都懒得该而已。
我们在创建项目时会针对不同环境创建三个常设分支:
main(master):生产环境的稳定分支,生产环境基于该分支构建。仅用来发布新版本。
dev:测试环境的稳定分支,测试环境基于该分支构建。
release:发布环境的稳定分支,发布环境基于该分支构建。
1.新建分支
接下来就创建另外两个分支。
$ git branch dev
$ git branch release
这会在当前所在的提交对象上创建两个指针。
由上图可以看到,尽管创建了两个新分支,但是HEAD只指向了main分支,这点也是和其它版本控制系统(如 Subversion 或 CVS)里的HEAD不同的地方,在 Git中,HEAD是一个指针,指向当前所在的本地分支,也就是当前操作的分支。
我们也可以通过git log查看当前的分支情况。
值得注意的是,如果git branch命令不加分支名,则会列出所有分支。
$ git branch
如果想要列出所有分支(包括远程仓库),可以加-a参数。
$ git branch -a
2.切换分支
上面讲了分支创建,下面就看看如何进行分支切换。
$ git checkout dev
接下来当前的分支就从main分支切换到了dev分支了,从log信息也可以看出HEAD指向了dev。
当然啦,还可以将创建分支和切换分支使用一个命令。
$ git checkout -b dev
上述命令和前面的两条命令是等效的。
3.分支合并
当你在dev分支上完成了开发任务后,测试也通过后,就可将dev分支合并到main分支。
你只需要检出到你想合并入的分支,然后运行git merge 命令:
$ git checkout main
$ git merge dev
接着将本地仓库推送到远程。
可以看到main分支已经将dev分支的合并了,而且远程仓库也同步了。
如果项目开发完成后,你不在需要dev分支,则可以使用以下命令删除。
$ git branch -d dev
如果要删除远程dev分支,可以使用以下命令。
$ git push origin --delete dev
值得一提的是checkout不仅可以切换分支,而且可以切换commit、tag。
切换指定commit:
$ git checkout 81596b112fe5f6d20dff4b433c952faf2b2d948d
切换指定标签:
$ git checkout V1.0.0
有时,当你在项目的一部分上已经工作一段时间后,所有东西都进入了混乱的状态,而这时你想要切换到另一个分支做一点别的事情(比如修改bug)或者在开发过程中你的搭档上传了新的代码到远程,这个时候你又需要对方的代码。 这时可以用git stash命令将已经修改的内容保存至堆栈区,切换到其他分支干完事后或者拉取了最新的代码后,然后将堆栈中的内容恢复到工作区,这样就可以继续开发,不耽误任何事情。
这里说明下如何在开发过程中拉取最新的代码同时能保存当前的工作状态。
$ git stash
$ git pull
$ git stash pop
以上命令就可以在拉取最新代码后恢复工作区的状态。
当然啦,如果使用git stash命令也可能引入一些冗余文件,这时你可以使用 git clean 命令去除冗余文件或者清理工作目录。git clean删除的内容是没有 tracked的,也就是没有被管理过的文件。
另外git reset –hard也可能会引入冗余文件。常用命令如下:
$ git clean -d -fx
参数说明:
-n :显示将要被删除的文件
-d :删除未被添加到 git 路径中的文件(将 .gitignore 文件标记的文件全部删除)
-f :强制运行
-x :删除没有被 track 的文件
值得注意的是,git clean删除了就找不回了,一定要慎用。
好了,关于Git的操作就讲这些,本文介绍的命令在学习工作中已基本够用了,在遇到问题时再查阅相关手册就可以了。
关于Git的更多操作,更多命令以参数请参看Git官方手册,
官方手册:
Pro Git
BruceOu的哔哩哔哩
BruceOu的主页
BruceOu的博客
BruceOu的CSDN博客
BruceOu的简书
BruceOu的知乎
关注公众号[嵌入式实验楼]获取更多资讯
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。