DEV Community

loading...
Cover image for 10 Git Tricks to Save Your Time and Sanity

10 Git Tricks to Save Your Time and Sanity

jacobherrington profile image Jacob Herrington (he/him) Updated on ・4 min read

1. Checkout a single file from another branch

Have you ever destroyed a file and just wished you could have a fresh start? Or needed the changes you made in one file in another branch? This command lets you grab just one file from another branch.

git checkout some-other-branch -- yarn.lock

You can use the same trick to checkout one file from a specific commit.

git checkout 9146367 -- yarn.lock

A terminal screenshot showing the output of the above commands on the Solidus project

This is an effective trick if cherry-pick would pick up other files that you don't need.

2. View the log without merge commits

Merge commits annoy some people. In fact, some people would rather never use the merge command because they are so annoyed by merge commits.

Personally, I think they are an important part of the history of a project, and you shouldn't try to circumvent them in your workflow.

That being said, if you want to look at a project's history in at a glance, you can use this flag to filter out merge commits.

git log --oneline --no-merges

A terminal screenshot showing the output of the above command on the Solidus project

3. Rewrite your last commit message

This one comes in handy when you accidentally commit something with a typo or misleading commit message.

git commit -v --amend

The -v is optional, but I like it because it shows a lot of information about the changes which helps me to write a more descriptive commit message.

4. Get rid of all untracked changes

Pretty self-explanatory, but in case you're not familiar with the idea:

If you create a new file that didn't previously exist in the git history, you've made an untracked change. To start tracking that file, you'd need to commit it to the repo.

A screenshot with examples of untracked files

Sometimes, you change your mind halfway through a commit and really just want to start over without all the changes you've got. Well, git checkout . will get rid of all the tracked changes you've made, but your untracked changes will still be floating around. To remedy that, we've got git clean.

git clean -f -d

5. Print out a cool visualization of your log

This one mostly just makes you look cool. It can be useful, though, to visualize all of your long standing branches.

git log --pretty=oneline --graph --decorate --all

Try it out.

6. Ask git for a changelog

If you're looking for a condensed explanation of what changed, and who changed it, you can ask git to give you something that looks a lot like a changelog.

git shortlog <commit>..HEAD

In this example <commit> should be replaced with the commit you want to target for the beginning of your log. Basically with git shortlog eafbc3c..HEAD you're saying, "Show me what changed between commit eafbc3c and right now."

A screenshot of git shortlog's output

The shortlog is grouped by commit author and shows the first line of each commit message. If your commit messages are well-written this should give you a solid idea of what each commit actually did.

You can do cool tricks like git shortlog HEAD~20.. to get the shortlog for the last 20 commits.

7. View the log for a specific date range

In a similar vein of thinking, you might need to see what changed in a repo between two days.

Thankfully, git has your back. The git log command accepts --since and --until as flags.

So if I wanted to see what happened in Solidus between February 10th, 2016 and February 19th, 2016 I could run:

git log --since='FEB 10 2016' --until='FEB 19 2016'

A screenshot of the Solidus git log between Febrary 10th, 2016 and February 19th, 2016

Now, I can see that Murphy was pretty active in mid February.

8. List all git aliases

Sometimes you might alias a few commands and forget them later, or maybe there are some aliases defined by a shared config you use.

This is a trick I found somewhere, and even though it's not exclusively a feature of git, we are taking advantage of the git config command.

git config -l | grep alias | sed 's/^alias\.//g'

Try it out, see if you have any forgotten aliases!

9. Search for commits that include a keyword

If you know exactly what piece of code you are looking for, or exactly what keyword you need to find changes on, you can search the log by code.

This will give you a list of commits that somehow affected a line of code or text containing your search string.

git log -S"config.menu_items"

In this example, I'll find a list of commits that somehow manipulated the string config.menu_items.

10. Super secret list of git tutorials

Not that secret, but pretty super:

git help -g

Try it out and see. 🤠

git is a powerful tool, and is full of neat tricks. This list is in no way comprehensive or complete, so you should do some exploring and read the git documentation. Feel free to share any cool tricks you find on this thread, I love learning about the tools that help me write code!

If you liked this article, I wrote another one with more tricks: 10 More Git Tricks That You Should Know. 🤖

Discussion (25)

pic
Editor guide
Collapse
foresthoffman profile image
Forest Hoffman

Nice post Jacob!

