2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-29 13:38:26 +00:00

michal created page: Backporting a Merge Request

Michał Kępień 2018-02-20 16:44:40 -05:00
parent decc50c1a5
commit 0c9c6acb19

@ -0,0 +1,257 @@
# 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.