|
|
# Backporting a Merge Request
|
|
|
|
|
|
## Introduction
|
|
|
|
|
|
When a Merge Request is merged into *master*, it may need to be backported to maintenance branches. As each Merge Request consists of multiple commits, backporting it to another branch without squashing boils down to:
|
|
|
|
|
|
* cherry-picking the original commits comprising the Merge Request one-by-one on top of the target branch (i.e. the branch to which the Merge Request is being backported),
|
|
|
* creating a merge commit linking the previous HEAD of the target branch with the last cherry-picked commit, preferably retaining the commit log message from the original merge commit (e.g. to leave the Merge Request identifier intact for future reference).
|
|
|
|
|
|
## `git-replay-merge` to the rescue
|
|
|
|
|
|
### Overview
|
|
|
|
|
|
A script called `git-replay-merge` was written to make backporting Merge Requests more convenient by automating the process as much as possible.
|
|
|
|
|
|
### How it works
|
|
|
|
|
|
The script can be thought of as a wrapper for `git cherry-pick` and `git merge` which cowardly interrupts its actions as soon as things go south. In short, the script:
|
|
|
|
|
|
1. Detects the range of commits that needs to be cherry-picked.
|
|
|
1. Initiates the cherry-picking process on a throwaway local branch, called the replay branch below.
|
|
|
1. If conflicts arise while cherry-picking, you need to fix them yourself and let the script know when you are done.
|
|
|
1. Once cherry-picking succeeds, the replay branch is merged into a local branch with the same name as the target branch. Original merge commit log message with a modified subject line is used for the merge commit in the target branch.
|
|
|
1. The replay branch is removed.
|
|
|
|
|
|
Summing up, given:
|
|
|
|
|
|
* the ID of the merge commit in the "original" branch (i.e. the branch the Merge Request was merged into),
|
|
|
* the name of the git remote to ask about the target branch,
|
|
|
* the name of the target branch,
|
|
|
|
|
|
the script prepares a local branch with the relevant Merge Request "replayed" for pushing.
|
|
|
|
|
|
The script only prepares a branch for pushing; it is *not* pushed automatically.
|
|
|
|
|
|
If at any point in the process the user decides the script should not continue its work, `git replay-merge --abort` can be used to restore the working copy to the state it was in before `git replay-merge` was called.
|
|
|
|
|
|
### Installation
|
|
|
|
|
|
To use the script, [download it](https://gitlab.isc.org/isc-projects/bind9/snippets/2/raw?inline=false) to your development machine, saving it as `git-replay-merge` somewhere in your `$PATH`. Subsequently running `git replay-merge` (without the first hyphen) should reward you with a usage message:
|
|
|
|
|
|
```sh
|
|
|
$ git replay-merge
|
|
|
Usage:
|
|
|
|
|
|
git replay-merge <merge_commit_id> <target_remote> <target_branch>
|
|
|
git replay-merge --continue
|
|
|
git replay-merge --abort
|
|
|
```
|
|
|
|
|
|
### Example: the hard case (with conflicts)
|
|
|
|
|
|
Let's take a deep dive first and imagine !22 needs to be backported to *v9_12*.
|
|
|
|
|
|
#### Initial situation
|
|
|
|
|
|
Git setup:
|
|
|
|
|
|
```sh
|
|
|
$ git remote -v
|
|
|
gitlab git@gitlab.isc.org:isc-projects/bind9.git (fetch)
|
|
|
gitlab git@gitlab.isc.org:isc-projects/bind9.git (push)
|
|
|
...
|
|
|
```
|
|
|
|
|
|
Original branch (excerpt):
|
|
|
|
|
|
```sh
|
|
|
$ git log --no-decorate --graph --oneline gitlab/master
|
|
|
...
|
|
|
* 3abc7bf264 Merge branch 'fix-loadpending-handling' into 'master'
|
|
|
|\
|
|
|
| * 801dfe8f5d Add CHANGES entry
|
|
|
| * f5079bb877 Do not recheck DNS_ZONEFLG_LOADPENDING in zone_asyncload()
|
|
|
| * b9e9361c7b Asynchronous zone load events have no way of getting canceled
|
|
|
| * 29b7efdd9f Only clear DNS_ZONEFLG_LOADPENDING in zone_asyncload() if zone loading is completed immediately
|
|
|
| * 0e4fba2ced Lock zone before checking whether its asynchronous load is already pending
|
|
|
* | 883a9485e9 [master] copyrights
|
|
|
|/
|
|
|
* 3548061d03 Merge branch 'gitlab-ci-keep-artifacts' into 'master'
|
|
|
...
|
|
|
```
|
|
|
|
|
|
Target branch:
|
|
|
|
|
|
```sh
|
|
|
$ git log --no-decorate --max-count=1 --oneline gitlab/v9_12
|
|
|
44d995992a Merge branch 'fix-cpp-check-errors' into 'v9_12'
|
|
|
```
|
|
|
|
|
|
#### `git replay-merge` session log
|
|
|
|
|
|
```sh
|
|
|
$ git replay-merge 3abc7bf264 gitlab v9_12
|
|
|
Attempting to replay 883a9485e95916a686e56d81fff5130ac3102953..801dfe8f5d69ccb1252fbc57e2feeed34a9a438c on top of gitlab/v9_12 in fix-loadpending-handling-v9_12...
|
|
|
Switched to a new branch 'fix-loadpending-handling-v9_12'
|
|
|
[fix-loadpending-handling-v9_12 6b5b0dcb94] Lock zone before checking whether its asynchronous load is already pending
|
|
|
Date: Thu Feb 15 20:31:49 2018 +0100
|
|
|
1 file changed, 5 insertions(+), 2 deletions(-)
|
|
|
[fix-loadpending-handling-v9_12 8c9de139df] Only clear DNS_ZONEFLG_LOADPENDING in zone_asyncload() if zone loading is completed immediately
|
|
|
Date: Thu Feb 15 20:31:51 2018 +0100
|
|
|
1 file changed, 4 insertions(+), 2 deletions(-)
|
|
|
[fix-loadpending-handling-v9_12 eb8de80fcd] Asynchronous zone load events have no way of getting canceled
|
|
|
Date: Thu Feb 15 20:31:53 2018 +0100
|
|
|
1 file changed, 1 insertion(+), 6 deletions(-)
|
|
|
[fix-loadpending-handling-v9_12 87ac04b893] Do not recheck DNS_ZONEFLG_LOADPENDING in zone_asyncload()
|
|
|
Date: Thu Feb 15 20:31:54 2018 +0100
|
|
|
1 file changed, 11 deletions(-)
|
|
|
error: could not apply 801dfe8f5d... Add CHANGES entry
|
|
|
hint: after resolving the conflicts, mark the corrected paths
|
|
|
hint: with 'git add <paths>' or 'git rm <paths>'
|
|
|
hint: and commit the result with 'git commit'
|
|
|
Replay failed. Cherry-picking needs to be completed manually.
|
|
|
When done, run "git replay-merge --continue".
|
|
|
Use "git replay-merge --abort" to abort the replay.
|
|
|
```
|
|
|
|
|
|
Oops, there is a conflict in `CHANGES`:
|
|
|
|
|
|
```sh
|
|
|
$ git status
|
|
|
On branch fix-loadpending-handling-v9_12
|
|
|
Your branch is ahead of 'gitlab/v9_12' by 4 commits.
|
|
|
(use "git push" to publish your local commits)
|
|
|
|
|
|
You are currently cherry-picking commit 801dfe8f5d.
|
|
|
(fix conflicts and run "git cherry-pick --continue")
|
|
|
(use "git cherry-pick --abort" to cancel the cherry-pick operation)
|
|
|
|
|
|
Unmerged paths:
|
|
|
(use "git add <file>..." to mark resolution)
|
|
|
|
|
|
both modified: CHANGES
|
|
|
|
|
|
no changes added to commit (use "git add" and/or "git commit -a")
|
|
|
```
|
|
|
|
|
|
Let's fix the conflict using an editor of choice. Once that is done,
|
|
|
cherry-picking can continue:
|
|
|
|
|
|
```sh
|
|
|
$ $VISUAL CHANGES
|
|
|
$ git add CHANGES
|
|
|
$ git cherry-pick --continue
|
|
|
[fix-loadpending-handling-v9_12 b7a6e602e9] Add CHANGES entry
|
|
|
Date: Thu Feb 15 20:31:55 2018 +0100
|
|
|
1 file changed, 6 insertions(+)
|
|
|
```
|
|
|
|
|
|
With cherry-picking finished, it is time to poke `git replay-merge` to resume its work:
|
|
|
|
|
|
```sh
|
|
|
$ git replay-merge --continue
|
|
|
Attempting to merge fix-loadpending-handling-v9_12 into v9_12...
|
|
|
Switched to a new branch 'v9_12'
|
|
|
Merge made by the 'recursive' strategy.
|
|
|
CHANGES | 6 ++++++
|
|
|
lib/dns/zone.c | 29 +++++++++--------------------
|
|
|
2 files changed, 15 insertions(+), 20 deletions(-)
|
|
|
|
|
|
Replayed fix-loadpending-handling onto v9_12.
|
|
|
To push the replay, use:
|
|
|
|
|
|
git push gitlab v9_12:v9_12
|
|
|
```
|
|
|
|
|
|
That's it, the replay is complete:
|
|
|
|
|
|
```sh
|
|
|
$ git log --graph --oneline v9_12
|
|
|
* 6801ed2c4f (v9_12) Merge branch 'fix-loadpending-handling-v9_12' into 'v9_12'
|
|
|
|\
|
|
|
| * b7a6e602e9 Add CHANGES entry
|
|
|
| * 87ac04b893 Do not recheck DNS_ZONEFLG_LOADPENDING in zone_asyncload()
|
|
|
| * eb8de80fcd Asynchronous zone load events have no way of getting canceled
|
|
|
| * 8c9de139df Only clear DNS_ZONEFLG_LOADPENDING in zone_asyncload() if zone loading is completed immediately
|
|
|
| * 6b5b0dcb94 Lock zone before checking whether its asynchronous load is already pending
|
|
|
|/
|
|
|
* 44d995992a (gitlab/v9_12) Merge branch 'fix-cpp-check-errors' into 'v9_12'
|
|
|
...
|
|
|
```
|
|
|
|
|
|
The branch is now ready to be pushed using the command suggested by `git replay-merge`.
|
|
|
|
|
|
### Example: the easy case (no conflicts)
|
|
|
|
|
|
With the pessimistic scenario sorted out, let's now try backporting !21 to *v9_12*.
|
|
|
|
|
|
#### Initial situation
|
|
|
|
|
|
Git setup:
|
|
|
|
|
|
```sh
|
|
|
$ git remote -v
|
|
|
gitlab git@gitlab.isc.org:isc-projects/bind9.git (fetch)
|
|
|
gitlab git@gitlab.isc.org:isc-projects/bind9.git (push)
|
|
|
...
|
|
|
```
|
|
|
|
|
|
Original branch (excerpt):
|
|
|
|
|
|
```sh
|
|
|
$ git log --no-decorate --graph --oneline gitlab/master
|
|
|
...
|
|
|
* 54823ea037 Merge branch 'fix-dnstap-output-file-rolling' into 'master'
|
|
|
|\
|
|
|
| * 448eb98797 (gitlab/mr/21) Add CHANGES entry
|
|
|
| * 02063cbae2 Make dns_dt_send() call dns_dt_reopen() asynchronously
|
|
|
| * 8e3c16175a Make dns_dt_reopen() request task-exclusive mode on its own
|
|
|
| * f199a5a9ae Add dns_dt_create2()
|
|
|
|/
|
|
|
* 522e5dd9bc 4893. [bug] Address various issues reported by cppcheck. [GL #51]
|
|
|
...
|
|
|
```
|
|
|
|
|
|
Target branch:
|
|
|
|
|
|
```sh
|
|
|
$ git log --no-decorate --max-count=1 --oneline gitlab/v9_12
|
|
|
fba6c2e982 Merge branch 'fix-loadpending-handling-v9_12' into v9_12
|
|
|
```
|
|
|
|
|
|
#### `git replay-merge` session log
|
|
|
|
|
|
```sh
|
|
|
$ git replay-merge 54823ea037 gitlab v9_12
|
|
|
Attempting to replay 522e5dd9bc6df64bc2388e4056ff293377ef678c..448eb987970144461b264fe2f238c850a50090e4 on top of gitlab/v9_12 in fix-dnstap-output-file-rolling-v9_12...
|
|
|
Switched to a new branch 'fix-dnstap-output-file-rolling-v9_12'
|
|
|
[fix-dnstap-output-file-rolling-v9_12 4871445b5c] Add dns_dt_create2()
|
|
|
Date: Mon Feb 5 21:19:44 2018 +0100
|
|
|
3 files changed, 25 insertions(+), 2 deletions(-)
|
|
|
[fix-dnstap-output-file-rolling-v9_12 9cac22573b] Make dns_dt_reopen() request task-exclusive mode on its own
|
|
|
Date: Mon Feb 5 21:22:53 2018 +0100
|
|
|
3 files changed, 15 insertions(+), 6 deletions(-)
|
|
|
[fix-dnstap-output-file-rolling-v9_12 b4ace595e1] Make dns_dt_send() call dns_dt_reopen() asynchronously
|
|
|
Date: Mon Feb 5 21:04:39 2018 +0100
|
|
|
1 file changed, 93 insertions(+), 6 deletions(-)
|
|
|
[fix-dnstap-output-file-rolling-v9_12 6529f4d5b5] Add CHANGES entry
|
|
|
Date: Fri Feb 16 09:38:48 2018 +0100
|
|
|
1 file changed, 3 insertions(+)
|
|
|
Attempting to merge fix-dnstap-output-file-rolling-v9_12 into v9_12...
|
|
|
Switched to a new branch 'v9_12'
|
|
|
Merge made by the 'recursive' strategy.
|
|
|
CHANGES | 3 ++
|
|
|
bin/named/server.c | 8 ++---
|
|
|
lib/dns/dnstap.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
|
|
|
lib/dns/include/dns/dnstap.h | 17 ++++++++--
|
|
|
lib/dns/win32/libdns.def.in | 1 +
|
|
|
5 files changed, 136 insertions(+), 14 deletions(-)
|
|
|
|
|
|
Replayed fix-dnstap-output-file-rolling onto v9_12.
|
|
|
To push the replay, use:
|
|
|
|
|
|
git push gitlab v9_12:v9_12
|
|
|
```
|
|
|
|
|
|
That's it, the branch is all set for pushing. |