4. Get rid of all untracked changes

I also like to use git reset --hard HEAD.

5. Print out a cool visualization of your log

My fav git aliases that are always in my ~/.gitconfig:

[alias]
    lg1=log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all
    lg2=log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n''          %C(white)%s%C(reset) %C(dim white)- %an%C(reset)' --all
    lg=!git lg1
Collapse
jacobherrington profile image
Collapse
sqlrob profile image
Robert Myers

git commit -v --amend

Do not do this if you've already pushed that commit. If a push ever requires --force, you need to think long and hard about whether or not it's something you should do.

One thing that I use a lot on Windows is Posh-git. It places branch, commit and index status in the command prompt

Collapse
christhekeele profile image
Christopher Keele

Love this, incorporating some of them into my git tricks!

Building on your alias list command, I have an alias called alias:

alias.alias !alias(){ git config --get-regexp "^alias.${1}"; }; alias

This'll list all aliases matching the provided prefix, or just all aliases with no arguments:

git alias
# ... many lines of cool aliases
git alias alias
# alias.alias !alias(){ git config --get-regexp "^alias.${1}"; }; alias
git alias ancestor
# alias.ancestor merge-base --octopus
git alias c
# alias.current rev-parse --abbrev-ref HEAD
# alias.contains !contains(){ git fetch && git rev-parse ${1:-HEAD} | xargs git branch -r --contains; }; contains
# alias.cherrypick cherry-pick
Collapse
edmistond profile image
edmistond

Nice list! I've been using git consistently for five or six years now and still learn new stuff about it all the time.

Related to #5, this is probably my single most used git alias and quite possibly the most-used CLI command on my system:

[alias]
    lg = log --color --graph --decorate --pretty=format:'%C(bold cyan)%h%Creset -%C(bold red)%d%Creset %s %C(bold green)(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit

I have it zsh aliased to 'ggd' so I can quickly check the history of my current branch and whether certain other branches are in the history (basically seeing at what point I last rebased on master, or if I need to again).

Collapse
ahferroin7 profile image
Austin S. Hemmelgarn • Edited

Two other big ones come to mind that I often see underutilized, but find indispensable on a regular basis:

Interactively stage individual parts of files for a commit

Sometimes, you end up fixing bugs in nearby code that you didn't notice until you were testing something else. Or maybe you end up needing to split out a big bunch of changes into separate logical steps. Or perhaps you just forgot to commit something before starting on the next thing.

In any case, you can use:

git add -i

It will bring up a menu-based interface to step through individual hunks of the current set of changes to the working tree and let you select only the parts you want to include in the commit.

Rearrange (or completely rewrite) the history of a branch

git rebase also has an interactive mode (also enabled with the -i switch). You can use this to edit history back to a certain commit without moving the branch you're on by running:

git rebase -i <commit>

The commit you specify should be the last one before the commit you want to start editing things at. Just like a normal rebase, you can specify a tag or a branch name instead of a commit here (or use a commit-like form like HEAD~8). This will open an editor with a list of instructions to git for how to perform the rebase (it defaults to what it would do if you were doing a regular rebase), as well as some comments below that explaining what each instruction means.

Things you can do with this include:

  • Change the order of commits. Just reorder them in the list that shows up when you start the interactive rebase.
  • Completely remove commits from history. Just remove the lines for those commits from the list, or change the pick at the beginning of the lines to drop.
  • Insert completely new commits. Add a break to the list where you want to put the new commit, the rebase will stop there and drop to a shell, where you can make your code changes and commit them just like normal, then run git rebase --continue to resume the rebase. You can also use this to merge branches or cherry pick commits into arbitrary locations in your history.
  • Amend commits arbitrarily far back in the history. Replace pick with edit, the commit will be applied, but the rebase will stop immediately afterwards so you can use git commit --amend to update the commit. This is essentially a shorter form of adding a break after the commit.
  • Squash commits. Put the line for the commit you want to squash right after the one you want to squash it into, then replace the pick with squash.
  • Meld fixes into the commit they fix. Same as squashing, just use fixup instead of squash and it will just use the commit message from the first commit as-is instead of prompting you to enter a new one for the single commit it produces.
  • Split commits. Mark the commit you want to split to be amended, then run git reset HEAD~ when the rebase stops to let you amend that commit. This will remove that commit from the history, but leave all the changes it made to the working tree in place, and you can then split them out into new commits however you want (possibly using git add -i to simplify the process).
  • Verify the code state between commits. You can add a line starting with exec wherever you want in the sequence of commands. The rebase process will pass the rest of that line to the shell to be run as a command (essentially, it calls sh -c followed by the rest of the line) at that point during the rebase, and will stop the rebase if the command fails (just like if a merge failed) so that you can investigate what happened and either cancel the rebase or continue it as you see fit.
