So far, we have been looking at the ideal scenario: you write code, make commits, create a Pull Request. But in real life, things often go wrong: you forgot to add a file, made a typo in the commit message, or realized you went in the wrong direction. In this lecture, we will go over how to solve the most common problems.
1. Rollback — rolling back local changes before a commit
Scenario: you changed a file, but realized all your edits are wrong and want to quickly return the file to the state it was in when it was saved in the last commit.
Use the Rollback function:
- Open the
Committab on the left panel. - Find the modified file in the list (or select multiple files) that you want to “roll back.”
- Right-click it and select
Rollback(or click the rounded arrow icon).
The IDE will warn you that unsaved changes will be lost. Confirm, and the file will instantly return to its last “clean” version from Git. This is the simplest and safest way to discard local experiments.
2. “Amend” magic: how to add to the last commit
Scenario: you just clicked Commit and immediately realized you forgot to include an important file (for example, an updated test) or made a silly typo in the commit message. Creating a new commit with a message like “Added the forgotten file” is bad form.
Instead, use the Amend function (add/fix):
- In the
Commitwindow, apply the needed fixes to the forgotten file and check it. - In the Commit Message panel, find the Amend checkbox (or the gear icon -> Amend Commit) and enable it.
- The IDE will pull in the message from your previous commit. You can edit it.
- Click
Commit.
Instead of creating a new “junk” commit, Git will neatly “open up” your previous commit, add the new files, update the message, and seal it back. Your history will stay perfectly clean!
Read more about this feature in the official JetBrains tutorial.
3. Undo Commit
Scenario: you made a commit but have not pushed it via Push yet, and then realized you rushed it. You want the commit to disappear from history, but you want the actual code changes to remain in your editor so you can keep working.
Use the Undo Commit function:
- Open the history: the
Git -> Logtab at the bottom. - Right-click the topmost (your latest) commit.
- Select
Undo Commit.
Select the changelist (usually Default Changelist) and click OK. The commit will disappear from history, and all your files will return to the Commit window with the “modified” status. You didn’t lose anything and can safely continue writing code.
4. Reset — deleting local history (Caution!)
The Reset Current Branch to Here... function is a powerful tool that moves your branch pointer to any selected commit. Depending on the situation, this button can either gently undo recent actions or radically wipe out broken code.
Scenario A: Undoing recent local commits
You made several commits locally (before Push) and realized that the whole development direction was wrong.
- Open the
Git -> Logtab. - Find the last “good” commit you want to return to (the one that was before the series of mistaken ones).
- Right-click it and select
Reset Current Branch to Here....
Scenario B: Resetting to the server state
You experimented locally, got tangled in conflicts, and the project no longer builds. You want to say: “Git, delete everything I did here and make my branch as clean as it currently is on the GitHub server.”
- Open
Git -> Log. - Find the remote branch marker in the list. Usually it’s a commit with a purple label
origin/main(ororigin/your_branch_name). This is the latest known state of the code on the server. - Right-click that commit and select
Reset Current Branch to Here....
Reset modes
In both scenarios, the IDE will ask you to choose a reset mode. For beginners, it’s important to know two of them:
- Mixed / Soft: Safe mode. Commits will be removed from history, but all file changes will remain in your editor (like a big
Undo). Perfect for Scenario A if you want to rebuild commits from scratch. - Hard: Radical mode. Permanently deletes both commits and all uncommitted code changes. Your project will fully roll back to the selected commit state. This mode is exactly what you need for Scenario B so your local branch becomes an exact copy of the server. Use it only if you are 100% sure you will never need the locally written code again.
5. What to do after Push?
Scenario: you did a push and only then noticed a mistake in it.
Once a commit reaches the remote server, it becomes part of the shared project history. Trying to rewrite that history (for example, via Reset or Amend) can create huge problems for your teammates. Imagine they already pulled your changes and started working based on them.
The correct and safe solution:
Just make a new commit that fixes the mistake and push it with a normal push. This is absolutely standard practice. If the bug is related to functionality or code logic, it’s better to write the fix explicitly. A linear history where broken code is followed by a clear fix commit makes the project’s evolution transparent for the whole team.
The IDE also has a special function Revert Commit. If you right-click the mistaken commit in Git -> Log and select Revert Commit, the IDE will automatically create a new “anti-commit” that does exactly the opposite of what the mistaken one did (for example, removes added lines).
When should you use Revert?
Even though the button looks magical, you should use it carefully for complex code. When commit history turns into a chain like “Commit -> Commit -> Revert -> Commit”, your teammates doing code review can literally “break their brains” trying to understand what ends up working and what was canceled.
But Revert Commit works perfectly for isolated text changes. For example, marketing asked you to update the text on the homepage. You made a commit and pushed it, but an hour later the business changed its mind and asked to revert to the old text. In that case, one-click Revert will restore everything as it was, saving you time and leaving a completely clear trace in history.
6. Stash / Shelve: how to temporarily hide changes
Scenario: you are actively coding a new feature; all your files are red and blue. Suddenly your team lead runs in: “Drop everything, we urgently need to fix a bug on the main branch!” You can’t switch to main because you have unfinished conflicting changes. And you also can’t commit half-written code.
The solution is to temporarily “hide” your work in a virtual box. In Git this is called Stash, and IntelliJ IDEA has its own improved counterpart — Shelve (put on a shelf).
- Open the
Commitwindow and select the modified files. - Right-click and choose
Shelve Changes...(or Git -> Stash Changes). - Enter a meaningful name for this “bookmark” and click Shelve.
Your files will instantly return to their original state. Now you can safely switch to main, fix the bug, commit, and push it. When the urgent task is done, return to your working branch, open the Shelf tab, right-click your hidden changes, and select Unshelve. Your unfinished code will return to its place, and you can continue working!
7. Looking into the past: advanced work with the log
We already know the Git -> Log window, but it’s not just a list of commits — it’s a powerful investigation tool.
- Commit hash: you’ve probably noticed in the log a string of letters and numbers, for example
a1b2c3d. This hash is a unique identifier for each “snapshot” of your code.![Commit hash in logs]()
- You can filter history by branch, author, date, or even by a word in the commit message. This helps you quickly find who worked on a specific part of functionality and when.
- Want to find the commit where a specific line of code was added or removed? In the
Logwindow, there is a search field that looks not only through messages but also through the contents of the changes themselves. - Right-click in the code editor gutter (where the line numbers are) and select
Annotate with Git Blame. The IDE will show next to each line who last changed it and in which commit. This is incredibly useful for understanding why the code is written the way it is.
8. Danger zone: Force Push
So, we established that changing pushed commits is a bad idea. But what if you did rewrite your local history (for example, using Undo Commit or Amend), while the server still has the old version? When you try to do a normal push, Git will throw an error, protecting shared history from being overwritten.
For those cases, there is an option — force push. This command tells the server: “Forget everything you had. My local version of history is the only correct one. Replace your history with mine.”
WARNING! In 99% of cases, using force push in team projects is a disaster. It can permanently delete commits your teammates have already pulled and are working on.
When you absolutely MUST NOT use force push:
- On any shared branches:
main,develop,master. Never. - On any branch that someone else besides you is working with.
The only acceptable scenario:
You work in your personal feature branch that no one has seen or pulled yet. You made several “messy” commits, pushed them to the server, and then decided to squash them or fix a typo via Amend. In that case, you can force push to update your branch on the server before creating a Pull Request.
In the IDE, this option is usually hidden in the dropdown menu of the Push button. The IDE developers did this intentionally so you don’t click it accidentally.
If you’re not 100% sure what you’re doing, never use force push. It’s safer to create a new commit with fixes (or use Revert).
9. Updating the project
We already talked about this in the second lecture, but this topic is so important for teamwork that we are making it a separate rule.
Always click Update Project before starting work on a new task and every morning at the start of your workday. This will save you from many future merge conflicts and ensure you start working with the most up-to-date version of the code by pulling all the latest commits from your teammates from the server.
10. Git Log context menu
When you open the Git -> Log tab and right-click any commit, a huge context menu appears. Let’s go over the most useful commands, grouping them by tasks.
Group 1: Viewing and analysis (safe actions)
Copy Revision Number— copies the unique commit hash to the clipboard. Useful if you need to send a link to a commit to a teammate in chat or reference it in a task (Jira/YouTrack).Compare with Local— lets you compare the state of any file from that old commit with what you currently have unsaved in the editor (in the working directory). A great way to answer: “What did this code look like a month ago?”Show Repository at Revision— opens a window where you can view the structure of all project files exactly as they were at the moment of that commit (while your current working branch does not change).
Group 2: Working with branches
New Branch...— creates a new branch directly from the selected commit in the past. Essential if you moved far forward, realized you broke everything, and want to start a new branch from the last “working” point.Checkout Revision— switches your local project to that commit. You will end up in a “detached HEAD” state. This means you can run the old code and explore it, but if you start making new commits, they won’t be attached to any branch. To return to normal work, simply check out your usual branch (for example,main).
Group 3: Changing and moving
Cherry-Pick(the cherry on top) — an incredibly useful command! It lets you “pull out” one specific commit from someone else’s (or your other) branch and copy it into your current branch. For example, someone fixed a bug in another branch that blocks your work — you just cherry-pick that commit into your branch without merging the entire branch.Edit Commit Message...— if the commit hasn’t been pushed to the server yet (no Push), you can quickly rename its message. Much more convenient than Amend if you only need to change the text.
Group 4: Undoing changes
We covered these commands in detail in the previous sections:
Reset Current Branch to Here...— moves your current branch to this commit (deleting the history ahead).Revert Commit— creates an “anti-commit” for a safe undo on the server.Undo Commit...— unpacks a local commit back into the changes window.
Group 5: Rewriting history
The menu also includes commands like Squash Commits... (combine several commits into one), Drop Commits (delete a commit from the middle of history), or Interactively Rebase from Here.... These are tools for powerful surgical work on local history (Interactive Rebase).
While you are learning, try not to use these commands, because they completely rewrite history. In real team work, they are used to “tidy up” your draft local branch before sending it for review (creating a Pull Request).
11. Final words
Version control systems may seem complicated: branches, merges, conflicts, scary commit hashes. But remember the main thing: Git is your safety net. Every senior developer once panic-googled how to exit “detached HEAD” or how to undo an accidental push. Don’t be afraid to experiment. Making mistakes locally is absolutely normal, because now you have the full arsenal of IntelliJ IDEA tools to undo, roll back, and fix any slip-up before the team sees it. Commit often, write clear messages, always run Update Project in the morning, and you’ll become a reliable and professional contributor to any project.
12. Bonus practical task
We suggest you complete a small but very tricky task. This problem is encountered by 90% of developers when they start working on multiple projects.
Your task:
- Register a second GitHub account with a different email address.
- Add this new account in IntelliJ IDEA.
- Create a new repository, clone it, write a couple lines of code, and do a
CommitandPush, selecting your new account. - Now go to the
git-logpage and carefully look at the commit history.
What’s the catch?
Most likely, you will be surprised to find that the commit author is your first (main) account, even though you definitely pushed as the new profile!
Investigation:
Your goal is to figure out why that happened. Hint: GitHub website authentication (to have Push permissions) and the author signature inside the commit itself are not the same thing. Find a way (via the terminal?) to switch user.name and user.email so they apply only to this specific project, without breaking your main account settings. Make a new commit and ensure the history shows a commit from account #2.

GO TO FULL VERSION