<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>沈小朋</title>
    <description>欢迎来到我的个人站~</description>
    <link>https://761669642.github.io/</link>
    <atom:link href="https://761669642.github.io/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Sun, 05 Sep 2021 09:57:10 +0000</pubDate>
    <lastBuildDate>Sun, 05 Sep 2021 09:57:10 +0000</lastBuildDate>
    <generator>Jekyll v3.9.0</generator>
    
      <item>
        <title>Freertos任务调度原理</title>
        <description>&lt;h2 id=&quot;任务创建&quot;&gt;任务创建&lt;/h2&gt;
&lt;p&gt;freerots中使用xTaskCreate来进行任务的创建，该函数的调用关系如下图所示，首先是通过pvPortMalloc函数创建任务的堆栈stack以及任务控制块TCB。值得一提的是，有些时候为了检测堆栈溢出，分配的stack内会被填充满0xA5(10100101)，当检测到栈的末尾的值不是0xA5了，说明栈溢出了。而TCB的作用就是记录所创建的任务的所有信息，包括名字，优先级，栈的开始位置，以及的栈指针sp寄存器的值。然后会调用prvInitialiseNewTask为这个任务的栈填充一些必要的值使其看起来像是已经被调度过之后的状态了。之后会通过prvAddNewTaskToReadyList函数来将这个新创建的任务加到readylist的任务链表里，readylist这个链表里都是已经就绪，准备被调度的task。&lt;/p&gt;

&lt;h3 id=&quot;初始化堆栈stack&quot;&gt;初始化堆栈stack&lt;/h3&gt;
&lt;p&gt;prvInitialiseNewTask这个函数主要是为stack填充了下面几个值，为什么是如下这些值可以参考&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&amp;lt;arm cortex-ms 权威指南&amp;gt;&amp;gt;&lt;/code&gt;的p146。一个任务被切换的时候必然会进入中断，而cortex-m3进入中断是硬件自动将xPSR PC等等，也就是表格最后一行之前的那些寄存器都入栈，然后等切回该任务又会将其出栈，所里这里的栈中的值可以理解为提前准备好的一些值，等这个任务被调度了，这些值就会被pop回寄存器中。关于最后一行的那些寄存器，它们是由软件来保存的，后面任务切换taskyield那边会提到。&lt;/p&gt;
&lt;center&gt;

 | stack的值| 
 |--- |
 |xPSR状态寄存器|
 |task函数的指针(PC指针) |
 | LR寄存器|
 |R12 R3 R2 R1 值是内存初始化值，其实就是0|
 | R0 (task函数传入的参数)|
 | R11 R10 R9 R8 R7 R6 R5 R4 值是内存初始化值，其实就是0|

&lt;/center&gt;

&lt;h3 id=&quot;各种任务链表&quot;&gt;各种任务链表&lt;/h3&gt;
&lt;p&gt;freerots中task存在几种状态，就绪态、阻塞态等，其实在代码中具体实现就是靠各种链表维护的，不同状态的task的TCB被插入到自己所处状态的链表中，当状态改变之后，从原来状态的链表中摘除，然后插入到新状态的链表中。例如vTaskDelay()延时的任务就会处于delayedTasklist链表，阻塞状态的任务就会处于supendedTasklist链表。而运行态的任务比较特殊，它不是一个链表，因为一个时刻运行的只有一个task，它是一个指针&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pxCurrentTCB&lt;/code&gt;。而就绪任务链表readylist是指以及满足条件可以被调度的task，其实这是一个链表组，每个链表中都是同一优先级的task，而不同优先级的链表头组成了一个数组，数组下标就是优先级。prvAddNewTaskToReadyList函数的调用关系如下，其中prvInitialiseTaskLists是用来初始化这些任务链表的，但这个函数只有在第一次创建task时才执行。这里存在一种特殊的情况，就是新创建的task比当前所有task的优先级都高，那么就应该直接把当前运行的task顶掉，所以就调用了taskYIELD_IF_USING_PREEMPTION。这个函数最终调用的是portYIELD。&lt;/p&gt;

