Posts tagged: git

All posts with the tag "git"

25 posts latest post 2026-05-06
Publishing rhythm
May 2026 | 1 posts

Has no upstream branch errors in git can be such a damn productivity killer. You gotta stop your flow and swap over the branch, there is a config so that you don’t have to do this.

fatal has no upstream branch #

If you have not yet configured git to always push to the current branch, you will get a has no upstream branch error if you don’t explicitly set it.

Let’s show an example

git checkout -b feat/ingest-inventory-data
git add conf/base/catalog.yml
git commit -m "feat: ingest inventory data from abc-db"
git push

You will be presented with the following error.

fatal: The current branch feat/ingest-inventory-data has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin feat/ingest-inventory-data

Option 1: follow the instructions #

To resolve this fatal error your first option is simply to follow the instructions given. Just copy and paste it in.

git push --set-upstream origin feat/ingest-inventory-data

Option 2: push to the current branch without setting upstream #

Honestly, I am pretty aware of the branch I am on, and Very few times have I ever accidentally pushed to the wrong branch. The one that you might have a bigger chance with a more detrimental effect is main, which I will argue you should have blocked to require a passing ci, and potential reviewers to merge in. Therefore you can’t even push to main anyway.

To just push to the branch you are currently on each and every time and never see this error again, you can run this to configure git to always push to your current branch.

git config --global push.default current

Once you give a branch the big D (git branch -D mybranch) its gone, its lost from your history. It’s completely removed from your log. There will be no reference to these commits, or will there?

TLDR #

Checkout is your savior, all you need is the commit hash.

Immediate Regret #

your terminal is still open

We have all done this, you give branch the big D only to realize it was the wrong one. Don’t worry, not all is lost, this is the easiest to recover from. When you run the delete command you will see something like this.

❯ git branch -D new
Deleted branch new (was bc02a64).

Notice the hash is right there is the hash of your commit. You can use that to get your content back.

git checkout -b bc02a64
git branch new

# or in one swoop checkout your new branch at the `start-point` you want
git checkout -b new bc02a64

Delayed reaction #

you have closed your terminal

If you have closed your terminal, or have deleted with a gui or something that does not tell you the hash as you run it, don’t fret, all your work is still there (as long as you have commited). You just have to dig it out. The reflog contains a list of all git operations that have occurred on your git repo, and can be incredibly helpful with this.

Kinda Recent #

If your botched delete operation was recent just diving right into the reflog will show it.

❯ git reflog
03a3338 (main) HEAD@{0}: checkout: moving from new to main
bc02a64 (HEAD -> another, new) HEAD@{4}: commit: newfile
03a3338 (main) HEAD@{2}: checkout: moving from main to new

In this example, I checked out a branch called new, commited a new file, then switched back to main and deleted new.

Now That I have the commit hash I can use the same solution to get my branch back.

git checkout -b bc02a64
git branch new

# or in one swoop checkout your new branch at the `start-point` you want
git checkout -b new bc02a64

A lot has happened since then #

If a lot has happened since then, you are going to need to pull out some more tool to sift through that reflog, especially if its a big one. The first suggestion that I have is to pipe into grep and look for commit messages, or the name of the branch.

❯ git reflog | grep "moving from"
03a3338 HEAD@{1}: checkout: moving from main to branch/oops
03a3338 HEAD@{2}: checkout: moving from oops to main
03a3338 HEAD@{4}: checkout: moving from main to oops
03a3338 HEAD@{5}: checkout: moving from another to main
bc02a64 HEAD@{6}: checkout: moving from main to another
03a3338 HEAD@{7}: checkout: moving from another to main
bc02a64 HEAD@{8}: checkout: moving from new to another
bc02a64 HEAD@{9}: checkout: moving from bc02a64bbe5683d905e333e8dfcbbb91a5e77549 to new
bc02a64 HEAD@{10}: checkout: moving from main to bc02a64bbe56
03a3338 HEAD@{11}: checkout: moving from new to main
03a3338 HEAD@{13}: checkout: moving from main to new
03a3338 HEAD@{14}: checkout: moving from other to main
03a3338 HEAD@{18}: checkout: moving from main to other

