A blog about git that will actually help you at work

Leonidas Boutsikaris
8 min readFeb 13, 2022
Photo of metro lines
Photo by Jeremy Bezanger on Unsplash

What would we do without git? pass usbs around? I’m sure you already know what git is and that’s why you’re here so i'm skipping the long intros and dive right in.

We will go through some tips and scenarios that are really common in the development lifecycle.

At first let’s stay clear about what we’re talking about. Most software companies use the following git structure with many variations but the base idea holds true. Let me draw that for you.

The main branch is used only for release cycles (when your favourite app has an update), for example every week or month, and has to be stable. That means no bugs, no problems, etc. (who am I even kidding?).

The development branch is used for the between periods of these cycles. In this branch a feature is merged after its completion.

Finally we have the individual feature branches, these are the things you work on.

When you start working on something, you “branch out” (that’s the slang) from the stable development branch, you append your changes, do some testing and finally you merge your feature in the development branch. After all this fuzz everything that made it to the development branch gets merged to the main branch and is released.

A thing to not miss out is that you always have to rebase before doing a merge into the development branch. Your feature was being developed for some time (days or weeks). That means you were left behind after you branched out and missed commits that were merged into the development branch (bug fixes, features, an API you might need? etc).

Pull the remote master locally, checkout in your branch and hit this command

>$git rebase master

Side note: Things can go wrong, for example having some conflicts while rebasing. Resolve them with your favourite tool and then use

>$git rebase --continue

or you can decide that this is not the day and just

>$git rebase --abort

Stay committed (or not)

Are you sure you’re writing the right commit messages? (if not that’s okay).
Commit messages in feature branches are basically useful when collaborating with other engineers and also when you forget what you coded the day before. That’s why over describing your commit messages is useful for you only if its useful for you. If you know what you’re doing go ahead and just put a “changes” message.

If you actually want to make it classy, instead of using the inline message with git commit -m, drop the -m parameter and an editor will open. Write as a single line a brief description, let an empty line and get in the details there. This will come up really useful if someone actually reads it and has a nice title-paragraph format.

The same applies for how you group your commits. If you want to make it right, differentiate the changes in chunks that make sense, not just big file changes.

We all know the git addcommand. But what about its arguments?
You should definitely try the -p parameter.

>$git add file.cpp -p

This will make Git go to each individual change you made in this file and ask you if you want to stage it. That way you can easily differentiate your commits and seperate it in pieces that make sense.

But why good commit messages and splitting commits into pieces is not necessary?

When your feature is ready you will try to merge it into the development branch. While merging you should probably squash your commits into one
meaning that if you have 10 individual commits they should become one.
This is needed to avoid filling the development branch with tons of commits.

You can craft one big beautiful commit message by including the number and the name of your feature ticket, a brief overview of it and a paragraph description that gets into more details about the actual changes. This actually can be done in the process of squashing the commits. Squashing is really easy.

  1. >$git rebase -i HEAD~x

where x are the number of the commits you have.

After hitting enter a vim or vi editor will open (insert not funny and not overplayed joke about vim here).

2. Hit the a button that stands for append and now you can interactively write in the terminal.

3. Change every pick with an s except the first one.

4. Hit the esc button and write :wq and enter

5. A new editor will open, do the same thing (presa), and delete all lines that do not start with a #.

6. Then you can finally write your beautiful descriptive commit message.

Don’t forget your feature issue number!

A lot of tools and developers use the commit messages to match the code to the appropriate issues and its really useful.

7. Hit the esc button and write :wq again and you’re done.

Now you have a different history from the branch that is uploaded to github/bitbucket etc. You should push using the -f parameter. This will re-write the branch history on the remote side.

Please inform any other devs about that if they are working on this branch before doing so because they will be affected by these changes!

what about now, what about today?!

After rebasing, the first commit probably has an old timestamp. You can easily change that by using:

>$git commit --date=now

OK, you did that, you have a ready squashed your commit and forgot to upload a string change.

Git add and then commit by using the amend argument. This creates a change in the previous commit without the fuzz of rebasing again.

>$git commit --amend

An editor will open, you can just :wq and exit or modify the commit message and save. Force push again because this rewrites the history as mentioned before!

Does your colleague works on some stuff you need to use too?

