<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Kacper Rychel</title>
    <description>The latest articles on Forem by Kacper Rychel (@zdybit).</description>
    <link>https://forem.com/zdybit</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F968075%2Fcb13e97c-c50e-4467-ac34-de678648fe60.jpeg</url>
      <title>Forem: Kacper Rychel</title>
      <link>https://forem.com/zdybit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/zdybit"/>
    <language>en</language>
    <item>
      <title>Learn Git fast forward by reproducing GitHub's merges in practice</title>
      <dc:creator>Kacper Rychel</dc:creator>
      <pubDate>Tue, 16 May 2023 10:04:52 +0000</pubDate>
      <link>https://forem.com/zdybit/learn-git-fast-forward-reproducing-github-pull-requests-in-practice-3b8g</link>
      <guid>https://forem.com/zdybit/learn-git-fast-forward-reproducing-github-pull-requests-in-practice-3b8g</guid>
      <description>&lt;p&gt;We are engineers for many reasons. The common one is we want to know, &lt;strong&gt;how it works&lt;/strong&gt; and &lt;strong&gt;how it is made&lt;/strong&gt;. In this article, I will feed your curiosity hunger for both &lt;em&gt;how it works&lt;/em&gt;, showing you how GitHub closes pulls request under the hood, and &lt;em&gt;how it is made&lt;/em&gt;, playing with Git commands to reproduce the same result. On top of that, I made a practical brief in fast-forward (&lt;code&gt;--ff&lt;/code&gt;, &lt;code&gt;--no-ff&lt;/code&gt;, &lt;code&gt;--ff-only&lt;/code&gt;) merge strategies in practice. Two birds with one stone 🐦🐦&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note 💬&lt;br&gt;
If you are not interested in details of reproducing the GitHub's pull requests results, you can go straight to the TLDR section, but I do not recommend missing out on the fun 😎&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's start the adventure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of content
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Table of content&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--ff&lt;/code&gt;, or &lt;code&gt;--no-ff&lt;/code&gt;, or &lt;code&gt;ff-only&lt;/code&gt;, that is that question

&lt;ul&gt;
&lt;li&gt;fast forward in practice&lt;/li&gt;
&lt;li&gt;Bonus puzzle&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Closing Pull Request in practice

&lt;ul&gt;
&lt;li&gt;Create a merge commit&lt;/li&gt;
&lt;li&gt;Squash and merge&lt;/li&gt;
&lt;li&gt;Rebase and merge&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;TLDR&lt;/li&gt;

&lt;li&gt;Ending&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;--ff&lt;/code&gt;, or &lt;code&gt;--no-ff&lt;/code&gt;, or &lt;code&gt;ff-only&lt;/code&gt;, that is that question
&lt;/h2&gt;

&lt;p&gt;GitHub merges branches with the &lt;code&gt;--no-ff&lt;/code&gt; or &lt;code&gt;--ff-only&lt;/code&gt; strategy depending on the closing options.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--ff&lt;/code&gt; (fast forward) and &lt;code&gt;--ff-only&lt;/code&gt; (fast forward only) simply move a pointer forward. They vary when fast forward is not possible. Then, &lt;code&gt;--ff&lt;/code&gt; switches to &lt;code&gt;--no-ff&lt;/code&gt;, and &lt;code&gt;--ff-only&lt;/code&gt; rejects the operation.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--no-ff&lt;/code&gt; (no fast forward) just creates a merge commit.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--ff&lt;/code&gt; is a default strategy for &lt;code&gt;git pull&lt;/code&gt; and &lt;code&gt;git merge&lt;/code&gt; commands.&lt;/p&gt;

&lt;h3&gt;
  
  
  fast forward in practice
&lt;/h3&gt;

&lt;p&gt;Now, let's see how it works in practice with the initial state:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv74kocz1729hubn5erov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv74kocz1729hubn5erov.png" alt="ff---tree-before-merge-ff"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;git pull origin feature/4
From https://github.com/zdybasny/git-merges-strategies
 &lt;span class="k"&gt;*&lt;/span&gt; branch            feature/4  -&amp;gt; FETCH_HEAD