Collapse
ferricoxide profile image
Thomas H Jones II

A few weeks ago, I used #9 to figure out "how long has this feature been in our code before someone made a complaint about it."

Specifically, we do build-automation. Someone requested we include tool in the resultant builds. I'd looked at the system the person was complaining didn't have the component, only to find that it was present ...just that the associated run-time service wasn't activated. Ultimately, found that the tool had been in the build for nearly four years in this "installed but not automatically started" state. Was able to tell the requestor, "good news: the tool has been in place four nearly four years; I just had to update the automation to ensure that it actually starts on boot. The only remediation you need for old systems is to enable the service rather than having to figure out how to deploy and install it."

...All this because the maker of the tool had a botch in their installer that caused the enablement to not actually happen. :p

Collapse
jacobherrington profile image
Jacob Herrington (he/him) Author

Stories like this are the reason I think it makes sense to learn git at a deeper level than branch, add, commit, repeat.

Collapse
ferricoxide profile image
Thomas H Jones II

True, but that could be said for many (most?) tools. That said, a lot of people never have reason to use those advanced features (and, on the rare occasion that they do, if their Google-Fu is half-decent, they'll find what they need that way).

Collapse
thbp profile image
The Half Blood Prince

For #8, you can simply type

git alias

to list all the configured aliases for you.

Collapse
jacobherrington profile image
Jacob Herrington (he/him) Author • Edited

Hmm. Are you sure that's not a plugin or something? It doesn't seem to work for me.

EDIT: Found it! github.com/tj/git-extras/blob/mast...

Collapse
thbp profile image
The Half Blood Prince

Oh yes! This plugin has integrated so seamlessly with my flow that I almost forgot I had it installed.

Collapse
veebuv profile image
Vaibhav Namburi

git reflog is an absolute saviour for me too!

Collapse
adam_cyclones profile image
Adam Crockett

Emergency servival kit: ohshitgit.com/

Collapse
jacobherrington profile image
Jacob Herrington (he/him) Author

That's awesome.

Always an option:

cd ..
sudo rm -r fucking-git-repo-dir
git clone https://some.github.url/fucking-git-repo-dir.git
cd fucking-git-repo-dir
Collapse
christhekeele profile image
Christopher Keele

Or just:

rm -rf .git
git init .
git remote add origin https://some.github.url/fucking-git-repo-dir.git
git fetch

To avoid moving out of the directory, overwriting it, or losing changes

Collapse
hendrikpelk profile image
Henk Pelk

Thank you!

Collapse
rhymes profile image
rhymes

This is great!

Collapse
raulingg profile image
Raul Robinson Quispe Mendez • Edited

I'm disappointed 😞 to not see here one of my favorite git commands

git worktree add <path-to> <branch-name>

ref 👉 git-scm.com/docs/git-worktree

Collapse
nagrass profile image
Nathan Grass

What is the purpose of -- in the first command? Seems an unnecessary argument.

Collapse
jacobherrington profile image
Jacob Herrington (he/him) Author

It is kind of superfluous. It is just a more formal way to indicate that you are specifying the file name. I included it here in case someone took the example and made an alias out of it.

Imagine you had a file called master and you wanted to check that file out, git checkout master would probably do something different than you wanted, but git checkout -- master would check that file out from the current branch.

Collapse
jessekphillips profile image
Jesse Phillips

It signifies the end of command options. It is useful with transferring control to sub commands. In D you can provide compiler options then pass arguments to the compiled program.

Dmd run foo.d -- --verbose

D will compile foo then call

foo --verbose 
Collapse
bantya profile image
Rahul Thakare

Point 7 (about date ranges) was pretty much unknown for me. Thanks for including it.

P.S. all the tips are really very helpful. Thanks again.

Collapse
jacobherrington profile image
Jacob Herrington (he/him) Author

Super glad it's helpful!

Collapse
nickolasbenakis profile image
Nickolas Benakis

great article :D