2020-11-10

mdlbear: (technonerdmonster)

Hopefully, this post will become the first of a series about solving various common problems with Git. Note that the grouping in that phrase is intentionally ambiguous – it could be either “(solving various common problems) with Git”, or “solving (various common problems with Git)”, and I expect to cover both meanings. Often there are aspects of both: Git got you into trouble, and you need to use Git to get yourself out of it.

“It is easy to shoot your foot off with git, but also easy to revert to a previous foot and merge it with your current leg.” —Jack William Bell

In many cases, though, this will involve git rebase rather than merge, and I think “rebase it onto your current leg” reads better.

Overcoming your fear of git rebase

Many introductions to Git leave out rebase, either because the author considers it an “advanced technique”, or because “it changes history” and the author thinks that it’s undesirable to do so. The latter is undermined by the fact that they usually do talk about git commit --amend. But, like amend, rebase lets you correct mistakes that you would otherwise simply have to live with, and avoid some situations that you would have a lot of trouble backing out of.

In order to rebase fearlessly, you only need to follow these simple rules:

  • Always commit your changes before you pull, merge, rebase, or check out another branch! If you have your changes committed, you can always back out with git reset if something goes wrong. Stashing also works, because git stash commits your work in progress before resetting back to the last commit.
  • Never rebase or amend a commit that’s already been pushed to a shared branch! You can undo changes that were pushed by mistake with git revert. (There are a few cases where you really have to force-push changes, for example if you foolishly commit a configuration file that has passwords in it. It’s a huge hassle, and everyone else on your team will be annoyed at you. If you’re working on a personal project, you’ll be annoyed at yourself, which might be even worse.)
  • If you’re collaborating, do your work on a feature branch. You can use amend and rebase to clean it up before you merge it. You can even share it with a teammate (although it might be simpler to email a patch set).

That last rule is a lot less important if you’re working by yourself, but it’s still a good idea if you want to keep your history clean and understandable – see Why and How To Keep Your Master Happy. And remember that you’re effectively collaborating if your project is on GitHub or GitLab, even if nobody’s forked it yet.

Push rejected (not fast forward)

One common situation where you may want to rebase is when you try to push a commit and it gets rejected because there’s another commit on the remote repo. You can detect this situation without actually trying to push – just use git fetch followed by git status.

I get into this situation all the time with my to-do file, because I make my updates on the master branch and I have one laptop on my desk and a different one in my bedroom, and sometimes I make and commit some changes without pulling first to sync up. This usually happens before I’ve had my first cup of coffee.

The quick fix is git pull --rebase. Now all of the changes you made are sitting on top of the commit you just pulled, and it’s safe for you to push. If you’re developing software, be sure to run all your tests first, and take a close look at the files that were merged. Just because Git is happy with your rebase or merge, that doesn’t mean that something didn’t go subtly wrong.

Pull before pushing changes

I get into a similar situation at bedtime if I try to pull the day’s updates and discover that I hadn’t pushed the changes I made the previous night, resulting in either a merge commit that I didn’t want, or merge conflicts that I really didn’t want. You can avoid this problem by always using git pull --rebase (and you can set the config variable pull.rebase to true to make that the default, but it’sa little risky). But you can also fix the problem.

If you have a conflict, you can back get out of it with git merge --abort. (Remember that pull is just shorthand for fetch followed by merge.) If the merge succeeded and made an unwanted merge commit, you can use git reset --hard HEAD^.

Another possibility in this situation is that you have some uncommitted changes. In most cases Git will either go ahead with the merge, or warn you that a locally-modified file will be overwritten by the merge. In the first case, you may have merge conflicts to resolve. In the second, you can stash your changes with git stash, and after the pull has finished, merge them back in with git stash pop. (This combination is almost exactly the same as committing your changes and then rebasing on top of the pulled commit – stash actually makes two hidden commits, one to preserve the working tree, and the other to preserve the index. You can see it in action with gitk --all.

… and I’m going to stop here, because this has been sitting in my drafts folder, almost completely finished, since the middle of January.

Resources

NaBloPoMo stats:
   5524 words in 11 posts this month (average 502/post)
    967 words in 1 post today

Another fine post from The Computer Curmudgeon (also at computer-curmudgeon.com).
Donation buttons in profile.

Most Popular Tags

Style Credit

Page generated 2025-05-17 12:51 am
Powered by Dreamwidth Studios