Strong Opinion on git Workflow

Strong Opinion

    <p>
      <em><strong>Whether working in trunk-based style or feature-branch style, the main (or trunk, also known as master) branch, along with any release branches if doing that thing, SHOULD NOT have merge-commits.</strong></em>
    </p>

    <h2>
      Reasons
    </h2>

    <p>
      I do not like to see merge commits in the main/trunk branch. I prefer a straight, unblemished history line.
    </p>

    <p class="has-white-color has-black-background-color has-text-color has-background" style="line-height: 1;">
      * d589966 -(17 hours ago) (HEAD->main, origin/main,origin/HEAD)<br />* 5f3c420 -(17 hours ago) Merge branch &#8216;main&#8217;<br />|\<br />| * 4650c10 -(17 hours ago)<br />| * ae51497 -(17 hours ago)<br />| * b45fa11 -(18 hours ago)<br />* | d532336 -(17 hours ago)<br />|/<br />* 3d7bb50 -(18 hours ago)<br />* 6851949 -(18 hours ago)<br />* 28a191b -(19 hours ago)<br />* 01f6be2 -(19 hours ago)
    </p>

    <p>
      Merge commits make reviewing the history more difficult for humans and source control tools to understand, explore, and resolve issues. Other than the visual part, they introduce complexities to reverting any issues that come up which cross the merge-commit line.
    </p>

    <h2>
      gitconfig
    </h2>

    <p>
      I have these settings in my global <code>.gitconfig</code> to fail any merges that are not fast-forward. This forces me to resolve those with a proper rebase before merging into main.
    </p>

    <p class="has-white-color has-black-background-color has-text-color has-background" style="line-height: 1;">
      <code> [merge]&lt;br> &nbsp;&nbsp;&nbsp;&nbsp;ff = only&lt;br> [pull]&lt;br> &nbsp;&nbsp;&nbsp;&nbsp;ff = only&lt;br> </code>
    </p>

    <h2>
      Workflow
    </h2>

    <p>
      The workflow I use to resolve such problems is:
    </p>

    <ul>
      <li>
        Create a branch at the current state <code>git branch wip</code>
      </li>
      <li>
        Reset the <code>main</code> branch upstream head either by resetting it then pulling or fetching then resetting it.
      </li>
      <li>
        Change to the <code>wip</code> branch <code>git checkout wip</code>
      </li>
      <li>
        Rebase <code>wip</code> onto main <code>get rebase main</code> (resolve any merge conflicts the usual way)
      </li>
      <li>
        Change to the main branch <code>git checkout main</code>
      </li>
      <li>
        Merge the <code>wip</code> branch into main (since it will fast forward now) <code>git merge wip</code>
      </li>
      <li>
        Delete the <code>wip</code> branch <code>git branch -d wip</code>
      </li>
    </ul>

    <h2>
      scripts.sh
    </h2>

    <p>
      As with anything that I do more than a couple of times, I have automated this workflow. The scripts below are in the set that I source into my current terminal. When I <code>git pull</code> and it fails because it cannot fast forward (see the <code>.gitconfig</code> settings above), I can just <code>repo_wip_rebase</code>.
    </p>

    <p>
      The script stops at the rebase step if there are merge conflicts to resolve. I use the normal <code>git mergetool</code> and <code>get rebase --continue</code> to handle those and then <code>repo_wip_merge</code> to finish up.
    </p>

    <p>
      Note that when there are no merge conflicts, <code>repo_wip_rebase</code> does all the work.
    </p>

    <p class="has-white-color has-black-background-color has-text-color has-background" style="line-height: 1;">
      function repo_wip_merge()<br />{ <br />&nbsp;&nbsp;&nbsp;&nbsp;git checkout main || return $?<br />&nbsp;&nbsp;&nbsp;&nbsp;git merge &#8211;ff-only wip || return $?<br />&nbsp;&nbsp;&nbsp;&nbsp;git branch -d wip || return $?<br />}<br /><br />function repo_wip_rebase()<br />{ <br />&nbsp;&nbsp;&nbsp;&nbsp;git branch wip || return $?<br />&nbsp;&nbsp;&nbsp;&nbsp;git reset &#8211;hard origin/main || return $?<br />&nbsp;&nbsp;&nbsp;&nbsp;git checkout wip || return $?<br />&nbsp;&nbsp;&nbsp;&nbsp;git rebase main || return $?<br />&nbsp;&nbsp;&nbsp;&nbsp;# if there are merge conflicts, it stops here. Resolve them and then do this separately<br />&nbsp;&nbsp;&nbsp;&nbsp;repo_wip_merge<br />}
    </p>

    <p>
      It is possible to do this as get aliases instead so that the commands might be: <code>git wip_rebase</code>.
    </p>

    <p>
      I tend not to create git aliases for two reasons. The most important is to keep custom stuff clearly custom. I do not want to get in the habit of using <code>git xx</code> short cuts and erode my memory of the raw git commands. Having scripts with the prefix <code>repo_</code> makes it clear to my brain that this is a custom thing. The second reason is that having these in a script separate from my home folder that is under source control is easier for me to manage.
    </p>

    <h2>
      Coda
    </h2>

    <p>
      I know there are other options for both of these. This is what works for me.
    </p>

    <p>
      I hold no strong opinions on how you should automate your work. I only hold that you should automate as much as you can in whatever way works for you.
    </p>
  </div>
</div>