Repository: VirtusLab/git-machete
Pull request #1077 (2023-10-20 13:42:46)

Release v3.20.0


Patch:
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index e9ff96c..fcf58bf 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,5 +1,12 @@
 # Release notes
 
+## New in git-machete 3.20.0
+
+- added: `git machete github create-pr` adds a comment linking the PR to its base PR; `github retarget-pr` keeps that comment up to date (suggested by @guyboltonking)
+- added: new subcommand `git machete github restack-pr`, which (force-)pushes and retargets the PR, without adding code owners as reviewers in the process (suggested by @raylu)
+- improved: when running `git machete squash` against a root branch, the error message suggests using `--fork-point=...` flag (suggested by @levinotik)
+- improved: simplified & clarified docs in multiple places (partly suggested by @kgadek)
+
 ## New in git-machete 3.19.0
 
 - added: support for Python 3.12
diff --git a/ci/checks/prohibit-exempli-gratia-in-rst.sh b/ci/checks/prohibit-exempli-gratia-in-rst.sh
new file mode 100755
index 0000000..438edb4
--- /dev/null
+++ b/ci/checks/prohibit-exempli-gratia-in-rst.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+
+set -e -o pipefail -u
+
+self_dir=$(cd "$(dirname "$0")" &>/dev/null; pwd -P)
+source "$self_dir"/utils.sh
+
+if git grep -En 'e\. ?g\.' -- '*.rst'; then
+  die 'Do not use `e.g.` in docs; use a clearer alternative like `for example` instead'
+fi
diff --git a/ci/checks/prohibit-id-est-in-rst.sh b/ci/checks/prohibit-id-est-in-rst.sh
new file mode 100755
index 0000000..a22cc0e
--- /dev/null
+++ b/ci/checks/prohibit-id-est-in-rst.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+
+set -e -o pipefail -u
+
+self_dir=$(cd "$(dirname "$0")" &>/dev/null; pwd -P)
+source "$self_dir"/utils.sh
+
+if git grep -En 'i\. ?e\.' -- '*.rst'; then
+  die 'Do not use `i.e.` in docs; use a clearer alternative like a long dash `---` instead'
+fi
diff --git a/ci/checks/run-all-checks.sh b/ci/checks/run-all-checks.sh
index c7a3dd9..4389b63 100755
--- a/ci/checks/run-all-checks.sh
+++ b/ci/checks/run-all-checks.sh
@@ -26,6 +26,8 @@ enforce-release-notes-up-to-date.sh
 enforce-shell-scripts-pass-shellcheck.sh
 prohibit-bash-usages-from-python.sh
 prohibit-deploy-step-in-circleci.sh
+prohibit-exempli-gratia-in-rst.sh
+prohibit-id-est-in-rst.sh
 prohibit-double-backticks-in-python.sh
 prohibit-markdown-links-in-rst.sh
 prohibit-single-backtick-in-rst.sh
