top

Git tips and tricks/common use-cases faced by developers

 A few days ago, I was told to refactor the codebase of a project where the name of a package and its references were to be modified. At first, it seems like the task has a pretty simple solution i.e. Rename the files and directories. git add --all git commit -m "[refactor] Rename package XYZ to ABC" git push origin master And you’re done! (you think) But what happens when you head over to GitHub and look at the commit histories of the renamed files and folders? The histories of the files have suddenly vanished and, to a person who hasn’t been working on the project since its inception, it looks like the renamed files were newly added in the last [refactor] commit. Background When you rename a file in a git repository, git log <filename> only shows the changes done to the renamed files after the rename operation, if you use the --follow argument, however, it continues history before a rename operation (that is, it searches for similar content using the heuristics and ignores the names of the files). Unfortunately, there is no way to see the history of the files before the rename operation on GitHub, since GitHub shows logs similar to the output of the git log command without — -follow. In short, git (implicitly) doesn’t show commit histories of files after they have been renamed. A lot of developers I’ve worked with, face such challenges with git and waste precious time banging their heads around looking for solutions, so I decided to put together a list of some commonly faced git blockers, their quick fixes, and some tips & techniques. I’ll be using this sample project repository on GitHub throughout the rest of the article as the remote repository for my local setup. 1. Rename a file/folder and preserve its history While looking for a solution to preserve and persist the commit histories of renamed files and folders to GitHub, I found this gist  The above script is a lifesaver when you have over 4000 commits with more than 200 files to refactor/move! The usage and working of the script is very well documented in the commented sections. Learn to use the script to rename files while persisting their histories (before renaming) to GitHub. 2. Branch off a set of recent commits Let’s say you started working on a new feature for an existing project, you made some changes, pushed a few commits, and then you realized you should have made a new branch to work on the feature. So how do you move those changes from, let’s say, master, to a new feature branch? You simply want to keep these changes (commits) in the feature branch and roll back to the commit before the new feature’s code was added in the master branch —   You need to be extra careful with 2 steps - Resetting to the commit you want: Make sure you correctly enter the commit hash (from where your feature branch branches off) to ensure none of your commits are lost in the process of migrating them to a new branch. Updating the remote forcefully: Other developers in your team might have already pushed to your remote (while you were working on the new feature) and if you want to keep those changes, doing a git pull before pushing would be the way to go. [WARN] Force pushing commits to the remote may lead to code loss! 3. Stage all (only) deleted files for your next commit If you have additions, modifications and deletions all at once, ready to be committed, it is generally a better idea to put all 3 operations in separate commits for ease of debugging, in case the changes break something in production. So how do you select only the deleted files to stage them for a commit? Well, this simple one line works like magic for exactly that —   The magic words add (technically track removals of) all the deleted files to the stage. They can now be easily committed and pushed while keeping the additions and modifications in the next commits. Unlike git add * which adds all the additions to the stage, git rm * doesn’t add all the deletions, instead, it deletes all the files in the repository and stages them for a commit. Use git rm very cautiously, you have been warned! 4. Git Resets — Soft, Mixed, Hard Ever wonder why there are three types of resets? The fundamental difference between the different kinds of resets is what you want to do with the changes done after the commit you are resetting to. git reset --hard <commit-id>resets the HEAD to <commit-id>and all the changes after<commit-id> are discarded. If you are absolutely sure you don’t want any changes after <commit-id>, you should do a hard reset. git reset --mixed <commit-id>resets the HEAD to <commit-id>and all the changes after<commit-id> are added to your working tree i.e. under “Changes not staged for commit”. This preserves all the changes done after <commit-id> and you can continue work on them. git reset --soft <commit-id> resets the HEAD to <commit-id> and all the changes after <commit-id> are added to your staging area i.e. under “Changes to be committed” so that you can immediately commit them with an updated commit message. A soft reset is essentially a mixed reset plus an additional step of adding the changes to the staging area i.e. doing a git add <files> after resetting. 5. Forgot to add a few changes in the last commit? If you’ve ever been in a position when you stage (add) some changes to be committed, modify them again and forget to add them again before committing, you will know what I’m talking about. You just forget to add that one line change in the last commit and now you have to add it to the next commit which may not describe the context of that one line change. Instead of creating two (or more) commits for a single context, you can update the changes in the last commit. Use the--amend flag with git commit, if you forget to add some files or code changes. Note that if you had already pushed the latest commit to your remote, then you will need to do a forced update, since amending a commit changes its hash and will throw an error when you try to push the amended commit to your remote Fix some stuff, add to stage, amend the latest commit and push to remote! 6. Delete a commit / undo a remote push! The good old use case where you were too excited to commit your changes and pushed them to your remote without realizing you added a bug in the commit and want to roll back, so how do you delete the commit once it’s pushed to your remote? The gist of the solution is that you have to roll back to an earlier commit in your local repository and then do a forced push to overwrite your remote. The -f flag pushes the commits forcefully if your local branch and remote branch have diverged and syncs up your remote with your local branch. Be extra careful as there is a chance that it will overwrite some of the remote commits. Hopefully, I’ve made your life a little easier, if you are frequently looking for git commands/procedures to do something, do share your experiences, until then happy coding!
Rated 4.0/5 based on 20 customer reviews
Normal Mode Dark Mode

Git tips and tricks/common use-cases faced by developers

Sahil Lamba
Blog
05th Jan, 2018
Git tips and tricks/common use-cases faced by developers

 A few days ago, I was told to refactor the codebase of a project where the name of a package and its references were to be modified. At first, it seems like the task has a pretty simple solution i.e.

  1. Rename the files and directories.
  2. git add --all
  3. git commit -m "[refactor] Rename package XYZ to ABC"
  4. git push origin master
  5. And you’re done! (you think)

