GitCheat

GitCheat

In my previous post, I introduced a long-existing tool that helps you migrate your workflow from Perforce to Git. Here, I elaborate more on those workflows that Git makes possible or does a better job at. These are things I wish I had known earlier.

Reflecting on the Past

Having a good view of a project's history is essential to developers. There is a well-known git log command that shows you some information about every commit.

Article content
git log

But you can customise the output of the git log. Running

git log --pretty=format:"%C(Red)%d %C(Yellow)%h %C(Cyan)%ch %C(Green)%cn %C(White)%s" --graph --all        

will give you the following:

Article content
git log --pretty=format:"<format>"

To make it easier to use, you can add an alias for this better log:

git config --global alias.blog 'log --pretty=format:"%C(Yellow)%h %C(Cyan)%ch %C(Green)%cn %C(White)%s" --graph --all'        

Later, you can invoke that by running git blog. git log can also be useful for debugging. Running

git log -S "expression"        

will show you the commit that changed the expression's occurrences or

git log -L :function:file        

for commits that changed inside a function.

Shaping the Past

Imagine you were working on a feature and wanted to integrate it into the main branch. Your history will look like this:

Article content
git log before merge

We have two options at this point. One is to merge these two branches and another to rebase. Let's go through the first option quickly. Running the following command for merge:

git checkout main
git merge feat1        

It is likely that we run into a conflict. Because work has been done on the main branch while we were on our feature branch.

Article content
conflict in git merge

vim has a good merge tool and we can set it up as default for git.

git config --global merge.tool vimdiff        
Article content
vimdiff as git's merge tool

After dealing with conflicts and committing, our history will look like this:

Article content
first option: to merge branches

The second option is to rebase. In other words, ask Git to take whatever we have done in our feature branch and rebase it on the main. Some repositories prefer this approach to have a clear view of the work done. Also, if you are working with an older VCS through a bridge to Git as we did in my previous post, you may be obligated to rebase and keep a linear history.

The commands are as follows:

git checkout feat
git rebase main
git checkout main
git merge feat        

On our feature branch, we rebase on the main. It does not change the main branch yet, but alters the feature branch in a way that looks like we did our work on the main:

Article content

Then we will switch to the main branch and do a fast-forward merge. We will end up with a history like this:

Article content

If we push our feature branch to a remote and some colleagues start working on that branch, then rebasing can put them in much trouble. You should not rebase what you have pushed.

Changing the Past

Let's say you have committed a large binary file that was unnecessary to put under version control. From now on whoever wants to clone your project has to download that file. Even if you remove it from git:

git rm file.big        

It is still in your history and will be downloaded by each cloning. Imagine another scenario where you want to publish your project but don't want your email to be in the commit data. Or what if your project has two parts, say front-end and back-end, and you want to separate them into two different projects? While preserving history for each.

In all of these scenarios, you have to rewrite the past, and the tool for doing that is filter-branch. The following command tells Git to start from a specific commit and remove a file from each one.

git filter-branch --index-filter 'git rm --ignore-unmatch --cached file.big' -- abc123^..        

The following command changes the author and committer email to something else:

git filter-branch --commit-filter '
        GIT_AUTHOR_NAME="New User";
        GIT_AUTHOR_EMAIL="new.user@mail.com";
        GIT_COMMITTER_NAME="New User";
        GIT_COMMITTER_EMAIL="new.user@mail.com";
        git commit-tree "$@";' HEAD        

Lastly, the following separates a subdirectory into another git repository:

git filter-branch --subdirectory-filter module HEAD        

Sharing the Past

In the scenario of my last post, where you were working locally with Git and were pushing to a Perforce repository, you can not have another remote and share work with colleagues through that. Pushing to Perforce changes each commit's hash by adding the change-list number at the end of the commit message. You will end up with rebasing a pushed work.

You can share your half-done work with colleagues via patches. To do that, switch to your feature branch and run the following:

git format-patch origin/main # or remote/p4/master in our scenario        

That gives you a patch file. Your colleague can apply the patch by running:

git am -i 0001-commit-message.patch        

The -i option brings the interactive menu from which your colleague can choose what to apply and what to ignore.

Article content

Keep Diaries of the Past (Bonus)

If you are planning to move from another VCS to Git, you may be in need of a solution to host your Git server. There is an open-source solution called Gitea. In this Docker-Compose file, I have set up a reverse proxy for personal Git hosting. There is also a link in my Dot Bash History repository that teaches how to set up an SSL certificate for your home server without a public IP address.

version: '3.8'

services:
  nginxproxymanager:
    image: 'jc21/nginx-proxy-manager:latest'
    container_name: nginxproxymanager
    restart: always
    ports:
      - '80:80'
      - '81:81'
      - '443:443'
    volumes:
      - ./nginx/data:/data
      - ./nginx/letsencrypt:/etc/letsencrypt
  gitea:
    image: 'gitea/gitea:latest'
    container_name: gitea
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=postgres:5432
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=gitea
      #- GITEA__service__DISABLE_REGISTRATION=true
      #- GITEA__admin__DISABLE_REGULAR_ORG_CREATION=true
      #- GITEA__openid__ENABLE_OPENID_SIGNUP=false
    restart: always
    ports:
      - '2222:22'
    volumes:
      - ./gitea:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    depends_on:
      - postgres

  postgres:
    image: 'postgres:latest'
    container_name: postgres
    restart: always
    environment:
      - POSTGRES_USER=gitea
      - POSTGRES_PASSWORD=gitea
      - POSTGRES_DB=gitea
    volumes:
      - ./postgres:/var/lib/postgresql/data        

To view or add a comment, sign in

More articles by Mohammad Rahimi

  • Programming Policies

    Picture this situation: you've got a continuous flow of data coming in, and your task is to process it and present the…

  • Exception x Exception

    In my previous post, we demystified std::forward. In this one, we'll explore what happens when you encounter another…

  • Forward Demystified

    In this post I have shed some light on one of the darker corners of the C++ language: std::forward. The code snippets…

  • Maintainer's Dream

    The distributed workflow for Linux source code development is called Benevolent Dictator. Some maintainers collect…

    2 Comments
  • FTowerX

    A few years ago, I was developing a program for remotely controlling a signal generator. I wanted to create a simple…

    1 Comment
  • Perforce meets Git

    Perforce is a Centralised Version Control System founded in 1995 and used by many companies. Git came into existence…

  • In Mail IP

    I was planning to run a home server and access it remotely. But there was an issue.

    2 Comments
  • StateBench

    While developing software, from large to small scale, you can use state machines to reduce code complexity and help…

  • Dot Bash History

    I have prepared a GitHub repository where you can find many useful commands and scripts. The following explains how you…

  • C++ and Benchmarking

    It may sound extreme, but I believe benchmarking too should get the special treatment of testing in a project. There…

Others also viewed

Explore content categories