Skip to content

doc: git-rebase: clarify DESCRIPTION section #1949

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
303 changes: 137 additions & 166 deletions Documentation/git-rebase.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -16,197 +16,81 @@ SYNOPSIS

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Junio C Hamano wrote (reply to this):

"Julia Evans via GitGitGadget" <[email protected]> writes:

> From: Julia Evans <[email protected]>
>
> Start with an example that mirrors the example in the `git-merge` man
> page, to make it easier for folks to understand the difference between a
> rebase and a merge.
>
> Signed-off-by: Julia Evans <[email protected]>
> ---
>  Documentation/git-rebase.adoc | 46 +++++++++++++++--------------------
>  1 file changed, 20 insertions(+), 26 deletions(-)
>
> diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc
> index 956d3048f5a6..fee73623990d 100644
> --- a/Documentation/git-rebase.adoc
> +++ b/Documentation/git-rebase.adoc
> @@ -16,6 +16,26 @@ SYNOPSIS
>  
>  DESCRIPTION
>  -----------
> +Transplant a series of commits onto a different starting point.

It is an excellent idea to start with what it does, before
explaining how you would drive it.

> +For example, assume the following history exists and the current branch is `topic`:

The line is a bit overly long, and the style a bit on the
"description of dry facts" side, which might want to give
a bit more "motivation" to help readers.

Here is my attempt.

    Imagine that you have been working on the `topic` branch in this
    history, and you'd want to "catch up" to the work done on the
    `master` branch.

      *** Illustration of "before" state ***

    You want to transplant the commits you made on `topic` since it
    diverged from `master` (i.e. A, B, and C), on top of the current
    `master`.  You can do so by running `git rebase master` while the
    `topic` branch is checked out.  `git rebase master topic` works
    as a short-cut to `git checkout topic && git rebase master`.

      *** Illustration of "after" state ***

perhaps?  

Note that you lost the mention of `git rebase master topic` syntax
and the explanation that the form being a shorthand for checkout
followed by rebase, which I do not think appear anywhere else in the
document, so my suggested rewrite above resurrects it.