git has a built in --grep flag, but I don’t think there is a way to filter by branch name, regardless it still is helpful.

❯ git reflog --grep new
bc02a64 (HEAD -> another, new) HEAD@{4}: commit: newfile

Maybe if you can remember a filename you can pass in -- <filename>.

git reflog -- readme.md

RTFM #

There are many other ways to slice up a git log, and reflog alike. check out man git log for some more flags.

It’s nearly impossible to completely loose a file if it is commited to git. It’s likely harder to fully remove the file than it is to recover it, but how do we go about recovering those precious files that we have lost.

Listing all the deleted files in all of git history can be done by combining git log with --diff-filter. The log gives you lots of options to show different bits of information about the commit that happened at that point. It’s even possible to get a completely clean list of files that are in your git history but have been deleted.

git log –diff-filter #

These various commands will show all files that were ever deleted on your current branch.

# This one includes the date, commit hash, and Author
git log --diff-filter D

# this one could be a git alias, but includes empty lines
git log --diff-filter D --pretty="format:" --name-only

# this one has the empty lines cleaned up
git log --diff-filter D --pretty="format:" --name-only | sed '/^$/d'

git diff-filter

git reflog –diff-filter #

The reflog can be super powerful in finding lost files here, as it only cares about git operations, not just the current branch. It will search accross all branches for deleted files and report them.

# This one includes the commit hash, branch, tag, and commit message
git reflog --diff-filter D

# You might want to at least add the filename
git reflog --diff-filter D --name-only

# this one could be a git alias, but includes empty lines
git reflog --diff-filter D --pretty="format:" --name-only

# this one has the empty lines cleaned up
git reflog --diff-filter D --pretty="format:" --name-only | sed '/^$/d'

get the last commit from a file #

git log -n 1 --pretty=format:%H -- file

If you want dont like how the output looks or you want your default pager to be different you can configure the default pager see Set Your Git Pager Config.

Git commands such as diff, log, whatchanged all take a flag called --diff-filter. This can filter for only certain types of diffs, such as added (A), modified (M), or deleted (D).

Man page #

You can find the full description by searching for --diff-filter in the man git diff page.

--diff-filter=[(A|C|D|M|R|T|U|X|B)...[*]]
    Select only files that are Added (A), Copied (C), Deleted (D), Modified (M), Renamed (R), have their type (i.e. regular file, symlink, submodule, ...)
    changed (T), are Unmerged (U), are Unknown (X), or have had their pairing Broken (B). Any combination of the filter characters (including none) can be used.
    When * (All-or-none) is added to the combination, all paths are selected if there is any file that matches other criteria in the comparison; if there is no
    file that matches other criteria, nothing is selected.

    Also, these upper-case letters can be downcased to exclude. E.g.  --diff-filter=ad excludes added and deleted paths.

    Note that not all diffs can feature all types. For instance, diffs from the index to the working tree can never have Added entries (because the set of paths
    included in the diff is limited by what is in the index). Similarly, copied and renamed entries cannot appear if detection for those types is disabled.

Try it out #

Open up a git repo and play around with this, here are some example that I played with that seemed useful to me.

# find when any files were deleted
git log --diff-filter D

# find when all files were added
git log --diff-filter A

# only one specific file
git log --diff-filter A -- readme.md

# partial match to a single file
git log --diff-filter A -- read*

# Find when all python files were added
git log --diff-filter A -- *.py

Git has a built in way to rebase all the way back to the beginning of time. There is no need to scroll through the log to find the first hash, or find the total number of commits. Just use --root.

git rebase --root

Git reflog can perform some serious magic in reviving your hard work from the dead if you happen to loose it.

caveat #

You must git commit! If you never commit the file, git cannot help you. You might look into your trashcan, filesystem versions, onedrive, box, dropbox. If you have none of this, then you are probably hosed.

practice #

I really like to practice these techniques before I need to use them so that I understand how they work in a low stakes fashion. This helps me understand what I can and cannot do, and how to do it in a place that does not matter in any way at all.

