Skip to content
GitTree

Lesson 28 · Recovery

Revert a change in shared history

Undo a bad change that others already have by adding a new commit that cancels it, instead of deleting history, and publish the fix with a normal push.

Progress saved in this browser.

Playing loads the video from YouTube (privacy-enhanced mode).

English narration · English and Arabic captions

Open the video pageWatch on YouTube

What you will learn

  • Explain revert as an inverse commit, and why it suits history others have.
  • Predict the new commit's parent and the file it leaves.
  • Handle a revert that conflicts, and know why reverting a merge asks for a parent.
  • Publish the fix without force.

Before you start

You are on shared/dark-regression, a branch from lesson 18's result M. Its last commit, "style: introduce the rehearsal contrast regression", adds one rule to styles.css, body { color: #f7f8fa; }, which turns the page text the same pale colour as the background. That commit is already on a local server you set up for this lesson, so a teammate may have it. You want the text readable again without rewriting anything that others already have.

Everything is local and on the Free plan (GitTree asks you to sign in): no hosting account, no provider, no internet. The server is a local bare repository, not GitHub. Every identity is fictional (example.com). Never run this lesson in a real project.

Make the changes in GitTree or with the native Git commands, not both in the same copy. Reading the result is always safe. The menu labels come from GitTree 2.0's own strings and the native commands were run with Git 2.43 in disposable copies. The GitTree steps were not run on macOS, Windows or Linux.

Concept and state effects

Revert looks at what one commit changed and adds a new commit that applies the opposite change. The bad commit stays in the history, and so does the proof that it happened. Reset does the opposite: it moves the branch back and leaves the commit out, which is fine for work only you have, but it forces everyone who already has the commit to repair their copy.

Here the new commit's parent is the bad commit, and the file ends up exactly as it was before. The branch moves forward by one commit and the server accepts it with an ordinary push, because it only adds to what is there.

An inverse change depends on what is around it. If someone changed the same lines after the bad commit, the revert stops with a conflict, and you resolve it like any other (lesson 23), or abort. Reverting a merge commit asks you to choose which parent the change is measured against: usually the first, the branch merged into. Reverting a merge also tells Git that its changes were handled, so merging the same branch later may report nothing to merge. This lesson reverts an ordinary commit and only shows that dialog.

Only the current branch gets a commit. Nothing else moves until you push.

Prepare the exact project state

Start from a separate ordinary copy of lesson 18's result M and leave your original untouched. Create the branch, save DOC_DARK_BAD over styles.css and commit it, create a new empty bare repository beside the copy, add it as a remote called rehearsal and push the branch with its upstream. CSS_RESTORED is what the file must look like after the revert.

Complete file inputs

Save only at the phase named below. These are full UTF-8/LF bodies with a final newline; never paste Markdown fences. Unchanged runnable files inherit the named input. Versions in separate variants must not overwrite the original.

styles.css — DOC_DARK_BAD

File excerpt · css
:root {
  color-scheme: light;
  --page: #f7f8fa;
  --surface: #ffffff;
  --text: #17212e;
  --link: #154ab5;
  --line: #bac4d0;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--page);
  color: var(--text);
  font: 1rem/1.6 system-ui, sans-serif;
}

a {
  color: var(--link);
}

.site-header,
main,
footer {
  width: min(68rem, 100% - 2rem);
  margin-inline: auto;
}

.site-header {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 1rem;
}

.hero {
  max-width: 48rem;
  padding-block: 3rem;
}

h1 {
  font-size: clamp(2rem, 5vw, 3.5rem);
  line-height: 1.2;
}

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
  gap: 1rem;
}

.feature-card {
  padding: 1rem;
  border: 1px solid var(--line);
  border-radius: 0.75rem;
  background: var(--surface);
}

section {
  margin-block-end: 2rem;
}

button,
input {
  font: inherit;
}

button,
input {
  padding: 0.5rem 0.75rem;
}

:focus-visible {
  outline: 3px solid var(--link);
  outline-offset: 3px;
}

