What do you think? Discuss, post comments, or ask questions at the end of this article [More about me]

(heart) git.  No, seriously...  I've also used several git clients over my short lifetime, my current favourite is tig (ncurses git client)  - although if I had to choose a gui git client it would definitely be gitkraken.

Full disclosure: I am the AUR (Arch Linux User Repository) package maintainer for gitkraken (standalone version for air-gapped networks and labs).

However, sometimes I need to do stuff that's just easier on the command line.  Below are several of these commands that I always seem to forget (dang you nice git client - making me forget my terminal roots...).

git reset back to a previous commit

Sometimes you might want to take a local branch back to a previous commit (or state... like in between several amend commits) without actually reverting commits.

First, have a look at your reflog.  For example, to look at the previous 10 git operations do

git reflog -10

This will show a list of hashes (representing commits) and the actions taken, or commit message.

Now, copy the hash and do

git reset --soft <hash>

Now, this will point HEAD to the selected hash state.

Simply reset and you're back to a previous commit:

git reset --hard HEAD

Reset specific file to previous commit version

From time to time you may need/want to reset a specific file to a version in a previous commit.  What you essentially want to do is "checkout" the file from said commit:

git checkout <COMMIT-HASH> -- path/file

For example, suppose I want to check reset the current file found in the path (relative to the git root foler) "i3/config", I would do:

git checkout 3b5e3ff7ab0c3d61984532950a1c7acfe3f9efa6 -- i3/config

where 3b5e3ff7ab0c3d61984532950a1c7acfe3f9efa6 is the commit hash (you can also use the "short" hash 3b5e3ff).

Create orphan branch

In some instances you might need/want to create a branch which is not based on another branch.  This is known as an orphan branch.  Some git front-ends provide a nice way of doing this but many (even good ones) do not.  No matter, you can always create one from the command line, like so:

git checkout --orphan <name-of-orphan-branch>
<add/remove files to the working directory as desired>
git add .
git commit -m '<message here>'

.gitignore not working

A situation can occur where you've already committed a file to the repo, and then later on try to add the same file to .gitignore.  In this case, it may appear that .gitignore is not working (git is actually working as designed).

You'll need to remove the previously added files (that you now want to ignore) from the repo.  Fortunately, this is quite easy to do by running the following commands in git bash:

git rm . -r --cached
git add .

You'll note that now the files you previously added (but now what to ignore) have been deleted from the working folder.

Now commit and things will be working as expected. 

Perform bash/shell operations on every commit (and rewrite history)

A few times I've needed to perform certain operations on every commit of a branch in history (like say delete a file that shouldn't have been added to the repo).  In a recent case, I was actually separating out multiple code bases from one big repository into several smaller repositories.  Luckily, the repository contained separate folders, where each folder contained separate and independent projects.  So basically I just had to move these folders into separate repositories... while keeping all history for the specific folder only.

This turned out to be fairly straightforward (see, not all things with git are difficult (smile)).

Basically, I created a repo for each project and pushed master (which contained all folders and all changes over the original repo history) to each repo (i.e. basically just created copies of the original repo).  I then checked out each of the just created repos and ran the following from bash (or git bash if you've installed it on windows):

git filter-branch --tree-filter 'ls | grep -v project1-folder | xargs rm -rf; rm -f .gitignore' HEAD

where project1-folder  is the folder that I wanted to keep for this specific repo.  ls | grep -v project1-folder | xargs rm -rf lists all folders EXCEPT project1-folder and then pipes to xargs which runs rm -rf on each (which deletes those other folders). You'll note that I also deleted .gitignore (which contained a whole bunch of ignores for each project - I'll recreate this for each new repo just with the specific ignores relevant to the new repo).

Now git filter-branch can be pretty destructive as it rewrites history, so it creates a backup before the operation.  Once you're happy with the your filtering you can remove this backup with:

rm -rf .git/refs/original/

We have a problem now... we've removed all of the other folders from repo history (which is what we wanted) but it's resulted in lots of empty commits (e.g. when anyone changed files within one of the now deleted folders, these commits are now empty but still existent).

Not to worry, git contains a command for just this case.  The following will go through each commit, and if its empty, will remove it:

git filter-branch --prune-empty

After it is completed, you should now have only those commits that changed files/folders within the actual folder you want to keep (e.g. project-folder1).

You'll also probably want to remove the backup refs (which git creates before pruning empty commits)

rm -rf .git/refs/original/

git can't checkout on windows due to filename too long...

If you're using a git client on windows that uses msysgit as the backend, it might have been compiled to only accept filenames no longer than 260 characters.  If this affects you, you can workaround the issue by opening a git bash window (as admin) and:

git config --system core.longpaths true

Keeping git clean and happy

git generally is great at keeping things clean and in order.  I was on a project using a system that wasn't designed for source code management (but git still worked great!) and we had to commit several binary files.

I found that the following process really helped in keeping the repo in check (especially sizes due having to commit and comment on binary files often).

Get to git bash

In git extensions (for example) go to 'tools' menus, and select 'Git bash'.

Expire your reflog!

git reflog expire --expire=now --all

Prune!

git prune

(use 'git prune -n' to see what commits will be pruned without actually pruning).

Done...

unless you want to garbage collect and recompress everything (only do every so often to save space and house clean).

git gc

References

  1. https://stackoverflow.com/questions/22575662/filename-too-long-in-git-for-windows