To compare last two commits.
git diff HEAD^ HEAD
Since comparison with HEAD is the default you can omit it for even shorter syntax.
git diff HEAD^
If you are using Git 1.8.5+ you can use @ which is an Alias for HEAD.
git diff @~..@
# or simply
git diff @^
But even easier is some Git suger which defaults to the same thing.
git show
To compare HEAD to any other commit.
git diff commit_id HEAD
# or simply
git diff commit_id
To compare the state of the index with HEAD.
git diff --cached
If you only want the list names of staged files then you can use.
git diff --name-only --cached
There are various methods, using rebase
for example. Probably the simplest way is to use reset
however.
reset
does not create commits, it just updates a branch (which is a pointer to a commit) to point to a different commit. reset
has four main options to control what happens to your work tree and index during the reset. If you use --soft
then all your work remains intact, the index represents the state of all the commits from the commit you reset to, and all th changes are ready to be committed. You can use this when all you work is good but you just want to commit it differently ie. roll several commits into a single commit.
Say we wanted to squash the last 3 commits into 1.
git reset --soft HEAD~3
git commit -m "Description of last 3 commits"
If you already pushed these commits to a remote then pushing again will likely be rejected, warning you that there are remote chnages which you need to pull first. To overwrite the remote history, use --force
or -f
.
git push -f
Warning - you never want to squash commits and force push to a remote if others are using that branch. You are rewriting the history of the branch and changing it will cause problems for the other users and you will not be popular!
Note that the syntax HEAD~3
means go back 3 commits from HEAD. We can be more explicit and pick out the specific commit we want to reset to. The reflog helps with this.
$ git reflog
e175e4c (HEAD -> master) HEAD@{0}: commit: updated f2 again
4675ed2 HEAD@{1}: commit: updated file f1 again
347596b HEAD@{2}: commit: updated file f2
1db1328 HEAD@{3}: commit: updated file f1
4675ed2 HEAD@{4}: commit (initial): added some files
The following would all be equivalent.
git reset HEAD~3
git reset
If you are currently checked out on master
at commit C
in the following example.
- A - B - C (HEAD, master)
And then you do a.
git reset B
You end up with.
- A - B (HEAD, master) C
C is still in the repo, and you can find it by looking at git reflog show HEAD
or git reflog show master
, but it's not actually accessible from any branch anymore. Git will permanently delete C
after 30 days, unless you recover it by pointing a branch at it again (git checkout C; git branch <new branch name>
).
Note that the following are equivalent since --mixed
is the default control option for reset
.
git reset B
git reset B --mixed
It's behaviour resets the index (it's empty) and the work tree contains the work that you commited in B and C. So you would need to add them to the index again before you can commit them. This default behaviour (--mixed
) is because you generally use reset when you realise you made a mistake back in commit B
and want to modify it. Note that you do not lose any work, the work tree will also contain the changes you committed in C
. You can make your changes and choose to commit everything in a new single commit or you could just add the changes you wanted to make for B
to the index, commit them and then add/commit whats left for C
again if you wanted to preserve the history. Note that git refloggit log
Note the difference in behaviour compared to checkout. Say we are back here.
- A - B - C (HEAD, master)
And then you do.
git checkout B
You end up with.
- A - B (HEAD) - C (master)
Now you're in detached HEAD state. HEAD, work tree, index all match B, but the master branch was left behind at C. If you make a new commit D at this point, you'll get this.
- A - B - C (master)
\
D (HEAD)
You can see the history of commands in the reflog.
$ git reflog
76eb921 (HEAD -> master) HEAD@{0}: commit: Updated f2
ff8adbf HEAD@{1}: commit: Added f2
6e4247a HEAD@{2}: commit: Updated f1
a72bf1d HEAD@{3}: commit (initial): Added f1
And the git log.
$ git log --pretty=oneline --abbrev-commit
76eb921 (HEAD -> master) Updated f2
ff8adbf Added f2
6e4247a Updated f1
a72bf1d Added f1
But it is important to understand that the reflog is a history of commands, not commits. Lets say we made a mistake in the second commit and we reset the last 3 commits.
git reset --soft HEAD~3
git commit -m "Squash commit"
The following would have been equivalent for the reset command.
git reset --soft a72bf1d
git reset --soft HEAD@{3}
The reflog now shows two more commands, 1 for the reset, amd 1 for the new commit.
$ git reflog
a1478a2 (HEAD -> master) HEAD@{0}: commit: Squash commit
a72bf1d HEAD@{1}: reset: moving to HEAD~3
76eb921 HEAD@{2}: commit: Updated f2
ff8adbf HEAD@{3}: commit: Added f2
6e4247a HEAD@{4}: commit: Updated f1
a72bf1d HEAD@{5}: commit (initial): Added f1
But if we look at the git log now we can see that we only have two commits.
$ git log --pretty=oneline --abbrev-commit
a1478a2 (HEAD -> master) Squash commit
a72bf1d Added f1
Delete a local branch.
git branch -d branch_name
Delete a remote branch.
git push origin --delete branch_name
Switch to local branch you want to rename.
git checkout <old_branch_name>
Rename the local branch.
git branch -m <new_branch_name>
If <old_branch_name>
already exists in the remote then push the <new_branch_name> and reset the upstream branch.
git push origin -u <new_branch_name>
Delete the <old_branch_name> in the remote.
git push origin --delete <old_branch_name>
To remove all changes from your working tree you can use the following - note this is senstitive to where you are currently located if not in the root of the project.
git checkout .
The above does not remove untracked files. To do that you can use.
git clean
Amend the most recent commit but beware that any staged changes will also get committed.
git commit --amend -m "New commit message"
If the commit was already pushed to a remote then you will need to force push it if you want to share it but of course this changes the history and will impact anyone else working on that branch.
git push <remote> <branch> -f
To go back 1 commit (~1
) and put the changes from that commit into your working copy (--soft
) - use this if you want to make further modifications to that last commit.
git reset --soft HEAD~1
If you want to be more destructive and want to discard the changes from that commit then use the --hard
flag.
git reset --hard HEAD~1
Unstage all chnages from the index.
git reset
Unstage specific files.
git reset <file>
To see the different between the working tree and HEAD.
git diff
git diff HEAD
To just get a brief summary of changes.
git diff --shortstat
119 files changed, 181 insertions(+), 181 deletions(-)
Get a summary of changes between the last two commits.
git diff HEAD^ HEAD --shortstat
Get a summary of changes between two arbitrary commits.
git diff 9e56a72..e1977064 --shortstat
Back out of a problematic merge that is in progress (depends on MERGE_HEAD being present which it should be if you're in the middle of a merge). The following
git merge --abort # v1.7.4+
# or
git reset --merge # v1.6.1+
If you encounter conflicts during a merge but know the changes being merged in are all correct then you can force the merge with the following ie. this overwrites any changes in your current branch with the changes being merged in.
git merge [branch] --strategy-option theirs
If, on the other hand, you want to do the merge but reject all inbound changes then you can do.
git merge [branch] --strategy-option ours