form {
  display: grid;
  max-width: 28rem;
  gap: 0.5rem;
}

body { color: #f7f8fa; }

styles.css — CSS_RESTORED

File excerpt · css
:root {
  color-scheme: light;
  --page: #f7f8fa;
  --surface: #ffffff;
  --text: #17212e;
  --link: #154ab5;
  --line: #bac4d0;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--page);
  color: var(--text);
  font: 1rem/1.6 system-ui, sans-serif;
}

a {
  color: var(--link);
}

.site-header,
main,
footer {
  width: min(68rem, 100% - 2rem);
  margin-inline: auto;
}

.site-header {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 1rem;
}

.hero {
  max-width: 48rem;
  padding-block: 3rem;
}

h1 {
  font-size: clamp(2rem, 5vw, 3.5rem);
  line-height: 1.2;
}

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
  gap: 1rem;
}

.feature-card {
  padding: 1rem;
  border: 1px solid var(--line);
  border-radius: 0.75rem;
  background: var(--surface);
}

section {
  margin-block-end: 2rem;
}

button,
input {
  font: inherit;
}

button,
input {
  padding: 0.5rem 0.75rem;
}

:focus-visible {
  outline: 3px solid var(--link);
  outline-offset: 3px;
}

form {
  display: grid;
  max-width: 28rem;
  gap: 0.5rem;
}

In GitTree

Open the palette with Command+Shift+P (Ctrl+Shift+P on Ubuntu). Actions on a commit use that commit's right-click menu.

  1. Build the branch, the commit and the local server as the next section shows. Keep shared/dark-regression checked out and the work tree clean.
  2. In the graph select the bad commit and read its details: one changed path, styles.css, and one added line.
  3. Right-click it and choose Revert Commit. It applies at once on the branch you are on; there is no message editor. The notice says "Reverted." If it stops instead, the banner "Reverting … on shared/dark-regression" offers Resolve, Continue and Abort….
  4. Select the new commit. Its subject is Revert "style: introduce the rehearsal contrast regression" and its parent is the bad commit. The bad commit is still in the graph.
  5. Open styles.css: it is CSS_RESTORED, without the added rule.
  6. Click the toolbar Push. This is a normal push of the branch to its upstream; do not choose Force Push (With Lease)…. The notice says "Pushed shared/dark-regression to rehearsal/shared/dark-regression."
  7. Check the result: the work tree is clean, both commits are in the graph and the server branch is on the new commit.

If you right-click a merge commit, Revert Commit opens "Revert merge …" with First parent (the branch merged into) and Second parent (the branch merged in). Do not run it here.

For every ordinary commit in the course: Summary is the stated subject, Description is empty and the options Amend Previous Commit, Skip Hooks, Add Signed-off-by, Different Author, Push After Commit and Allow Empty Commit stay off.

In the terminal

In a separate copy of lesson 18's result:

Command · bash
git switch --create shared/dark-regression

Save DOC_DARK_BAD as styles.css, then:

Command · bash
git add -- styles.css
git commit --quiet -m "style: introduce the rehearsal contrast regression"
BAD_SHA="$(git rev-parse HEAD)"
git init --quiet --bare --initial-branch=main ../gittree-revert-origin.git
git remote add rehearsal ../gittree-revert-origin.git
git push --quiet --set-upstream rehearsal shared/dark-regression

Now revert, publish and read the result:

Command · bash
git revert --no-edit "$BAD_SHA"
git merge-base --is-ancestor "$BAD_SHA" HEAD; echo $?
git log -1 --format=%s
git push --quiet rehearsal shared/dark-regression
git status --short
git ls-remote --heads rehearsal refs/heads/shared/dark-regression
git rev-parse HEAD
Output
0
Revert "style: introduce the rehearsal contrast regression"

