@@ -36,7 +36,7 @@ You can undo changes at any point in this workflow:
...
@@ -36,7 +36,7 @@ You can undo changes at any point in this workflow:
-[When you're working locally](#undo-local-changes) and haven't yet pushed to a remote repository.
-[When you're working locally](#undo-local-changes) and haven't yet pushed to a remote repository.
- When you have already pushed to a remote repository and you want to:
- When you have already pushed to a remote repository and you want to:
-[Keep the history intact](#undo-remote-changes-without-changing-history)(preferred).
-[Keep the history intact](#undo-remote-changes-without-changing-history)(preferred).
-[Change the history](#undo-remote-changes-with-modifying-history)(requires
-[Change the history](#undo-remote-changes-while-changing-history)(requires
coordination with team and force pushes).
coordination with team and force pushes).
## Undo local changes
## Undo local changes
...
@@ -152,7 +152,7 @@ If you want to change to another branch, you can use [`git stash`](https://www.g
...
@@ -152,7 +152,7 @@ If you want to change to another branch, you can use [`git stash`](https://www.g
## Undo committed local changes
## Undo committed local changes
When you commit to your local repository (`git commit`), the version control system records
When you commit to your local repository (`git commit`), Git records
your changes. Because you did not push to a remote repository yet, your changes are
your changes. Because you did not push to a remote repository yet, your changes are
not public (or shared with other developers). At this point, you can undo your changes.
not public (or shared with other developers). At this point, you can undo your changes.
...
@@ -218,64 +218,53 @@ which clashes with what other developers have locally.
...
@@ -218,64 +218,53 @@ which clashes with what other developers have locally.
### Undo staged local changes with history modification
### Undo staged local changes with history modification
You can rewrite history in Git, but you should avoid it, because it can cause problems
The following tasks rewrite Git history.
when multiple developers are contributing to the same codebase.
There is one command for history modification and that is `git rebase`. Command
#### Delete a specific commit
provides interactive mode (`-i` flag) which enables you to:
-**reword** commit messages (there is also `git commit --amend` for editing
You can delete a specific commit. For example, if you have
last commit message).
commits `A-B-C-D` and you want to delete commit `B`.
-**edit** the commit content (changes introduced by commit) and message.
-**squash** multiple commits into a single one, and have a custom or aggregated
commit message.
-**drop** commits - delete them.
- and few more options.
Let us check few examples. Again there are commits `A-B-C-D` where you want to
1. Rebase the range from current commit `D` to `B`:
delete commit `B`.
- Rebase the range from current commit D to A:
```shell
git rebase -i A
```shell
```
git rebase -i A
```
- Command opens your favorite editor where you write `drop` in front of commit
A list of commits is displayed in your editor.
`B`, but you leave default `pick` with all other commits. Save and exit the
editor to perform a rebase. Remember: if you want to cancel delete whole
file content before saving and exiting the editor
In case you want to modify something introduced in commit `B`.
1. In front of commit `B`, replace `pick` with `drop`.
1. Leave the default, `pick`, for all other commits.
1. Save and exit the editor.
- Rebase the range from current commit D to A:
#### Modify a specific commit
```shell
You can modify a specific commit. For example, if you have
git rebase -i A
commits `A-B-C-D` and you want to modify something introduced in commit `B`.
```
- Command opens your favorite text editor where you write `edit` in front of commit
1. Rebase the range from current commit `D` to `B`:
`B`, but leave default `pick` with all other commits. Save and exit the editor to
perform a rebase.
- Now do your edits and commit changes:
```shell
git rebase -i A
```
```shell
A list of commits is displayed in your editor.
git commit -a
```
1. In front of commit `B`, replace `pick` with `edit`.
1. Leave the default, `pick`, for all other commits.
1. Save and exit the editor.
1. Open the file in your editor, make your edits, and commit the changes:
You can find some more examples in the section explaining
```shell
[how to modify history](#how-modifying-history-is-done).
git commit -a
```
### Redoing the undo
### Redoing the undo
Sometimes you realize that the changes you undid were useful and you want them
You can recall previous local commits. However, not all previous commits are available, because
back. Well because of first paragraph you are in luck. Command `git reflog`
Git regularly [cleans the commits that are unreachable by branches or tags](https://git-scm.com/book/en/v2/Git-Internals-Maintenance-and-Data-Recovery).
enables you to *recall* detached local commits by referencing or applying them
via commit ID. Although, do not expect to see really old commits in reflog, because
Git regularly [cleans the commits which are *unreachable* by branches or tags](https://git-scm.com/book/en/v2/Git-Internals-Maintenance-and-Data-Recovery).
To view repository history and to track older commits you can use below command:
To view repository history and track prior commits, run `git reflog show`. For example:
```shell
```shell
$ git reflog show
$ git reflog show
...
@@ -293,63 +282,46 @@ eb37e74 HEAD@{6}: rebase -i (pick): Commit C
...
@@ -293,63 +282,46 @@ eb37e74 HEAD@{6}: rebase -i (pick): Commit C
6e43d59 HEAD@{16}: commit: Commit B
6e43d59 HEAD@{16}: commit: Commit B
```
```
Output of command shows repository history. In first column there is commit ID,
This output shows the repository history, including:
in following column, number next to `HEAD` indicates how many commits ago something
was made, after that indicator of action that was made (commit, rebase, merge, ...)
and then on end description of that action.
## Undo remote changes without changing history
- The commit SHA.
- How many `HEAD`-changing actions ago the commit was made (`HEAD@{12}` was 12 `HEAD`-changing actions ago).
- The action that was taken, for example: commit, rebase, merge.
- A description of the action that changed `HEAD`.
This topic is roughly same as modifying committed local changes without modifying
## Undo remote changes without changing history
history. **It should be the preferred way of undoing changes on any remote repository
or public branch.** Keep in mind that branching is the best solution when you want
to retain the history of faulty development, yet start anew from certain point.
Branching
To undo changes in the remote repository, you can create a new commit with the changes you
enables you to include the existing changes in new development (by merging) and
want to undo. You should follow this process, which preserves the history and
it also provides a clear timeline and development structure.
provides a clear timeline and development structure. However, you only
need to follow this procedure if your work was merged into a branch that
other developers are using as the base for their work (for example, `main`).
![Use revert to keep branch flowing](img/revert.png)
![Use revert to keep branch flowing](img/revert.png)
If you want to revert changes introduced in certain `commit-id`, you can
To revert changes introduced in a specific commit `B`:
revert that `commit-id` (swap additions and deletions) in newly created commit:
You can do this with
```shell
```shell
git revert commit-id
git revert B
```
```
or creating a new branch:
## Undo remote changes while changing history
```shell
git checkout commit-id
git checkout -b new-path-of-feature
```
## Undo remote changes with modifying history
You can undo remote changes and change history.
This is useful when you want to *hide* certain things - like secret keys,
Even with an updated history, old commits can still be
passwords, and SSH keys. It is and should not be used to hide mistakes, as
accessed by commit SHA, at least until all the automated cleanup
it makes it harder to debug in case there are some other bugs. The main
of detached commits is performed, or a cleanup is run manually. Even the cleanup might not remove old commits if there are still refs pointing to them.
reason for this is that you loose the real development progress. Keep in
mind that, even with modified history, commits are just detached and can still be
accessed through commit ID - at least until all repositories perform
the automated cleanup of detached commits.
![Modifying history causes problems on remote branch](img/rebase_reset.png)
![Modifying history causes problems on remote branch](img/rebase_reset.png)
### Where modifying history is generally acceptable
### When changing history is acceptable
Modified history breaks the development chain of other developers, as changed
You should not change the history when you're working in a public branch
history does not have matching commit IDs. For that reason it should not be
or a branch that might be used by other developers.
used on any public branch or on branch that might be used by other developers.
When contributing to big open source repositories (for example, [GitLab](https://gitlab.com/gitlab-org/gitlab/blob/master/CONTRIBUTING.md#contribution-acceptance-criteria)
itself), it is acceptable to squash commits into a single one, to present a
nicer history of your contribution.
Keep in mind that this also removes the comments attached to certain commits
When you contribute to large open source repositories, like [GitLab](https://gitlab.com/gitlab-org/gitlab),
in merge requests, so if you need to retain traceability in GitLab, then
you can squash your commits into a single one.
modifying history is not acceptable.
A feature branch of a merge request is a public branch and might be used by
A feature branch of a merge request is a public branch and might be used by
other developers, but project process and rules might allow or require
other developers, but project process and rules might allow or require
...
@@ -362,20 +334,12 @@ at merge).
...
@@ -362,20 +334,12 @@ at merge).
NOTE:
NOTE:
Never modify the commit history of your [default branch](../../../user/project/repository/branches/default.md) or shared branch.
Never modify the commit history of your [default branch](../../../user/project/repository/branches/default.md) or shared branch.
### How modifying history is done
### How to change history
After you know what you want to modify (how far in history or how which range of
You can modify history by using `git rebase -i`. This command allows modification, squashing, deletion
old commits), use `git rebase -i commit-id`. This command displays all the commits from
of commits.
current version to chosen commit ID and allow modification, squashing, deletion
of that commits.
```shell
```shell
$ git rebase -i commit1-id..commit3-id
pick <commit1-id> <commit1-commit-message>
pick <commit2-id> <commit2-commit-message>
pick <commit3-id> <commit3-commit-message>
# Rebase commit1-id..commit3-id onto <commit4-id> (3 command(s))