This is what I did to revive a dropped docker-compose.yml file. The idea is that if I can find the commit hash, I can cherry-pick it.

git init
touch readme.md
git add readme.md
git commit -m "add readme"
touch docker-compose.yml
git add docker-compose.yml
git commit -m "add docker-compose"
git reset 3cfc --hard
git reflog
# copy the hash of the commit with my docker-compose commit
git cherry-pick fd74df3

reflog #

Here was the final reflog that shows all of my git actions. note I did reset twice.

❯ git reflog --name-only
0404b6a (HEAD -> main) HEAD@{0}: cherry-pick: add docker-compose
docker-compose.yml
3cfcab9 HEAD@{1}: reset: moving to 3cfc
readme.md
9175695 HEAD@{2}: cherry-pick: add docker-compose
docker-compose.yml
3cfcab9 HEAD@{3}: reset: moving to 3cfc
readme.md
fd74df3 HEAD@{4}: commit: add docker-compose
docker-compose.yml
3cfcab9 HEAD@{5}: reset: moving to HEAD
readme.md
3cfcab9 HEAD@{6}: commit (initial): add readme
readme.md

Right inside the git docs, is states that the git reflog command runs git reflog show by default which is an alias for git log -g --abbrev-commit --pretty=oneline

This epiphany deepens my understanding of git, and lets me understand that most git log flags might also work with git log -g.

full or short format #

Here are some git commands for you to try out on your own that are all pretty similar, but vary in how much information they show.

# These show only first line of the commit message subject, the hash, and index
git reflog
git log -g --abbrev-commit --pretty=oneline

# similar to git log, this is a fully featured log with author, date, and full
# commit message
git log -g

add files #

If I am looking for a missing file, I might want to leverage --name-only or --stat, to see where I might have hard reset that file, or deleted it.

git reflog --stat
git log -g --stat --abbrev-commit --pretty=oneline

git reflog --name-only
git log -g --name-only --abbrev-commit --pretty=oneline

example #

Here is an example where I lost my docker-compose.yml file in a git reset, and got it back by finding the commit hash with git reflog and cherry picked it back.

❯ git reflog --name-only
0404b6a (HEAD -> main) HEAD@{0}: cherry-pick: add docker-compose
docker-compose.yml
3cfcab9 HEAD@{1}: reset: moving to 3cfc
readme.md
9175695 HEAD@{2}: cherry-pick: add docker-compose
docker-compose.yml
3cfcab9 HEAD@{3}: reset: moving to 3cfc
readme.md
fd74df3 HEAD@{4}: commit: add docker-compose
docker-compose.yml
3cfcab9 HEAD@{5}: reset: moving to HEAD
readme.md
3cfcab9 HEAD@{6}: commit (initial): add readme
readme.md

This just proves that its harder to remove something from git, than it is to get it back. It can feel impossible to get something back, but once its in, it feels even more impossible to get it out.

I’ve never found a great use for a global .gitignore file. Mostly I fear that by adding a lot of the common things like .pyc files it will be missing from the project and inevitably be committed to the project by someone else.

Personal Tools #

Within the past year I have added some tools to my personal setup that are not required to run the project, but works really well with my setup. They are direnv and pyflyby. Since these both support project level configuration, are less common, and not in most .gitignore templates they make for great candidates to add to a global .gitignore file.

create the config #

Like any .gitignore it supports gits wildignore syntax. I made a ~/dotfiles/git/.global_gitignore file, and added the following to it.

.envrc
.pyflyby
.copier-defaults
.venv*/
.python-version
markout
.markata.cache

Once I had this file, I stowed it into ~/.global_gitignore.

cd ~/dotfiles/
stow git

Always stow your dotfiles, don’t set yourself up for wondering why your next machine is not working right.

stow note #

Note, the reason that it is a ~/.global_gitignore and not a ~/.gitignore is that I was unable to stow a .gitignore file. They must be ignored by default, and I was unable to figure out how to turn it back on.

set the config #

Next run this command to add the ~/.global_gitignore to your gitignore as a global excludesfile.