The ancestor check prints 0: the bad commit is still behind HEAD. styles.css is CSS_RESTORED, the status is empty, and the two IDs from ls-remote and rev-parse are equal. No force option was involved. In a separate copy where a later commit edits the same line, git revert stops with "CONFLICT (content)" and the hints git revert --continue and git revert --abort. And git revert on a merge commit refuses with "is a merge but no -m option was given"; -m 1 picks the first parent.

Result

Where you end: shared/dark-regression has one new commit that cancels the bad one. The page uses CSS_RESTORED, the bad commit is still in the history, the server branch is on the new commit, the work tree is clean and nothing was forced.

Common mistakes

  • Using reset and a force push to "remove" a commit others already have.
  • Expecting the bad commit to disappear from the log.
  • Reverting a merge without choosing its parent on purpose.
  • Pushing before you looked at the file the revert left.

If something goes wrong

If the revert stops with a conflict, read the banner. Resolve and Continue finish it as in lesson 23; Abort… returns the branch to where it was. If the revert itself was a mistake after it finished, revert the revert: it is just another commit.

Check your understanding

Answer each question, then read why. Questions marked “Required” count toward completing the lesson.

  1. Question 1 of 6

    What does revert do to the bad commit?

    Choose one answer.

  2. Question 2 of 6

    After the revert, which of these are true?

    Choose every answer that applies.

  3. Question 3 of 6
    Required

    You revert the bad commit and push normally. Where is the server branch afterwards?

    Choose one answer.

  4. Question 4 of 6

    Change the input: a later commit edits the same line the bad commit added, and then you revert the bad commit. What happens?

    Choose one answer.

  5. Question 5 of 6
    Required

    You try to revert a merge commit and Git says "is a merge but no -m option was given". What is going on, and what do you do?

    Choose one answer.

  6. Question 6 of 6
    Required

    After the revert and the push, which readbacks show it did what the lesson says?

    Choose every answer that applies.

0 of 6 correct

Answer the required questions correctly to complete the lesson.

Checked in this browser.

Practice

Report the revert's subject, its parent, whether the bad commit is still an ancestor and whether the server branch equals HEAD. Then predict what happens if a later commit had changed the same line before you reverted.

Your own confirmation: the site cannot see your computer.

To complete the lesson, answer the required questions correctly and confirm the practical exercise.

Recap

  • Revert adds a commit that cancels another.
  • History stays; the file goes back.
  • A normal push is enough.

Glossary terms introduced

Revert — عكس; inverse change — تغيير معاكس; mainline parent — الأب الرئيسي.

Video transcript

The narration of the video, cue by cue.

Show the transcript
  1. Undo a change that other people already have.
  2. Reset moves the branch back. Revert adds a new commit instead.
  3. Others already have the old commit. Revert leaves their copy valid.
  4. Build the shared branch with a bad commit, and a local server that has it.
  5. The graph shows M, and the bad commit on top.
  6. The bad commit adds one line, and the text becomes pale.
  7. Write down its ID.
  8. Right-click it, and choose Revert Commit.
  9. It applies at once. There is no message editor.
  10. The notice says reverted.
  11. The new commit is named after the old one.
  12. Its parent is the bad commit. Nothing was removed.
  13. The file is back as it was. The pale rule is gone.
  14. And the graph shows both commits. The history only grew.
  15. Only this branch moved. Nothing is published yet.
  16. The branch is one commit ahead of the server.
  17. Click Push. This is a normal push.
  18. The notice names the branch and the server.
  19. No force was needed. The push only added a commit.
  20. In the terminal, git revert with no edit does the same.
  21. The bad commit is still an ancestor. The answer is zero.
  22. And the subject starts with Revert.
  23. Push, then compare the server's ID with HEAD. They are equal.
  24. Change the input: someone edited the same line later.
  25. The revert stops with a conflict. Resolve and continue, or abort.
  26. Reverting a merge asks for a parent. Usually it is the first.
  27. After reverting a merge, merging that branch again may do nothing.
  28. Practice: report the subject, parent, ancestor check and the server ID.
  29. Next, find a lost commit with the reflog.