&lt;h2 id=&quot;任务调度&quot;&gt;任务调度&lt;/h2&gt;
&lt;p&gt;任务调度的核心就是切换task，依赖于portYIELD函数，其实这个就只是挂起了pendsv的中断位，其他的事情就交给pendsv这个中断的handler来处理了。freertos的cortex-m3代码中，pendsv中断服务函数是xPortPendSVHandler，其具体的内容如下&lt;/p&gt;
&lt;div class=&quot;language-armasm highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nl&quot;&gt;mrs&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;r0&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;psp&lt;/span&gt; 
&lt;span class=&quot;nl&quot;&gt;isb&lt;/span&gt;                         &lt;span class=&quot;c&quot;&gt;;步骤1&lt;/span&gt;

&lt;span class=&quot;nl&quot;&gt;ldr&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;r3&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;pxCurrentTCBConst&lt;/span&gt; 
&lt;span class=&quot;nl&quot;&gt;ldr&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;r2&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;r3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;                &lt;span class=&quot;c&quot;&gt;;步骤2&lt;/span&gt;

&lt;span class=&quot;nl&quot;&gt;stmdb&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;r0&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;!,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;r4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;r11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; 
&lt;span class=&quot;nl&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;r0&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;r2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;                &lt;span class=&quot;c&quot;&gt;;步骤3&lt;/span&gt;

&lt;span class=&quot;nl&quot;&gt;stmdb&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;!,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;r3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;r14&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; 
&lt;span class=&quot;nl&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;r0&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;%0&lt;/span&gt; 
&lt;span class=&quot;nl&quot;&gt;msr&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;basepri&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;r0&lt;/span&gt; 
&lt;span class=&quot;nl&quot;&gt;bl&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;vTaskSwitchContext&lt;/span&gt; 
&lt;span class=&quot;nl&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;r0&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; 
&lt;span class=&quot;nl&quot;&gt;msr&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;basepri&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;r0&lt;/span&gt; 
&lt;span class=&quot;nl&quot;&gt;ldmia&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;!,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;r3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;r14&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;        &lt;span class=&quot;c&quot;&gt;;步骤4&lt;/span&gt;

&lt;span class=&quot;nl&quot;&gt;ldr&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;r1&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;r3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; 
&lt;span class=&quot;nl&quot;&gt;ldr&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;r0&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;r1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; 
&lt;span class=&quot;nl&quot;&gt;ldmia&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;r0&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;!,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;r4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;r11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; 
&lt;span class=&quot;nl&quot;&gt;msr&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;psp&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;r0&lt;/span&gt; 
&lt;span class=&quot;nl&quot;&gt;isb&lt;/span&gt; 
&lt;span class=&quot;nl&quot;&gt;bx&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;r14&lt;/span&gt;                      &lt;span class=&quot;c&quot;&gt;;步骤5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;下面详细解释一下每一步的过程，每个步骤跟下面的小标对应&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;cortex-m3有是由两个堆栈指针寄存器的，一个msp，一个psp。当进入中断状态之后，msp起作用，步骤1就是将psp值赋给r0，psp指向的是中断之前的task的堆栈顶部。&lt;/li&gt;
  &lt;li&gt;将pxCurrentTCBConst(其实就是pxCurrentTCB)的地址赋给r3，将r3指向的值进行赋给r2，此时r2其实存放的就是当前task(也就是中断之前的task)的TCB地址。&lt;/li&gt;
  &lt;li&gt;将r4到r11的寄存器依次存入r0往下的空间(r0放的就是之前task的是sp指针)，其实就是入栈了，r0还更新成了最新的栈顶指针。将r0的值赋值给[r2]，其实就是将最新的栈指针赋值给了TCB的第一项。结合前两个步骤看，这些动作其实就是中断之后，软件完成r4到r11寄存器的入栈，并且将最新的栈顶指针存到TCB相应的成员中。&lt;/li&gt;
  &lt;li&gt;此时sp其实是msp，将r3 r14存入msp所指向的主堆栈。意味着主堆栈中存放至pxCurrentTCBConst变量地址和LR的值，这个LR的值是入中断时自动更新的，值是EXC_RETURN。这里可以理解为保护这两个寄存器的值，因为等会儿还要跳转到vTaskSwithContext，说不定就会把这连个寄存器的值冲掉。而后面还要用到这连个寄存器，所以先保护起来。后面先屏蔽中断，调用vTaskSwithContext，这个函数其实就是把readylist中符合运行条件的task赋值给pxCurrentTCB。之后开启中断，将r3 r4再从主堆栈pop出来。&lt;/li&gt;
  &lt;li&gt;从r3中取出新TCB的指针，然后将这个TCB存储的sp指针值赋值给r0，然后从r0往上的栈空间的值赋值给r4-r11，其实也就是出栈，然后将最新的sp指针再psp寄存器，最终bx r14跳出中断。
