diff options
Diffstat (limited to 'doc/topics/git/git_rebase.md')
-rw-r--r-- | doc/topics/git/git_rebase.md | 286 |
1 files changed, 137 insertions, 149 deletions
diff --git a/doc/topics/git/git_rebase.md b/doc/topics/git/git_rebase.md index 2f12201fb45..ec28d0b6431 100644 --- a/doc/topics/git/git_rebase.md +++ b/doc/topics/git/git_rebase.md @@ -3,132 +3,98 @@ stage: Create group: Source Code info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments" type: concepts, howto -description: "Introduction to Git rebase and force-push, methods to resolve merge conflicts through the command line." +description: "Introduction to Git rebase and force push, methods to resolve merge conflicts through the command line." --- -# Introduction to Git rebase and force-push **(FREE)** +# Git rebase and force push **(FREE)** -This guide helps you to get started with rebasing, force-pushing, and fixing +This guide helps you to get started with rebases, force pushes, and fixing [merge conflicts](../../user/project/merge_requests/conflicts.md) locally. - -Before diving into this document, make sure you are familiar with using +Before you attempt a force push or a rebase, make sure you are familiar with [Git through the command line](../../gitlab-basics/start-using-git.md). -## Git rebase - -[Rebasing](https://git-scm.com/docs/git-rebase) is a very common operation in -Git, and has these options: - -- [Regular rebase](#regular-rebase). -- [Interactive rebase](#interactive-rebase). - -### Before rebasing - WARNING: `git rebase` rewrites the commit history. It **can be harmful** to do it in shared branches. It can cause complex and hard to resolve [merge conflicts](../../user/project/merge_requests/conflicts.md). In these cases, instead of rebasing your branch against the default branch, -consider pulling it instead (`git pull origin master`). It has a similar -effect without compromising the work of your contributors. - -It's safer to back up your branch before rebasing to make sure you don't lose -any changes. For example, consider a [feature branch](../../gitlab-basics/start-using-git.md#branches) -called `my-feature-branch`: - -1. Open your feature branch in the terminal: - - ```shell - git checkout my-feature-branch - ``` - -1. Checkout a new branch from it: - - ```shell - git checkout -b my-feature-branch-backup - ``` - -1. Go back to your original branch: - - ```shell - git checkout my-feature-branch - ``` +consider pulling it instead (`git pull origin master`). Pulling has similar +effects with less risk compromising the work of your contributors. -Now you can safely rebase it. If anything goes wrong, you can recover your -changes by resetting `my-feature-branch` against `my-feature-branch-backup`: +In Git, a rebase updates your feature branch with the contents of another branch. +This step is important for Git-based development strategies. Use a rebase to confirm +that your branch's changes don't conflict with any changes added to your target branch +_after_ you created your feature branch. -1. Make sure you're in the correct branch (`my-feature-branch`): - - ```shell - git checkout my-feature-branch - ``` +When you rebase: -1. Reset it against `my-feature-branch-backup`: +1. Git imports all the commits submitted to your target branch _after_ you initially created + your feature branch from it. +1. Git stacks the commits you have in your feature branch on top of all + the commits it imported from that branch: - ```shell - git reset --hard my-feature-branch-backup - ``` +![Git rebase illustration](img/git_rebase_v13_5.png) -If you added changes to `my-feature-branch` after creating the backup branch, -you lose them when resetting. +While most rebases are performed against `main`, you can rebase against any other +branch, such as `release-15-3`. You can also specify a different remote repository +(such as `upstream`) instead of `origin`. -### Regular rebase +## Back up a branch before rebase -With a regular rebase you can update your feature branch with the default -branch (or any other branch). -This step is important for Git-based development strategies. You can -ensure your new changes don't break any -existing changes added to the target branch _after_ you created your feature -branch. +To back up a branch before taking any destructive action, like a rebase or force push: -For example, to update your branch `my-feature-branch` with your -[default branch](../../user/project/repository/branches/default.md) (here, using `main`): +1. Open your feature branch in the terminal: `git checkout my-feature` +1. Check out a new branch from it: `git checkout -b my-feature-backup` + Any changes added to `my-feature` after this point are lost + if you restore from the backup branch. +1. Change back to your original branch: `git checkout my-feature` -1. Fetch the latest changes from `main`: +Your branch is backed up, and you can try a rebase or a force push. +If anything goes wrong, restore your branch from its backup: - ```shell - git fetch origin main - ``` +1. Make sure you're in the correct branch (`my-feature`): `git checkout my-feature` +1. Reset it against `my-feature-backup`: `git reset --hard my-feature-backup` -1. Checkout your feature branch: +## Rebase a branch - ```shell - git checkout my-feature-branch - ``` +[Rebases](https://git-scm.com/docs/git-rebase) are very common operations in +Git, and have these options: -1. Rebase it against `main`: +- **Regular rebases.** This type of rebase can be done through the + [command line](#regular-rebase) and [the GitLab UI](#from-the-gitlab-ui). +- [**Interactive rebases**](#interactive-rebase) give more flexibility by + enabling you to specify how to handle each commit. Interactive rebases + must be done on the command line. - ```shell - git rebase origin/main - ``` +Any user who rebases a branch is treated as having added commits to that branch. +If a project is configured to +[**prevent approvals by users who add commits**](../../user/project/merge_requests/approvals/settings.md#prevent-approvals-by-users-who-add-commits), +a user who rebases a branch cannot also approve its merge request. -1. [Force-push](#force-push) to your branch. +### Regular rebase -When you rebase: +Standard rebases replay the previous commits on a branch without changes, stopping +only if merge conflicts occur. -1. Git imports all the commits submitted to `main` _after_ the - moment you created your feature branch until the present moment. -1. Git puts the commits you have in your feature branch on top of all - the commits imported from `main`: +Prerequisites: -![Git rebase illustration](img/git_rebase_v13_5.png) +- You must have permission to force push branches. -You can replace `main` with any other branch you want to rebase against, for -example, `release-10-3`. You can also replace `origin` with other remote -repositories, for example, `upstream`. To check what remotes you have linked to your local -repository, you can run `git remote -v`. +To update your branch `my-feature` with recent changes from your +[default branch](../../user/project/repository/branches/default.md) (here, using `main`): -If there are merge conflicts, Git prompts you to fix -them before continuing the rebase. +1. Fetch the latest changes from `main`: `git fetch origin main` +1. Check out your feature branch: `git checkout my-feature` +1. Rebase it against `main`: `git rebase origin/main` +1. [Force push](#force-push) to your branch. -To learn more, check Git's documentation on [rebasing](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) -and [rebasing strategies](https://git-scm.com/book/en/v2/Git-Branching-Rebasing). +If there are merge conflicts, Git prompts you to fix them before continuing the rebase. -#### Rebase from the GitLab UI +### From the GitLab UI -You can rebase your feature branch directly from the merge request through a -[quick action](../../user/project/quick_actions.md#issues-merge-requests-and-epics), -if all of these conditions are met: +The `/rebase` [quick action](../../user/project/quick_actions.md#issues-merge-requests-and-epics) +rebases your feature branch directly from its merge request if all of these +conditions are met: - No merge conflicts exist for your feature branch. - You have the **Developer** role for the source project. This role grants you @@ -145,51 +111,61 @@ To rebase from the UI: GitLab schedules a rebase of the feature branch against the default branch and executes it as soon as possible. -The user performing the rebase action is considered -a user that added commits to the merge request. When the merge request approvals setting -[**Prevent approvals by users who add commits**](../../user/project/merge_requests/approvals/settings.md#prevent-approvals-by-users-who-add-commits) -is enabled, the user can't also approve the merge request. - ### Interactive rebase -You can use interactive rebase to modify commits. For example, amend a commit -message, squash (join multiple commits into one), edit, or delete -commits. Use a rebase for changing past commit messages, -and organizing the commit history of your branch to keep it clean. +Use an interactive rebase (the `--interactive` flag, or `-i`) to simultaneously +update a branch while you modify how its commits are handled. +For example, to edit the last five commits in your branch (`HEAD~5`), run: -NOTE: -Keeping the default branch commit history clean doesn't require you to -manually squash all your commits before merging every merge request. -With [Squash and Merge](../../user/project/merge_requests/squash_and_merge.md), -GitLab does it automatically. - -When you want to change anything in recent commits, use interactive -rebase by passing the flag `--interactive` (or `-i`) to the rebase command. +```shell +git rebase -i HEAD~5 +``` -For example, if you want to edit the last three commits in your branch -(`HEAD~3`), run: +Git opens the last five commits in your terminal text editor, oldest commit first. +Each commit shows the action to take on it, the SHA, and the commit title: ```shell -git rebase -i HEAD~3 +pick 111111111111 Second round of structural revisions +pick 222222222222 Update inbound link to this changed page +pick 333333333333 Shifts from H4 to H3 +pick 444444444444 Adds revisions from editorial +pick 555555555555 Revisions continue to build the concept part out + +# Rebase 111111111111..222222222222 onto zzzzzzzzzzzz (5 commands) +# +# Commands: +# p, pick <commit> = use commit +# r, reword <commit> = use commit, but edit the commit message +# e, edit <commit> = use commit, but stop for amending +# s, squash <commit> = use commit, but meld into previous commit +# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous ``` -Git opens the last three commits in your terminal text editor and describes -all the interactive rebase options you can use. The default option is `pick`, -which maintains the commit unchanged. Replace the keyword `pick` according to +After the list of commits, a commented-out section shows some common actions you +can take on a commit: + +- **Pick** a commit to use it with no changes. The default option. +- **Reword** a commit message. +- **Edit** a commit to use it, but pause the rebase to amend (add changes to) it. +- **Squash** multiple commits together to simplify the commit history + of your feature branch. + +Replace the keyword `pick` according to the operation you want to perform in each commit. To do so, edit the commits in your terminal's text editor. For example, with [Vim](https://www.vim.org/) as the text editor in -a macOS's Zsh shell, you can `squash` or `fixup` (combine) all three commits: +a macOS Zsh shell, you can `squash` or `fixup` (combine) all of the commits together: -<!-- vale gitlab.FirstPerson = NO --> +NOTE: +The steps for editing through the command line can be slightly +different depending on your operating system and the shell you use. -1. Press <kbd>i</kbd> - on your keyboard to switch to Vim's editing mode. +1. Press <kbd>i</kbd> on your keyboard to switch to Vim's editing mode. 1. Use your keyboard arrows to edit the **second** commit keyword - from `pick` to `squash` or `fixup` (or `s` or `f`). Do the same to the **third** commit. - The first commit should be left **unchanged** (`pick`) as we want to squash - the second and third into the first. + from `pick` to `squash` or `fixup` (or `s` or `f`). Do the same to the remaining commits. + Leave the first commit **unchanged** (`pick`) as we want to squash + all other commits into it. 1. Press <kbd>Escape</kbd> to leave the editing mode. 1. Type `:wq` to "write" (save) and "quit". 1. When squashing, Git outputs the commit message so you have a chance to edit it: @@ -198,45 +174,57 @@ a macOS's Zsh shell, you can `squash` or `fixup` (combine) all three commits: - To leave it as it is, type `:wq`. To edit the commit message: switch to the editing mode, edit the commit message, and save it as you just did. 1. If you haven't pushed your commits to the remote branch before rebasing, - push your changes normally. If you had pushed these commits already, - [force-push](#force-push) instead. + push your changes without a force push. If you had pushed these commits already, + [force push](#force-push) instead. -<!-- vale gitlab.FirstPerson = YES --> +#### Configure squash options for a project -The steps for editing through the command line can be slightly -different depending on your operating system and the shell you're using. +Keeping the default branch commit history clean doesn't require you to +manually squash all your commits on each merge request. GitLab provides +[squash and merge](../../user/project/merge_requests/squash_and_merge.md#configure-squash-options-for-a-project), +options at a project level. -See [Numerous undo possibilities in Git](numerous_undo_possibilities_in_git/index.md#undo-staged-local-changes-without-modifying-history) -for a deeper look into interactive rebase. +## Force push -## Force-push +Complex operations in Git require you to force an update to the remote branch. +Operations like squashing commits, resetting a branch, or rebasing a branch rewrite +the history of your branch. Git requires a forced update to help safeguard against +these more destructive changes from happening accidentally. -When you perform more complex operations, for example, squash commits, reset or -rebase your branch, you must _force_ an update to the remote branch. -These operations imply rewriting the commit history of the branch. -To force an update, pass the flag `--force` or `-f` to the `push` command. For -example: +Force pushing is not recommended on shared branches, as you risk destroying the +changes of others. + +If the branch you want to force push is [protected](../../user/project/protected_branches.md), +you can't force push to it unless you either: + +- Unprotect it. +- [Allow force pushes](../../user/project/protected_branches.md#allow-force-push-on-a-protected-branch) + to it. + +Then you can force push and protect it again. + +### `--force-with-lease` flag + +The [`--force-with-lease`](https://git-scm.com/docs/git-push#Documentation/git-push.txt---force-with-leaseltrefnamegt) +flag force pushes. Because it preserves any new commits added to the remote +branch by other people, it is safer than `--force`: ```shell -git push --force origin my-feature-branch +git push --force-with-lease origin my-feature ``` -Forcing an update is **not** recommended when you're working on shared -branches. +### `--force` flag -Alternatively, you can pass the flag [`--force-with-lease`](https://git-scm.com/docs/git-push#Documentation/git-push.txt---force-with-leaseltrefnamegt) -instead, as it is safer. This flag preserves any new commits added to the remote -branch by other people: +The `--force` flag forces pushes, but does not preserve any new commits added to +the remote branch by other people. To use this method, pass the flag `--force` or `-f` +to the `push` command: ```shell -git push --force-with-lease origin my-feature-branch +git push --force origin my-feature ``` -If the branch you want to force-push is [protected](../../user/project/protected_branches.md), -you can't force push to it unless you either: +## Related topics -- Unprotect it. -- [Allow force push](../../user/project/protected_branches.md#allow-force-push-on-a-protected-branch) - to it. - -Then you can force push and protect it again. +- [Numerous undo possibilities in Git](numerous_undo_possibilities_in_git/index.md#undo-staged-local-changes-without-modifying-history) +for a deeper look into interactive rebases. +- [Git documentation for branches and rebases](https://git-scm.com/book/en/v2/Git-Branching-Rebasing). |