git config --global core.excludesfile ~/.global_gitignore

commit it #

Once you have done this you should have both your ~/dotfiles/git/.gitconfig and ~/dotfiles/.global_gitignore ready to commit.

cd ~/dotfiles

git add git/.global_gitignore
git add git/.gitconfig

git commit -m "add global_gitignore"

You didn’t stow your .gitconfig #

the shame!

No worries, lets get it into your dotfiles repo and stow it.

cd ~/dotfiles

# if you dont have a git directory make it.
mkdir git
mv ~/.gitconfig ~/devtainer/git
# now use stow to symlink it back to where it was
# so git works as expected.
stow git

You dont have a dotfiles directory #

double shame 😲

If you dont already have a dotfiles directry you should. It is important for it to be in your home directory for stow to work properly, if you really don’t want it there, look up how to configure stow to account for this.

# make a dotfiles directory and go there
mkdir ~/dotfiles
cd ~/dotfiles

# make it a git repo
git init

# if you dont have a git directory make it.

mkdir git
mv ~/.gitconfig ~/devtainer/git
# now use stow to symlink it back to where it was
# so git works as expected.
stow git

Fugitive comes with a pretty sick way to commit files and see the diff at the same time with verbose commit. Opening the fugitive menu with :G brings up your git status, you can stage files with s, unstage them with u, toggle them with -, and toggle their diff with >. Once you have staged your files for commit, you can commit with cc, but today I found that you can commit verbose with cvc. This brings up not only a commit widow with your git status shown, but the diff that you are about to commit.

fugitive verbose commit example

example of a verbose commit in fugitive

Code Review from the comfort of vim | Diffurcate

I often review Pull requests from the browser as it just makes it so easy to see the diffs and navigate through them, but there comes a time when the diffs get really big and hard to follow. That’s when its time to bring in the comforts of vim. https://youtu.be/5NKaZFavM0E Plugins needed # [1] This all stems from the great plugin by AndrewRadev [2]. It breaks a down into a project. So rather than poping into a pager from git [3] diff, you can pipe to diffurcate and it will setup a project in a tmp directory for you and you can browse this project just like any other except it’s just a diff. Plug 'AndrewRadev/diffurcate.vim' My aliases # [4] First to quickly checkout PR’s from azure devops I have setup an alias to fuzzy select a pr and let the az command do the checkout. alias azcheckout='az repos pr checkout --id $(az repos pr list --output table | tail -n -2 | fzf | cut -d " " -f1)' Next I have a few aliases setup for checking diffs. The first one checks what is staged vs the...

Git in Depth Notes

These are my notes from taking @nnja’s FEM course git-in-depth [1]. requirements # [2] - git --version > than 2.0 creating a git # [4] echo "hello" | git hash-object --stdin References: [1]: https://frontendmasters.com/courses/git-in-depth/ [2]: #requirements [3]: /glossary/git/ [4]: #creating-a-git
1 min read

How I configure git

Git [1] can be a bit tricky to get configured correctly. I often stumble into config issues weeks after setting up a new machine that I did not even notice. These are my notes to remind me how I configure git. Identity # [2] git config --global user.name "John Doe" git config --global user.email [email protected] rebase # [3] editor # [4] git config --global core.editor nvim default branch # [5] git config --global init.defaultBranch main push to current bransh wihtout setting upstream # [6] git config --global push.default current Autostash # [7] git config pull.rebase true git config rebase.autoStash true References: [1]: /glossary/git/ [2]: #identity [3]: #rebase [4]: #editor [5]: #default-branch [6]: #push-to-current-bransh-wihtout-setting-upstream [7]: #autostash

How to use git cherry pick