此外，这里还隐藏了两个重要的步骤，前面提到中断时候，会有自动入栈，中断结束会自动出栈。所以步骤1之前，原先的task的stack中已经被压入了xPSR, PC等寄存器，而步骤5跳出中断之后，新的task的这组寄存器也会被硬件自动弹出栈。总之整个过程就是旧task的寄存器值入栈，并更新TCB内保存的sp指针值，切换currentTask，然后新task的寄存器出栈，更新psp。这是个对称的操作过程。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;任务延时阻塞及唤醒&quot;&gt;任务延时阻塞及唤醒&lt;/h2&gt;
&lt;p&gt;vTaskDelay函数的调用关系如下，主要是prvAddCurrentTaskToDelayedList这个函数，在加入了delaylist之后，剩下的就交给systick的中断处理函数，systick系统时钟，在一定时间例如1ms就会产生一个systick中断，也就是系统节拍。在systick中断中调用了xTaskIncrementTick()函数，这个函数每次为ticks加1，并且和delaylist中的task的delay time的值比较，如果发现有task的延迟时间到期了，就会将这个task摘下来挂到readylist中。之后的操作和portYield类似，挂起了pendsv中断，等待pendsv中断handler来进行实际的任务切换。&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
其实分析下来，freertos任务调度的底层核心就是pendsv的handler，它负责实际task的上下文的切换，内核通过systick中断或者主动portYield来触发pendsv中断。逻辑上则依赖于各类状态的链表来维护各个状态的任务，并通过pxCurrentTCB来指向当前运行的任务。抛开pendsv中断这个底层实现来看，freertos的任务调度从逻辑上就变成了怎样将TCB从一个状态链表上摘下来放到另一个状态链表上的过程了。
&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;分析了一大堆之后，发现&lt;a href=&quot;https://my.oschina.net/u/3699634/blog/1547467&quot;&gt;别人&lt;/a&gt;比我分析的还要到位，所以我这篇总结自己将就看看吧，是真的懒得把所有示意图都画出来了。&lt;/p&gt;
</description>
        <pubDate>Tue, 10 Aug 2021 00:00:00 +0000</pubDate>
        <link>https://761669642.github.io/2021/08/freertos_shceduler/</link>
        <guid isPermaLink="true">https://761669642.github.io/2021/08/freertos_shceduler/</guid>
        
        
      </item>
    
      <item>
        <title>Git 命令详解</title>
        <description>&lt;h2 id=&quot;git的工作区暂存区和版本库的概念&quot;&gt;git的工作区、暂存区和版本库的概念&lt;/h2&gt;
