Useful git commands and snippets
Table of Contents
Git is probably the most used developer tool (aside from operating systems, shells and browsers/editors) out there. It can be a lifesaver but if used wrong cause terrible headache. Here are some commands and snippets I’ve encountered and found useful.
Prerequisites
I’m assuming that the default remote in these examples is origin
and a foreign remote is called remote
.
As example remote URL I will either use pseudo local paths, random urls or linux because it has infinite collaborators.
The default branch is master
and any other branch is mostly called branch
.
The example tag in these examples is in most cases v1.0.0
.
Git Subcommands
git lfs
Git-LFS (Large File Storage) is a git plugin/subcommand that lets you save binary files outside of the git object storage. This saves some space when cloning and works faster. To install it, follow the instructions on the website.
Repository setup
Assuming you want to track .bin
files with Git-LFS:
- Change into project directory
- Initialize Git-LFS:
git lfs install
- Tell Git-LFS which files to track:
git lfs track "*.bin"
- Add the
.gitattributes
file and commit:git add .gitattributes && git commit
Alternatively, you can skip/speed up step three by putting the following in the .gitattributes
file (create it if it’s not there):
*.bin filter=lfs diff=lfs merge=lfs -text
git log
When run with no arguments, git log
shows a vi/less style commit history output.
But it can also be used for more specific stuff.
Show log for specific remote/branch/tag
The following command can be used to show the history of a specific tag or remote and/or branch:
git log remote/master
Show log for specific subdirectories
You can use the following command if you’re in a subdirectory of a repository and only want to see the local history:
git log -- ./
The --
delimiter is used to make a clear separation between refs and paths.
This can also be seen in some git error messages.
git tag
A tag is like a bookmark on a specific commit. Mostly it’s a good practice to tag the version bump commits (if those are made).
Listing tags
While git tags can be seen as an appendage next to the commit id in git log
, it’s also possible to show all available tags.
Local tags
I think the command below is self-explanatory:
git tag -l
Remote tags
Listing remote tags can either be done for the default remote:
git ls-remote --tags
Or a specific remote:
git ls-remote --tags remote
Creating tags
A tag can be created like this:
git tag v1.0.0
The tag will now show up in the git history as v1.0.0
which is an often used scheme.
Btw have you heard of semver?
Pushing tags
Since git push
doesn’t submit patches by default, a flag is needed:
git push --tags
Deleting tags
Deleting tags can be necessary when doing debug stuff (like CI that should build on every tag but there’s a bug in a script of a dependency that only runs on CI so you end up with something like v1.0.0.a.a.a.a.a.a.a.a.a.a.h.e.l.p
).
Local tags
Local tags can be deleted quite easily:
git tag -d v1.0.0
Remote tags
When removing remote tags, it’s required to to a push:
git push remote :v1.0.0
You can see more info on this syntax in the section about git push
below.
Update: Since git 1.8.0, you can also use the following which is a bit more readable:
git push --delete remote v1.0.0
git push
The push
command allows to submit changes to a remote.
When used without arguments in a properly set up repository, it will push all new (for the remote) commits on the current branch (or it’s remote counterpart) to the default remote.
git remote
A remote is in most cases a collaboration server like GitLab, GitHub or Gitea, but it can in general be any local (or local-ish mounted) or remotely (ssh/http(s)) available .git
folder.
Showing available remotes
All available remotes can be printed out by running:
git remote -v
Setting a default remote
A default (upstream) remote can be set by running the following:
git push -u remote branch
This sets the foreign remote called remote
as upstream that will be used by default when running push
or pull
.
The -u
flag can be extended to --set-upstream
.
Adding a remote
Adding a remote can be done by running the following:
git remote add remote /path/to/repo/.git
When adding a remote remote (haha), a protocol and/or authentication might be required:
git remote add remote ssh://git@example.com:foo/bar.git
Deleting a remote
Deleting a remote can be done like this:
git remote remove remote
Changing a remote’s URL
When a repository was moved or there is some other stuff going on, a remote url can be changed by running the following:
git remote set-url remote /path/to/new/repo/.git
Note that by default, only the fetch url is updated by this command.
It’s also default for git to use only the fetch url when a push url is not explicitly set.
In case of local mirrors (see section about git clone
below) it might be required to set the push url to the original remote.
This can be done by appending the --push
flag after set-url
(and before the url).
git clone
Cloning means downloading/synchronizing a remote repository to/with your machine.
It’s used to get an initial copy of the repository (updating is done with git pull
instead).
Cloning in general
Most Git platforms mentioned above support cloning by passing the URL of the repository’s web frontend like the following:
git clone https://github.com/torvalds/linux
This will result in the repository being cloned into the current folder as linux
.
On some platforms, the URL might look more like https://github.com/torvalds/linux.git
.
In this case, git will omit the .git
extension for the local folder.
Changing the output directory
To clone a remote repository into a folder other than the repository name, you can simply append it. If it does not exist, git will create it automatically. Example:
git clone /path/to/repo/.git ../otherName
The command above will result in the repository being cloned into the parent of the current folder as otherName
.
You can also specify an absolute path.
Cloning as mirror
In some cases it might be wise to store an exact copy of a remote repository on another place. For instance when the connection to a remote repository is pretty slow or the network has a limited transmission rate.
It can be a good idea to clone a very remote repository onto a more local server and let local clients pull from there.
The remote repository can be cloned as mirror by appending the --mirror
flag:
git clone --mirror ssh://user@host:repo.git
The mirror can be kept up to date by running git remote update
.
This can be intervalled by using something like the following crontab which will update the mirror every fifteen minutes:
*/15 * * * * cd /path/to/mirror && git remote update >/dev/null 2>&1
Advanced Stuff
Below is more advanced stuff. Some of it requires additional software.
Cleaning up messy contributor stats
We all probably had to deal with this at some point: The committer you use in your git hoster is not the same you use locally and suddenly you appear as three different people because you made commits on your local machine, the web ui and maybe some dev system.
You can clean this up by rewriting the git history and changing the duplicated committers to use your desired data. Note that this requires a force push and will open a whole new can of worms when lots of people use the repo!
First install git-filter-repo
, and then modify the following command to suit your setup (the callback is python code):
The bytearray("".encode())
at the new_name
variable is needed in case the committer name you want to use contains special characters. You could also supply a plain string if your name doesn’t contain special chars.
git filter-repo --commit-callback ' ⬡ 16.15.0
old_email = b"duplicated_committer@example.com"
new_email = b"real_committer@example.com"
new_name = bytearray("New Committer Name".encode())
if commit.author_email == old_email:
commit.author_email = new_email
commit.author_name = new_name
if commit.committer_email == old_email:
commit.committer_email = new_email
commit.committer_name = new_name
'
Note: git-filter-repo
will complain when not run on a fresh clone, and will delete your remotes, so you don’t accidentally do weird stuff. You’ll need to re-add them in order to force push after the correction.