> +------------
> +          A---B---C topic
> +         /
> +    D---E---F---G master
> +------------
> +
> +Then `git rebase master` will find all of the commits since `topic` diverged
> +from `master` and copy the changes in each of those commits on top of the
> +`master` branch.
> +
> +------------
> +                  A'--B'--C' topic
> +                 /
> +    D---E---F---G master
> +------------
> +
>  If `<branch>` is specified, `git rebase` will perform an automatic
>  `git switch <branch>` before doing anything else.  Otherwise
>  it remains on the current branch.
> @@ -58,32 +78,6 @@ that caused the merge failure with `git rebase --skip`.  To check out the
>  original `<branch>` and remove the `.git/rebase-apply` working files, use
>  the command `git rebase --abort` instead.
>  
> -Assume the following history exists and the current branch is "topic":
> -
> -------------
> -          A---B---C topic
> -         /
> -    D---E---F---G master
> -------------
> -
> -From this point, the result of either of the following commands:
> -
> -
> -    git rebase master
> -    git rebase master topic
> -
> -would be:
> -
> -------------
> -                  A'--B'--C' topic
> -                 /
> -    D---E---F---G master
> -------------
> -
> -*NOTE:* The latter form is just a short-hand of `git checkout topic`
> -followed by `git rebase master`. When rebase exits `topic` will
> -remain the checked-out branch.
> -
>  If the upstream branch already contains a change you have made (e.g.,
>  because you mailed a patch which was applied upstream), then that commit
>  will be skipped and warnings will be issued (if the 'merge' backend is

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, "Julia Evans" wrote (reply to this):

On Fri, Aug 8, 2025, at 12:30 PM, Junio C Hamano wrote:
> "Julia Evans via GitGitGadget" <[email protected]> writes:
>
>> From: Julia Evans <[email protected]>
>>
>> Start with an example that mirrors the example in the `git-merge` man
>> page, to make it easier for folks to understand the difference between a
>> rebase and a merge.
>>
>> Signed-off-by: Julia Evans <[email protected]>
>> ---
>>  Documentation/git-rebase.adoc | 46 +++++++++++++++--------------------
>>  1 file changed, 20 insertions(+), 26 deletions(-)
>>
>> diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc
>> index 956d3048f5a6..fee73623990d 100644
>> --- a/Documentation/git-rebase.adoc
>> +++ b/Documentation/git-rebase.adoc
>> @@ -16,6 +16,26 @@ SYNOPSIS
>>  
>>  DESCRIPTION
>>  -----------
>> +Transplant a series of commits onto a different starting point.
>
> It is an excellent idea to start with what it does, before
> explaining how you would drive it.
>
>> +For example, assume the following history exists and the current branch is `topic`:
>
> The line is a bit overly long, and the style a bit on the
> "description of dry facts" side, which might want to give
> a bit more "motivation" to help readers.

I'm very happy to hear that feedback: I thought it was very dry as well, but I copied that part from elsewhere in the man page because I thought that was git's preferred documentation style. I'll work on making it less dry.

> Note that you lost the mention of `git rebase master topic` syntax
> and the explanation that the form being a shorthand for checkout
> followed by rebase, which I do not think appear anywhere else in the
> document, so my suggested rewrite above resurrects it.

That's actually in PATCH 3/5 of this series: I also thought the explanation that the form being a shorthand for checkout followed by rebase was extremely clear so I moved it to be the primary explanation of what `git rebase <upstream> <branch>` does.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Junio C Hamano wrote (reply to this):

"Julia Evans" <[email protected]> writes:

> I'm very happy to hear that feedback: I thought it was very dry as
> well, but I copied that part from elsewhere in the man page
> because I thought that was git's preferred documentation
> style. I'll work on making it less dry.

[administrivia: please wrap your overlong lines; we write for this
hypothetiical reader on an 80-column terminal].

>> Note that you lost the mention of `git rebase master topic` syntax
>> and the explanation that the form being a shorthand for checkout
>> followed by rebase, which I do not think appear anywhere else in the
>> document, so my suggested rewrite above resurrects it.
>
> That's actually in PATCH 3/5 of this series:...

Which I think is too late; both are quite commonly used and useful,
so showing them upfront when the command line examples are first
introduced would be more preferrable.

Thanks.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Junio C Hamano wrote (reply to this):

"Julia Evans via GitGitGadget" <[email protected]> writes:

> +Transplant a series of commits onto a different starting point.
> +
> +For example, imagine that you have been working on the `topic` branch in this
> +history, and you want to "catch up" to the work done on the `master` branch.
> +
> +------------
> +          A---B---C topic
> +         /
> +    D---E---F---G master
> +------------
> +
> +You want to transplant the commits you made on `topic` since it diverged from
> +`master` (i.e. A, B, and C), on top of the current `master`.  You can do this
> +by running `git rebase master` while the `topic` branch is checked out.  If you
> +want to rebase `topic` while on another branch, `git rebase master topic` is a
> +short-cut for `git switch topic && git rebase master`.

s/switch/checkout/, as that is how the original defines the short-cut.

> +------------
> +                  A'--B'--C' topic
> +                 /
> +    D---E---F---G master
> +------------
> +

Other than that, looks great.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Phillip Wood wrote (reply to this):

Hi Julia

On 09/08/2025 02:14, Julia Evans via GitGitGadget wrote:
> From: Julia Evans <[email protected]>
> > Start with an example that mirrors the example in the `git-merge` man
> page, to make it easier for folks to understand the difference between a
> rebase and a merge.

This is a very welcome improvement which makes it clear what rebasing a branch does. There are other important uses of rebase such as rearranging commits and squashing fixup commits that it would be nice to mention early on in the man page as well. Those do not necessarily change the base of the branch. I wonder if we could add a sentence about that which links to the INTERACTIVE MODE section.

Thanks

Phillip

> Signed-off-by: Julia Evans <[email protected]>
> ---
>   Documentation/git-rebase.adoc | 49 ++++++++++++++++-------------------
>   1 file changed, 23 insertions(+), 26 deletions(-)
> > diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc
> index 956d3048f5a6..449f01fba560 100644
> --- a/Documentation/git-rebase.adoc
> +++ b/Documentation/git-rebase.adoc
> @@ -16,6 +16,29 @@ SYNOPSIS
>   >   DESCRIPTION
>   -----------
> +Transplant a series of commits onto a different starting point.
> +
> +For example, imagine that you have been working on the `topic` branch in this
> +history, and you want to "catch up" to the work done on the `master` branch.
> +
> +------------
> +          A---B---C topic
> +         /
> +    D---E---F---G master
> +------------
> +
> +You want to transplant the commits you made on `topic` since it diverged from
> +`master` (i.e. A, B, and C), on top of the current `master`.  You can do this
> +by running `git rebase master` while the `topic` branch is checked out.  If you
> +want to rebase `topic` while on another branch, `git rebase master topic` is a
> +shortcut for `git checkout topic && git rebase master`.
> +
> +------------
> +                  A'--B'--C' topic
> +                 /
> +    D---E---F---G master
> +------------
> +
>   If `<branch>` is specified, `git rebase` will perform an automatic
>   `git switch <branch>` before doing anything else.  Otherwise
>   it remains on the current branch.
> @@ -58,32 +81,6 @@ that caused the merge failure with `git rebase --skip`.  To check out the
>   original `<branch>` and remove the `.git/rebase-apply` working files, use
>   the command `git rebase --abort` instead.
>   > -Assume the following history exists and the current branch is "topic":
> -
> -------------
> -          A---B---C topic
> -         /
> -    D---E---F---G master
> -------------
> -
> -From this point, the result of either of the following commands:
> -
> -
> -    git rebase master
> -    git rebase master topic
> -
> -would be:
> -
> -------------
> -                  A'--B'--C' topic
> -                 /
> -    D---E---F---G master
> -------------
> -
> -*NOTE:* The latter form is just a short-hand of `git checkout topic`
> -followed by `git rebase master`. When rebase exits `topic` will
> -remain the checked-out branch.
> -
>   If the upstream branch already contains a change you have made (e.g.,
>   because you mailed a patch which was applied upstream), then that commit
>   will be skipped and warnings will be issued (if the 'merge' backend is

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Patrick Steinhardt wrote (reply to this):

On Sat, Aug 09, 2025 at 01:14:13AM +0000, Julia Evans via GitGitGadget wrote:
> From: Julia Evans <[email protected]>
> 
> Start with an example that mirrors the example in the `git-merge` man
> page, to make it easier for folks to understand the difference between a
> rebase and a merge.

Makes sense.

> diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc
> index 956d3048f5a6..449f01fba560 100644
> --- a/Documentation/git-rebase.adoc
> +++ b/Documentation/git-rebase.adoc
> @@ -16,6 +16,29 @@ SYNOPSIS
>  
>  DESCRIPTION
>  -----------
> +Transplant a series of commits onto a different starting point.

Nit: while this is one use case for git-rebase(1), I'd claim that
nowadays with interactive rebases there's the other use case where we
don't change the starting point but only edit the range of commits
itself.

So how about this instead:

    Edit a series of commits and optionally transplant it onto a
    different starting point.

We could also make the two different modes a bit more explicit by
pointing out both different use cases directly.

If you agree then we should probably also modernize the description in
the NAME section accordingly.

Patrick

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Karthik Nayak wrote (reply to this):

"Julia Evans via GitGitGadget" <[email protected]> writes:

> From: Julia Evans <[email protected]>
>
> Start with an example that mirrors the example in the `git-merge` man
> page, to make it easier for folks to understand the difference between a
> rebase and a merge.
>

Happy to see these changes.

> Signed-off-by: Julia Evans <[email protected]>
> ---
>  Documentation/git-rebase.adoc | 49 ++++++++++++++++-------------------
>  1 file changed, 23 insertions(+), 26 deletions(-)
>
> diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc
> index 956d3048f5a6..449f01fba560 100644
> --- a/Documentation/git-rebase.adoc
> +++ b/Documentation/git-rebase.adoc
> @@ -16,6 +16,29 @@ SYNOPSIS
>
>  DESCRIPTION
>  -----------
> +Transplant a series of commits onto a different starting point.
> +
> +For example, imagine that you have been working on the `topic` branch in this
> +history, and you want to "catch up" to the work done on the `master` branch.
> +
> +------------
> +          A---B---C topic
> +         /
> +    D---E---F---G master
> +------------
> +
> +You want to transplant the commits you made on `topic` since it diverged from
> +`master` (i.e. A, B, and C), on top of the current `master`.  You can do this
> +by running `git rebase master` while the `topic` branch is checked out.  If you
> +want to rebase `topic` while on another branch, `git rebase master topic` is a
> +shortcut for `git checkout topic && git rebase master`.
> +

Nit: now that `git-switch(1)` is no longer experimental, we should start
recommending it over `git-checkout(1)` as necessary. So perhaps, we
could s/checkout/switch here?

[snip]

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Ben Knoble wrote (reply to this):

> Le 11 août 2025 à 05:29, Karthik Nayak <[email protected]> a écrit :
> 
> "Julia Evans via GitGitGadget" <[email protected]> writes:
>> 
>> Signed-off-by: Julia Evans <[email protected]>
>> ---
>> Documentation/git-rebase.adoc | 49 ++++++++++++++++-------------------
>> 1 file changed, 23 insertions(+), 26 deletions(-)
>> 
>> diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc
>> index 956d3048f5a6..449f01fba560 100644
>> --- a/Documentation/git-rebase.adoc
>> +++ b/Documentation/git-rebase.adoc
>> @@ -16,6 +16,29 @@ SYNOPSIS
>> 
>> DESCRIPTION
>> -----------
>> +Transplant a series of commits onto a different starting point.
>> +
>> +For example, imagine that you have been working on the `topic` branch in this
>> +history, and you want to "catch up" to the work done on the `master` branch.
>> +
>> +------------
>> +          A---B---C topic
>> +         /
>> +    D---E---F---G master
>> +------------
>> +
>> +You want to transplant the commits you made on `topic` since it diverged from
>> +`master` (i.e. A, B, and C), on top of the current `master`.  You can do this
>> +by running `git rebase master` while the `topic` branch is checked out.  If you
>> +want to rebase `topic` while on another branch, `git rebase master topic` is a
>> +shortcut for `git checkout topic && git rebase master`.
>> +
> 
> Nit: now that `git-switch(1)` is no longer experimental, we should start
> recommending it over `git-checkout(1)` as necessary. So perhaps, we
> could s/checkout/switch here?

Junio previously recommended checkout (a prior version used switch): I suspect because checkout will detach head without extra syntax (where switch will not)?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Phillip Wood wrote (reply to this):

On 11/08/2025 10:13, Karthik Nayak wrote:
> "Julia Evans via GitGitGadget" <[email protected]> writes:
>> +
>> +You want to transplant the commits you made on `topic` since it diverged from
>> +`master` (i.e. A, B, and C), on top of the current `master`.  You can do this
>> +by running `git rebase master` while the `topic` branch is checked out.  If you
>> +want to rebase `topic` while on another branch, `git rebase master topic` is a
>> +shortcut for `git checkout topic && git rebase master`.
>> +
> > Nit: now that `git-switch(1)` is no longer experimental, we should start
> recommending it over `git-checkout(1)` as necessary. So perhaps, we
> could s/checkout/switch here?

Junio has already expressed a preference for "checkout" here c.f. <[email protected]>. I think that is technically correct as "topic" can be a commitish and "git switch <object-id>" fails without "--detach". Also rebase does not do any of the extra checks that "git switch" does before switching branches (I'm not saying that is necessarily a good thing).

Thanks

Phillip

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Karthik Nayak wrote (reply to this):

Ben Knoble <[email protected]> writes:

>> Le 11 août 2025 à 05:29, Karthik Nayak <[email protected]> a écrit :
>>
>> "Julia Evans via GitGitGadget" <[email protected]> writes:
>>>
>>> Signed-off-by: Julia Evans <[email protected]>
>>> ---
>>> Documentation/git-rebase.adoc | 49 ++++++++++++++++-------------------
>>> 1 file changed, 23 insertions(+), 26 deletions(-)
>>>
>>> diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc
>>> index 956d3048f5a6..449f01fba560 100644
>>> --- a/Documentation/git-rebase.adoc
>>> +++ b/Documentation/git-rebase.adoc
>>> @@ -16,6 +16,29 @@ SYNOPSIS
>>>
>>> DESCRIPTION
>>> -----------
>>> +Transplant a series of commits onto a different starting point.
>>> +
>>> +For example, imagine that you have been working on the `topic` branch in this
>>> +history, and you want to "catch up" to the work done on the `master` branch.
>>> +
>>> +------------
>>> +          A---B---C topic
>>> +         /
>>> +    D---E---F---G master
>>> +------------
>>> +
>>> +You want to transplant the commits you made on `topic` since it diverged from
>>> +`master` (i.e. A, B, and C), on top of the current `master`.  You can do this
>>> +by running `git rebase master` while the `topic` branch is checked out.  If you
>>> +want to rebase `topic` while on another branch, `git rebase master topic` is a
>>> +shortcut for `git checkout topic && git rebase master`.
>>> +
>>
>> Nit: now that `git-switch(1)` is no longer experimental, we should start
>> recommending it over `git-checkout(1)` as necessary. So perhaps, we
>> could s/checkout/switch here?
>
> Junio previously recommended checkout (a prior version used switch): I suspect because checkout will detach head without extra syntax (where switch will not)?

I totally missed that, that makes sense!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Karthik Nayak wrote (reply to this):

Phillip Wood <[email protected]> writes:

> On 11/08/2025 10:13, Karthik Nayak wrote:
>> "Julia Evans via GitGitGadget" <[email protected]> writes:
>>> +
>>> +You want to transplant the commits you made on `topic` since it diverged from
>>> +`master` (i.e. A, B, and C), on top of the current `master`.  You can do this
>>> +by running `git rebase master` while the `topic` branch is checked out.  If you
>>> +want to rebase `topic` while on another branch, `git rebase master topic` is a
>>> +shortcut for `git checkout topic && git rebase master`.
>>> +
>>
>> Nit: now that `git-switch(1)` is no longer experimental, we should start
>> recommending it over `git-checkout(1)` as necessary. So perhaps, we
>> could s/checkout/switch here?
>
> Junio has already expressed a preference for "checkout" here c.f.
> <[email protected]>. I think that is technically correct as
> "topic" can be a commitish and "git switch <object-id>" fails without
> "--detach". Also rebase does not do any of the extra checks that "git
> switch" does before switching branches (I'm not saying that is
> necessarily a good thing).
>

I missed that, but since we do mention that `topic` is a branch, it
still makes sense to use 'git switch'. But either way this is okay.

> Thanks
>
> Phillip

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Junio C Hamano wrote (reply to this):

Karthik Nayak <[email protected]> writes:

>>> Nit: now that `git-switch(1)` is no longer experimental, we should start
>>> recommending it over `git-checkout(1)` as necessary. So perhaps, we
>>> could s/checkout/switch here?

Besides, the reason why it is no longer experimental is *not*
because it has proved to be so much better than "checkout" and the
users are encouraged to use it.

It is no longer experimental only because it and its set of options
are now engraved in users' mustle memory deep enough that we can no
longer change it without disrupting users.  We were hoping to
improve its end-user experience relative to "checkout", and allowed
it to deviate from the precendent that was set by "git checkout",
and that was the reason why "this is experimental. UI and behaviour
is subject to change" label was on it.

The experiment unfortunately was not a great success.  There were
only a few meaningful differences invented during the experiment.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, "D. Ben Knoble" wrote (reply to this):

On Tue, Aug 12, 2025 at 5:11 AM Karthik Nayak <[email protected]> wrote:
>
> Phillip Wood <[email protected]> writes:
>
> > On 11/08/2025 10:13, Karthik Nayak wrote:
> >> "Julia Evans via GitGitGadget" <[email protected]> writes:
> >>> +
> >>> +You want to transplant the commits you made on `topic` since it diverged from
> >>> +`master` (i.e. A, B, and C), on top of the current `master`.  You can do this
> >>> +by running `git rebase master` while the `topic` branch is checked out.  If you
> >>> +want to rebase `topic` while on another branch, `git rebase master topic` is a
> >>> +shortcut for `git checkout topic && git rebase master`.
> >>> +
> >>
> >> Nit: now that `git-switch(1)` is no longer experimental, we should start
> >> recommending it over `git-checkout(1)` as necessary. So perhaps, we
> >> could s/checkout/switch here?
> >
> > Junio has already expressed a preference for "checkout" here c.f.
> > <[email protected]>. I think that is technically correct as
> > "topic" can be a commitish and "git switch <object-id>" fails without
> > "--detach". Also rebase does not do any of the extra checks that "git
> > switch" does before switching branches (I'm not saying that is
> > necessarily a good thing).
> >
>
> I missed that, but since we do mention that `topic` is a branch, it
> still makes sense to use 'git switch'. But either way this is okay.

In the general case, it may not be a branch though. (Of course, that's
too confusing a detail for this section anyway.)

-- 
D. Ben Knoble

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Junio C Hamano wrote (reply to this):

"D. Ben Knoble" <[email protected]> writes:

>> > Junio has already expressed a preference for "checkout" here c.f.
>> > <[email protected]>. I think that is technically correct as
>> > "topic" can be a commitish and "git switch <object-id>" fails without
>> > "--detach".

I wanted the new documentation text to use what was already used in
the parts of the document that are not touched; otherwise we end up
with text that uses checkout in some places and switch in others,
which would invite confusions among uninitiated readers, who
rightfully would wonder what criteria were used to decide which one
among these two different commands to use.  And teaching that these
two commands are quasi-equivalent with possibly subtle differences
is not what the documentation of "git rebase" needs to do---it is a
distraction.

I do not mind a new proposal to do a documentation sweep, aiming for
Git 3.0 timeframe, to examine all mentions of "git checkout" in the
documentation and replace them with "git switch" or "git restore"
when appropriate (there are of course ones that are not for obvious
reasons, like the ones in "git checkout" documentation itself, and
possibly in "git switch" and "git restore" documentation pages that
may say things like "'git switch X' is similar to 'git checkout Y'").

But let's leave that outside this topic.

Thanks.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Junio C Hamano wrote (reply to this):

"Julia Evans via GitGitGadget" <[email protected]> writes:

> From: Julia Evans <[email protected]>
>
> - Start with an example that mirrors the example in the `git-merge` man
>   page, to make it easier for folks to understand the difference between
>   a rebase and a merge.
> - Mention that rebase can combine or reorder commits

The new comment added by this second point looks somewhat out of
place.  With this patch as-is, the text talks only about the best
case of the basic usage, tell the reader to go elsewhere if they are
not interested in learning the basic usage, and then switches back
to the basic usage topic and talks about conflict resolution.

It may be easier to read if you covered the fact that there are two
major use cases upfront, perhaps like:

    DESCRIPTION
    -----------
    Transplant a series of commits onto a different starting point.
    You can also use `git rebase` to reorder or combine commits: see
    INTERACTIVE MODE below for how to do that.

    For example, imagine ...

DESCRIPTION
-----------
If `<branch>` is specified, `git rebase` will perform an automatic
`git switch <branch>` before doing anything else. Otherwise
it remains on the current branch.
Transplant a series of commits onto a different starting point.
You can also use `git rebase` to reorder or combine commits: see INTERACTIVE
MODE below for how to do that.

If `<upstream>` is not specified, the upstream configured in
`branch.<name>.remote` and `branch.<name>.merge` options will be used (see
linkgit:git-config[1] for details) and the `--fork-point` option is
assumed. If you are currently not on any branch or if the current
branch does not have a configured upstream, the rebase will abort.

All changes made by commits in the current branch but that are not
in `<upstream>` are saved to a temporary area. This is the same set
of commits that would be shown by `git log <upstream>..HEAD`; or by
`git log 'fork_point'..HEAD`, if `--fork-point` is active (see the
description on `--fork-point` below); or by `git log HEAD`, if the
`--root` option is specified.

The current branch is reset to `<upstream>` or `<newbase>` if the
`--onto` option was supplied. This has the exact same effect as
`git reset --hard <upstream>` (or `<newbase>`). `ORIG_HEAD` is set
to point at the tip of the branch before the reset.

[NOTE]
`ORIG_HEAD` is not guaranteed to still point to the previous branch tip
at the end of the rebase if other commands that write that pseudo-ref
(e.g. `git reset`) are used during the rebase. The previous branch tip,
however, is accessible using the reflog of the current branch
(i.e. `@{1}`, see linkgit:gitrevisions[7]).

The commits that were previously saved into the temporary area are
then reapplied to the current branch, one by one, in order. Note that
any commits in `HEAD` which introduce the same textual changes as a commit
in `HEAD..<upstream>` are omitted (i.e., a patch already accepted upstream
with a different commit message or timestamp will be skipped).

It is possible that a merge failure will prevent this process from being
completely automatic. You will have to resolve any such merge failure
and run `git rebase --continue`. Another option is to bypass the commit
that caused the merge failure with `git rebase --skip`. To check out the
original `<branch>` and remove the `.git/rebase-apply` working files, use
the command `git rebase --abort` instead.

Assume the following history exists and the current branch is "topic":
For example, imagine that you have been working on the `topic` branch in this
history, and you want to "catch up" to the work done on the `master` branch.

------------
A---B---C topic
/
D---E---F---G master
------------

From this point, the result of either of the following commands:


git rebase master
git rebase master topic

would be:
You want to transplant the commits you made on `topic` since it diverged from
`master` (i.e. A, B, and C), on top of the current `master`. You can do this
by running `git rebase master` while the `topic` branch is checked out. If you
want to rebase `topic` while on another branch, `git rebase master topic` is a
shortcut for `git checkout topic && git rebase master`.

------------
A'--B'--C' topic
/
D---E---F---G master
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Junio C Hamano wrote (reply to this):

"Julia Evans via GitGitGadget" <[email protected]> writes:

> +If there is a merge conflict during this process, `git rebase` will stop at the
> +first problematic commit and leave conflict markers. If this happens, you can:

By using the numbered enumeration below, we may mislead the readers
that these are the things they have to do in this order, when we
actually are giving them choices.

	If this happens, you can do one of these things:

would be an easy workaround.

> +1. Resolve the conflict. You can use `git diff` to find the markers (<<<<<<)
> +   and make edits to resolve the conflict. For each file you edit, you need to
> +   tell Git that the conflict has been resolved. Typically this would be
> +   done with
> +
> +   git add <filename>
> +
> +   You can then continue the rebasing process with
> +
> +   git rebase --continue

The original (below) does not do a perfect job, but I am afraid that
this increases the chance of misunderstanding by new readers that
they'd run "continue" after marking each confclited fines as "done"
with "git add", by reducing a sentence to a mere "then" in "You can
then continue".

	Typically after resolving all the conflicts in a single
	file, you would tell Git that you are done with this file:

	git add <filename>

	And after dealing with all the conflicted files and telling
	Git that you are done, you would continue the rebasing
	process with

	git rebase --continue

or something?

> +2. Stop the `git rebase` and return your branch to its original state with
> +
> +   git rebase --abort
> +
> +3. Skip the commit that caused the merge conflict with
> +
> +   git rebase --skip

The explanation in the above looks good, and the new organization is
much easier to follow and is definite improvement compared to the
original.

I am not sure how the above how the above formats, though,
especially on a medium that is not monospaced text (e.g., html
rendition, not "git help -m rebase" on terminals).  The prose should
typeset just like the normal text (i.e. your "Transplant a series of
commits" in the previous step that starts the description section),
but the command that the users would type should be typeset in
monospace typewriter.  Have you tried?

Thanks.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Patrick Steinhardt wrote (reply to this):

On Sat, Aug 09, 2025 at 01:14:14AM +0000, Julia Evans via GitGitGadget wrote:
> diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc
> index 449f01fba560..e30b9535fff1 100644
> --- a/Documentation/git-rebase.adoc
> +++ b/Documentation/git-rebase.adoc
> @@ -39,6 +39,27 @@ shortcut for `git checkout topic && git rebase master`.
>      D---E---F---G master
>  ------------
>  
> +If there is a merge conflict during this process, `git rebase` will stop at the
> +first problematic commit and leave conflict markers. If this happens, you can do
> +one of these things:
> +
> +1. Resolve the conflict. You can use `git diff` to find the markers (<<<<<<)
> +   and make edits to resolve the conflict. For each file you edit, you need to
> +   tell Git that the conflict has been resolved. You can mark the conflict as
> +   resolved with  `git add <filename>`. After resolving all of the conflicts,
> +   you can continue the rebasing process with
> +
> +   git rebase --continue
> +
> +2. Stop the `git rebase` and return your branch to its original state with
> +
> +   git rebase --abort
> +
> +3. Skip the commit that caused the merge conflict with
> +
> +   git rebase --skip
> +
> +
>  If `<branch>` is specified, `git rebase` will perform an automatic
>  `git switch <branch>` before doing anything else.  Otherwise
>  it remains on the current branch.

Yup, this reads a lot nicer.

> @@ -74,13 +95,6 @@ any commits in `HEAD` which introduce the same textual changes as a commit
>  in `HEAD..<upstream>` are omitted (i.e., a patch already accepted upstream
>  with a different commit message or timestamp will be skipped).
>  
> -It is possible that a merge failure will prevent this process from being
> -completely automatic.  You will have to resolve any such merge failure
> -and run `git rebase --continue`.  Another option is to bypass the commit
> -that caused the merge failure with `git rebase --skip`.  To check out the
> -original `<branch>` and remove the `.git/rebase-apply` working files, use
> -the command `git rebase --abort` instead.
> -
>  If the upstream branch already contains a change you have made (e.g.,
>  because you mailed a patch which was applied upstream), then that commit
>  will be skipped and warnings will be issued (if the 'merge' backend is

We lose the bit about `.git/rebase-apply`, but I don't think that's a
bad thing. The user shouldn't have to care how exactly a rebase looks on
disk. All they should need to know is that `git rebase --abort` gets
them out of the state.

Patrick

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Phillip Wood wrote (reply to this):

On 11/08/2025 09:46, Patrick Steinhardt wrote:
> On Sat, Aug 09, 2025 at 01:14:14AM +0000, Julia Evans via GitGitGadget wrote:
>>   >> -It is possible that a merge failure will prevent this process from being
>> -completely automatic.  You will have to resolve any such merge failure
>> -and run `git rebase --continue`.  Another option is to bypass the commit
>> -that caused the merge failure with `git rebase --skip`.  To check out the
>> -original `<branch>` and remove the `.git/rebase-apply` working files, use
>> -the command `git rebase --abort` instead.
>> -
>>   If the upstream branch already contains a change you have made (e.g.,
>>   because you mailed a patch which was applied upstream), then that commit
>>   will be skipped and warnings will be issued (if the 'merge' backend is
> > We lose the bit about `.git/rebase-apply`, but I don't think that's a
> bad thing. The user shouldn't have to care how exactly a rebase looks on
> disk.

Exactly, we don't want to encourage the user to poke about in the state directory. Also ".git/rebase-apply" wont exist unless the user requested the apply backend so mentioning it is misleading (the default is ".git/rebase-merge" these days).

Thanks

Phillip

------------

*NOTE:* The latter form is just a short-hand of `git checkout topic`
followed by `git rebase master`. When rebase exits `topic` will
remain the checked-out branch.

If the upstream branch already contains a change you have made (e.g.,
because you mailed a patch which was applied upstream), then that commit
will be skipped and warnings will be issued (if the 'merge' backend is
used). For example, running `git rebase master` on the following
history (in which `A'` and `A` introduce the same set of changes, but
have different committer information):

------------
A---B---C topic
/
D---E---A'---F master
------------

will result in:

------------
B'---C' topic
/
D---E---A'---F master
------------

Here is how you would transplant a topic branch based on one
branch to another, to pretend that you forked the topic branch
from the latter branch, using `rebase --onto`.

First let's assume your 'topic' is based on branch 'next'.
For example, a feature developed in 'topic' depends on some
functionality which is found in 'next'.

------------
o---o---o---o---o master
\
o---o---o---o---o next
\
o---o---o topic
------------

We want to make 'topic' forked from branch 'master'; for example,
because the functionality on which 'topic' depends was merged into the
more stable 'master' branch. We want our tree to look like this:

------------
o---o---o---o---o master
| \
| o'--o'--o' topic
\
o---o---o---o---o next
------------

We can get this using the following command:

git rebase --onto master next topic


Another example of --onto option is to rebase part of a
branch. If we have the following situation:

------------
H---I---J topicB
/
E---F---G topicA
/
A---B---C---D master
------------

then the command

git rebase --onto master topicA topicB

would result in:

------------
H'--I'--J' topicB
/
| E---F---G topicA
|/
A---B---C---D master
------------

This is useful when topicB does not depend on topicA.

A range of commits could also be removed with rebase. If we have
the following situation:

------------
E---F---G---H---I---J topicA
------------

then the command

git rebase --onto topicA~5 topicA~3 topicA

would result in the removal of commits F and G:

------------
E---H'---I'---J' topicA
------------

This is useful if F and G were flawed in some way, or should not be
part of topicA. Note that the argument to `--onto` and the `<upstream>`
parameter can be any valid commit-ish.

In case of conflict, `git rebase` will stop at the first problematic commit
and leave conflict markers in the tree. You can use `git diff` to locate
the markers (<<<<<<) and make edits to resolve the conflict. For each
file you edit, you need to tell Git that the conflict has been resolved,
typically this would be done with

If there is a merge conflict during this process, `git rebase` will stop at the
first problematic commit and leave conflict markers. If this happens, you can do
one of these things:

git add <filename>
1. Resolve the conflict. You can use `git diff` to find the markers (<<<<<<)
and make edits to resolve the conflict. For each file you edit, you need to
tell Git that the conflict has been resolved. You can mark the conflict as
resolved with `git add <filename>`. After resolving all of the conflicts,
you can continue the rebasing process with

git rebase --continue

After resolving the conflict manually and updating the index with the
desired resolution, you can continue the rebasing process with
2. Stop the `git rebase` and return your branch to its original state with

git rebase --abort

git rebase --continue
3. Skip the commit that caused the merge conflict with

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Junio C Hamano wrote (reply to this):

"Julia Evans via GitGitGadget" <[email protected]> writes:

> From: Julia Evans <[email protected]>
>
> This removes the explanation of `git rebase <upstream> <branch>`, since
> it was already explained above that it's shorthand for `git switch
> <branch> && git rebase <upstream>`

We usually do not say "This [commit] does X".  In this particular
case, since the objective is so obvious, we do not necessarily have
to follow the whole "summarize the current state, explain why it is
problematic, and then tell somebody sitting in front of a keyboard
to change the code in this and that way to make it improve" 9 yards;
just tweaking it into

	Remove the redundant explanation of `git ...`, since it was
	already explained earlier.

would be sufficient.

Although the proposed log message does not mention it, I actually
like the other change in this patch, that rephrases the "if you
don't specify...", even better.  The addtional "to rebase onto" does
make the description clearer.

Thanks.

> Signed-off-by: Julia Evans <[email protected]>
> ---
>  Documentation/git-rebase.adoc | 7 +------
>  1 file changed, 1 insertion(+), 6 deletions(-)
>
> diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc
> index a93c616f38b1..e700b92e35ac 100644
> --- a/Documentation/git-rebase.adoc
> +++ b/Documentation/git-rebase.adoc
> @@ -59,12 +59,7 @@ one of these things:
>  
>     git rebase --skip
>  
> -
> -If `<branch>` is specified, `git rebase` will perform an automatic
> -`git switch <branch>` before doing anything else.  Otherwise
> -it remains on the current branch.
> -
> -If `<upstream>` is not specified, the upstream configured in
> +If you don't specify an `<upstream>` to rebase onto, the upstream configured in
>  `branch.<name>.remote` and `branch.<name>.merge` options will be used (see
>  linkgit:git-config[1] for details) and the `--fork-point` option is
>  assumed.  If you are currently not on any branch or if the current

git rebase --skip

Alternatively, you can undo the 'git rebase' with
If you don't specify an `<upstream>` to rebase onto, the upstream configured in
`branch.<name>.remote` and `branch.<name>.merge` options will be used (see
linkgit:git-config[1] for details) and the `--fork-point` option is
assumed. If you are currently not on any branch or if the current
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Junio C Hamano wrote (reply to this):

"Julia Evans via GitGitGadget" <[email protected]> writes:

> From: Julia Evans <[email protected]>
>
> - make it clearer that we're talking about three steps of a process

A good call.  "First, then, then" does make it clear where each new
step begins.  "First, then, finally" would make it even more obvious
which step concludes the sequence, though.

> - delete a duplicate explanation of how git rebase skips commits with
>   the same textual changes (it's explained in more detail a few lines
>   further down)

OK.

> - move the `ORIG_HEAD` note down so that it doesn't interrupt the
>   discussion of the mechanics.

I thought you moved the "finally we reapply" up, instead of moving
the note down ;-) And when viewed that way, a more direct way to
justify this change is that you made sure these three steps are kept
together.

Again, good change.

> -All changes made by commits in the current branch but that are not
> +Here is a more detailed description of what `git rebase <upstream>` does:
> +
> +First, all changes made by commits in the current branch but that are not
>  in `<upstream>` are saved to a temporary area.  This is the same set
>  of commits that would be shown by `git log <upstream>..HEAD`; or by
>  `git log 'fork_point'..HEAD`, if `--fork-point` is active (see the
>  description on `--fork-point` below); or by `git log HEAD`, if the
>  `--root` option is specified.

If we are ambitious, we may want to rewrite this first step to put
almost no stress on "saving" and "temporary area".  Especially when
you rebase with merge backend, it would be morally a sequence of
cherry-pick, without us having to save anything---we only need to
figure out which commits to replay in what order.

    First, the command figures out changes from which commits to
    replay in what order.  The set of commits are those shown by
    `git log <base>..HEAD`, where <base> is the <upstream> if given,
    or computed fork-point if the `--fork-point` option is active,
    or all commits that lead to `HEAD` if `--root` is given.


> -The current branch is reset to `<upstream>` or `<newbase>` if the
> +Then the current branch is reset to `<upstream>` or `<newbase>` if the
>  `--onto` option was supplied.  This has the exact same effect as
>  `git reset --hard <upstream>` (or `<newbase>`). `ORIG_HEAD` is set
>  to point at the tip of the branch before the reset.

"This has the exact same effect as" made my reading hiccup.

    Then `git reset --hard` rewinds the current branch to the commit
    given by `--onto`, if specified, or to `<upstream>`.

I am not sure if it is worth talking about ORIG_HEAD at this point,
as that is part of what `git reset --hard` does with this step.
Given especially that we have NOTE that says ORIG_HEAD cannot be
relied upon once the replaying of commits begin, mentioning the
value of ORIG_HEAD before the replaying begins does not seem to add
much value to the explanation.

> +Then the commits that were previously saved into the temporary area are
> +reapplied to the current branch, one by one, in order.

Yup.  As I suggested, "Then" -> "Finally".

I do not know how much detail we want to give to readers, and I do
to prefer to tell some white lie in end-user facing documentation if
simplified description helps the initial understanding, so I am not
yet decided if I recommend rewriting these "three step" explanation,
but FYI, the modern rebase machinery works slightly differently:

    First the command figures out what to replay, on which commit,
    and in what order.

    Then the HEAD is detached ("git checkout --detach <onto>") to
    the commit the first change is replayed onto.

    Then the changes are replayed, one by one, in the order.
    Optionally a mergy history can be rebased while retaining
    the topology.

    Finally, the branch being rebased is made to point at the
    resulting HEAD (i.e. equivalent to "git checkout -B <branch>"
    to jump back from the detached HEAD state).

The difference between the simplified procedure and this "replaying
is done while on detached HEAD" procedure gives us a somewhat big
usability improvement, as a rebasing will be recorded in a single
reflog event for the branch getting rebased, no matter how many
commits are on the branch.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Phillip Wood wrote (reply to this):

Hi Julia

On 09/08/2025 02:14, Julia Evans via GitGitGadget wrote:
> From: Julia Evans <[email protected]>
> > +Here is a more detailed description of what `git rebase <upstream>` does:
> +
> +First, it makes a list of all commits in the current branch that are not in
> +`<upstream>`. This is the same set of commits that would be shown by `git log
> +<upstream>..HEAD`.

The existing text is not quite accurate here, it should really say `git log --cherry-pick --right-only <upstream>...HEAD` as we drop any commits from the branch that have already been cherry-picked to <upstream>.

> You can use `--fork-point` or `--root` to change how this
> +list of commits is constructed.

`--reapply-cherry-picks` also changes how the list is constructed so I think it would be worth adding that option here as well.

Thanks for working on this, it makes the description section much more readable.

Phillip
> +
> +Then it checks out `<upstream>` (or `<newbase>` if the `--onto` option was
> +supplied) with the equivalent of `git switch --detach <upstream>`.
> +
> +Then it replays the commits, one by one, in order. This is similar to running
> +`git cherry-pick <commit>` for each commit. See REBASING MERGES for how merges
> +are handled.
> +
> +Finally, it updates your branch to point to the final commit with the equivalent
> +of `git switch -C <branch>`.
>   >   [NOTE]
> +`ORIG_HEAD` is set to point at the tip of the branch before the rebase.
>   `ORIG_HEAD` is not guaranteed to still point to the previous branch tip
>   at the end of the rebase if other commands that write that pseudo-ref
>   (e.g. `git reset`) are used during the rebase. The previous branch tip,
>   however, is accessible using the reflog of the current branch
>   (i.e. `@{1}`, see linkgit:gitrevisions[7]).
>   > -The commits that were previously saved into the temporary area are
> -then reapplied to the current branch, one by one, in order. Note that
> -any commits in `HEAD` which introduce the same textual changes as a commit
> -in `HEAD..<upstream>` are omitted (i.e., a patch already accepted upstream
> -with a different commit message or timestamp will be skipped).
> -
>   If the upstream branch already contains a change you have made (e.g.,
>   because you mailed a patch which was applied upstream), then that commit
>   will be skipped and warnings will be issued (if the 'merge' backend is

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, "D. Ben Knoble" wrote (reply to this):

On Sun, Aug 10, 2025 at 11:42 AM Phillip Wood <[email protected]> wrote:
>
> Hi Julia
>
> On 09/08/2025 02:14, Julia Evans via GitGitGadget wrote:
> > From: Julia Evans <[email protected]>
> >
> > +Here is a more detailed description of what `git rebase <upstream>` does:
> > +
> > +First, it makes a list of all commits in the current branch that are not in
> > +`<upstream>`. This is the same set of commits that would be shown by `git log
> > +<upstream>..HEAD`.
>
> The existing text is not quite accurate here, it should really say `git
> log --cherry-pick --right-only <upstream>...HEAD` as we drop any commits
> from the branch that have already been cherry-picked to <upstream>.

This reminds me, I've been meaning to ask: is there a meaningful
difference between the following 2 commands?

    git rev-list --count --left-only @{u}...
    git rev-list --count ..@{u}

In my estimation, since we're not using --cherry-pick here, the first
says "list (count) the commits on upstream but not in HEAD, since the
merge-base (which is reachable from both)" while the second says "list
(count) the commits on upstream but not in HEAD." Is there ever a
situation where those sets aren't the same?

Of course the symmetric difference is not always the same as the range
notation, but when we add --left-only [or --right-only, by symmetry
;)], does this collapse to the 2-dot notation?

I've been assuming the latter is more performant, too, but if that's
not the case, that's also good to know.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Junio C Hamano wrote (reply to this):

"D. Ben Knoble" <[email protected]> writes:

>     git rev-list --count --left-only @{u}...
>     git rev-list --count ..@{u}
>
> In my estimation, since we're not using --cherry-pick here, the first
> says "list (count) the commits on upstream but not in HEAD, since the
> merge-base (which is reachable from both)" while the second says "list
> (count) the commits on upstream but not in HEAD." Is there ever a
> situation where those sets aren't the same?

I don't think of any; let us know when you find a case that the
above does not hold true ;-).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Patrick Steinhardt wrote (reply to this):

On Sat, Aug 09, 2025 at 01:14:17AM +0000, Julia Evans via GitGitGadget wrote:
> diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc
> index 50c84f138212..c16ee37b46a7 100644
> --- a/Documentation/git-rebase.adoc
> +++ b/Documentation/git-rebase.adoc
> @@ -65,31 +65,31 @@ linkgit:git-config[1] for details) and the `--fork-point` option is
>  assumed.  If you are currently not on any branch or if the current
>  branch does not have a configured upstream, the rebase will abort.
>  
> -All changes made by commits in the current branch but that are not
> -in `<upstream>` are saved to a temporary area.  This is the same set
> -of commits that would be shown by `git log <upstream>..HEAD`; or by
> -`git log 'fork_point'..HEAD`, if `--fork-point` is active (see the
> -description on `--fork-point` below); or by `git log HEAD`, if the
> -`--root` option is specified.
> -
> -The current branch is reset to `<upstream>` or `<newbase>` if the
> -`--onto` option was supplied.  This has the exact same effect as
> -`git reset --hard <upstream>` (or `<newbase>`). `ORIG_HEAD` is set
> -to point at the tip of the branch before the reset.
> +Here is a more detailed description of what `git rebase <upstream>` does:
> +
> +First, it makes a list of all commits in the current branch that are not in
> +`<upstream>`. This is the same set of commits that would be shown by `git log
> +<upstream>..HEAD`. You can use `--fork-point` or `--root` to change how this
> +list of commits is constructed.
> +
> +Then it checks out `<upstream>` (or `<newbase>` if the `--onto` option was
> +supplied) with the equivalent of `git switch --detach <upstream>`.
> +
> +Then it replays the commits, one by one, in order. This is similar to running
> +`git cherry-pick <commit>` for each commit. See REBASING MERGES for how merges
> +are handled.
> +
> +Finally, it updates your branch to point to the final commit with the equivalent
> +of `git switch -C <branch>`.

Would it make sense to convert this into a bulleted list to further
highlight this multi-step process?

>  [NOTE]
> +`ORIG_HEAD` is set to point at the tip of the branch before the rebase.
>  `ORIG_HEAD` is not guaranteed to still point to the previous branch tip
>  at the end of the rebase if other commands that write that pseudo-ref
>  (e.g. `git reset`) are used during the rebase. The previous branch tip,
>  however, is accessible using the reflog of the current branch
>  (i.e. `@{1}`, see linkgit:gitrevisions[7]).

This information feels somewhat contradictory. Should we maybe say
something like this:

    When starting the rebase, `ORIG_HEAD` is set to point to at the tip
    of the to-be-rebased branch. As `ORIG_HEAD` may be modified by
    various operations during the rebase, it is not guaranteed to still
    point to this branch at the end of the rebase. The previous branch
    tip, however, is accessible using the reflog of the current branch
    (i.e. `@{1}`, see linkgit:gitrevisions[7]).

Note that I'm also dropping the reference to "pseudo-ref". ORIG_HEAD is
not a pseudo-ref, as we have clarified in 6fd8037564
(Documentation/glossary: redefine pseudorefs as special refs,
2024-05-15).

Patrick

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Ben Knoble wrote (reply to this):

> Le 11 août 2025 à 04:46, Patrick Steinhardt <[email protected]> a écrit :
> 
> On Sat, Aug 09, 2025 at 01:14:17AM +0000, Julia Evans via GitGitGadget wrote:
>> diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc
>> index 50c84f138212..c16ee37b46a7 100644
>> --- a/Documentation/git-rebase.adoc
>> +++ b/Documentation/git-rebase.adoc
>> @@ -65,31 +65,31 @@ linkgit:git-config[1] for details) and the `--fork-point` option is
>> assumed.  If you are currently not on any branch or if the current
>> branch does not have a configured upstream, the rebase will abort.
>> 
>> -All changes made by commits in the current branch but that are not
>> -in `<upstream>` are saved to a temporary area.  This is the same set
>> -of commits that would be shown by `git log <upstream>..HEAD`; or by
>> -`git log 'fork_point'..HEAD`, if `--fork-point` is active (see the
>> -description on `--fork-point` below); or by `git log HEAD`, if the
>> -`--root` option is specified.
>> -
>> -The current branch is reset to `<upstream>` or `<newbase>` if the
>> -`--onto` option was supplied.  This has the exact same effect as
>> -`git reset --hard <upstream>` (or `<newbase>`). `ORIG_HEAD` is set
>> -to point at the tip of the branch before the reset.
>> +Here is a more detailed description of what `git rebase <upstream>` does:
>> +
>> +First, it makes a list of all commits in the current branch that are not in
>> +`<upstream>`. This is the same set of commits that would be shown by `git log
>> +<upstream>..HEAD`. You can use `--fork-point` or `--root` to change how this
>> +list of commits is constructed.
>> +
>> +Then it checks out `<upstream>` (or `<newbase>` if the `--onto` option was
>> +supplied) with the equivalent of `git switch --detach <upstream>`.
>> +
>> +Then it replays the commits, one by one, in order. This is similar to running
>> +`git cherry-pick <commit>` for each commit. See REBASING MERGES for how merges
>> +are handled.
>> +
>> +Finally, it updates your branch to point to the final commit with the equivalent
>> +of `git switch -C <branch>`.
> 
> Would it make sense to convert this into a bulleted list to further
> highlight this multi-step process?

Nit: ordered list, perhaps? Unless we don’t use those in our manuals (away from documentation at the moment). 

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Patrick Steinhardt wrote (reply to this):

On Mon, Aug 11, 2025 at 08:29:42AM -0400, Ben Knoble wrote:
> 
> > Le 11 août 2025 à 04:46, Patrick Steinhardt <[email protected]> a écrit :
> > 
> > On Sat, Aug 09, 2025 at 01:14:17AM +0000, Julia Evans via GitGitGadget wrote:
> >> diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc
> >> index 50c84f138212..c16ee37b46a7 100644
> >> --- a/Documentation/git-rebase.adoc
> >> +++ b/Documentation/git-rebase.adoc
> >> @@ -65,31 +65,31 @@ linkgit:git-config[1] for details) and the `--fork-point` option is
> >> assumed.  If you are currently not on any branch or if the current
> >> branch does not have a configured upstream, the rebase will abort.
> >> 
> >> -All changes made by commits in the current branch but that are not
> >> -in `<upstream>` are saved to a temporary area.  This is the same set
> >> -of commits that would be shown by `git log <upstream>..HEAD`; or by
> >> -`git log 'fork_point'..HEAD`, if `--fork-point` is active (see the
> >> -description on `--fork-point` below); or by `git log HEAD`, if the
> >> -`--root` option is specified.
> >> -
> >> -The current branch is reset to `<upstream>` or `<newbase>` if the
> >> -`--onto` option was supplied.  This has the exact same effect as
> >> -`git reset --hard <upstream>` (or `<newbase>`). `ORIG_HEAD` is set
> >> -to point at the tip of the branch before the reset.
> >> +Here is a more detailed description of what `git rebase <upstream>` does:
> >> +
> >> +First, it makes a list of all commits in the current branch that are not in
> >> +`<upstream>`. This is the same set of commits that would be shown by `git log
> >> +<upstream>..HEAD`. You can use `--fork-point` or `--root` to change how this
> >> +list of commits is constructed.
> >> +
> >> +Then it checks out `<upstream>` (or `<newbase>` if the `--onto` option was
> >> +supplied) with the equivalent of `git switch --detach <upstream>`.
> >> +
> >> +Then it replays the commits, one by one, in order. This is similar to running
> >> +`git cherry-pick <commit>` for each commit. See REBASING MERGES for how merges
> >> +are handled.
> >> +
> >> +Finally, it updates your branch to point to the final commit with the equivalent
> >> +of `git switch -C <branch>`.
> > 
> > Would it make sense to convert this into a bulleted list to further
> > highlight this multi-step process?
> 
> Nit: ordered list, perhaps? Unless we don’t use those in our manuals
> (away from documentation at the moment). 

Ah, I actually wanted to propose using an ordered list, not bulleted list.
Thanks for correcting me.

Patrick

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, "Julia Evans" wrote (reply to this):

> Would it make sense to convert this into a bulleted list to further
> highlight this multi-step process?

I like this idea. I think we can combine this with Phillip's point about
"--reapply-cherry-picks" to expand the list a little more and remove the
paragraph at the end about how duplicate commits are skipped. ("If the upstream
branch already contains a change you have made...") I think probably the diagram
can be removed too, I'm not sure how much value it's adding.

1. Make a list of all commits in the current branch that are not in
   `<upstream>`. This is the same set of commits that would be shown by `git log
   <upstream>..HEAD`. You can use `--fork-point` or `--root` to change how this
   list of commits is constructed.
2. Check whether any of those commits are duplicates of commits already
   in `<upstream>`, remove them from the list, and print out a warning about
   each removed commit. You can use `--reapply-cherry-picks` to include
   duplicate commits.
3. Check out `<upstream>` (or `<newbase>` if the `--onto` option was
   supplied) with the equivalent of `git checkout --detach <upstream>`.
4. Replay the commits, one by one, in order. This is similar to running
   `git cherry-pick <commit>` for each commit. See REBASING MERGES for how merges
   are handled.
5. Update your branch to point to the final commit with the equivalent
   of `git switch -C <branch>`.

I imagine actually #1 and #2 happen concurrently, but I split them up because
it felt unwieldy to explain them both the same point.

>
>>  [NOTE]
>> +`ORIG_HEAD` is set to point at the tip of the branch before the rebase.
>>  `ORIG_HEAD` is not guaranteed to still point to the previous branch tip
>>  at the end of the rebase if other commands that write that pseudo-ref
>>  (e.g. `git reset`) are used during the rebase. The previous branch tip,
>>  however, is accessible using the reflog of the current branch
>>  (i.e. `@{1}`, see linkgit:gitrevisions[7]).
>
> This information feels somewhat contradictory. Should we maybe say
> something like this:
>
>     When starting the rebase, `ORIG_HEAD` is set to point to at the tip
>     of the to-be-rebased branch. As `ORIG_HEAD` may be modified by
>     various operations during the rebase, it is not guaranteed to still
>     point to this branch at the end of the rebase. The previous branch
>     tip, however, is accessible using the reflog of the current branch
>     (i.e. `@{1}`, see linkgit:gitrevisions[7]).
>
> Note that I'm also dropping the reference to "pseudo-ref". ORIG_HEAD is
> not a pseudo-ref, as we have clarified in 6fd8037564
> (Documentation/glossary: redefine pseudorefs as special refs,
> 2024-05-15).

I like that! Also very happy to remove the word "pseudo-ref".

- Julia

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Junio C Hamano wrote (reply to this):

"Julia Evans via GitGitGadget" <[email protected]> writes:

> +Here is a simplified description of what `git rebase <upstream>` does:

Now this is a "this is roughly how it works", we can afford to give
a simplified picture that may not exactly match the implementation,
which is great freedom to have while writing documentation for first
time users.

> +1. Make a list of all new commits on your current branch since it branched
> +   off from `<upstream>`. This is the same set of commits that would be shown
> +   by `git log  <upstream>..HEAD`. You can use `--fork-point` or  `--root` to
> +   change how this list of commits is constructed.
> +2. Check whether any of those commits are duplicates of commits already
> +   in `<upstream>` and remove them from the list.

So, I do not mind #2 described as a separate step.  Conceptually, it
is between "create a list of all commits in the range and filter out
what have already been applied" and "create a list of all commits
that are not in the upstream yet".  We may be able to rephrase "all
new commits on your current branch" somewhat to make it unnecessary
to describe #2, though.  If we are willing to stop talking about
"This is the same set of ...", then the description becomes very
simple:

    Make a list of all commits on your current branch since it
    branched off from `<upstream>` that do not have equivalent
    change in `<upstream>`.

> +3. Check out `<upstream>` with the equivalent of `git checkout --detach <upstream>`.

The line is getting overly long here and a few lines below.  If your
editor has the feature, tell it to auto-wrap at around 66-72 column.

> +4. Replay the commits, one by one, in order. This is similar to running
> +   `git cherry-pick <commit>` for each commit. See REBASING MERGES for how merges
> +   are handled.
> +5. Update your branch to point to the final commit with the equivalent
> +   of `git checkout -C <branch>`.

Force-create-branch while checking out is `git checkout -B
<branch>`, not -C.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, "Julia Evans" wrote (reply to this):

> So, I do not mind #2 described as a separate step.  Conceptually, it
> is between "create a list of all commits in the range and filter out
> what have already been applied" and "create a list of all commits
> that are not in the upstream yet".  We may be able to rephrase "all
> new commits on your current branch" somewhat to make it unnecessary
> to describe #2, though.  If we are willing to stop talking about
> "This is the same set of ...", then the description becomes very
> simple:

To me it feels relevant that `git rebase` is using two separate mechanisms to
compare commits: the sha and (I think?) what `git patch-id` does. As a user
it feels like an important distinction because normally when I look at my git
commits I only use the sha (via something like `git log --graph`) to see what
the new commits on a branch are.

Maybe the second part can be rephrased like this to emphasize that it's using a
different mechanism than the SHA:

> 2. Check whether any of those commits contain the same changes (according to
   `git patch-id`) as a commit already in `<upstream>` and remove them from
   the list.


> The line is getting overly long here and a few lines below.  If your
> editor has the feature, tell it to auto-wrap at around 66-72 column.

Will fix, and I'll add a pre-commit hook to check for that.

> Force-create-branch while checking out is `git checkout -B
> <branch>`, not -C.

Will fix.

branch does not have a configured upstream, the rebase will abort.

Here is a simplified description of what `git rebase <upstream>` does:

1. Make a list of all new commits on your current branch since it branched
off from `<upstream>`. This is the same set of commits that would be shown
by `git log <upstream>..HEAD`.
2. Check whether any of those commits contain the same changes (according to
`git patch-id`) as a commit already in `<upstream>` and remove them from
the list.
3. Check out `<upstream>` with the equivalent of
`git checkout --detach <upstream>`.
4. Replay the commits, one by one, in order. This is similar to running
`git cherry-pick <commit>` for each commit. See REBASING MERGES for how merges
are handled.
5. Update your branch to point to the final commit with the equivalent
of `git checkout -B <branch>`.

git rebase --abort
[NOTE]
When starting the rebase, `ORIG_HEAD` is set to point to the commit at the tip
of the to-be-rebased branch. However, `ORIG_HEAD` is not guaranteed to still
point to that commit at the end of the rebase if other commands that change
`ORIG_HEAD` (like `git reset`) are used during the rebase. The previous branch
tip, however, is accessible using the reflog of the current branch (i.e. `@{1}`,
see linkgit:gitrevisions[7].

MODE OPTIONS
------------
Expand Down Expand Up @@ -253,6 +137,8 @@ As a special case, you may use "A\...B" as a shortcut for the
merge base of A and B if there is exactly one merge base. You can
leave out at most one of A and B, in which case it defaults to HEAD.

See TRANSPLANTING A TOPIC BRANCH WITH --ONTO below for examples.

--keep-base::
Set the starting point at which to create the new commits to the
merge base of `<upstream>` and `<branch>`. Running
Expand Down Expand Up @@ -1031,6 +917,91 @@ consistent (they compile, pass the testsuite, etc.) you should use
after each commit, test, and amend the commit if fixes are necessary.


TRANSPLANTING A TOPIC BRANCH WITH --ONTO
----------------------------------------

Here is how you would transplant a topic branch based on one
branch to another, to pretend that you forked the topic branch
from the latter branch, using `rebase --onto`.

First let's assume your 'topic' is based on branch 'next'.
For example, a feature developed in 'topic' depends on some
functionality which is found in 'next'.

------------
o---o---o---o---o master
\
o---o---o---o---o next
\
o---o---o topic
------------

We want to make 'topic' forked from branch 'master'; for example,
because the functionality on which 'topic' depends was merged into the
more stable 'master' branch. We want our tree to look like this:

------------
o---o---o---o---o master
| \
| o'--o'--o' topic
\
o---o---o---o---o next
------------

We can get this using the following command:

git rebase --onto master next topic


Another example of --onto option is to rebase part of a
branch. If we have the following situation:

------------
H---I---J topicB
/
E---F---G topicA
/
A---B---C---D master
------------

then the command

git rebase --onto master topicA topicB

would result in:

------------
H'--I'--J' topicB
/
| E---F---G topicA
|/
A---B---C---D master
------------

This is useful when topicB does not depend on topicA.

A range of commits could also be removed with rebase. If we have
the following situation:

------------
E---F---G---H---I---J topicA
------------

then the command

git rebase --onto topicA~5 topicA~3 topicA

would result in the removal of commits F and G:

------------
E---H'---I'---J' topicA
------------

This is useful if F and G were flawed in some way, or should not be
part of topicA. Note that the argument to `--onto` and the `<upstream>`
parameter can be any valid commit-ish.


RECOVERING FROM UPSTREAM REBASE
-------------------------------

Expand Down
Loading