DEV Community

Cover image for 4 Useful Solutions to Common Git Problems
Jacob Herrington (he/him)
Jacob Herrington (he/him)

Posted on • Edited on

4 Useful Solutions to Common Git Problems

Cover Image: Black and Silver Laptop Computer on Round Brown Wooden Table by Christina Morillo
These days, most developers are using some form of version control, and the most popular version control system used today is git.

I've written a few handy git articles:

This one will be a continuation of those articles, with more focus on pragmatism. In the previous articles, I was sharing interesting things that you could do with git. In this article, I'm going to focus on things that every developer should know how to do.

1. Add a change to the last commit

Modifying the last commit is handy for anyone who has forgotten to stage a change (which is everyone who has ever used git).

This is really simple. All you have to do is stage the file you forgot to stage and then use the git commit --amend command.

Say I forgot to stage my changes to the README file, this is what I'd do:

git add README.md
git commit --amend
Enter fullscreen mode Exit fullscreen mode

I'd be prompted to modify the commit message and then: Tada! The README.md changes have been added to the last commit.

2. Split the last commit into multiple commits

Say you have a different problem. If you accidentally staged something and then made a commit when you intended to make two commits.

All you need to do is walk back by one commit using the git reset command:

git reset HEAD~1
Enter fullscreen mode Exit fullscreen mode

Then use git add to stage your changes (I recommend using git add --patch, I wrote an article on it, if you're not familiar), and commit:

git add --patch
git commit -m "This is the first commit"
Enter fullscreen mode Exit fullscreen mode

Then repeat the process as many times as needed:

git add --patch
git commit -m "This is the second commit"
Enter fullscreen mode Exit fullscreen mode

One commit has become many. 💥

3. Squash some previous commits into one commit

Every developer should know how to squash commits. This is something you will probably be asked to do if you make a contribution to Open Source and you've made a few redundant or poorly-worded commit messages.

I'm going to share a couple of approaches, one that uses git rebase, and one that doesn't.

Without git rebase, you can easily squash the last few commits into one. First, you'll want to use the git reset command to move back through the commits you'd like to squash.

You can do this by passing HEAD~x to the git rebase command, where x is the number of commits you'd like to squash together.

For example, if you want to squash the previous three commits into one commit, you'd run the following command:

git reset HEAD~3
Enter fullscreen mode Exit fullscreen mode

If you were to check the log, you'd notice that you are now three commits behind your previous position.

A quick git log will reveal that all of the changes from the previous three commits still exist on your machine, but are now unstaged.

To squash them together into one commit, all you need to do is stage them and commit them:

git commit -am "Some commit message"
Enter fullscreen mode Exit fullscreen mode

Congratulations! 🎉 You've squashed three commits into one!

Alternatively, you can use git rebase with the interactive flag. That would look something like this:

git rebase -i master
Enter fullscreen mode Exit fullscreen mode

Replace master with the branch where you will eventually merge.

git rebase -i will drop you into a text editor that looks something like this:

pick 1eabc17a8 Add data-attributes to container elements
pick 90c58b487 Fix react-dom warning

Rebase 5f5e140ea..90c58b487 onto 5f5e140ea (2 commands)

Commands:
p, pick <commit> = use commit
r, reword <commit> = use commit, but edit the commit message
e, edit <commit> = use commit, but stop for amending
s, squash <commit> = use commit, but meld into previous commit
f, fixup <commit> = like "squash", but discard this commit's log message
x, exec <command> = run command (the rest of the line) using shell
b, break = stop here (continue rebase later with 'git rebase --continue')
d, drop <commit> = remove commit
l, label <label> = label current HEAD with a name
t, reset <label> = reset HEAD to a label
m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
.       create a merge commit using the original merge commit's
.       message (or the oneline, if no original merge commit was
.       specified). Use -c <commit> to reword the commit message.

These lines can be re-ordered; they are executed from top to bottom.

If you remove a line here THAT COMMIT WILL BE LOST.

However, if you remove everything, the rebase will be aborted.

Note that empty commits are commented out
Enter fullscreen mode Exit fullscreen mode

Now, this can be a little intimidating if you've never performed a rebase before, but it's actually very simple.

To squash these two commits, simply replace the word pick on the second line with squash:

pick 1eabc17a8 Add data-attributes for styling checkout
squash 90c58b487 Fix react-dom warning; non-critical, but annoying
Enter fullscreen mode Exit fullscreen mode

Save the file (if you're in Vim, this is done with :wq), and you'll be dropped into a text editor to write a commit message. When you're happy, save the commit and you're done!

Woohoo! 🤠 Now you've seen two different ways to squash commits.

You might be wondering why you'd choose to use rebase when the first method was so easy, that's understandable. Using git rebase gives you a lot more control over big changes, and I recommend getting comfortable with it for those problems.

4. Get the most recent changes from another branch

When you work with feature branches, this is something you might be doing multiple times a day!

There are two ways to do this too.

The most common method for doing this is the git merge command. All you have to do with run the git merge command with the name of the branch you'd like to pull in changes from. For example, if you're getting the most recent changes from master:

git merge master
Enter fullscreen mode Exit fullscreen mode

This creates a new commit, which is referred to as a merge commit. A merge commit is basically just an indication that a merge happened, and there is nothing wrong with having these commits in your git history (in my opinion).

However, some people dislike the presence of merge commits in their repository's history. For those people, you can use git rebase to catch up with another branch. This is typically how I like to get my feature branches caught up with master:

git rebase master
Enter fullscreen mode Exit fullscreen mode

As long as nothing funky has happened, this will painlessly replay your changes on top of the master branch, catching you up without creating a merge commit.

🤗

There's more...

I'm writing a lot of articles these days, I run a podcast, and I've started sending out a newsletter digest about all of the awesome stories I'm hearing.

You can also follow me on Twitter, where I make silly memes and talk about being a developer.

Top comments (4)

Collapse
 
skyboyer profile image
Yevhen Kozlov • Edited

Thanks for raising this topic. People typically underestimate git's flexibility.

Let me add few notes.

git add -p + git commit may be replaced with single git commit -p. Actually -p is applicable to most file-level commands like add, commit, checkout and stash save.

As for git rebase master: if we are talking about "re-apply my comment on most recent code" then it would probably more handy git pull --rebase origin master: it's like 2-in-1: git fetch + git rebase.

And it were not noted here but I believe it's also handy: git log with keys -S and -G may really help to find out "when it has been introduced?" or "where it has been removed?". annotate works on per-line basis that is not granular enough.

Another thing is git bisect: once you have some shell command that fails or succeeds and you'd like to find out commit that "broke things" it's fast way to go.

Collapse
 
bootcode profile image
Robin Palotai

For long, I was merge-averse on feature branches, and used rebase too. But it gets messy once others start interacting with your branches, since after the rebase you need a forced push. That ruins others' state.

So recently I stick with merge commits as you advise.

Collapse
 
jessekphillips profile image
Jesse Phillips

If you only rebase, no modification to the commits, everyone can rebase to stay up-to-date. Merge conflicts still happen, but git will understand you already have other commits.

But yes, a shared feature branch should be treated as a shared branch.

Collapse
 
bngcebetsha profile image
Buntu Ngcebetsha

I love

git commit --amend

Thanks