But what happens when you head over to GitHub and look at the commit histories of the renamed files and folders?

The histories of the files have suddenly vanished and, to a person who hasn’t been working on the project since its inception, it looks like the renamed files were newly added in the last [refactor] commit.

Background

When you rename a file in a git repository, git log <filename> only shows the changes done to the renamed files after the rename operation, if you use the --follow argument, however, it continues history before a rename operation (that is, it searches for similar content using the heuristics and ignores the names of the files).

Unfortunately, there is no way to see the history of the files before the rename operation on GitHub, since GitHub shows logs similar to the output of the git log command without — -follow. In short, git (implicitly) doesn’t show commit histories of files after they have been renamed.

A lot of developers I’ve worked with, face such challenges with git and waste precious time banging their heads around looking for solutions, so I decided to put together a list of some commonly faced git blockers, their quick fixes, and some tips & techniques.

I’ll be using this sample project repository on GitHub throughout the rest of the article as the remote repository for my local setup.

1. Rename a file/folder and preserve its history

While looking for a solution to preserve and persist the commit histories of renamed files and folders to GitHub, I found this gist 

The above script is a lifesaver when you have over 4000 commits with more than 200 files to refactor/move! The usage and working of the script is very well documented in the commented sections. Learn to use the script to rename files while persisting their histories (before renaming) to GitHub.

2. Branch off a set of recent commits

Let’s say you started working on a new feature for an existing project, you made some changes, pushed a few commits, and then you realized you should have made a new branch to work on the feature. So how do you move those changes from, let’s say, master, to a new feature branch?

github11

You simply want to keep these changes (commits) in the feature branch and roll back to the commit before the new feature’s code was added in the master branch —

 

github22

You need to be extra careful with 2 steps -

  • Resetting to the commit you want: Make sure you correctly enter the commit hash (from where your feature branch branches off) to ensure none of your commits are lost in the process of migrating them to a new branch.
  • Updating the remote forcefully: Other developers in your team might have already pushed to your remote (while you were working on the new feature) and if you want to keep those changes, doing a git pull before pushing would be the way to go.
[WARN] Force pushing commits to the remote may lead to code loss!

3. Stage all (only) deleted files for your next commit

If you have additions, modifications and deletions all at once, ready to be committed, it is generally a better idea to put all 3 operations in separate commits for ease of debugging, in case the changes break something in production.

github33

So how do you select only the deleted files to stage them for a commit? Well, this simple one line works like magic for exactly that —

 

github44

The magic words add (technically track removals of) all the deleted files to the stage. They can now be easily committed and pushed while keeping the additions and modifications in the next commits.

Unlike git add * which adds all the additions to the stage, git rm * doesn’t add all the deletions, instead, it deletes all the files in the repository and stages them for a commit. Use git rm very cautiously, you have been warned!

4. Git Resets — Soft, Mixed, Hard

Ever wonder why there are three types of resets? The fundamental difference between the different kinds of resets is what you want to do with the changes done after the commit you are resetting to.

github77

git reset --hard <commit-id>resets the HEAD to <commit-id>and all the changes after<commit-id> are discarded. If you are absolutely sure you don’t want any changes after <commit-id>, you should do a hard reset.

git reset --mixed <commit-id>resets the HEAD to <commit-id>and all the changes after<commit-id> are added to your working tree i.e. under “Changes not staged for commit”. This preserves all the changes done after <commit-id> and you can continue work on them.

git reset --soft <commit-id> resets the HEAD to <commit-id> and all the changes after <commit-id> are added to your staging area i.e. under “Changes to be committed” so that you can immediately commit them with an updated commit message.

A soft reset is essentially a mixed reset plus an additional step of adding the changes to the staging area i.e. doing a git add <files> after resetting.

5. Forgot to add a few changes in the last commit?

If you’ve ever been in a position when you stage (add) some changes to be committed, modify them again and forget to add them again before committing, you will know what I’m talking about.

You just forget to add that one line change in the last commit and now you have to add it to the next commit which may not describe the context of that one line change.

Instead of creating two (or more) commits for a single context, you can update the changes in the last commit. Use the--amend flag with git commit, if you forget to add some files or code changes.

Note that if you had already pushed the latest commit to your remote, then you will need to do a forced update, since amending a commit changes its hash and will throw an error when you try to push the amended commit to your remote

Fix some stuff, add to stage, amend the latest commit and push to remote!

6. Delete a commit / undo a remote push!

The good old use case where you were too excited to commit your changes and pushed them to your remote without realizing you added a bug in the commit and want to roll back, so how do you delete the commit once it’s pushed to your remote?

The gist of the solution is that you have to roll back to an earlier commit in your local repository and then do a forced push to overwrite your remote.

The -f flag pushes the commits forcefully if your local branch and remote branch have diverged and syncs up your remote with your local branch. Be extra careful as there is a chance that it will overwrite some of the remote commits.

Hopefully, I’ve made your life a little easier, if you are frequently looking for git commands/procedures to do something, do share your experiences, until then happy coding!

Sahil

Sahil Lamba

Blog Author

A recent BTech graduate from Indian Institute of Technology Roorkee, Sahil has an active interest in Software Design & Development. Focused on creating enterprise solutions through codes, working with various tech stacks, design patterns, and software methodologies. He is currently a Software Engineer at PayPal.

GitHub: https://github.com/thedrumsknight

Leave a Reply

Your email address will not be published. Required fields are marked *

SUBSCRIBE OUR BLOG

Follow Us On

Share on

other Blogs