{"id":600411,"date":"2023-01-23T06:49:43","date_gmt":"2023-01-23T12:49:43","guid":{"rendered":"https:\/\/news.sellorbuyhomefast.com\/index.php\/2023\/01\/23\/git-commands-you-probably-do-not-need\/"},"modified":"2023-01-23T06:49:43","modified_gmt":"2023-01-23T12:49:43","slug":"git-commands-you-probably-do-not-need","status":"publish","type":"post","link":"https:\/\/newsycanuse.com\/index.php\/2023\/01\/23\/git-commands-you-probably-do-not-need\/","title":{"rendered":"Git Commands You Probably Do Not Need"},"content":{"rendered":"<section>\n<p>Ah, <a href=\"https:\/\/git-scm.com\/\">git<\/a>! Love it, hate it. Few things are as central to the modern software<br \/>\ndevelopment workflow as source-control management (SCM) tools. Although there<br \/>\nhave been and still are plenty of alternatives to <code>git<\/code> in the world of SCMs,<br \/>\nnone other seem quite as prevalent both in open-source and the enterprise.<\/p>\n<p>Regardless of how central <code>git<\/code> has grown to be for many (most?) software<br \/>\ndevelopers, I frequently get the impression that people have a tendency to shy<br \/>\naway from anything beyond the relatively basic functionality it provides.<\/p>\n<p>Since its very inception <code>git<\/code> has been notorious for its often unfriendly,<br \/>\ninconsistent and occasionally hostile command line interface:<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/myme.no\/images\/git-abuse-tracked-repo.png\" alt=\"Git man page generator\" title=\"Git man page generator\"><\/p>\n<p>In this post I\u2019ll present a few <code>git<\/code> commands and operations I run or have run<br \/>\non occasion, in no particular order, that the majority of <code>git<\/code> users out there<br \/>\nmight not ever need.<\/p>\n<h2 id=\"the-empty-commit\">The empty commit \u2205<\/h2>\n<p><code>git<\/code> is designed to track content and changes to that content over time, so<br \/>\ncreating <em>empty<\/em> commits doesn\u2019t sound like a very productive or sensible thing<br \/>\nto do. Not <em>adding<\/em> any content might seem like a waste.<\/p>\n<p>Yet, I know off the top of my mind at least two occasions where creating an<br \/>\nempty commit can make quite a bit of sense:<\/p>\n<ol type=\"1\">\n<li>Initializing new repositories.<\/li>\n<li>Triggering continuous deployment pipelines.<\/li>\n<\/ol>\n<h3 id=\"initializing-a-new-repository\">1. Initializing a new repository<\/h3>\n<p>My main reason for using the <code>--allow-empty<\/code> flag to <code>git<\/code> is to create an<br \/>\ninitial commit in a repository <em>before<\/em> I have any content for it.<\/p>\n<p>Why?<\/p>\n<p>Primary reason for me is that <code>git rebase<\/code> is somewhat troublesome when wanting<br \/>\nto rewrite the first commit of a repository. The <code>rebase<\/code> command centers around<br \/>\nworking against an <code><upstream><\/code> commit<a href=\"http:\/\/myme.no\/#fn1\" id=\"fnref1\" role=\"doc-noteref\"><sup>1<\/sup><\/a>, and by definition the first commit<br \/>\nof a branch has no upstream. In later versions of <code>git<\/code> the <code>--root<\/code> option has<br \/>\nbeen added to remedy this, but I still like to use the opportunity to start any<br \/>\nnew line of history with an empty commit:<\/p>\n<pre><code>\u276f git commit --allow-empty -m \"Initial commit\"\n\u276f git show --stat 592f9dd06d1013e4b5300311e6c1b7033c17ab9b\ncommit 592f9dd06d1013e4b5300311e6c1b7033c17ab9b\nAuthor: Martin \u00d8in\u00e6s Myrseth <<span \n                data-original-string='rMspv\/wivslRhU2ljTk6Nw==7f4UKsa7WlsY7EC543WDkQrZqgyRF9Lw5ci\/KBpjCN3HXo='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>my<span class=\"apbct-blur\">*****<\/span>@<span class=\"apbct-blur\">***<\/span>il.com<\/span>>\nDate:   Thu Dec 21 21:25:24 2017 +0100\n\n    Initial commit\n<\/code><\/pre>\n<p>Observe that the typical <em>diff<\/em> of the <code>git show<\/code> command is missing. There was<br \/>\nno content in this commit.<\/p>\n<h3 id=\"trigger-a-build\">2. Trigger a build<\/h3>\n<p>Depending on your continuous integration\/deployment setup chances are that<br \/>\nautomated work is scheduled based on updates to some <code>git<\/code> repository. Alas,<br \/>\nflaky network and computation resources, tests and dependency management often<br \/>\ncome in the way of a flawless pipeline execution.<\/p>\n<p>Creating empty <code>git<\/code> commits and pushing them to a PR might be all you need to<br \/>\nscratch that \u201clet\u2019s just try it once more, without <em>changing<\/em> anything\u201d itch.<\/p>\n<h2 id=\"pushing-locally\">Pushing locally ????<\/h2>\n<p>It\u2019s hard to use <code>git<\/code> for long without encountering <code>git push<\/code>, the <code>git<\/code><br \/>\nsub-command for transferring (copying) stuff from your local repository to some<br \/>\n<em>other<\/em>, remote <code>git<\/code> repository. The format of the command I\u2019m sure most people<br \/>\nare familiar with is:<\/p>\n<pre><code>git push <remote>\n<\/code><\/pre>\n<p>Or for the more adventurous:<\/p>\n<pre><code>git push <remote> <refspec>\n<\/code><\/pre>\n<p>Now what if I told you it\u2019s possible to push a <em>remote<\/em> reference to your<br \/>\n<em>local<\/em> repository, effectively reversing the direction of the push operation?<\/p>\n<pre><code>git push . origin\/main:main\n<\/code><\/pre>\n<p>The dot (<code>.<\/code>) in the command above is what tells <code>git<\/code> that the destination<br \/>\nrepository is the current directory. It\u2019s equivalent to <code>.\/<\/code> or the absolute<br \/>\npath of the repository (i.e. <code>$(pwd)<\/code>).<\/p>\n<blockquote>\n<p>Why on earth would you ever want to do this? Haven\u2019t you heard of git pull?<\/p>\n<\/blockquote>\n<p>Well, first off <code>git pull<\/code> is a combination of <em>two<\/em> <code>git<\/code> operations: <code>git<br \/>\nfetch<\/code> followed by either a <code>git merge<\/code> or a <code>git rebase<\/code> depending on user<br \/>\nconfiguration.<\/p>\n<p>Truthfully, a <code>git fetch<\/code> does the opposite of a typical <code>git push<\/code>, which is to<br \/>\nupdate the remote-tracking branches locally, including retrieving all objects<br \/>\nrequired to complete the history of the newfound remote commits. Then \u2013 in the<br \/>\n<code>git pull<\/code> case \u2013 <code>git merge<\/code> or <code>git rebase<\/code> are invoked to update the<br \/>\ncurrently checked out <code>ref<\/code> to whichever commit is pointed to by the<br \/>\nremote-tracking branch defined as the \u201cupstream\u201d of the current local branch<br \/>\n(<em>pheeew<\/em>).<\/p>\n<p>The main issue with <code>git pull<\/code> in this scenario is the second step, as both <code>git<br \/>\nmerge<\/code> and <code>git rebase<\/code> require the <code>ref<\/code> about to be updated to also be checked<br \/>\nout. This is because any potential conflicts must be resolved \u201con the branch\u201d.<\/p>\n<p>In fact, in order to properly \u201cpush to the local repository\u201d it\u2019s <em>necessary<\/em> to<br \/>\ninvoke a <code>git fetch<\/code> first to ensure that the remote tracking branches are<br \/>\nupdated.<\/p>\n<p>Remote-tracking branches are <code>git<\/code> references (<code>refs<\/code>) typically stored in<br \/>\n<code>.git\/refs\/remotes\/<remote-name>\/<branch-name><\/code> which keep track of the branch<br \/>\nstate of a remote repository. For the most part the <code>remote-name<\/code> and <code>branch<\/code><br \/>\ncombinations are unambiguous allowing references to remote-tracking branches to<br \/>\nbe abbreviated like <code>origin\/main<\/code>. These references are managed by the <code>git<br \/>\nfetch<\/code> command primarily. It\u2019s important to be aware that <code>git<\/code> does <em>not<\/em> hit<br \/>\nthe network to check for updates when simply referring to <code>refs<\/code> like<br \/>\n<code>origin\/main<\/code>.<\/p>\n<p>Let\u2019s say that you have been working for a long-ish period of time on a feature<br \/>\nbranch, leaving your local copy of the project\u2019s <code>main<\/code> branch significantly out<br \/>\nof date. For whatever reason (diffing, cherry-picking, etc) you want to update<br \/>\n<code>main<\/code> with the latest upstream changes, but you don\u2019t want to navigate <em>off<\/em><br \/>\nyour feature branch<a href=\"http:\/\/myme.no\/#fn2\" id=\"fnref2\" role=\"doc-noteref\"><sup>2<\/sup><\/a>. One reason might be to avoid invalidating much of a<br \/>\nslow-running incremental build.<\/p>\n<p>Instead of using e.g. the <code>worktree<\/code> functionality to create and checkout a<br \/>\nwhole new working tree, we can use the \u201cpush to local\u201d to update whichever<br \/>\n<em>passive<\/em><a href=\"http:\/\/myme.no\/#fn3\" id=\"fnref3\" role=\"doc-noteref\"><sup>3<\/sup><\/a> <code>head<\/code> that we wish.<\/p>\n<p><code>git<\/code> provides the same branch protection rules as when pushing to remote<br \/>\nrepositories. Which is to say that it denies non-fast-forward pushes by default,<br \/>\nbut allows overrides through <code>+<commit>:<ref><\/code> refspecs, or the <code>--force<\/code> and<br \/>\n<code>--force-with-lease<\/code> command line options.<\/p>\n<p>Keep in mind that using these overrides are destructive and may lead to<br \/>\ndataloss. Always create a backup branch with something like <code>git branch<br \/>\n<some-temp-name> <branch-to-update><\/code> if you\u2019re not 100% certain you\u2019re in<br \/>\ncontrol of what\u2019s going to get pushed.<\/p>\n<h2 id=\"commit-ranking\">Commit ranking ????<\/h2>\n<p>Perhaps in need for something to serve as the year-end bonus rounds tie-breaker?<br \/>\nWhat better way to settle the implicit battle between your peers of \u201cwho\u2019s<br \/>\nproviding most value\u201d than by having a \u201cgit commit count\u201d showdown?<\/p>\n<blockquote>\n<p>No, that\u2019s a horrible idea \u2026<\/p>\n<\/blockquote>\n<p>Yes, yes it is. Any sensible developer knows that nothing good ever comes out of<br \/>\nplacing merit in lines of code changed or number of commits committed.<\/p>\n<p>But <em>should you<\/em>, God forbid, ever be in need to know (for the sake of<br \/>\ncuriosity) who\u2019s been committing the most to a repository, here\u2019s <code>git rank<\/code>:<\/p>\n<pre><code>git shortlog -s -n --no-merges\n<\/code><\/pre>\n<p>Configure it as an alias in <code>~\/.config\/git\/config<\/code> with:<\/p>\n<pre><code>[alias]\n  rank = \"shortlog -s -n --no-merges\"\n<\/code><\/pre>\n<p>and simply run:<\/p>\n<pre><code>git rank\n<\/code><\/pre>\n<p>As a quick example, behold, here\u2019s the horrendous output from my own <code>dotfiles<\/code><br \/>\nrepository, where I\u2019ve been able to make commits under different names and<br \/>\nidentities:<\/p>\n<pre><code>\u276f git shortlog -nse\n   567  Martin \u00d8in\u00e6s Myrseth <<span \n                data-original-string='fuCahf7hkwQ5+lluDbq3+A==7f472Rs2Ob61XYgOWJ82ih5zIDZBMihsHiw7W\/bvN5p+yE='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>my<span class=\"apbct-blur\">*****<\/span>@<span class=\"apbct-blur\">***<\/span>il.com<\/span>>\n   322  Martin \u00d8in\u00e6s Myrseth <<span \n                data-original-string='Hv8tkWzpRRi11LhqmOwijA==7f45D0A7gf7hd0f0F48ayzzMi9EnDJfr2IY\/QiGePLVp5o='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>mm<span class=\"apbct-blur\">******<\/span>@<span class=\"apbct-blur\">***<\/span>co.com<\/span>>\n   142  Martin Myrseth <<span \n                data-original-string='hu9jH+dGOVzRFuajdyOZZw==7f4YVzq+flHGbqYN5s4cdkZoQ=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'><span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">**<\/span>me.no<\/span>>\n    14  Martin Myrseth <<span \n                data-original-string='tQOtAEQHJYbJma77I7b7cQ==7f4LyHaeLxdtxAGluRg4W+oK8mVkkNA\/QQU1u6kEWR6eFI='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>my<span class=\"apbct-blur\">*****<\/span>@<span class=\"apbct-blur\">***<\/span>il.com<\/span>>\n     4  Martin \u00d8in\u00e6s Myrseth <<span \n                data-original-string='4K+r6f7lu5MJubqDZIPdTQ==7f4a76DhJU6O7gsp10PNiMURg=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'><span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">**<\/span>me.no<\/span>>\n     3  Martin Myrseth <myme@map>\n     2  Martin \u00d8in\u00e6s Myrseth <<span \n                data-original-string='P9bh4yC1hvS2Z\/eBmwh9BA==7f4v8IOG+Bbap0Sad1YcNrhkVZ+6MeL7st+4ZLLFmeesm0='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>my<span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">***<\/span>le.localdomain<\/span>>\n     2  Martin \u00d8in\u00e6s Myrseth <<span \n                data-original-string='uQOPiayY\/jvVA0btq8cweQ==7f4YNkOJo8VKTgIEuRlf9s+gGmMZo77aXqxIcG+IhdA58Y='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>my<span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">*<\/span>ap.localdomain<\/span>>\n<\/code><\/pre>\n<p>It\u2019s painful to read, I know. Try to imagine the pain and embarrassment it is<br \/>\nfor me to share it. And have you been unfortunate or careless enough to get<br \/>\nyourself into a similar situation, please read on. I\u2019ll revisit this problem in<br \/>\nthe <a href=\"http:\/\/myme.no\/#filter-branch\">filter branch<\/a> section below.<\/p>\n<h2 id=\"cat-file\">Cat file ????<\/h2>\n<p>This is more of a <code>git<\/code> party trick than anything to actually make much use of.<br \/>\nBut I should say I have made use of <code>git cat-file -p<\/code> on a couple of occasions<br \/>\nto help people <em>visualize<\/em> and actually grok <code>git<\/code>\u2019s data model.<\/p>\n<p>As the name hints at, the <code>cat-file<\/code> command outputs information about <code>git<\/code><br \/>\nobjects. I\u2019ve personally only used <code>cat-file<\/code> with the <code>-p<\/code> (pretty-print) flag,<br \/>\nwhich first determines the type of the object before printing it out. Let\u2019s<br \/>\nstart off with inspecting the <code>HEAD<\/code> commit:<\/p>\n<pre><code>$ git cat-file -p HEAD\ntree 9491ada70010d722646b674d2e2a26521628df94\nparent 9d7e5a6490c9f560f54fee9e1af5d72429bb26c7\nauthor Martin Myrseth <<span \n                data-original-string='NC8B3vR1fdC7QNUBtHas2w==7f4\/kGEUe6roBM6khl0oJe6EQ=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'><span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">**<\/span>me.no<\/span>> 1665439490 +0200\ncommitter Martin Myrseth <<span \n                data-original-string='b9vvBtGFIp4NJT4LvuSViw==7f4fCZX2FFQIqFOIW\/GWTmMRg=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'><span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">**<\/span>me.no<\/span>> 1665439490 +0200\n\nDelete Docker deploy action\n<\/code><\/pre>\n<p>We see the main metadata that git associates with a commit: the repository file<br \/>\nstructure (<code>tree<\/code>), a parent commit <code>SHA1<\/code> reference, author information and<br \/>\nfinally the commit message after a blank line. Let\u2019s dig further by passing the<br \/>\n<code>SHA1<\/code> (<code>9491ad..94<\/code>) of the <code>tree<\/code> associated with the <code>HEAD<\/code> commit:<\/p>\n<pre><code>$ git cat-file -p 9491ada70010d722646b674d2e2a26521628df94\n040000 tree 6d71faa5d70085c5d07228d8fa522fb712253b6d    .github\n100644 blob e09fe0dc282fdcaff06bcc6a9bbf57cbfc845eb4    .gitignore\n100644 blob da7e7945524871726071f919690c9c9f6c1e173d    README.md\n100644 blob e6be557357c3fe2e3cce6f1b7b9b3c9c55981a16    default.nix\n040000 tree 4f69a79c432cde80b4a1c486974b03cab84b45b9    docker\n100644 blob 2f8aacd9efa3cfdf9e5f2860fa7226b510ed83bc    feed-cors.conf\n100644 blob 14b9e2dd0a41aa932c1f4bb5938519547f37f82c    flake.lock\n100644 blob eeae336837db94ca62255a7e5fa7f32ae3363716    flake.nix\n100644 blob f1f8ef836b3b9b9ea011a43972a28ffaa713c868    image.nix\n040000 tree 5cad033d973f19ece938c33c3bb912eb63dc3305    site\n040000 tree 49dc35d8e519f02f6f1a647f437226af198d225a    ssg\n100644 blob 60dede4bba8cd9479b0bec49048da1397e14f352    todo.org\n<\/code><\/pre>\n<p>The result of printing a <code>tree<\/code> is what looks like a directory listing of the<br \/>\ncontents of that \u201ctree\u201d directory. Each directory entry is represented as some<br \/>\nmode bits, an object type, the <code>SHA1<\/code> of the object and the name of the entry.<br \/>\nTrees may contain other <code>tree<\/code> objects to create a directory structure, or<br \/>\n<code>blob<\/code> objects which contain file contents.<\/p>\n<p>Finally, let\u2019s inspect one of the <code>blob<\/code> in the output, like <code>.gitignore<\/code><br \/>\n(<code>e09fe0..b4<\/code>):<\/p>\n<pre><code>$ git cat-file -p e09fe0dc282fdcaff06bcc6a9bbf57cbfc845eb4\n.stack-work\n_cache\npublic\ndist-newstyle\n.ghc.environment.*\n\n# nix-build\nresult\n<\/code><\/pre>\n<p>Which prints out the actual content of <code>.gitignore<\/code> the way it was committed<br \/>\ninto the current <code>HEAD<\/code> commit.<\/p>\n<blockquote>\n<p>Wait? What? So everything is just <em>text<\/em> files?<\/p>\n<\/blockquote>\n<p>Conceptually, yes. However, modern <code>git<\/code> does a lot more to optimize storage<br \/>\n(re)usage and whatnot to ensure that a repository stays as small as possible.<br \/>\nThere are other, scarier objects lurking under <code>.git\/objects<\/code> in a <code>git<\/code><br \/>\nrepository.<\/p>\n<h3 id=\"the-git-parable\">The git parable<\/h3>\n<p>As I said in the beginning of this section, I\u2019ve used <code>cat-file<\/code> to help myself<br \/>\nand others understand the <code>git<\/code> object model. Learning all the details of that<br \/>\nmodel isn\u2019t the purpose of this section (or post) though. However, if reading<br \/>\nthis ignited some form of curiosity on your part I would gladly recommend the<br \/>\ntalk \u201cThe Git Parable\u201d which dives deeper into the <code>git<\/code> object model, as<br \/>\npresented by my good friend <a href=\"https:\/\/herland.net\/\">Johan Herland<\/a>:<\/p>\n<p>\n  <iframe loading=\"lazy\" width=\"1279\" height=\"721\" src=\"https:\/\/www.youtube.com\/embed\/ANNboouhNHE\" title=\"The Git Parable: a different approach to understanding Git\" frameborder=\"3\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen><br \/>\n  <\/iframe>\n<\/p>\n<h3 id=\"use-case\">Use case<\/h3>\n<p>Now, why would you want to use <code>cat-file<\/code>? (Except you wouldn\u2019t, but let\u2019s just<br \/>\nplay along here)<\/p>\n<p>I was deep into some refactoring and clean-up of a set of template files used<br \/>\nfor various messages sent out from a system. Each template directory would<br \/>\ncontain a set of files for each message template. I\u2019ve been working with the<br \/>\nfiles for a while when a feeling grew on me that several of these templates<br \/>\nseemed to be fairly similar, identical in fact.<\/p>\n<p>At this point I had already been making some work-in-progress commits, which<br \/>\nwould definitely get in the way for any attempt at checking if there were<br \/>\nidentical template directories in my working copy. I wanted to compare the<br \/>\ncontents of the template directories at the point <em>before<\/em> I started making my<br \/>\nchanges.<\/p>\n<p>The primary tool for checking differences to files in <code>git<\/code> is obviously the<br \/>\n<code>git diff<\/code> command. It can easily check the differences between files stored in<br \/>\nthe <code>git<\/code> history. Typical usage of <code>diff<\/code> is to compare a <em>single<\/em> path across<br \/>\nvarious versions. However, looking closer at it\u2019s synopsis we can see that there<br \/>\nare a couple of call signatures that might do somewhat what we need:<\/p>\n<pre><code>NAME\n       git-diff - Show changes between commits, commit and working tree, etc\n\nSYNOPSIS\n       git diff [<options>] [<commit>] [--] [<path>...]\n       git diff [<options>] --cached [--merge-base] [<commit>] [--] [<path>...]\n       git diff [<options>] [--merge-base] <commit> [<commit>...] <commit> [--] [<path>...]\n       git diff [<options>] <commit>...<commit> [--] [<path>...]\n       git diff [<options>] <blob> <blob>\n       git diff [<options>] --no-index [--] <path> <path>\n<\/code><\/pre>\n<p>Primarily <code>git diff <blob> <blob><\/code> which would let us compare any two <code>git<\/code> blob<br \/>\nobjects. There\u2019s also a note under \u201cDESCRIPTION\u201d which states:<\/p>\n<blockquote>\n<p>Just in case you are doing something exotic, it should be noted that all of the<br \/>\n<code><commit><\/code> in the above description, except in the <code>--merge-base<\/code> case and in<br \/>\nthe last two forms that use <code>..<\/code> notations, can be any <code><tree><\/code>.<\/p>\n<\/blockquote>\n<p>Which means that also <code>git diff <blob> <blob><\/code> should do something along the<br \/>\nlines that we want. And surely, doing something similar to the following yielded<br \/>\nan empty diff (where <code>HEAD~3<\/code> is the commit I based my work on):<\/p>\n<pre><code>\u276f git diff HEAD~3:some\/templates\/path\/ HEAD~3:some\/templates\/other-path\/\n<\/code><\/pre>\n<p>The manual page for <code>git-diff<\/code> states that it takes two <em>blobs<\/em>, but it\u2019s just<br \/>\nas valid with any tree-like object, often called <code>tree-ish<\/code> in the <code>git<\/code><br \/>\ndocumentation.<\/p>\n<p>So I had found one pair of templates that were identical, and which could be<br \/>\ncoalesced into one. But what if there were more? Using <code>git diff<\/code> alone I would<br \/>\nhave had to compare all permutations of template directories to see if the<br \/>\nresults were empty.<\/p>\n<p>No time for that\u2026<\/p>\n<p>Instead we can use <code>cat-file<\/code> to simply dump all the hashes of every sub<br \/>\n<code><tree><\/code>. Then we can use a familiar shell pipeline to group the hashes and<br \/>\ncount them:<\/p>\n<pre><code>\u276f git cat-file -p HEAD~3:some\/templates\/ \n  | awk '{ print $3; }' \n  | sort \n  | uniq -c \n  | sort -rn\n      2 af83bb357f2b8dc42f6c9f07620140590dc7fd44\n      2 228182da5a0ffcf4c0d263bfa54852176f0250d2\n      1 ef1a471185c2092e6708349fa710702dd416f892\n      1 e453cb9d3dddbdd46a65c811068352ac40941fcd\n      1 e3df1181dae478172a7ae6bbc1618a3af2151db4\n      1 de0f6cd53ea97cb100a74c812f75c0d4844c0efa\n      1 d7f239da6283c927dad650599d49639ddc761465\n      1 d7d8f5aa3571ea2392028e353ad958d778d2bee0\n      1 cc03005d684b5735da337a6e5ca9765751943d7d\n      ... # A bunch more\n<\/code><\/pre>\n<p>Et voil\u00e0! We clearly see that there are not just one pair of duplicate<br \/>\ntemplates, but two!<\/p>\n<p>I should note that this approach is brittle in the sense that should there be<br \/>\n<em>any<\/em> difference to the blobs <em>at all<\/em> this method falls apart. In my case it<br \/>\nworked perfectly, but your mileage might vary. In my experience there are often<br \/>\nseveral ways to do the \u201csame thing\u201d using <code>git<\/code>, so it would be nice to hear of<br \/>\nother approaches.<\/p>\n<h2 id=\"orphan-commits\">Orphan commits ????<\/h2>\n<p>Every commit in a <code>git<\/code> repository has a reference to its parent, which is the<br \/>\ncommit that chronologically came immediately <em>before<\/em> the commit. For merge<br \/>\ncommits the number of parents are greater than one.<\/p>\n<p>Well, that\u2019s not 100% accurate. As discussed in <a href=\"http:\/\/myme.no\/#the-empty-commit\">the empty commit<\/a> the initial<br \/>\ncommit of a branch is somewhat special: it has no parents. Commits without any<br \/>\nreference to a parent is called an \u201corphan\u201d commit. In most repos there would<br \/>\nonly be one such commit, the initial commit.<\/p>\n<p>However, <code>git<\/code> is by no means limited to a single orphan commit. The default<br \/>\nbehavior when creating a new branch is that the new branch is based on some<br \/>\n<code>start-point<\/code>. Using <code>git checkout --orphan<\/code> (or the <em>currently<\/em> unstable <code>git<br \/>\nswitch --orphan<\/code>) it\u2019s possible to start off a completely new and independent<br \/>\nline of history totally disconnected from the rest of the repository.<\/p>\n<p>The main use-case I\u2019ve had for <code>git<\/code>\u2019s support of this functionality is not to<br \/>\nstart \u201corphaned\u201d histories, but rather <em>absorb<\/em> the history of a branch from<br \/>\nanother, unrelated repository. It\u2019s very useful when coalescing many smaller<br \/>\nrepositories into a monorepo or when vendorizing some library.<\/p>\n<h3 id=\"merging-histories\">Merging histories<\/h3>\n<p>As a synthetic case-study let\u2019s import the <code>doomemacs<\/code> history into my<br \/>\n<code>dotfiles<\/code> repo!<\/p>\n<p>First let\u2019s create a new <code>worktree<\/code> so that we don\u2019t mess up my <em>actual<\/em> files:<\/p>\n<pre><code>\u276f git worktree add ~\/code\/doomfiles doomfiles\nPreparing worktree (checking out 'doomfiles')\nHEAD is now at a0b32f8 machine: deque: Setup nginx with rtcp.myme.no\n\u276f cd ~\/code\/doomfiles\n<\/code><\/pre>\n<p>Doing a <code>git log<\/code> of the most recent commits we can see that they\u2019re all mine:<\/p>\n<pre><code>\u276f git log --oneline --graph -5\n* 0f1f6cd machine: map: Enable podman\n* 46099b9 emacs: Add React fn-component snippet\n* 2e75458 ssh: Update hosts\n* 445ade4 machine: deque: Set SSH port\n* bf0a552 flake: Add utils as \"apps\"\n<\/code><\/pre>\n<p>Another \u201clittle known\u201d feature of <code>git<\/code> is that it\u2019s trivial to fetch \u201ca random\u201d<br \/>\nupstream repository without adding an explicit <code>git remote<\/code>. This can be quite<br \/>\nuseful when e.g. checking out some incoming one-off contribution. Just pass the<br \/>\nremote url to <code>fetch<\/code> directly:<\/p>\n<pre><code>\u276f git fetch <span \n                data-original-string='xFa6DuyarixBiS9Zp4GwSw==7f478iSk5yBpFv4iOm7wORnJA=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>gi<span class=\"apbct-blur\">*<\/span>@<span class=\"apbct-blur\">****<\/span>ub.com<\/span>:doomemacs\/doomemacs\nremote: Enumerating objects: 118606, done.\nremote: Counting objects: 100% (20\/20), done.\nremote: Compressing objects: 100% (17\/17), done.\nremote: Total 118606 (delta 4), reused 15 (delta 3), pack-reused 118586\nReceiving objects: 100% (118606\/118606), 26.98 MiB | 6.80 MiB\/s, done.\nResolving deltas: 100% (82950\/82950), done.\nFrom github.com:doomemacs\/doomemacs\n * branch                HEAD       -> FETCH_HEAD\n<\/code><\/pre>\n<p>As the output states, the result of the fetch is placed in the special <code>git<\/code> ref<br \/>\n<code>FETCH_HEAD<\/code>. We can use this ref to refer to the <code>doomemacs<\/code> commit that was<br \/>\nfetched when we wish to merge the histories.<\/p>\n<p>Now, <code>git<\/code> won\u2019t let us merge without warning us first:<\/p>\n<pre><code>\u276f git merge FETCH_HEAD\nfatal: refusing to merge unrelated histories\n<\/code><\/pre>\n<p>Easily enough we can add the <code>--allow-unrelated-histories<\/code> telling <code>git<\/code> we\u2019re<br \/>\nbeing quite serious right here:<\/p>\n<pre><code>\u276f git merge --allow-unrelated-histories FETCH_HEAD\nAuto-merging .gitignore\nCONFLICT (add\/add): Merge conflict in .gitignore\nAuto-merging README.md\nCONFLICT (add\/add): Merge conflict in README.md\nRecorded preimage for '.gitignore'\nRecorded preimage for 'README.md'\nAutomatic merge failed; fix conflicts and then commit the result.\n<\/code><\/pre>\n<p><em>Pfffft<\/em>, conflicts \u2026 Let\u2019s get on with our lives by simply resetting the conflicting files to the imported versions <code>#yolo<\/code>.<\/p>\n<pre><code>\u276f git checkout --theirs -- .gitignore README.md\n\u276f git add .gitignore README.md\n\u276f git commit -m 'Pulling in Doom Emacs!'\nRecorded resolution for '.gitignore'.\nRecorded resolution for 'README.md'.\n[doomfiles 11826ae12] Pulling in Doom Emacs!\n<\/code><\/pre>\n<p>And that\u2019s about it! Let\u2019s inspect the result:<\/p>\n<pre><code>\u276f git show\ncommit 11826ae125834cc4e2263172275d8c51bca11d63 (HEAD -> doomfiles)\nMerge: a0b32f85f e96624926\nAuthor: Martin Myrseth <<span \n                data-original-string='mD+f6LxSYgjHpLrGkbLWYQ==7f4K+iCJikvCdV2fxuYPf22lQ=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'><span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">**<\/span>me.no<\/span>>\nDate:   Thu Jan 19 01:13:17 2023 +0100\n\n    Pulling in Doom Emacs!\n<\/code><\/pre>\n<p>We can see that the commit is a merge commit, where one parent is <a href=\"https:\/\/github.com\/myme\/dotfiles\/commit\/a0b32f85fd07eaa09fcb2bc06b695b7a067a43aa\">a0b32f85f<\/a> from<br \/>\nmy <code>dotfiles<\/code> while the other parent <a href=\"https:\/\/github.com\/doomemacs\/doomemacs\/commit\/e96624926d724aff98e862221422cd7124a99c19\">e96624926<\/a> is the current<a href=\"http:\/\/myme.no\/#fn4\" id=\"fnref4\" role=\"doc-noteref\"><sup>4<\/sup><\/a> <code>HEAD<\/code> from the<br \/>\n<code>doomemacs<\/code> repo.<\/p>\n<p>We have successfully merged the histories of my <code>dotfiles<\/code> repository with Doom<br \/>\nEmacs!<\/p>\n<p>As stated previously, this can be quite useful when pulling in e.g. an<br \/>\nexperimental repository, vendorizing some dependency or similarly constructing a<br \/>\nmonorepo from separate smaller repositories.<\/p>\n<p>The next section is about one (of several) times I\u2019ve found this useful myself.<\/p>\n<h3 id=\"dotfiles-bankruptcy\">Dotfiles bankruptcy<\/h3>\n<p>I agree that the previous example of absorbing <code>Doom Emacs<\/code> into my <code>dotfiles<\/code> is kind of silly, but it illustrates <em>possibilities<\/em>.<\/p>\n<p>Stepping away from synthetic examples I also would like to show one of a few<br \/>\noccasions where I\u2019ve made use of it to solve a real-world use-case.<\/p>\n<p>Let\u2019s step back into my <code>dotfiles<\/code>.<\/p>\n<p>With our new knowledge about orphan commits we may wonder if there is a way to easily query for them in a <code>git<\/code> repository. And there sure is:<\/p>\n<pre><code>\u276f git log --all --max-parents=0\ncommit 6fa853118711f557a911b98f00d5c4a2eb3ded71\nAuthor: Martin Myrseth <<span \n                data-original-string='TpYNBXz5DEEFdMJFLBT+nw==7f4vQwAvX6YsHBmJSFKiUB1uA=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'><span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">**<\/span>me.no<\/span>>\nDate:   Mon Jan 17 21:44:43 2022 +0000\n\n    nixos: Initial commit\n\ncommit 61a3f80babec8c1339391462590dafe7ff30fe7f\nAuthor: Martin Myrseth <<span \n                data-original-string='aB1BtDpDlYkbYfFt7vpFTQ==7f47fIMMWPCTFWPZwpR083ngg=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'><span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">**<\/span>me.no<\/span>>\nDate:   Wed Feb 10 11:59:23 2016 +0100\n\n    Inital import of tuple\n<\/code><\/pre>\n<p>There is not one, but <em>two<\/em> commits in the <code>dotfiles<\/code> repository which doesn\u2019t<br \/>\nhave any parents.<\/p>\n<ol type=\"1\">\n<li>The <em>real<\/em> \u201c<a href=\"https:\/\/github.com\/myme\/dotfiles\/commit\/61a3f80babec8c1339391462590dafe7ff30fe7f\">Initial import<\/a>\u201d created at the beginning of time<a href=\"http:\/\/myme.no\/#fn5\" id=\"fnref5\" role=\"doc-noteref\"><sup>5<\/sup><\/a>.<\/li>\n<li>The much more recent \u201c<a href=\"https:\/\/github.com\/myme\/dotfiles\/commit\/6fa853118711f557a911b98f00d5c4a2eb3ded71\">nixos: Initial commit<\/a>\u201d.<\/li>\n<\/ol>\n<p>The second commit was the beginning of my attempt to move my machine<br \/>\nconfigurations towards a fully <code>NixOS<\/code> managed declarative setup built on top of<br \/>\n<code>flakes<\/code>. I\u2019ve already covered this journey in <a href=\"http:\/\/myme.no\/2022-06-14-nixos-confederation.html\">another post<\/a> which also links to<br \/>\nthe state of my configuration management <em>before<\/em> that migration.<\/p>\n<p>In any case, when starting my configuration rewrite I wasn\u2019t yet sure if I would<br \/>\nwant a clean slate or eventually port it into my <code>dotfiles<\/code>. In the end I<br \/>\nfigured I could have both, by simply pulling in the experiment into my already<br \/>\nexisting history.<\/p>\n<p>Eventually my experiment had matured to the point where I was convinced I had<br \/>\nwhat I wanted. It was time to import it into the <code>dotfiles<\/code> repository.<br \/>\nFollowing pretty much the same steps as in the previous section I ended up with<br \/>\nthe following <a href=\"https:\/\/github.com\/myme\/dotfiles\/commit\/79977b007099390a53e11f540e178f6285137206\">merge commit<\/a>:<\/p>\n<pre><code>\u276f git show 79977b007099390a53e11f540e178f6285137206\ncommit 79977b007099390a53e11f540e178f6285137206\nMerge: ad28da4 841eec3\nAuthor: Martin Myrseth <<span \n                data-original-string='tW+zPm5l3Am1c8nJn7z4EA==7f4pCtOb0DkOTonrCcZUV2K9A=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'><span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">**<\/span>me.no<\/span>>\nDate:   Wed Feb 2 00:19:24 2022 +0100\n\n    nixos: Declare dotfile bankruptcy\n<\/code><\/pre>\n<div>\n<p>I remember reading an email thread on the <code>git<\/code> mailing list in the early days<br \/>\nof <code>git<\/code> where Linus Torvalds boasted performing this \u201cabsorption\u201d operation in<br \/>\norder to pull in some unrelated history.<\/p>\n<p>And equally interesting I remember reading an analysis which touched on how many<br \/>\norphan commits there are in the <code>Linux<\/code> main tree. I remember there being four,<br \/>\none in particular seemed like a \u201ccareless\u201d unintentional mistake.<\/p>\n<p>Edit 2023-01-23:<\/p>\n<p>Initially I didn\u2019t spend enough effort trying to find these two sources, but<br \/>\nboth through help from readers and some more search-fu vigilance I was able to<br \/>\nfind what I was referring to:<\/p>\n<ol type=\"1\">\n<li>\n<p>From <a href=\"https:\/\/lore.kernel.org\/\">lore.kernel.org<\/a>: Behold, <a href=\"https:\/\/lore.kernel.org\/all\/<span \n                data-original-string='enwMuGoCKTJqkUCuHnftgQ==7f41n4fe1S5ItR4kvmmgI5JAsIrLlpu6iJ5Zqrp0ZStGo7sr9ndKZ+CooDPx\/8vvVk3Uo7yA3wP12CXiYbxA0hYmQ=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>Pi<span class=\"apbct-blur\">******************************<\/span>@<span class=\"apbct-blur\">*********<\/span>dl.org<\/span>\/T\/&#8221;>The coolest merge EVER!<\/a><\/p>\n<\/li>\n<li>\n<p>Thanks to a reader I was directed towards exactly the post I was after that<br \/>\nresearched \u201cweird\u201d <code>Linux<\/code> commits (from <a href=\"https:\/\/www.destroyallsoftware.com\/\">Destroy All Software)<\/a>:<\/p>\n<p><a href=\"https:\/\/www.destroyallsoftware.com\/blog\/2017\/the-biggest-and-weirdest-commits-in-linux-kernel-git-history\">\u201cThe Biggest and Weirdest Commits in Linux Kernel Git History\u201d<\/a> goes through<br \/>\nboth octopus merges and orphaned commits in the history <code>Linux<\/code>.<\/p>\n<\/li>\n<\/ol>\n<\/div>\n<h2 id=\"filter-branch\">Filter branch ????<\/h2>\n<p>The <code>git filter-branch<\/code> command has got WARNING written all over it. Please<br \/>\nproceed with caution. This section illuminates usage of <code>filter-branch<\/code> to fix a<br \/>\nparticular problem. As the section goes on to explain, there are better, less<br \/>\ndestructive ways to resolve these problems.<\/p>\n<p>I tend to work in a number of <code>git<\/code> repositories across various machines. I also<br \/>\nsplit work between my personal projects and anything related to <code>$DAYJOB<\/code>. I do<br \/>\nnot want to taint the <code>git<\/code> history in work repositories with <em>personal<\/em> email<br \/>\naddresses and other \u201cunprofessionalism\u201d.<\/p>\n<p>Turns out I <em>do<\/em> though. Remember the painful output from <a href=\"http:\/\/myme.no\/#commit-ranking\">commit ranking<\/a>?<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/myme.no\/images\/git-email-messup.png\" alt=\"A shameful git log view with localdomain author email addresses\" title=\"A shameful git log view with localdomain author email addresses\"><\/p>\n<p>Oh, the embarrassment! It\u2019s unbearable!<\/p>\n<p>This example <del>is<\/del> was from <a href=\"https:\/\/github.com\/myme\/dotfiles\">my dotfiles<\/a> history before I cleaned it up. I<br \/>\noccasionally setup new machines, and my <code>dotfiles<\/code> repo is the first thing I<br \/>\npull in after the machine boots. Every host typically needs some form of<br \/>\ntweaking, and not realizing I haven\u2019t setup my <code>git<\/code> configurations correctly, I<br \/>\nstart patching and committing configurations for the new host.<\/p>\n<p>Next thing I know I\u2019ve completely missed the fact that I\u2019ve been committing with<br \/>\nall kinds of ad-hoc <code>user<\/code> settings inferred from <code>git<\/code> without letting me know.<\/p>\n<p>I\u2019ve been aware of this potential issue for a long time, and have proactively<br \/>\ntried to mitigate it using various strategies on several occasions in the past.<br \/>\nSometimes bad commits manage to slip through though. With a stricter focus on a<br \/>\nholistic <code>nix flakes<\/code> host setup, I hope I\u2019m rid this issue of partial<br \/>\n(mis)configuration once and for all.<\/p>\n<h3 id=\"the-filter-branch-cleanup\">The filter-branch cleanup<\/h3>\n<p>Most people familiar with rewriting <code>git<\/code> history know about <code>git rebase<\/code> and<br \/>\n<code>git rebase --interactive<\/code>, which allow operations like \u201cmoving\u201d (or replaying)<br \/>\ncommits onto new parents, rewriting commit messages, re-assigning author<br \/>\ninformation, as well as making changes to the source tree.<\/p>\n<p>Perhaps less familiar to people is the <code>git filter-branch<\/code> command, which is<br \/>\nsort of the <em>hydrogen bomb<\/em> of history rewriting. I urge you to heed the glaring warning<br \/>\nthat meets you in <code>man git-filter-branch(1)<\/code> and perhaps consider alternative<br \/>\nsolutions like <a href=\"https:\/\/github.com\/newren\/git-filter-repo\/\">git-filter-repo<\/a>:<\/p>\n<pre><code>WARNING\n       git filter-branch has a plethora of pitfalls that can produce non-obvious\n       manglings of the intended history rewrite (and can leave you with little\n       time to investigate such problems since it has such abysmal performance).\n       These safety and performance issues cannot be backward compatibly fixed\n       and as such, its use is not recommended. Please use an alternative\n       history filtering tool such as git filter-repo. If you still need to use\n       git filter-branch, please carefully read the section called \u201cSAFETY\u201d (and\n       the section called \u201cPERFORMANCE\u201d) to learn about the land mines of\n       filter-branch, and then vigilantly avoid as many of the hazards listed\n       there as reasonably possible.\n<\/code><\/pre>\n<p>Warning aside, a few factors lead me to believe this was what I wanted in this particular scenario:<\/p>\n<ol type=\"1\">\n<li>All the faulty commits were fairly recent, I wouldn\u2019t touch very old history.<\/li>\n<li>I\u2019ve had experience running <code>filter-branch<\/code> from way back and felt confident<br \/>\nchoosing it again.<\/li>\n<li>The manpage has the exact use-case exemplified.<\/li>\n<\/ol>\n<p>With a few modifications from <a href=\"https:\/\/stackoverflow.com\/a\/750182\">this Stack Overflow answer<\/a>:<\/p>\n<pre><code>\u276f git filter-branch --env-filter '\nWRONG_EMAIL=\"<span \n                data-original-string='Is4aC2iH2SjEsUV+buc7\/w==7f4FgCUzE79GkIFoppUnAOy67J\/CCcUsadJ7aWFRm3XfFo='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>ma<span class=\"apbct-blur\">****<\/span>@<span class=\"apbct-blur\">*****<\/span>ne.localdomain<\/span>\"\nNEW_NAME=\"Martin Myrseth\"\nNEW_EMAIL=\"<span \n                data-original-string='KrXdJs4zslLfUmjg1pFv1w==7f4PX2GNo+n7WI71PASTYDtdckcPE7eKnfQ7LE2RZfH+UM='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>ma<span class=\"apbct-blur\">****<\/span>@<span class=\"apbct-blur\">*****<\/span>le.com<\/span>\"\n\nif [ \"$GIT_COMMITTER_EMAIL\" = \"$WRONG_EMAIL\" ]\nthen\n    export GIT_COMMITTER_NAME=\"$NEW_NAME\"\n    export GIT_COMMITTER_EMAIL=\"$NEW_EMAIL\"\nfi\nif [ \"$GIT_AUTHOR_EMAIL\" = \"$WRONG_EMAIL\" ]\nthen\n    export GIT_AUTHOR_NAME=\"$NEW_NAME\"\n    export GIT_AUTHOR_EMAIL=\"$NEW_EMAIL\"\nfi\n' --tag-name-filter cat -- --branches --tags\n<\/code><\/pre>\n<p>I do not have the output of this command ready at hand. It\u2019s a while since I ran<br \/>\nit, and I do not intending to do it again any time soon. All I can say is it<br \/>\nworked out nicely for me.<\/p>\n<p>I don\u2019t think there\u2019s much reason to invest a whole lot of effort into<br \/>\nunderstanding all the ins and outs of <code>filter-branch<\/code>. There are most likely<br \/>\nalways better options to solve the problems it too can solve, so try your best<br \/>\nto avoid it.<\/p>\n<p>Following are some other workarounds to avoid committing with a broken <code>user<\/code><br \/>\nconfiguration or ensuring that faults are at least concealed in command outputs.<\/p>\n<h3 id=\"git-templates-and-pre-commit-hooks\">Git templates and pre-commit hooks<\/h3>\n<p>Before all of the other mitigations outlined in the sections below I used to<br \/>\nhave a <code>.gittemplates<\/code> folder containing a few <code>git hooks<\/code> that would be added<br \/>\nto every newly created repository. One of these hooks was the <a href=\"https:\/\/github.com\/myme\/dotfiles\/blob\/cee48efdbfc34f2cf156234501e88337583b852c\/git\/.gittemplate\/hooks\/pre-commit\">pre-commit hook<\/a><br \/>\nwhich checked that I had a properly configured <code>user.name<\/code> and <code>user.email<\/code>.<\/p>\n<div id=\"cb26\" data-org-language=\"sh\">\n<pre><code><span id=\"cb26-1\"><span>#!\/usr\/bin\/env bash<\/span><\/span>\n<span id=\"cb26-2\"><span>if<\/span> <span>!<\/span><span>(<\/span><span>git<\/span> config user.name <span>&><\/span> \/dev\/null <span>&&<\/span> <span>git<\/span> config user.email <span>&><\/span> \/dev\/null<span>);<\/span> <span>then<\/span><\/span>\n<span id=\"cb26-3\">    <span>echo<\/span> <span>\"Please setup your repository with a user.name and user.email\"<\/span> <span>>&<\/span><span>2<\/span><\/span>\n<span id=\"cb26-4\">    <span>exit<\/span> 1<\/span>\n<span id=\"cb26-5\"><span>fi<\/span><\/span><\/code><\/pre>\n<\/div>\n<p>If I ever forgot to properly setup particularly the <code>user.email<\/code> for a specific<br \/>\nrepository then <code>git<\/code> wouldn\u2019t let me commit without annoying me with a warning.<br \/>\nSince I rarely change my name (I haven\u2019t yet), I would hardcode <code>user.name<\/code> into<br \/>\nmy user-global git configuration.<\/p>\n<p>Due to the chicken-and-egg problem, these hooks weren\u2019t created for my<br \/>\n<code>dotfiles<\/code> repo on new hosts because they\u2019re <em>in<\/em> the <code>dotfiles<\/code> repo. It\u2019s a<br \/>\nwhile since I abandoned this approach alltogether as it\u2019s obsoleted by the<br \/>\nsolution of the next section.<\/p>\n<p>Keep in mind this was added a while ago, and before I\u2019d learned about the<br \/>\nsuperior means of working around this problem which I\u2019ll get to below. This<br \/>\nsolution is most likely not what you want.<\/p>\n<h3 id=\"no-second-guessing-please\">No second guessing please!<\/h3>\n<p>One of the <code>git<\/code> defaults I\u2019m not very fond of is the <code>user.useConfigOnly<\/code><br \/>\nconfiguration which is <code>false<\/code> by default. Here\u2019s its excerpt from <code>man<br \/>\ngit-config(1)<\/code>:<\/p>\n<pre><code>user.useConfigOnly\n    Instruct Git to avoid trying to guess defaults for user.email and user.name,\n    and instead retrieve the values only from the configuration. For example, if\n    you have multiple email addresses and would like to use a different one for\n    each repository, then with this configuration option set to true in the\n    global config along with a name, Git will prompt you to set up an email\n    before making new commits in a newly cloned repository. Defaults to false.\n<\/code><\/pre>\n<p>I guess the documentation outlines my \u201cdefault\u201d use-case, which is to use<br \/>\ndifferent email addresses for the repository I work in. With the following<br \/>\nconfiguration <code>git<\/code> will refuse to commit when <code>user<\/code> configuration is missing,<br \/>\nthus obsoleting my <code>pre-commit<\/code> hook:<\/p>\n<pre><code>[user]\n  name = \"Martin Myrseth\"\n  useConfigOnly = true\n<\/code><\/pre>\n<h3 id=\"git-conditional-configuration\">Git conditional configuration<\/h3>\n<p>It\u2019s hard to argue against the fact that the <em>best<\/em> way to solve <em>any<\/em> problem,<br \/>\nis to not have the problem in the first place. Using some \u201cclever\u201d conditional<br \/>\nconfiguration sections it\u2019s possible to include additional configurations for<br \/>\ne.g. repositories within specific sub-directories on the filesystem, ensuring<br \/>\nthat there never <em>is<\/em> a partial <code>user<\/code> configuration.<\/p>\n<p>Once I became aware of this configuration trick I took more care as to where I<br \/>\nplaced repositories on disk. Making sure to have separate directories for<br \/>\npersonal and work related repos. With this repository directory layout, it\u2019s<br \/>\npossible to have a conditional section in <code>gitconfig<\/code> which applies additional<br \/>\nconfigurations to any repository matching the predicate (i.e. placement on<br \/>\ndisk):<\/p>\n<pre><code>[includeIf \"gitdir:~\/code\/work\/\"]\n    path = \".\/work_config\"\n<\/code><\/pre>\n<p>Any repository under <code>~\/code\/work<\/code> will include the configuration from<br \/>\n<code>.\/work_config<\/code>, which may contain something like the following:<\/p>\n<pre><code>[commit]\n    gpgSign = true\n[tag]\n    forceAnnotated = true\n    gpgSign = true\n[user]\n    email = \"<span \n                data-original-string='notAnsc05GaAyRQXA3Ks0w==7f4CtvOlbBav+5kD8eWSEymCg=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>ma<span class=\"apbct-blur\">****<\/span>@<span class=\"apbct-blur\">*<\/span>ay.job<\/span>\"\n    signingKey = \"<span \n                data-original-string='MASM5169ORqd7QVJHgmbDQ==7f4A8rR3kaLQ8UtftuBzaqO7w=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>ma<span class=\"apbct-blur\">****<\/span>@<span class=\"apbct-blur\">*<\/span>ay.job<\/span>\"\n<\/code><\/pre>\n<h3 id=\"mailmap\">.mailmap<\/h3>\n<p>Although the <code>filter-branch<\/code> command allows a full cleanup of the history of a<br \/>\n<code>git<\/code> repository, it shouldn\u2019t be understated the potential damage and<br \/>\n<em>inconvenience<\/em> such an operation has on the repository integrity. Rewriting<br \/>\nhistory has the viral effect of changing <code>SHA1<\/code> sums of all subsequent commits,<br \/>\nleading to parallel histories (old vs. new). This is most likely not what you<br \/>\nwant for public histories.<\/p>\n<p>On the other end of the spectrum <code>git<\/code> provides a rather convenient and<br \/>\nnon-destructive feature to solve this particular issue through its <code>mailmap<\/code><br \/>\nsupport. Quoting the <code>man gitmailmap<\/code>:<\/p>\n<blockquote>\n<p>If the file <code>.mailmap<\/code> exists at the toplevel of the repository \u2026 it is used<br \/>\nto map author and committer names and email addresses to canonical real names<br \/>\nand email addresses.<\/p>\n<\/blockquote>\n<p>The man page of <code>gitmailmap<\/code> contains syntactical examples of mailmap entries.<br \/>\nTo correct a simple incorrect email one can add an entry on the format:<\/p>\n<pre><code><<span \n                data-original-string='DwTOu7kz6kynKsttvatryQ==7f4GYsKy0DDz94PEUBN7F5iJg=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>pr<span class=\"apbct-blur\">****<\/span>@<span class=\"apbct-blur\">***<\/span>il.xx<\/span>> <<span \n                data-original-string='GPLRB0IrfjAZ2rejejAMQA==7f41jCcZpl2chJVbBRMeO\/9Vg=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>co<span class=\"apbct-blur\">****<\/span>@<span class=\"apbct-blur\">***<\/span>il.xx<\/span>>\n<\/code><\/pre>\n<p>The <code>.mailmap<\/code> can also correct <code>user.name<\/code> issues as well as correct <em>specific<\/em><br \/>\ncommits and so on. Here\u2019s the <a href=\"https:\/\/github.com\/myme\/dotfiles\/blob\/0736ab8da3312418750961c9eaa75d34da7bbada\/.mailmap\">.mailmap<\/a> file from my <code>dotfiles<\/code> which fixes up a<br \/>\nfew of my past mistakes:<\/p>\n<pre><code>Martin Myrseth <<span \n                data-original-string='nxX9byRXwOnM47mLlhjdZA==7f4JGelK+nt5yLUT8FO8PIqQQ=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'><span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">**<\/span>me.no<\/span>> <<span \n                data-original-string='TKkg0TgtlmR5DuJKhCQoKA==7f40oNA55IBzjanoFBwMkf1EA=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'><span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">**<\/span>me.no<\/span>>\nMartin Myrseth <<span \n                data-original-string='5xG8XebOmGrUnXnGwcJ11g==7f46veHvqamNy0KnmhYLtMXOg=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'><span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">**<\/span>me.no<\/span>> <<span \n                data-original-string='xuUBr7EeA3Q2LVYWiF2q7w==7f4cEjdgKSBZEFq7Gj3CuSUYFkGy9JULK8Gmmi5KPTjsA0='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>my<span class=\"apbct-blur\">*****<\/span>@<span class=\"apbct-blur\">***<\/span>il.com<\/span>>\nMartin Myrseth <<span \n                data-original-string='erllJox2ePBqJbys0VPyXA==7f4nKHjecWZRDot4yAbTDKECw=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'><span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">**<\/span>me.no<\/span>> <<span \n                data-original-string='BN0DbqmMFfQuGjPMBQ5OqA==7f4qcPEGylxdO7Enu\/0Vu7E\/8KG\/55Pff6+tgihsi\/m6Ac='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>mm<span class=\"apbct-blur\">******<\/span>@<span class=\"apbct-blur\">***<\/span>co.com<\/span>>\nMartin Myrseth <<span \n                data-original-string='wSvukqfM7TNg3c5bqgneUw==7f4Tgho4bFA3yImqvdsvBe7qQ=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'><span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">**<\/span>me.no<\/span>> <<span \n                data-original-string='4Dsvus94vAyk1Nuhm+FY9Q==7f4cafUuNpAoEKtR+aNfNVwAv+1NUfSPZ7G+DYW320xt+E='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>my<span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">***<\/span>le.localdomain<\/span>>\nMartin Myrseth <<span \n                data-original-string='5AZ9uoCYScnlXhZbYTyz\/g==7f45wpBVc+LxW7p8e8ymx6\/rQ=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'><span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">**<\/span>me.no<\/span>> <<span \n                data-original-string='BSbFUkkBtIMi+zW+7fZ7\/w==7f4bNpSgszjfN\/PRX6ALEBPYkdARppdEtPogzCvcCKHOTg='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>my<span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">*<\/span>ap.localdomain<\/span>>\n<\/code><\/pre>\n<h2 id=\"octopus-merge\">Octopus merge ????<\/h2>\n<p>I must admit, I never use this, but I remember being amazed the first time I learned about the many-parent merge ability of <code>git<\/code> long ago.<\/p>\n<p>I would assume most people live their life thinking a merge commit is just the<br \/>\ncombined result of <em>two<\/em> somewhat related histories. Ideally two histories that<br \/>\nforked off one another in (hopefully) the not too distant past.<\/p>\n<p>Yet, we\u2019ve already seen and debunked the fact that histories have to be<br \/>\n\u201csomewhat related\u201d in order to be merged. That\u2019s what the \u201cabsorb some other<br \/>\nrepository\u201d functionality covered in the <a href=\"http:\/\/myme.no\/#orphan-commits\">orphan commits<\/a> section was all about.<\/p>\n<p>I guess then it comes as no surprise that the assumption of merges only ever<br \/>\nhaving just two parents is <em>also<\/em> not a hard limitation.<\/p>\n<h3 id=\"tentacles\">Tentacles<\/h3>\n<p>Let\u2019s see how we can create a many-parent merge commit, called an \u201coctopus<br \/>\nmerge\u201d, by starting off a new repository and adding a bunch of branches to it:<\/p>\n<pre><code>\u276f mkdir octopus\n\u276f cd octopus\/\n\u276f git init\nInitialized empty Git repository in \/home\/myme\/tmp\/octopus\/.git\/\n\n\u276f git commit --allow-empty -m 'Initial commit'\nAuthor identity unknown\n\n*** Please tell me who you are.\n\nRun\n\n  git config --global user.email \"<span \n                data-original-string='pcvRowXIhyXufb4jNqTN2Q==7f4bVJCg5WPlriB88Tyrk\/pEA=='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>yo<span class=\"apbct-blur\">*<\/span>@<span class=\"apbct-blur\">*****<\/span>le.com<\/span>\"\n  ...\n<\/code><\/pre>\n<p>Ah \u2026 right. Forgot about that ????<\/p>\n<pre><code>\u276f git config user.email '<span \n                data-original-string='7UoQKe3bgNnhulAgLGmU4A==7f4zaqEnrA1dUgjaWwLmEGKBOQmtj46gl3TM0PWc8MtrsA='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>da<span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">******<\/span>le.org<\/span>'\n\u276f git commit --allow-empty -m 'Initial commit'\n[main (root-commit) 9ff0a71] Initial commit\n<\/code><\/pre>\n<p>At this point we have a new <code>git<\/code> repository with a single <code>main<\/code> branch containing a single <em>empty<\/em> commit:<\/p>\n<pre><code>\u276f git log --all --oneline --graph\n* 9ff0a71 (HEAD -> main) Initial commit\n<\/code><\/pre>\n<p>Let\u2019s create some branches with content:<\/p>\n<pre><code>\u276f git checkout -b tentacle\nSwitched to a new branch 'tentacle'\n\u276f date > tentacle.txt\n\u276f git add tentacle.txt\n\u276f git commit -m 'Add day of tentacle.txt'\n[tentacle 4dadc16] Add day of tentacle.txt\n 1 file changed, 1 insertion(+)\n create mode 100644 tentacle.txt\n\n<\/code><\/pre>\n<p>Yay, one limb (aka branch) in place!<\/p>\n<pre><code>\u276f git log --all --oneline --graph\n* 4dadc16 (HEAD -> tentacle) Add day of tentacle.txt\n* 9ff0a71 (main) Initial commit\n<\/code><\/pre>\n<p>But creating limbs is tedious. Let\u2019s push the fast-forward button:<\/p>\n<div id=\"cb38\">\n<pre><code><span id=\"cb38-1\"><span>for<\/span> count <span>in<\/span> nine eight seven six five four three two one<span>;<\/span> <span>do<\/span><\/span>\n<span id=\"cb38-2\">  <span>limb<\/span><span>=<\/span><span>\"<\/span><span>${count}<\/span><span>tacle\"<\/span><\/span>\n<span id=\"cb38-3\">  <span>git<\/span> checkout <span>-b<\/span> <span>\"<\/span><span>$limb<\/span><span>\"<\/span> main<\/span>\n<span id=\"cb38-4\">  <span>date<\/span> <span>><\/span> <span>\"<\/span><span>${limb}<\/span><span>.txt\"<\/span><\/span>\n<span id=\"cb38-5\">  <span>git<\/span> add <span>\"<\/span><span>${limb}<\/span><span>.txt\"<\/span><\/span>\n<span id=\"cb38-6\">  <span>git<\/span> commit <span>-m<\/span> <span>\"Add <\/span><span>${limb}<\/span><span>\"<\/span><\/span>\n<span id=\"cb38-7\"><span>done<\/span><\/span><\/code><\/pre>\n<\/div>\n<pre><code>Switched to a new branch 'ninetacle'\n[ninetacle 3f7a95e] Add ninetacle\n 1 file changed, 1 insertion(+)\n create mode 100644 ninetacle.txt\nSwitched to a new branch 'eighttacle'\n[eighttacle e9cd39a] Add eighttacle\n 1 file changed, 1 insertion(+)\n create mode 100644 eighttacle.txt\nSwitched to a new branch 'seventacle'\n...\nSwitched to a new branch 'sixtacle'\n...\nSwitched to a new branch 'fivetacle'\n...\nSwitched to a new branch 'fourtacle'\n...\nSwitched to a new branch 'threetacle'\n...\nSwitched to a new branch 'twotacle'\n...\nSwitched to a new branch 'onetacle'\n[onetacle c78c58a] Add onetacle\n 1 file changed, 1 insertion(+)\n create mode 100644 onetacle.txt\n<\/code><\/pre>\n<p>And we got ourselves a bunch of limbs!<\/p>\n<pre><code>\u276f git log --all --oneline --graph\n* e9cd39a (eighttacle) Add eighttacle\n| * e310cbc (fivetacle) Add fivetacle\n|\/\n| * 44ad755 (fourtacle) Add fourtacle\n|\/\n| * 3f7a95e (ninetacle) Add ninetacle\n|\/\n| * c78c58a (HEAD -> onetacle) Add onetacle\n|\/\n| * 6be7cf4 (seventacle) Add seventacle\n|\/\n| * a54e5c1 (sixtacle) Add sixtacle\n|\/\n| * 3b1a5da (threetacle) Add threetacle\n|\/\n| * bb79112 (twotacle) Add twotacle\n|\/\n| * 4dadc16 (tentacle) Add day of tentacle.txt\n|\/\n* 9ff0a71 (main) Initial commit\n<\/code><\/pre>\n<p>Time to assemble our squid:<\/p>\n<pre><code>\u276f git merge tentacle ninetacle eighttacle seventacle sixtacle fivetacle fourtacle threetacle twotacle onetacle -m 'Assemble squid'\nFast-forwarding to: tentacle\nTrying simple merge with ninetacle\nTrying simple merge with eighttacle\nTrying simple merge with seventacle\nTrying simple merge with sixtacle\nTrying simple merge with fivetacle\nTrying simple merge with fourtacle\nTrying simple merge with threetacle\nTrying simple merge with twotacle\nTrying simple merge with onetacle\nMerge made by the 'octopus' strategy.\n eighttacle.txt | 1 +\n fivetacle.txt  | 1 +\n fourtacle.txt  | 1 +\n ninetacle.txt  | 1 +\n onetacle.txt   | 1 +\n seventacle.txt | 1 +\n sixtacle.txt   | 1 +\n tentacle.txt   | 1 +\n threetacle.txt | 1 +\n twotacle.txt   | 1 +\n 10 files changed, 10 insertions(+)\n create mode 100644 eighttacle.txt\n create mode 100644 fivetacle.txt\n create mode 100644 fourtacle.txt\n create mode 100644 ninetacle.txt\n create mode 100644 onetacle.txt\n create mode 100644 seventacle.txt\n create mode 100644 sixtacle.txt\n create mode 100644 tentacle.txt\n create mode 100644 threetacle.txt\n create mode 100644 twotacle.txt\n<\/code><\/pre>\n<p>The end result is the most wonderful <code>git<\/code> graph ever!<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/myme.no\/images\/git-octopus-merge.png\"><\/p>\n<p>We\u2019ve managed to create a new commit in our repository with no less than <em>ten<\/em><br \/>\nparents. We can also confirm this using <code>git show<\/code>:<\/p>\n<pre><code>\u276f git show\ncommit 442b9a2852fc2707517690f1a994c1c5a38ac20b (HEAD -> main)\nMerge: 4dadc16 3f7a95e e9cd39a 6be7cf4 a54e5c1 e310cbc 44ad755 3b1a5da bb79112 c78c58a\nAuthor: Martin Myrseth <<span \n                data-original-string='WQJRw+7sC16iQf4lYrknTA==7f4A1L2ZUSCgMUMan+oUBZII5f16S2sHRPNBCAciZePetY='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>da<span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">******<\/span>le.org<\/span>>\nDate:   Fri Jan 20 01:09:57 2023 +0100\n\n    Assemble squid\n<\/code><\/pre>\n<p>Note the <code>Merge:<\/code> line with all the parent <code>SHA1<\/code> sums. Also notice how <code>git<br \/>\nshow<\/code> deviates from the more \u201cvanilla\u201d <code>cat-file -p<\/code> output by renaming each of the<br \/>\nmetadata labels.<\/p>\n<h3 id=\"use-cases\">Use-cases<\/h3>\n<p>Honestly, in practice I haven\u2019t found a single valid use-case for octopus merges<br \/>\nwhich aren\u2019t already covered by sequencing a series of merges, one after the<br \/>\nother. Perhaps there are some integration use-cases out there which really let\u2019s<br \/>\nthe octopus merge strategy shine. Let me know!<\/p>\n<p>I should also note that the octopus merge strategy is quite conservative and<br \/>\nbluntly refuses to merge anything which doesn\u2019t trivially apply without<br \/>\nconflicts. I imaging trying to juggle changes and their origins during a merge<br \/>\nresolution to be quite the mess.<\/p>\n<p>One thing I like though about the octopus merge is that it quite visually shows<br \/>\nhow simple the <code>git<\/code> graph model really is. It has helped me build intuition<br \/>\nabout what goes on during a merge operation in <code>git<\/code>.<\/p>\n<h3 id=\"the-dishonest-merge\">The dishonest merge ????<\/h3>\n<p>While on the topic of merges, I\u2019d like to quickly break down some of the<br \/>\nmisconception(?) that merge commits are something special in <code>git<\/code>.<\/p>\n<p>It might be true that there\u2019s some special sauce involving <code>merge-bases<\/code> and<br \/>\nheuristics in order to determine the merge <em>result<\/em> of joining multiple<br \/>\nhistories. But once a commit with multiple parents have been made there\u2019s no<br \/>\nrequirement that whichever <code>tree<\/code> is associated with a merge commit to make any<br \/>\nkind of sense with regards to the merge operation its parent relationship<br \/>\nreflects.<\/p>\n<p>Let\u2019s continue from where the octopus merge left off and see that we\u2019ve got all<br \/>\nten *tacles in place:<\/p>\n<pre><code>\u276f ls -la\ntotal 52\ndrwxr-xr-x 3 myme users 4096 Jan 20 01:09 .\ndrwxr-xr-x 7 myme users 4096 Jan 20 00:42 ..\n-rw-r--r-- 1 myme users   32 Jan 20 01:09 eighttacle.txt\n-rw-r--r-- 1 myme users   32 Jan 20 01:09 fivetacle.txt\n-rw-r--r-- 1 myme users   32 Jan 20 01:09 fourtacle.txt\ndrwxr-xr-x 9 myme users 4096 Jan 20 01:09 .git\n-rw-r--r-- 1 myme users   32 Jan 20 01:09 ninetacle.txt\n-rw-r--r-- 1 myme users   32 Jan 20 01:09 onetacle.txt\n-rw-r--r-- 1 myme users   32 Jan 20 01:09 seventacle.txt\n-rw-r--r-- 1 myme users   32 Jan 20 01:09 sixtacle.txt\n-rw-r--r-- 1 myme users   32 Jan 20 01:09 tentacle.txt\n-rw-r--r-- 1 myme users   32 Jan 20 01:09 threetacle.txt\n-rw-r--r-- 1 myme users   32 Jan 20 01:09 twotacle.txt\n<\/code><\/pre>\n<p>There\u2019s nothing stopping us at this point to <em>delete<\/em> everything introduced by<br \/>\nmerging all the tentacles and amending the <code>HEAD<\/code> commit:<\/p>\n<pre><code>\u276f git rm *.txt\nrm 'eighttacle.txt'\nrm 'fivetacle.txt'\nrm 'fourtacle.txt'\nrm 'ninetacle.txt'\nrm 'onetacle.txt'\nrm 'seventacle.txt'\nrm 'sixtacle.txt'\nrm 'tentacle.txt'\nrm 'threetacle.txt'\nrm 'twotacle.txt'\n\n\u276f git commit --amend -C HEAD\n[main 8494ef5] Assemble squid\n Date: Fri Jan 20 01:09:57 2023 +0100\n<\/code><\/pre>\n<p>All files are gone:<\/p>\n<pre><code>\u276f ls -l\ntotal 0\n<\/code><\/pre>\n<p>Yet the default view of <code>git show<\/code> of the merge doesn\u2019t hint at anything suspicious:<\/p>\n<pre><code>commit de3e016de71484e62e6ac7e6dda08fe7f9d85af4 (HEAD -> main)\nMerge: 4dadc16 3f7a95e e9cd39a 6be7cf4 a54e5c1 e310cbc 44ad755 3b1a5da bb79112 c78c58a\nAuthor: Martin Myrseth <<span \n                data-original-string='HvwJ92wIdB+\/kHrADzBu9A==7f42u11CZJud3Fa2Bz6s3t1mOsRBJEU+U7KfbgbGPRP\/4I='\n                class='apbct-email-encoder'\n                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>da<span class=\"apbct-blur\">**<\/span>@<span class=\"apbct-blur\">******<\/span>le.org<\/span>>\nDate:   Fri Jan 20 01:09:57 2023 +0100\n\n    Assemble squid\n\n<\/code><\/pre>\n<p>While asking it to also include the <em>merge<\/em> commits it\u2019s fairly obvious that<br \/>\nsomebody have been messing around with the merge resolution:<\/p>\n<pre><code>\u276f git show --pretty=oneline -m --stat\nde3e016de71484e62e6ac7e6dda08fe7f9d85af4 (from 4dadc16d89758ed1625223286e1218b63c988313) (HEAD -> main) Assemble squid\n tentacle.txt | 1 -\n 1 file changed, 1 deletion(-)\nde3e016de71484e62e6ac7e6dda08fe7f9d85af4 (from 3f7a95ecac18a92451f7e205c8ea0bb2366c2e97) (HEAD -> main) Assemble squid\n ninetacle.txt | 1 -\n 1 file changed, 1 deletion(-)\nde3e016de71484e62e6ac7e6dda08fe7f9d85af4 (from e9cd39ad4664b04f29263250396ec1b270e4eeb8) (HEAD -> main) Assemble squid\n eighttacle.txt | 1 -\n 1 file changed, 1 deletion(-)\nde3e016de71484e62e6ac7e6dda08fe7f9d85af4 (from 6be7cf4b00f640a32d61a9e205e0b4a1e18b3bb8) (HEAD -> main) Assemble squid\n seventacle.txt | 1 -\n 1 file changed, 1 deletion(-)\nde3e016de71484e62e6ac7e6dda08fe7f9d85af4 (from a54e5c16f807a3f9aad8dd0c5187abcc9e6b6c7d) (HEAD -> main) Assemble squid\n sixtacle.txt | 1 -\n 1 file changed, 1 deletion(-)\nde3e016de71484e62e6ac7e6dda08fe7f9d85af4 (from e310cbcfecaa3cb6f08084a64c18318f7552a8a7) (HEAD -> main) Assemble squid\n fivetacle.txt | 1 -\n 1 file changed, 1 deletion(-)\nde3e016de71484e62e6ac7e6dda08fe7f9d85af4 (from 44ad755cc07047ee3dd25c5170aa9d4dde60475c) (HEAD -> main) Assemble squid\n fourtacle.txt | 1 -\n 1 file changed, 1 deletion(-)\nde3e016de71484e62e6ac7e6dda08fe7f9d85af4 (from 3b1a5da6c6e5b2d0b93517dda20c3295ed893374) (HEAD -> main) Assemble squid\n threetacle.txt | 1 -\n 1 file changed, 1 deletion(-)\nde3e016de71484e62e6ac7e6dda08fe7f9d85af4 (from bb791123be4bd03a0c6427d1990cd57898dd9793) (HEAD -> main) Assemble squid\n twotacle.txt | 1 -\n 1 file changed, 1 deletion(-)\nde3e016de71484e62e6ac7e6dda08fe7f9d85af4 (from c78c58a2debbab2d88ed0e747a54f4d750f8378f) (HEAD -> main) Assemble squid\n onetacle.txt | 1 -\n 1 file changed, 1 deletion(-)\n<\/code><\/pre>\n<p>In the end, a merge commit in <code>git<\/code> tracks a <code>tree<\/code><a href=\"http:\/\/myme.no\/#fn6\" id=\"fnref6\" role=\"doc-noteref\"><sup>6<\/sup><\/a> \u2013 like any other commit \u2013<br \/>\nand it only <em>extends<\/em> on the parent commit metadata by including one <code>parent<\/code><br \/>\nfield for all commits that serves as inputs to the merge operation. Furthermore,<br \/>\nit places no constraints onto the changes <em>made<\/em> to the <code>tree<\/code> associated with<br \/>\nthat commit. Which basically gives a committer full \u201cartistic freedom\u201d as to<br \/>\nwhat should be the result of a merge, ranging from the trivial \u201csum of all<br \/>\ndifferences\u201d or minor conflict resolutions to absolutely wild rewrites that had<br \/>\nabsolutely nothing to do with the differences that went into a merge to begin with.<\/p>\n<h2 id=\"rounding-off\">Rounding off<\/h2>\n<p>I\u2019m sure that many of these features of <code>git<\/code> are by no means news to the<br \/>\nreaders of this post, and I\u2019m not exactly sure what pushed me towards writing it<br \/>\nin the first place. If anything, it\u2019s a recollection of (silly) things I\u2019ve done<br \/>\nin the past. Hopefully it could also inspire people to go learn tools that serve<br \/>\nas their daily drivers beyond just the basic or core functionality.<\/p>\n<p>I\u2019m a believer that not everything we learn or do has to necessarily have some<br \/>\nobvious usefulness in and of itself. Often when learning tools, techniques,<br \/>\nprogramming languages, and everything else in the field of software, I find that<br \/>\ngoing off on tangents can help build intuition about core concepts, ultimately<br \/>\nleading to a deeper understanding. Of course, the few times this peripheral<br \/>\nknowledge is of <em>actual<\/em> use in real-life situations it\u2019s even better.<\/p>\n<p>I <em>do<\/em> place great value in utility, but I also like to remind people to have<br \/>\nfun, experiment, and to build simply for the sake of building. Which, while<br \/>\ntyping out this summary, reminded me of this recent post \u2013 <a href=\"https:\/\/twitchard.github.io\/posts\/2023-01-18-unicycles.html\">\u201cTake your pragmatism for a unicycle ride\u201d<\/a> \u2013 which appeared on <a href=\"https:\/\/lobste.rs\/s\/wdeoob\/take_your_pragmatism_for_unicycle_ride\">my favorite tech aggregator site<\/a> the other day. A post which also touched on the importance of<br \/>\n<em>developer energy<\/em>. That\u2019s something I consider very central to my own<br \/>\nmotivation and mental well-being. If there\u2019s fun to be had in learning \u2013 or<br \/>\nbuilding \u2013 we\u2019re much less likely to burn out from it.<\/p>\n<section role=\"doc-endnotes\">\n<hr>\n<ol>\n<li id=\"fn1\" role=\"doc-endnote\">\n<p>The commit onto which to rebase\/rewrite the selected list of commits.<a href=\"http:\/\/myme.no\/#fnref1\" role=\"doc-backlink\">\u21a9\ufe0e<\/a><\/p>\n<\/li>\n<li id=\"fn2\" role=\"doc-endnote\">\n<p>Diffing and cherry-pick can of course also be done towards remote refs,<br \/>\nbut for the incremental builds case it\u2019s <em>very<\/em> useful to update a branch<br \/>\n<em>before<\/em> checking it out.<a href=\"http:\/\/myme.no\/#fnref2\" role=\"doc-backlink\">\u21a9\ufe0e<\/a><\/p>\n<\/li>\n<li id=\"fn3\" role=\"doc-endnote\">\n<p>By \u201cpassive\u201d I\u2019m talking about any branch that\u2019s currently not checked<br \/>\nout in the repository or any associated <code>worktree<\/code>.<a href=\"http:\/\/myme.no\/#fnref3\" role=\"doc-backlink\">\u21a9\ufe0e<\/a><\/p>\n<\/li>\n<li id=\"fn4\" role=\"doc-endnote\">\n<p>At the time of writing.<a href=\"http:\/\/myme.no\/#fnref4\" role=\"doc-backlink\">\u21a9\ufe0e<\/a><\/p>\n<\/li>\n<li id=\"fn5\" role=\"doc-endnote\">\n<p>Yeah, yeah\u2026 Not that long ago, I know. But I did also track<br \/>\nconfigurations prior to starting my \u201cnew dotfiles\u201d journey. However, I guess<br \/>\nthat history wasn\u2019t something I cared to take along with me and so the initial<br \/>\ncommit wasn\u2019t an an empty commit, but more the traditional \u201cInitial import\u201d of<br \/>\nexisting files.<a href=\"http:\/\/myme.no\/#fnref5\" role=\"doc-backlink\">\u21a9\ufe0e<\/a><\/p>\n<\/li>\n<li id=\"fn6\" role=\"doc-endnote\">\n<p>Remember that <code>git<\/code> operates on <em>snapshots<\/em>, not changes (aka patches).<a href=\"http:\/\/myme.no\/#fnref6\" role=\"doc-backlink\">\u21a9\ufe0e<\/a><\/p>\n<\/li>\n<\/ol>\n<\/section>\n<\/section>\n<p><a href=\"https:\/\/myme.no\/posts\/2023-01-22-git-commands-you-do-not-need.html\" class=\"button purchase\" rel=\"nofollow noopener\" target=\"_blank\">Read More<\/a><br \/>\n Leigha Guillemette<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ah, git! Love it, hate it. Few things are as central to the modern software development workflow as source-control management (SCM) tools. Although there have been and still are plenty of alternatives to git in the world of SCMs, none other seem quite as prevalent both in open-source and the enterprise. Regardless of how central<\/p>\n","protected":false},"author":1,"featured_media":600412,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[34701,4188,46],"tags":[],"class_list":{"0":"post-600411","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-commands","8":"category-probably","9":"category-technology"},"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/newsycanuse.com\/index.php\/wp-json\/wp\/v2\/posts\/600411","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/newsycanuse.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/newsycanuse.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/newsycanuse.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/newsycanuse.com\/index.php\/wp-json\/wp\/v2\/comments?post=600411"}],"version-history":[{"count":0,"href":"https:\/\/newsycanuse.com\/index.php\/wp-json\/wp\/v2\/posts\/600411\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/newsycanuse.com\/index.php\/wp-json\/wp\/v2\/media\/600412"}],"wp:attachment":[{"href":"https:\/\/newsycanuse.com\/index.php\/wp-json\/wp\/v2\/media?parent=600411"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/newsycanuse.com\/index.php\/wp-json\/wp\/v2\/categories?post=600411"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/newsycanuse.com\/index.php\/wp-json\/wp\/v2\/tags?post=600411"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}