&lt;p&gt;关于这三个区域的概念其实网上以及讲的很多了，但是发现有些概念不同的人理解的不一样。
&lt;br /&gt;
我自己的理解是如图所示
&lt;img src=&quot;/images/posts/git-guide/git_worktree_index.jpg&quot; alt=&quot;&quot; height=&quot;40%&quot; width=&quot;40%&quot; /&gt;
工作区的内容就是工作目录中实际显示出来的文件，而暂存区的内容是工作区的内容的快照，暂存区对工作区的内容进行跟踪，而版本库的内容是对暂存区的一种保存，版本库对工作区进行跟踪。通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git add&lt;/code&gt;命令就能够将此时工作区的内容推送到暂存区，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git commit&lt;/code&gt;命令则是将暂存区的内容推送到版本库。&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/git-guide/git_status_test.jpg&quot; alt=&quot;&quot; height=&quot;70%&quot; width=&quot;70%&quot; /&gt;&lt;br /&gt;
如图所示，通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git status&lt;/code&gt;命令可以看到绿色字体以及红色字体的两类不同的文件，绿色字体的”modifiled: file1”对应着上图中file1暂存区和版本库的差异(绿色箭头)，而红色字体包含多种情况，上图中file1工作区和版本库的差异(红色箭头)对应着红色字体的”modified: file1”，而file2是在工作区创建了文件，却没有add和commit，于是暂存区和版本库就没有对该文件的追踪，对应着图中”Untracked files: file2”，file3是在add并commit之后删除了工作区的file3文件，对应着图中“deleted: file3”。总之，工作区与暂存区的差异总是红色字体表示，而暂存区与版本库的差异总是绿色字体，具体是属于modified，deleted，还是untracked files类型，则是由产生差异的原因决定。例如modified就是由于文件更改而产生的差异。&lt;br /&gt;&lt;br /&gt;
我始终认为，工作区的内容就是工作目录中看到的内容，暂存区的内容就是工作区推送过来的全部内容。但是网上有些博客的理解似乎是将暂存区和版本库的差异(git status出现的绿字内容)当成了暂存区的内容。尤其是&lt;a href=&quot;https://www.liaoxuefeng.com/wiki/896043488029600/897271968352576&quot;&gt;廖雪峰&lt;/a&gt;的网站对于git的暂存区的讲解，说commit之后暂存区就空了，我是非常不认可的，因为如果接受了这样的设定，后面理解&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git checkout -- file，git restore，git reset&lt;/code&gt;等命令就会很不直观，在很多教程中就会出现类似“将reset带来的差异放入暂存区”之类的话，其实本质就是将版本库的文件整个回退到暂存区，却由于将暂存区错误理解成暂存区与版本库之间的差异，而将原本简单的概念理解复杂了。&lt;/p&gt;

&lt;h2 id=&quot;git版本回退的几个命令&quot;&gt;git版本回退的几个命令&lt;/h2&gt;
&lt;p&gt;在接受了之前对三个区域的理解之后，git版本回退的命令就非常好理解了。有三个命令可以用于版本回退，&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;git checkout -- file&lt;/li&gt;
  &lt;li&gt;git reset HEAD -- file&lt;/li&gt;
  &lt;li&gt;git restore -- file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其中&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git restore&lt;/code&gt;是git新版本用来代替&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git checkout&lt;/code&gt;在版本回退方面的作用的，而checkout另一个作用切换分支则是被&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git switch&lt;/code&gt;替代了。&lt;a href=&quot;https://zhuanlan.zhihu.com/p/259385054&quot;&gt;参考这里&lt;/a&gt;&lt;br /&gt;
其实本质上就是将文件从版本库覆盖到暂存区，或者从暂存区覆盖到工作区，下图展示了具体从哪个区覆盖到哪个区。
&lt;img src=&quot;/images/posts/git-guide/git_restore.jpg&quot; alt=&quot;git restore&quot; height=&quot;60%&quot; width=&quot;60%&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;关于git的切换branch时工作区的变化&quot;&gt;关于git的切换branch时工作区的变化&lt;/h2&gt;
&lt;p&gt;有时候在工作区有改动但还没有add的情况下需要切换到其他branch，切换branch后若该文件在repository中的数据没有变化，那么该文件就不会在工作区得到刷新，工作区原先的修改就会得以保留。如果切换branch之后该文件在repository中的数据变化了，工作区文件就得跟着刷新，就有可能覆盖掉原来工作区文件，所以这时候一般git会提示你不能切换，要先commit或者stash。&lt;/p&gt;

