git --everything-is-local
Git is a great and very powerful tool. There are probably over a hundred command line options that allow you to take advantage of its features. Here are few interesting things you can do easily.
Restore a deleted file
To restore a file that has been deleted several commits ago you have to find the commit that deleted the file. Then checkout the version of the file that was before the delete commit. Suppose you have the following in your repository.
1 2 3 4 5 6 |
$ git log --oneline 329d44c Add main method. 2626b9b Remove main.c d21af2d Java support. bd83dbf Add comment. 14d7424 Initial import. |
As you can see originally there was a file named main.c but at some point it got deleted by: 2626b9b. To restore the content of main.c you can find the last commit that changed it.
1 2 |
$ git rev-list -n 1 HEAD -- main.c 2626b9bf3d8864eda5eb9d3ad5d4f2f963655171 |
And checkout the previous version (denoted by the caret character: ^).
1 |
$ git checkout 2626b9b^ -- main.c |
Of course you checkout the previous version because the last commit that changed the file is the one that deleted it.
Rename files and directories
Git is very smart when it comes to renaming files. You don’t actually have to tell git that you’re renaming a file, it will figure it out on its own. Here’s an example, first we rename the file with whatever tool we like.
1 |
$ mv Main.java Launcher.java |
At this point git knows that you deleted a file that was in the repository (Main.java) and created a new file that is still untracked (Launcher.java).
1 2 3 4 5 6 7 8 9 |
$ git status On branch master Changes not staged for commit: deleted: Main.java Untracked files: Launcher.java |
Then you can just add these changes to the staging area and git will know that you renamed the file because the content is the same.
1 2 3 4 5 6 7 |
$ git add Main.java Launcher.java $ git status On branch master Changes to be committed: renamed: Main.java -> Launcher.java |
Actually it will detect that you renamed a file even if you made changes to the content. In this case I also changed the name of the class to keep it a valid Java file.
1 2 3 4 5 6 7 8 9 |
$ git diff --cached -M similarity index 68% rename from Main.java rename to Launcher.java -class Main { +class Launcher { public static void main(String[] args) { |
As you can see git calculates a similarity index of 68% and correctly concludes this is a rename operation.
Revert commits
Sometimes you’ll need to revert the changes introduced by one or more commits. There are generally two approaches to do that. One is to change the existing git history and the other to create new commits. Changing history can lead to issues if the commits are shared (when other people have them in their repositories). So probably the easiest and safest way is to use the revert command to create new commits with the opposite changes. Lets look at the following history.
1 2 3 4 5 6 7 |
$ git log --oneline 65e3b35 Add class Javadoc. 329d44c Add main method. 2626b9b Remove main.c d21af2d Java support. bd83dbf Add comment. 14d7424 Initial import. |
Commit 329d44c adds a main method.
1 2 3 4 5 6 7 8 9 |
$ git show --oneline 329d44c 329d44c Add main method. class Main { + public static void main(String[] args) { + + } } |
You can create a new commit that has exactly the opposite changes.
1 2 3 |
$ git revert 329d44c [master d6776ad] Revert "Add main method." 1 file changed, 3 deletions(-) |
Now the history contains a new commit that reverted the changes 329d44c introduced.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
$ git log --oneline d6776ad Revert "Add main method." 65e3b35 Add class Javadoc. 329d44c Add main method. 2626b9b Remove main.c d21af2d Java support. bd83dbf Add comment. 14d7424 Initial import. $ git show --oneline d6776ad d6776ad Revert "Add main method." class Main { - public static void main(String[] args) { - - } } |