Updating 6cf021a..fa923ee
Fast-forward
 README.md | 4 ++++
 1 file changed, 4 insertions&lt;span class="o"&gt;(&lt;/span&gt;+&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;git push


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;See? Fast-forward was used by default, even if no flag was provided.&lt;/p&gt;

&lt;p&gt;The result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fruuri9nvgxxdbcgeojw9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fruuri9nvgxxdbcgeojw9.png" alt="ff---tree-after-merge-ff"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is a sample scenario, which &lt;em&gt;fast forward&lt;/em&gt; is not possible for:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ld9f8ye45beyy73st46.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ld9f8ye45beyy73st46.png" alt="ff---tree-before-failing-merge-ff-only"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;git pull origin feature/5 &lt;span class="nt"&gt;--ff-only&lt;/span&gt;

From https://github.com/zdybasny/git-merges-strategies
 &lt;span class="k"&gt;*&lt;/span&gt; branch            feature/5  -&amp;gt; FETCH_HEAD
fatal: Not possible to fast-forward, aborting.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The operation was aborted for the &lt;code&gt;--ff-only&lt;/code&gt; strategy.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;git pull origin feature/5 &lt;span class="nt"&gt;--ff&lt;/span&gt;

From https://github.com/zdybasny/git-merges-strategies
 &lt;span class="k"&gt;*&lt;/span&gt; branch            feature/5  -&amp;gt; FETCH_HEAD
Merge made by the &lt;span class="s1"&gt;'ort'&lt;/span&gt; strategy.
 new-file.md | 3 +++
 1 file changed, 3 insertions&lt;span class="o"&gt;(&lt;/span&gt;+&lt;span class="o"&gt;)&lt;/span&gt;
 create mode 100644 new-file.md


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For the &lt;code&gt;--ff&lt;/code&gt; strategy, the operation switched to the &lt;code&gt;--no-ff&lt;/code&gt; one and created a merge commit:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8q0tu1gcwici0jtzio4a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8q0tu1gcwici0jtzio4a.png" alt="ff---tree-after-merge-ff-switched-to-no-ff"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus puzzle
&lt;/h3&gt;

&lt;p&gt;Being familiar with the fast forward strategy, you can try yourself with a simple puzzle.&lt;/p&gt;

&lt;p&gt;What will the command below do for &lt;code&gt;feature/5&lt;/code&gt; branch in current state:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F12w6nk5gwcfa52fem0i7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F12w6nk5gwcfa52fem0i7.png" alt="ff-bonus---current-tree"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git pull origin main --no-ff&lt;/code&gt;?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⁝&lt;/p&gt;

&lt;p&gt;⁝&lt;/p&gt;

&lt;p&gt;⁝&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz45w2u3v1pwpino5g46t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz45w2u3v1pwpino5g46t.png" alt="ff-bonus---merge-no-ff"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Imagine more such merges between more branches 😱&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git pull origin main --ff&lt;/code&gt;?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⁝&lt;/p&gt;

&lt;p&gt;⁝&lt;/p&gt;

&lt;p&gt;⁝&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F677kl3e9pm8zoei1xah3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F677kl3e9pm8zoei1xah3.png" alt="ff-bonus---merge-ff"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Much cleaner 🤩&lt;/p&gt;

&lt;p&gt;Unfortunately, GitHub supports only &lt;code&gt;--no-ff&lt;/code&gt; merges, what you will see in the next part of the article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Pull Request in practice
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create a merge commit
&lt;/h3&gt;

&lt;p&gt;Before starting to play with reproducing the outcome of *Create a merge commit`, let's see what GitHub's documentation says about it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#merge-your-commits" rel="noopener noreferrer"&gt;Merge your commits&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you click the default Merge pull request option on a pull request on GitHub.com, all commits from the feature branch are added to the base branch in a merge commit. The pull request is merged using the &lt;strong&gt;--no-ff&lt;/strong&gt; option.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;em&gt;feature branch&lt;/em&gt; here means the &lt;strong&gt;source branch&lt;/strong&gt;, and the &lt;em&gt;base branch&lt;/em&gt; means the &lt;strong&gt;target branch&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And this is the result from Github to reproduce:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbk70q7h8zx95brpb0bu2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbk70q7h8zx95brpb0bu2.png" alt="merge---state-to-reproduce"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Knowing the theory and what is to do, we can use following Git commands:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ git checkout main&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ git pull origin feature/3 --no-ff&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ git push&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcfy30gr5yvtc2ipipbea.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcfy30gr5yvtc2ipipbea.png" alt="merge---after-merge"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are two things to notice:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Even the green branch has been removed, its history still exists. The same will happen if we remove the &lt;code&gt;feature/3&lt;/code&gt; branch. This is exactly the result of the &lt;code&gt;--no-ff&lt;/code&gt; strategy of &lt;code&gt;git pull&lt;/code&gt; and &lt;code&gt;git merge&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Default messages generated by GitHub and by Git differ.

&lt;ol&gt;
&lt;li&gt;The message generated by Git doesn't include a PR's number. We can still write our own merge commit message and attach a PR reference to it. We must add the &lt;code&gt;-m&lt;/code&gt; flag to do so.&lt;/li&gt;
&lt;li&gt;I described default messages from GitHub in the &lt;a href="https://dev.to/zdybit/3-options-to-close-pull-requests-on-github-what-2j3n"&gt;first post of this series&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;As you see, the results are pretty the same, so you know what happens behind the scenes of GitHub when it merges your PR.&lt;/p&gt;

&lt;h3&gt;
  
  
  Squash and merge
&lt;/h3&gt;

&lt;p&gt;GitHub's documentation says about Squash and merge:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#squash-and-merge-your-commits" rel="noopener noreferrer"&gt;Squash and merge your commits&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you select the Squash and merge option on a pull request on GitHub.com, the pull request's commits are squashed into a single commit. Instead of seeing all of a contributor's individual commits from a topic branch, the commits are combined into one commit and merged into the default branch. Pull requests with squashed commits are merged using the &lt;strong&gt;fast-forward&lt;/strong&gt; option.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The documentation says that the fast-forward strategy is used for squashing. From Git point of view, &lt;a href="https://git-scm.com/docs/git-merge#Documentation/git-merge.txt---squash" rel="noopener noreferrer"&gt;--squash&lt;/a&gt; is the flag of &lt;code&gt;git merge&lt;/code&gt;. The statement seems to be informative only, because:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ git merge origin feature/6 --squash --no-ff&lt;/code&gt;&lt;br&gt;
&lt;code&gt;fatal: You cannot combine --squash with --no-ff.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here is the state to reproduce:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F79ksy0afi6y4h2r7339y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F79ksy0afi6y4h2r7339y.png" alt="squash---state-to-reproduce"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Git CLI commands:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ git checkout main&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ git merge origin feature/7 --squash&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--ff&lt;/code&gt; flag could be skipped in the command above because it is the default strategy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsof2ouzwqp4wtext97gq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsof2ouzwqp4wtext97gq.png" alt="squash---after-squash"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ git commit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fks7d04u558txhw0feqzu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fks7d04u558txhw0feqzu.png" alt="squash---vscode-commit-editor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We could also use &lt;code&gt;-m&lt;/code&gt; flag to write a message in a terminal inline, but writing it in an editor is more convenient and a there is a default message to edit already.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ git push&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flcp24zh7ucdy1zkpw2cv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flcp24zh7ucdy1zkpw2cv.png" alt="squash---after-squash-n-push"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The results look the same but the default commit messages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rebase and merge
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Rebase&lt;/em&gt; is a powerful tool to rewriting a Git history, and its description in the documentation is pretty complex and focused on vary cases. You can just read it on your risk 😉 here: &lt;a href="https://git-scm.com/docs/git-rebase" rel="noopener noreferrer"&gt;https://git-scm.com/docs/git-rebase&lt;/a&gt;. Just kidding, it is worth to read it.&lt;/p&gt;

&lt;p&gt;GitHub rebases in only one way, so its documentation is much clearer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#rebase-and-merge-your-commits" rel="noopener noreferrer"&gt;Rebase and merge your commits&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you select the Rebase and merge option on a pull request on GitHub.com, all commits from the topic branch (or head branch) are added onto the base branch individually without a merge commit. In that way, the rebase and merge behavior resembles a &lt;strong&gt;fast-forward&lt;/strong&gt; merge by maintaining a linear project history. However, rebasing achieves this by re-writing the commit history on the base branch with new commits.&lt;/p&gt;

&lt;p&gt;The rebase and merge behavior on GitHub &lt;strong&gt;deviates&lt;/strong&gt; slightly from Git rebase. Rebase and merge on GitHub will always update the committer information and create new commit SHAs, whereas Git rebase outside of GitHub does not change the committer information when the rebase happens on top of an ancestor commit&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The state to reproduce:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnsnlbnjqo9ccveeze9m5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnsnlbnjqo9ccveeze9m5.png" alt="rebase---state-to-reproduce"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This time, it is not enough to run one or two Git commands to achieve the same result.&lt;/p&gt;

&lt;p&gt;Long story short, we need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;rebase the &lt;code&gt;feature/9&lt;/code&gt; branch onto the &lt;code&gt;main&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;move the &lt;code&gt;main&lt;/code&gt; pointer to &lt;code&gt;feature/9&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;reset &lt;code&gt;feature/9&lt;/code&gt; to &lt;code&gt;origin/feature/9&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, let's do it step by step.&lt;/p&gt;

&lt;p&gt;First, we rebase the &lt;code&gt;feature/9&lt;/code&gt; branch onto the &lt;code&gt;main&lt;/code&gt; branch with the flag &lt;code&gt;--force-rebase&lt;/code&gt; to achieve a similar state on your local:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ checkout feature/9&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ git rebase --force-rebase --onto main main feature/9&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fle6hl00urn9683xlg9tl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fle6hl00urn9683xlg9tl.png" alt="rebase---after-rebase-on-local"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the difference, which GitHub says in its documentation about. The &lt;code&gt;feature/9&lt;/code&gt; is rebased onto &lt;code&gt;main&lt;/code&gt;, but GitHub doesn't touch the source branch. So, you don't as well. You need to just keep the &lt;code&gt;origin/feature/9&lt;/code&gt; as it is. To do so, we need to move the &lt;code&gt;main&lt;/code&gt; pointer to &lt;code&gt;feature/9&lt;/code&gt; and reset &lt;code&gt;feature/9&lt;/code&gt; to &lt;code&gt;origin/feature/9&lt;/code&gt;. To move the pointer, we will use the fast-forward strategy we have already learned:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ git checkout main&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ git merge feature/9 --ff-only&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ git push&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ee8mg3pk67tfqehipwa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ee8mg3pk67tfqehipwa.png" alt="rebase---after-rebase-n-merge"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now the reset of &lt;code&gt;feature/9&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ git checkout -&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ git reset HEAD~2 --hard&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ git pull&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git checkout -&lt;/code&gt; is a shortcut for &lt;code&gt;git checkout @{-1}&lt;/code&gt;. It is useful when you need to switch to the previous branch.&lt;/p&gt;

&lt;p&gt;The reset command above moved the branch pointer back for 2 commits (&lt;code&gt;HEAD~2&lt;/code&gt;) and dropped their changes (&lt;code&gt;--hard&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git pull&lt;/code&gt; is for updating the local &lt;code&gt;feature/9&lt;/code&gt; branch to the state of the remote &lt;code&gt;origin/feature/9&lt;/code&gt; branch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F18n3g7q7duxxp24wks77.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F18n3g7q7duxxp24wks77.png" alt="rebase---after-rebase-n-reset"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Voilà!&lt;/strong&gt; 🎉 The result is the same as we did it by using the GitHub's UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  TLDR
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Create a merge commit:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ git checkout main&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ git pull origin feature/source-branch --no-ff&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ git push&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Squash and merge:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ git checkout main&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ git merge origin feature/source-branch --squash --ff&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ git commit&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ git push --force&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rebase and merge:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ git checkout feature/source-branch&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ git rebase --force-rebase --onto main main feature/source-branch&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ git checkout main&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ git merge feature/source-branch --ff &lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ git push&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ git checkout -&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ git reset HEAD~2 --hard&lt;/code&gt;&lt;br&gt;
&lt;code&gt;$ git pull&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ending
&lt;/h2&gt;

&lt;p&gt;I hope you enjoyed our play with Git and GitHub. Asking yourself questions like &lt;em&gt;"How does it work?"&lt;/em&gt; and &lt;em&gt;"How can I do it by myself?"&lt;/em&gt; is a wonderful way to learn something new. Very often, something we have never thought about before, like me before when I started to draft this article.&lt;/p&gt;

&lt;p&gt;This article is the last part of the series, within which I wanted to share with you &lt;strong&gt;what&lt;/strong&gt; options of closing pull requests on GitHub are, &lt;strong&gt;when&lt;/strong&gt; to use them, and now &lt;strong&gt;how&lt;/strong&gt; to do it by yourself. I hope you have found it useful and interesting.&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>devops</category>
    </item>
    <item>
      <title>Container runtime vocabulary</title>
      <dc:creator>Kacper Rychel</dc:creator>
      <pubDate>Sun, 23 Apr 2023 22:00:00 +0000</pubDate>
      <link>https://forem.com/zdybit/container-runtime-vocabulary-4chp</link>
      <guid>https://forem.com/zdybit/container-runtime-vocabulary-4chp</guid>
      <description>&lt;p&gt;Docker became the most popular standard of the containerization. It's so popular that almost everything in the containerized world is called Docker. You can't argue with that. What you can do is learning it. No matter if you are a developer, a sysadmin, or a DevOps engineer, you should know Docker to work with a modern infrastructure.&lt;/p&gt;

&lt;p&gt;In this post, I will show you that &lt;strong&gt;Docker is only a little part of the containerization now&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So, why almost everything in the containerized world is called Docker❓ What is the difference between a container engine and a container runtime❓ Why is it so confusing❓ What are example tools we can call low-level container runtime and high-level container runtime❓&lt;/p&gt;

&lt;p&gt;All these questions and more will be answering in this post.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE&lt;br&gt;
The terminology in the post is linked to each other. You can find:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;internal links to other terms within the post&lt;/li&gt;
&lt;li&gt;external links to home pages of tools&lt;/li&gt;
&lt;li&gt;bold terms (they are described in the previous post of this series: &lt;a href="https://dev.to/zdybit/container-and-image-vocabulary-123k"&gt;Container and image vocabulary&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Table of contents&lt;/li&gt;
&lt;li&gt;Terminology schema&lt;/li&gt;
&lt;li&gt;
Docker legacy

&lt;ul&gt;
&lt;li&gt;Docker breakup&lt;/li&gt;
&lt;li&gt;Container runtime vs container engine&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Tool categories

&lt;ul&gt;
&lt;li&gt;container runtime - low-level&lt;/li&gt;
&lt;li&gt;container runtime - high-level&lt;/li&gt;
&lt;li&gt;container image build tool&lt;/li&gt;
&lt;li&gt;container orchestrator&lt;/li&gt;
&lt;li&gt;daemon&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Standards

&lt;ul&gt;
&lt;li&gt;Open Container Initiative&lt;/li&gt;
&lt;li&gt;OCI Image Format Specification (image-spec)&lt;/li&gt;
&lt;li&gt;OCI Distribution Specification (distribution-spec)&lt;/li&gt;
&lt;li&gt;OCI Runtime Specification (runtime-spec)&lt;/li&gt;
&lt;li&gt;OCI Image Configuration&lt;/li&gt;
&lt;li&gt;container image format&lt;/li&gt;
&lt;li&gt;image manifest&lt;/li&gt;
&lt;li&gt;image index&lt;/li&gt;
&lt;li&gt;registry, repository, tag&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Ending&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Terminology schema
&lt;/h2&gt;

&lt;p&gt;I prepared the next map 🗺️ with relations between terms for you. With it, we can continue our journey.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---z54Cd_3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mdi1x580uwvvghw3bpgo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---z54Cd_3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mdi1x580uwvvghw3bpgo.png" alt="schema" width="800" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;[The schema was edited on 26-apr-2023]&lt;/strong&gt;&lt;br&gt;
Podman itself is not a low-level container runtime. It uses crun by default to create and run containers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Docker legacy
&lt;/h2&gt;

&lt;p&gt;A little historical overview is needed to fairly learn containerization tools and to finally answer the question &lt;em&gt;"why is everything called Docker?"&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker breakup
&lt;/h3&gt;

&lt;p&gt;When Docker was spreading its popularity, it was a monolith tool. It could build and manage images, transfer them to/from a container registry, run and manage containers, interact with isolated applications and resources withing the containers, and more. Pretty much, isn't it?&lt;/p&gt;

&lt;p&gt;Docker Inc. broken up its tool into the modules: &lt;a href="https://github.com/opencontainers/runc"&gt;runC&lt;/a&gt;, &lt;a href="https://containerd.io"&gt;containerd&lt;/a&gt;, &lt;a href="https://docs.docker.com/engine/reference/commandline/dockerd/"&gt;dockerd&lt;/a&gt;, and docker CLI client. Some of those modules can be called container runtimes or container engines now. containerd has been donated to &lt;a href="https://www.cncf.io"&gt;Cloud Native Computing Foundation&lt;/a&gt;, and runC to Open Container Initiative.&lt;/p&gt;

&lt;p&gt;The company, CoreOS, and other leaders in the containerization established Open Container Initiative in 2015. OCI provides standards around the containerization, but Docker Inc. contributes to the runtime specification part only.&lt;/p&gt;

&lt;h3&gt;
  
  
  Container runtime vs container engine
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developers.redhat.com/blog/2018/02/22/container-terminology-practical-introduction"&gt;🔗&lt;/a&gt; &lt;a href="https://www.docker.com/products/container-runtime/"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are no clear definitions of &lt;strong&gt;container runtime&lt;/strong&gt; and &lt;strong&gt;container engine&lt;/strong&gt;. They are used interchangeably. It shouldn't be far from the truth that container runtimes can be divided into to two parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;low-level container runtime&lt;/li&gt;
&lt;li&gt;
high-level container runtime.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;strong&gt;container engines&lt;/strong&gt; can be considered as a &lt;strong&gt;high-level container runtimes&lt;/strong&gt;. They include such features like a user interface (like CLI or REST API) and handling of images. They often call &lt;strong&gt;low-level container runtimes&lt;/strong&gt; to let them create and run containers. In such usage of the terms, &lt;strong&gt;low-level container runtimes&lt;/strong&gt; are called just &lt;strong&gt;container runtimes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NLPwfykO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uc0tg9ry7y6jefxbjo2c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NLPwfykO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uc0tg9ry7y6jefxbjo2c.png" alt="runtime-division" width="300" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Red Hat is the example organization using terms like this, but Docker Inc.  mixes both terms within its &lt;a href="https://docs.docker.com/engine/"&gt;Docker Engine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CqauaHty--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/85ks59f6952vv6cp9702.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CqauaHty--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/85ks59f6952vv6cp9702.png" alt="docker-engine-runtime" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is not necessary that tools must implement all features destined for container runtimes. Some of tools implement only a part of features and interacts with other container runtime (e.g. dockerd and containerd).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In this post I am going to use the &lt;strong&gt;low-level container runtime&lt;/strong&gt; and &lt;strong&gt;high-level container runtime&lt;/strong&gt; terms. In my opinion they are more accurate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Tool categories
&lt;/h2&gt;

&lt;h3&gt;
  
  
  container runtime - low-level
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://web.archive.org/web/20221206075452/https://www.ianlewis.org/en/container-runtimes-part-1-introduction-container-r"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Low-level container runtimes are responsible for managing &lt;strong&gt;container&lt;/strong&gt; lifecycle. Nothing more. It means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;creating, starting, stopping, and deleting &lt;strong&gt;containers&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;setting up &lt;strong&gt;namespaces&lt;/strong&gt;, &lt;strong&gt;cgroups&lt;/strong&gt; and operating system security features (like &lt;strong&gt;SELinux&lt;/strong&gt; policies and &lt;strong&gt;AppArmor&lt;/strong&gt; rules) for containers&lt;/li&gt;
&lt;li&gt;consuming mounting points (&lt;strong&gt;volumes&lt;/strong&gt;) prepared by high-level container runtimes
&lt;/li&gt;
&lt;li&gt;and then running commands inside those &lt;strong&gt;namespaces&lt;/strong&gt; and &lt;strong&gt;cgroups&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Low-level runtimes abstract the OS primitives only, they are not designed to perform other tasks. They are mostly used by &lt;strong&gt;high-level container runtimes&lt;/strong&gt;. You can interact with them directly, but it is not convenient.&lt;/p&gt;

&lt;p&gt;Examples: &lt;a href="https://github.com/opencontainers/runc"&gt;runC&lt;/a&gt;, &lt;a href="https://github.com/containers/crun"&gt;crun&lt;/a&gt;, &lt;a href="https://linuxcontainers.org/"&gt;LXC&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  container runtime - high-level
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://medium.com/tektutor/container-engine-vs-container-runtime-667a99042f3"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To fully derive benefits of containerization, it is not enough to just run containers. Their state and networking need to be managed and monitored. Containers are based on images, and images need to be managed. All of that needs to be done in a way that is easy to use and understand for developers, so the layer of interfaces needs to be provided as well.&lt;/p&gt;

&lt;p&gt;Common container runtimes can co-work with container orchestrators, han handling individual &lt;strong&gt;containers&lt;/strong&gt; runnings on every node in the cluster.&lt;/p&gt;

&lt;p&gt;High-level container runtimes usually:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rely on low-level container runtimes to run &lt;strong&gt;containers&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;set up networking between &lt;strong&gt;containers&lt;/strong&gt;, so that they can communicate with each other, or the outside world&lt;/li&gt;
&lt;li&gt;handle inputs over CLI or API&lt;/li&gt;
&lt;li&gt;transport container &lt;strong&gt;images&lt;/strong&gt; from/to a registry server&lt;/li&gt;
&lt;li&gt;prepare &lt;strong&gt;volumes&lt;/strong&gt; for &lt;strong&gt;containers&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;prepare metadata for &lt;strong&gt;low-level container runtimes&lt;/strong&gt; (e.g., to overwrite &lt;code&gt;CMD&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;monitor host &lt;strong&gt;resources&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples of high-level container runtime: &lt;a href="https://containerd.io"&gt;containerd&lt;/a&gt;, &lt;a href="https://www.docker.com/"&gt;dockerd&lt;/a&gt;, &lt;a href="https://cri-o.io/"&gt;CRI-O&lt;/a&gt;, &lt;a href="https://linuxcontainers.org/lxd/"&gt;LXD&lt;/a&gt;, &lt;a href="https://podman.io/"&gt;Podman&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  container image build tool
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://earthly.dev/blog/docker-vs-buildah-vs-kaniko/"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What runtimes can't do, by definition, is building container &lt;strong&gt;images&lt;/strong&gt; (mostly from a Dockerfile). It is a job for container image build tools.&lt;/p&gt;

&lt;p&gt;Container runtimes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;may use separate modules (like dockerd using BuildKit, or Podman using Buildah)&lt;/li&gt;
&lt;li&gt;may not provide the the feature at all (like CRI-O and containerd).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples of image build tools: &lt;a href="https://docs.docker.com/build/buildkit/"&gt;BuildKit&lt;/a&gt;, &lt;a href="https://buildah.io/"&gt;Buildah&lt;/a&gt;, &lt;a href="https://github.com/GoogleContainerTools/kaniko"&gt;kaniko&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  container orchestrator
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developers.redhat.com/blog/2018/02/22/container-terminology-practical-introduction#container_orchestration"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A container orchestrator does two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dynamically schedules container workloads within a cluster of computers&lt;/li&gt;
&lt;li&gt;provides a standardized application definition file (kube yaml, docker compose, etc.).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It allows containers within a cluster to be scheduled completely separately. Thanks to that it is easy to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deploy new instances of the same application into new environments&lt;/li&gt;
&lt;li&gt;scale up or down the number of instances of an application&lt;/li&gt;
&lt;li&gt;move an application from one environment to another.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On-premise examples: &lt;a href="https://kubernetes.io/"&gt;Kubernetes&lt;/a&gt;, &lt;a href="https://docs.docker.com/engine/swarm/"&gt;Docker Swarm&lt;/a&gt;.&lt;br&gt;
Examples of cloud vendors' solutions: &lt;a href="https://aws.amazon.com/ecs/"&gt;Amazon ECS&lt;/a&gt;, &lt;a href="https://azure.microsoft.com/en-us/services/container-instances/"&gt;Azure Container Instances&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  daemon
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Sometimes, you can see a "daemon" term in the context of container runtimes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The daemon, in general, is a long-running &lt;strong&gt;process&lt;/strong&gt; that performs some tasks in the background. So some container runtimes, their modules, and tools around the containerization can be called like that due to their long-running nature.&lt;/p&gt;

&lt;p&gt;The example tools called daemon: &lt;a href="https://docs.docker.com/engine/reference/commandline/dockerd/"&gt;dockerd&lt;/a&gt;, &lt;a href="https://containerd.io/"&gt;containerd&lt;/a&gt;, &lt;a href="https://linuxcontainers.org/lxd/"&gt;LXD&lt;/a&gt;, &lt;a href="https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/"&gt;kubelet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In contrast to those, &lt;a href="https://podman.io/"&gt;Podman&lt;/a&gt; is the daemonless container runtime.&lt;/p&gt;
&lt;h2&gt;
  
  
  Standards
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Open Container Initiative
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://opencontainers.org/"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://opencontainers.org/"&gt;OCI&lt;/a&gt; is an open governance structure for the express purpose of creating open industry standards around container formats and runtimes. OCI specification is a set of standards for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
containers image format (image-spec)&lt;/li&gt;
&lt;li&gt;
runtime (runtime-spec)&lt;/li&gt;
&lt;li&gt;
distribution (distribution-spec).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OCI develops &lt;a href="https://github.com/opencontainers/runc"&gt;runC&lt;/a&gt; as well.&lt;/p&gt;
&lt;h3&gt;
  
  
  OCI Image Format Specification (image-spec)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/opencontainers/image-spec/blob/main/spec.md"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The image-spec defines an &lt;strong&gt;OCI Image&lt;/strong&gt;, consisting of an image manifest, an image index (optional), a set of filesystem layers, and a configuration. The goal of this specification is to enable the creation of interoperable tools for building, transporting, and preparing a container image to run.&lt;/p&gt;
&lt;h3&gt;
  
  
  OCI Distribution Specification (distribution-spec)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/opencontainers/distribution-spec/blob/main/spec.md/"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OCI distribution-spec defines an API protocol to facilitate and standardize the distribution of content. Container Registry supports pushing and pulling images complaint with OCI image-spec. However, the distribution-spec is designed to be agnostic of content types.&lt;/p&gt;
&lt;h3&gt;
  
  
  OCI Runtime Specification (runtime-spec)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/opencontainers/runtime-spec/blob/main/spec.md"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OCI runtime-spec aims to specify the configuration, execution environment, and lifecycle of a &lt;strong&gt;container&lt;/strong&gt; created from an OCI Image.&lt;/p&gt;

&lt;p&gt;A container's configuration is specified as the config.json for the supported platforms and details the fields that enable the creation of a container. The execution environment is specified to ensure that applications running inside a container have a consistent environment between runtimes along with common actions defined for the container's lifecycle.&lt;/p&gt;
&lt;h3&gt;
  
  
  OCI Image Configuration
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/opencontainers/image-spec/blob/main/config.md"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This specification outlines the JSON format describing OCI images for use with a container runtime and its relationship to filesystem changesets, described in &lt;strong&gt;layers&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  container image format
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developers.redhat.com/blog/2018/02/22/container-terminology-practical-introduction#container_image_format"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Container runtimes used to have their own container image formats. Docker images formats became the most popular standard. Over time, the industry has established a new standard - OCI Image Format Specification, originally based on the &lt;a href="https://docs.docker.com/registry/spec/manifest-v2-2/"&gt;Docker Image Manifest V2, Schema 2&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  image manifest
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/opencontainers/image-spec/blob/main/manifest.md"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The manifest is an image format based JSON file that provides configuration and set of &lt;strong&gt;layers&lt;/strong&gt; for a single &lt;strong&gt;container image&lt;/strong&gt; for a specific architecture and operating system.&lt;/p&gt;
&lt;h3&gt;
  
  
  image index
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/opencontainers/image-spec/blob/main/image-index.md"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The image index is defined by OCI. It is a higher-level manifest which points to specific one or more image manifests, ideal for one or more platforms.&lt;/p&gt;

&lt;p&gt;Docker supports &lt;a href="https://docs.docker.com/registry/spec/manifest-v2-2/#manifest-list"&gt;manifest list&lt;/a&gt; instead of the image index.&lt;/p&gt;
&lt;h3&gt;
  
  
  registry, repository, tag
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;registry&lt;/strong&gt; is the service to store and download (push/pull) container &lt;em&gt;images&lt;/em&gt;. The information about the images is stored in the manifest files.&lt;/p&gt;

&lt;p&gt;Images inside the registry are grouped into &lt;strong&gt;repositories&lt;/strong&gt;, and repositories can have subgroups such as organizations, projects, etc. A repository contains multiple &lt;strong&gt;tags&lt;/strong&gt;. Each tag is a pointer to a specific image. The tag is usually a version number, but it can be any string. The default tag is &lt;code&gt;latest&lt;/code&gt;. It is mutable (mutable) as opposed to blob sha256 (immutable).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run my-registry.com/some-/-organization/repository:tag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Ending
&lt;/h2&gt;

&lt;p&gt;The second part of the containerization journey behind us. You won't be confused anymore when you hear about runtime-related terms, like container runtime, Docker Engine, Podman, containerd, runC.&lt;/p&gt;

&lt;p&gt;OCI is a new Docker now.&lt;/p&gt;

&lt;p&gt;Now, knowing what is not the Docker, you must be eager to know what  actually is it. A part of a Docker tooling has been described here. Next time, I will expand upon the Docker environment. It is still significant part of the containerization world, so it desires for a separate post.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>containers</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Container and image vocabulary</title>
      <dc:creator>Kacper Rychel</dc:creator>
      <pubDate>Mon, 03 Apr 2023 10:00:00 +0000</pubDate>
      <link>https://forem.com/zdybit/container-and-image-vocabulary-123k</link>
      <guid>https://forem.com/zdybit/container-and-image-vocabulary-123k</guid>
      <description>&lt;p&gt;&lt;em&gt;Kubernetes is a container orchestration system which is used to manage containers grouped in pods which are hosted on nodes. To do that Kubernetes uses an agent called kubelet which uses a Docker (or another container runtime or container engine) which uses a container runtime (once again?) to manage the container lifecycle.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🛑 HOLD ON!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So much layers, terms, and dependencies here. It is not easy to understand how all these components work together. Especially, when some of terms are used interchangeably.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;😉 Don't worry.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I will clarify those uncertainties in this series. I will take you on a journey to understand the concept of containers and containerization, and their terms one by one. From bottom to top. From the smallest units, which are system resources and process. At the end of the day, they compute and store your data, not Kubernetes.&lt;/p&gt;

&lt;p&gt;In this post I will explain the terms related to containers and images, and a bit of the operating system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🧭 Let's start the journey!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Table of contents&lt;/li&gt;
&lt;li&gt;Terminology on the schema&lt;/li&gt;
&lt;li&gt;
Operating system specific terms

&lt;ul&gt;
&lt;li&gt;process&lt;/li&gt;
&lt;li&gt;resource&lt;/li&gt;
&lt;li&gt;namespace&lt;/li&gt;
&lt;li&gt;cgroups&lt;/li&gt;
&lt;li&gt;SELinux&lt;/li&gt;
&lt;li&gt;AppArmor&lt;/li&gt;
&lt;li&gt;SELinux vs AppArmor&lt;/li&gt;
&lt;li&gt;union mount&lt;/li&gt;
&lt;li&gt;copy-on-write&lt;/li&gt;
&lt;li&gt;overlayFS&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Containers and images specific terms

&lt;ul&gt;
&lt;li&gt;container&lt;/li&gt;
&lt;li&gt;volume&lt;/li&gt;
&lt;li&gt;image&lt;/li&gt;
&lt;li&gt;base image&lt;/li&gt;
&lt;li&gt;parent image&lt;/li&gt;
&lt;li&gt;layer&lt;/li&gt;
&lt;li&gt;blob&lt;/li&gt;
&lt;li&gt;writable layer&lt;/li&gt;
&lt;li&gt;build&lt;/li&gt;
&lt;li&gt;context&lt;/li&gt;
&lt;li&gt;Dockerfile&lt;/li&gt;
&lt;li&gt;Dockerfile instruction&lt;/li&gt;
&lt;li&gt;entrypoint / cmd&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Ending&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Terminology on the schema
&lt;/h2&gt;

&lt;p&gt;I prepared a map 🗺️ for you to not get lost in the terminology journey.&lt;br&gt;
The following schema shows the relationship between the terms.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhay0jmzn22mtwn0tjb5s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhay0jmzn22mtwn0tjb5s.png" alt="schema"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;* Docker is only one of container runtimes and can do much more than presented in the map. I will explain it with details in the next post from this series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Operating system specific terms
&lt;/h2&gt;

&lt;h3&gt;
  
  
  process
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Process_(computing)" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A process of an operating system is an instance of a computer program that is being executed by one or many threads. It is a basic unit of work.&lt;/p&gt;

&lt;h3&gt;
  
  
  resource
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Process_(computing)#Representation" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A resource is a hardware of software component managed by an operating system. Resources are allocated to processes to perform some work.&lt;/p&gt;

&lt;p&gt;Sample Linux resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;net - network interfaces&lt;/li&gt;
&lt;li&gt;mnt - file system mount points&lt;/li&gt;
&lt;li&gt;pid - process ID space&lt;/li&gt;
&lt;li&gt;uts - hostname and NIS domain name&lt;/li&gt;
&lt;li&gt;user - user and group IDs&lt;/li&gt;
&lt;li&gt;ipc - inter-process communication mechanisms.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  namespace
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.nginx.com/blog/what-are-namespaces-cgroups-how-do-they-work/" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A Linux namespace is functionality of the Linux kernel that isolates and virtualizes system resource. Processes can only interact with resources or processes that are part of the same namespace. Namespaces exist for each type of resource.&lt;/p&gt;

&lt;p&gt;Namespaces provide isolation of system resources, and cgroups allow for fine‑grained control and enforcement of limits for those resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  cgroups
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://towardsdatascience.com/the-power-of-linux-cgroups-how-containers-take-control-of-their-resources-ba564fef13b0" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A control group (cgroup) is a Linux kernel feature that limits, accounts for, and isolates the usage of resources by a collection of &lt;a href="https://dev.toprocess"&gt;processes&lt;/a&gt;. Docker relies on &lt;em&gt;cgroups&lt;/em&gt; to control and isolate resource limits.&lt;/p&gt;

&lt;p&gt;In the context of containers, cgroups limits the resources that each container can consume. cgroups ensures the container has the resources it needs to function properly.&lt;/p&gt;

&lt;h3&gt;
  
  
  SELinux
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.redhat.com/en/topics/linux/what-is-selinux" rel="noopener noreferrer"&gt;🔗&lt;/a&gt; &lt;a href="https://www.arhea.net/posts/2020-04-28-selinux-for-containers" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SELinux (Security-Enhanced Linux) defines access controls for the applications, processes, and files on a system. It uses security policies, which are a set of rules that tell SELinux what can or can’t be accessed. When an application or process makes a request to access an object, like a file, SELinux checks with an access vector cache (AVC), where permissions are cached for subjects and objects. If SELinux is unable to make a decision about access based on the cached permissions, it sends the request to the security server. SELinux supports mandatory access control (MAC).&lt;/p&gt;

&lt;p&gt;SELinux is used by containers to control accesses withing them.&lt;/p&gt;

&lt;h3&gt;
  
  
  AppArmor
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.apparmor.net/" rel="noopener noreferrer"&gt;🔗&lt;/a&gt; &lt;a href="https://cloud.google.com/container-optimized-os/docs/how-to/secure-apparmor" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AppArmor (Application Armor) is security module that you can use to restrict the capabilities of processes running on the host operating system. Each process can have its own security profile. The security profile allows or disallows specific capabilities, such as network access or file read/write/execute permissions. It proactively protects the operating system and applications from external or internal threats. AppArmor supplements the traditional Unix discretionary access control (DAC) model by providing mandatory access control (MAC).&lt;/p&gt;

&lt;p&gt;For any given container, you can apply either the default AppArmor security profile, or a custom security profile that you provide.&lt;/p&gt;

&lt;h3&gt;
  
  
  SELinux vs AppArmor
&lt;/h3&gt;

&lt;p&gt;Most popular Linux distributions contain pre-installed one or both of those security modules.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You could have gotten hold of an impression that SELinux and AppArmor were remarkably similar to each other. It is true. Here you can dive into the comparison between them: &lt;a href="https://www.maketecheasier.com/selinux-vs-apparmor/" rel="noopener noreferrer"&gt;https://www.maketecheasier.com/selinux-vs-apparmor/&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  union mount
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Union_mount" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In computer operating systems, union mount is a technique to mount multiple directories into one that appears to contain their combined contents.&lt;/p&gt;

&lt;h3&gt;
  
  
  copy-on-write
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/glossary/" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is a technique used to optimize the performance of file systems by copying of a resource only when it is modified. Non-modified resources are shared between the original and the copy of an operation.&lt;/p&gt;

&lt;h3&gt;
  
  
  overlayFS
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/OverlayFS" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OverlayFS is the union mount implementation included in the Linux kernel since the version 3.18. It implements the copy-on-write technique by overlaying file system layers on top of another one and interacting with the merged result.&lt;/p&gt;

&lt;p&gt;OverlayFS is used by Docker to create images and containers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Containers and images specific terms
&lt;/h2&gt;

&lt;h3&gt;
  
  
  container
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/get-started/" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A container is a running instance of an image. It is a standardized package containing software along with dependencies, configuration, data, and everything that is needed to run.&lt;br&gt;
From the operating system point of view, a container represents an isolated process (or group of processes) that exists in its own namespace. Processes inside the container do not see processes outside it and vice versa. The container cannot access resources belonging to another container or process resources outside the container.&lt;/p&gt;

&lt;h3&gt;
  
  
  volume
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/storage/volumes/" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A volume is a directory on a host machine that is mounted into one or more containers. It is used to store persistent data outside of the container. It is independent of the container lifecycle. There are three types of Docker volumes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;host volume&lt;/strong&gt; - lives on the Docker host file system and can be accessed from within the container&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;named volume&lt;/strong&gt; - is managed by Docker, where it is created on disk, but is given a name&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;anonymous volume&lt;/strong&gt; - is similar to the named volume, but it can be difficult to reference the same volume over time; the volume is managed by Docker, where the files are stored.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  image
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/get-started/" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An image is a read-only template that is used to create a container. It contains an ordered collection of changes to the root file system and the corresponding execution parameters for use at &lt;strong&gt;container runtime&lt;/strong&gt;. The change of the file system is called a layer. They are stacked on top of each other starting from a base image or from scratch.&lt;/p&gt;

&lt;p&gt;An image is built using a Dockerfile.&lt;/p&gt;

&lt;h3&gt;
  
  
  base image
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/glossary/" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A base image does not have a specified parent image in its Dockerfile. It is created using a Dockerfile with the &lt;code&gt;FROM scratch&lt;/code&gt; directive.&lt;/p&gt;

&lt;p&gt;The base image is usually the latest version of an operating system. It is usually a minimal image, which contains only the operating system and few basic tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  parent image
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/glossary/" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A parent image is an image that is used by another. It is specified in the &lt;code&gt;FROM&lt;/code&gt; directive in a child Dockerfile.&lt;/p&gt;

&lt;h3&gt;
  
  
  layer
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/glossary/" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A layer is a modification of an image, represented by a Dockerfile instruction. To create the final image, layers are sequentially applied to the parent image or to the root. When the image is updated or rebuilt, only changed layers need to be updated. Unchanged layers are cached locally to be reused.&lt;/p&gt;

&lt;h3&gt;
  
  
  blob
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/registry/spec/api/" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A blob is an immutable binary large object. Blobs are used to store content of the image layers.&lt;/p&gt;

&lt;h3&gt;
  
  
  writable layer
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/storage/storagedriver/#container-and-layers/" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When a container is created, a writable layer is created on top of the image. It is a layer that can be modified by the container to store changes made to the container. The writable layer is discarded when the container is deleted.&lt;/p&gt;

&lt;h3&gt;
  
  
  build
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dev.to/thakkaryash94/how-many-ways-to-build-a-container-image-4g3p"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The process of building a Docker image using a Dockerfile and a context.&lt;/p&gt;

&lt;p&gt;Sample tools that implement the build process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/engine/reference/commandline/build/" rel="noopener noreferrer"&gt;docker build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/containers/buildah" rel="noopener noreferrer"&gt;buildah&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/genuinetools/img" rel="noopener noreferrer"&gt;img&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GoogleContainerTools/kaniko" rel="noopener noreferrer"&gt;kaniko&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://podman.io/" rel="noopener noreferrer"&gt;podman&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  context
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/engine/reference/commandline/build/" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Build’s context is a set of files located in the specified &lt;code&gt;PATH&lt;/code&gt; or &lt;code&gt;URL&lt;/code&gt;. The build process of an image can refer to any files in the context. For example, your build can use the Dockerfile &lt;code&gt;COPY&lt;/code&gt; instruction to reference the file in the context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dockerfile
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/engine/reference/builder/" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dockerfile is a text file that contains instructions for &lt;a href="https://dev.tobuild"&gt;building&lt;/a&gt; an image in the order that they are written in the file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dockerfile instruction
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/engine/reference/builder/" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A Dockerfile instruction is a command that is executed during the build process. The instructions are executed in the order they are written in the Dockerfile. An instruction can be: &lt;code&gt;FROM&lt;/code&gt;, &lt;code&gt;RUN&lt;/code&gt;, &lt;code&gt;CMD&lt;/code&gt;, &lt;code&gt;LABEL&lt;/code&gt;, &lt;code&gt;EXPOSE&lt;/code&gt;, &lt;code&gt;ENV&lt;/code&gt;, &lt;code&gt;ADD&lt;/code&gt;, &lt;code&gt;COPY&lt;/code&gt;, &lt;code&gt;ENTRYPOINT&lt;/code&gt;, &lt;code&gt;VOLUME&lt;/code&gt;, &lt;code&gt;USER&lt;/code&gt;, &lt;code&gt;WORKDIR&lt;/code&gt;, &lt;code&gt;ARG&lt;/code&gt;, &lt;code&gt;ONBUILD&lt;/code&gt;, &lt;code&gt;STOPSIGNAL&lt;/code&gt;, &lt;code&gt;HEALTHCHECK&lt;/code&gt;, &lt;code&gt;SHELL&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  entrypoint / cmd
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/engine/reference/builder/#entrypoint" rel="noopener noreferrer"&gt;🔗&lt;/a&gt; &lt;a href="https://docs.docker.com/engine/reference/builder/#cmd" rel="noopener noreferrer"&gt;🔗&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want your Dockerfile to be able to run without passing additional arguments to the &lt;code&gt;docker run&lt;/code&gt; command, you must specify &lt;code&gt;ENTRYPOINT&lt;/code&gt;, &lt;code&gt;CMD&lt;/code&gt; &lt;a href="https://dev.toinstructions"&gt;instructions&lt;/a&gt; of both.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;CMD&lt;/code&gt; is a statement that is run by default when the container is started, but can be easily overridden if the container is started with a command.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ENTRYPOINT&lt;/code&gt; is set to a single command. Even if you don't specify &lt;code&gt;ENTRYPOINT&lt;/code&gt;, it can be inherited from a parent image. It is not overridden even if the container is started with a command. To override &lt;code&gt;ENTRYPOINT&lt;/code&gt; you can use the &lt;code&gt;--entrypoint&lt;/code&gt; option of the &lt;code&gt;docker build&lt;/code&gt; command.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The instructions are reduced to run the command according to the pattern: &lt;code&gt;&amp;lt;ENTRYPOINT&amp;gt; &amp;lt;CMD&amp;gt;&lt;/code&gt;. Therefore, &lt;code&gt;CMD&lt;/code&gt; can be used to pass default arguments for the command in &lt;code&gt;ENTRYPOINT&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ending
&lt;/h2&gt;

&lt;p&gt;Pretty much terms you just learned, aren't they? I gathered them from different sources for you to have a quick reference.&lt;/p&gt;

&lt;p&gt;The list is not complete, but it should be enough for you to jump into the world of Docker right now. You've just learned terminology of container components.&lt;/p&gt;

&lt;p&gt;Next time I will show you what kind of tools manage them. You will get know what is and what isn't a Docker.&lt;br&gt;
What is the difference between a container engine, a container runtime, and Docker Engine? How does Kubernetes interact with Docker? Does it even use Docker?&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this article. If you found it helpful or just liked it, share it with your friends.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>beginners</category>
      <category>devops</category>
      <category>programming</category>
    </item>
    <item>
      <title>Merge, squash &amp; rebase on GitHub - pros &amp; cons</title>
      <dc:creator>Kacper Rychel</dc:creator>
      <pubDate>Wed, 15 Mar 2023 23:05:00 +0000</pubDate>
      <link>https://forem.com/zdybit/when-to-use-particular-options-to-close-pull-requests-on-github-3ce8</link>
      <guid>https://forem.com/zdybit/when-to-use-particular-options-to-close-pull-requests-on-github-3ce8</guid>
      <description>&lt;p&gt;In the previous post, we got to know three ways to close pull requests on GitHub. Their different outcomes for the git history are more accurate for specific scenarios and branching strategies than others. All the closing options have their pros* and cons*. Let’s focus on them in this post and learn how to choose the closing options. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Catches explanation&lt;/strong&gt; &lt;br&gt;
Even if I list some content under ✅ &amp;amp; ❌ bullet points below, it doesn't mean ✅s are always good and ❌s are always bad. You can consider them more like desired outcomes, and their consequences we need to be aware of.&lt;br&gt;
So yes, the pros and cons phrase here is kind of clickbait 😎&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Table of content
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Table of content&lt;/li&gt;
&lt;li&gt;
Pros and cons

&lt;ul&gt;
&lt;li&gt;Create a merge commit&lt;/li&gt;
&lt;li&gt;Squash and merge&lt;/li&gt;
&lt;li&gt;
Rebase and merge

&lt;ul&gt;
&lt;li&gt;Clean your feature&lt;/li&gt;
&lt;li&gt;GitHub rebase&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;A little bit of practice&lt;/li&gt;

&lt;li&gt;Summary&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pros and cons
&lt;/h2&gt;

&lt;p&gt;Pardon. Desired outcomes &amp;amp; consequences.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a merge commit
&lt;/h3&gt;

&lt;p&gt;Since the &lt;code&gt;Create a merge commit&lt;/code&gt; option keeps all commits and branches, it is the easiest way to synchronize branches with each other in both directions. Conflicts between branches, of course, can appear. They are resolved within a new &lt;em&gt;merge commit&lt;/em&gt;, so no changes should be lost. It is as easy as it can be, but this easiness can be ugly and messy when we look at the git history.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;So, what exactly is wrong with the simple and easy merge?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;❌ It generates a new commit which does not provide any meaningful changes. It just merges the history of both branches. Finally, having two branches and the extra commit, the result is not the cleanest we could imagine. Even if a team (or an automated rule) removes all &lt;em&gt;feature&lt;/em&gt; branches right after their pull requests are closed, the history of the &lt;em&gt;feature&lt;/em&gt; branches remains, and the entire history looks like rails in the train barn.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2oyhftpxdgoim7c8ozt1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2oyhftpxdgoim7c8ozt1.png" alt="Rails in the train barn"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;❌ Bad commit messages are the second awful thing in the "simple merge". We often don't bother about quality of them while we are working on our &lt;em&gt;feature&lt;/em&gt; branches (I will call such commits &lt;strong&gt;&lt;em&gt;working commits&lt;/em&gt;&lt;/strong&gt; below). It is fine unless such commits are not merged into a &lt;em&gt;long-living&lt;/em&gt; branch, like &lt;em&gt;main&lt;/em&gt; or &lt;em&gt;develop&lt;/em&gt;. Unfortunately, they are merged too often, and they stay there forever.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsk7vtg3qemo3vuwjpdlm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsk7vtg3qemo3vuwjpdlm.png" alt="Commit messages mess"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is true that bad commit messages are a concern regardless of the way of the pull request closing, but the &lt;code&gt;merge&lt;/code&gt; and &lt;code&gt;rebase&lt;/code&gt; options preserve all commits in &lt;em&gt;long-living&lt;/em&gt; branches.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt; 💡 &lt;br&gt;
I heartily recommend reading the article about &lt;a href="https://cbea.ms/git-commit/" rel="noopener noreferrer"&gt;How to Write a Git Commit Message&lt;/a&gt;. &lt;br&gt;
The approach, described in the article, makes my commits more descriptive and enforces me to think over what I commit. Even when I am still working on my &lt;em&gt;feature&lt;/em&gt; branch with my &lt;em&gt;working commits&lt;/em&gt;. It taught me discipline.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;❌ Even if all contributors wrote really good commit messages, &lt;em&gt;merge commits&lt;/em&gt; would still be interspersed with those from deleted (or not yet) &lt;em&gt;feature&lt;/em&gt; branches. The commits from the different branches can be even more mixed together in the flatten view of the history on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa3yvajmbzfe1ergta7bi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa3yvajmbzfe1ergta7bi.png" alt="Mixed commits from different branches"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is tough to recognize which ones belong to which branches or tasks unless we study them in the full git tree.&lt;/p&gt;

&lt;p&gt;✅ On the other hand, &lt;em&gt;merge&lt;/em&gt; option preserves the entire history if you really need to have such.&lt;/p&gt;

&lt;p&gt;✅ The most important benefit of the &lt;em&gt;merge&lt;/em&gt; option is that it does not rewrite the history. Rewriting history is especially annoying when you need to compare two branches with the same changes from the same commits, but when the commits have been rewritten.&lt;/p&gt;

&lt;p&gt;✅ You can create a next &lt;em&gt;merge commit&lt;/em&gt; from the same &lt;em&gt;source&lt;/em&gt; branch with new commits to the same &lt;em&gt;target&lt;/em&gt; branch (e.g. from &lt;code&gt;develop&lt;/code&gt; to &lt;code&gt;main&lt;/code&gt;). Changes already merged before are not compared once again.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;CONCLUSION&lt;/strong&gt; 👁️‍🗨️ &lt;br&gt;
The &lt;code&gt;Create a merge commit&lt;/code&gt; option is the best option for merging code between two long-living branches.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Squash and merge
&lt;/h3&gt;

&lt;p&gt;Using the &lt;code&gt;Squash and merge&lt;/code&gt; option, we can simply rephrase our entire work that we did on a &lt;em&gt;feature&lt;/em&gt; branch. That way we get rid of multiple &lt;em&gt;working commits&lt;/em&gt; with not always meaningful messages. The outcome is one elegant commit.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt; 💡 &lt;br&gt;
Such commit message (according to &lt;a href="https://cbea.ms/git-commit/" rel="noopener noreferrer"&gt;How to Write a Git...&lt;/a&gt;) should contain a short descriptive title (&lt;em&gt;what&lt;/em&gt;), an optional body explaining changes (&lt;em&gt;why&lt;/em&gt;, not &lt;del&gt;how&lt;/del&gt;), and a reference to a task ID and/or a pull request.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;But what if there was so much to describe and so many changes to compare in one commit? Would it still be elegant?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First things first, when there is too much on commit's plate, it is the forceful signal to distribute changes to multiple commits or/and to split a task into smaller tasks or subtasks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt; 💡 &lt;br&gt;
A suitable candidate, what we can extract to a separate subtask, is refactor. If your team collects more and more refactoring tasks, I recommend reading the great article about &lt;a href="https://dev.to/adambrandizzi/billing-the-technical-debt-45kb"&gt;A smooth way to pay your technical debt&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;❎ As we rephrase our work during the &lt;em&gt;squash&lt;/em&gt; of &lt;em&gt;working commits&lt;/em&gt;, we are more likely to catch whether we have made too many changes than during the simple &lt;code&gt;Create a merge commit&lt;/code&gt; option.&lt;/p&gt;

&lt;p&gt;To give the devil his due, if we fill the pull request descriptions honestly, the chances to catch it will be as high. And even &lt;em&gt;squashes&lt;/em&gt; can be made inattentively committing only default messages.&lt;/p&gt;

&lt;p&gt;I call taking care of writing good commit descriptions a &lt;strong&gt;&lt;em&gt;git hygiene&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt; 💡 &lt;br&gt;
Fill pull requests fields honestly as well. &lt;br&gt;
First, it is a message for your colleagues reviewing your job. &lt;br&gt;
Second, default commit messages on GitHub can be generated from the pull request description and title.&lt;/p&gt;

&lt;p&gt;Write descriptive messages of every commit which is supposed to be a part of the pull request. &lt;br&gt;
Exactly for the same reason.&lt;/p&gt;

&lt;p&gt;I described default messages generated by GitHub in the previous post &lt;u&gt;Default messages of &lt;a href="https://dev.to/zdybit/3-options-to-close-pull-requests-on-github-what-2j3n#default-messages-of-merge"&gt;merge&lt;/a&gt; and &lt;a href="https://dev.to/zdybit/3-options-to-close-pull-requests-on-github-what-2j3n#default-messages-of-squash"&gt;squash&lt;/a&gt;&lt;/u&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;End of digressions...&lt;/em&gt; &lt;br&gt;
&lt;em&gt;... for now&lt;/em&gt; 😉&lt;/p&gt;

&lt;p&gt;❌ There is a risk of losing precious information from the git history when a &lt;em&gt;feature&lt;/em&gt; branch is removed after it is &lt;em&gt;squashed&lt;/em&gt; to a &lt;em&gt;target&lt;/em&gt; branch. We need to carefully read messages we want to rephrase.&lt;/p&gt;

&lt;p&gt;❌ Have you ever tried to &lt;em&gt;squash&lt;/em&gt; the same branch twice?&lt;/p&gt;

&lt;p&gt;Let's see the scenario when the &lt;code&gt;71670e04&lt;/code&gt; commit from the &lt;code&gt;feature/7&lt;/code&gt; has been &lt;em&gt;squashed&lt;/em&gt; into the &lt;code&gt;main&lt;/code&gt; (&lt;code&gt;f489ebfc&lt;/code&gt;), but we pushed another commit into the &lt;code&gt;feature/7&lt;/code&gt; (&lt;code&gt;f4c25692&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffo184x550gg655f2ugzx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffo184x550gg655f2ugzx.png" alt="A Git tree before the squash"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We want to &lt;em&gt;squash&lt;/em&gt; the new commit to the &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmtf4lav8a1oziwzql05v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmtf4lav8a1oziwzql05v.png" alt="The conflict view in VS Code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes, there is a conflict, even if a content has been added to the end of the file in the &lt;code&gt;feature/7&lt;/code&gt; and nothing has been changed in the &lt;code&gt;main&lt;/code&gt;. Nobody touched the &lt;code&gt;main&lt;/code&gt; after our last &lt;em&gt;squash&lt;/em&gt; to it. The conflict occurred due to lack of relation between those two branches, so git doesn’t know which change precedes.&lt;/p&gt;

&lt;p&gt;Let's take a look what will happen on GitHub when we raise a pull request for the &lt;code&gt;feature/7&lt;/code&gt; :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpszwml5m9wbzc52socz9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpszwml5m9wbzc52socz9.png" alt="A conflict in the pull request"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are our expected conflict and two commits to merge.&lt;/p&gt;

&lt;p&gt;Wait! What? Why are there two commits when the first of them has been &lt;em&gt;squashed&lt;/em&gt; into the &lt;code&gt;main&lt;/code&gt; already?&lt;/p&gt;

&lt;p&gt;What's more, our conflict is even more misleading on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwtvldpanihglzbpyiufj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwtvldpanihglzbpyiufj.png" alt="The conflict view on GitHub"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This all happens due to the &lt;code&gt;feature/7&lt;/code&gt; and the &lt;code&gt;main&lt;/code&gt; have nothing in common after the &lt;code&gt;93eda5e0&lt;/code&gt; commit. Please remember.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;CONCLUSION&lt;/strong&gt; 👁️‍🗨️ &lt;br&gt;
The &lt;code&gt;Squash and merge&lt;/code&gt; option is not suitable for using with two &lt;em&gt;long-living&lt;/em&gt; branches. &lt;br&gt;
It is the great option to close pull requests of &lt;em&gt;feature&lt;/em&gt; branches.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Does it mean &lt;code&gt;Squash and merge&lt;/code&gt; is a bad option?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not at all.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;✅✅ Thanks to the &lt;em&gt;squash&lt;/em&gt;, we can keep the git history spotless and readable. There are no &lt;em&gt;merge commits&lt;/em&gt; which tell us nothing meaningful. Especially when all &lt;em&gt;feature&lt;/em&gt; branches are removed after their changes are put into a &lt;em&gt;target&lt;/em&gt; branch. We just need to maintain the &lt;em&gt;git hygiene&lt;/em&gt; 😷 and not forget about references in the commit messages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Falxj7stevunjprkqvugs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Falxj7stevunjprkqvugs.png" alt="Squashed commits in the GH history"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsn6owtnagbjqyb47d7zn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsn6owtnagbjqyb47d7zn.png" alt="Squashed commits in the git tree"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is much more readable now than when we merged it with the &lt;em&gt;merge commits&lt;/em&gt;. We know immediately what and when specific changes have been added to the &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;abde78a2&lt;/code&gt; commit squashed all commits from the &lt;code&gt;feature/10&lt;/code&gt;, both the &lt;em&gt;working commits&lt;/em&gt; and the &lt;em&gt;merge commits&lt;/em&gt;. If we wanted to keep the commits from the &lt;code&gt;feature/9&lt;/code&gt;, it would be better to &lt;em&gt;squash&lt;/em&gt; them separately into the &lt;code&gt;main&lt;/code&gt; and resolve potential conflicts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rebase and merge
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Clean your feature
&lt;/h4&gt;

&lt;p&gt;Alternatively, we could drop the &lt;em&gt;merge commits&lt;/em&gt; by &lt;em&gt;rebasing&lt;/em&gt; the &lt;code&gt;feature/10&lt;/code&gt; locally before we create a pull request and &lt;em&gt;rebase&lt;/em&gt; new clean commits into the &lt;code&gt;main&lt;/code&gt; by the &lt;code&gt;Rebase and merge&lt;/code&gt; closing option of the pull request. Even if we planed to close the pull request with &lt;code&gt;Create a merge commit&lt;/code&gt; option, we still could &lt;em&gt;rebase&lt;/em&gt; all &lt;em&gt;working commits&lt;/em&gt; locally to keep only one well described commit.&lt;/p&gt;

&lt;p&gt;✅✅ Rebase is a great tool to clean our &lt;em&gt;feature&lt;/em&gt; branch before we raise a pull request. Such rebasing on local is pretty complex and offers many options, but it brings great effects as a final point. It allows us to shape our &lt;em&gt;feature&lt;/em&gt; branches to the state we are proud to share within a pull request.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt; 💡 &lt;br&gt;
You can learn more about the most commonly used git commands with the great article: &lt;a href="https://dev.to/lydiahallie/cs-visualized-useful-git-commands-37p1"&gt;🌳🚀 CS Visualized: Useful Git Commands&lt;/a&gt;. You can also find the explanation with visualizations for &lt;strong&gt;Interactive Rebase&lt;/strong&gt; and &lt;strong&gt;Resetting&lt;/strong&gt; what help you to clean a &lt;em&gt;feature&lt;/em&gt; up.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  GitHub rebase
&lt;/h4&gt;

&lt;p&gt;Despite the fact the previous section does not deal with the post topic directly, it still is important. Especially when we are going to close our pull request with the &lt;code&gt;Rebase and merge&lt;/code&gt; option. All commits from a &lt;em&gt;feature&lt;/em&gt; branch are copied into &lt;em&gt;the main&lt;/em&gt;. And then it is too late to clean up the commits.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;WARNING&lt;/strong&gt; ⚠️ &lt;br&gt;
&lt;strong&gt;Never &lt;code&gt;git rebase&lt;/code&gt; on public branches, like &lt;code&gt;main&lt;/code&gt; or &lt;code&gt;develop&lt;/code&gt;.&lt;/strong&gt; &lt;br&gt;
When somebody had created its &lt;em&gt;feature&lt;/em&gt; branch from a commit that has been removed from the &lt;code&gt;origin main&lt;/code&gt;, the &lt;em&gt;feature&lt;/em&gt; branch is cut off from the &lt;code&gt;origin main&lt;/code&gt; as well. The removed commits exist only on the &lt;code&gt;local main&lt;/code&gt;. All of them will be considered as new commits during a pull request to the &lt;code&gt;origin main&lt;/code&gt;. And that brings vast number of conflicts! Even if such commits contain the same messages and changes, they have the different signatures than the original commits.&lt;/p&gt;

&lt;p&gt;Rebasing public branches causes huge trouble to clean the &lt;em&gt;origin&lt;/em&gt; up and continue working with the same common code base.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Ok then. Rebase is difficult and dangerous. So, should we avoid using it?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not really. I strongly encourage learning &lt;code&gt;git rebase&lt;/code&gt;, use it locally, and use GitHub's &lt;code&gt;Rebase and merge&lt;/code&gt; option when necessary.&lt;/p&gt;

&lt;p&gt;✅ &lt;code&gt;Rebase and merge&lt;/code&gt; option is pretty similar to the &lt;code&gt;Squash and merge&lt;/code&gt; one. It helps us to keep the git history clean and readable without &lt;em&gt;merge commits&lt;/em&gt;. The difference is, &lt;em&gt;rebase&lt;/em&gt; can add more than one commit into &lt;em&gt;the main&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We just need to maintain the &lt;em&gt;git hygiene&lt;/em&gt; and do not forget about references in the commit messages.&lt;/p&gt;

&lt;p&gt;❌ This time, in contrast to the &lt;em&gt;squash&lt;/em&gt; option, we need to remember adding the references to the messages of &lt;em&gt;feature&lt;/em&gt; commits before we can close a pull request. There is no place to edit commit messages within the pull request, like with the &lt;em&gt;squash&lt;/em&gt; option where it is possible.&lt;/p&gt;

&lt;p&gt;❌ Since commits are copied to a &lt;em&gt;target&lt;/em&gt; branch, there is no relation between the &lt;em&gt;source&lt;/em&gt; and the &lt;em&gt;target&lt;/em&gt; branches. It raises the possibility of having conflicts between changes which are already existing on the both branches. The same case as with the &lt;em&gt;squash&lt;/em&gt; option.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;CONCLUSION&lt;/strong&gt; 👁️‍🗨️ &lt;br&gt;
The &lt;code&gt;Rebase and merge&lt;/code&gt; option is not suitable for using with two &lt;em&gt;long-living&lt;/em&gt; branches. &lt;br&gt;
It is the good option to close pull requests of &lt;em&gt;feature&lt;/em&gt; branches when you want to keep multiple commits.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A little bit of practice
&lt;/h2&gt;

&lt;p&gt;Probably, the most commonly used branching strategy is &lt;a href="https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow" rel="noopener noreferrer"&gt;Gitflow workflow&lt;/a&gt;. It is also one of the most complex strategy. So, let's try to study such case.&lt;/p&gt;

&lt;p&gt;As we have learned already, the &lt;strong&gt;merge&lt;/strong&gt; option is suitable to multiple long-live branches like &lt;code&gt;main&lt;/code&gt; and &lt;code&gt;develop&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On the other hand, the &lt;strong&gt;squash&lt;/strong&gt; and the &lt;strong&gt;rebase&lt;/strong&gt; options make an excellent job when we want to move changes from a branch which is about to be deleted right after its pull request is closed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fry8w1hizgeifbkf4vgfh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fry8w1hizgeifbkf4vgfh.png" alt="Gitflow strategy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the picture above, the &lt;code&gt;hotfix&lt;/code&gt; is &lt;strong&gt;merged&lt;/strong&gt; into both the &lt;code&gt;main&lt;/code&gt; (1) and the &lt;code&gt;develop&lt;/code&gt; (2). If the &lt;code&gt;hotfix&lt;/code&gt; were &lt;strong&gt;squashed&lt;/strong&gt; or &lt;strong&gt;rebased&lt;/strong&gt;, the same change from the &lt;code&gt;hotfix&lt;/code&gt; would be a conflict to itself in the pull request from the &lt;code&gt;release&lt;/code&gt; to the &lt;code&gt;main&lt;/code&gt; (3).&lt;/p&gt;

&lt;p&gt;On the other hand, let see at the &lt;code&gt;develop&lt;/code&gt; and &lt;code&gt;feature&lt;/code&gt; branches (4). Imagine there are even more &lt;code&gt;feature&lt;/code&gt; branches developers are working on in parallel. So, what can we do to avoid the rails in the train barn between the &lt;code&gt;develop&lt;/code&gt; and &lt;code&gt;feature&lt;/code&gt;s?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;First thing, keep &lt;code&gt;feature&lt;/code&gt;s clean and write good commit messages.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;STATEMENT&lt;/strong&gt; &lt;br&gt;
I know I keep talking about writing good commit messages over and over. I do because they are really that important, and very often neglected by developers. We all are aware of the quality of code and documentation.&lt;br&gt;
And what are commit messages if not the code and the documentation?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The second thing, we can &lt;strong&gt;squash&lt;/strong&gt; &lt;code&gt;feature&lt;/code&gt;s into the &lt;code&gt;main&lt;/code&gt;, or &lt;strong&gt;rebase&lt;/strong&gt; them if we would like to keep multiple commits of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;We've just learnt more about the outcomes of the options to close pull requests on GitHub, and what consequences they raise. Now, we can handle our pull requests more effectively and wisely. We are not afraid of other options than &lt;code&gt;Create a merge commit&lt;/code&gt; anymore.&lt;/p&gt;

&lt;p&gt;In the series we learnt &lt;strong&gt;what&lt;/strong&gt; we could use, and &lt;strong&gt;when&lt;/strong&gt; we would have used them.&lt;br&gt;
In the next post we will get to know &lt;strong&gt;how&lt;/strong&gt; GitHub uses git under the hood to give us the discussed outcomes. We will dive a little bit into git commands.&lt;/p&gt;

</description>
      <category>github</category>
      <category>git</category>
      <category>tutorial</category>
      <category>devops</category>
    </item>
    <item>
      <title>Options to close pull requests on GitHub</title>
      <dc:creator>Kacper Rychel</dc:creator>
      <pubDate>Sun, 05 Mar 2023 16:35:28 +0000</pubDate>
      <link>https://forem.com/zdybit/3-options-to-close-pull-requests-on-github-what-2j3n</link>
      <guid>https://forem.com/zdybit/3-options-to-close-pull-requests-on-github-what-2j3n</guid>
      <description>&lt;p&gt;One of the common practices of contributing with a developer's solution to a common main code base is Pull Requests.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Are pull requests good practice? Dave Farley doubts it on: &lt;a href="https://www.youtube.com/watch?v=ASOSEiJCyEM"&gt;Why Pull Requests Are A BAD IDEA&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Table of content
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Table of content&lt;/li&gt;
&lt;li&gt;Pull Requests on GitHub&lt;/li&gt;
&lt;li&gt;
Create a merge commit

&lt;ul&gt;
&lt;li&gt;Merge in practice&lt;/li&gt;
&lt;li&gt;Default messages of merge&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Squash and merge

&lt;ul&gt;
&lt;li&gt;Squash in practice&lt;/li&gt;
&lt;li&gt;Default messages of squash&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Rebase and merge

&lt;ul&gt;
&lt;li&gt;Rebase in practice&lt;/li&gt;
&lt;li&gt;Messages of rebase&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pull Requests on GitHub
&lt;/h2&gt;

&lt;p&gt;On GitHub, you can choose up to 3 ways of closing them, by adding the code to the main branch. You can choose which ways are allowed for a given repository in its &lt;em&gt;Setting&lt;/em&gt; &amp;gt; &lt;em&gt;General&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--su_1TKQ7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ndul109tjulom1wowwsb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--su_1TKQ7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ndul109tjulom1wowwsb.png" alt="pull requests options in settings" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And this is how it looks like in a pull request:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---MkBRwxy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yp9ctfk4jgtrwvs0sp5l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---MkBRwxy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yp9ctfk4jgtrwvs0sp5l.png" alt="pull requests options in pr" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we can see, &lt;strong&gt;merge&lt;/strong&gt;, &lt;strong&gt;squash&lt;/strong&gt;, and &lt;strong&gt;rebase&lt;/strong&gt; are explained in the GitHub repository settings and also in pull requests. So, we can close the topic, can't we?&lt;/p&gt;

&lt;p&gt;Not just yet. Those commits types are a little bit more complex. You can read more about that in the &lt;a href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges"&gt;GitHub documentation&lt;/a&gt;. In this post, however, we will go into more details to understand how it looks in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;u&gt;Create a merge commit&lt;/u&gt;
&lt;/h2&gt;

&lt;p&gt;he default way of closing a pull request on GitHub is &lt;em&gt;merge&lt;/em&gt;. The &lt;a href="https://git-scm.com/docs/git-merge"&gt;git documentation&lt;/a&gt; says about it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Incorporates changes from the named commits [...] into the current branch.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What &lt;em&gt;Incorporates&lt;/em&gt; mean, it is easier to show than describe, so let's see.&lt;/p&gt;

&lt;h3&gt;
  
  
  Merge in practice
&lt;/h3&gt;

&lt;p&gt;After &lt;strong&gt;Create a merge commit&lt;/strong&gt; button is pressed, the input fields appear. We can edit a merge commit message here, it is: its title and its body.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xkWu6zST--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/08rw2rydlqmc4ke4ctbm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xkWu6zST--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/08rw2rydlqmc4ke4ctbm.png" alt="merge message" width="800" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the result of the &lt;strong&gt;Merge pull request&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o9lXqplO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q2mid3hr3bpw3fqws865.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o9lXqplO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q2mid3hr3bpw3fqws865.png" alt="git tree after merge" width="793" height="133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The changes from the &lt;code&gt;feature&lt;/code&gt; have been merged with a new merge commit. Now &lt;code&gt;007a0485&lt;/code&gt; and &lt;code&gt;0f830a20&lt;/code&gt; commits are a part of the &lt;code&gt;main&lt;/code&gt;, but still only on a side branch.&lt;/p&gt;

&lt;p&gt;Now, we can delete the &lt;code&gt;feature&lt;/code&gt; from GitHub since deleting feature branches is a good practice. Keep in mind, that branches removed from the origin by GitHub are not removed from your local.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yb9AfBJe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5ta80v1q83l9ome095wc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yb9AfBJe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5ta80v1q83l9ome095wc.png" alt="remove branch after merge" width="687" height="75"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VjlJPRTk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/suxfq4km2jn1mhx24o7b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VjlJPRTk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/suxfq4km2jn1mhx24o7b.png" alt="git tree after merge and removal" width="793" height="133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt; 💡&lt;br&gt;
Do not call your branches just &lt;code&gt;feature&lt;/code&gt; or &lt;code&gt;release&lt;/code&gt;. Such feature causes troubles with e.g. feature/whatever later.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Default messages of merge
&lt;/h3&gt;

&lt;p&gt;GitHub generates a default commit message depending on a chosen option in repository &lt;em&gt;Setting&lt;/em&gt; &amp;gt; &lt;em&gt;General&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gmKMOiyJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q7eephnyd4ezhwnb5d9k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gmKMOiyJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q7eephnyd4ezhwnb5d9k.png" alt="merge default messages" width="769" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Default message&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;default message&lt;/em&gt; generates a commit title with the pattern: &lt;code&gt;Merge pull request #&amp;lt;PR number&amp;gt; from &amp;lt;source branch&amp;gt;&lt;/code&gt;, where &lt;code&gt;#&amp;lt;PR number&amp;gt;&lt;/code&gt; is a reference to the current pull request (&lt;code&gt;#2&lt;/code&gt; in the sample above). From Git point of view, it is just a plain text, we can put it to a message even locally. GitHub resolves each &lt;code&gt;#&amp;lt;number&amp;gt;&lt;/code&gt; syntax to a link to a pull request of a given repository, even if such a pull request doesn't exist.&lt;/p&gt;

&lt;p&gt;The body is consisted of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;merged commit's message - in case of only one commit to merge&lt;/li&gt;
&lt;li&gt;PR's title - in case of multiple commits.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Default to pull request title&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Only the title of the commit is generated with the pattern &lt;code&gt;&amp;lt;PR title&amp;gt; (#&amp;lt;PR number&amp;gt;)&lt;/code&gt;. The body is empty.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Default to pull request title and description&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In addition to &lt;em&gt;the Default to pull request title&lt;/em&gt;, the commit body is copied from the PR's description.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;u&gt;Squash and merge&lt;/u&gt;
&lt;/h2&gt;

&lt;p&gt;The second method of closing pull requests on GitHub is &lt;strong&gt;squash&lt;/strong&gt;. According to the &lt;a href="https://git-scm.com/docs/git-merge#Documentation/git-merge.txt---squash"&gt;Git documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Produce the working tree and index state as if a real merge happened [...]. This allows you to create a single commit on top of the current branch, whose effect is the same as merging another branch.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Squash in practice
&lt;/h3&gt;

&lt;p&gt;This is the result of &lt;strong&gt;Squash and merge&lt;/strong&gt; option on GitHub:&lt;/p&gt;

&lt;p&gt;Before:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---ePylUcI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/koh5rxd0fh2wvblecitu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---ePylUcI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/koh5rxd0fh2wvblecitu.png" alt="git tree before squash" width="796" height="114"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QmTCQvUk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/260e8purl76j1bl3pgqc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QmTCQvUk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/260e8purl76j1bl3pgqc.png" alt="git tree after squash" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What happened here?&lt;/p&gt;

&lt;p&gt;There is a new commit pushed to the &lt;code&gt;main&lt;/code&gt; with the changes from &lt;code&gt;feature/6&lt;/code&gt; branch, but both &lt;code&gt;main&lt;/code&gt; and &lt;code&gt;feature/6&lt;/code&gt; branches are not related to each other. It looks like a merge of another branch than &lt;code&gt;feature/6&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After the deletion of &lt;code&gt;feature/6&lt;/code&gt; from both remote and local, the history looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--STi-va8u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ieer39bte6vv2al0jgal.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--STi-va8u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ieer39bte6vv2al0jgal.png" alt="git tree after squash and removal" width="641" height="84"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is no sign of &lt;code&gt;feature/6&lt;/code&gt; at all. The only trace kept is the reference to a pull request (&lt;code&gt;#3&lt;/code&gt;), so pay attention to keeping it in commit messages.&lt;/p&gt;

&lt;p&gt;Such result is called &lt;strong&gt;&lt;em&gt;linear history&lt;/em&gt;&lt;/strong&gt;. GitHub allows us to enforce such history for given branches as their &lt;strong&gt;Branch protection rule&lt;/strong&gt;. You can read more about it in the documentation: &lt;a href="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/about-protected-branches#require-linear-history"&gt;About protected branches &amp;gt; Require linear history&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Default messages of squash
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Squash and merge&lt;/em&gt; option generates default messages as well, and the same as in &lt;em&gt;Merge pull request&lt;/em&gt; option.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7h4ZWEmY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bjneq5ecqkzr8kq77glz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7h4ZWEmY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bjneq5ecqkzr8kq77glz.png" alt="squash default messages" width="508" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Default message&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;default message&lt;/em&gt; generates a commit depending on a count of commits to squash and merge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;1 commit&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the title pattern: &lt;code&gt;&amp;lt;commit title&amp;gt; (#&amp;lt;PR number&amp;gt;)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;the body is copied from the commit body&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;multiple commits&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the title pattern: &lt;code&gt;&amp;lt;pull request title&amp;gt; (#&amp;lt;PR number&amp;gt;)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;the body is a list of commits' entire messages&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Default to pull request title&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The same as in &lt;em&gt;merge&lt;/em&gt;'s &lt;em&gt;Default to pull request title&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Default to pull request title and commit details&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The same as the &lt;em&gt;multiple commits&lt;/em&gt; case for &lt;em&gt;default message&lt;/em&gt; of &lt;em&gt;squash&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Default to pull request title and description&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The same as in &lt;em&gt;merge&lt;/em&gt;'s &lt;em&gt;Default to pull request title and description&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;u&gt;Rebase and merge&lt;/u&gt;
&lt;/h2&gt;

&lt;p&gt;The last GitHub's method of closing a PR is &lt;strong&gt;rebase&lt;/strong&gt;. The Git &lt;em&gt;rebase&lt;/em&gt; is a complex and powerful tool that can rewrite a Git history. Fortunately, rebasing by GitHub is limited only to a single specific case. Before I explain it, let’s see what &lt;a href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#rebase-and-merge-your-commits"&gt;GitHub documentation&lt;/a&gt; says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When you select the &lt;strong&gt;Rebase and merge&lt;/strong&gt; option on a pull request on GitHub.com, all commits from the topic branch (or head branch) are added onto the base branch individually without a merge commit. In that way, the rebase and merge behavior resembles a &lt;a href="https://git-scm.com/docs/git-merge#_fast_forward_merge"&gt;fast-forward merge&lt;/a&gt; by maintaining a linear project history. However, rebasing achieves this by re-writing the commit history on the base branch with new commits.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Rebase in practice
&lt;/h3&gt;

&lt;p&gt;Actually GitHub implements its own way:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The rebase and merge behavior on GitHub deviates slightly from &lt;code&gt;git rebase&lt;/code&gt;. Rebase and merge on GitHub will always update the committer information and create new commit SHAs, whereas &lt;code&gt;git rebase&lt;/code&gt; outside of GitHub does not change the committer information when the rebase happens on top of an ancestor commit.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And this is how it works in practice:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T1aGgltc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hef36ux64jqj4bxndpcf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T1aGgltc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hef36ux64jqj4bxndpcf.png" alt="git tree before rebase" width="647" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Right after &lt;strong&gt;Rebase and merge&lt;/strong&gt; button is pressed, there is no input field to provide or edit a commit message this time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M0CuQdK_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l66b86wfrwkavp35e7pr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M0CuQdK_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l66b86wfrwkavp35e7pr.png" alt="rebase confirmation" width="800" height="73"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And this is the result after &lt;strong&gt;Confirm rebase and merge&lt;/strong&gt; is pressed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--L9bUGXnC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j94a3ozc6oen7rtu41p4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L9bUGXnC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j94a3ozc6oen7rtu41p4.png" alt="git tree after rebase" width="685" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similarly to the squash, the merge doesn't bind a source branch to the target one and creates new commits (with new hash) to the target.&lt;/p&gt;

&lt;h3&gt;
  
  
  Messages of rebase
&lt;/h3&gt;

&lt;p&gt;The difference is that the &lt;em&gt;merge&lt;/em&gt; creates commits with the same changes and the same messages as commits in the source branch. After the &lt;code&gt;feature/8&lt;/code&gt; is removed, there is no track where the commit came from.&lt;/p&gt;

&lt;p&gt;So, what can we do about that?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We can create a new &lt;strong&gt;draft pull request&lt;/strong&gt; for our feature branch right after it is created. The draft pull request can't be closed nor merged.&lt;/li&gt;
&lt;li&gt;Knowing the PR's number, we can just add a reference &lt;code&gt;(#&amp;lt;PR number&amp;gt;)&lt;/code&gt; to the titles of our commits of the feature branch.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Now we know &lt;strong&gt;what&lt;/strong&gt; we can do to close a pull request on GitHub.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Merge&lt;/strong&gt; joins a source branch with all its commits and a base branch by creating a new merge commit in the base. Such a commit is the point in git history where both branches were bound.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Squash&lt;/strong&gt; consolidates changes from all commits from a source branch and pushes them together within a new single commit into the base branch. The branches are not bound and the same changes on both branches are considered as totally different ones in comparison.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rebase&lt;/strong&gt; copies commits from the target branch and pushes them into the base with the same changes, commit messages, but different SHAs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the next post, we will consider &lt;strong&gt;when&lt;/strong&gt; to use the three of them. We will dive into their pros and cons and use cases.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>git</category>
      <category>github</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