diff --git a/completion/git-machete.completion.bash b/completion/git-machete.completion.bash
index 845079f..9d8b6f9 100644
--- a/completion/git-machete.completion.bash
+++ b/completion/git-machete.completion.bash
@@ -6,7 +6,7 @@ _git_machete() {
 
   local categories="addable childless managed slidable slidable-after unmanaged with-overridden-fork-point"
   local directions="down first last next prev root up"
-  local github_subcommands="anno-prs checkout-prs create-pr retarget-pr sync"
+  local github_subcommands="anno-prs checkout-prs create-pr restack-pr retarget-pr sync"
   local locations="current $directions"
   local opt_color_args="always auto never"
   local opt_return_to_args="here nearest-remaining stay"
diff --git a/completion/git-machete.completion.zsh b/completion/git-machete.completion.zsh
index 66efc4c..774494c 100644
--- a/completion/git-machete.completion.zsh
+++ b/completion/git-machete.completion.zsh
@@ -252,6 +252,7 @@ __git_machete_github_subcommands() {
         'anno-prs:annotate the branches based on their corresponding GitHub PR numbers and authors'
         'checkout-prs:check out the given pull requests locally'
         'create-pr:create a PR for the current branch, using the upstream (parent) branch as the PR base'
+        'restack-pr:(force-)push and retarget the PR, without adding code owners as reviewers in the process'
         'retarget-pr:set the base of the current branch PR to upstream (parent) branch'
         'sync:synchronize with the remote repository: checkout open PRs for the current user associated with the GitHub token, delete unmanaged branches and also delete untracked managed branches with no downstream branch'
       )
diff --git a/completion/git-machete.fish b/completion/git-machete.fish
index 376491a..6640995 100644
--- a/completion/git-machete.fish
+++ b/completion/git-machete.fish
@@ -97,15 +97,16 @@ complete -c git-machete -n "__fish_seen_subcommand_from fork-point; and not __fi
 
 # git machete github
 complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands"                                                                                          -f -a github                                               -d 'Create, check out and manage GitHub PRs while keeping them reflected in git machete'
-complete -c git-machete -n "__fish_seen_subcommand_from github; and not __fish_seen_subcommand_from anno-prs checkout-prs create-pr retarget-pr sync"                     -f -a anno-prs                                             -d 'Annotates the branches based on their corresponding GitHub PR numbers and authors'
-complete -c git-machete -n "__fish_seen_subcommand_from github; and not __fish_seen_subcommand_from anno-prs checkout-prs create-pr retarget-pr sync"                     -x -a checkout-prs                                         -d 'Check out the head branch of the given pull requests (specified by number), also traverse chain of pull requests upwards, adding branches one by one to git-machete and check them out locally'
-complete -c git-machete -n "__fish_seen_subcommand_from github; and not __fish_seen_subcommand_from anno-prs checkout-prs create-pr retarget-pr sync"                     -f -a create-pr                                            -d 'Creates a PR for the current branch, using the upstream (parent) branch as the PR base'
-complete -c git-machete -n "__fish_seen_subcommand_from github; and not __fish_seen_subcommand_from anno-prs checkout-prs create-pr retarget-pr sync"                     -f -a retarget-pr                                          -d 'Sets the base of PR for the current branch to upstream (parent) branch, as seen by git machete (see git machete show up)'
-complete -c git-machete -n "__fish_seen_subcommand_from github; and not __fish_seen_subcommand_from anno-prs checkout-prs create-pr retarget-pr sync"                     -f -a sync                                                 -d 'Synchronize with the remote repository: checkout open PRs for the current user associated with the GitHub token, delete unmanaged branches and also delete untracked managed branches with no downstream branch'
+complete -c git-machete -n "__fish_seen_subcommand_from github; and not __fish_seen_subcommand_from anno-prs checkout-prs create-pr restack-pr retarget-pr sync"          -f -a anno-prs                                             -d 'Annotates the branches based on their corresponding GitHub PR numbers and authors'
+complete -c git-machete -n "__fish_seen_subcommand_from github; and not __fish_seen_subcommand_from anno-prs checkout-prs create-pr restack-pr retarget-pr sync"          -x -a checkout-prs                                         -d 'Check out the head branch of the given pull requests (specified by number), also traverse chain of pull requests upwards, adding branches one by one to git-machete and check them out locally'
+complete -c git-machete -n "__fish_seen_subcommand_from github; and not __fish_seen_subcommand_from anno-prs checkout-prs create-pr restack-pr retarget-pr sync"          -f -a create-pr                                            -d 'Creates a PR for the current branch, using the upstream (parent) branch as the PR base'
+complete -c git-machete -n "__fish_seen_subcommand_from github; and not __fish_seen_subcommand_from anno-prs checkout-prs create-pr restack-pr retarget-pr sync"          -f -a restack-pr                                           -d '(Force-)pushes and retargets the PR, without adding code owners as reviewers in the process'
+complete -c git-machete -n "__fish_seen_subcommand_from github; and not __fish_seen_subcommand_from anno-prs checkout-prs create-pr restack-pr retarget-pr sync"          -f -a retarget-pr                                          -d 'Sets the base of PR for the current branch to upstream (parent) branch, as seen by git machete (see git machete show up)'
+complete -c git-machete -n "__fish_seen_subcommand_from github; and not __fish_seen_subcommand_from anno-prs checkout-prs create-pr restack-pr retarget-pr sync"          -f -a sync                                                 -d 'Synchronize with the remote repository: checkout open PRs for the current user associated with the GitHub token, delete unmanaged branches and also delete untracked managed branches with no downstream branch'
 complete -c git-machete -n "__fish_seen_subcommand_from github; and __fish_seen_subcommand_from checkout-prs; and not __fish_seen_subcommand_from --all"                  -f -l all                                                  -d 'Checkout all open PRs'
 complete -c git-machete -n "__fish_seen_subcommand_from github; and __fish_seen_subcommand_from checkout-prs; and not __fish_seen_subcommand_from --by"                   -x -l by                                                   -d "Checkout someone's open PRs"
 complete -c git-machete -n "__fish_seen_subcommand_from github; and __fish_seen_subcommand_from checkout-prs; and not __fish_seen_subcommand_from --mine"                 -x -l mine                                                 -d 'Checkout open PRs for the current user associated with the GitHub token'
-complete -c git-machete -n "__fish_seen_subcommand_from github; and __fish_seen_subcommand_from create-pr; and not __fish_seen_subcommand_from --draft"                   -f -l draft                                                -d 'Creates the new PR as a draft'
+complete -c git-machete -n "__fish_seen_subcommand_from github; and __fish_seen_subcommand_from create-pr;    and not __fish_seen_subcommand_from --draft"                -f -l draft                                                -d 'Creates the new PR as a draft'
 complete -c git-machete -n "__fish_seen_subcommand_from github; and __fish_seen_subcommand_from retarget-pr; and not __fish_seen_subcommand_from --branch"                -x -l branch -s b       -a '(__machete_managed_branches)'  -d 'Specify the branch for which the associated PR base will be set to its upstream (parent) branch'
 complete -c git-machete -n "__fish_seen_subcommand_from github; and __fish_seen_subcommand_from retarget-pr; and not __fish_seen_subcommand_from --ignore-if-missing"     -f -l ignore-if-missing                                    -d 'Ignore errors and quietly terminate execution if there is no PR opened for current (or specified) branch'
 
diff --git a/docs/man/git-machete.1 b/docs/man/git-machete.1
index bf8561a..04e6fa9 100644
--- a/docs/man/git-machete.1
+++ b/docs/man/git-machete.1
@@ -27,9 +27,9 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
 .in \\n[rst2man-indent\\n[rst2man-indent-level]]u
 ..
-.TH "GIT-MACHETE" "1" "Sep 23, 2023" "" "git-machete"
+.TH "GIT-MACHETE" "1" "Oct 20, 2023" "" "git-machete"
 .SH NAME
-git-machete \- git-machete 3.19.0
+git-machete \- git-machete 3.20.0
 .sp
 git machete is a robust tool that \fBsimplifies your git workflows\fP\&.
 .sp
@@ -279,7 +279,7 @@ $ git machete anno \(aq\(aq
 then clears the annotation for the current branch (or a branch specified with \fB\-b/\-\-branch\fP).
 .sp
 If invoked with \fB\-H\fP or \fB\-\-sync\-github\-prs\fP, annotates the branches based on their corresponding GitHub PR numbers and authors.
-When the current user is \fBnot\fP the owner of the PR associated with that branch, adds \fBrebase=no push=no\fP branch qualifiers used by \fBgit machete traverse\fP,
+When the current user is NOT the owner of the PR associated with that branch, adds \fBrebase=no push=no\fP branch qualifiers used by \fBgit machete traverse\fP,
 so that you don\(aqt rebase or push someone else\(aqs PR by accident (see help for \fI\%traverse\fP).
 Any existing annotations (except branch qualifiers) are overwritten for the branches that have an opened PR; annotations for the other branches remain untouched.
 .sp
@@ -351,7 +351,7 @@ E.g. \fBgit config machete.github.organization VirtusLab; git config machete.git
 .UNINDENT
 .UNINDENT
 .sp
-Note that you do \fBnot\fP need to set all four keys at once.
+Note that you do NOT need to set all four keys at once.
 For example, in a typical usage of GitHub Enterprise, it should be enough to just set \fBmachete.github.domain\fP\&.
 Only \fBmachete.github.organization\fP and \fBmachete.github.repository\fP must be specified together.
 .UNINDENT
@@ -411,7 +411,7 @@ E.g. \fBgit config machete.github.organization VirtusLab; git config machete.git
 .UNINDENT
 .UNINDENT
 .sp
-Note that you do \fBnot\fP need to set all four keys at once.
+Note that you do NOT need to set all four keys at once.
 For example, in a typical usage of GitHub Enterprise, it should be enough to just set \fBmachete.github.domain\fP\&.
 Only \fBmachete.github.organization\fP and \fBmachete.github.repository\fP must be specified together.
 .UNINDENT
@@ -429,7 +429,7 @@ There should be \fBno\fP need for the user to interact with this key directly,
 .INDENT 7.0
 .INDENT 3.5
 To make it easier to select branch name from the \fBstatus\fP output on certain terminals
-(e.g. \fI\%Alacritty\fP), you can add an extra space between └─ and \fBbranch name\fP
+(like \fI\%Alacritty\fP), you can add an extra space between └─ and \fBbranch name\fP
 by setting \fBgit config machete.status.extraSpaceBeforeBranchName true\fP\&.
 .sp
 For example, by default the status is displayed as:
@@ -597,7 +597,7 @@ E.g. \fBgit config machete.github.organization VirtusLab; git config machete.git
 .UNINDENT
 .UNINDENT
 .sp
-Note that you do \fBnot\fP need to set all four keys at once.
+Note that you do NOT need to set all four keys at once.
 For example, in a typical usage of GitHub Enterprise, it should be enough to just set \fBmachete.github.domain\fP\&.
 Only \fBmachete.github.organization\fP and \fBmachete.github.repository\fP must be specified together.
 .UNINDENT
@@ -762,7 +762,7 @@ If the reply was \fBe[dit]\fP, additionally an editor is opened (as in: \fBgit m
 .TP
 .BI \-C\fP,\fB  \-\-checked\-out\-since\fB= <date>
 Only consider branches checked out at least once since the given date.
-<date> can be e.g. \fB2 weeks ago\fP or \fB2020\-06\-01\fP, as in \fBgit log \-\-since=<date>\fP\&.
+\fB<date>\fP can be, for example, \fB2 weeks ago\fP or \fB2020\-06\-01\fP, as in \fBgit log \-\-since=<date>\fP\&.
 If not present, the date is selected automatically so that around 10 branches are included.
 .TP
 .B  \-l\fP,\fB  \-\-list\-commits
@@ -889,7 +889,7 @@ git machete fork\-point \-\-unset\-override [<branch>]
 Note: in all three forms, if no \fB<branch>\fP is specified, the currently checked out branch is assumed.
 The branch in question does not need to occur in the branch layout file.
 .sp
-Without any option, displays full hash of the fork point commit for the \fB<branch>\fP\&.
+Without any option, \fBgit machete fork\-point\fP displays full hash of the fork point commit for the \fB<branch>\fP\&.
 Fork point of the given \fB<branch>\fP is the commit at which the history of the \fB<branch>\fP diverges from history of any other branch.
 .sp
 Fork point is assumed by many \fBgit machete\fP commands as the place where the unique history of the \fB<branch>\fP starts.
@@ -908,27 +908,25 @@ provided to \fBgit diff\fP/\fBlog\fP by \fBgit machete\fP \fBdiff\fP/\fBlog\fP\&
 .UNINDENT
 .sp
 \fBgit machete\fP assumes fork point of \fB<branch>\fP is the most recent commit in the log of \fB<branch>\fP that has NOT been introduced on that very branch,
-but instead occurs on a reflog (see help for \fBgit reflog\fP) of some other, usually chronologically earlier, branch.
+but instead occurs on a reflog (see help for \fBgit reflog\fP) of some other branch.
 This yields a correct result in typical cases, but there are some situations
 (esp. when some local branches have been deleted) where the fork point might not be determined correctly.
 Thus, all rebase\-involving operations (\fBreapply\fP, \fBslide\-out\fP, \fBtraverse\fP and \fBupdate\fP) run \fBgit rebase\fP in the interactive mode by default,
-unless told explicitly not to do so by \fB\-\-no\-interactive\-rebase\fP flag, so that the suggested commit range can be inspected before the rebase commences.
+unless told explicitly not to do so by \fB\-\-no\-interactive\-rebase\fP flag. This way, the suggested commit range can be inspected before the rebase starts.
 Also, \fBreapply\fP, \fBslide\-out\fP, \fBsquash\fP, and \fBupdate\fP allow to specify the fork point explicitly by a command\-line option.
 .sp
 \fBgit machete fork\-point\fP is different (and more powerful) than \fBgit merge\-base \-\-fork\-point\fP,
 since the latter takes into account only the reflog of the one provided upstream branch,
 while the former scans reflogs of all local branches and their remote tracking branches.
-This makes git machete\(aqs \fBfork\-point\fP more resilient to modifications of \fB\&.git/machete\fP \fI\%file\fP where certain branches are re\-attached under new parents (upstreams).
+This makes git machete\(aqs \fBfork\-point\fP more resilient to modifications of \fB\&.git/machete\fP \fI\%file\fP when certain branches are re\-attached under new parents (upstreams).
 .sp
 With \fB\-\-override\-to=<revision>\fP, sets up a fork point override for \fB<branch>\fP\&.
 Fork point for \fB<branch>\fP will be overridden to the provided <revision> (commit) as long as the \fB<branch>\fP still points to (or is descendant of) that commit.
-Even if revision is a symbolic name (e.g. other branch name or \fBHEAD~3)\fP and not explicit commit hash (like \fBa1b2c3ff\fP),
-it\(aqs still resolved to a specific commit hash at the moment the override is set up (and not later when the override is actually used).
 The override data is stored under \fBmachete.overrideForkPoint.<branch>.to\fP git config key.
-Note: the provided fork point <revision> must be an ancestor of the current \fB<branch>\fP commit.
+Note: the provided fork point \fB<revision>\fP must be an ancestor of the current \fB<branch>\fP commit.
 .sp
 With \fB\-\-override\-to\-parent\fP, overrides fork point of the \fB<branch>\fP to the commit currently pointed by \fB<branch>\fP\(aqs parent in the branch dependency tree.
-Note: this will only work if \fB<branch>\fP has a parent at all (i.e. is not a root) and parent of \fB<branch>\fP is an ancestor of current \fB<branch>\fP commit.
+Note: this will only work if \fB<branch>\fP has a parent at all (is not a root branch) and parent of \fB<branch>\fP is an ancestor of current \fB<branch>\fP commit.
 .sp
 With \fB\-\-inferred\fP, displays the commit that \fBgit machete fork\-point\fP infers to be the fork point of \fB<branch>\fP\&.
 If there is NO fork point override for \fB<branch>\fP, this is identical to the output of \fBgit machete fork\-point\fP\&.
@@ -971,12 +969,12 @@ In the above example \fBdevelop\fP and \fBmaster\fP are roots of the tree of bra
 Branches \fBadjust\-reads\-prec\fP, \fBedit\-margin\-not\-allowed\fP and \fBgrep\-errors\-script\fP are direct downstream branches for \fBdevelop\fP\&.
 \fBblock\-cancel\-order\fP is a downstream branch of \fBadjust\-reads\-prec\fP, \fBchange\-table\fP is a downstream branch of \fBblock\-cancel\-order\fP and so on.
 .sp
-Every branch name can be followed (after a single space as a delimiter) by a custom annotation, e.g. \fBPR #234 rebase=no push=no\fP, \fBPR #235 rebase=no\fP or \fBpush=no\fP\&.
-Annotations might contain underlined branch qualifiers (\fBrebase=no\fP, \fBpush=no\fP) that control rebase and push behavior of \fBtraverse\fP (see help for \fI\%traverse\fP).
+Every branch name can be followed (after a single space as a delimiter) by a custom annotation, for example \fBPR #234 rebase=no push=no\fP, \fBPR #235 rebase=no\fP or \fBpush=no\fP\&.
+These annotations might contain branch qualifiers (\fBpush=no\fP, \fBrebase=no\fP, \fBslide\-out=no\fP) that control the behavior of \fBtraverse\fP (see help for \fI\%traverse\fP).
 Also see help for \fI\%anno\fP command.
 .sp
 Tabs or any number of spaces can be used as indentation.
-It\(aqs only important to be consistent wrt. the sequence of characters used for indentation between all lines.
+It\(aqs only important to use indentation characters consistently between all lines.
 .SH GITHUB
 .sp
 \fBUsage:\fP
@@ -1063,7 +1061,7 @@ E.g. \fBgit config machete.github.organization VirtusLab; git config machete.git
 .UNINDENT
 .UNINDENT
 .sp
-Note that you do \fBnot\fP need to set all four keys at once.
+Note that you do NOT need to set all four keys at once.
 For example, in a typical usage of GitHub Enterprise, it should be enough to just set \fBmachete.github.domain\fP\&.
 Only \fBmachete.github.organization\fP and \fBmachete.github.repository\fP must be specified together.
 .UNINDENT
@@ -1076,7 +1074,7 @@ Only \fBmachete.github.organization\fP and \fBmachete.github.repository\fP must
 Annotates the branches based on their corresponding GitHub PR numbers and authors.
 Any existing annotations are overwritten for the branches that have an opened PR; annotations for the other branches remain untouched.
 Equivalent to \fBgit machete anno \-\-sync\-github\-prs\fP\&.
-When the current user is \fBnot\fP the owner of the PR associated with that branch, adds \fBrebase=no push=no\fP branch qualifiers used by \fBgit machete traverse\fP,
+When the current user is NOT the owner of the PR associated with that branch, adds \fBrebase=no push=no\fP branch qualifiers used by \fBgit machete traverse\fP,
 so that you don\(aqt rebase or push someone else\(aqs PR by accident (see help for \fI\%traverse\fP).
 .TP
 .B \fBcheckout\-prs [\-\-all | \-\-by=<github\-login> | \-\-mine | <PR\-number\-1> ... <PR\-number\-N>]\fP:
@@ -1084,7 +1082,7 @@ Check out the head branch of the given pull requests (specified by numbers or by
 also traverse chain of pull requests upwards, adding branches one by one to git\-machete and check them out locally.
 Once the specified pull requests are checked out locally, annotate local branches with corresponding pull request numbers.
 If only one PR has been checked out, then switch the local repository\(aqs HEAD to its head branch.
-When the current user is \fBnot\fP the owner of the PR associated with that branch, adds \fBrebase=no push=no\fP branch qualifiers used by \fBgit machete traverse\fP,
+When the current user is NOT the owner of the PR associated with that branch, adds \fBrebase=no push=no\fP branch qualifiers used by \fBgit machete traverse\fP,
 so that you don\(aqt rebase or push someone else\(aqs PR by accident (see help for \fI\%traverse\fP).
 .sp
 \fBOptions:\fP
@@ -1108,9 +1106,13 @@ Checkout open PRs for the current user associated with the GitHub token.
 Creates a PR for the current branch, using the upstream (parent) branch as the PR base.
 Once the PR is successfully created, annotates the current branch with the new PR\(aqs number.
 .sp
-If \fB\&.git/info/description\fP or \fB\&.github/pull_request_template.md\fP file is present, its contents are used as PR description.
 If \fB\&.git/info/milestone\fP file is present, its contents (a single number \-\-\- milestone id) are used as milestone.
 If \fB\&.git/info/reviewers\fP file is present, its contents (one GitHub login per line) are used to set reviewers.
+If \fB\&.git/info/description\fP or \fB\&.github/pull_request_template.md\fP file is present, its contents are used as PR description.
+.sp
+If the newly\-created PR is stacked atop another PR, the actual PR description posted to GitHub will be prepended with the following header:
+.sp
+\fB# Based on PR #<number of PR for base branch>\fP
 .sp
 \fBOptions:\fP
 .INDENT 7.0
@@ -1119,9 +1121,25 @@ If \fB\&.git/info/reviewers\fP file is present, its contents (one GitHub login p
 Creates the new PR as a draft.
 .UNINDENT
 .TP
+.B \fBrestack\-pr\fP:
+Perform the following sequence of actions:
+1. If the PR for the current branch is ready for review, it gets converted to a draft.
+2. The branch is (force\-)pushed into remote.
+3. The PR is retargeted to its upstream (parent) branch, as in \fBretarget\-pr\fP\&.
+4. If the PR has been converted to draft in step 1, it\(aqs reverted to ready for review state.
+.sp
+The drafting/undrafting is useful in case the GitHub repository has set up \fI\%CODEOWNERS\fP\&.
+Draft PRs don\(aqt get code owners automatically added as reviewers.
+.TP
 .B \fBretarget\-pr [\-b|\-\-branch=<branch>] [\-\-ignore\-if\-missing]\fP:
 Sets the base of the current (or specified) branch\(aqs PR to upstream (parent) branch, as seen by git machete (see \fBgit machete show up\fP).
 .sp
+If after changing the base, the PR ends up stacked atop another PR, the PR description at GitHub will be prepended with the following header:
+.sp
+\fB# Based on PR #<number of PR for base branch>\fP
+.sp
+This header will be updated or removed accordingly with the subsequent runs of \fBretarget\-pr\fP\&.
+.sp
 \fBOptions:\fP
 .INDENT 7.0
 .TP
@@ -1261,8 +1279,8 @@ zero or more times during \fBtraverse\fP (every time a slide\-out operation is c
 .UNINDENT
 .UNINDENT
 .sp
-If the hook returns a non\-zero exit code, then an error is raised and the execution of the command is aborted,
-i.e. \fBslide\-out\fP won\(aqt attempt rebase of the new downstream branches and \fBtraverse\fP won\(aqt continue the traversal.
+If the hook returns a non\-zero exit code, then an error is raised and the execution of the command is aborted \-\-\-
+\fBslide\-out\fP won\(aqt attempt rebase of the new downstream branches and \fBtraverse\fP won\(aqt continue the traversal.
 In case of \fBadvance\fP there is no difference (other than exit code of the entire \fBadvance\fP command being non\-zero),
 since slide\-out is the last operation that happens within \fBadvance\fP\&.
 .sp
@@ -1301,7 +1319,7 @@ The standard output of this hook is displayed at the end of the line, after bran
 are ignored, and printing the status continues as usual.
 .sp
 Note: the hook is always invoked with \fBASCII_ONLY\fP variable passed into the environment.
-If \fBstatus\fP runs in ASCII\-only mode (i.e. if \fB\-\-color=auto\fP and stdout is not a terminal, or if \fB\-\-color=never\fP),
+If \fBstatus\fP runs in ASCII\-only mode (if \fB\-\-color=auto\fP and stdout is NOT a terminal, or if \fB\-\-color=never\fP),
 then \fBASCII_ONLY=true\fP, otherwise \fBASCII_ONLY=false\fP\&.
 .UNINDENT
 .sp
@@ -1321,7 +1339,7 @@ git machete is\-managed [<branch>]
 .UNINDENT
 .UNINDENT
 .sp
-Returns with zero exit code if the given branch (or current branch, if none specified) is managed by git machete (i.e. listed in .git/machete).
+Returns with zero exit code if the given branch (or current branch, if none specified) is \fBmanaged\fP by git machete (that is, listed in .git/machete).
 .sp
 Returns with a non\-zero exit code in case:
 .INDENT 0.0
@@ -1366,7 +1384,7 @@ Lists all branches that fall into one of the specified categories:
 \fBslidable\fP: all managed branches that have an upstream and can be slid out with \fI\%slide\-out\fP command
 .IP \(bu 2
 \fBslidable\-after <branch>\fP: the downstream branch of the <branch>, if it exists and is the only downstream of <branch>
-(i.e. the one that can be slid out immediately following <branch>),
+(and thus can be slid out immediately following <branch>),
 .IP \(bu 2
 \fBunmanaged\fP: all local branches that don\(aqt appear in the branch layout file,
 .IP \(bu 2
@@ -1491,24 +1509,10 @@ git machete slide\-out [\-d|\-\-down\-fork\-point=<down\-fork\-point\-commit>] [
 .UNINDENT
 .UNINDENT
 .sp
-Removes the given branch (or multiple branches) from the branch layout.  If no branch has been specified current branch is assumed as the only branch.
+Removes the given branch (or multiple branches) from the branch layout. If no branch has been specified, current branch is removed.
 Then synchronizes the downstream (child) branches of the last specified branch on the top of the upstream (parent) branch of the first specified branch.
 Sync is performed either by rebase (default) or by merge (if \fB\-\-merge\fP option passed).
 .sp
-The most common use is to slide out a single branch whose upstream was a \fBdevelop\fP/\fBmaster\fP branch and that has been recently merged.
-.sp
-Since this tool is designed to perform only one single rebase/merge at the end, provided branches must form a chain, i.e. all of the following conditions must be met:
-.INDENT 0.0
-.INDENT 3.5
-.INDENT 0.0
-.IP \(bu 2
-for i=1..N\-1, (i+1)\-th branch must be the only downstream (child) branch of the i\-th branch,
-.IP \(bu 2
-all provided branches must have an upstream branch (so, in other words, roots of branch dependency tree cannot be slid out).
-.UNINDENT
-.UNINDENT
-.UNINDENT
-.sp
 For example, let\(aqs assume the following dependency tree:
 .INDENT 0.0
 .INDENT 3.5
@@ -1526,7 +1530,6 @@ develop
 .UNINDENT
 .UNINDENT
 .sp
-And now let\(aqs assume that \fBadjust\-reads\-prec\fP and later \fBblock\-cancel\-order\fP were merged to develop.
 After running \fBgit machete slide\-out adjust\-reads\-prec block\-cancel\-order\fP the tree will be reduced to:
 .INDENT 0.0
 .INDENT 3.5
@@ -1544,6 +1547,20 @@ develop
 .sp
 and \fBchange\-table\fP and \fBadd\-notification\fP will be rebased onto develop (fork point for this rebase is configurable, see \fB\-d\fP option below).
 .sp
+The most common use is to slide out a single branch whose upstream was a \fBdevelop\fP/\fBmaster\fP branch and that has been recently merged.
+.sp
+The provided branches must form a chain \-\-\- all of the following conditions must be met:
+.INDENT 0.0
+.INDENT 3.5
+.INDENT 0.0
+.IP \(bu 2
+for i=1..N\-1, (i+1)\-th branch must be the only downstream (child) branch of the i\-th branch,
+.IP \(bu 2
+all provided branches must have an upstream branch (so, in other words, roots of branch dependency tree cannot be slid out).
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
 Note: This command doesn\(aqt delete any branches from git, just removes them from the tree of branch dependencies.
 .sp
 \fBOptions:\fP
@@ -1567,7 +1584,7 @@ If updating by merge, equivalent to \fB\-\-no\-edit\-merge\fP\&.
 .TP
 .B  \-\-no\-edit\-merge
 If updating by merge, skip opening the editor for merge commit message while doing
-\fBgit merge\fP (i.e. pass \fB\-\-no\-edit\fP flag to underlying \fBgit merge\fP).
+\fBgit merge\fP (that is, pass \fB\-\-no\-edit\fP flag to the underlying \fBgit merge\fP).
 Not allowed if updating by rebase.
 .TP
 .B  \-\-no\-interactive\-rebase
@@ -1600,11 +1617,13 @@ Squashes the commits belonging uniquely to the current branch into a single comm
 The chunk of the history to be squashed starts at the automatically computed fork point of the current branch by default,
 but can also be set explicitly by \fB\-\-fork\-point\fP\&.
 See help for \fI\%fork\-point\fP for more details on meaning of the \fIfork point\fP\&.
-The message for the squashed is taken from the earliest squashed commit, i.e. the commit directly following the fork point.
+The message for the resulting commit is taken from the earliest squashed commit (the commit directly following the fork point).
 .sp
-Note: the current squashed branch does not need to occur in the branch layout file.
+To simply squash the most recent N commits, use \fB\-\-fork\-point=HEAD~<N>\fP,
+for example \fBgit machete squash \-\-fork\-point=HEAD~3\fP\&.
 .sp
-Tip: \fBsquash\fP does NOT run \fBgit rebase\fP under the hood. For more complex scenarios that require rewriting the history of current branch, see \fBreapply\fP and \fBupdate\fP\&.
+Tip: \fBsquash\fP does NOT run \fBgit rebase\fP under the hood.
+For more complex scenarios that require rewriting the history of current branch, see \fBreapply\fP and \fBupdate\fP\&.
 .sp
 \fBOptions:\fP
 .INDENT 0.0
@@ -1636,15 +1655,15 @@ Apart from simply ASCII\-formatting the branch layout file, this also:
 colors the edges between upstream (parent) and downstream (children) branches:
 .INDENT 2.0
 .IP \(bu 2
-red edge means that the downstream branch tip is not a direct descendant of the upstream branch tip,
+red edge means \fInot in sync\fP\&. The downstream branch is NOT a direct descendant of the upstream branch.
 .IP \(bu 2
-yellow edge means that the downstream branch tip is a direct descendant of the upstream branch tip,
-but the \fI\%fork point\fP of the downstream branch is not equal to the upstream branch tip,
+yellow edge means \fIin sync but fork point off\fP\&. The downstream branch is a direct descendant of the upstream branch,
+but the \fI\%fork point\fP of the downstream branch is NOT equal to the upstream branch.
 .IP \(bu 2
-green edge means that the downstream branch tip is a direct descendant of the upstream branch tip
-and the fork point of the downstream branch is equal to the upstream branch tip,
+green edge means \fIin sync\fP\&. The downstream branch is a direct descendant of the upstream branch
+and the fork point of the downstream branch is equal to the upstream branch.
 .IP \(bu 2
-grey/dimmed edge means that the downstream branch has been merged to the upstream branch,
+grey/dimmed edge means \fImerged\fP\&. The downstream branch has been merged to the upstream branch,
 detected by commit equivalency (default), or by strict detection of merge commits (if \fB\-\-no\-detect\-squash\-merges\fP passed).
 .UNINDENT
 .IP \(bu 2
@@ -1652,7 +1671,7 @@ prints (\fBuntracked\fP/\fBahead of <remote>\fP/\fBbehind <remote>\fP/\fBdiverge
 is not in sync with its remote counterpart;
 .IP \(bu 2
 displays the custom annotations (see help for \fI\%format\fP and \fI\%anno\fP) next to each branch, if present. Annotations might contain underlined branch
-qualifiers (\fBrebase=no\fP, \fBpush=no\fP) that control rebase and push behavior of \fBtraverse\fP (see help for \fI\%traverse\fP);
+qualifiers (\fBpush=no\fP, \fBrebase=no\fP, \fBslide\-out=no\fP) that control rebase and push behavior of \fBtraverse\fP (see help for \fI\%traverse\fP);
 .IP \(bu 2
 displays the output of \fBmachete\-status\-branch hook\fP (see help for \fI\%hooks\fP), if present;
 .IP \(bu 2
@@ -1664,14 +1683,15 @@ optionally lists commits introduced on each branch if \fB\-l/\-\-list\-commits\f
 Name of the currently checked\-out branch is underlined (or shown in blue on terminals that don\(aqt support underline).
 .sp
 In case of yellow edge, use \fB\-l\fP or \fB\-L\fP flag to show the exact location of the inferred fork point
-(which indicates e.g. what range of commits is going to be rebased when the branch is updated).
+(which indicates, among other things, what range of commits is going to be rebased when the branch is updated).
 The inferred fork point can be always overridden manually, see help for \fI\%fork\-point\fP\&.
 .sp
 Grey/dimmed edge suggests that the downstream branch can be slid out (see help for \fI\%slide\-out\fP and \fI\%traverse\fP).
 .sp
-Using colors can be disabled with a \fB\-\-color\fP flag set to \fBnever\fP\&.
-With \fB\-\-color=always\fP, git machete always emits colors and with \fB\-\-color=auto\fP, it emits colors only when standard output is connected to a terminal.
-\fB\-\-color=auto\fP is the default. When colors are disabled, relation between branches is represented in the following way (not including the hash\-comments):
+Use of colors can be disabled with a \fB\-\-color\fP flag set to \fBnever\fP\&.
+With \fB\-\-color=always\fP, git machete always emits colors.
+With \fB\-\-color=auto\fP (the default), it emits colors only when standard output is connected to a terminal.
+When colors are disabled, relation between branches is represented in the following way (not including the hash\-comments):
 .INDENT 0.0
 .INDENT 3.5
 .sp
@@ -1695,7 +1715,7 @@ m\-<branch4>     # grey (merged to parent)
 .INDENT 0.0
 .INDENT 3.5
 To make it easier to select branch name from the \fBstatus\fP output on certain terminals
-(e.g. \fI\%Alacritty\fP), you can add an extra space between └─ and \fBbranch name\fP
+(like \fI\%Alacritty\fP), you can add an extra space between └─ and \fBbranch name\fP
 by setting \fBgit config machete.status.extraSpaceBeforeBranchName true\fP\&.
 .sp
 For example, by default the status is displayed as:
@@ -1736,7 +1756,7 @@ develop
 .INDENT 0.0
 .TP
 .BI \-\-color\fB= WHEN
-Colorize the output; WHEN can be \fBalways\fP, \fBauto\fP (default; i.e. only if stdout is a terminal), or \fBnever\fP\&.
+Colorize the output; WHEN can be \fBalways\fP, \fBauto\fP (default: colorize only if stdout is a terminal), or \fBnever\fP\&.
 .TP
 .B  \-l\fP,\fB  \-\-list\-commits
 Additionally list the commits introduced on each branch.
@@ -1756,7 +1776,7 @@ when detecting if a branch is merged into its upstream (parent).
 .INDENT 7.0
 .INDENT 3.5
 To make it easier to select branch name from the \fBstatus\fP output on certain terminals
-(e.g. \fI\%Alacritty\fP), you can add an extra space between └─ and \fBbranch name\fP
+(like \fI\%Alacritty\fP), you can add an extra space between └─ and \fBbranch name\fP
 by setting \fBgit config machete.status.extraSpaceBeforeBranchName true\fP\&.
 .UNINDENT
 .UNINDENT
@@ -1778,8 +1798,8 @@ git machete t[raverse] [\-F|\-\-fetch] [\-l|\-\-list\-commits] [\-M|\-\-merge]
 .UNINDENT
 .UNINDENT
 .sp
-Traverses the branch tree in pre\-order (i.e. simply in the order as they occur in the branch layout file).
-By default \fBtraverse\fP starts from the current branch.
+Traverses the branches in the order as they occur in branch layout file.
+By default, \fBtraverse\fP starts from the current branch.
 This behavior can, however, be customized using options: \fB\-\-start\-from=\fP, \fB\-\-whole\fP or \fB\-w\fP, \fB\-W\fP\&.
 .sp
 For each branch, the command:
@@ -1792,33 +1812,33 @@ detects if the branch is merged (grey edge) to its parent (aka upstream):
 .IP \(bu 2
 by commit equivalency (default), or by strict detection of merge commits (if \fB\-\-no\-detect\-squash\-merges\fP passed),
 .IP \(bu 2
-if so, asks the user whether to slide out the branch from the dependency tree (typically branches are no longer needed after they\(aqre merged);
+if so, asks the user whether to \fBslide out\fP the branch from the dependency tree (typically branches are no longer needed after they\(aqre merged);
 .UNINDENT
 .IP \(bu 2
 otherwise, if the branch has a red or yellow edge to its parent/upstream (see help for \fI\%status\fP):
 .INDENT 2.0
 .IP \(bu 2
-asks the user whether to rebase (default) or merge (if \fB\-\-merge\fP passed) the branch onto into its upstream branch
-\-\-\- equivalent to \fBgit machete update\fP with no \fB\-\-fork\-point\fP option passed;
+asks the user whether to \fBrebase\fP (default) or merge (if \fB\-\-merge\fP passed) the branch onto into its upstream branch
+\-\-\- equivalent to \fBgit machete update\fP;
 .UNINDENT
 .IP \(bu 2
 if the branch is not tracked on a remote, is ahead of its remote counterpart, or diverged from the counterpart &
 has newer head commit than the counterpart:
 .INDENT 2.0
 .IP \(bu 2
-asks the user whether to push the branch (possibly with \fB\-\-force\-with\-lease\fP if the branches diverged);
+asks the user whether to \fBpush\fP the branch (possibly with \fB\-\-force\-with\-lease\fP if the branches diverged);
 .UNINDENT
 .IP \(bu 2
 otherwise, if the branch diverged from the remote counterpart & has older head commit than the counterpart:
 .INDENT 2.0
 .IP \(bu 2
-asks the user whether to \fBgit reset \-\-keep\fP the branch to its remote counterpart
+asks the user whether to \fBreset\fP (\fBgit reset \-\-keep\fP) the branch to its remote counterpart
 .UNINDENT
 .IP \(bu 2
 otherwise, if the branch is behind its remote counterpart:
 .INDENT 2.0
 .IP \(bu 2
-asks the user whether to pull the branch;
+asks the user whether to \fBpull\fP the branch;
 .UNINDENT
 .IP \(bu 2
 and finally, if any of the above operations has been successfully completed:
@@ -1830,18 +1850,18 @@ prints the updated \fBstatus\fP\&.
 .UNINDENT
 .UNINDENT
 .sp
-By default \fBtraverse\fP asks if the branch should be pushed, this behavior can, however, be changed with the \fBmachete.traverse.push\fP configuration key.
+By default \fBtraverse\fP asks if the branch should be pushed. This behavior can, however, be changed with the \fBmachete.traverse.push\fP configuration key.
 It can also be customized using options: \fB\-\-[no\-]push\fP or \fB\-\-[no\-]push\-untracked\fP \-\-\- the order of the flags defines their precedence over each other
 (the one on the right overriding the ones on the left). More on them in the \fBOptions\fP section below.
 .sp
 If the traverse flow is stopped (typically due to merge/rebase conflicts), just run \fBgit machete traverse\fP after the merge/rebase is finished.
-It will pick up the walk from the current branch (unless \fB\-\-start\-from=\fP or \fB\-w\fP etc. is passed).
-Unlike with e.g. \fBgit rebase\fP, there is no special \fB\-\-continue\fP flag, as \fBtraverse\fP is stateless
-(doesn\(aqt keep a state of its own like \fBgit rebase\fP does in \fB\&.git/rebase\-apply/\fP).
+It will pick up the walk from the current branch.
+Unlike with \fBgit rebase\fP or \fBgit cherry\-pick\fP, there is no special \fB\-\-continue\fP flag, as \fBtraverse\fP is stateless.
+\fBtraverse\fP doesn\(aqt keep a state of its own like \fBgit rebase\fP does in \fB\&.git/rebase\-apply/\fP\&.
 .sp
 The rebase, push and slide\-out behaviors of \fBtraverse\fP can also be customized for each branch separately using \fIbranch qualifiers\fP\&.
-There are \fBrebase=no\fP, \fBpush=no\fP and \fBslide\-out=no\fP qualifiers that can be used to opt out of default behavior (rebasing, pushing and sliding the branch out of the dependency tree).
-The qualifier can appear anywhere in the annotation but needs to be separated by a whitespace from any other character, e.g. \fBsome_annotation_text rebase=no push=no slide\-out=no\fP\&.
+There are \fBpush=no\fP, \fBrebase=no\fP and \fBslide\-out=no\fP qualifiers that can be used to opt out of default behavior (rebasing, pushing and sliding the branch out).
+The qualifier can appear anywhere in the annotation, but needs to be separated by a whitespace from any other character, as in: \fBsome_annotation_text rebase=no push=no slide\-out=no\fP\&.
 Qualifiers can only be overwritten by manually editing \fB\&.git/machete\fP file or modifying it with \fBgit machete e[dit]\fP, or by updating annotations with \fBgit machete anno\fP\&.
 Example machete file with branch qualifiers:
 .INDENT 0.0
@@ -1860,7 +1880,7 @@ master
 .UNINDENT
 .sp
 Operations like \fBgit machete github anno\-prs\fP and \fBgit machete github checkout\-prs\fP add \fBrebase=no push=no\fP branch qualifiers
-when the current user is \fBnot\fP the owner of the PR associated with that branch. \fBgit machete discover\fP doesn\(aqt modify the qualifiers.
+when the current user is NOT the owner of the PR associated with that branch.
 .sp
 \fBOptions:\fP
 .INDENT 0.0
@@ -1883,7 +1903,7 @@ when detecting if a branch is merged into its upstream (parent).
 .TP
 .B  \-\-no\-edit\-merge
 If updating by merge, skip opening the editor for merge commit message while doing \fBgit merge\fP
-(i.e. pass \fB\-\-no\-edit\fP flag to the underlying \fBgit merge\fP). Not allowed if updating by rebase.
+(that is, pass \fB\-\-no\-edit\fP flag to the underlying \fBgit merge\fP). Not allowed if updating by rebase.
 .TP
 .B  \-\-no\-interactive\-rebase
 If updating by rebase, run \fBgit rebase\fP in non\-interactive mode (without \fB\-i/\-\-interactive\fP flag).
@@ -1986,7 +2006,7 @@ If updating by merge, equivalent to \fB\-\-no\-edit\-merge\fP\&.
 .TP
 .B  \-\-no\-edit\-merge
 If updating by merge, skip opening the editor for merge commit message while doing \fBgit merge\fP
-(i.e. pass \fB\-\-no\-edit\fP flag to underlying \fBgit merge\fP). Not allowed if updating by rebase.
+(that is, pass \fB\-\-no\-edit\fP flag to underlying \fBgit merge\fP). Not allowed if updating by rebase.
 .TP
 .B  \-\-no\-interactive\-rebase
 If updating by rebase, run \fBgit rebase\fP in non\-interactive mode (without \fB\-i/\-\-interactive\fP flag).
diff --git a/docs/source/cli/anno.rst b/docs/source/cli/anno.rst
index 1e57964..5fdd882 100644
--- a/docs/source/cli/anno.rst
+++ b/docs/source/cli/anno.rst
@@ -21,7 +21,7 @@ If invoked with a single empty string <annotation text>, like:
 then clears the annotation for the current branch (or a branch specified with ``-b/--branch``).
 
 If invoked with ``-H`` or ``--sync-github-prs``, annotates the branches based on their corresponding GitHub PR numbers and authors.
-When the current user is **not** the owner of the PR associated with that branch, adds ``rebase=no push=no`` branch qualifiers used by ``git machete traverse``,
+When the current user is NOT the owner of the PR associated with that branch, adds ``rebase=no push=no`` branch qualifiers used by ``git machete traverse``,
 so that you don't rebase or push someone else's PR by accident (see help for :ref:`traverse`).
 Any existing annotations (except branch qualifiers) are overwritten for the branches that have an opened PR; annotations for the other branches remain untouched.
 
diff --git a/docs/source/cli/discover.rst b/docs/source/cli/discover.rst
index ca6c009..6d86174 100644
--- a/docs/source/cli/discover.rst
+++ b/docs/source/cli/discover.rst
@@ -18,7 +18,7 @@ If the reply was ``e[dit]``, additionally an editor is opened (as in: ``git mach
 **Options:**
 
 -C, --checked-out-since=<date>   Only consider branches checked out at least once since the given date.
-                                 <date> can be e.g. ``2 weeks ago`` or ``2020-06-01``, as in ``git log --since=<date>``.
+                                 ``<date>`` can be, for example, ``2 weeks ago`` or ``2020-06-01``, as in ``git log --since=<date>``.
                                  If not present, the date is selected automatically so that around 10 branches are included.
 
 -l, --list-commits               When printing the discovered tree, additionally lists the messages of commits introduced on each branch
diff --git a/docs/source/cli/fork-point.rst b/docs/source/cli/fork-point.rst
index 22926bd..1407359 100644
--- a/docs/source/cli/fork-point.rst
+++ b/docs/source/cli/fork-point.rst
@@ -23,7 +23,7 @@ Note: in all three forms, if no ``<branch>`` is specified, the currently checked
 The branch in question does not need to occur in the branch layout file.
 
 
-Without any option, displays full hash of the fork point commit for the ``<branch>``.
+Without any option, ``git machete fork-point`` displays full hash of the fork point commit for the ``<branch>``.
 Fork point of the given ``<branch>`` is the commit at which the history of the ``<branch>`` diverges from history of any other branch.
 
 Fork point is assumed by many ``git machete`` commands as the place where the unique history of the ``<branch>`` starts.
@@ -34,28 +34,26 @@ The range of commits between the fork point and the tip of the given branch is,
     * provided to ``git diff``/``log`` by ``git machete`` ``diff``/``log``.
 
 ``git machete`` assumes fork point of ``<branch>`` is the most recent commit in the log of ``<branch>`` that has NOT been introduced on that very branch,
-but instead occurs on a reflog (see help for ``git reflog``) of some other, usually chronologically earlier, branch.
+but instead occurs on a reflog (see help for ``git reflog``) of some other branch.
 This yields a correct result in typical cases, but there are some situations
 (esp. when some local branches have been deleted) where the fork point might not be determined correctly.
 Thus, all rebase-involving operations (``reapply``, ``slide-out``, ``traverse`` and ``update``) run ``git rebase`` in the interactive mode by default,
-unless told explicitly not to do so by ``--no-interactive-rebase`` flag, so that the suggested commit range can be inspected before the rebase commences.
+unless told explicitly not to do so by ``--no-interactive-rebase`` flag. This way, the suggested commit range can be inspected before the rebase starts.
 Also, ``reapply``, ``slide-out``, ``squash``, and ``update`` allow to specify the fork point explicitly by a command-line option.
 
 ``git machete fork-point`` is different (and more powerful) than ``git merge-base --fork-point``,
 since the latter takes into account only the reflog of the one provided upstream branch,
 while the former scans reflogs of all local branches and their remote tracking branches.
-This makes git machete's ``fork-point`` more resilient to modifications of ``.git/machete`` :ref:`file` where certain branches are re-attached under new parents (upstreams).
+This makes git machete's ``fork-point`` more resilient to modifications of ``.git/machete`` :ref:`file` when certain branches are re-attached under new parents (upstreams).
 
 
 With ``--override-to=<revision>``, sets up a fork point override for ``<branch>``.
 Fork point for ``<branch>`` will be overridden to the provided <revision> (commit) as long as the ``<branch>`` still points to (or is descendant of) that commit.
-Even if revision is a symbolic name (e.g. other branch name or ``HEAD~3)`` and not explicit commit hash (like ``a1b2c3ff``),
-it's still resolved to a specific commit hash at the moment the override is set up (and not later when the override is actually used).
 The override data is stored under ``machete.overrideForkPoint.<branch>.to`` git config key.
-Note: the provided fork point <revision> must be an ancestor of the current ``<branch>`` commit.
+Note: the provided fork point ``<revision>`` must be an ancestor of the current ``<branch>`` commit.
 
 With ``--override-to-parent``, overrides fork point of the ``<branch>`` to the commit currently pointed by ``<branch>``'s parent in the branch dependency tree.
-Note: this will only work if ``<branch>`` has a parent at all (i.e. is not a root) and parent of ``<branch>`` is an ancestor of current ``<branch>`` commit.
+Note: this will only work if ``<branch>`` has a parent at all (is not a root branch) and parent of ``<branch>`` is an ancestor of current ``<branch>`` commit.
 
 With ``--inferred``, displays the commit that ``git machete fork-point`` infers to be the fork point of ``<branch>``.
 If there is NO fork point override for ``<branch>``, this is identical to the output of ``git machete fork-point``.
diff --git a/docs/source/cli/format.rst b/docs/source/cli/format.rst
index aff39bb..2a7ff77 100644
--- a/docs/source/cli/format.rst
+++ b/docs/source/cli/format.rst
@@ -23,9 +23,9 @@ In the above example ``develop`` and ``master`` are roots of the tree of branch
 Branches ``adjust-reads-prec``, ``edit-margin-not-allowed`` and ``grep-errors-script`` are direct downstream branches for ``develop``.
 ``block-cancel-order`` is a downstream branch of ``adjust-reads-prec``, ``change-table`` is a downstream branch of ``block-cancel-order`` and so on.
 
-Every branch name can be followed (after a single space as a delimiter) by a custom annotation, e.g. ``PR #234 rebase=no push=no``, ``PR #235 rebase=no`` or ``push=no``.
-Annotations might contain underlined branch qualifiers (``rebase=no``, ``push=no``) that control rebase and push behavior of ``traverse`` (see help for :ref:`traverse`).
+Every branch name can be followed (after a single space as a delimiter) by a custom annotation, for example ``PR #234 rebase=no push=no``, ``PR #235 rebase=no`` or ``push=no``.
+These annotations might contain branch qualifiers (``push=no``, ``rebase=no``, ``slide-out=no``) that control the behavior of ``traverse`` (see help for :ref:`traverse`).
 Also see help for :ref:`anno` command.
 
 Tabs or any number of spaces can be used as indentation.
-It's only important to be consistent wrt. the sequence of characters used for indentation between all lines.
+It's only important to use indentation characters consistently between all lines.
diff --git a/docs/source/cli/github.rst b/docs/source/cli/github.rst
index c418d53..1e61a80 100644
--- a/docs/source/cli/github.rst
+++ b/docs/source/cli/github.rst
@@ -21,7 +21,7 @@ Creates, checks out and manages GitHub PRs while keeping them reflected in branc
     Annotates the branches based on their corresponding GitHub PR numbers and authors.
     Any existing annotations are overwritten for the branches that have an opened PR; annotations for the other branches remain untouched.
     Equivalent to ``git machete anno --sync-github-prs``.
-    When the current user is **not** the owner of the PR associated with that branch, adds ``rebase=no push=no`` branch qualifiers used by ``git machete traverse``,
+    When the current user is NOT the owner of the PR associated with that branch, adds ``rebase=no push=no`` branch qualifiers used by ``git machete traverse``,
     so that you don't rebase or push someone else's PR by accident (see help for :ref:`traverse`).
 
 
@@ -30,7 +30,7 @@ Creates, checks out and manages GitHub PRs while keeping them reflected in branc
     also traverse chain of pull requests upwards, adding branches one by one to git-machete and check them out locally.
     Once the specified pull requests are checked out locally, annotate local branches with corresponding pull request numbers.
     If only one PR has been checked out, then switch the local repository's HEAD to its head branch.
-    When the current user is **not** the owner of the PR associated with that branch, adds ``rebase=no push=no`` branch qualifiers used by ``git machete traverse``,
+    When the current user is NOT the owner of the PR associated with that branch, adds ``rebase=no push=no`` branch qualifiers used by ``git machete traverse``,
     so that you don't rebase or push someone else's PR by accident (see help for :ref:`traverse`).
 
     **Options:**
@@ -49,17 +49,37 @@ Creates, checks out and manages GitHub PRs while keeping them reflected in branc
     Creates a PR for the current branch, using the upstream (parent) branch as the PR base.
     Once the PR is successfully created, annotates the current branch with the new PR's number.
 
-    If ``.git/info/description`` or ``.github/pull_request_template.md`` file is present, its contents are used as PR description.
     If ``.git/info/milestone`` file is present, its contents (a single number --- milestone id) are used as milestone.
     If ``.git/info/reviewers`` file is present, its contents (one GitHub login per line) are used to set reviewers.
+    If ``.git/info/description`` or ``.github/pull_request_template.md`` file is present, its contents are used as PR description.
+
+    If the newly-created PR is stacked atop another PR, the actual PR description posted to GitHub will be prepended with the following header:
+
+    ``# Based on PR #<number of PR for base branch>``
 
     **Options:**
 
     --draft    Creates the new PR as a draft.
 
+``restack-pr``:
+    Perform the following sequence of actions:
+    1. If the PR for the current branch is ready for review, it gets converted to a draft.
+    2. The branch is (force-)pushed into remote.
+    3. The PR is retargeted to its upstream (parent) branch, as in ``retarget-pr``.
+    4. If the PR has been converted to draft in step 1, it's reverted to ready for review state.
+
+    The drafting/undrafting is useful in case the GitHub repository has set up `CODEOWNERS <https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners>`_.
+    Draft PRs don't get code owners automatically added as reviewers.
+
 ``retarget-pr [-b|--branch=<branch>] [--ignore-if-missing]``:
     Sets the base of the current (or specified) branch's PR to upstream (parent) branch, as seen by git machete (see ``git machete show up``).
 
+    If after changing the base, the PR ends up stacked atop another PR, the PR description at GitHub will be prepended with the following header:
+
+    ``# Based on PR #<number of PR for base branch>``
+
+    This header will be updated or removed accordingly with the subsequent runs of ``retarget-pr``.
+
     **Options:**
 
     -b, --branch=<branch>     Specify the branch for which the associated PR base will be set to its upstream (parent) branch. The current branch is used if the option is absent.
diff --git a/docs/source/cli/hooks.rst b/docs/source/cli/hooks.rst
index cecf04b..e45fc20 100644
--- a/docs/source/cli/hooks.rst
+++ b/docs/source/cli/hooks.rst
@@ -29,8 +29,8 @@ Note: ``hooks`` is not a command as such, just a help topic (there is no ``git m
         * exactly once during a ``slide-out`` execution (even if multiple branches are slid out),
         * zero or more times during ``traverse`` (every time a slide-out operation is confirmed).
 
-    If the hook returns a non-zero exit code, then an error is raised and the execution of the command is aborted,
-    i.e. ``slide-out`` won't attempt rebase of the new downstream branches and ``traverse`` won't continue the traversal.
+    If the hook returns a non-zero exit code, then an error is raised and the execution of the command is aborted ---
+    ``slide-out`` won't attempt rebase of the new downstream branches and ``traverse`` won't continue the traversal.
     In case of ``advance`` there is no difference (other than exit code of the entire ``advance`` command being non-zero),
     since slide-out is the last operation that happens within ``advance``.
 
@@ -61,7 +61,7 @@ Note: ``hooks`` is not a command as such, just a help topic (there is no ``git m
     are ignored, and printing the status continues as usual.
 
     Note: the hook is always invoked with ``ASCII_ONLY`` variable passed into the environment.
-    If ``status`` runs in ASCII-only mode (i.e. if ``--color=auto`` and stdout is not a terminal, or if ``--color=never``),
+    If ``status`` runs in ASCII-only mode (if ``--color=auto`` and stdout is NOT a terminal, or if ``--color=never``),
     then ``ASCII_ONLY=true``, otherwise ``ASCII_ONLY=false``.
 
 Please see `hook_samples <https://github.com/VirtusLab/git-machete/tree/master/hook_samples>`_ directory in git-machete project for examples.
diff --git a/docs/source/cli/is-managed.rst b/docs/source/cli/is-managed.rst
index 7573743..a682a47 100644
--- a/docs/source/cli/is-managed.rst
+++ b/docs/source/cli/is-managed.rst
@@ -8,7 +8,7 @@ is-managed
 
     git machete is-managed [<branch>]
 
-Returns with zero exit code if the given branch (or current branch, if none specified) is managed by git machete (i.e. listed in .git/machete).
+Returns with zero exit code if the given branch (or current branch, if none specified) is **managed** by git machete (that is, listed in .git/machete).
 
 Returns with a non-zero exit code in case:
 
diff --git a/docs/source/cli/list.rst b/docs/source/cli/list.rst
index 00a4e72..3c2753c 100644
--- a/docs/source/cli/list.rst
+++ b/docs/source/cli/list.rst
@@ -17,7 +17,7 @@ Lists all branches that fall into one of the specified categories:
     * ``managed``: all branches that appear in the branch layout file,
     * ``slidable``: all managed branches that have an upstream and can be slid out with :ref:`slide-out` command
     * ``slidable-after <branch>``: the downstream branch of the <branch>, if it exists and is the only downstream of <branch>
-      (i.e. the one that can be slid out immediately following <branch>),
+      (and thus can be slid out immediately following <branch>),
     * ``unmanaged``: all local branches that don't appear in the branch layout file,
     * ``with-overridden-fork-point``: all local branches that have a :ref:`fork point<fork-point>` override set up
       (even if this override does not affect the location of their fork point anymore).
diff --git a/docs/source/cli/slide-out.rst b/docs/source/cli/slide-out.rst
index fbefda5..4238978 100644
--- a/docs/source/cli/slide-out.rst
+++ b/docs/source/cli/slide-out.rst
@@ -8,17 +8,10 @@ slide-out
 
     git machete slide-out [-d|--down-fork-point=<down-fork-point-commit>] [--delete] [-M|--merge] [-n|--no-edit-merge|--no-interactive-rebase] [<branch> [<branch> [<branch> ...]]]
 
-Removes the given branch (or multiple branches) from the branch layout.  If no branch has been specified current branch is assumed as the only branch.
+Removes the given branch (or multiple branches) from the branch layout. If no branch has been specified, current branch is removed.
 Then synchronizes the downstream (child) branches of the last specified branch on the top of the upstream (parent) branch of the first specified branch.
 Sync is performed either by rebase (default) or by merge (if ``--merge`` option passed).
 
-The most common use is to slide out a single branch whose upstream was a ``develop``/``master`` branch and that has been recently merged.
-
-Since this tool is designed to perform only one single rebase/merge at the end, provided branches must form a chain, i.e. all of the following conditions must be met:
-
-    * for i=1..N-1, (i+1)-th branch must be the only downstream (child) branch of the i-th branch,
-    * all provided branches must have an upstream branch (so, in other words, roots of branch dependency tree cannot be slid out).
-
 For example, let's assume the following dependency tree:
 
 .. code-block::
@@ -30,7 +23,6 @@ For example, let's assume the following dependency tree:
                     drop-location-type
                 add-notification
 
-And now let's assume that ``adjust-reads-prec`` and later ``block-cancel-order`` were merged to develop.
 After running ``git machete slide-out adjust-reads-prec block-cancel-order`` the tree will be reduced to:
 
 .. code-block::
@@ -42,6 +34,13 @@ After running ``git machete slide-out adjust-reads-prec block-cancel-order`` the
 
 and ``change-table`` and ``add-notification`` will be rebased onto develop (fork point for this rebase is configurable, see ``-d`` option below).
 
+The most common use is to slide out a single branch whose upstream was a ``develop``/``master`` branch and that has been recently merged.
+
+The provided branches must form a chain --- all of the following conditions must be met:
+
+    * for i=1..N-1, (i+1)-th branch must be the only downstream (child) branch of the i-th branch,
+    * all provided branches must have an upstream branch (so, in other words, roots of branch dependency tree cannot be slid out).
+
 Note: This command doesn't delete any branches from git, just removes them from the tree of branch dependencies.
 
 **Options:**
@@ -59,7 +58,7 @@ Note: This command doesn't delete any branches from git, just removes them from
                                                   If updating by merge, equivalent to ``--no-edit-merge``.
 
 --no-edit-merge                                   If updating by merge, skip opening the editor for merge commit message while doing
-                                                  ``git merge`` (i.e. pass ``--no-edit`` flag to underlying ``git merge``).
+                                                  ``git merge`` (that is, pass ``--no-edit`` flag to the underlying ``git merge``).
                                                   Not allowed if updating by rebase.
 
 --no-interactive-rebase                           If updating by rebase, run ``git rebase`` in non-interactive mode (without ``-i/--interactive`` flag).
diff --git a/docs/source/cli/squash.rst b/docs/source/cli/squash.rst
index d56da33..092dd85 100644
--- a/docs/source/cli/squash.rst
+++ b/docs/source/cli/squash.rst
@@ -12,11 +12,13 @@ Squashes the commits belonging uniquely to the current branch into a single comm
 The chunk of the history to be squashed starts at the automatically computed fork point of the current branch by default,
 but can also be set explicitly by ``--fork-point``.
 See help for :ref:`fork-point` for more details on meaning of the *fork point*.
-The message for the squashed is taken from the earliest squashed commit, i.e. the commit directly following the fork point.
+The message for the resulting commit is taken from the earliest squashed commit (the commit directly following the fork point).
 
-Note: the current squashed branch does not need to occur in the branch layout file.
+To simply squash the most recent N commits, use ``--fork-point=HEAD~<N>``,
+for example ``git machete squash --fork-point=HEAD~3``.
 
-Tip: ``squash`` does NOT run ``git rebase`` under the hood. For more complex scenarios that require rewriting the history of current branch, see ``reapply`` and ``update``.
+Tip: ``squash`` does NOT run ``git rebase`` under the hood.
+For more complex scenarios that require rewriting the history of current branch, see ``reapply`` and ``update``.
 
 **Options:**
 
diff --git a/docs/source/cli/status.rst b/docs/source/cli/status.rst
index 0e9ec38..bdec0ed 100644
--- a/docs/source/cli/status.rst
+++ b/docs/source/cli/status.rst
@@ -26,15 +26,15 @@ Apart from simply ASCII-formatting the branch layout file, this also:
 
     * colors the edges between upstream (parent) and downstream (children) branches:
 
-      - :red:`red edge` means that the downstream branch tip is not a direct descendant of the upstream branch tip,
+      - :red:`red edge` means *not in sync*. The downstream branch is NOT a direct descendant of the upstream branch.
 
-      - :yellow:`yellow edge` means that the downstream branch tip is a direct descendant of the upstream branch tip,
-        but the :ref:`fork point<fork-point>` of the downstream branch is not equal to the upstream branch tip,
+      - :yellow:`yellow edge` means *in sync but fork point off*. The downstream branch is a direct descendant of the upstream branch,
+        but the :ref:`fork point<fork-point>` of the downstream branch is NOT equal to the upstream branch.
 
-      - :green:`green edge` means that the downstream branch tip is a direct descendant of the upstream branch tip
-        and the fork point of the downstream branch is equal to the upstream branch tip,
+      - :green:`green edge` means *in sync*. The downstream branch is a direct descendant of the upstream branch
+        and the fork point of the downstream branch is equal to the upstream branch.
 
-      - :grey:`grey/dimmed edge` means that the downstream branch has been merged to the upstream branch,
+      - :grey:`grey/dimmed edge` means *merged*. The downstream branch has been merged to the upstream branch,
         detected by commit equivalency (default), or by strict detection of merge commits (if ``--no-detect-squash-merges`` passed).
 
 
@@ -42,7 +42,7 @@ Apart from simply ASCII-formatting the branch layout file, this also:
       is not in sync with its remote counterpart;
 
     * displays the custom annotations (see help for :ref:`format` and :ref:`anno`) next to each branch, if present. Annotations might contain underlined branch
-      qualifiers (``rebase=no``, ``push=no``) that control rebase and push behavior of ``traverse`` (see help for :ref:`traverse`);
+      qualifiers (``push=no``, ``rebase=no``, ``slide-out=no``) that control rebase and push behavior of ``traverse`` (see help for :ref:`traverse`);
 
     * displays the output of ``machete-status-branch hook`` (see help for :ref:`hooks`), if present;
 
@@ -51,14 +51,15 @@ Apart from simply ASCII-formatting the branch layout file, this also:
 Name of the currently checked-out branch is underlined (or shown in blue on terminals that don't support underline).
 
 In case of :yellow:`yellow edge`, use ``-l`` or ``-L`` flag to show the exact location of the inferred fork point
-(which indicates e.g. what range of commits is going to be rebased when the branch is updated).
+(which indicates, among other things, what range of commits is going to be rebased when the branch is updated).
 The inferred fork point can be always overridden manually, see help for :ref:`fork-point`.
 
 :grey:`Grey/dimmed edge` suggests that the downstream branch can be slid out (see help for :ref:`slide-out` and :ref:`traverse`).
 
-Using colors can be disabled with a ``--color`` flag set to ``never``.
-With ``--color=always``, git machete always emits colors and with ``--color=auto``, it emits colors only when standard output is connected to a terminal.
-``--color=auto`` is the default. When colors are disabled, relation between branches is represented in the following way (not including the hash-comments):
+Use of colors can be disabled with a ``--color`` flag set to ``never``.
+With ``--color=always``, git machete always emits colors.
+With ``--color=auto`` (the default), it emits colors only when standard output is connected to a terminal.
+When colors are disabled, relation between branches is represented in the following way (not including the hash-comments):
 
 .. code-block::
 
@@ -76,7 +77,7 @@ With ``--color=always``, git machete always emits colors and with ``--color=auto
 
 **Options:**
 
---color=WHEN                      Colorize the output; WHEN can be ``always``, ``auto`` (default; i.e. only if stdout is a terminal), or ``never``.
+--color=WHEN                      Colorize the output; WHEN can be ``always``, ``auto`` (default: colorize only if stdout is a terminal), or ``never``.
 
 -l, --list-commits                Additionally list the commits introduced on each branch.
 
diff --git a/docs/source/cli/traverse.rst b/docs/source/cli/traverse.rst
index f044e6c..77016b6 100644
--- a/docs/source/cli/traverse.rst
+++ b/docs/source/cli/traverse.rst
@@ -23,8 +23,8 @@ traverse
                            [--[no-]push] [--[no-]push-untracked]
                            [--return-to=WHERE] [--start-from=WHERE] [-w|--whole] [-W] [-y|--yes]
 
-Traverses the branch tree in pre-order (i.e. simply in the order as they occur in the branch layout file).
-By default ``traverse`` starts from the current branch.
+Traverses the branches in the order as they occur in branch layout file.
+By default, ``traverse`` starts from the current branch.
 This behavior can, however, be customized using options: ``--start-from=``, ``--whole`` or ``-w``, ``-W``.
 
 For each branch, the command:
@@ -32,42 +32,42 @@ For each branch, the command:
     * detects if the branch is merged (:grey:`grey` edge) to its parent (aka upstream):
 
       - by commit equivalency (default), or by strict detection of merge commits (if ``--no-detect-squash-merges`` passed),
-      - if so, asks the user whether to slide out the branch from the dependency tree (typically branches are no longer needed after they're merged);
+      - if so, asks the user whether to **slide out** the branch from the dependency tree (typically branches are no longer needed after they're merged);
 
     * otherwise, if the branch has a :red:`red` or :yellow:`yellow` edge to its parent/upstream (see help for :ref:`status`):
 
-      - asks the user whether to rebase (default) or merge (if ``--merge`` passed) the branch onto into its upstream branch
-        --- equivalent to ``git machete update`` with no ``--fork-point`` option passed;
+      - asks the user whether to **rebase** (default) or merge (if ``--merge`` passed) the branch onto into its upstream branch
+        --- equivalent to ``git machete update``;
 
     * if the branch is not tracked on a remote, is ahead of its remote counterpart, or diverged from the counterpart &
       has newer head commit than the counterpart:
 
-      - asks the user whether to push the branch (possibly with ``--force-with-lease`` if the branches diverged);
+      - asks the user whether to **push** the branch (possibly with ``--force-with-lease`` if the branches diverged);
 
     * otherwise, if the branch diverged from the remote counterpart & has older head commit than the counterpart:
 
-      - asks the user whether to ``git reset --keep`` the branch to its remote counterpart
+      - asks the user whether to **reset** (``git reset --keep``) the branch to its remote counterpart
 
     * otherwise, if the branch is behind its remote counterpart:
 
-      - asks the user whether to pull the branch;
+      - asks the user whether to **pull** the branch;
 
     * and finally, if any of the above operations has been successfully completed:
 
       - prints the updated ``status``.
 
-By default ``traverse`` asks if the branch should be pushed, this behavior can, however, be changed with the ``machete.traverse.push`` configuration key.
+By default ``traverse`` asks if the branch should be pushed. This behavior can, however, be changed with the ``machete.traverse.push`` configuration key.
 It can also be customized using options: ``--[no-]push`` or ``--[no-]push-untracked`` --- the order of the flags defines their precedence over each other
 (the one on the right overriding the ones on the left). More on them in the **Options** section below.
 
 If the traverse flow is stopped (typically due to merge/rebase conflicts), just run ``git machete traverse`` after the merge/rebase is finished.
-It will pick up the walk from the current branch (unless ``--start-from=`` or ``-w`` etc. is passed).
-Unlike with e.g. ``git rebase``, there is no special ``--continue`` flag, as ``traverse`` is stateless
-(doesn't keep a state of its own like ``git rebase`` does in ``.git/rebase-apply/``).
+It will pick up the walk from the current branch.
+Unlike with ``git rebase`` or ``git cherry-pick``, there is no special ``--continue`` flag, as ``traverse`` is stateless.
+``traverse`` doesn't keep a state of its own like ``git rebase`` does in ``.git/rebase-apply/``.
 
 The rebase, push and slide-out behaviors of ``traverse`` can also be customized for each branch separately using *branch qualifiers*.
-There are ``rebase=no``, ``push=no`` and ``slide-out=no`` qualifiers that can be used to opt out of default behavior (rebasing, pushing and sliding the branch out of the dependency tree).
-The qualifier can appear anywhere in the annotation but needs to be separated by a whitespace from any other character, e.g. ``some_annotation_text rebase=no push=no slide-out=no``.
+There are ``push=no``, ``rebase=no`` and ``slide-out=no`` qualifiers that can be used to opt out of default behavior (rebasing, pushing and sliding the branch out).
+The qualifier can appear anywhere in the annotation, but needs to be separated by a whitespace from any other character, as in: ``some_annotation_text rebase=no push=no slide-out=no``.
 Qualifiers can only be overwritten by manually editing ``.git/machete`` file or modifying it with ``git machete e[dit]``, or by updating annotations with ``git machete anno``.
 Example machete file with branch qualifiers:
 
@@ -80,7 +80,7 @@ Example machete file with branch qualifiers:
         branch-for-local-experiments  push=no
 
 Operations like ``git machete github anno-prs`` and ``git machete github checkout-prs`` add ``rebase=no push=no`` branch qualifiers
-when the current user is **not** the owner of the PR associated with that branch. ``git machete discover`` doesn't modify the qualifiers.
+when the current user is NOT the owner of the PR associated with that branch.
 
 
 **Options:**
@@ -97,7 +97,7 @@ when the current user is **not** the owner of the PR associated with that branch
                              when detecting if a branch is merged into its upstream (parent).
 
 --no-edit-merge              If updating by merge, skip opening the editor for merge commit message while doing ``git merge``
-                             (i.e. pass ``--no-edit`` flag to the underlying ``git merge``). Not allowed if updating by rebase.
+                             (that is, pass ``--no-edit`` flag to the underlying ``git merge``). Not allowed if updating by rebase.
 
 --no-interactive-rebase      If updating by rebase, run ``git rebase`` in non-interactive mode (without ``-i/--interactive`` flag).
                              Not allowed if updating by merge.
diff --git a/docs/source/cli/update.rst b/docs/source/cli/update.rst
index c9e056f..f30bca4 100644
--- a/docs/source/cli/update.rst
+++ b/docs/source/cli/update.rst
@@ -28,7 +28,7 @@ If updating by merge, merges the upstream (parent) branch into the current branc
                                         If updating by merge, equivalent to ``--no-edit-merge``.
 
 --no-edit-merge                         If updating by merge, skip opening the editor for merge commit message while doing ``git merge``
-                                        (i.e. pass ``--no-edit`` flag to underlying ``git merge``). Not allowed if updating by rebase.
+                                        (that is, pass ``--no-edit`` flag to underlying ``git merge``). Not allowed if updating by rebase.
 
 --no-interactive-rebase                 If updating by rebase, run ``git rebase`` in non-interactive mode (without ``-i/--interactive`` flag).
                                         Not allowed if updating by merge.
diff --git a/docs/source/github_config_keys.rst b/docs/source/github_config_keys.rst
index 527c0c8..2451188 100644
--- a/docs/source/github_config_keys.rst
+++ b/docs/source/github_config_keys.rst
@@ -12,7 +12,7 @@
         Organization and repository name
             E.g. ``git config machete.github.organization VirtusLab; git config machete.github.repository git-machete``
 
-    Note that you do **not** need to set all four keys at once.
+    Note that you do NOT need to set all four keys at once.
     For example, in a typical usage of GitHub Enterprise, it should be enough to just set ``machete.github.domain``.
     Only ``machete.github.organization`` and ``machete.github.repository`` must be specified together.
 
diff --git a/docs/source/status_config_key.rst b/docs/source/status_config_key.rst
index 4a8e9de..c5dbfe2 100644
--- a/docs/source/status_config_key.rst
+++ b/docs/source/status_config_key.rst
@@ -1,7 +1,7 @@
 .. note::
 
     To make it easier to select branch name from the ``status`` output on certain terminals
-    (e.g. `Alacritty <https://github.com/alacritty/alacritty>`_), you can add an extra space between └─ and ``branch name``
+    (like `Alacritty <https://github.com/alacritty/alacritty>`_), you can add an extra space between └─ and ``branch name``
     by setting ``git config machete.status.extraSpaceBeforeBranchName true``.
 
     For example, by default the status is displayed as:
diff --git a/git_machete/__init__.py b/git_machete/__init__.py
index 786602c..c170b8d 100644
--- a/git_machete/__init__.py
+++ b/git_machete/__init__.py
@@ -1,1 +1,1 @@
-__version__ = '3.19.0'
+__version__ = '3.20.0'
diff --git a/git_machete/cli.py b/git_machete/cli.py
index 9a82a32..f3809a2 100644
--- a/git_machete/cli.py
+++ b/git_machete/cli.py
@@ -241,7 +241,7 @@ def create_cli_parser() -> argparse.ArgumentParser:
         usage=argparse.SUPPRESS,
         add_help=False,
         parents=[common_args_parser])
-    github_parser.add_argument('subcommand', choices=['anno-prs', 'checkout-prs', 'create-pr', 'retarget-pr', 'sync'])
+    github_parser.add_argument('subcommand', choices=['anno-prs', 'checkout-prs', 'create-pr', 'restack-pr', 'retarget-pr', 'sync'])
     github_parser.add_argument('pr_no', nargs='*', type=int)
     github_parser.add_argument('-b', '--branch', default=argparse.SUPPRESS)
     github_parser.add_argument('--all', action='store_true')
@@ -701,6 +701,8 @@ def launch(orig_args: List[str]) -> None:
                     head=current_branch,
                     opt_draft=cli_opts.opt_draft,
                     opt_onto=cli_opts.opt_onto)
+            elif github_subcommand == "restack-pr":
+                machete_client.restack_github_pr()
             elif github_subcommand == "retarget-pr":
                 branch = parsed_cli.branch if 'branch' in parsed_cli else git.get_current_branch()
                 machete_client.expect_in_managed_branches(branch)
@@ -798,7 +800,14 @@ def launch(orig_args: List[str]) -> None:
                 machete_client.check_that_fork_point_is_ancestor_or_equal_to_tip_of_branch(
                     fork_point_hash=cli_opts.opt_fork_point, branch=current_branch)
 
-            squash_fork_point = cli_opts.opt_fork_point or machete_client.fork_point(branch=current_branch, use_overrides=True)
+            squash_fork_point = cli_opts.opt_fork_point or machete_client.fork_point_or_none(branch=current_branch, use_overrides=True)
+            if squash_fork_point is None:
+                raise MacheteException(
+                    f"git-machete cannot determine the range of commits unique to branch <b>{current_branch}</b>.\n"
+                    f"Use `git machete squash --fork-point=...` to select the commit "
+                    f"after which the commits of <b>{current_branch}</b> start.\n"
+                    "For example, if you want to squash 3 latest commits, use `git machete squash --fork-point=HEAD~3`."
+                )
             machete_client.squash(current_branch=current_branch, opt_fork_point=squash_fork_point)
         elif cmd in {"status", alias_by_command["status"]}:
             machete_client.read_branch_layout_file(perform_interactive_slide_out=should_perform_interactive_slide_out)
diff --git a/git_machete/client.py b/git_machete/client.py
index 23d928b..c3fd030 100644
--- a/git_machete/client.py
+++ b/git_machete/client.py
@@ -892,7 +892,7 @@ class MacheteClient:
                         self.__handle_untracked_state(
                             branch=current_branch,
                             is_called_from_traverse=True,
-                            is_called_from_create_pr=False,
+                            is_called_from_github=False,
                             opt_push_untracked=opt_push_untracked,
                             opt_push_tracked=opt_push_tracked,
                             opt_yes=opt_yes)
@@ -1359,14 +1359,16 @@ class MacheteClient:
             else:
                 return fp_hash, containing_branch_pairs
 
-    def fork_point(
-            self,
-            branch: LocalBranchShortName,
-            use_overrides: bool
-    ) -> FullCommitHash:
+    def fork_point(self, branch: LocalBranchShortName, use_overrides: bool) -> FullCommitHash:
         hash, containing_branch_pairs = self.__fork_point_and_containing_branch_pairs(branch, use_overrides)
         return FullCommitHash.of(hash)
 
+    def fork_point_or_none(self, branch: LocalBranchShortName, use_overrides: bool) -> Optional[FullCommitHash]:
+        try:
+            return self.fork_point(branch, use_overrides)
+        except MacheteException:
+            return None
+
     def diff(self, *, branch: Optional[LocalBranchShortName], opt_stat: bool) -> None:
         diff_branch = branch or self.__git.get_current_branch()
         fork_point = self.fork_point(diff_branch, use_overrides=True)
@@ -1497,8 +1499,7 @@ class MacheteClient:
                 raise MacheteException(f"The machete-post-slide-out hook exited with {exit_code}, aborting.")
 
     def squash(self, *, current_branch: LocalBranchShortName, opt_fork_point: AnyRevision) -> None:
-        commits: List[GitLogEntry] = self.__git.get_commits_between(
-            opt_fork_point, current_branch)
+        commits: List[GitLogEntry] = self.__git.get_commits_between(opt_fork_point, current_branch)
         if not commits:
             raise MacheteException(
                 "No commits to squash. Use `-f` or `--fork-point` to specify the "
@@ -1590,9 +1591,9 @@ class MacheteClient:
         org_repo_remote = self.__derive_org_repo_and_remote(domain=domain)
         github_client = GitHubClient(domain=domain, organization=org_repo_remote.organization, repository=org_repo_remote.repository)
         print('Checking for open GitHub PRs... ', end='', flush=True)
-        current_user: Optional[str] = github_client.derive_current_user_login()
+        current_user: Optional[str] = github_client.get_current_user_login()
         debug('Current GitHub user is ' + (bold(current_user or '<none>')))
-        all_open_prs: List[GitHubPullRequest] = github_client.derive_open_pull_requests()
+        all_open_prs: List[GitHubPullRequest] = github_client.get_open_pull_requests()
         print(fmt('<green><b>OK</b></green>'))
         self.__sync_annotations_to_branch_layout_file(all_open_prs, current_user, verbose=True)
 
@@ -1802,7 +1803,7 @@ class MacheteClient:
             *,
             branch: LocalBranchShortName,
             is_called_from_traverse: bool,
-            is_called_from_create_pr: bool,
+            is_called_from_github: bool,
             opt_push_untracked: bool,
             opt_push_tracked: bool,
             opt_yes: bool
@@ -1813,7 +1814,7 @@ class MacheteClient:
               "repository, or 'n' to skip this branch, or " \
               "'q' to quit the traverse: " if is_called_from_traverse \
             else f"Select number 1..{len(rems)} to specify the destination remote " \
-                 "repository, or 'q' to quit creating pull request: "
+                 "repository, or 'q' to quit the operation: "
 
         ans = input(msg).lower()
         if ans in ('q', 'quit'):
@@ -1826,13 +1827,13 @@ class MacheteClient:
                 new_remote=rems[index],
                 branch=branch,
                 is_called_from_traverse=is_called_from_traverse,
-                is_called_from_create_pr=is_called_from_create_pr,
+                is_called_from_github=is_called_from_github,
                 opt_push_untracked=opt_push_untracked,
                 opt_push_tracked=opt_push_tracked,
                 opt_yes=opt_yes)
         except ValueError:
-            if is_called_from_create_pr:
-                raise MacheteException('Could not establish remote repository, pull request creation interrupted.')
+            if is_called_from_github:
+                raise MacheteException('Could not establish remote repository, operation interrupted.')
 
     def __handle_untracked_branch(
             self,
@@ -1840,7 +1841,7 @@ class MacheteClient:
             new_remote: str,
             branch: LocalBranchShortName,
             is_called_from_traverse: bool,
-            is_called_from_create_pr: bool,
+            is_called_from_github: bool,
             opt_push_untracked: bool,
             opt_push_tracked: bool,
             opt_yes: bool
@@ -1868,7 +1869,7 @@ class MacheteClient:
                     self.__pick_remote(
                         branch=branch,
                         is_called_from_traverse=is_called_from_traverse,
-                        is_called_from_create_pr=is_called_from_create_pr,
+                        is_called_from_github=is_called_from_github,
                         opt_push_untracked=opt_push_untracked,
                         opt_push_tracked=opt_push_tracked,
                         opt_yes=opt_yes)
@@ -2033,7 +2034,7 @@ class MacheteClient:
         github_client = GitHubClient(domain=domain, organization=org_repo_remote.organization, repository=org_repo_remote.repository)
         print('Checking for open GitHub PRs... ', end='', flush=True)
 
-        current_user: Optional[str] = github_client.derive_current_user_login()
+        current_user: Optional[str] = github_client.get_current_user_login()
         if not current_user and mine:
             msg = ("Could not determine current user name, please check that the GitHub API token provided by one of the: "
                    f"{GitHubToken.get_possible_providers()}is valid.")
@@ -2042,7 +2043,7 @@ class MacheteClient:
             else:
                 warn(msg)
                 return
-        all_open_prs: List[GitHubPullRequest] = github_client.derive_open_pull_requests()
+        all_open_prs: List[GitHubPullRequest] = github_client.get_open_pull_requests()
         print(fmt('<green><b>OK</b></green>'))
 
         applicable_prs: List[GitHubPullRequest] = self.__get_applicable_pull_requests(
@@ -2178,6 +2179,103 @@ class MacheteClient:
                 return remote
         return None
 
+    def restack_github_pr(self) -> None:
+        domain = self.__derive_github_domain()
+        head = self.__git.get_current_branch()
+        org_repo_remote = self.__derive_org_repo_and_remote(domain=domain, branch_used_for_tracking_data=head)
+        github_client = GitHubClient(domain=domain, organization=org_repo_remote.organization, repository=org_repo_remote.repository)
+
+        prs: List[GitHubPullRequest] = github_client.get_open_pull_requests_by_head(head)
+        if not prs:
+            raise MacheteException(f"No PRs have `{head}` as its head")
+        if len(prs) > 1:
+            raise MacheteException(f"Multiple PRs have `{head}` as its head: " + ", ".join(f"#{_pr.number}" for _pr in prs))
+        pr = prs[0]
+
+        self.__git.fetch_remote(org_repo_remote.remote)
+
+        self.__empty_line_status = True
+        current_branch = self.__git.get_current_branch()
+        s, remote = self.__git.get_combined_remote_sync_status(current_branch)
+        statuses_to_push = (
+            SyncToRemoteStatuses.UNTRACKED,
+            SyncToRemoteStatuses.AHEAD_OF_REMOTE,
+            SyncToRemoteStatuses.DIVERGED_FROM_AND_NEWER_THAN_REMOTE)
+        if s in statuses_to_push:
+            if current_branch in self.annotations and not self.annotations[current_branch].qualifiers.push:
+                warn(f'Branch <b>{current_branch}</b> is marked as `push=no`; skipping the push.\n'
+                     'Did you want to just use `git machete github retarget-pr`?\n')
+
+                self.retarget_github_pr(head, ignore_if_missing=False)
+            else:
+                converted_to_draft = github_client.set_draft_status_of_pull_request(pr.number, target_draft_status=True)
+                if converted_to_draft:
+                    print(f'PR #{bold(str(pr.number))} has been temporarily marked as draft')
+
+                if s == SyncToRemoteStatuses.AHEAD_OF_REMOTE:
+                    assert remote is not None
+                    self.__handle_ahead_state(
+                        current_branch=current_branch,
+                        remote=remote,
+                        is_called_from_traverse=False,
+                        opt_push_tracked=True,
+                        opt_yes=True)
+                elif s == SyncToRemoteStatuses.UNTRACKED:
+                    self.__handle_untracked_state(
+                        branch=current_branch,
+                        is_called_from_traverse=False,
+                        is_called_from_github=True,
+                        opt_push_tracked=True,
+                        opt_push_untracked=True,
+                        opt_yes=True)
+                elif s == SyncToRemoteStatuses.DIVERGED_FROM_AND_NEWER_THAN_REMOTE:  # pragma: no branch
+                    assert remote is not None
+                    self.__handle_diverged_and_newer_state(
+                        current_branch=current_branch,
+                        remote=remote,
+                        is_called_from_traverse=False,
+                        opt_push_tracked=True,
+                        opt_yes=True)
+
+                self.__print_new_line(False)
+                self.status(
+                    warn_when_branch_in_sync_but_fork_point_off=True,
+                    opt_list_commits=False,
+                    opt_list_commits_with_hashes=False,
+                    opt_no_detect_squash_merges=False)
+                self.__print_new_line(False)
+
+                self.retarget_github_pr(head, ignore_if_missing=False)
+
+                if converted_to_draft:
+                    github_client.set_draft_status_of_pull_request(prs[0].number, target_draft_status=False)
+                    print(f'PR #{bold(str(pr.number))} has been marked as ready for review again')
+
+        else:
+            if s == SyncToRemoteStatuses.BEHIND_REMOTE:
+                warn(f"Branch {bold(current_branch)} is behind its remote counterpart. Consider using `git pull`.\n")
+            elif s == SyncToRemoteStatuses.DIVERGED_FROM_AND_OLDER_THAN_REMOTE:
+                warn(f"Branch {bold(current_branch)} is diverged from and older than its remote counterpart. "
+                     "Consider using `git reset --keep`.\n")
+            elif s == SyncToRemoteStatuses.NO_REMOTES:  # pragma: no cover; case handled elsewhere
+                raise MacheteException(
+                    "Could not retarget pull request - there are no remote repositories!")
+
+            self.retarget_github_pr(head, ignore_if_missing=False)
+
+    @staticmethod
+    def __get_updated_github_pr_description(old_description: Optional[str], pr_number_for_base_branch: Optional[int]) -> str:
+        def skip_leading_empty(strs: List[str]) -> List[str]:
+            return list(itertools.dropwhile(lambda line: line.strip() == '', strs))
+
+        lines = skip_leading_empty(old_description.split('\n')) if old_description else []
+        if lines and '# Based on PR #' in lines[0]:
+            lines = lines[1:]
+        lines = skip_leading_empty(lines)
+        if pr_number_for_base_branch:
+            lines = [f'# Based on PR #{pr_number_for_base_branch}', ''] + lines
+        return '\n'.join(lines)
+
     def retarget_github_pr(self, head: LocalBranchShortName, ignore_if_missing: bool) -> None:
         domain = self.__derive_github_domain()
         org_repo_remote = self.__derive_org_repo_and_remote(domain=domain, branch_used_for_tracking_data=head)
@@ -2185,7 +2283,7 @@ class MacheteClient:
 
         debug(f'organization is {org_repo_remote.organization}, repository is {org_repo_remote.repository}')
 
-        prs: List[GitHubPullRequest] = github_client.derive_open_pull_requests_by_head(head)
+        prs: List[GitHubPullRequest] = github_client.get_open_pull_requests_by_head(head)
         if not prs:
             if ignore_if_missing:
                 warn(f"no PRs have `{head}` as its head")
@@ -2207,9 +2305,22 @@ class MacheteClient:
 
         if pr.base != new_base:
             github_client.set_base_of_pull_request(pr.number, base=new_base)
-            print(f'The base branch of PR #{bold(str(pr.number))} has been switched to {bold(new_base)}')
+            print(f'Base branch of PR #{bold(str(pr.number))} has been switched to {bold(new_base)}')
         else:
-            print(f'The base branch of PR #{bold(str(pr.number))} is already {bold(new_base)}')
+            print(f'Base branch of PR #{bold(str(pr.number))} is already {bold(new_base)}')
+
+        pr_number_for_base_branch: Optional[int] = None
+        prs_for_base_branch = github_client.get_open_pull_requests_by_head(new_base)
+        if len(prs_for_base_branch) >= 1:
+            pr_number_for_base_branch = prs_for_base_branch[0].number
+        new_description = self.__get_updated_github_pr_description(pr.description, pr_number_for_base_branch)
+        if pr.description != new_description:
+            github_client.set_description_of_pull_request(pr.number, description=new_description)
+            if not pr_number_for_base_branch:
+                print(f'Base PR header has been removed from the description of PR #{bold(str(pr.number))}')
+            else:
+                print(f'Base PR header in the description of PR #{bold(str(pr.number))} '
+                      f'now points to PR #{bold(str(pr_number_for_base_branch))}')
 
         if self.__annotations.get(head) and self.__annotations[head].qualifiers_text:
             self.__annotations[head] = Annotation(f'PR #{pr.number} ' + self.__annotations[head].qualifiers_text)
@@ -2323,18 +2434,23 @@ class MacheteClient:
         github_client = GitHubClient(domain=domain, organization=org_repo_remote.organization, repository=org_repo_remote.repository)
         print(f"Fetching {bold(org_repo_remote.remote)}...")
         self.__git.fetch_remote(org_repo_remote.remote)
+        pr_number_for_base_branch: Optional[int] = None
         if '/'.join([org_repo_remote.remote, base]) not in self.__git.get_remote_branches():
             warn(f'Base branch for this PR ({bold(base)}) is not found on remote, pushing...')
             self.__handle_untracked_branch(
                 branch=base,
                 new_remote=org_repo_remote.remote,
                 is_called_from_traverse=False,
-                is_called_from_create_pr=True,
+                is_called_from_github=True,
                 opt_push_tracked=False,
                 opt_push_untracked=True,
                 opt_yes=False)
+        else:
+            prs_for_base_branch = github_client.get_open_pull_requests_by_head(base)
+            if len(prs_for_base_branch) >= 1:
+                pr_number_for_base_branch = prs_for_base_branch[0].number
 
-        current_user: Optional[str] = github_client.derive_current_user_login()
+        current_user: Optional[str] = github_client.get_current_user_login()
         debug(f'organization is {org_repo_remote.organization}, repository is {org_repo_remote.repository}')
         debug('current GitHub user is ' + (current_user or '<none>'))
 
@@ -2348,6 +2464,9 @@ class MacheteClient:
         else:
             description = ''
 
+        if pr_number_for_base_branch:
+            description = f'# Based on PR #{pr_number_for_base_branch}\n\n' + description
+
         fork_point = self.fork_point(head, use_overrides=True)
         commits: List[GitLogEntry] = self.__git.get_commits_between(fork_point, head)
         # git-machete can still see an empty range of unique commits (e.g. in case of yellow edge)
@@ -2432,7 +2551,7 @@ class MacheteClient:
             *,
             branch: LocalBranchShortName,
             is_called_from_traverse: bool,
-            is_called_from_create_pr: bool,
+            is_called_from_github: bool,
             opt_push_tracked: bool,
             opt_push_untracked: bool,
             opt_yes: bool
@@ -2444,7 +2563,7 @@ class MacheteClient:
                 new_remote=remotes[0],
                 branch=branch,
                 is_called_from_traverse=is_called_from_traverse,
-                is_called_from_create_pr=is_called_from_create_pr,
+                is_called_from_github=is_called_from_github,
                 opt_push_untracked=opt_push_untracked,
                 opt_push_tracked=opt_push_tracked,
                 opt_yes=opt_yes)
@@ -2453,7 +2572,7 @@ class MacheteClient:
                 new_remote="origin",
                 branch=branch,
                 is_called_from_traverse=is_called_from_traverse,
-                is_called_from_create_pr=is_called_from_create_pr,
+                is_called_from_github=is_called_from_github,
                 opt_push_untracked=opt_push_untracked,
                 opt_push_tracked=opt_push_tracked,
                 opt_yes=opt_yes)
@@ -2463,7 +2582,7 @@ class MacheteClient:
             self.__pick_remote(
                 branch=branch,
                 is_called_from_traverse=is_called_from_traverse,
-                is_called_from_create_pr=is_called_from_create_pr,
+                is_called_from_github=is_called_from_github,
                 opt_push_untracked=opt_push_untracked,
                 opt_push_tracked=opt_push_tracked,
                 opt_yes=opt_yes)
@@ -2578,7 +2697,7 @@ class MacheteClient:
                     self.__handle_untracked_state(
                         branch=current_branch,
                         is_called_from_traverse=False,
-                        is_called_from_create_pr=True,
+                        is_called_from_github=True,
                         opt_push_tracked=True,
                         opt_push_untracked=True,
                         opt_yes=opt_yes)
diff --git a/git_machete/generated_docs.py b/git_machete/generated_docs.py
index de15819..86505ad 100644
--- a/git_machete/generated_docs.py
+++ b/git_machete/generated_docs.py
@@ -125,7 +125,7 @@ long_docs: Dict[str, str] = {
         then clears the annotation for the current branch (or a branch specified with `-b/--branch`).
 
         If invoked with `-H` or `--sync-github-prs`, annotates the branches based on their corresponding GitHub PR numbers and authors.
-        When the current user is <b>not</b> the owner of the PR associated with that branch, adds `rebase=no push=no` branch qualifiers used by `git machete traverse`,
+        When the current user is NOT the owner of the PR associated with that branch, adds `rebase=no push=no` branch qualifiers used by `git machete traverse`,
         so that you don't rebase or push someone else's PR by accident (see help for `traverse`).
         Any existing annotations (except branch qualifiers) are overwritten for the branches that have an opened PR; annotations for the other branches remain untouched.
 
@@ -160,7 +160,7 @@ long_docs: Dict[str, str] = {
 
            Organization and repository name
               E.g. `git config machete.github.organization VirtusLab; git config machete.github.repository git-machete`
-        Note that you do <b>not</b> need to set all four keys at once.
+        Note that you do NOT need to set all four keys at once.
         For example, in a typical usage of GitHub Enterprise, it should be enough to just set `machete.github.domain`.
         Only `machete.github.organization` and `machete.github.repository` must be specified together.
 
@@ -226,7 +226,7 @@ long_docs: Dict[str, str] = {
 
            Organization and repository name
               E.g. `git config machete.github.organization VirtusLab; git config machete.github.repository git-machete`
-        Note that you do <b>not</b> need to set all four keys at once.
+        Note that you do NOT need to set all four keys at once.
         For example, in a typical usage of GitHub Enterprise, it should be enough to just set `machete.github.domain`.
         Only `machete.github.organization` and `machete.github.repository` must be specified together.
 
@@ -290,7 +290,7 @@ long_docs: Dict[str, str] = {
                  Organization and repository name
               E.g. `git config machete.github.organization VirtusLab; git config machete.github.repository git-machete`
 
-              Note that you do <b>not</b> need to set all four keys at once.
+              Note that you do NOT need to set all four keys at once.
               For example, in a typical usage of GitHub Enterprise, it should be enough to just set `machete.github.domain`.
               Only `machete.github.organization` and `machete.github.repository` must be specified together.
 
@@ -306,7 +306,7 @@ long_docs: Dict[str, str] = {
            `machete.status.extraSpaceBeforeBranchName`:
 
               To make it easier to select branch name from the `status` output on certain terminals
-              (e.g. Alacritty), you can add an extra space between └─ and `branch name`
+              (like Alacritty), you can add an extra space between └─ and `branch name`
               by setting `git config machete.status.extraSpaceBeforeBranchName true`.
 
               For example, by default the status is displayed as:
@@ -396,7 +396,7 @@ long_docs: Dict[str, str] = {
         <b>Options:</b>
            <b>-C</b>, <b>--checked-out-since=<date></b>
               Only consider branches checked out at least once since the given date.
-              <date> can be e.g. `2 weeks ago` or `2020-06-01`, as in `git log --since=<date>`.
+              `<date>` can be, for example, `2 weeks ago` or `2020-06-01`, as in `git log --since=<date>`.
               If not present, the date is selected automatically so that around 10 branches are included.
            <b>-l</b>, <b>--list-commits</b>
               When printing the discovered tree, additionally lists the messages of commits introduced on each branch
@@ -463,7 +463,7 @@ long_docs: Dict[str, str] = {
         Note: in all three forms, if no `<branch>` is specified, the currently checked out branch is assumed.
         The branch in question does not need to occur in the branch layout file.
 
-        Without any option, displays full hash of the fork point commit for the `<branch>`.
+        Without any option, `git machete fork-point` displays full hash of the fork point commit for the `<branch>`.
         Fork point of the given `<branch>` is the commit at which the history of the `<branch>` diverges from history of any other branch.
 
         Fork point is assumed by many `git machete` commands as the place where the unique history of the `<branch>` starts.
@@ -473,27 +473,25 @@ long_docs: Dict[str, str] = {
            * provided to `git diff`/`log` by `git machete` `diff`/`log`.
 
         `git machete` assumes fork point of `<branch>` is the most recent commit in the log of `<branch>` that has NOT been introduced on that very branch,
-        but instead occurs on a reflog (see help for `git reflog`) of some other, usually chronologically earlier, branch.
+        but instead occurs on a reflog (see help for `git reflog`) of some other branch.
         This yields a correct result in typical cases, but there are some situations
         (esp. when some local branches have been deleted) where the fork point might not be determined correctly.
         Thus, all rebase-involving operations (`reapply`, `slide-out`, `traverse` and `update`) run `git rebase` in the interactive mode by default,
-        unless told explicitly not to do so by `--no-interactive-rebase` flag, so that the suggested commit range can be inspected before the rebase commences.
+        unless told explicitly not to do so by `--no-interactive-rebase` flag. This way, the suggested commit range can be inspected before the rebase starts.
         Also, `reapply`, `slide-out`, `squash`, and `update` allow to specify the fork point explicitly by a command-line option.
 
         `git machete fork-point` is different (and more powerful) than `git merge-base --fork-point`,
         since the latter takes into account only the reflog of the one provided upstream branch,
         while the former scans reflogs of all local branches and their remote tracking branches.
-        This makes git machete's `fork-point` more resilient to modifications of `.git/machete` `file` where certain branches are re-attached under new parents (upstreams).
+        This makes git machete's `fork-point` more resilient to modifications of `.git/machete` `file` when certain branches are re-attached under new parents (upstreams).
 
         With `--override-to=<revision>`, sets up a fork point override for `<branch>`.
         Fork point for `<branch>` will be overridden to the provided <revision> (commit) as long as the `<branch>` still points to (or is descendant of) that commit.
-        Even if revision is a symbolic name (e.g. other branch name or `HEAD~3)` and not explicit commit hash (like `a1b2c3ff`),
-        it's still resolved to a specific commit hash at the moment the override is set up (and not later when the override is actually used).
         The override data is stored under `machete.overrideForkPoint.<branch>.to` git config key.
-        Note: the provided fork point <revision> must be an ancestor of the current `<branch>` commit.
+        Note: the provided fork point `<revision>` must be an ancestor of the current `<branch>` commit.
 
         With `--override-to-parent`, overrides fork point of the `<branch>` to the commit currently pointed by `<branch>`'s parent in the branch dependency tree.
-        Note: this will only work if `<branch>` has a parent at all (i.e. is not a root) and parent of `<branch>` is an ancestor of current `<branch>` commit.
+        Note: this will only work if `<branch>` has a parent at all (is not a root branch) and parent of `<branch>` is an ancestor of current `<branch>` commit.
 
         With `--inferred`, displays the commit that `git machete fork-point` infers to be the fork point of `<branch>`.
         If there is NO fork point override for `<branch>`, this is identical to the output of `git machete fork-point`.
@@ -529,12 +527,12 @@ long_docs: Dict[str, str] = {
         Branches `adjust-reads-prec`, `edit-margin-not-allowed` and `grep-errors-script` are direct downstream branches for `develop`.
         `block-cancel-order` is a downstream branch of `adjust-reads-prec`, `change-table` is a downstream branch of `block-cancel-order` and so on.
 
-        Every branch name can be followed (after a single space as a delimiter) by a custom annotation, e.g. `PR #234 rebase=no push=no`, `PR #235 rebase=no` or `push=no`.
-        Annotations might contain underlined branch qualifiers (`rebase=no`, `push=no`) that control rebase and push behavior of `traverse` (see help for `traverse`).
+        Every branch name can be followed (after a single space as a delimiter) by a custom annotation, for example `PR #234 rebase=no push=no`, `PR #235 rebase=no` or `push=no`.
+        These annotations might contain branch qualifiers (`push=no`, `rebase=no`, `slide-out=no`) that control the behavior of `traverse` (see help for `traverse`).
         Also see help for `anno` command.
 
         Tabs or any number of spaces can be used as indentation.
-        It's only important to be consistent wrt. the sequence of characters used for indentation between all lines.
+        It's only important to use indentation characters consistently between all lines.
    """,
     "github": """
         <b>Usage:</b><b>
@@ -575,7 +573,7 @@ long_docs: Dict[str, str] = {
 
            Organization and repository name
               E.g. `git config machete.github.organization VirtusLab; git config machete.github.repository git-machete`
-        Note that you do <b>not</b> need to set all four keys at once.
+        Note that you do NOT need to set all four keys at once.
         For example, in a typical usage of GitHub Enterprise, it should be enough to just set `machete.github.domain`.
         Only `machete.github.organization` and `machete.github.repository` must be specified together.
 
@@ -584,7 +582,7 @@ long_docs: Dict[str, str] = {
               Annotates the branches based on their corresponding GitHub PR numbers and authors.
               Any existing annotations are overwritten for the branches that have an opened PR; annotations for the other branches remain untouched.
               Equivalent to `git machete anno --sync-github-prs`.
-              When the current user is <b>not</b> the owner of the PR associated with that branch, adds `rebase=no push=no` branch qualifiers used by `git machete traverse`,
+              When the current user is NOT the owner of the PR associated with that branch, adds `rebase=no push=no` branch qualifiers used by `git machete traverse`,
               so that you don't rebase or push someone else's PR by accident (see help for `traverse`).
 
            `checkout-prs [--all | --by=<github-login> | --mine | <PR-number-1> ... <PR-number-N>]`:
@@ -593,7 +591,7 @@ long_docs: Dict[str, str] = {
               also traverse chain of pull requests upwards, adding branches one by one to git-machete and check them out locally.
               Once the specified pull requests are checked out locally, annotate local branches with corresponding pull request numbers.
               If only one PR has been checked out, then switch the local repository's HEAD to its head branch.
-              When the current user is <b>not</b> the owner of the PR associated with that branch, adds `rebase=no push=no` branch qualifiers used by `git machete traverse`,
+              When the current user is NOT the owner of the PR associated with that branch, adds `rebase=no push=no` branch qualifiers used by `git machete traverse`,
               so that you don't rebase or push someone else's PR by accident (see help for `traverse`).
 
               <b>Options:</b>
@@ -617,19 +615,40 @@ long_docs: Dict[str, str] = {
               Creates a PR for the current branch, using the upstream (parent) branch as the PR base.
               Once the PR is successfully created, annotates the current branch with the new PR's number.
 
-              If `.git/info/description` or `.github/pull_request_template.md` file is present, its contents are used as PR description.
               If `.git/info/milestone` file is present, its contents (a single number — milestone id) are used as milestone.
               If `.git/info/reviewers` file is present, its contents (one GitHub login per line) are used to set reviewers.
+              If `.git/info/description` or `.github/pull_request_template.md` file is present, its contents are used as PR description.
+
+              If the newly-created PR is stacked atop another PR, the actual PR description posted to GitHub will be prepended with the following header:
+
+              `# Based on PR #<number of PR for base branch>`
 
               <b>Options:</b>
 
                  <b>--draft</b>
               Creates the new PR as a draft.
 
+           `restack-pr`:
+ 
+              Perform the following sequence of actions:
+              1. If the PR for the current branch is ready for review, it gets converted to a draft.
+              2. The branch is (force-)pushed into remote.
+              3. The PR is retargeted to its upstream (parent) branch, as in `retarget-pr`.
+              4. If the PR has been converted to draft in step 1, it's reverted to ready for review state.
+
+              The drafting/undrafting is useful in case the GitHub repository has set up CODEOWNERS.
+              Draft PRs don't get code owners automatically added as reviewers.
+
            `retarget-pr [-b|--branch=<branch>] [--ignore-if-missing]`:
  
               Sets the base of the current (or specified) branch's PR to upstream (parent) branch, as seen by git machete (see `git machete show up`).
 
+              If after changing the base, the PR ends up stacked atop another PR, the PR description at GitHub will be prepended with the following header:
+
+              `# Based on PR #<number of PR for base branch>`
+
+              This header will be updated or removed accordingly with the subsequent runs of `retarget-pr`.
+
               <b>Options:</b>
 
                  <b>-b</b>, <b>--branch=<branch></b>
@@ -717,8 +736,8 @@ long_docs: Dict[str, str] = {
 
                  * zero or more times during `traverse` (every time a slide-out operation is confirmed).
 
-              If the hook returns a non-zero exit code, then an error is raised and the execution of the command is aborted,
-              i.e. `slide-out` won't attempt rebase of the new downstream branches and `traverse` won't continue the traversal.
+              If the hook returns a non-zero exit code, then an error is raised and the execution of the command is aborted —
+              `slide-out` won't attempt rebase of the new downstream branches and `traverse` won't continue the traversal.
               In case of `advance` there is no difference (other than exit code of the entire `advance` command being non-zero),
               since slide-out is the last operation that happens within `advance`.
 
@@ -753,7 +772,7 @@ long_docs: Dict[str, str] = {
               are ignored, and printing the status continues as usual.
 
               Note: the hook is always invoked with `ASCII_ONLY` variable passed into the environment.
-              If `status` runs in ASCII-only mode (i.e. if `--color=auto` and stdout is not a terminal, or if `--color=never`),
+              If `status` runs in ASCII-only mode (if `--color=auto` and stdout is NOT a terminal, or if `--color=never`),
               then `ASCII_ONLY=true`, otherwise `ASCII_ONLY=false`.
 
         Please see hook_samples directory in git-machete project for examples.
@@ -763,7 +782,7 @@ long_docs: Dict[str, str] = {
         <b>Usage:</b><b>
            git machete is-managed [<branch>]</b>
 
-        Returns with zero exit code if the given branch (or current branch, if none specified) is managed by git machete (i.e. listed in .git/machete).
+        Returns with zero exit code if the given branch (or current branch, if none specified) is <b>managed</b> by git machete (that is, listed in .git/machete).
 
         Returns with a non-zero exit code in case:
            * the <branch> is provided but isn't managed (or doesn't exist), or
@@ -782,7 +801,7 @@ long_docs: Dict[str, str] = {
            * `managed`: all branches that appear in the branch layout file,
            * `slidable`: all managed branches that have an upstream and can be slid out with `slide-out` command
            * `slidable-after <branch>`: the downstream branch of the <branch>, if it exists and is the only downstream of <branch>
-             (i.e. the one that can be slid out immediately following <branch>),
+             (and thus can be slid out immediately following <branch>),
            * `unmanaged`: all local branches that don't appear in the branch layout file,
            * `with-overridden-fork-point`: all local branches that have a `fork point<fork-point>` override set up
              (even if this override does not affect the location of their fork point anymore).
@@ -847,16 +866,10 @@ long_docs: Dict[str, str] = {
         <b>Usage:</b><b>
            git machete slide-out [-d|--down-fork-point=<down-fork-point-commit>] [--delete] [-M|--merge] [-n|--no-edit-merge|--no-interactive-rebase] [<branch> [<branch> [<branch> ...]]]</b>
 
-        Removes the given branch (or multiple branches) from the branch layout.  If no branch has been specified current branch is assumed as the only branch.
+        Removes the given branch (or multiple branches) from the branch layout. If no branch has been specified, current branch is removed.
         Then synchronizes the downstream (child) branches of the last specified branch on the top of the upstream (parent) branch of the first specified branch.
         Sync is performed either by rebase (default) or by merge (if `--merge` option passed).
 
-        The most common use is to slide out a single branch whose upstream was a `develop`/`master` branch and that has been recently merged.
-
-        Since this tool is designed to perform only one single rebase/merge at the end, provided branches must form a chain, i.e. all of the following conditions must be met:
-           * for i=1..N-1, (i+1)-th branch must be the only downstream (child) branch of the i-th branch,
-           * all provided branches must have an upstream branch (so, in other words, roots of branch dependency tree cannot be slid out).
-
         For example, let's assume the following dependency tree:
         <dim>
           develop
@@ -867,7 +880,6 @@ long_docs: Dict[str, str] = {
                       add-notification
         </dim>
 
-        And now let's assume that `adjust-reads-prec` and later `block-cancel-order` were merged to develop.
         After running `git machete slide-out adjust-reads-prec block-cancel-order` the tree will be reduced to:
         <dim>
           develop
@@ -878,6 +890,12 @@ long_docs: Dict[str, str] = {
 
         and `change-table` and `add-notification` will be rebased onto develop (fork point for this rebase is configurable, see `-d` option below).
 
+        The most common use is to slide out a single branch whose upstream was a `develop`/`master` branch and that has been recently merged.
+
+        The provided branches must form a chain — all of the following conditions must be met:
+           * for i=1..N-1, (i+1)-th branch must be the only downstream (child) branch of the i-th branch,
+           * all provided branches must have an upstream branch (so, in other words, roots of branch dependency tree cannot be slid out).
+
         Note: This command doesn't delete any branches from git, just removes them from the tree of branch dependencies.
 
         <b>Options:</b>
@@ -895,7 +913,7 @@ long_docs: Dict[str, str] = {
               If updating by merge, equivalent to `--no-edit-merge`.
            <b>--no-edit-merge</b>
               If updating by merge, skip opening the editor for merge commit message while doing
-              `git merge` (i.e. pass `--no-edit` flag to underlying `git merge`).
+              `git merge` (that is, pass `--no-edit` flag to the underlying `git merge`).
               Not allowed if updating by rebase.
            <b>--no-interactive-rebase</b>
               If updating by rebase, run `git rebase` in non-interactive mode (without `-i/--interactive` flag).
@@ -915,11 +933,13 @@ long_docs: Dict[str, str] = {
         The chunk of the history to be squashed starts at the automatically computed fork point of the current branch by default,
         but can also be set explicitly by `--fork-point`.
         See help for `fork-point` for more details on meaning of the fork point.
-        The message for the squashed is taken from the earliest squashed commit, i.e. the commit directly following the fork point.
+        The message for the resulting commit is taken from the earliest squashed commit (the commit directly following the fork point).
 
-        Note: the current squashed branch does not need to occur in the branch layout file.
+        To simply squash the most recent N commits, use `--fork-point=HEAD~<N>`,
+        for example `git machete squash --fork-point=HEAD~3`.
 
-        Tip: `squash` does NOT run `git rebase` under the hood. For more complex scenarios that require rewriting the history of current branch, see `reapply` and `update`.
+        Tip: `squash` does NOT run `git rebase` under the hood.
+        For more complex scenarios that require rewriting the history of current branch, see `reapply` and `update`.
 
         <b>Options:</b>
            <b>-f</b>, <b>--fork-point=<fork-point-commit></b>
@@ -933,31 +953,32 @@ long_docs: Dict[str, str] = {
 
         Apart from simply ASCII-formatting the branch layout file, this also:
            * colors the edges between upstream (parent) and downstream (children) branches:
-              - <red>red edge</red> means that the downstream branch tip is not a direct descendant of the upstream branch tip,
-              - <yellow>yellow edge</yellow> means that the downstream branch tip is a direct descendant of the upstream branch tip,
-                but the `fork point<fork-point>` of the downstream branch is not equal to the upstream branch tip,
-              - <green>green edge</green> means that the downstream branch tip is a direct descendant of the upstream branch tip
-                and the fork point of the downstream branch is equal to the upstream branch tip,
-              - grey/dimmed edge means that the downstream branch has been merged to the upstream branch,
+              - <red>red edge</red> means not in sync. The downstream branch is NOT a direct descendant of the upstream branch.
+              - <yellow>yellow edge</yellow> means in sync but fork point off. The downstream branch is a direct descendant of the upstream branch,
+                but the `fork point<fork-point>` of the downstream branch is NOT equal to the upstream branch.
+              - <green>green edge</green> means in sync. The downstream branch is a direct descendant of the upstream branch
+                and the fork point of the downstream branch is equal to the upstream branch.
+              - grey/dimmed edge means merged. The downstream branch has been merged to the upstream branch,
                 detected by commit equivalency (default), or by strict detection of merge commits (if `--no-detect-squash-merges` passed).
            * prints (`untracked`/`ahead of <remote>`/`behind <remote>`/`diverged from [& older than] <remote>`) message if the branch
              is not in sync with its remote counterpart;
            * displays the custom annotations (see help for `format` and `anno`) next to each branch, if present. Annotations might contain underlined branch
-             qualifiers (`rebase=no`, `push=no`) that control rebase and push behavior of `traverse` (see help for `traverse`);
+             qualifiers (`push=no`, `rebase=no`, `slide-out=no`) that control rebase and push behavior of `traverse` (see help for `traverse`);
            * displays the output of `machete-status-branch hook` (see help for `hooks`), if present;
            * optionally lists commits introduced on each branch if `-l/--list-commits` or `-L/--list-commits-with-hashes` is supplied.
 
         Name of the currently checked-out branch is underlined (or shown in blue on terminals that don't support underline).
 
         In case of <yellow>yellow edge</yellow>, use `-l` or `-L` flag to show the exact location of the inferred fork point
-        (which indicates e.g. what range of commits is going to be rebased when the branch is updated).
+        (which indicates, among other things, what range of commits is going to be rebased when the branch is updated).
         The inferred fork point can be always overridden manually, see help for `fork-point`.
 
         Grey/dimmed edge suggests that the downstream branch can be slid out (see help for `slide-out` and `traverse`).
 
-        Using colors can be disabled with a `--color` flag set to `never`.
-        With `--color=always`, git machete always emits colors and with `--color=auto`, it emits colors only when standard output is connected to a terminal.
-        `--color=auto` is the default. When colors are disabled, relation between branches is represented in the following way (not including the hash-comments):
+        Use of colors can be disabled with a `--color` flag set to `never`.
+        With `--color=always`, git machete always emits colors.
+        With `--color=auto` (the default), it emits colors only when standard output is connected to a terminal.
+        When colors are disabled, relation between branches is represented in the following way (not including the hash-comments):
         <dim>
           <branch0>
           |
@@ -971,7 +992,7 @@ long_docs: Dict[str, str] = {
         </dim>
 
         To make it easier to select branch name from the `status` output on certain terminals
-        (e.g. Alacritty), you can add an extra space between └─ and `branch name`
+        (like Alacritty), you can add an extra space between └─ and `branch name`
         by setting `git config machete.status.extraSpaceBeforeBranchName true`.
 
         For example, by default the status is displayed as:
@@ -994,7 +1015,7 @@ long_docs: Dict[str, str] = {
 
         <b>Options:</b>
            <b>--color=WHEN</b>
-              Colorize the output; WHEN can be `always`, `auto` (default; i.e. only if stdout is a terminal), or `never`.
+              Colorize the output; WHEN can be `always`, `auto` (default: colorize only if stdout is a terminal), or `never`.
            <b>-l</b>, <b>--list-commits</b>
               Additionally list the commits introduced on each branch.
            <b>-L</b>, <b>--list-commits-with-hashes</b>
@@ -1006,7 +1027,7 @@ long_docs: Dict[str, str] = {
         <b>Config keys:</b>
            `machete.status.extraSpaceBeforeBranchName`
               To make it easier to select branch name from the `status` output on certain terminals
-              (e.g. Alacritty), you can add an extra space between └─ and `branch name`
+              (like Alacritty), you can add an extra space between └─ and `branch name`
               by setting `git config machete.status.extraSpaceBeforeBranchName true`.
 
    """,
@@ -1017,39 +1038,39 @@ long_docs: Dict[str, str] = {
                                   [--[no-]push] [--[no-]push-untracked]
                                   [--return-to=WHERE] [--start-from=WHERE] [-w|--whole] [-W] [-y|--yes]</b>
 
-        Traverses the branch tree in pre-order (i.e. simply in the order as they occur in the branch layout file).
-        By default `traverse` starts from the current branch.
+        Traverses the branches in the order as they occur in branch layout file.
+        By default, `traverse` starts from the current branch.
         This behavior can, however, be customized using options: `--start-from=`, `--whole` or `-w`, `-W`.
 
         For each branch, the command:
            * detects if the branch is merged (grey edge) to its parent (aka upstream):
               - by commit equivalency (default), or by strict detection of merge commits (if `--no-detect-squash-merges` passed),
-              - if so, asks the user whether to slide out the branch from the dependency tree (typically branches are no longer needed after they're merged);
+              - if so, asks the user whether to <b>slide out</b> the branch from the dependency tree (typically branches are no longer needed after they're merged);
            * otherwise, if the branch has a <red>red</red> or <yellow>yellow</yellow> edge to its parent/upstream (see help for `status`):
-              - asks the user whether to rebase (default) or merge (if `--merge` passed) the branch onto into its upstream branch
-                — equivalent to `git machete update` with no `--fork-point` option passed;
+              - asks the user whether to <b>rebase</b> (default) or merge (if `--merge` passed) the branch onto into its upstream branch
+                — equivalent to `git machete update`;
            * if the branch is not tracked on a remote, is ahead of its remote counterpart, or diverged from the counterpart &
              has newer head commit than the counterpart:
-              - asks the user whether to push the branch (possibly with `--force-with-lease` if the branches diverged);
+              - asks the user whether to <b>push</b> the branch (possibly with `--force-with-lease` if the branches diverged);
            * otherwise, if the branch diverged from the remote counterpart & has older head commit than the counterpart:
-              - asks the user whether to `git reset --keep` the branch to its remote counterpart
+              - asks the user whether to <b>reset</b> (`git reset --keep`) the branch to its remote counterpart
            * otherwise, if the branch is behind its remote counterpart:
-              - asks the user whether to pull the branch;
+              - asks the user whether to <b>pull</b> the branch;
            * and finally, if any of the above operations has been successfully completed:
               - prints the updated `status`.
 
-        By default `traverse` asks if the branch should be pushed, this behavior can, however, be changed with the `machete.traverse.push` configuration key.
+        By default `traverse` asks if the branch should be pushed. This behavior can, however, be changed with the `machete.traverse.push` configuration key.
         It can also be customized using options: `--[no-]push` or `--[no-]push-untracked` — the order of the flags defines their precedence over each other
         (the one on the right overriding the ones on the left). More on them in the <b>Options</b> section below.
 
         If the traverse flow is stopped (typically due to merge/rebase conflicts), just run `git machete traverse` after the merge/rebase is finished.
-        It will pick up the walk from the current branch (unless `--start-from=` or `-w` etc. is passed).
-        Unlike with e.g. `git rebase`, there is no special `--continue` flag, as `traverse` is stateless
-        (doesn't keep a state of its own like `git rebase` does in `.git/rebase-apply/`).
+        It will pick up the walk from the current branch.
+        Unlike with `git rebase` or `git cherry-pick`, there is no special `--continue` flag, as `traverse` is stateless.
+        `traverse` doesn't keep a state of its own like `git rebase` does in `.git/rebase-apply/`.
 
         The rebase, push and slide-out behaviors of `traverse` can also be customized for each branch separately using branch qualifiers.
-        There are `rebase=no`, `push=no` and `slide-out=no` qualifiers that can be used to opt out of default behavior (rebasing, pushing and sliding the branch out of the dependency tree).
-        The qualifier can appear anywhere in the annotation but needs to be separated by a whitespace from any other character, e.g. `some_annotation_text rebase=no push=no slide-out=no`.
+        There are `push=no`, `rebase=no` and `slide-out=no` qualifiers that can be used to opt out of default behavior (rebasing, pushing and sliding the branch out).
+        The qualifier can appear anywhere in the annotation, but needs to be separated by a whitespace from any other character, as in: `some_annotation_text rebase=no push=no slide-out=no`.
         Qualifiers can only be overwritten by manually editing `.git/machete` file or modifying it with `git machete e[dit]`, or by updating annotations with `git machete anno`.
         Example machete file with branch qualifiers:
         <dim>
@@ -1061,7 +1082,7 @@ long_docs: Dict[str, str] = {
         </dim>
 
         Operations like `git machete github anno-prs` and `git machete github checkout-prs` add `rebase=no push=no` branch qualifiers
-        when the current user is <b>not</b> the owner of the PR associated with that branch. `git machete discover` doesn't modify the qualifiers.
+        when the current user is NOT the owner of the PR associated with that branch.
 
         <b>Options:</b>
            <b>-F</b>, <b>--fetch</b>
@@ -1077,7 +1098,7 @@ long_docs: Dict[str, str] = {
               when detecting if a branch is merged into its upstream (parent).
            <b>--no-edit-merge</b>
               If updating by merge, skip opening the editor for merge commit message while doing `git merge`
-              (i.e. pass `--no-edit` flag to the underlying `git merge`). Not allowed if updating by rebase.
+              (that is, pass `--no-edit` flag to the underlying `git merge`). Not allowed if updating by rebase.
            <b>--no-interactive-rebase</b>
               If updating by rebase, run `git rebase` in non-interactive mode (without `-i/--interactive` flag).
               Not allowed if updating by merge.
@@ -1147,7 +1168,7 @@ long_docs: Dict[str, str] = {
               If updating by merge, equivalent to `--no-edit-merge`.
            <b>--no-edit-merge</b>
               If updating by merge, skip opening the editor for merge commit message while doing `git merge`
-              (i.e. pass `--no-edit` flag to underlying `git merge`). Not allowed if updating by rebase.
+              (that is, pass `--no-edit` flag to underlying `git merge`). Not allowed if updating by rebase.
            <b>--no-interactive-rebase</b>
               If updating by rebase, run `git rebase` in non-interactive mode (without `-i/--interactive` flag).
               Not allowed if updating by merge.
diff --git a/git_machete/github.py b/git_machete/github.py
index 029440a..52c54c6 100644
--- a/git_machete/github.py
+++ b/git_machete/github.py
@@ -11,7 +11,7 @@ from typing import Any, Dict, List, NamedTuple, Optional, Tuple
 
 from .exceptions import MacheteException, UnprocessableEntityHTTPError
 from .git_operations import GitContext, LocalBranchShortName
-from .utils import bold, debug, fmt, popen_cmd, warn
+from .utils import bold, compact_dict, debug, fmt, popen_cmd, warn
 
 
 class GitHubPullRequest(NamedTuple):
@@ -21,6 +21,7 @@ class GitHubPullRequest(NamedTuple):
     head: str
     html_url: str
     state: str
+    description: Optional[str]
     full_repository_name: str
     repository_url: str
 
@@ -34,6 +35,7 @@ class GitHubPullRequest(NamedTuple):
                    head=pr_json['head']['ref'],
                    html_url=pr_json['html_url'],
                    state=pr_json['state'],
+                   description=pr_json['body'],
                    full_repository_name=pr_json['head']['repo']['full_name'] if pr_json['head']['repo'] else None,
                    repository_url=pr_json['head']['repo']['html_url'] if pr_json['head']['repo'] else None)
 
@@ -226,6 +228,8 @@ class GitHubClient:
 
         if self.__domain == self.DEFAULT_GITHUB_DOMAIN:
             url_prefix = 'https://api.' + self.__domain
+        elif path == '/graphql':
+            url_prefix = 'https://' + self.__domain + '/api'
         else:
             url_prefix = 'https://' + self.__domain + '/api/v3'
 
@@ -233,7 +237,7 @@ class GitHubClient:
         json_body: Optional[str] = json.dumps(request_body) if request_body else None
         http_request = urllib.request.Request(url, headers=headers, data=json_body.encode() if json_body else None, method=method.upper())
         debug(f'firing a {method} request to {url} with {"a" if self.__token else "no"} '
-              f'bearer token and request body {json_body or "<none>"}')
+              f'bearer token and request body {compact_dict(request_body) if request_body else "<none>"}')
 
         try:
             with urllib.request.urlopen(http_request) as response:
@@ -306,6 +310,9 @@ class GitHubClient:
         except OSError as e:  # pragma: no cover
             raise MacheteException(f'Could not connect to {url_prefix}: {e}')
 
+    def __fire_github_graphql_api_request(self, query: str) -> Any:
+        return self.__fire_github_api_request(method='POST', path='/graphql', request_body={"query": query})
+
     @staticmethod
     def __extract_failure_info_from_422(response: Any) -> str:
         if response['message'] != 'Validation Failed':
@@ -356,6 +363,12 @@ class GitHubClient:
                                        path=f'/repos/{self.__organization}/{self.__repository}/pulls/{number}',
                                        request_body=request_body)
 
+    def set_description_of_pull_request(self, number: int, description: str) -> None:
+        request_body: Dict[str, str] = {'body': description}
+        self.__fire_github_api_request(method='PATCH',
+                                       path=f'/repos/{self.__organization}/{self.__repository}/pulls/{number}',
+                                       request_body=request_body)
+
     def set_milestone_of_pull_request(self, number: int, milestone: str) -> None:
         request_body: Dict[str, str] = {'milestone': milestone}
         # Setting milestone is only available via the Issues API, not PRs API.
@@ -363,17 +376,63 @@ class GitHubClient:
                                        path=f'/repos/{self.__organization}/{self.__repository}/issues/{number}',
                                        request_body=request_body)
 
-    def derive_open_pull_requests_by_head(self, head: LocalBranchShortName) -> List[GitHubPullRequest]:
+    # As of September 2023, REST (v3) GitHub API does **not** allow for setting PR draft status,
+    # only for creating a draft PR or retrieving draft status on an existing PR.
+    # See https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#update-a-pull-request
+    # and https://github.com/orgs/community/discussions/45174.
+    # GraphQL (v4) API mutation needs to be used for that purpose.
+    def set_draft_status_of_pull_request(self, number: int, target_draft_status: bool) -> bool:
+        """Returns true if PR had a different draft status, and draft status has been toggled.
+        Returns false if PR already had the desired draft status, and hence draft status has NOT been toggled."""
+
+        # This query is required to get the GraphQL-specific id of the PR
+        query = f"""query {{
+            repository(owner: "{self.organization}", name: "{self.repository}") {{
+                pullRequest(number: {number}) {{
+                    id
+                    isDraft
+                }}
+            }}
+        }}"""
+        response = self.__fire_github_graphql_api_request(query)
+        debug(f"query response is {response}")
+        is_draft = response["data"]["repository"]["pullRequest"]["isDraft"]
+        if is_draft and target_draft_status is True:
+            debug(f"PR #{number} is already a draft")
+            return False
+        if not is_draft and target_draft_status is False:
+            # This case is not covered by tests since there's currently no scenario
+            # in `git machete github restack-pr` that could reach here.
+            debug(f"PR #{number} is already ready for review")
+            return False
+
+        # Ids are of the form "PR_kwDOB1DpPc5bDwiF"
+        graphql_id = response["data"]["repository"]["pullRequest"]["id"]
+
+        mutation = "convertPullRequestToDraft" if target_draft_status else "markPullRequestReadyForReview"
+        query = f"""mutation {{
+            {mutation}(input: {{pullRequestId: "{graphql_id}"}}) {{
+                pullRequest {{
+                    id
+                    isDraft
+                }}
+            }}
+        }}"""
+        response = self.__fire_github_graphql_api_request(query)
+        debug(f"mutation response is {response}")
+        return True
+
+    def get_open_pull_requests_by_head(self, head: LocalBranchShortName) -> List[GitHubPullRequest]:
         path = f'/repos/{self.__organization}/{self.__repository}/pulls?head={self.__organization}:{head}'
         prs = self.__fire_github_api_request(method='GET', path=path)
         return [GitHubPullRequest.from_json(pr) for pr in prs]
 
-    def derive_open_pull_requests(self) -> List[GitHubPullRequest]:
+    def get_open_pull_requests(self) -> List[GitHubPullRequest]:
         path = f'/repos/{self.__organization}/{self.__repository}/pulls?per_page={self.MAX_PULLS_PER_PAGE_COUNT}'
         prs = self.__fire_github_api_request(method='GET', path=path)
         return list(map(GitHubPullRequest.from_json, prs))
 
-    def derive_current_user_login(self) -> Optional[str]:
+    def get_current_user_login(self) -> Optional[str]:
         if not self.__token:
             return None
         user = self.__fire_github_api_request(method='GET', path='/user')
diff --git a/git_machete/utils.py b/git_machete/utils.py
index f2e3895..1ccf253 100644
--- a/git_machete/utils.py
+++ b/git_machete/utils.py
@@ -99,6 +99,10 @@ def find_executable(executable: str) -> Optional[str]:
     return None
 
 
+def compact_dict(d: Dict[str, Any]) -> Dict[str, str]:
+    return {k: re.sub('\n +', ' ', str(v), re.MULTILINE) for k, v in d.items()}
+
+
 def debug(msg: str) -> None:
     if debug_mode:
         function_name = bold(inspect.stack()[1].function)
@@ -108,10 +112,10 @@ def debug(msg: str) -> None:
         for arg, value in values.items():
             if arg in args_to_be_redacted or any(value_ in str(value) for value_ in GITHUB_TOKEN_PREFIXES):
                 values[arg] = '***'
+            if type(values[arg]) is dict:
+                values[arg] = compact_dict(values[arg])
 
-        excluded_args = {'self'}
-        allowed_args = excluding(args, excluded_args)
-        args_and_values_list = [arg + '=' + str(values[arg]) for arg in allowed_args]
+        args_and_values_list = [arg + '=' + str(values[arg]) for arg in excluding(args, {'self'})]
         args_and_values_str = ', '.join(args_and_values_list)
         args_and_values_bold_str = bold(f'({args_and_values_str})')
 
diff --git a/requirements/coverage.txt b/requirements/coverage.txt
index 42351af..6f77dea 100644
--- a/requirements/coverage.txt
+++ b/requirements/coverage.txt
@@ -6,4 +6,5 @@ pytest==7.0.1; python_version <= '3.6'
 pytest==7.4.2; python_version >= '3.7'
 
 pytest-mock==3.6.1; python_version <= '3.6'
-pytest-mock==3.11.1; python_version >= '3.7'
+pytest-mock==3.11.1; python_version >= '3.7' and python_version < '3.8'
+pytest-mock==3.12.0; python_version >= '3.8'
diff --git a/requirements/mypy.txt b/requirements/mypy.txt
index df1df48..e294b44 100644
--- a/requirements/mypy.txt
+++ b/requirements/mypy.txt
@@ -1,9 +1,10 @@
 mypy==0.971; python_version <= '3.6'
 mypy==1.4.1; python_version >= '3.7' and python_version < '3.8'
-mypy==1.5.1; python_version >= '3.8'
+mypy==1.6.1; python_version >= '3.8'
 
 pytest==7.0.1; python_version <= '3.6'
 pytest==7.4.2; python_version >= '3.7'
 
 pytest-mock==3.6.1; python_version <= '3.6'
-pytest-mock==3.11.1; python_version >= '3.7'
+pytest-mock==3.11.1; python_version >= '3.7' and python_version < '3.8'
+pytest-mock==3.12.0; python_version >= '3.8'


Test patch:
diff --git a/requirements/testenv.txt b/requirements/testenv.txt
index 2bd2443..886d1c2 100644
--- a/requirements/testenv.txt
+++ b/requirements/testenv.txt
@@ -2,7 +2,8 @@ pytest==7.0.1; python_version <= '3.6'
 pytest==7.4.2; python_version >= '3.7'
 
 pytest-mock==3.6.1; python_version <= '3.6'
-pytest-mock==3.11.1; python_version >= '3.7'
+pytest-mock==3.11.1; python_version >= '3.7' and python_version < '3.8'
+pytest-mock==3.12.0; python_version >= '3.8'
 
 pytest-xdist==2.4.0; python_version <= '3.6'
 pytest-xdist==3.3.1; python_version >= '3.7'
diff --git a/tests/completion_e2e/test_completion_e2e.py b/tests/completion_e2e/test_completion_e2e.py
index 070831f..11899f2 100644
--- a/tests/completion_e2e/test_completion_e2e.py
+++ b/tests/completion_e2e/test_completion_e2e.py
@@ -57,7 +57,7 @@ test_cases: Dict[str, str] = {
     "git machete fork-point --unset-override ":
         "develop",
     "git machete github ":
-        "anno-prs checkout-prs create-pr retarget-pr sync",
+        "anno-prs checkout-prs create-pr restack-pr retarget-pr sync",
     "git machete github checkout-prs -":
         "--all --by --debug -h --help --mine -v --verbose",
     "git machete github create-pr -":
diff --git a/tests/mockers_github.py b/tests/mockers_github.py
index 24e56a7..0e1860e 100644
--- a/tests/mockers_github.py
+++ b/tests/mockers_github.py
@@ -14,6 +14,26 @@ mock_repository_info: Dict[str, str] = {'full_name': 'tester/repo_sandbox',
                                         'html_url': 'https://github.com/tester/repo_sandbox.git'}
 
 
+def mock_pr_json(head: str, base: str, number: int,
+                 repo: Optional[Dict[str, str]] = mock_repository_info,
+                 user: str = 'some_other_user',
+                 html_url: str = 'www.github.com',
+                 body: Optional[str] = '# Summary',
+                 state: str = 'open',
+                 draft: bool = False
+                 ) -> Dict[str, Any]:
+    return {
+        'head': {'ref': head, 'repo': repo},
+        'user': {'login': user},
+        'base': {'ref': base},
+        'number': str(number),
+        'html_url': html_url,
+        'body': body,
+        'state': state,
+        'draft': draft
+    }
+
+
 def mock_github_token_for_domain_none(_domain: str) -> None:
     return None
 
@@ -47,12 +67,12 @@ class MockGitHubAPIResponse:
 
 
 class MockGitHubAPIState:
-    def __init__(self, pulls: List[Dict[str, Any]]) -> None:
+    def __init__(self, *pulls: Dict[str, Any]) -> None:
         self.__pulls: List[Dict[str, Any]] = [dict(pull) for pull in pulls]
 
-    def get_pull_by_number(self, pull_no: str) -> Optional[Dict[str, Any]]:
+    def get_pull_by_number(self, pull_no: int) -> Optional[Dict[str, Any]]:
         for pull in self.__pulls:
-            if pull['number'] == pull_no:
+            if pull['number'] == str(pull_no):
                 return pull
         return None
 
@@ -142,10 +162,10 @@ def __mock_urlopen_impl(github_api_state: MockGitHubAPIState, request: Request)
                     link_header = ''
                 return MockGitHubAPIResponse(HTTPStatus.OK, response_data=pulls[start:end], headers={'link': link_header})
         elif url_path_matches('/repos/*/*/pulls/[0-9]+'):
-            number = url_segments[-1]
-            prs_ = github_api_state.get_pull_by_number(number)
-            if prs_:
-                return MockGitHubAPIResponse(HTTPStatus.OK, prs_)
+            pull_no = int(url_segments[-1])
+            pull = github_api_state.get_pull_by_number(pull_no)
+            if pull:
+                return MockGitHubAPIResponse(HTTPStatus.OK, pull)
             raise error_404()
         elif url_path_matches('/user'):
             return MockGitHubAPIResponse(HTTPStatus.OK, {'login': 'github_user', 'type': 'User', 'company': 'VirtusLab'})
@@ -170,8 +190,8 @@ def __mock_urlopen_impl(github_api_state: MockGitHubAPIState, request: Request)
                 raise error_422({'message': 'Validation Failed', 'errors': [
                     {'message': f'A pull request already exists for test_repo:{head}.'}]})
             return create_pull_request()
-        elif url_path_matches("/repos/*/*/(pulls|issues)/[0-9]+/*"):
-            pull_no = url_segments[-2]  # e.g. /repos/example-org/example-repo/pulls/5/requested_reviewers
+        elif url_path_matches("/repos/*/*/(pulls|issues)/[0-9]+/(assignees|requested_reviewers)"):
+            pull_no = int(url_segments[-2])
             pull = github_api_state.get_pull_by_number(pull_no)
             assert pull is not None
             if "invalid-user" in list(json_data.values())[0]:
@@ -182,11 +202,35 @@ def __mock_urlopen_impl(github_api_state: MockGitHubAPIState, request: Request)
             else:
                 fill_pull_request_from_json_data(pull)
                 return MockGitHubAPIResponse(HTTPStatus.OK, pull)
+        elif parsed_url.path in ("/api/graphql", "/graphql"):  # /api/graphql for Enterprise domains
+            query_or_mutation = json_data['query']
+            if 'query {' in query_or_mutation:
+                match = re.search(r'pullRequest\(number: ([0-9]+)\)', query_or_mutation)
+                assert match is not None
+                pr_number = int(match.group(1))
+                pr = github_api_state.get_pull_by_number(pr_number)
+                assert pr is not None
+                pr_is_draft: bool = pr.get("draft") is True
+                return MockGitHubAPIResponse(HTTPStatus.OK, {
+                    # Let's just use PR number as PR GraphQL id, for simplicity
+                    'data': {'repository': {'pullRequest': {'id': str(pr_number), 'isDraft': pr_is_draft}}}
+                })
+            else:
+                match = re.search(r'([a-zA-Z]+)\(input: \{pullRequestId: "([0-9]+)"}\)', query_or_mutation)
+                assert match is not None
+                target_draft_state = match.group(1) == "convertPullRequestToDraft"
+                pr_number = int(match.group(2))
+                pr = github_api_state.get_pull_by_number(pr_number)
+                assert pr is not None
+                pr['draft'] = target_draft_state
+                return MockGitHubAPIResponse(HTTPStatus.OK, {
+                    'data': {'repository': {'pullRequest': {'id': str(pr_number), 'isDraft': target_draft_state}}}
+                })
         else:
             raise error_404()
 
     def update_pull_request() -> "MockGitHubAPIResponse":
-        pull_no = url_segments[-1]
+        pull_no = int(url_segments[-1])
         pull = github_api_state.get_pull_by_number(pull_no)
         assert pull is not None
         fill_pull_request_from_json_data(pull)
@@ -195,6 +239,7 @@ def __mock_urlopen_impl(github_api_state: MockGitHubAPIState, request: Request)
     def create_pull_request() -> "MockGitHubAPIResponse":
         pull = {'user': {'login': 'some_other_user'},
                 'html_url': 'www.github.com',
+                'body': '# Summary',
                 'state': 'open',
                 'head': {'ref': "", 'repo': {'full_name': 'testing:checkout_prs', 'html_url': 'https:/example.org/pull/1234'}},
                 'base': {'ref': ""}}
diff --git a/tests/test_github.py b/tests/test_github.py
index 39a902a..782e8fd 100644
--- a/tests/test_github.py
+++ b/tests/test_github.py
@@ -16,7 +16,7 @@ from tests.mockers import (assert_failure, assert_success, launch_command,
 from tests.mockers_github import (MockGitHubAPIState, mock_from_url,
                                   mock_github_token_for_domain_fake,
                                   mock_github_token_for_domain_none,
-                                  mock_repository_info, mock_shutil_which,
+                                  mock_pr_json, mock_shutil_which,
                                   mock_urlopen)
 
 
@@ -39,15 +39,11 @@ class TestGitHub(BaseTest):
 
     PR_COUNT_FOR_TEST_GITHUB_API_PAGINATION = 12
 
-    github_api_state_for_test_github_api_pagination = MockGitHubAPIState([{
-        'head': {'ref': f'feature_{i:02d}', 'repo': {'full_name': 'tester/repo_sandbox',
-                                                     'html_url': 'https://github.com/tester/repo_sandbox.git'}},
-        'user': {'login': 'some_other_user'},
-        'base': {'ref': 'develop'},
-        'number': str(i),
-        'html_url': 'www.github.com',
-        'state': 'open'
-    } for i in range(PR_COUNT_FOR_TEST_GITHUB_API_PAGINATION)])
+    github_api_state_for_test_github_api_pagination = MockGitHubAPIState(*[
+        mock_pr_json(
+            head=f'feature_{i:02d}', base='develop', number=i,
+            repo={'full_name': 'tester/repo_sandbox', 'html_url': 'https://github.com/tester/repo_sandbox.git'}
+        ) for i in range(PR_COUNT_FOR_TEST_GITHUB_API_PAGINATION)])
 
     def test_github_api_pagination(self, mocker: MockerFixture) -> None:
         self.patch_symbol(mocker, 'builtins.input', mock_input_returning_y)
@@ -82,7 +78,7 @@ class TestGitHub(BaseTest):
     def test_github_enterprise_domain_unauthorized_without_token(self, mocker: MockerFixture) -> None:
         self.patch_symbol(mocker, 'git_machete.github.GitHubToken.for_domain', mock_github_token_for_domain_none)
         self.patch_symbol(mocker, 'git_machete.github.OrganizationAndRepository.from_url', mock_from_url)
-        self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(MockGitHubAPIState([])))
+        self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(MockGitHubAPIState()))
 
         self.repo_sandbox.set_git_config_key('machete.github.domain', '403.example.org')
 
@@ -97,7 +93,7 @@ class TestGitHub(BaseTest):
     def test_github_enterprise_domain_unauthorized_with_token(self, mocker: MockerFixture) -> None:
         self.patch_symbol(mocker, 'git_machete.github.GitHubToken.for_domain', mock_github_token_for_domain_fake)
         self.patch_symbol(mocker, 'git_machete.github.OrganizationAndRepository.from_url', mock_from_url)
-        self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(MockGitHubAPIState([])))
+        self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(MockGitHubAPIState()))
 
         self.repo_sandbox.set_git_config_key('machete.github.domain', '403.example.org')
 
@@ -109,16 +105,7 @@ class TestGitHub(BaseTest):
         assert_failure(['github', 'checkout-prs', '--all'], expected_error_message)
 
     github_api_state_for_test_github_enterprise_domain = MockGitHubAPIState(
-        [
-            {
-                'head': {'ref': 'snickers', 'repo': mock_repository_info},
-                'user': {'login': 'github_user'},
-                'base': {'ref': 'develop'},
-                'number': '7',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            }
-        ]
+        mock_pr_json(head='snickers', base='develop', number=7, user='github_user')
     )
 
     def test_github_enterprise_domain(self, mocker: MockerFixture) -> None:
diff --git a/tests/test_github_anno_prs.py b/tests/test_github_anno_prs.py
index 19f484d..c82bcba 100644
--- a/tests/test_github_anno_prs.py
+++ b/tests/test_github_anno_prs.py
@@ -5,25 +5,16 @@ from tests.mockers import (assert_failure, assert_success, launch_command,
                            rewrite_branch_layout_file)
 from tests.mockers_github import (MockGitHubAPIState,
                                   mock_github_token_for_domain_fake,
-                                  mock_repository_info, mock_urlopen)
+                                  mock_pr_json, mock_urlopen)
 
 
 class TestGitHubAnnoPRs(BaseTest):
 
     github_api_state_for_test_anno_prs = MockGitHubAPIState(
-        [{
-            'head': {'ref': head, 'repo': mock_repository_info},
-            'user': {'login': user},
-            'base': {'ref': base},
-            'number': str(number),
-            'html_url': 'www.github.com',
-            'state': 'open'
-        } for (number, user, head, base) in (
-            (3, 'some_other_user', 'ignore-trailing', 'hotfix/add-trigger'),
-            (7, 'some_other_user', 'allow-ownership-link', 'develop'),
-            (31, 'github_user', 'call-ws', 'develop'),
-            (37, 'github_user', 'develop', 'master'),
-        )]
+        mock_pr_json(number=3, user='some_other_user', head='ignore-trailing', base='hotfix/add-trigger'),
+        mock_pr_json(number=7, user='some_other_user', head='allow-ownership-link', base='develop'),
+        mock_pr_json(number=31, user='github_user', head='call-ws', base='develop'),
+        mock_pr_json(number=37, user='github_user', head='develop', base='master')
     )
 
     def test_github_anno_prs(self, mocker: MockerFixture) -> None:
@@ -141,20 +132,8 @@ class TestGitHubAnnoPRs(BaseTest):
         )
 
     github_api_state_for_test_local_branch_name_different_than_tracking_branch_name = MockGitHubAPIState(
-        [
-            {
-                'head': {'ref': 'feature_repo', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'root'}, 'number': '15',
-                'html_url': 'www.github.com', 'state': 'open'
-            },
-            {
-                'head': {'ref': 'feature_1', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'feature_repo'}, 'number': '20',
-                'html_url': 'www.github.com', 'state': 'open'
-            }
-        ]
+        mock_pr_json(head='feature_repo', base='root', number=15),
+        mock_pr_json(head='feature_1', base='feature_repo', number=20)
     )
 
     def test_github_anno_prs_local_branch_name_different_than_tracking_branch_name(self, mocker: MockerFixture) -> None:
diff --git a/tests/test_github_checkout_prs.py b/tests/test_github_checkout_prs.py
index 16a813e..8414f37 100644
--- a/tests/test_github_checkout_prs.py
+++ b/tests/test_github_checkout_prs.py
@@ -10,85 +10,22 @@ from tests.mockers import (assert_failure, assert_success, launch_command,
 from tests.mockers_github import (MockGitHubAPIState, mock_from_url,
                                   mock_github_token_for_domain_fake,
                                   mock_github_token_for_domain_none,
-                                  mock_repository_info, mock_urlopen)
+                                  mock_pr_json, mock_repository_info,
+                                  mock_urlopen)
 
 
 class TestGitHubCheckoutPRs(BaseTest):
     github_api_state_for_test_checkout_prs = MockGitHubAPIState(
-        [
-            {
-                'head': {'ref': 'chore/redundant_checks', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'restrict_access'},
-                'number': '18',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            },
-            {
-                'head': {'ref': 'restrict_access', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'allow-ownership-link'},
-                'number': '17',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            },
-            {
-                'head': {'ref': 'allow-ownership-link', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'bugfix/feature'},
-                'number': '12',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            },
-            {
-                'head': {'ref': 'bugfix/feature', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'enhance/feature'},
-                'number': '6',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            },
-            {
-                'head': {'ref': 'enhance/add_user', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'develop'},
-                'number': '19',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            },
-            {
-                'head': {'ref': 'testing/add_user', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'bugfix/add_user'},
-                'number': '22',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            },
-            {'head': {'ref': 'chore/comments', 'repo': mock_repository_info},
-             'user': {'login': 'some_other_user'},
-             'base': {'ref': 'testing/add_user'},
-             'number': '24',
-             'html_url': 'www.github.com',
-             'state': 'open'
-             },
-            {
-                'head': {'ref': 'ignore-trailing', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'hotfix/add-trigger'},
-                'number': '3',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            },
-            {
-                'head': {'ref': 'bugfix/remove-n-option',
-                         'repo': {'full_name': 'tester/repo_sandbox', 'html_url': GitRepositorySandbox.second_remote_path}},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'develop'},
-                'number': '5',
-                'html_url': 'www.github.com',
-                'state': 'closed'
-            }
-        ]
+        mock_pr_json(head='chore/redundant_checks', base='restrict_access', number=18),
+        mock_pr_json(head='restrict_access', base='allow-ownership-link', number=17),
+        mock_pr_json(head='allow-ownership-link', base='bugfix/feature', number=12),
+        mock_pr_json(head='bugfix/feature', base='enhance/feature', number=6),
+        mock_pr_json(head='enhance/add_user', base='develop', number=19),
+        mock_pr_json(head='testing/add_user', base='bugfix/add_user', number=22),
+        mock_pr_json(head='chore/comments', base='testing/add_user', number=24),
+        mock_pr_json(head='ignore-trailing', base='hotfix/add-trigger', number=3),
+        mock_pr_json(head='bugfix/remove-n-option', base='develop', number=5, state='closed',
+                     repo={'full_name': 'tester/repo_sandbox', 'html_url': GitRepositorySandbox.second_remote_path})
     )
 
     def test_github_checkout_prs(self, mocker: MockerFixture) -> None:
@@ -343,41 +280,11 @@ class TestGitHubCheckoutPRs(BaseTest):
         assert_success(['github', 'checkout-prs', '3', '12'], expected_msg)
 
     github_api_state_for_test_github_checkout_prs_fresh_repo = MockGitHubAPIState(
-        [
-            {
-                'head': {'ref': 'comments/add_docstrings', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'improve/refactor'},
-                'number': '2',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            },
-            {
-                'head': {'ref': 'restrict_access', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'allow-ownership-link'},
-                'number': '17',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            },
-            {
-                'head': {'ref': 'improve/refactor', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'chore/sync_to_docs'},
-                'number': '1',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            },
-            {
-                'head': {'ref': 'sphinx_export',
-                         'repo': {'full_name': 'tester/repo_sandbox', 'html_url': GitRepositorySandbox.second_remote_path}},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'comments/add_docstrings'},
-                'number': '23',
-                'html_url': 'www.github.com',
-                'state': 'closed'
-            }
-        ]
+        mock_pr_json(head='comments/add_docstrings', base='improve/refactor', number=2),
+        mock_pr_json(head='restrict_access', base='allow-ownership-link', number=17),
+        mock_pr_json(head='improve/refactor', base='chore/sync_to_docs', number=1),
+        mock_pr_json(head='sphinx_export', base='comments/add_docstrings', number=23, state='closed',
+                     repo={'full_name': 'tester/repo_sandbox', 'html_url': GitRepositorySandbox.second_remote_path})
     )
 
     def test_github_checkout_prs_freshly_cloned(self, mocker: MockerFixture) -> None:
@@ -478,23 +385,8 @@ class TestGitHubCheckoutPRs(BaseTest):
         )
 
     github_api_state_for_test_github_checkout_prs_from_fork_with_deleted_repo = MockGitHubAPIState(
-        [
-            {
-                'head': {'ref': 'feature/allow_checkout', 'repo': None},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'develop'},
-                'number': '2',
-                'html_url': 'www.github.com',
-                'state': 'closed'
-            },
-            {
-                'head': {'ref': 'bugfix/allow_checkout', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'develop'},
-                'number': '3',
-                'html_url': 'www.github.com',
-                'state': 'open'}
-        ]
+        mock_pr_json(head='feature/allow_checkout', base='develop', number=2, repo=None, state='closed'),
+        mock_pr_json(head='bugfix/allow_checkout', base='develop', number=3)
     )
 
     def test_github_checkout_prs_from_fork_with_deleted_repo(self, mocker: MockerFixture) -> None:
@@ -539,81 +431,16 @@ class TestGitHubCheckoutPRs(BaseTest):
              )
 
     github_api_state_for_test_github_checkout_prs_of_current_user_and_other_users = MockGitHubAPIState(
-        [
-            {
-                'head': {'ref': 'chore/redundant_checks', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'restrict_access'},
-                'number': '18',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            },
-            {
-                'head': {'ref': 'restrict_access', 'repo': mock_repository_info},
-                'user': {'login': 'github_user'},
-                'base': {'ref': 'allow-ownership-link'},
-                'number': '17',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            },
-            {
-                'head': {'ref': 'allow-ownership-link', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'bugfix/feature'},
-                'number': '12',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            },
-            {
-                'head': {'ref': 'bugfix/feature', 'repo': mock_repository_info},
-                'user': {'login': 'github_user'},
-                'base': {'ref': 'enhance/feature'},
-                'number': '6',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            },
-            {
-                'head': {'ref': 'enhance/add_user', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'develop'},
-                'number': '19',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            },
-            {
-                'head': {'ref': 'testing/add_user', 'repo': mock_repository_info},
-                'user': {'login': 'github_user'},
-                'base': {'ref': 'bugfix/add_user'},
-                'number': '22',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            },
-            {
-                'head': {'ref': 'chore/comments', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'testing/add_user'},
-                'number': '24',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            },
-            {
-                'head': {'ref': 'ignore-trailing', 'repo': mock_repository_info},
-                'user': {'login': 'github_user'},
-                'base': {'ref': 'hotfix/add-trigger'},
-                'number': '3',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            },
-            {
-                'head': {'ref': 'bugfix/remove-n-option',
-                         'repo': {'full_name': 'tester/repo_sandbox', 'html_url': GitRepositorySandbox.second_remote_path}},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'develop'},
-                'number': '5',
-                'html_url': 'www.github.com',
-                'state': 'closed'
-            }
-        ]
+        mock_pr_json(head='chore/redundant_checks', base='restrict_access', number=18),
+        mock_pr_json(head='restrict_access', base='allow-ownership-link', number=17, user='github_user'),
+        mock_pr_json(head='allow-ownership-link', base='bugfix/feature', number=12),
+        mock_pr_json(head='bugfix/feature', base='enhance/feature', number=6, user='github_user'),
+        mock_pr_json(head='enhance/add_user', base='develop', number=19),
+        mock_pr_json(head='testing/add_user', base='bugfix/add_user', number=22, user='github_user'),
+        mock_pr_json(head='chore/comments', base='testing/add_user', number=24),
+        mock_pr_json(head='ignore-trailing', base='hotfix/add-trigger', number=3, user='github_user'),
+        mock_pr_json(head='bugfix/remove-n-option', base='develop', number=5, state='closed',
+                     repo={'full_name': 'tester/repo_sandbox', 'html_url': GitRepositorySandbox.second_remote_path})
     )
 
     def test_github_checkout_prs_of_current_user_and_other_users(self, mocker: MockerFixture) -> None:
@@ -768,7 +595,7 @@ class TestGitHubCheckoutPRs(BaseTest):
 
     def test_github_checkout_prs_misc_failures_and_warns(self, mocker: MockerFixture) -> None:
         self.patch_symbol(mocker, 'git_machete.github.OrganizationAndRepository.from_url', mock_from_url)
-        self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(MockGitHubAPIState([])))
+        self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(MockGitHubAPIState()))
 
         self.patch_symbol(mocker, 'git_machete.github.GitHubToken.for_domain', mock_github_token_for_domain_none)
         assert_success(
@@ -808,16 +635,7 @@ class TestGitHubCheckoutPRs(BaseTest):
         )
 
     github_api_state_for_test_github_checkout_prs_single_pr = MockGitHubAPIState(
-        [
-            {
-                'head': {'ref': 'develop', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'master'},
-                'number': '18',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            }
-        ]
+        mock_pr_json(head='develop', base='master', number=18)
     )
 
     def test_github_checkout_prs_remote_already_added(self, mocker: MockerFixture) -> None:
diff --git a/tests/test_github_create_pr.py b/tests/test_github_create_pr.py
index b70c9fc..4186435 100644
--- a/tests/test_github_create_pr.py
+++ b/tests/test_github_create_pr.py
@@ -11,22 +11,13 @@ from tests.mockers import (assert_failure, assert_success,
 from tests.mockers_github import (MockGitHubAPIState, mock_from_url,
                                   mock_github_token_for_domain_fake,
                                   mock_github_token_for_domain_none,
-                                  mock_repository_info, mock_urlopen)
+                                  mock_pr_json, mock_urlopen)
 
 
 class TestGitHubCreatePR(BaseTest):
 
     github_api_state_for_test_create_pr = MockGitHubAPIState(
-        [
-            {
-                'head': {'ref': 'ignore-trailing', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'hotfix/add-trigger'},
-                'number': '3',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            }
-        ]
+        mock_pr_json(head='ignore-trailing', base='hotfix/add-trigger', number=3)
     )
 
     def test_github_create_pr(self, mocker: MockerFixture) -> None:
@@ -82,7 +73,7 @@ class TestGitHubCreatePR(BaseTest):
             """
             master
                 hotfix/add-trigger
-                    ignore-trailing
+                    ignore-trailing  PR #3
                         chore/fields
             develop
                 allow-ownership-link
@@ -101,7 +92,7 @@ class TestGitHubCreatePR(BaseTest):
             |
             o-hotfix/add-trigger (diverged from origin)
               |
-              o-ignore-trailing (diverged from & older than origin)
+              o-ignore-trailing  PR #3 (diverged from & older than origin)
                 |
                 o-chore/fields (untracked)
 
@@ -131,7 +122,7 @@ class TestGitHubCreatePR(BaseTest):
               |
               o-hotfix/add-trigger (diverged from origin)
                 |
-                o-ignore-trailing (diverged from & older than origin)
+                o-ignore-trailing  PR #3 (diverged from & older than origin)
                   |
                   o-chore/fields *
 
@@ -152,6 +143,14 @@ class TestGitHubCreatePR(BaseTest):
             Adding foo, bar as reviewers to PR #5... OK
             """
         )
+        pr = self.github_api_state_for_test_create_pr.get_pull_by_number(5)
+        assert pr is not None
+        assert pr['body'] == '# Based on PR #3\n\n# PR title\n## Summary\n##Test plan\n'
+        assert pr['draft'] is True
+        assert pr['milestone'] == '42'
+        assert pr['assignees'] == ['github_user']
+        assert pr['reviewers'] == ['foo', 'bar']
+
         assert_success(
             ['status'],
             """
@@ -159,7 +158,7 @@ class TestGitHubCreatePR(BaseTest):
             |
             o-hotfix/add-trigger (diverged from origin)
               |
-              o-ignore-trailing (diverged from & older than origin)
+              o-ignore-trailing  PR #3 (diverged from & older than origin)
                 |
                 o-chore/fields *  PR #5
 
@@ -190,7 +189,7 @@ class TestGitHubCreatePR(BaseTest):
             |
             o-hotfix/add-trigger *  PR #6
               |
-              x-ignore-trailing (diverged from & older than origin)
+              x-ignore-trailing  PR #3 (diverged from & older than origin)
                 |
                 o-chore/fields  PR #5
 
@@ -285,16 +284,7 @@ class TestGitHubCreatePR(BaseTest):
         )
 
     github_api_state_for_test_create_pr_missing_base_branch_on_remote = MockGitHubAPIState(
-        [
-            {
-                'head': {'ref': 'chore/redundant_checks', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'restrict_access'},
-                'number': '18',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            }
-        ]
+        mock_pr_json(head='chore/redundant_checks', base='restrict_access', number=18)
     )
 
     def test_github_create_pr_missing_base_branch_on_remote(self, mocker: MockerFixture) -> None:
@@ -342,14 +332,7 @@ class TestGitHubCreatePR(BaseTest):
         )
 
     github_api_state_for_test_github_create_pr_with_multiple_non_origin_remotes = MockGitHubAPIState(
-        [
-            {
-                'head': {'ref': 'branch-1', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'root'}, 'number': '15',
-                'html_url': 'www.github.com', 'state': 'open'
-            }
-        ]
+        mock_pr_json(head='branch-1', base='root', number=15)
     )
 
     def test_github_create_pr_with_multiple_non_origin_remotes(self, mocker: MockerFixture) -> None:
@@ -395,7 +378,7 @@ class TestGitHubCreatePR(BaseTest):
         Branch feature is untracked and there's no origin repository.
         [1] origin_1
         [2] origin_2
-        Select number 1..2 to specify the destination remote repository, or 'q' to quit creating pull request:
+        Select number 1..2 to specify the destination remote repository, or 'q' to quit the operation:
         """
         assert_success(
             ['github', 'create-pr'],
@@ -411,7 +394,7 @@ class TestGitHubCreatePR(BaseTest):
         self.patch_symbol(mocker, 'builtins.input', mock_input_returning('xd'))
         assert_failure(
             ['github', 'create-pr'],
-            "Could not establish remote repository, pull request creation interrupted."
+            "Could not establish remote repository, operation interrupted."
         )
 
         self.patch_symbol(mocker, 'builtins.input', mock_input_returning('1', 'n'))
@@ -426,7 +409,7 @@ class TestGitHubCreatePR(BaseTest):
         Branch feature is untracked and there's no origin repository.
         [1] origin_1
         [2] origin_2
-        Select number 1..2 to specify the destination remote repository, or 'q' to quit creating pull request:
+        Select number 1..2 to specify the destination remote repository, or 'q' to quit the operation:
         Branch feature is untracked, but its remote counterpart candidate origin_1/feature already exists and both branches point to the same commit.
         Set the remote of feature to origin_1 without pushing or pulling? (y, N, q, yq)
         """  # noqa: E501
@@ -450,7 +433,7 @@ class TestGitHubCreatePR(BaseTest):
         Branch feature is untracked and there's no origin repository.
         [1] origin_1
         [2] origin_2
-        Select number 1..2 to specify the destination remote repository, or 'q' to quit creating pull request:
+        Select number 1..2 to specify the destination remote repository, or 'q' to quit the operation:
         Branch feature is untracked, but its remote counterpart candidate origin_1/feature already exists and both branches point to the same commit.
         Set the remote of feature to origin_1 without pushing or pulling? (y, N, q, yq)
 
@@ -514,7 +497,7 @@ class TestGitHubCreatePR(BaseTest):
         Branch feature_2 is untracked and there's no origin repository.
         [1] origin_1
         [2] origin_2
-        Select number 1..2 to specify the destination remote repository, or 'q' to quit creating pull request:
+        Select number 1..2 to specify the destination remote repository, or 'q' to quit the operation:
         Push untracked branch feature_2 to origin_1? (y, Q)
 
           root
@@ -603,7 +586,7 @@ class TestGitHubCreatePR(BaseTest):
     def test_github_create_pr_for_no_push_qualifier(self, mocker: MockerFixture) -> None:
         self.patch_symbol(mocker, 'git_machete.github.OrganizationAndRepository.from_url', mock_from_url)
         self.patch_symbol(mocker, 'git_machete.github.GitHubToken.for_domain', mock_github_token_for_domain_none)
-        self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(MockGitHubAPIState([])))
+        self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(MockGitHubAPIState()))
 
         (
             self.repo_sandbox
@@ -639,7 +622,7 @@ class TestGitHubCreatePR(BaseTest):
     def test_github_create_pr_for_branch_behind_remote(self, mocker: MockerFixture) -> None:
         self.patch_symbol(mocker, 'git_machete.github.OrganizationAndRepository.from_url', mock_from_url)
         self.patch_symbol(mocker, 'git_machete.github.GitHubToken.for_domain', mock_github_token_for_domain_none)
-        self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(MockGitHubAPIState([])))
+        self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(MockGitHubAPIState()))
 
         (
             self.repo_sandbox
@@ -703,7 +686,7 @@ class TestGitHubCreatePR(BaseTest):
     def test_github_create_pr_for_branch_diverged_from_and_older_than_remote(self, mocker: MockerFixture) -> None:
         self.patch_symbol(mocker, 'git_machete.github.OrganizationAndRepository.from_url', mock_from_url)
         self.patch_symbol(mocker, 'git_machete.github.GitHubToken.for_domain', mock_github_token_for_domain_none)
-        self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(MockGitHubAPIState([])))
+        self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(MockGitHubAPIState()))
 
         (
             self.repo_sandbox
diff --git a/tests/test_github_restack_pr.py b/tests/test_github_restack_pr.py
new file mode 100644
index 0000000..5677a5a
--- /dev/null
+++ b/tests/test_github_restack_pr.py
@@ -0,0 +1,296 @@
+
+from pytest_mock import MockerFixture
+
+from tests.base_test import BaseTest
+from tests.mockers import (assert_failure, assert_success,
+                           fixed_author_and_committer_date_in_past,
+                           rewrite_branch_layout_file)
+from tests.mockers_github import (MockGitHubAPIState, mock_from_url,
+                                  mock_github_token_for_domain_fake,
+                                  mock_pr_json, mock_urlopen)
+
+
+class TestGitHubRestackPR(BaseTest):
+
+    def setup_method(self) -> None:
+        super().setup_method()
+        self.github_api_state_for_test_restack_pr = MockGitHubAPIState(
+            mock_pr_json(head='feature_1', base='develop', number=14, draft=True),
+            mock_pr_json(head='feature', base='develop', number=15, body='# Based on PR #14\n# Summary'),
+            mock_pr_json(head='multiple-pr-branch', base='develop', number=16),
+            mock_pr_json(head='multiple-pr-branch', base='feature', number=17),
+        )
+
+    def test_github_restack_pr_no_prs_or_multiple_prs(self, mocker: MockerFixture) -> None:
+        self.patch_symbol(mocker, 'git_machete.github.GitHubToken.for_domain', mock_github_token_for_domain_fake)
+        self.patch_symbol(mocker, 'git_machete.github.OrganizationAndRepository.from_url', mock_from_url)
+        self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(self.github_api_state_for_test_restack_pr))
+
+        self.repo_sandbox.new_branch("develop").commit()
+
+        assert_failure(
+            ['github', 'restack-pr'],
+            "No PRs have develop as its head"
+        )
+
+        self.repo_sandbox.new_branch("multiple-pr-branch").commit()
+        assert_failure(
+            ['github', 'restack-pr'],
+            "Multiple PRs have multiple-pr-branch as its head: #16, #17"
+        )
+
+    def test_github_restack_pr_branch_in_sync(self, mocker: MockerFixture) -> None:
+        self.patch_symbol(mocker, 'git_machete.github.GitHubToken.for_domain', mock_github_token_for_domain_fake)
+        self.patch_symbol(mocker, 'git_machete.github.OrganizationAndRepository.from_url', mock_from_url)
+        self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(self.github_api_state_for_test_restack_pr))
+
+        (
+            self.repo_sandbox.new_branch("master")
+            .commit()
+            .new_branch("develop")
+            .commit()
+            .commit()
+            .push()
+            .new_branch('feature')
+            .commit()
+            .push()
+        )
+        body: str = \
+            """
+            master
+                develop
+                feature
+            """
+        rewrite_branch_layout_file(body)
+
+        assert_success(
+            ['github', 'restack-pr'],
+            """
+            Base branch of PR #15 has been switched to master
+            Base PR header has been removed from the description of PR #15
+            """
+        )
+        pr = self.github_api_state_for_test_restack_pr.get_pull_by_number(15)
+        assert pr is not None
+        assert pr['draft'] is False
+        assert pr['base']['ref'] == 'master'
+        assert pr['body'] == '# Summary'
+
+    def test_github_restack_pr_branch_untracked(self, mocker: MockerFixture) -> None:
+        self.patch_symbol(mocker, 'git_machete.github.GitHubToken.for_domain', mock_github_token_for_domain_fake)
+        self.patch_symbol(mocker, 'git_machete.github.OrganizationAndRepository.from_url', mock_from_url)
+        self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(self.github_api_state_for_test_restack_pr))
+
+        (
+            self.repo_sandbox.new_branch("master")
+            .commit()
+            .new_branch('feature_1')
+            .commit()
+        )
+        body: str = \
+            """
+            master
+                feature_1
+            """
+        rewrite_branch_layout_file(body)
+
+        assert_success(
+            ['github', 'restack-pr'],
+            """
+            Pushing untracked branch feature_1 to origin...
+
+              master (untracked)
+              |
+              o-feature_1 *
+
+            Base branch of PR #14 has been switched to master
+            """
+        )
+        pr = self.github_api_state_for_test_restack_pr.get_pull_by_number(14)
+        assert pr is not None
+        assert pr['draft'] is True
+        assert pr['base']['ref'] == 'master'
+
+    def test_github_restack_pr_branch_diverged_and_newer(self, mocker: MockerFixture) -> None:
+        self.patch_symbol(mocker, 'git_machete.github.GitHubToken.for_domain', mock_github_token_for_domain_fake)
+        self.patch_symbol(mocker, 'git_machete.github.OrganizationAndRepository.from_url', mock_from_url)
+        self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(self.github_api_state_for_test_restack_pr))
+
+        with fixed_author_and_committer_date_in_past():
+            (
+                self.repo_sandbox.new_branch("master")
+                .commit()
+                .new_branch('feature')
+                .commit()
+                .push()
+            )
+        self.repo_sandbox.amend_commit()
+        body: str = \
+            """
+            master
+                feature
+            """
+        rewrite_branch_layout_file(body)
+
+        assert_success(
+            ['github', 'restack-pr'],
+            """
+            PR #15 has been temporarily marked as draft
+            Branch feature diverged from (and has newer commits than) its remote counterpart origin/feature.
+            Pushing feature with force-with-lease to origin...
+
+              master (untracked)
+              |
+              o-feature *
+
+            Base branch of PR #15 has been switched to master
+            Base PR header has been removed from the description of PR #15
+            PR #15 has been marked as ready for review again
+            """
+        )
+        pr = self.github_api_state_for_test_restack_pr.get_pull_by_number(15)
+        assert pr is not None
+        assert pr['draft'] is False
+        assert pr['base']['ref'] == 'master'
+
+    def test_github_restack_pr_branch_ahead(self, mocker: MockerFixture) -> None:
+        self.patch_symbol(mocker, 'git_machete.github.GitHubToken.for_domain', mock_github_token_for_domain_fake)
+        self.patch_symbol(mocker, 'git_machete.github.OrganizationAndRepository.from_url', mock_from_url)
+        self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(self.github_api_state_for_test_restack_pr))
+
+        (
+            self.repo_sandbox.new_branch("master")
+            .commit()
+            .new_branch('feature')
+            .commit()
+            .push()
+            .commit()
+            .set_git_config_key('machete.github.domain', 'git.example.org')
+        )
+        body: str = \
+            """
+            master
+                feature
+            """
+        rewrite_branch_layout_file(body)
+
+        assert_success(
+            ['github', 'restack-pr'],
+            """
+            PR #15 has been temporarily marked as draft
+            Pushing feature to origin...
+
+              master (untracked)
+              |
+              o-feature *
+
+            Base branch of PR #15 has been switched to master
+            Base PR header has been removed from the description of PR #15
+            PR #15 has been marked as ready for review again
+            """
+        )
+        pr = self.github_api_state_for_test_restack_pr.get_pull_by_number(15)
+        assert pr is not None
+        assert pr['draft'] is False
+        assert pr['base']['ref'] == 'master'
+
+    def test_github_restack_pr_branch_ahead_push_no(self, mocker: MockerFixture) -> None:
+        self.patch_symbol(mocker, 'git_machete.github.GitHubToken.for_domain', mock_github_token_for_domain_fake)
+        self.patch_symbol(mocker, 'git_machete.github.OrganizationAndRepository.from_url', mock_from_url)
+        self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(self.github_api_state_for_test_restack_pr))
+
+        (
+            self.repo_sandbox.new_branch("master")
+            .commit()
+            .new_branch('feature')
+            .commit()
+            .push()
+            .commit()
+        )
+        body: str = \
+            """
+            master
+                feature push=no
+            """
+        rewrite_branch_layout_file(body)
+
+        assert_success(
+            ['github', 'restack-pr'],
+            """
+            Warn: Branch feature is marked as push=no; skipping the push.
+            Did you want to just use git machete github retarget-pr?
+
+            Base branch of PR #15 has been switched to master
+            Base PR header has been removed from the description of PR #15
+            """
+        )
+        pr = self.github_api_state_for_test_restack_pr.get_pull_by_number(15)
+        assert pr is not None
+        assert pr['draft'] is False
+        assert pr['base']['ref'] == 'master'
+
+    def test_github_restack_pr_branch_no_behind(self, mocker: MockerFixture) -> None:
+        self.patch_symbol(mocker, 'git_machete.github.GitHubToken.for_domain', mock_github_token_for_domain_fake)
+        self.patch_symbol(mocker, 'git_machete.github.OrganizationAndRepository.from_url', mock_from_url)
+        self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(self.github_api_state_for_test_restack_pr))
+
+        (
+            self.repo_sandbox.new_branch("master")
+            .commit()
+            .new_branch('feature')
+            .commit()
+            .push()
+            .reset_to("HEAD~")
+        )
+        body: str = \
+            """
+            master
+                feature
+            """
+        rewrite_branch_layout_file(body)
+
+        assert_success(
+            ['github', 'restack-pr'],
+            """
+            Warn: Branch feature is behind its remote counterpart. Consider using git pull.
+
+            Base branch of PR #15 has been switched to master
+            Base PR header has been removed from the description of PR #15
+            """
+        )
+
+    def test_github_restack_pr_branch_diverged_and_older(self, mocker: MockerFixture) -> None:
+        self.patch_symbol(mocker, 'git_machete.github.GitHubToken.for_domain', mock_github_token_for_domain_fake)
+        self.patch_symbol(mocker, 'git_machete.github.OrganizationAndRepository.from_url', mock_from_url)
+        self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(self.github_api_state_for_test_restack_pr))
+
+        (
+            self.repo_sandbox.new_branch("master")
+            .commit()
+            .new_branch('feature')
+            .commit()
+            .push()
+        )
+        with fixed_author_and_committer_date_in_past():
+            self.repo_sandbox.amend_commit()
+
+        body: str = \
+            """
+            master
+                feature
+            """
+        rewrite_branch_layout_file(body)
+
+        assert_success(
+            ['github', 'restack-pr'],
+            """
+            Warn: Branch feature is diverged from and older than its remote counterpart. Consider using git reset --keep.
+
+            Base branch of PR #15 has been switched to master
+            Base PR header has been removed from the description of PR #15
+            """
+        )
+        pr = self.github_api_state_for_test_restack_pr.get_pull_by_number(15)
+        assert pr is not None
+        assert pr['draft'] is False
+        assert pr['base']['ref'] == 'master'
diff --git a/tests/test_github_retarget_pr.py b/tests/test_github_retarget_pr.py
index e5d89dd..00c5bea 100644
--- a/tests/test_github_retarget_pr.py
+++ b/tests/test_github_retarget_pr.py
@@ -6,51 +6,19 @@ from tests.base_test import BaseTest
 from tests.mockers import (assert_failure, assert_success, launch_command,
                            rewrite_branch_layout_file)
 from tests.mockers_github import (MockGitHubAPIState, mock_from_url,
-                                  mock_repository_info, mock_urlopen)
+                                  mock_pr_json, mock_urlopen)
 
 
 class TestGitHubRetargetPR(BaseTest):
 
     github_api_state_for_test_retarget_pr = MockGitHubAPIState(
-        [
-            {
-                'head': {'ref': 'feature', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'master'}, 'number': '15',
-                'html_url': 'www.github.com', 'state': 'open'
-            },
-            {
-                'head': {'ref': 'feature_1', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'master'}, 'number': '20',
-                'html_url': 'www.github.com', 'state': 'open'
-            },
-            {
-                'head': {'ref': 'feature_2', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'master'}, 'number': '25',
-                'html_url': 'www.github.com', 'state': 'open'
-            },
-            {
-                'head': {'ref': 'feature_3', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'master'}, 'number': '30',
-                'html_url': 'www.github.com', 'state': 'open'
-            },
-            {
-                'head': {'ref': 'feature_4', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'feature'}, 'number': '35',
-                'html_url': 'www.github.com', 'state': 'open'
-            },
-            # Let's include another PR for `feature_2`, but with a different base branch
-            {
-                'head': {'ref': 'feature_4', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'feature'}, 'number': '40',
-                'html_url': 'www.github.com', 'state': 'open'
-            },
-        ]
+        mock_pr_json(head='feature', base='master', number=15),
+        mock_pr_json(head='feature_1', base='master', number=20, body='# Summary\n\n'),
+        mock_pr_json(head='feature_2', base='master', number=25, body=None),
+        mock_pr_json(head='feature_3', base='master', number=30),
+        mock_pr_json(head='feature_4', base='feature', number=35),
+        # Let's include another PR for `feature_2`, but with a different base branch
+        mock_pr_json(head='feature_4', base='feature', number=40),
     )
 
     def test_github_retarget_pr(self, mocker: MockerFixture) -> None:
@@ -105,7 +73,7 @@ class TestGitHubRetargetPR(BaseTest):
             It looks like the organization or repository name got changed recently and is outdated.
             New organization is example-org and new repository is example-repo.
             You can update your remote repository via: git remote set-url <remote_name> <new_repository_url>.
-            The base branch of PR #15 has been switched to develop
+            Base branch of PR #15 has been switched to develop
             """
         )
 
@@ -125,7 +93,7 @@ class TestGitHubRetargetPR(BaseTest):
 
         assert_success(
             ['github', 'retarget-pr'],
-            'The base branch of PR #15 is already develop\n'
+            'Base branch of PR #15 is already develop\n'
         )
 
         self.repo_sandbox.check_out("feature_4")
@@ -136,14 +104,7 @@ class TestGitHubRetargetPR(BaseTest):
         )
 
     github_api_state_for_test_github_retarget_pr_explicit_branch = MockGitHubAPIState(
-        [
-            {
-                'head': {'ref': 'feature', 'repo': mock_repository_info},
-                'user': {'login': 'some_other_user'},
-                'base': {'ref': 'root'}, 'number': '15',
-                'html_url': 'www.github.com', 'state': 'open'
-            }
-        ]
+        mock_pr_json(head='feature', base='root', number=15)
     )
 
     def test_github_retarget_pr_explicit_branch(self, mocker: MockerFixture) -> None:
@@ -196,7 +157,7 @@ class TestGitHubRetargetPR(BaseTest):
 
         assert_success(
             ['github', 'retarget-pr', '--branch', 'feature'],
-            'The base branch of PR #15 has been switched to branch-1\n'
+            'Base branch of PR #15 has been switched to branch-1\n'
         )
 
         expected_status_output = """
@@ -288,8 +249,13 @@ class TestGitHubRetargetPR(BaseTest):
 
         assert_success(
             ['github', 'retarget-pr'],
-            'The base branch of PR #20 has been switched to feature\n'
+            'Base branch of PR #20 has been switched to feature\n'
+            'Base PR header in the description of PR #20 now points to PR #15\n'
         )
+        pr20 = self.github_api_state_for_test_retarget_pr.get_pull_by_number(20)
+        assert pr20 is not None
+        assert pr20['base']['ref'] == 'feature'
+        assert pr20['body'] == '# Based on PR #15\n\n# Summary\n\n'
 
         # branch feature_2 is not present in any of the remotes
         (
@@ -318,8 +284,13 @@ class TestGitHubRetargetPR(BaseTest):
 
         assert_success(
             ['github', 'retarget-pr'],
-            'The base branch of PR #25 has been switched to feature\n'
+            'Base branch of PR #25 has been switched to feature\n'
+            'Base PR header in the description of PR #25 now points to PR #15\n'
         )
+        pr25 = self.github_api_state_for_test_retarget_pr.get_pull_by_number(25)
+        assert pr25 is not None
+        assert pr25['base']['ref'] == 'feature'
+        assert pr25['body'] == '# Based on PR #15\n'
 
         # branch feature_3 present in only one remote: origin_1 and has tracking data
         (
@@ -342,15 +313,59 @@ class TestGitHubRetargetPR(BaseTest):
 
         assert_success(
             ['github', 'retarget-pr'],
-            'The base branch of PR #30 has been switched to feature_2\n'
+            'Base branch of PR #30 has been switched to feature_2\n'
+            'Base PR header in the description of PR #30 now points to PR #25\n'
         )
+        pr30 = self.github_api_state_for_test_retarget_pr.get_pull_by_number(30)
+        assert pr30 is not None
+        assert pr30['base']['ref'] == 'feature_2'
+        assert pr30['body'] == '# Based on PR #25\n\n# Summary'
 
-    github_api_state_for_test_retarget_pr_root_branch = MockGitHubAPIState([{
-        'head': {'ref': 'master', 'repo': mock_repository_info},
-        'user': {'login': 'some_other_user'},
-        'base': {'ref': 'root'}, 'number': '15',
-        'html_url': 'www.github.com', 'state': 'open'
-    }])
+        body = \
+            """
+            root
+                branch-1
+                    feature
+                        feature_1
+                        feature_2
+                        feature_3
+            """
+        rewrite_branch_layout_file(body)
+
+        assert_success(
+            ['github', 'retarget-pr'],
+            'Base branch of PR #30 has been switched to feature\n'
+            'Base PR header in the description of PR #30 now points to PR #15\n'
+        )
+        pr30 = self.github_api_state_for_test_retarget_pr.get_pull_by_number(30)
+        assert pr30 is not None
+        assert pr30['base']['ref'] == 'feature'
+        assert pr30['body'] == '# Based on PR #15\n\n# Summary'
+
+        body = \
+            """
+            root
+                branch-1
+                    feature
+                        feature_1
+                        feature_2
+                feature_3
+            """
+        rewrite_branch_layout_file(body)
+
+        assert_success(
+            ['github', 'retarget-pr'],
+            'Base branch of PR #30 has been switched to root\n'
+            'Base PR header has been removed from the description of PR #30\n'
+        )
+        pr30 = self.github_api_state_for_test_retarget_pr.get_pull_by_number(30)
+        assert pr30 is not None
+        assert pr30['base']['ref'] == 'root'
+        assert pr30['body'] == '# Summary'
+
+    github_api_state_for_test_retarget_pr_root_branch = MockGitHubAPIState(
+        mock_pr_json(head='master', base='root', number=15)
+    )
 
     def test_github_retarget_pr_root_branch(self, mocker: MockerFixture) -> None:
         self.patch_symbol(mocker, 'git_machete.github.OrganizationAndRepository.from_url', mock_from_url)
diff --git a/tests/test_github_sync.py b/tests/test_github_sync.py
index d86dac5..e4f05ef 100644
--- a/tests/test_github_sync.py
+++ b/tests/test_github_sync.py
@@ -5,22 +5,13 @@ from tests.mockers import (assert_success, launch_command,
                            mock_input_returning_y, rewrite_branch_layout_file)
 from tests.mockers_github import (MockGitHubAPIState, mock_from_url,
                                   mock_github_token_for_domain_fake,
-                                  mock_repository_info, mock_urlopen)
+                                  mock_pr_json, mock_urlopen)
 
 
 class TestGitHubSync(BaseTest):
 
     github_api_state_for_test_github_sync = MockGitHubAPIState(
-        [
-            {
-                'head': {'ref': 'snickers', 'repo': mock_repository_info},
-                'user': {'login': 'github_user'},
-                'base': {'ref': 'master'},
-                'number': '7',
-                'html_url': 'www.github.com',
-                'state': 'open'
-            }
-        ]
+        mock_pr_json(head='snickers', base='master', number=7, user='github_user')
     )
 
     def test_github_sync(self, mocker: MockerFixture) -> None:
diff --git a/tests/test_squash.py b/tests/test_squash.py
index 21066d7..b9649f7 100644
--- a/tests/test_squash.py
+++ b/tests/test_squash.py
@@ -7,14 +7,18 @@ from .mockers import (assert_failure, assert_success,
 
 class TestSquash(BaseTest):
 
+    def test_squash_root_branch(self) -> None:
+        self.repo_sandbox.new_branch("master").commit().commit()
+
+        assert_failure(
+            ["squash"],
+            "git-machete cannot determine the range of commits unique to branch master.\n"
+            "Use git machete squash --fork-point=... to select the commit after which the commits of master start.\n"
+            "For example, if you want to squash 3 latest commits, use git machete squash --fork-point=HEAD~3."
+        )
+
     def test_squash_no_commits(self) -> None:
-        with fixed_author_and_committer_date_in_past():
-            (
-                self.repo_sandbox
-                .new_branch("master")
-                .commit()
-                .new_branch("develop")
-            )
+        self.repo_sandbox.new_branch("master").commit().new_branch("develop")
 
         assert_failure(
             ["squash"],