From d905955af12429d414eed2493486aef26f9fb7cc Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 7 Dec 2022 23:00:57 -0500 Subject: [PATCH 1/7] DOC: move recovering above rebasing on main --- doc/devel/development_workflow.rst | 62 +++++++++++++++--------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/doc/devel/development_workflow.rst b/doc/devel/development_workflow.rst index cf74012d81b6..0162206e7b09 100644 --- a/doc/devel/development_workflow.rst +++ b/doc/devel/development_workflow.rst @@ -170,6 +170,38 @@ To see a linear list of commits for this branch:: git log + +.. _recovering-from-mess-up: + +Recovering from mess-ups +------------------------ + +Sometimes, you mess up merges or rebases. Luckily, in git it is +relatively straightforward to recover from such mistakes. + +If you mess up during a rebase:: + + git rebase --abort + +If you notice you messed up after the rebase:: + + # reset branch back to the saved point + git reset --hard tmp + +If you forgot to make a backup branch:: + + # look at the reflog of the branch + git reflog show cool-feature + + 8630830 cool-feature@{0}: commit: BUG: io: close file handles immediately + 278dd2a cool-feature@{1}: rebase finished: refs/heads/my-feature-branch onto 11ee694744f2552d + 26aa21a cool-feature@{2}: commit: BUG: lib: make seek_gzip_factory not leak gzip obj + ... + + # reset the branch to where it was before the botched rebase + git reset --hard cool-feature@{2} + + .. _rebase-on-main: Rebasing on ``upstream/main`` @@ -242,36 +274,6 @@ some related help on merging in the git user manual - see `resolving a merge`_. .. _resolving a merge: https://schacon.github.io/git/user-manual.html#resolving-a-merge -.. _recovering-from-mess-up: - -Recovering from mess-ups ------------------------- - -Sometimes, you mess up merges or rebases. Luckily, in git it is -relatively straightforward to recover from such mistakes. - -If you mess up during a rebase:: - - git rebase --abort - -If you notice you messed up after the rebase:: - - # reset branch back to the saved point - git reset --hard tmp - -If you forgot to make a backup branch:: - - # look at the reflog of the branch - git reflog show cool-feature - - 8630830 cool-feature@{0}: commit: BUG: io: close file handles immediately - 278dd2a cool-feature@{1}: rebase finished: refs/heads/my-feature-branch onto 11ee694744f2552d - 26aa21a cool-feature@{2}: commit: BUG: lib: make seek_gzip_factory not leak gzip obj - ... - - # reset the branch to where it was before the botched rebase - git reset --hard cool-feature@{2} - .. _rewriting-commit-history: Rewriting commit history From 58ac02d0298bd1034500bec36964ef586818759c Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 7 Dec 2022 23:02:08 -0500 Subject: [PATCH 2/7] DOC: put re-writing your own commits above rebasing on main --- doc/devel/development_workflow.rst | 145 ++++++++++++++--------------- 1 file changed, 72 insertions(+), 73 deletions(-) diff --git a/doc/devel/development_workflow.rst b/doc/devel/development_workflow.rst index 0162206e7b09..60bdce9dfa20 100644 --- a/doc/devel/development_workflow.rst +++ b/doc/devel/development_workflow.rst @@ -201,79 +201,6 @@ If you forgot to make a backup branch:: # reset the branch to where it was before the botched rebase git reset --hard cool-feature@{2} - -.. _rebase-on-main: - -Rebasing on ``upstream/main`` ------------------------------ - -Let's say you thought of some work you'd like to do. You -:ref:`update-mirror-main` and :ref:`make-feature-branch` called -``cool-feature``. At this stage, ``main`` is at some commit, let's call it E. -Now you make some new commits on your ``cool-feature`` branch, let's call them -A, B, C. Maybe your changes take a while, or you come back to them after a -while. In the meantime, ``main`` has progressed from commit E to commit (say) G: - -.. code-block:: none - - A---B---C cool-feature - / - D---E---F---G main - -At this stage you consider merging ``main`` into your feature branch, and you -remember that this here page sternly advises you not to do that, because the -history will get messy. Most of the time you can just ask for a review, and not -worry that ``main`` has got a little ahead. But sometimes, the changes in -``main`` might affect your changes, and you need to harmonize them. In this -situation you may prefer to do a rebase. - -``rebase`` takes your changes (A, B, C) and replays them as if they had been -made to the current state of ``main``. In other words, in this case, it takes -the changes represented by A, B, C and replays them on top of G. After the -rebase, your history will look like this: - -.. code-block:: none - - A'--B'--C' cool-feature - / - D---E---F---G main - -See `rebase without tears`_ for more detail. - -.. _rebase without tears: https://matthew-brett.github.io/pydagogue/rebase_without_tears.html - -To do a rebase on ``upstream/main``:: - - # Fetch changes from upstream/main - git fetch upstream - # go to the feature branch - git checkout cool-feature - # make a backup in case you mess up - git branch tmp cool-feature - # rebase cool-feature onto main - git rebase --onto upstream/main upstream/main cool-feature - -In this situation, where you are already on branch ``cool-feature``, the last -command can be written more succinctly as:: - - git rebase upstream/main - -When all looks good you can delete your backup branch:: - - git branch -D tmp - -If it doesn't look good you may need to have a look at -:ref:`recovering-from-mess-up`. - -If you have made changes to files that have also changed in ``main``, this may -generate merge conflicts that you need to resolve - see the `git rebase`_ man -page for some instructions at the end of the "Description" section. There is -some related help on merging in the git user manual - see `resolving a merge`_. - -.. _git rebase: https://git-scm.com/docs/git-rebase -.. _resolving a merge: https://schacon.github.io/git/user-manual.html#resolving-a-merge - - .. _rewriting-commit-history: Rewriting commit history @@ -360,3 +287,75 @@ and the history looks now like this:: If it went wrong, recovery is again possible as explained :ref:`above `. + + +.. _rebase-on-main: + +Rebasing on ``upstream/main`` +----------------------------- + +Let's say you thought of some work you'd like to do. You +:ref:`update-mirror-main` and :ref:`make-feature-branch` called +``cool-feature``. At this stage, ``main`` is at some commit, let's call it E. +Now you make some new commits on your ``cool-feature`` branch, let's call them +A, B, C. Maybe your changes take a while, or you come back to them after a +while. In the meantime, ``main`` has progressed from commit E to commit (say) G: + +.. code-block:: none + + A---B---C cool-feature + / + D---E---F---G main + +At this stage you consider merging ``main`` into your feature branch, and you +remember that this here page sternly advises you not to do that, because the +history will get messy. Most of the time you can just ask for a review, and not +worry that ``main`` has got a little ahead. But sometimes, the changes in +``main`` might affect your changes, and you need to harmonize them. In this +situation you may prefer to do a rebase. + +``rebase`` takes your changes (A, B, C) and replays them as if they had been +made to the current state of ``main``. In other words, in this case, it takes +the changes represented by A, B, C and replays them on top of G. After the +rebase, your history will look like this: + +.. code-block:: none + + A'--B'--C' cool-feature + / + D---E---F---G main + +See `rebase without tears`_ for more detail. + +.. _rebase without tears: https://matthew-brett.github.io/pydagogue/rebase_without_tears.html + +To do a rebase on ``upstream/main``:: + + # Fetch changes from upstream/main + git fetch upstream + # go to the feature branch + git checkout cool-feature + # make a backup in case you mess up + git branch tmp cool-feature + # rebase cool-feature onto main + git rebase --onto upstream/main upstream/main cool-feature + +In this situation, where you are already on branch ``cool-feature``, the last +command can be written more succinctly as:: + + git rebase upstream/main + +When all looks good you can delete your backup branch:: + + git branch -D tmp + +If it doesn't look good you may need to have a look at +:ref:`recovering-from-mess-up`. + +If you have made changes to files that have also changed in ``main``, this may +generate merge conflicts that you need to resolve - see the `git rebase`_ man +page for some instructions at the end of the "Description" section. There is +some related help on merging in the git user manual - see `resolving a merge`_. + +.. _git rebase: https://git-scm.com/docs/git-rebase +.. _resolving a merge: https://schacon.github.io/git/user-manual.html#resolving-a-merge From b9d9881e6cf14202620fec4b03c59705f51e97fb Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 7 Dec 2022 23:32:18 -0500 Subject: [PATCH 3/7] DOC: add section on force-pushing --- doc/devel/development_workflow.rst | 56 ++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/doc/devel/development_workflow.rst b/doc/devel/development_workflow.rst index 60bdce9dfa20..ae09c333cf04 100644 --- a/doc/devel/development_workflow.rst +++ b/doc/devel/development_workflow.rst @@ -288,6 +288,10 @@ and the history looks now like this:: If it went wrong, recovery is again possible as explained :ref:`above `. +If you have not yet pushed this branch to github, you can carry on as normal, +however if you *have* already pushed this commit see :ref:`force-push` for how +to replace your already published commits with the new ones. + .. _rebase-on-main: @@ -359,3 +363,55 @@ some related help on merging in the git user manual - see `resolving a merge`_. .. _git rebase: https://git-scm.com/docs/git-rebase .. _resolving a merge: https://schacon.github.io/git/user-manual.html#resolving-a-merge + + +If you have not yet pushed this branch to github, you can carry on as normal, +however if you *have* already pushed this commit see :ref:`force-push` for how +to replace your already published commits with the new ones. + + +.. _force-push: + + +Pushing, with force +------------------- + +If you have in some way re-written already pushed history (e.g. via +:ref:`rewriting-commit-history` or :ref:`rebase-on-main`), then when you try to +push the new commits to GitHub it will fail with an error that looks like :: + + $ git push + Pushing to github.com:YOURFORK/matplotlib.git + To github.com:YOURFORK/matplotlib.git + ! [rejected] cool_feature -> cool_feature (non-fast-forward) + error: failed to push some refs to 'github.com:tacaswell/matplotlib.git' + hint: Updates were rejected because the tip of your current branch is behind + hint: its remote counterpart. Integrate the remote changes (e.g. + hint: 'git pull ...') before pushing again. + hint: See the 'Note about fast-forwards' in 'git push --help' for details. + +What is going on is that by default ``git push`` tries to protect you from +accidentally make commits inaccessible by leaving no reference to them so if a +push *would* effectively discard commits on the remote, ``git`` rejects the +push. When this happens GitHub adds the helpful suggestion to pull the remote +changes and then try pushing again. In some cases, such as if you and a +colleague are both committing and pushing to the same branch, this is the +correct course of action (or rebase your local branch but it is a matter of +taste). Although both of things are good ideas, taken together they are not +what we want to do. + +In the case of having intentionally re-written history we *want* to make the +commits on GitHub inaccessible and replace them with the new-and-improved versions +on our local machines. In these cases what we want to do is :: + + $ git push --force-with-lease + +which tells git you are aware of the risks and want to do the push anyway. We +recommend using ``--force-with-lease`` over the ``--force`` flag. The +``--force`` will do the push no matter what, whereas ``--force-with-lease`` +will only do the push of the remote branch is where the local ``git`` client +thought it was. + +Be judicious with force-pushing. It is effectively re-writing published +history and if anyone has fetched the old commits will have a different view +of history which can cause confusion. From 307994424f3055c62a40e916003ce6420b3687b5 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 8 Dec 2022 00:36:34 -0500 Subject: [PATCH 4/7] DOC: tighten up wording Co-authored-by: Jody Klymak --- doc/devel/development_workflow.rst | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/doc/devel/development_workflow.rst b/doc/devel/development_workflow.rst index ae09c333cf04..49d7b49e90f2 100644 --- a/doc/devel/development_workflow.rst +++ b/doc/devel/development_workflow.rst @@ -391,18 +391,15 @@ push the new commits to GitHub it will fail with an error that looks like :: hint: See the 'Note about fast-forwards' in 'git push --help' for details. What is going on is that by default ``git push`` tries to protect you from -accidentally make commits inaccessible by leaving no reference to them so if a -push *would* effectively discard commits on the remote, ``git`` rejects the -push. When this happens GitHub adds the helpful suggestion to pull the remote +accidentally discarding commits on the remote by rejecting the +push. When this happens, GitHub adds the helpful suggestion to pull the remote changes and then try pushing again. In some cases, such as if you and a colleague are both committing and pushing to the same branch, this is the correct course of action (or rebase your local branch but it is a matter of -taste). Although both of things are good ideas, taken together they are not -what we want to do. +taste). -In the case of having intentionally re-written history we *want* to make the -commits on GitHub inaccessible and replace them with the new-and-improved versions -on our local machines. In these cases what we want to do is :: +However, in the case of having intentionally re-written history we *want* to discard the commits and +replace them with the new-and-improved versions from our local branch. In these cases what we want to do is :: $ git push --force-with-lease From 446eff4942f2c00f51ed0004060305a67edf5aab Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 8 Dec 2022 13:38:29 -0500 Subject: [PATCH 5/7] DOC: copy editing Co-authored-by: Oscar Gustafsson --- doc/devel/development_workflow.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/devel/development_workflow.rst b/doc/devel/development_workflow.rst index 49d7b49e90f2..3eac353771ad 100644 --- a/doc/devel/development_workflow.rst +++ b/doc/devel/development_workflow.rst @@ -312,7 +312,7 @@ while. In the meantime, ``main`` has progressed from commit E to commit (say) G: D---E---F---G main At this stage you consider merging ``main`` into your feature branch, and you -remember that this here page sternly advises you not to do that, because the +remember that this page sternly advises you not to do that, because the history will get messy. Most of the time you can just ask for a review, and not worry that ``main`` has got a little ahead. But sometimes, the changes in ``main`` might affect your changes, and you need to harmonize them. In this @@ -399,14 +399,14 @@ correct course of action (or rebase your local branch but it is a matter of taste). However, in the case of having intentionally re-written history we *want* to discard the commits and -replace them with the new-and-improved versions from our local branch. In these cases what we want to do is :: +replace them with the new-and-improved versions from our local branch. In these cases, what we want to do is :: $ git push --force-with-lease which tells git you are aware of the risks and want to do the push anyway. We recommend using ``--force-with-lease`` over the ``--force`` flag. The ``--force`` will do the push no matter what, whereas ``--force-with-lease`` -will only do the push of the remote branch is where the local ``git`` client +will only do the push if the remote branch is where the local ``git`` client thought it was. Be judicious with force-pushing. It is effectively re-writing published From 82932e8dfed6c5487e64cb3bdf31980c28e249f1 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 8 Dec 2022 13:55:37 -0500 Subject: [PATCH 6/7] DOC: add diagram and explanation --- doc/devel/development_workflow.rst | 43 +++++++++++++++++++----------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/doc/devel/development_workflow.rst b/doc/devel/development_workflow.rst index 3eac353771ad..c8c5a225321a 100644 --- a/doc/devel/development_workflow.rst +++ b/doc/devel/development_workflow.rst @@ -376,30 +376,43 @@ to replace your already published commits with the new ones. Pushing, with force ------------------- + If you have in some way re-written already pushed history (e.g. via -:ref:`rewriting-commit-history` or :ref:`rebase-on-main`), then when you try to -push the new commits to GitHub it will fail with an error that looks like :: +:ref:`rewriting-commit-history` or :ref:`rebase-on-main`) leaving you with +a git history that looks something like + +.. code-block:: none + + A'--E cool-feature + / + D---A---B---C origin/cool-feature + +where you have pushed the commits ``A,B,C`` to your fork on GitHub (under the +remote name *origin*) but now have the commits ``A'`` and ``E`` on your local +branch *cool-feature*. If you try to push the new commits to GitHub it will +fail with an error that looks like :: $ git push - Pushing to github.com:YOURFORK/matplotlib.git - To github.com:YOURFORK/matplotlib.git + Pushing to github.com:origin/matplotlib.git + To github.com:origin/matplotlib.git ! [rejected] cool_feature -> cool_feature (non-fast-forward) - error: failed to push some refs to 'github.com:tacaswell/matplotlib.git' + error: failed to push some refs to 'github.com:origin/matplotlib.git' hint: Updates were rejected because the tip of your current branch is behind hint: its remote counterpart. Integrate the remote changes (e.g. hint: 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details. -What is going on is that by default ``git push`` tries to protect you from -accidentally discarding commits on the remote by rejecting the -push. When this happens, GitHub adds the helpful suggestion to pull the remote -changes and then try pushing again. In some cases, such as if you and a -colleague are both committing and pushing to the same branch, this is the -correct course of action (or rebase your local branch but it is a matter of -taste). - -However, in the case of having intentionally re-written history we *want* to discard the commits and -replace them with the new-and-improved versions from our local branch. In these cases, what we want to do is :: +If this push had succeed then the commits ``A``, ``B``, and ``C`` would no +longer be referenced by any branch and be discarded. By default ``git push`` +helpfully tries to protect you from accidentally discarding commits by +rejecting the push to the remote. When this happens, GitHub also adds the +helpful suggestion to pull the remote changes and then try pushing again. In +some cases, such as if you and a colleague are both committing and pushing to +the same branch, this is a correct course of action. + +However, in the case of having intentionally re-written history we *want* to +discard the commits on the remote and replace them with the new-and-improved +versions from our local branch. In this case, what we want to do is :: $ git push --force-with-lease From cf53019e007d12443eff17f6816837685ed27926 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 9 Dec 2022 13:11:07 -0500 Subject: [PATCH 7/7] DOC: add diagram with missing commits Co-authored-by: Jody Klymak --- doc/devel/development_workflow.rst | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/doc/devel/development_workflow.rst b/doc/devel/development_workflow.rst index c8c5a225321a..02a5e87061de 100644 --- a/doc/devel/development_workflow.rst +++ b/doc/devel/development_workflow.rst @@ -403,12 +403,17 @@ fail with an error that looks like :: hint: See the 'Note about fast-forwards' in 'git push --help' for details. If this push had succeed then the commits ``A``, ``B``, and ``C`` would no -longer be referenced by any branch and be discarded. By default ``git push`` -helpfully tries to protect you from accidentally discarding commits by -rejecting the push to the remote. When this happens, GitHub also adds the -helpful suggestion to pull the remote changes and then try pushing again. In -some cases, such as if you and a colleague are both committing and pushing to -the same branch, this is a correct course of action. +longer be referenced by any branch and be discarded: + +.. code-block:: none + + D---A'---E cool-feature, origin/cool-feature + +By default ``git push`` helpfully tries to protect you from accidentally +discarding commits by rejecting the push to the remote. When this happens, +GitHub also adds the helpful suggestion to pull the remote changes and then try +pushing again. In some cases, such as if you and a colleague are both +committing and pushing to the same branch, this is a correct course of action. However, in the case of having intentionally re-written history we *want* to discard the commits on the remote and replace them with the new-and-improved