Skip to content

Instantly share code, notes, and snippets.

@jonjack
Last active April 25, 2024 19:12
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jonjack/95ac9bc9d81f8cf04491b6bbf1bdaff5 to your computer and use it in GitHub Desktop.
Save jonjack/95ac9bc9d81f8cf04491b6bbf1bdaff5 to your computer and use it in GitHub Desktop.
Git scraps

Comparing commits

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

Squashing commits with reset --soft

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 

More on 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)

Use the reflog

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

Deleting branches

Delete a local branch.

git branch -d branch_name

Delete a remote branch.

git push origin --delete branch_name

Renaming branches

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>

Cleaning your working tree

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

Amending commits

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

Deleting commits

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

Working with the index

Unstage all chnages from the index.

git reset

Unstage specific files.

git reset <file>

State of the working treee

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

Merging

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment