<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>branching on Digi Hunch</title><link>https://www.digihunch.com/tag/branching/</link><description>Recent content in branching on Digi Hunch</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Tue, 21 Jan 2025 20:49:35 -0500</lastBuildDate><atom:link href="https://www.digihunch.com/tag/branching/index.xml" rel="self" type="application/rss+xml"/><item><title>Git Branching Strategy</title><link>https://www.digihunch.com/2021/03/git-branching-strategy/</link><pubDate>Sun, 07 Mar 2021 19:20:42 -0400</pubDate><guid>https://www.digihunch.com/2021/03/git-branching-strategy/</guid><description>&lt;p class="wp-block-paragraph"&gt;I have been in two discussions about &lt;a href="https://www.digihunch.com/2019/06/git-explained-1-of-2/"&gt;Git&lt;/a&gt; branching strategy in different organizations. Too many concepts! So I open this post to jot down the lineage of common branching strategies to help organizations develop their branching policies. In terms of reference, there is a lot from Atlassian documentation. In addition, I also find this one &lt;a class="rank-math-link" href="https://medium.com/factualopinions/branching-models-in-a-nutshell-bf24ea1d888a"&gt;article &lt;/a&gt;a good resource.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-centralized-workflow-no-branching"&gt;Centralized workflow (no branching)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In &lt;a href="https://www.atlassian.com/git/tutorials/comparing-workflows#centralized-workflow" class="rank-math-link"&gt;Centralized Workflow&lt;/a&gt;, the team uses a central repository to serve as the single-point-of-entry for all changes to the project. The default branch is master, and all changes are committed to this branch. This workflow does not require any other branches beside master. Local changes may conflict with upstream commits, and conflict needs to be resolved.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This workflow is usually seen in teams transitioning from SVN, with very basic skill level. This workflow may also be adopted in teams working on configuration management instead of source code. Centralized workflow is great for small teams. The conflict resolution process can form a bottleneck as the team scales in size.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-feature-branch-workflow"&gt;Feature Branch Workflow &lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Instead of directly committing on their local master branch, developers create a new branch every time they start work on a new feature. Feature branches should have descriptive names (e.g. issue#112). Feature branches are pushed to the central repository so that they can be shared to other developers without touching any official (master) code. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To get feedback on the new feature branch, create a pull request in a repository management solution (e.g. Bitbucket Cloud, Bitbucket Server). Before merge, you may have to resolve merge conflicts if others have made changes to the master branch of repo. This is to make sure your local master is synchronized with the upstream master. When your pull request is approved and conflict free, you can merge your branch to master branch.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img decoding="async" src="https://wac-cdn.atlassian.com/dam/jcr:09308632-38a3-4637-bba2-af2110629d56/07.svg?cdnVersion=1491" alt="Feature Branch Workflow: Merging a feature branch" style="width:549px;height:97px"/&gt;&lt;figcaption class="wp-element-caption"&gt;feature branch workflow&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Git Feature Branch Workflow is a composable workflow that can be leveraged by other high-level Git workflows. Git Feature Branch Workflow is branching model focused, instead of release focused. The Git Feature Branch Workflow can be incorporated into other workflows. The Gitflow, and Git Forking Workflows traditionally use a Git Feature Branch Workflow in regards to their branching models.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-gitflow-workflow"&gt;Gitflow Workflow&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;First &lt;a href="https://nvie.com/posts/a-successful-git-branching-model/"&gt;published&lt;/a&gt; in 2010 by Vincent Driessen. Gitflow defines a strict branching model designed around the project release. This provides a robust framework for managing larger projects. In addition to Feature Branch Workflow, Gitflow workflow assigns very specific roles to different branches and defines how and when they should interact.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Instead of a single master branch, this workflow uses two branches to record the history of the project. The master branch stores the official release history, and the develop branch serves as an integration branch for features. It is also convenient to tag all commits in the master branch with a version number.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This workflow is operated in the following ways:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;A develop branch is created from master&lt;/li&gt;&#10;&lt;li&gt;Feature branches are created from develop. When a feature is complete, with PR reviewed, it is merged into the develop branch. Features branches&lt;/li&gt;&#10;&lt;li&gt;Once develop has acquired enough features for a release (or a predetermined release date is approaching), we fork a release branch off of develop. Creating this branch starts the next release cycle, so new features can be added to develop after this point. On the release branch itself, only bug fixes, documentation generation, and other release-oriented tasks should go in this branch. Once ready to ship, the release branch gets merged into master and tagged with a version number. In addition, it should also be merged back into develop, which may have progressed since the release was initiated.&lt;/li&gt;&#10;&lt;li&gt;Maintenance or hotfix branches are used to quicly patch production releases. Hotfix branches are a lot like release branchs and feature branches except they&amp;#8217;re based on master instead of develop. As soon as the fix is complete, it should be merged into both master and develop (or the current release branch), and master should be tagged with an updated version number. &lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img decoding="async" src="https://wac-cdn.atlassian.com/dam/jcr:61ccc620-5249-4338-be66-94d563f2843c/05%20(2).svg?cdnVersion=1491" alt="Git flow workflow - Hotfix Branches" style="width:572px;height:409px"/&gt;&lt;figcaption class="wp-element-caption"&gt;Gitflow workflow&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There is a Git extension named git-flow to provide high-level repository operations for this Workflow, such as start a release, finish a release, start a hotfix, finish a hotfix.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Gitflow is ideally suited for projects that have a scheduled release cycle and for the DevOps best practice of continuous delivery. It ensures that the master branch reflects what is deployed (e.g. in production). However, it is quite &lt;strong&gt;complex&lt;/strong&gt; and have a &lt;strong&gt;steep learning curve&lt;/strong&gt; for organizations. It also runs &lt;strong&gt;long-lived branches&lt;/strong&gt;, which is considered bad from CI/CD perspective. Branches are by definition to isolate and hide changes, whereas &lt;strong&gt;continuous integration &lt;/strong&gt;is about exposing changes early on and frequently. In that sense, the Gitflow branching model and CI/CD are &lt;strong&gt;mutually exclusive&lt;/strong&gt; ideas. &lt;/p&gt;&#10;&lt;blockquote class="wp-block-quote is-style-plain is-layout-flow wp-block-quote-is-layout-flow"&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In 2020, Vincent Driessen added a note at the beginning of his &lt;a href="https://nvie.com/posts/a-successful-git-branching-model/"&gt;article&lt;/a&gt; on Gitflow:&lt;/p&gt;&#10;&lt;cite&gt;If your team is doing continuous delivery of software, I would suggest to adopt a much simpler workflow (like GitHub flow) instead of trying to shoehorn git-flow into your team.&lt;br&gt;If, however, you are building software that is explicitly versioned, or if you need to support multiple versions of your software in the wild, then git-flow may still be as good of a fit to your team as it has been to people in the last 10 years.&lt;/cite&gt;&lt;/blockquote&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Driessen&amp;#8217;s notes also points to some simple alternatives. On the other hand, the &lt;a href="https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow"&gt;Atlassian tutorial&lt;/a&gt; on Gitflow has described Gitflow workflow as a legacy (since Aug 2021 based on web &lt;a href="https://web.archive.org/web/20210802194504/https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow"&gt;archive&lt;/a&gt;). It points out at the beginning that Gitflow has fallen in popularity in favor of&amp;nbsp;&lt;a href="https://www.atlassian.com/continuous-delivery/continuous-integration/trunk-based-development"&gt;trunk-based workflows&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-github-flow-trunk-based-development-tbd-and-gitlab-flow"&gt;GitHub flow, Trunk-based development (TBD) and GitLab flow&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It is operationally expensive to manage multiple mainlines in Git flow workflow, with both source control and release in the picture. Some simple alternatives have been evolved, with single mainline, for example, &lt;strong&gt;GitHub flow&lt;/strong&gt; and &lt;strong&gt;trunk-based development&lt;/strong&gt;. They differ in where the release is performed from. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the &lt;a href="https://guides.github.com/introduction/flow/" class="rank-math-link"&gt;GitHub flow&lt;/a&gt;, release is performed from branch before being merged back to master (trunk).&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://trunkbaseddevelopment.com/alternative-branching-models/githubflow1.png" alt=""/&gt;&lt;figcaption class="wp-element-caption"&gt;GitHub flow&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In &lt;a href="https://www.atlassian.com/continuous-delivery/continuous-integration/trunk-based-development" class="rank-math-link"&gt;trunk-based development&lt;/a&gt;, release is not performed until the feature branch has been merged to the trunk (master). In trunk-based development, feature branches are supposed to be short-lived. It is a common practice among DevOps teams, since it streamlines merging and integration phases.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter"&gt;&lt;img decoding="async" src="https://trunkbaseddevelopment.com/5-min-overview/trunk_pr.png" alt=""/&gt;&lt;figcaption class="wp-element-caption"&gt;Trunk-based development&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Trunk-based development has gained some momentum in recent years, due to its DevOps friendliness. &lt;a href="https://trunkbaseddevelopment.com/" class="rank-math-link"&gt;This &lt;/a&gt;is a website that advocates it and here&amp;#8217;s a DZone &lt;a href="https://dzone.com/articles/why-i-prefer-trunk-based-development-over-feature" class="rank-math-link"&gt;article &lt;/a&gt;about it.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-gitlab-flow"&gt;Gitlab Flow&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In response to the shortcomings of GitHub flow and Gitflow, Gitlab introduced its own proposal of branching strategy, known as &lt;a href="https://docs.gitlab.com/ee/topics/gitlab_flow.html" class="rank-math-link"&gt;Gitlab flow&lt;/a&gt;. The most distinctive aspect is the environment branches. In Gitlab flow, you run multiple long-lived branches, each of them representing an environment. The typical steps are as follows:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;You create short-lived feature branches, and merge them often to the master.&lt;/li&gt;&#10;&lt;li&gt;Every developer starts from master and targets master. Other branches are merged from previous lower environment branches.&lt;/li&gt;&#10;&lt;li&gt;You can deploy a new version to production, by merging master into the production branch.&lt;/li&gt;&#10;&lt;li&gt;If you need to know what code is in production, you can check out the production branch to see.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="528" height="700" src="https://www.digihunch.com/wp-content/uploads/2023/01/gitlab_flow.png" alt="" class="wp-image-7742" srcset="https://www.digihunch.com/wp-content/uploads/2023/01/gitlab_flow.png 528w, https://www.digihunch.com/wp-content/uploads/2023/01/gitlab_flow-226x300.png 226w" sizes="auto, (max-width: 528px) 100vw, 528px" /&gt;&lt;figcaption class="wp-element-caption"&gt;&lt;strong&gt;Gitlab flow&lt;/strong&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;You only need to work with release branches, if you need to release software code to the outside world. Here are some &lt;a class="rank-math-link" href="https://about.gitlab.com/topics/version-control/what-are-gitlab-flow-best-practices/"&gt;best practices&lt;/a&gt; in GitLab flow.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-forking-workflow"&gt;Forking Workflow&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Forking workflow is fundamentally different. The key steps are as follows:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;A developer &amp;#8216;forks&amp;#8217; an &amp;#8216;official&amp;#8217; server-side repository. This creates their own server-side copy. This is their personal public repository, and no other developer are allowed to push to it.&lt;/li&gt;&#10;&lt;li&gt;The new server-side copy is cloned to their local system. This forms an environment dedicated to this developer.&lt;/li&gt;&#10;&lt;li&gt;With the local clone, developer needs to create the upstream remote manually using &amp;#8220;git remote add upstream&amp;#8221; command. This allows the developer keep the local repository up-to-date as the official project progresses. &lt;/li&gt;&#10;&lt;li&gt;A new local feature branch is created. Developer commits to the new local branch, and pushes to their own copy of repository on server.&lt;/li&gt;&#10;&lt;li&gt;Developer files a pull request from the new branch (in own copy of repository) to the &amp;#8216;official&amp;#8217; repository. The project maintainer knows that an update is ready to be integrated. The PR also serves as a discussion thread.&lt;/li&gt;&#10;&lt;li&gt;The PR gets approved for merge and is merged into the original server-side repository.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This workflow has other names, such as &lt;a href="https://blog.scottlowe.org/2015/01/27/using-fork-branch-git-workflow/" class="rank-math-link"&gt;fork-and-branch workflow&lt;/a&gt;, and is commonly used in GitHub for managing open-source projects. However, this should not be confused with the aforementioned GitHub flow.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-conclusion"&gt;Conclusion&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In order to fully support distributed source control, Git abstract version control problems into concepts such as commit, branch, etc. This makes discussion about Git workflow and branching strategy difficult due to the conceptual hurdles and organization differences. We covered choices of Git branching strategy in this post. As Vincent Driessen commented in his &lt;a class="rank-math-link" href="https://nvie.com/posts/a-successful-git-branching-model/"&gt;original Gitflow posting&lt;/a&gt;, panaceas don&amp;#8217;t exist. We should consider the context (e.g. team size, Git skill level, etc) to determine the best Git branching strategy.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2021/02/interpret-census-data-from-statistics-canada/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Census Data from Statistics Canada&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2021/03/intro-to-data-analytics-platform/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Intro to Data Analytics Platform on Azure&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Common local Git operations</title><link>https://www.digihunch.com/2020/04/common-local-git-operations/</link><pubDate>Wed, 08 Apr 2020 11:54:00 -0400</pubDate><guid>https://www.digihunch.com/2020/04/common-local-git-operations/</guid><description>&lt;p class="wp-block-paragraph"&gt;This is a summary of concepts in common Git operations. We will discuss brach, merge, rebase, cherrypick, stash and reset. Then we&amp;#8217;ll discuss pull, fetch, and push.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-commit-branch-and-head"&gt;Commit, Branch and HEAD&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When you run &amp;#8220;git commit&amp;#8221;, the following happens:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Git checksums each subdirectory, and stores them as a tree object (file path and name) and blob object (file content) in Git repository;&lt;/li&gt;&lt;li&gt;Git creates a commit object that has the metadata and a pointer to the root project tree; or if this is not the first commit, the pointer will point to the commit immediately before it&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The operations above should form a chain of commit. It can be a long chain and may diverge into branches. In Git semantics however, a branch is simply a lightweight, movable pointer to one of the commits. The default branch name in Git is master. A Git repository may contain multiple branches and the name master itself does not suggest any privilege. There is also a special pointer called HEAD, which indicates the branch you are currently working on. So branch is essentially a pointer to a commit; HEAD is essentially a pointer to a branch. &amp;#8220;git checkout&amp;#8221; can switch branch that HEAD points to. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-basic-merge"&gt;Basic Merge&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;One type of basic merge simply moves branch pointer from one commit to another (along the same chain) without creating any commit. Here is a diagram before basic merge:&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://git-scm.com/book/en/v2/images/basic-branching-4.png" alt="Hotfix branch based on `master`."/&gt;&lt;figcaption&gt;Before basic merge, Hotfix branch is based on&amp;nbsp;&lt;code&gt;master&lt;/code&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The following command performs basic merge:&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 checkout master&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git merge hotfix&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Updating f42c576..3a0874c&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Fast-forward&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; index.html | 2 ++&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 1 file changed, 2 insertions(+)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then Git simply moves the pointer (named master) forward. There is no divergent work to move together, hence no chance of merge conflict. This type of basic merge is also called &amp;#8220;fast-forward&amp;#8221; merge.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://git-scm.com/book/en/v2/images/basic-branching-5.png" alt="`master` is fast-forwarded to `hotfix`."/&gt;&lt;figcaption&gt;After basic merge, &lt;code&gt;master&lt;/code&gt;&amp;nbsp;is fast-forwarded to&amp;nbsp;&lt;code&gt;hotfix&lt;/code&gt;&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The other type of merge involves reconciling divergent work together, which may or may not involve conflict. Suppose this is the commit tree to start with:&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://git-scm.com/book/en/v2/images/basic-merging-1.png" alt="Three snapshots used in a typical merge."/&gt;&lt;figcaption&gt;Three snapshots used in a typical merge&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The following commands perform the merge:&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 checkout master&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Switched to branch &amp;#39;master&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git merge iss53&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Merge made by the &amp;#39;recursive&amp;#39; strategy.&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;index.html | 1 +&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;1 file changed, 1 insertion(+)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Instead of just moving the branch pointer forward, Git creates a new snapshot that results from this three-way merge and automatically creates a new commit that points to it. This is referred to as a merge commit, and is special in that it has more than one parent.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://git-scm.com/book/en/v2/images/basic-merging-2.png" alt="A merge commit."/&gt;&lt;figcaption&gt;A merge commit&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now that your work is merged in, you have no further need for the&amp;nbsp;&lt;code&gt;iss53&lt;/code&gt;&amp;nbsp;branch. You can close the issue in your issue-tracking system, and delete the branch:&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 branch -d iss53&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;If commits from two respective branches changes the same file in different ways, then there is a merge conflict. In this case, Git cannot just create a merge commit. Instead it asks the user to resolve the conflict first. You have to choose either side of the change, or just merge the content yourself. At this point, if you introduce a change that does not appear in any parent, it is referred to as an evil merge. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Beyond the basic merge, there are more sophisticated merge conflict resolution tools covered in &lt;a href="https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging"&gt;advanced merging&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-merge-and-rebase"&gt;Merge and Rebase&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are two ways to integrate changes from one branch to another. Merge and rebase. Suppose your commit chain diverge into a master branch and a feature branch. Merging (from feature to master) takes the content of feature branch and integrate it with master branch. &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 checkout master&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git merge feature&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;When you rebase a feature branch onto master, you move the base of the feature branch to master branch’s ending point.&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 checkout feature&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git rebase master&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;After merge, you are still &lt;span style="text-decoration: underline;"&gt;on the same branch&lt;/span&gt;. The commits from other branch are integrated into the branch that you are already on. There is no change in any existing commits (history). After rebase, your base will be &lt;span style="text-decoration: underline;"&gt;moved to a different branch&lt;/span&gt;, along with the commits that you have made in the previous branch (since the diverge). In other words, by re-playing those commits on a different branch, it changed history.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://miro.medium.com/max/855/1*pzT4KMiZDOFsMOKH-cJjfQ.png" alt=""/&gt;&lt;figcaption&gt;merge vs rebase&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The chart above is stolen from &lt;a href="https://hackernoon.com/git-merge-vs-rebase-whats-the-diff-76413c117333"&gt;this article&lt;/a&gt;, which does a better job explain in detail the difference, pros and cons of merge and rebase. Merge does create a &amp;#8220;merge commit&amp;#8221;, and a git history full of merges can be cluttered. Rebase does not create an extra commit but since it changes the history of a branch, it has impact to other collaborators. It can be done in an interactive way (with -i switch). The golden rules of rebasing is covered in &lt;a href="https://www.atlassian.com/git/tutorials/merging-vs-rebasing"&gt;this&lt;/a&gt; article. One of the principles is that never perform a rebase on a public branch.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="600" height="335" src="https://www.digihunch.com/wp-content/uploads/2020/08/0.gif" alt="" class="wp-image-1196"/&gt;&lt;figcaption&gt;git operations&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;h3 class="wp-block-heading" id="h-cherrypick"&gt;Cherrypick&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In a cherrypick operation, the current branch does not change. You simply pick interested commits from other branches to re-apply to your current branch. You may pick a single or a series of commits from other branch. These commits are not &amp;#8220;moved&amp;#8221; to your current branch. They remain intact. They are just re-played as new commit to current branch. Unlike rebase, there is no re-writing of history, hence not as dangerous.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-reset-and-stash"&gt;Reset and Stash&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Suppose you are working on a part of a project and it starts getting messy. There has been an urgent bug that needs your immediate attention. It is time to save your changes and switch branches. If you are okay to give up your uncommitted work, you may perform a reset, in one of the three modes covered in a &lt;a href="https://www.digihunch.com/2019/06/git-explained-1-of-2/"&gt;previous article&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;But most likely, you don’t want to do a commit of half-done work. The solution is git stash. Stashing is handy if you need to quickly switch context and work on something else but you&amp;#8217;re mid-way through a code change and aren&amp;#8217;t quite ready to commit. In the most basic workflow, you need to run this command to save your uncommitted (but staged) work. As soon as you stash your change, the working directory is clean with all uncommitted local changes saved elsewhere. You can perform any other Git operations, such as change branch. When you&amp;#8217;re ready to resume, you may pop the stash. Here is an example:&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 add .&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git stash&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git checkout correctbranch&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git stash pop&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Instead of pop, you can also use apply to keep the changes in working directory. &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 stash apply&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;More details are on &lt;a href="https://www.atlassian.com/git/tutorials/saving-changes/git-stash"&gt;this page&lt;/a&gt; from Bitbutket.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-fetch-and-pull"&gt;Fetch and Pull&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A git fetch simply downloads blob data from remote so the .git directory comes in sync with the server. It does not attempt to update the local working directory. If there is staged or uncommitted local changes, fetch will not impact them. &lt;strong&gt;A git pull is essentially git fetch followed by git merge&lt;/strong&gt;. In addition to downloading blob data, it also updates local working directory. Therefore, there is a chance of merge conflict when the same file has been modified locally. Git will usually guide you through the merge conflict by flagging the conflict area in the file and let you decide the survival changes. For example:&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;&lt;span style="color:#75715e"&gt;#! /usr/bin/env ruby&#10;&lt;/span&gt;&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;def hello&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&amp;lt; HEAD&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;hola world&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;=======&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;hello mundo&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; mundo&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;end&#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;hello&lt;span style="color:#f92672"&gt;()&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;You will be prompted in an editor session to reconcile the conflict. Once the file is saved, you will also need to do a &amp;#8220;merge commit&amp;#8221;, before you can pull again.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-push"&gt;Push&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Git push is the opposite of pull, where you merge local branch to the remote. (There is no opposite of fetch because there is no point to merge to remote without updating working directory, no collaborator works on the working directory on the server after all). If the local branch has fallen out of sync with the remote, there is a chance of merge error during git push. To minimize the chance of a merge during push, we can run a git pull before and reconcile any potential conflict locally. This is known as a pre-merge.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Visualizer&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I came across a great visualizer of commit chain &lt;a href="https://git-school.github.io/visualizing-git/"&gt;here&lt;/a&gt;. In the command panel type some git command and it will print the commit graph for you&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/03/ntlm-and-kerberos/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;NTLM and Kerberos protocols&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/04/how-memory-usage-adds-up-in-linux/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;How memory usage adds up in Linux&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Clean up Git repository</title><link>https://www.digihunch.com/2019/10/clean-up-your-git-repository/</link><pubDate>Sat, 26 Oct 2019 20:33:00 -0400</pubDate><guid>https://www.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://www.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://www.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://www.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://www.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://www.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 1 of 2</title><link>https://www.digihunch.com/2019/06/git-explained-1-of-2/</link><pubDate>Sat, 08 Jun 2019 22:39:00 -0400</pubDate><guid>https://www.digihunch.com/2019/06/git-explained-1-of-2/</guid><description>&lt;p class="wp-block-paragraph"&gt;In a nutshell, Git is a distributed version control system, commonly used as source control management. It places files in one of three logical areas (working dir, staging, repo) below depending where it is in its lifecycle. &lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img loading="lazy" decoding="async" width="1618" height="992" src="https://www.digihunch.com/wp-content/uploads/2019/10/image.png" alt="" class="wp-image-178"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are many cheetsheets out there but this article will just sort through some concepts unique to Git. To understand how Git works it is crucial to think in terms of Git data model.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Working directory&lt;/strong&gt; a single checkout of one version of the project. These files are pulled out of the object database in the Git directory (upon checkout) and placed in the project directory on disk, for you to use or modify;&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Index&lt;/strong&gt; a file contained in your Git directory (stored as binary data in file .git/index) that keeps information about what will go into your next commit.&amp;nbsp;To display what&amp;#8217;s in the index, run `git ls-files &amp;#8211;stage`. Read &lt;a href="https://hackernoon.com/https-medium-com-zspajich-understanding-git-data-model-95eb16cc99f5"&gt;this post&lt;/a&gt; for further details&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Repository&lt;/strong&gt;: where Git stores the metadata and &lt;code&gt;&lt;span style="text-decoration: underline;"&gt;object database&lt;/span&gt;&lt;/code&gt; for your project.&amp;nbsp;The local repository is in .git/ under the project directory.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Add&lt;/strong&gt; &amp;#8211; register one or more modified files to staging area. You may edit several files with only a few needed registered for future commit. Add activity ensures the file edited are recorded in the index (as a preview of next commit). You technically need to run add against each file. But the command syntax with * or . allows you to capture all edits in the same directory or under.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Commit&lt;/strong&gt; &amp;#8211; persist the staged file edits to the repository (so they are stored in Git object database). A commit represents all the file edits that were staged by add command in previous steps.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Branch&lt;/strong&gt; &amp;#8211; a branch is simply a movable pointer to a commit. Default branch name created by git init is called &amp;#8220;master&amp;#8221;. Other than the name, there is nothing special about master branch. Everytime you commit, the master branch pointer moves forward automatically. Branch pointers are kept in .git/refs directory. Read &lt;a href="https://hackernoon.com/understanding-git-branching-2662f5882f9"&gt;this post&lt;/a&gt; for further details.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;HEAD&lt;/strong&gt; &amp;#8211; the pointer to the current branch reference, which is in turn a pointer to the last commit made on that branch. Git use HEAD pointer to know what branch you&amp;#8217;re currently on. HEAD will be the parent of the upcoming commit.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Tag&lt;/strong&gt; &amp;#8211; an annotated tag contains the SHA of the commit being tagged. Alias of a commit.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Merge&lt;/strong&gt; &amp;#8211; choose current commit of other branch and apply it onto your branch.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Rebase&lt;/strong&gt; &amp;#8211; copy all commits from other branch to your branch. Compared to merge, rebasing forms a cleaner commit history.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Cherrypick&lt;/strong&gt; &amp;#8211; choose a previous commit from other branch and apply it onto your branch.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Stash&lt;/strong&gt; &amp;#8211; &amp;nbsp;temporarily stashes changes you&amp;#8217;ve made to working tree so you can work on something else, and then come back and re-apply them later on.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Reset&lt;/strong&gt; &amp;#8211; at a high level, reset is to revert some operations. After pulling code, developer usually follow three steps: editing-&amp;gt;add-&amp;gt;commit. reset is to reverse these steps, based on different modes. The Pro Git reference has further details on the &lt;a href="https://git-scm.com/book/en/v2/Git-Tools-Reset-Demystified"&gt;three different modes&lt;/a&gt;:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;soft mode&lt;/strong&gt; (reverse operation of commit): based on what branch HEAD points to, move where that branch points to (e.g. from latest commit, to a different commit several steps up the link);&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;mixed mode&lt;/strong&gt; (default; reverse operation of commit and add) &amp;#8211; in addition to soft mode, also update index;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;hard mode&lt;/strong&gt; (reverse operation of commit, add and file editing)- in addition to mixed mode, also update working directory. Edit on files are discarded.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Git object model&lt;/strong&gt; &amp;#8211; In Git database, files, commits and directories are stored as objects, In Git &lt;a href="https://shafiul.github.io/gitbook/1_the_git_object_model.html"&gt;object model&lt;/a&gt;, there are three types (to tell object type, run `git cat-file -t`). Read &lt;a href="https://hackernoon.com/https-medium-com-zspajich-understanding-git-data-model-95eb16cc99f5"&gt;this post&lt;/a&gt; for further details:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;blob object&lt;/strong&gt; &amp;#8211; stores file data with metadata; use `git show` to examine blob object;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;tree object&lt;/strong&gt; &amp;#8211; represents a directory. It references other tree objects (sub-directories) or blob objects (files under the directory, of a certain version); use `git ls-tree` to examine tree object;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;commit object&lt;/strong&gt; &amp;#8211; represents a commit. It references its parent commit, as well as a tree object that represents the entire project directory. use `git cat-file -p` to inspect commit object;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This &lt;a href="https://git-scm.com/book/en/v2/Git-Internals-Git-Objects"&gt;diagram&lt;/a&gt; from from Git Pro outlines the interactions amongst these types of objects.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://git-scm.com/book/en/v2/images/data-model-3.png" alt="All the reachable objects in your Git directory."/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;For more details&lt;/strong&gt;, the&amp;nbsp;&lt;a href="https://git-scm.com/book/en/v2"&gt;official documentation&lt;/a&gt;&amp;nbsp;is actually the most helpful reference with illustrations. In addition, I find on Hakcermoon three excellent articles with thorough explanation on&amp;nbsp;&lt;a href="https://hackernoon.com/https-medium-com-zspajich-understanding-git-data-model-95eb16cc99f5"&gt;data model&lt;/a&gt;,&amp;nbsp;&lt;a href="https://hackernoon.com/understanding-git-branching-2662f5882f9"&gt;branching&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href="https://hackernoon.com/understanding-git-index-4821a0765cf"&gt;index&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2019/05/automation-with-ansible-a-primer/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Set up automation with Ansible&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2019/06/git-explained-2-of-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Git Explained 2 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item></channel></rss>