&lt;h2 id=&quot;关于远端更改然后pull到本地后遇到冲突的情况&quot;&gt;关于远端更改然后pull到本地后遇到冲突的情况&lt;/h2&gt;
&lt;p&gt;首先要理解&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull&lt;/code&gt;这个命令相当于先git fetch将远端的最新内容拉倒本地，然后通过merge合并到本地的分支。所以说其实pull遇到的冲突本质上是merge引起的冲突。有时候在工作区有改动但是还没有add的情况下需要pull拉下远端最新的内容，遇到的情况其实跟上面切换branch的情况类似，如果pull之后若该文件在repository中的数据没有变化，那么该文件就不会在工作区得到刷新，工作区原先的修改就会得以保留，否则就会跟着刷新，然后冲掉工作区的修改，这时候git会提示你工作区文件可能会被overwriten。&lt;/p&gt;

&lt;h2 id=&quot;解决方法&quot;&gt;解决方法&lt;/h2&gt;
&lt;p&gt;一般上面两种问题都可以通过git stash解决，但是也会存在一种情况，你本地工作区修改的文件，在你切换branch或者pull之后也被修改了，并且修改了同样的地方，那么git stash pop的时候也会产生冲突，其实这种冲突跟repository的冲突差不多，如图所示。
##&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/git-guide/git_stash_pop.jpg&quot; alt=&quot;图1 git stash pop冲突&quot; height=&quot;50%&quot; width=&quot;50%&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;git-merge&quot;&gt;git merge&lt;/h2&gt;
&lt;p&gt;主要分为三种情况，分别如图所示&lt;br /&gt;
&lt;img src=&quot;/images/posts/git-guide/git_merge.jpg&quot; alt=&quot;&quot; height=&quot;70%&quot; width=&quot;70%&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;1新分支与主分支都有新的commit时git-merge回主分支&quot;&gt;1.新分支与主分支都有新的commit时git merge回主分支&lt;/h3&gt;
&lt;p&gt;merge之后会生成新节点，且需要commit message&lt;/p&gt;

&lt;h3 id=&quot;2只有新分支更新了commit时采用git-merge-no-ff命令&quot;&gt;2.只有新分支更新了commit时采用git merge –no-ff命令&lt;/h3&gt;
&lt;p&gt;merge之后会生成新节点，且需要commit message&lt;/p&gt;

&lt;h3 id=&quot;3只有新分支更新了commit时采用git-merge命令&quot;&gt;3.只有新分支更新了commit时采用git merge命令&lt;/h3&gt;
&lt;p&gt;merge之后不会生成新节点，相当于直接把新分支的commit直接拉到主分支上来了&lt;/p&gt;

&lt;p&gt;&lt;br /&gt; 
未完待续。。。&lt;/p&gt;
</description>
        <pubDate>Tue, 18 May 2021 00:00:00 +0000</pubDate>
        <link>https://761669642.github.io/2021/05/git-guide/</link>
        <guid isPermaLink="true">https://761669642.github.io/2021/05/git-guide/</guid>
        
        
      </item>
    
      <item>
        <title>Github Pages + Jekyll搭建个人博客</title>
        <description>&lt;p&gt;一直想搭个博客记录一下自己的一些学习与总结，折腾了两天终于搭起来了，当然也遇到了不少的坑，主要是jekyll环境不太好配置，版本冲突和依赖关系等等，下面就总结一下搭建的过程，作为搭建成功后的第一篇博客试试水。&lt;/p&gt;

&lt;h2 id=&quot;github-pages&quot;&gt;Github Pages&lt;/h2&gt;
&lt;p&gt;github pages其实就是github的一个网站托管的功能，在自己的github账号上新建一个xxx.github.io的repository(xxx一定要是你自己的github账户名)，然后将网站的相关代码和文件传上去，就可以在浏览器中通过https://xxx.github.io这个网址进行访问了。而一般我们都不会自己去写html网页，所以可以通过一些工具和模板进行自动构建，因此我们选择jekyll来进行网站的构建，有了模板之后，我们写博客时只需要专注于markdown文件的撰写，写完之后可以在本地用jelly将其依照模板构建成网页进行预览。而github pages也是支持jekyll自动构建的，我们只需要将jekyll模板以及markdown文件push到github仓库，github就会自动构建，然后发布。&lt;/p&gt;