~/git via 🐍 v3.8.5 ❯ mkdir git-cherry-pick-learn ~/git via 🐍 v3.8.5 ❯ cd git-cherry-pick-learn ~/git/git-cherry-pick-learn ❯ git init Initialized empty Git repository in /home/walkews/git/git-cherry-pick-learn/.git/ git-cherry-pick-learn on  main ❯ touch readme.md git-cherry-pick-learn on  main [?] ❯ git status On branch main No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) readme.md nothing added to commit but untracked files present (use "git add" to track) git-cherry-pick-learn on  main [?] ❯ git add . git-cherry-pick-learn on  main [+] ❯ git commit -m "init readme" [main (root-commit) ebd1ff2] init readme 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 readme.md git-cherry-pick-learn on  main ❯ echo "Learn Cherry Pick" Learn Cherry Pick git-cherry-pick-learn on  main ❯ git add . git-cherry-pick-learn on  main ❯ git commit -m "add title git-cherry-pick-learn on  main ❯ echo "# Learn Cherry P...

Trim unused git branches

Trim branches no longer on origin # [1] git remote prune origin --dry-run git remote prune origin Find branches already merged # [2] git checkout main # list remote branches that have already been merged into main git branch -r --merged # list local branches that have already been merged into main git branch --merged References: [1]: #trim-branches-no-longer-on-origin [2]: #find-branches-already-merged

Gitui is a blazing fast terminal git interface

Gitui is a terminal-based git [1] user interface (TUI) that will change the way that you work with git. I have been a long-time user of the git cli, and it’s been hard to beat, mostly because there is nothing that keeps my fingers on the keyboard quite like it, except gitui which comes with some great ways to very quickly walk through a git project. installation # [2] Go to their [releases]https://github.com/extrawurst/gitui/releases) page, download the latest build, and pop it on your PATH. I have the following stuffed away in some install scripts to get the latest version. install latest release GITUI_VERSION=$(curl --silent https://github.com/extrawurst/gitui/releases/latest | tr -d '"' | sed 's/^.*tag\///g' | sed 's/>.*$//g' | sed 's/^v//') wget https://github.com/extrawurst/gitui/releases/download/v${GITUI_VERSION}/gitui-linux-musl.tar.gz -O- -q | sudo tar -zxf - -C /usr/bin/ run gitui # [3] It opens blazing fast. gitui Quick Commits # [4] Sometimes I edit a number of fi...
2 min read

Fix git commit author

I was 20 commits into a hackoberfest PR when I suddenly realized they they all had my work email on them instead of my personal email 😱. This is the story of how I corrected my email address on 19 individual commits after already submitting for a PR. - Change the email for this repo [1] - Prepare for rebasing [2] - start the rebase [3] - 🛠 Fix First wrong Commit [4] - Fix all commits [5] - Done [6] - ReCap [7] Change the email for this repo # [1] stop the bleeding Before anything else set the email correctly! cd kedro git config user.name "Waylon Walker" git config user.email [email protected] Prepare for rebasing # [2] First thing is to find how many commits back this mistake goes. I opened up the git [8] log, and saw mine went back 19 commits. I rolled back 20 just to be sure. $ git log ... commit a355926b9d7ec4c05659adaa254beefbdb036332 Author: WaylonWalker <[email protected]> Date: Sat Oct 17 10:28:59 2020 -0500 give name of function inside incorrect parameters erro...
3 min read

List the latest files to change in a git repo

while read file; do echo $(git log --pretty=format:%ad -n 1 --date=raw -- $file) $file; done < <(git ls-tree -r --name-only HEAD | grep static/stories) | sort -r | head -n 3 | cut -d " " -f 3

Strip Trailing Whitespace from Git projects

A common linting error thrown by various linters is for trailing whitespace. I most often use flake8. I generally have [pre-commit](https://waylonwalker.com/pre-commit-is-awesome hooks setup to strip this, but sometimes I run into situations where I jump into a project without it, and my editor lights up with errors. A simple fix is to run this one-liner. One-Liner to strip whitespace # [1] bash git grep -I --name-only -z -e '' | xargs -0 sed -i -e 's/[ \t]\+\(\r\?\)$/\1/' pre-commit is awesome I recently discovered the ✨ awesomeness that is pre-commit. I steered away from it for so long because it seemed like a big daunting thing to set up, but... Jun 5, 2020 [2] References: [1]: #one-liner-to-strip-whitespace [2]: /pre-commit-is-awesome/