For example an API might be in the workings but not fully ready. Until this is merged to the development branch and you rebase to get it you can use the first iteration of it instead of mocking your responses! This can help you develop faster and better.

>$git cherry-pick commit-hash-here

This will get all the code of this commit on top of your branch. You could also use the branch name, if you’re bored to look for the hash, and get the HEAD of the branch into yours.

>$git cherry-pick branch-name-here

Let’s say you commited something you shouldn't

First question, did you pushed them in the remote?
If the answer is yes you have to do a force push at the end. If not continue with your normal flow.

You can do a git reset or a git revert to go back to a previous commit. The first one moves the pointer of the HEAD of your branch to the commit you will specify while the later will just create a new commit undo the changes there and then commit it. No strong opinions here, use what seems better to you.

>$git revert HEAD~x..HEAD where x are the number of the last commits commit you want to revert.

>$git revert commit-sha1 commit-sha2 … for multiple commits also.

We can add the --soft or --hard parameter while resetting.

>$git reset --soft HEAD~x this argument will preserve your changes but in the staging area. With the same fashion the --hard parameter will not keep your changes in the temporary staging area.

Why did you changed the code authors?

Line endings are evil, try to use the same ones as your remote repository. If you don’t whole files will be committed as one big addition. Generally speaking that’s not inherently bad. When someone though needs information about a specific chunk of code in this file they will git blame and they will see your name. Then they will message you and you will not have a clue about the inner workings of this code. You should definitely change this.

  1. Rename the file you want to restore the original authors
  2. >$git checkout master -- filepath-of-original-file-here/file.cpp
    This will get you the original file from the master untouched.
  3. Vimdiff new-file-path old-file-path
    This will open a two-way splitted editor inside the terminal, like merging conflicts!
  4. Type ]c to go to each individual change each time, then hit dp to keep the changes only. Apply to all change patches.
  5. When you are done with it just :wq on both editors.
  6. >$rm renamed-file-path
  7. >$git add file && git commit --amend (do the usual stuff we talked above)

The other way around!

Suppose a developer starts by writing the skeleton of a class in a commit and you implement the whole thing. You then rebase in the first commit of the branch so you can merge with a single commit in the development branch and now the author of this file according to git blame seems to be the one that made the first commit. If someone tries to reach them about this chunk of code they will also not have a clue.

You can easily change that by using the --author argument.

>$git commit --amend --author=”Author Name <email@address.com>”

Don’t forget to push with force!

The keep your house clean section

While developing, the project will have countless external libraries, package managers etc, these files are downloaded on compile time and do not exist on the repository. Lot’s of time when versions and packages change intermediate versions of code stay in your local branch and disturb the compiling process leaving you wondering why this damn thing is not running.

>$git clean -f -d -x

will remove everything that is not on the remote repository. Now try compiling again.

Get the trash out

From time to time run the >$git prune command. This is an internal housekeeping utility that cleans up unreachable or "orphaned" Git objects. Unreachable objects are those that are inaccessible by any refs. Any commit that cannot be accessed through a branch or tag is considered unreachable.

Are you still in the same company?

After some time you may end up having lots of local branches that are now delete on the remote side because they are merged in the development branch or even test branches from stuff you tried. These slow you down and you should throw them away.

#remove all the local branches that are not on the remote side>$git fetch -p && git branch -vv | grep ': gone]' | awk '{print $1}' | xargs git branch -D#remove all local branches that are not on remote and that are fully merged and that are not usedgit fetch -p && git branch --merged | grep -v '*' | grep -v 'master' | xargs git branch -d
  • >$git fetch -p will prune all branches no longer existing on remote
  • >$git branch -vv will print local branches and the pruned branch will be tagged with the gone keyword
  • grep ': gone]' selects only the branches that are gone
  • awk '{print $1}' filters the output to display only the name of the branches
  • xargs git branch -D will loop over all lines (branches) and force remove the branches (-D needs to be capital for branches that are fully merged)

Git is awesome and you should definitely master it! Can you think any other useful commands or situations? Feel free to share your thoughts.

--

--

Leonidas Boutsikaris

Software Engineer, writing code and blogging about it. Found something interesting? Buy me a coffee! https://ko-fi.com/leobouts