&lt;h2 id=&quot;jekyll环境搭建&quot;&gt;jekyll环境搭建&lt;/h2&gt;
&lt;p&gt;在windows上的jekyll环境安装还是有点复杂的，要依赖于ruby，而且后面你选择的主题可能也依赖于特定版本的软件包。我是参考了&lt;a href=&quot;https://www.jianshu.com/p/9f71e260925d&quot;&gt;这篇文章&lt;/a&gt;搭建jekyll环境的，但是并没有一次成功，很多步骤我在这里还是重复一遍，尽量还原我自己的安装过程。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;首先是安装&lt;a href=&quot;https://rubyinstaller.org/downloads/&quot;&gt;ruby&lt;/a&gt;,因为看到其他教程有提到要安装Devkit，所以我选择的是Ruby+Devkit 2.7.3.1(x64)版本的，安装过程中会默认将ruby的可执行命令加到环境变量PATH里，安装完成后再&lt;strong&gt;新开&lt;/strong&gt;一个cmd或者powershell，敲&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ruby -v&lt;/code&gt;验证是否装成功，如果不新开一个cmd有可能敲不出来，当时就是在旧的cmd上死活敲不出来，也为没装成功。&lt;/li&gt;
  &lt;li&gt;下载&lt;a href=&quot;https://rubygems.org/pages/download&quot;&gt;RubyGems&lt;/a&gt;,解压到自己选定的一个目录，打开cmd进入该目录，然后执行&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ruby setup.rb&lt;/code&gt;，我的理解就是在ruby环境下对rubygem进行编译，并安装到ruby的目录下，成功后，就能敲出&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem&lt;/code&gt;命令，然后&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem install jekyll&lt;/code&gt;安装jekyll。&lt;/li&gt;
  &lt;li&gt;选择合适的jekyll模板，我是看了B站博主&lt;a href=&quot;https://www.bilibili.com/video/BV14x411t7ZU?t=1176&quot;&gt;酒石酸菌&lt;/a&gt;的教学，采用了&lt;a href=&quot;https://github.com/leopardpan/leopardpan.github.io&quot;&gt;潘柏信&lt;/a&gt;的模板，跟着视频里一样删除了Rakefile, Gemfile以及Gemfile.lock这三个文件，然后执行&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jekyll server&lt;/code&gt;在本地构建成功，就可以在本地4000端口即http://127.0.0.1:4000访问到页面，但是当我push到github上之后却发现github总是构建失败。然后我又把这三个文件加回来了，但是在本地却构建失败了。&lt;/li&gt;
  &lt;li&gt;照着失败的log搜了一下，搜到了&lt;a href=&quot;https://www.5616760.com/jekyll/2020/10/10/Jekyll.html&quot;&gt;这篇文章&lt;/a&gt;，然后敲了下面几个命令，然后重新在本地构建成功，push到github上后也成功了，具体是哪个命令起了作用以及起了什么作用，我也搞不明白深层的原理，毕竟术业有专攻，不干前端，只是拿来用一下，就不纠结了。&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ gem install bundler

$ bundle update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;注意这两个命令必须要在gemfile所在的目录里敲，因为没有这个file他就不知道要update哪个版本，相当于是根据这个file的依赖版本进行更新的。而这个gemfile实际就存在于上面提到的jekyll模板中。&lt;br /&gt;
然后后续我还换了个头像，push到github，却发现在github.io上却始终刷不出来，后来发现是浏览器自动把图像缓存了，要清除浏览器缓存才能刷出来。&lt;/p&gt;
</description>
        <pubDate>Mon, 17 May 2021 00:00:00 +0000</pubDate>
        <link>https://761669642.github.io/2021/05/github_pages-jekyll/</link>
        <guid isPermaLink="true">https://761669642.github.io/2021/05/github_pages-jekyll/</guid>
        
        
      </item>
    
  </channel>
</rss>
