This is Billy.
He is a trainee developer working for an important company.
Unfortunately for the company, today Billy has woken up and chosen violence.
This is because he finds learning Git very troublesome, boring and convoluted.
Branches, commits, checkouts, HEADs, features, staging, oh my!
It was just too much for him.
But then Billy had the absolutely best worst idea.
"What if I learn git by first learning what NOT to do, but then doing it anyway!"
This would accomplish three things:
- He would learn git's most common tools by giving himself fun little challenges that would normally be prohibited.
- If he learned what not to do, he could then later focus on what he should do.
- Learning this way satisfies his chaotic evil tendencies.
This was sounding more and more like a good idea to Billy. This would definitely make him a better developer.
There was just one problem though...
Git is a version control system used by developers to manage source code for software.
Whenever something is changed, added or even deleted by somebody, Git registers who did what.
If you wanted to do very bad things in, let's say, a repository owned by the company you work at, somebody could always trace it back to you.
And that would DEFINETELY get you fired.
So that's why Billy stole Trent's computer.
Trent is a big dumb-dumb who left his computer on and unprotected when he went to the bathroom.
He also ate the last slice of pizza at last month's after party without asking first.
With Trent's computer, Billy now has access to the same repositories, but under Trent's login credentials.
And so now Billy can learn Git by first learning everything he shouldn't do, like:
1. Pushing code to somebody else's branch with --force
Let's say the current git ecosystem looks like this:
Billy is currently checked out on an earlier version of another co-worker's branch.
This can happen if 2 people are checked out on the same branch, but one of them starts pushing changes to the remote.
This means that Billy is behind on the branch. If he wanted to get the latest changes of the branch he currently is on, he would write into the terminal git pull
Fun fact:
Git pull is actually the combination of 2 other git commands.
-
git fetch origin
(which fetches the latest changes of remote WITHOUT checking out or merging it) -
git merge origin/<branch name>
(which does merge your local changes to the remote. Since normally you wouldn't have local files to merge, git does what's called a "fast-forward" and you end up with the latest changes)
Billy wonders what would happen if he tried pushing on this person's branch, even if he's behind on the latest changes.
Normally if he tried to git push
some code, the attempt would fail with an error - Asking him to pull the latest changes first.
(This is a safety net that's built into git to avoid having work be lost. You can't push unless you pull first!)
But Billy can avoid this if he did a git push --force
command.
git push --force
is usually a big nono for developers. It's a command that forcibly overwrites the git history and lets you push your local changes despite it possibly deleting other people's work on the same branch.
Billy has never used it before and is a curious boy, so he did what any curious person would do.
He created a new file with special text inside:
echo "Trent was here" > Trent.txt
He committed his very important changes to the branch
git add Trent.txt
git commit -m "Trent committed this"
Which makes git look like this:
- He finishes by force-pushing his new changes to the branch
git push --force
After a few seconds...
Poof!
Billy's very important changes are now in Git!
And at the same time, all of his co-worker's work completely disappears!
If only Billy had used git push --force-with-lease
!
--force-with-lease
is the safer version of the --force
command. It would have prevented Billy from ever overwriting his coworker's work on the remote.
Oh well! What's done is done. And in the end, having fun is what matters.
Billy wonders how long he has before that co-worker notices.
He also wonders if there's something that's even worst he can do. Maybe... he can do something similar in Production?
A light bulb goes off in Billy's head! What if he did a:
2. Hard Reset on the production branch
Git reset
is a command that, similar to what Billy did to his co-worker, undo's the changes that have been created in a branch to a previous commit.
Unlike the git push --force
command he learned earlier, there's nothing to push. It just rewrites the git history by (usually) uncommitting everything done up to a certain commit.
There's 3 modes to the git reset command.
git reset --soft
which moves HEAD (Head being the commit you're currently checked out on) back to the specified commit, while also undoing all the changes made. All those changes go back to staged and allow you to commit again.git reset --mixed
does the same asgit reset --soft
, but leaves all the changes you've made unstaged. This is also the default mode for the git reset command. So if you writegit reset
, that would be the same as doinggit reset --mixed
.git reset --hard
is diabolical. Instead of undoing the changes and leaving them in staged / unstaged... it just discards them.
Poof
All those changes disappear when you hard reset to an old commit.
So if Billy were to say... hard reset to a commit that's from, oh I don't know, 6 months ago, that would be very bad for the company.
Billy smiles as he opens up the terminal.
Remembering what the git ecosystem looks like:
Billy happily starts:
Typing into the terminal
git checkout main
which positions him on the latest commit of that branch.Uses a git visual tool to find a commit from the main branch that's 6 months old.
Types
git reset --hard <commit hash>
to erase all the changes in the main branch from the last 6 months locally.Finishes off by using our good friend
git push --force
to permanently leave his local destructive changes in the remote.
After a few seconds...
Poof!
Billy successfully made some company executives very very angry!
But wait a minute...
It's weird that Billy was able to do that, wasn't it? After all...
Permissions are usually set in repositories so that nobody can push to the production branch directly.
This prevents accidental pushing or rewriting of git history that affects the website directly.
That's why something called a "Pull Request" (or a PR) exists.
A pull request is a proposal to merge a set of changes from one branch into another.
And normally, other tech savvy developers first have to accept your changes before you can merge anyway.
But what happens if those permissions are never set?
Well, seems like anybody like Billy can come and erase everybody's hard work from the last 2 quarters in a second.
He calculates he has maybe 10... no, 15 minutes before his changes to main impact in production.
So Billy has to act fast. He has time for maybe one more chaotic evil thing before somebody named Trent gets in trouble.
What to do...
OH, Billy knows! He should:
3. Expose the project's secrets and push it to production!
Billy can easily do this by modifying the .gitignore file.
.gitignore is a special type of file located in your project's directory. As the name implies, it specifies which files Git should ignore and avoid letting you stage.
This is super helpful when you have particular files you never want to upload to your repository in the first place.
One of those files you normally want to avoid uploading are your .env files.
.env files tend to be used in projects to hold environment variables you would use throughout your solution.
They feature key/value pairs with information you really really don't want to upload. Things such as API keys, database URIs, AUTH secrets and more.
But Billy doesn't believe in keeping secrets to oneself.
He is the hero of his story and must let the people know!
So if we assume our .gitignore files looks like this:
# Javascript node_modules
node_modules/
# API keys
.env
Then all Billy has to do is:
Locate the .gitignore file
-
Use his favorite IDE or terminal text editor to remove the .env line from the file and save it. (Which makes the file now look like this)
# Javascript node_modules node_modules/ # API keys are gone from ignore oops
Add the now changed .gitignore file to staged. Billy does this by typing
git add .gitignore
into the terminalOh wait! Don't forget - Since Git now isn't ignoring the .env file, Billy has to add that too!
git add .env
is typed into the terminal as well.-
Time for committing! Billy does this with this line:
git commit -m "FREEEEEEDOOOOOMMM!!!! #TrentWasHere"
Final step! Time to push to the main branch. And since again, for some reason there isn't any permissions set that would stop Billy, he can write
git push --force
into the terminal.
After a few seconds...
Poof!
"Liberté, égalité, fraternité" whispers Billy with glee! Right as he leaves Trent's computer where it belongs.
Good thing too, as he just heard the sound of a toilet flush from a room away.
Billy runs back to his desk, making it just in time before anybody notices.
Phew
Seems like Trent finally came back from his bathroom break and sat down at his desk.
But right before he could even open his laptop, Trent's phone started ringing.
Ring ring, ring ring
Billy waited in anticipation as he saw Trent slowly pick up his phone.
"Hello?"
"TRENT. OFFICE. NOW."
Billy could hear the yelling from Trent's phone from 5 desk's away.
"WOAH what happened boss?"
...
"I didn't do anything lik-"
...
"What do you something happened to production?
...
"I'm on my way"
And as Trent quickly runs to the office, probably for the screaming of his lifetime.
Billy sits back, relaxes and can finally say:
"I'm a better developer today".
This was the dumbest article I have ever written.
If you like my sense of humor then I think you'll like my newsletter "Exceptional Frontend" - the most entertaining front-end newsletter on the web.
It's for any devs out there that want a front-end centered newsletter that tries to be different from everybody else. Big focus was put into making it unique and most importantly — FUN!
While also at the same time helping devs become exceptional at what they do.
Top comments (93)
Poor Trent should have been familiar with
git reflog
:) Nice article!Thank you so much!
Git reflog would have definetely saved the day here
This article is genius, sucked me in with entertainment and actually got me to read something and become informed, thankyou!
Let's goooo, glad it helped and you enjoyed it!
Adding lessons in with a sense of humor, I love it. 😎
Thanks Kenneth! The world needs fun git tutorials hahaha
It most definitely does my friend 🙏
That was great, thanks for the laugh! Well written.
Thanks for the kind comment!
That's why everybody and their dog should not use plain
--force
because it may be destructive.Instead always use
git push --force-with-lease
(!)Found this little gem while watching "So You Think You Know Git - FOSDEM 2024" on YouTube, around 17:42. There is even part 2 of this video 😊
See:
Out of curiosity, is there a reason why they aren't flipped? Like why it wasn't designed with force being force with lease by default and the current force being a force without lease command?
So after a few google searches, I honestly couldn't find the answer! So I just went into the Git repository and went down to a tag made 20 years ago.
Here's the git push documentation from that moment:
github.com/git/git/blob/v0.99/Docu...
My guess is git push was made first and then somewhere down the line the option to push with lease was added later.
Thank you for mentioning it! The resources you posted are also excellent so it adds a lot.
Thanks to you and another commenter, I decided to at least mention it in the article.
If a previous commit is stable and you messed up things a lot (not necessary due to wrong git usage) then
git reset --hard <commit hash>
may be exactly what you needOh absolutely! There's a use case and everything for git reset. Especially when you know what you're doing. Git is super powerful for that reason.
Git push - -force also has its uses. It's necessary for doing a clean rebase or when you want to cleanly amend a commit.
It's all about responsible use.
This article was about irresponsible use 😅
The article is great! Just wanted to leave this one to make people curious. I also love converting on these kind of articles, finding gems I never heard/read before 👍
Congrats for this article. Pretty fun. In real conditions, sign your commits + protect main branches against forced push.
Pull requests can also mitigate those issues (which is mentioned in the post)
Creative way to provide some serious DON'Ts and why
Thank you for the kindness!
The best way to learn, doing wrong things.
There's no better way to learn 😄
Great article.
For reference, when a .env file is pushed to public GitHub, it is 54% likely to expose a secret (and it happens a lot..)
source: The State of Secrets Sprawl 2024
Awesome stat! Really liked the phrase:
“Compromised credentials are a gift that keeps on giving
(your stuff away)”
Some comments may only be visible to logged-in visitors. Sign in to view all comments.