The purpose of this post is to quickly go over some of the basic things most developers would require from source control software.
I highly recommend reading either Pro Git or Git Community Book.
If you committed some code by accident you have the following options:
If you just forgot a file or wrote the wrong comment, stage your changes and run
git commit --amend
. This will merge the new commit with the previous commit
(overriding the old comment).
Revert will create new commit which is the opposite of the commit you wish to remove, for example:
$ git log
dfdd917 added file2.txt
fbe043e added file1.txt
$ git revert dfdd917
$ git log
7bdd793 Revert "added file2.txt"
dfdd917 added file2.txt
If you already pushed the commit you wish to remove, this would be the best option.
If you want to get rid of the last commit (or several last commits) you can use the "git reset" command which will make the HEAD and the current branch point on the given commit.
$ git log
fcd3f55 (HEAD, master) added file3.txt
dfdd917 added file2.txt
fbe043e added file1.txt
$ git reset --hard dfdd917
$ git log
dfdd917 (HEAD, master) added file2.txt
fbe043e added file1.txt
You might ask yourself, what happened to the third commit? is it gone forever? well... the answer is no, the commit is still there, it's just unreachable.
To see all of the unreachable commits use the "git fsck" command:
$ git fsck --unreachable --no-reflogs
unreachable blob a83ffd
unreachable tree 984260
unreachable commit fcd3f5
This shows us we have three unreachable objects:
Wait, what's --no-reflogs?
The reflog records every action you perform in a git repository, if we were to run "git reflog" after the third commit this would have been the output:
$ git reflog
fcd3f55 HEAD@{1}: commit: added file3.txt
dfdd917 HEAD@{2}: commit: added file2.txt
fbe043e HEAD@{3}: commit (initial): added file1.txt
After we run the "git reset" command the reflog will look like this:
$ git reflog
dfdd917 HEAD@{0}: dfdd917: updating HEAD
fcd3f55 HEAD@{1}: commit: added file3.txt
dfdd917 HEAD@{2}: commit: added file2.txt
fbe043e HEAD@{3}: commit (initial): added file1.txt
As you can see the reset action did not remove the commit from the reflog, but added a new entry to the reflog saying that the HEAD was updated.
The --no-reflogs
argument tells git to not consider commits that are
referenced only by a reflog entry as reachable.
What do I do now?
Well, you can just leave that commit there, it won't be pushed to the remote repository and you can restore it whenever you like by running:
$ git checkout fcd3f55
If you want to remove you can cleanup you repository as described here at the bottom (basically, you expire the reflog entries, and then remove all unreachable objects).
What if I already pushed the commit?
Then this is option is highly unrecommended, but if you need to remove the commit from the server then use the "-f" attribute:
$ git push -f
The two most common ways to create a remote repository are:
git init --bare
in an empty
directory.If you do not have an existing local repository, the you should just clone the remote repository:
git clone path-to-repository
Notice that you get a warning, "You appear to have cloned an empty repository.".
If you have an existing local repository and the remote repository is empty you should push the local commits to the remote repository:
Add a new remote to your local repository, name the new remote "origin":
git remote add origin path-to-repository
This command will add the following lines to the .git/config file:
[remote "origin"]
url = path-to-repository
fetch = +refs/heads/*:refs/remotes/origin/*
These lines let git know that the "origin" remote is mapped to the specified url and that it should fetch the heads and the branches from the remote repository (this is the default behavior).
At this point, if you make some changes, commit them and try to push you will get the following error:
No refs in common and none specified; doing nothing.
Perhaps you should specify a branch such as 'master'.
Since the remote repository is empty there isn't a matching branch in origin, so the first time you push you must define which branch to push:
git push origin master
This command makes git push all commits in the "master" branch to the "origin" repository.
Note: if we had cloned a non-empty repository we could just run "git push".
To get the latest changes from a remote repository run:
git fetch
This command will download all the commits made to the remote repository since the latest fetch (or clone).
If there are changes in the remote repository we have the following options:
git merge origin/master
This command will take all of the commits from the given branch (in this case "origin/master") and incorporate them into the current branch.
Note #1: make sure you're in the correct branch before merging (run "git branch" to see which branch you're in).
Note #2: You can do fetch+merge automatically by running "git pull"
If you have made your own local commits before fetching new commits your history becomes split:
* my local commit #2
* my local commit #1
| * new remote commit #2
| * new remote commit #1
|/
* last commit before the split
One option is to do a merge and incorporate all of the remote commits into the current master branch. This will cause the history to look like this:
* Merge remote-tracking branch 'origin/master'
|\
| * new remote commit #2
| * new remote commit #1
* | my local commit #2
* | my local commit #1
|/
* last commit before the split
This is ugly...
The better way to do this is using Rebase which basically takes commits and modifies them (if needed) to match a different source commit.
So we can take the local master branch and make all of the commits from the split-off point match the current origin/master branch.
To perform a rebase we run:
git rebase origin/master
This command will make all of the changes from 'master' to the previous 'origin/master' match the new origin/master so the history will look like this:
* my local commit #2
* my local commit #1
* new remote commit #2
* new remote commit #1
* last commit before the split
For a more detailed example of rebase you can see my previous post.
Thanks for reading, I hope you find this article useful.
Until the next time, David.