<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>git hook on Digi Hunch</title><link>https://static.digihunch.com/tag/git-hook/</link><description>Recent content in git hook on Digi Hunch</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Sat, 20 Jul 2024 16:37:28 -0400</lastBuildDate><atom:link href="https://static.digihunch.com/tag/git-hook/index.xml" rel="self" type="application/rss+xml"/><item><title>Clean up Git repository</title><link>https://static.digihunch.com/2019/10/clean-up-your-git-repository/</link><pubDate>Sat, 26 Oct 2019 20:33:00 -0400</pubDate><guid>https://static.digihunch.com/2019/10/clean-up-your-git-repository/</guid><description>&lt;p class="wp-block-paragraph"&gt;A BitBucket repo has a hard limit of 2GB in size, and soft limit of 1GB. This is&amp;nbsp;&lt;a href="https://confluence.atlassian.com/bitbucket/what-kind-of-limits-do-you-have-on-repository-file-size-273877699.html"&gt;not expandable&lt;/a&gt;&amp;nbsp;as per&amp;nbsp;Bitbucket and contributors will start receiving warnings once soft limit is reached. We can tell the usage of a repo from the landing page of the repo in BitBucket.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://static.digihunch.com/wp-content/uploads/2020/03/git.png" alt="" class="wp-image-694" width="202" height="300"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Git is a distributed version control system for source code management, which implies the followings:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;It is intended for source code, or configuration code; but not for storing build artifacts, or installers;&lt;/li&gt;&#10;&lt;li&gt;Git remembers every single commit, including the ones associated with large files;&lt;/li&gt;&#10;&lt;li&gt;Even a contributor deletes a large file (&amp;#8220;git rm filename&amp;#8221;) after commit, the large file is only removed from the HEAD. The historical commit still stores the file. After all, the whole point of version control is to survive crazy deletion.&lt;/li&gt;&#10;&lt;li&gt;distributed means that those large files will be pulled down to contributors laptop (waste everybody&amp;#8217;s space although up to 2G:);&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With all these implications, shrinking the size of a repo isn&amp;#8217;t as straightforward as just removing large files from current commit. We&amp;#8217;d have to&amp;nbsp;rewrite the commit history. Here are the steps we should take once repo size grows over the soft limit.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-clean-up-remote-orphaned-branches"&gt;Clean up remote orphaned branches&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Removing these branches (remotes/origin/branchname) per se does not free up space. It simplifies the branch structure, leaving /remote/origin/HEAD the only branch left to cleanse for the rest of the steps.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# git push origin --delete branchname&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 class="wp-block-heading" id="h-remove-useless-files-in-current-commit-head"&gt;Remove useless files in current commit (HEAD)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In this step we remove useless files in current commit. Again we should not expect much space freed because all file committed previously, even deleted, are still stored. They are just now showing up in the working directory. For this step, we can create a separate local dir on Mac:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir -p /Users/digihunch/repo-cleanup&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd /Users/digihunch/repo-cleanup&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now within the new directory, we create a bare repo and then the full repo:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git clone --mirror https://gh@bitbucket.org/digihunch/source.git&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git clone https://gh@bitbucket.org/digihunch/source.git&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then we dive into the full repo and identify the large files:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;find . -type f -size +1000k -exec ls -lh {} \; |awk &amp;#39;{print $9&amp;#34;:&amp;#34; $5}&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;We can run &amp;#8220;git rm &amp;#8221; against the files identified as too large or deletable. Then commit and push to remote repo. This removes large files from current commit.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-remove-large-file-and-the-relevant-commits-in-the-history"&gt;Remove large file and the relevant commits in the history&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As previously mentioned, we have to re-write the history so history forget about the large files. After this step, the historical commits that large files are associated with will all be deleted. Compare the two charts below to understand what the effect is:&lt;/p&gt;&#10;&lt;figure class="wp-block-gallery aligncenter has-nested-images columns-default is-cropped wp-block-gallery-3 is-layout-flex wp-block-gallery-is-layout-flex"&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1206" height="1038" data-id="695" src="https://static.digihunch.com/wp-content/uploads/2020/03/git1.png" alt="" class="wp-image-695"/&gt;&lt;/figure&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1234" height="852" data-id="700" src="https://static.digihunch.com/wp-content/uploads/2020/03/git2-1.png" alt="" class="wp-image-700"/&gt;&lt;/figure&gt;&#10;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We can use the bare repo created in the last step, with &amp;#8220;git filter-branch&amp;#8221; tool to cleanse the branch tree. Some advocate as a faster third party tool&amp;nbsp;&lt;a href="https://rtyley.github.io/bfg-repo-cleaner/"&gt;BFG Repo-Cleaner&lt;/a&gt;&amp;nbsp;as a faster, third-party alternative but I usually lean towards native tool.&amp;nbsp;This&amp;nbsp;&lt;a href="https://www.nicoespeon.com/en/2014/04/clean-git-repo-like-a-boss/"&gt;article&lt;/a&gt;&amp;nbsp;explains the command switches.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git filter-branch -f --tree-filter &amp;#34;rm -rf \large_file.zip&amp;#34; --prune-empty -- --all&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;After this steps the repo should be cleansed. According to this&amp;nbsp;&lt;a href="https://confluence.atlassian.com/bitbucket/reduce-repository-size-321848262.html"&gt;guide&lt;/a&gt;&amp;nbsp;from BitBucket, we still need to contact their support to run a garbage collection for us in order to see the size change. It even takes time for the size to be reflected after garbage collection. This&amp;nbsp;&lt;a href="https://www.saschawillems.de/blog/2017/09/10/how-to-shrink-down-a-github-repository/"&gt;reference&lt;/a&gt;&amp;nbsp;also does great job explaining what we need to do.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-other-contributors-re-sync-history"&gt;Other contributors re-sync history&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It is important to understand that the step above modifies history. Although the commit hash did not change, they are assigned with different commit-ids and you can tell from the commit history where it displays former commit id.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This activity only affects remote repository. Each contributor&amp;#8217;s local repository still stores the old commits and should be sync&amp;#8217;ed with the remote origin by deleting the entire repo and run &amp;#8220;git clone&amp;#8221; again. Although not welcomed by every individual contributors, but it is a necessary evil and better approached with explicit instruction.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Because this activity takes higher risks, changes each commit, involves vendor support and requires activities by each contributor, the support team should focus on preventing this from happening instead of fixing it.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-configure-pre-commit-hook-as-a-preventive-measure"&gt;Configure pre-commit hook as a preventive measure&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As we have more Ansible tasks related, working directory becomes complicated and sometimes contributors accidentally committed large unwanted files (and pushed into the remote repo).&amp;nbsp;Down the road, the best practice is to prevent contributors from committing junks.&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The best spot to detect this should be a pre-receive hook on the server side, which is only available with self-hosted Bitbucket Server. Unfortunately, this is not a viable option for&amp;nbsp;&lt;a href="https://community.atlassian.com/t5/Bitbucket-questions/Does-Bitbucket-Cloud-support-git-pre-receive-hooks/qaq-p/950235"&gt;Bitbucket cloud&lt;/a&gt;. Our best bet is client-side pre-commit hook, in which a script&amp;nbsp;performs size check when contributors run &amp;#8220;git commit&amp;#8221;. The purpose is to fail the commit if total file size is over the limit (20M), and the hook itself should be version controlled as well. Compared to (server side) pre-receive hook, the drawback of (client side) pre-commit hook is it requires initial client configuration. The upside is it captures large files before commit.&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This hook can be a shell script as simple as this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;commitsizelimit&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;stagedfilelist&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;git diff --name-only --cached&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;stagedfilecnt&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$stagedfilelist&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;| sed &lt;span style="color:#e6db74"&gt;&amp;#39;/^\s*$/d&amp;#39;&lt;/span&gt; |wc -l&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[[&lt;/span&gt; $stagedfilecnt -gt &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#f92672"&gt;]]&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; totalcommitsize&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;du -cm $stagedfilelist | tail -1 | cut -f 1&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Redirect output to stderr.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; exec 1&amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[[&lt;/span&gt; $totalcommitsize &amp;gt; $commitsizelimit &lt;span style="color:#f92672"&gt;]]&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;Warning: Total size of all files in staging area is &amp;#34;&lt;/span&gt;$totalcommitsize&lt;span style="color:#e6db74"&gt;&amp;#34;MB, exceeding the limit of &amp;#34;&lt;/span&gt;$commitsizelimit&lt;span style="color:#e6db74"&gt;&amp;#34;MB.&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34; To list files by size, run &amp;#39;du -ch \$(git diff --name-only --cached)&amp;#39;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34; To drop large ones from staging area with &amp;#39;git rm -f filename&amp;#39;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34; To bypass this limit, use &amp;#39;git commit --no-verify&amp;#39;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; exit &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In the repo we will have a .githook directory to store hooks (e.g. ~/source/.githooks/pre-commit) and point to the hooks directory using the following command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git config core.hooksPath .githooks&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2019/10/storage-nitty-gritty-4-of-5-backup-and-archive-solutions/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Storage Nitty-Gritty 4 of 5 – Backup and Archive Solutions&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2019/11/networking-basics-layer-1-and-layer-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Networking Basics 1 of 3 – Layer 1 through Layer 3&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Git Explained 2 of 2</title><link>https://static.digihunch.com/2019/06/git-explained-2-of-2/</link><pubDate>Tue, 18 Jun 2019 17:06:00 -0400</pubDate><guid>https://static.digihunch.com/2019/06/git-explained-2-of-2/</guid><description>&lt;p class="wp-block-paragraph"&gt;This is a continuation from &lt;a href="https://static.digihunch.com/2019/06/git-explained-1-of-2/"&gt;Git Explained 1 of 2 &lt;/a&gt;where the fundamental concepts are covered. In this article we introduce some tools for customization and maintenance.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As for Git configuration, there are two files to dictate your Git configuration. ~/.gitconfig and .git/config in project directory. Running `git config &amp;#8211;list &amp;#8211;show-origin` shows all config entries and where they are from. For example you can custom your Git hooks location. Neither of the two files are being version controlled, so the configuration is only effective in the client environment,&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;Server side Git hooks&lt;/strong&gt;&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Git implementation supports server side hooks (pre-receive, update, post-receive). They are bash scripts placed in .git/hooks with specific names, fired upon event occurrence. Exit code of 1 from the scripts fails the event. Since server side Git hook consumes server resources, many repository hosting vendors (e.g. &lt;a href="https://community.atlassian.com/t5/Bitbucket-questions/Does-Bitbucket-Cloud-support-git-pre-receive-hooks/qaq-p/950235"&gt;BitBucket Cloud&lt;/a&gt;) do not support it. You will need to enable it in self-hosted servers (e.g. BitBucketServer).&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;Client side Git hooks&lt;/strong&gt;&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Since server side hooks are not widely supported in every vendor, client-side Git hooks is good alternative places to implement functions such as code style check, commit size check, etc&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The default directory for hooks is .git/hooks/ under the project directory is not version controlled and not easy to share with the team. If the hook needs to be shared among project contributors, we can place hooks files in .githooks/ under project directory. This will make the hook files version controlled. In addition, we need to point the hooks to this directory in configuration, by running `git config core.hooksPath .githooks` from project directory.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img loading="lazy" decoding="async" width="1020" height="767" src="https://static.digihunch.com/wp-content/uploads/2019/10/git-hooks.png" alt="" class="wp-image-211"/&gt;&lt;/figure&gt;&#10;&lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;Web hooks &lt;/strong&gt;&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Web hooks can be thought of as an event notification mechanism. It is a common feature provided by VCS repository hosting providers. If a certain type of event occurs to the repo, web hook will fire an RESTful API call. The HTTP Endpoint, authentication secret and event payload are pre-configured in the repo settings. Web hooks are commonly supported by Git-based repo implementation, such as &lt;a href="https://confluence.atlassian.com/bitbucket/manage-webhooks-735643732.html"&gt;BitBucket cloud&lt;/a&gt;, &lt;a href="https://developer.github.com/webhooks/"&gt;GitHub&lt;/a&gt;, &lt;a href="https://docs.gitlab.com/ee/user/project/integrations/webhooks.html"&gt;GitLab&lt;/a&gt; or &lt;a href="https://docs.aws.amazon.com/codecommit/latest/userguide/how-to-notify.html"&gt;AWS CodeCommit&lt;/a&gt;. It can also be enabled in self hosted Git repo. Web hook is a powerful tool to drive downstream event, such as Jenkins to start building the code. The major difference between server side hook and web hook is web hook is RESTful API driven, whereas server side hook is executing a script.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-pipelines"&gt;&lt;strong&gt;Pipelines&lt;/strong&gt;&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Some repo hosting vendor also provide a feature named pipeline. BitBucket has &lt;a href="https://confluence.atlassian.com/bitbucket/get-started-with-bitbucket-pipelines-792298921.html"&gt;Pipeline&lt;/a&gt; as a CI/CD tool, AWS has &lt;a href="https://aws.amazon.com/codepipeline/"&gt;AWS Code Pipeline&lt;/a&gt;, and GitLab offers &lt;a href="https://docs.gitlab.com/ee/ci/pipelines/"&gt;CI/CD pipeline&lt;/a&gt; as well. These pipelines are usually in the form of a YAML file in the repo with a special name. The YAML spell out the steps to perform along the pipeline.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;Squash&lt;/strong&gt; Commits&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For small projects I was in the habit of committing to main branch. I often need to squash a number of commits into one to &amp;#8220;clean up&amp;#8221;. Usually a Pull Request (e.g. in GitHub) or Merge Request (e.g. in GitLab) have such option during approval. We can squash a few commits with git command as well (suppose we want to squash the most recent 16 commits):&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git rebase -i HEAD~16&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git push origin +main&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The command will open text editor to allow you to mark what to do with each commit. You can mark all except one commit as squash. For the commit to keep, mark it as pick. Then save the text editor. Git rebase will perform the squash for you. However, since this is a rebase, do not do this if there are other collaborators working on the same branch.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;&lt;strong&gt;Cleanse a repository&lt;/strong&gt;&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Take BitBucket cloud for example, the size of a remote repo has a &lt;a href="https://confluence.atlassian.com/bitbucket/what-kind-of-limits-do-you-have-on-repository-file-size-273877699.html"&gt;non-expandable hard limit&lt;/a&gt; of 2GB, and a soft limit of 1GB. Once the soft limit is reached, a warning will be displayed on Bitbucket&amp;#8217;s landing page as well as when contributors pushes changes. Once the hard limit is reached, the entire repo will turn read-only mode.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Space consumption can be caused by accidental committing of large file. As covered in the &lt;a href="https://static.digihunch.com/2019/06/git-explained-1-of-2/"&gt;previous post&lt;/a&gt;, files are stored as blob objects in .git directory. If a file was deleted by `git rm` command, it simply means it is de-referenced from the next commit and on. After all, Git as a distributed version control system, has the ability to magically restore the deleted file when we want. The cost of that magic, is that deleted file is permanently stored in the repo, in the form of blob object, although not present in the working directory. It consumes space not only in remote repo, but also in the local repo of each contributor.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The purpose of Git repository is to store source code which are fairly small texts. However if a contributor pushed in large files, it can be tricky to cleanse a Git repo on the remote side. Here is some guidelines:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt; Before cleansing, identify large unwanted files in current working directory, delete them with `git rm` and then commit this change in master branch. &lt;/li&gt;&#10;&lt;li&gt;Consolidate branches (e.g. delete useless remote branches with `git push origin &amp;#8211;delete branch_name`). This step itself does not free up space in remote repo but it simplifies the branches;&lt;/li&gt;&#10;&lt;li&gt;Remove large blob objects and commit objects that reference them. This step essentially is re-writing the commit history of repo. Given the risk, it is recommended to perform this step from a separate local project directory with bare repo only without working directory. &lt;a href="https://www.nicoespeon.com/en/2014/04/clean-git-repo-like-a-boss/"&gt;This article&lt;/a&gt; presents some good advices and illustrations. The command recommended is `git filter-branch -f &amp;#8211;tree-filter &amp;#8220;rm -rf \large_file.zip&amp;#8221; &amp;#8211;prune-empty &amp;#8212; &amp;#8211;all` ; and if that is too slow &lt;a href="https://rtyley.github.io/bfg-repo-cleaner/"&gt;here&lt;/a&gt; is an alternative named BFG Repo-Cleaner.&lt;/li&gt;&#10;&lt;li&gt;After this step, each commit will have a new hash id. For the repo size to reduce, either wait until the next garbage collection on the server side, or request cloud vendor to run a manual garbage collection.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2019/06/git-explained-1-of-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Git Explained 1 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2019/07/practical-cryptography-for-it-professional/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Cryptography Basics 1 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item></channel></rss>