Re: Is there any way to "interrupt" a rebase?
On 20/02/18 11:44, Johannes Schindelin wrote: > > Hi Hilco, > > On Tue, 20 Feb 2018, Johannes Schindelin wrote: > >> When I am particularly tired and overworked (and therefore know that my >> working memory is less useful than usual), I therefore resort to my >> second-favorite strategy: U use the `done` file. >> >> I literally copy parts of $GIT_DIR/rebase-merge/done to the beginning of >> $GIT_DIR/rebase-merge/git-rebase-todo (the most convenient way to open the >> latter is `git rebase --edit-todo`). In your case, those would be the >> `pick` lines cherry-picking D and E. Then, as before, `git reset --hard >> ` (where I look up the `` using an aliased version of `git >> log --graph --oneline --left-right --boundary`), amend the commit, and >> then `git rebase --continue`. >> >> It might be even possible to design a new subcommand for the interactive >> rebase to facilitate a variation of this strategy (possibly even making >> use of the fact that the interactive rebase accumulates mappings between >> the original commits and the rewritten ones in >> $GIT_DIR/rebase-merge/rewritten-list, intended for use in the post-rewrite >> hook). > > This feature might look somewhat like this: > > git rebase --replay-latest-commits 3 > > and it would not even have to look at the `rewritten-list`. All it would > do is to put back the latest `pick` from the `done` file (in case of merge > conflicts) into the `git-rebase-todo` file, then insert `pick lines for > HEAD~3.. at the beginning of that todo file, and then `git reset --hard > HEAD~3`. > > By not using the original lines from the `done` file (i.e. *different* > from what I described as my second-favorite strategy), you would also get > the resolved merge conflicts rather than having to re-resolve them. Yes that's why I prefer using pasting the output of git log rather than bits of rebase-merge/done into the todo list when rewinding manually. > > (This all would of course only work properly without --preserve-merges and > without the upcoming --recreate-merges.) It shouldn't be too hard to support --recreate-merges in the case where you don't go past a merge commit, one would just have to check refs/rewritten to see if any label commands need to be inserted. Best Wishes Phillip > Ciao, > Johannes >
Re: Is there any way to "interrupt" a rebase?
On Tue, Feb 20, 2018 at 12:56 PM, Jeff Kingwrote: > On Tue, Feb 20, 2018 at 12:44:51PM +0100, Johannes Schindelin wrote: > >> > It might be even possible to design a new subcommand for the interactive >> > rebase to facilitate a variation of this strategy (possibly even making >> > use of the fact that the interactive rebase accumulates mappings between >> > the original commits and the rewritten ones in >> > $GIT_DIR/rebase-merge/rewritten-list, intended for use in the post-rewrite >> > hook). >> >> This feature might look somewhat like this: >> >> git rebase --replay-latest-commits 3 >> >> and it would not even have to look at the `rewritten-list`. All it would >> do is to put back the latest `pick` from the `done` file (in case of merge >> conflicts) into the `git-rebase-todo` file, then insert `pick lines for >> HEAD~3.. at the beginning of that todo file, and then `git reset --hard >> HEAD~3`. > > Keep in mind that the "pick" lines could be "edit", "squash", etc. > > I think the general form of your original email's proposal is something > like: What if we had a "git rebase --rewind" that could "undo" the prior > command? So if I had a todo file like: > > pick 1 > edit 2 > x make test > edit 3 > x make test > pick 4 > > and I failed at the second "make test", then I'd have: > > pick 1 > edit 2 > x make test > edit 3 > x make test > > in the "done" file, with the final pick remaining in "todo". Could I > then ask to "rewind" my state by moving "x make test" back to the > "todo". And two rewinds would get me back to applying patch 3, which I > could then fix up and re-run my test. Or four rewinds would get me back > to patch 2, which maybe is where I made the initial mistake. > > That's a bit more primitive than what you're proposing in this > follow-on, because you'd be doing the replay yourself (unless we remap > the commits). But it's very easy to reason about and implement. > > Anyway, just musing at this point. I haven't thought it through, but I > like the direction of everything you're saying. ;) > > -Peff Using a --rewind that simply tracks the point of each history and can reset back to each seems a bit more inline with what the original suggestion is. Sort of like "undo" in an editor might. You could even add a "rewind=x" so it could go back more than one step at a time, tho just re-running rewind until you get where you want would be doable as well. I like the overall direction of both these suggestions. Thanks, Jake
Re: Is there any way to "interrupt" a rebase?
On Tue, Feb 20, 2018 at 12:44:51PM +0100, Johannes Schindelin wrote: > > It might be even possible to design a new subcommand for the interactive > > rebase to facilitate a variation of this strategy (possibly even making > > use of the fact that the interactive rebase accumulates mappings between > > the original commits and the rewritten ones in > > $GIT_DIR/rebase-merge/rewritten-list, intended for use in the post-rewrite > > hook). > > This feature might look somewhat like this: > > git rebase --replay-latest-commits 3 > > and it would not even have to look at the `rewritten-list`. All it would > do is to put back the latest `pick` from the `done` file (in case of merge > conflicts) into the `git-rebase-todo` file, then insert `pick lines for > HEAD~3.. at the beginning of that todo file, and then `git reset --hard > HEAD~3`. Keep in mind that the "pick" lines could be "edit", "squash", etc. I think the general form of your original email's proposal is something like: What if we had a "git rebase --rewind" that could "undo" the prior command? So if I had a todo file like: pick 1 edit 2 x make test edit 3 x make test pick 4 and I failed at the second "make test", then I'd have: pick 1 edit 2 x make test edit 3 x make test in the "done" file, with the final pick remaining in "todo". Could I then ask to "rewind" my state by moving "x make test" back to the "todo". And two rewinds would get me back to applying patch 3, which I could then fix up and re-run my test. Or four rewinds would get me back to patch 2, which maybe is where I made the initial mistake. That's a bit more primitive than what you're proposing in this follow-on, because you'd be doing the replay yourself (unless we remap the commits). But it's very easy to reason about and implement. Anyway, just musing at this point. I haven't thought it through, but I like the direction of everything you're saying. ;) -Peff
Re: Is there any way to "interrupt" a rebase?
Hi Hilco, On Tue, 20 Feb 2018, Johannes Schindelin wrote: > When I am particularly tired and overworked (and therefore know that my > working memory is less useful than usual), I therefore resort to my > second-favorite strategy: U use the `done` file. > > I literally copy parts of $GIT_DIR/rebase-merge/done to the beginning of > $GIT_DIR/rebase-merge/git-rebase-todo (the most convenient way to open the > latter is `git rebase --edit-todo`). In your case, those would be the > `pick` lines cherry-picking D and E. Then, as before, `git reset --hard > ` (where I look up the `` using an aliased version of `git > log --graph --oneline --left-right --boundary`), amend the commit, and > then `git rebase --continue`. > > It might be even possible to design a new subcommand for the interactive > rebase to facilitate a variation of this strategy (possibly even making > use of the fact that the interactive rebase accumulates mappings between > the original commits and the rewritten ones in > $GIT_DIR/rebase-merge/rewritten-list, intended for use in the post-rewrite > hook). This feature might look somewhat like this: git rebase --replay-latest-commits 3 and it would not even have to look at the `rewritten-list`. All it would do is to put back the latest `pick` from the `done` file (in case of merge conflicts) into the `git-rebase-todo` file, then insert `pick lines for HEAD~3.. at the beginning of that todo file, and then `git reset --hard HEAD~3`. By not using the original lines from the `done` file (i.e. *different* from what I described as my second-favorite strategy), you would also get the resolved merge conflicts rather than having to re-resolve them. (This all would of course only work properly without --preserve-merges and without the upcoming --recreate-merges.) Ciao, Johannes
Re: Is there any way to "interrupt" a rebase?
Hi Hilco, On Mon, 19 Feb 2018, Hilco Wijbenga wrote: > On Mon, Feb 19, 2018 at 2:36 PM, brian m. carlson >wrote: > > On Mon, Feb 19, 2018 at 11:35:25AM -0800, Hilco Wijbenga wrote: > >> So a scenario like this: > >> > >> my-branch : X -> A -> B -> C -> D -> E -> F -> G > >> base-branch : X -> Y > >> > >> git rebase --onto base-branch HEAD~7 > >> commit A --> conflicts > >> ... lots of work ... > >> commit B --> conflicts > >> ... lots of work ... > >> commit C (Git handles conflicts) > >> commit D (no conflict) > >> commit E --> conflicts > >> ... er, that should have been fixed in commit C > >> > >> How do I keep all the work I did for commits A and B? I get the > >> impression that rerere does not help here because I did not finish the > >> rebase succesfully (and that makes perfect sense, of course). Is there > >> a way at this point in the rebase to "go back" to commit C (so without > >> "git rebase --abort")? > > > > What I do in this case is I unstage all the changes from the index, make > > the change that should have gone into commit C, use git commit --fixup > > (or --squash), and then restage the rest of the changes and continue > > with the rebase. I can then use git rebase -i --autosquash afterwards > > to insert the commit into the right place. > > Yes, that's essentially what I end up doing too. Obviously, in cases > like this, Murphy likes to drop by so commit D will have made changes > to the same files as commit C and you can't cleanly move the fix-up > commit to commit C. :-( I had hoped there might be an easier/cleaner > way to do it. I am a heavy user of interactive rebase. Which is unsurprising, given that I implemented the first version of it. And I find myself in the same situation quite often (as of recent, more often when doing complicated rebases involving non-linear commit topologies, but I digress). Being that familiar with the internals of the interactive rebase command gives me the opportunity to do "clever" things. My number one strategy is to mix interactive rebase with cherry-pick: I `git reset --hard HEAD^` (which refers to the rewritten C), fix up the commit as intended, then look at the latest interactive rebase command listed in the output of `git status`, then determine the commit range of commits I want to replay on top and then call `git cherry-pick ` (in your case, that would be `git cherry-pick C..E`. Admittedly, this strategy is a bit cumbersome because a lot of book-keeping is performed by my working memory instead of Git. When I am particularly tired and overworked (and therefore know that my working memory is less useful than usual), I therefore resort to my second-favorite strategy: U use the `done` file. I literally copy parts of $GIT_DIR/rebase-merge/done to the beginning of $GIT_DIR/rebase-merge/git-rebase-todo (the most convenient way to open the latter is `git rebase --edit-todo`). In your case, those would be the `pick` lines cherry-picking D and E. Then, as before, `git reset --hard ` (where I look up the `` using an aliased version of `git log --graph --oneline --left-right --boundary`), amend the commit, and then `git rebase --continue`. It might be even possible to design a new subcommand for the interactive rebase to facilitate a variation of this strategy (possibly even making use of the fact that the interactive rebase accumulates mappings between the original commits and the rewritten ones in $GIT_DIR/rebase-merge/rewritten-list, intended for use in the post-rewrite hook). Ciao, Johannes
Re: Is there any way to "interrupt" a rebase?
On 19/02/18 19:35, Hilco Wijbenga wrote: > > Hi all, > > When maintaining a long running branch, I regularly rebase onto our > active development branch so that my branch stays up-to-date. What > happens fairly often is that during such a rebase, Git will exit > because of rebase/merge conflicts. Nothing unexpected there, of > course, but as it sometimes turns out, the conflict should have been > fixed in an earlier commit. The only way that I know of to fix this, > is to abort the rebase and start over with "git rebase ... > --interactive" then "edit" every commit and go through them > one-by-one. This is often overkill, though. Is there a better way? > Perhaps I could "rewind" the rebase to an earlier commit and restart > from there? > > So a scenario like this: > > my-branch : X -> A -> B -> C -> D -> E -> F -> G > base-branch : X -> Y > > git rebase --onto base-branch HEAD~7 > commit A --> conflicts > lots of work ... > commit B --> conflicts > lots of work ... > commit C (Git handles conflicts) > commit D (no conflict) > commit E --> conflicts > er, that should have been fixed in commit C > > How do I keep all the work I did for commits A and B? I get the > impression that rerere does not help here because I did not finish the > rebase succesfully (and that makes perfect sense, of course). Is there > a way at this point in the rebase to "go back" to commit C (so without > "git rebase --abort")? > > (Surely, it's not as simple as doing a "git reset --hard > sha-of-commit-C" is it?) Hi Hilco In the past when I've wanted to edit the commit directly rather than create a fixup I've done something like git log --pretty="pick %h %s" C.. git rebase --edit-todo git checkout C git commit --amend git rebase --continue Best Wishes Phillip > Cheers, > Hilco >
Re: Is there any way to "interrupt" a rebase?
On Mon, Feb 19, 2018 at 11:35:25AM -0800, Hilco Wijbenga wrote: > git rebase --onto base-branch HEAD~7 > commit A --> conflicts > ... lots of work ... > commit B --> conflicts > ... lots of work ... > commit C (Git handles conflicts) > commit D (no conflict) > commit E --> conflicts > ... er, that should have been fixed in commit C > > How do I keep all the work I did for commits A and B? I get the > impression that rerere does not help here because I did not finish the > rebase succesfully (and that makes perfect sense, of course). Is there > a way at this point in the rebase to "go back" to commit C (so without > "git rebase --abort")? rerere should help, since the resolutions are stored when you run "git commit". Of course, sometimes there are changes outside of the conflicted regions that are necessary. One thing you can do is to repeat the rebase, but simply pick the resolved state at each step. E.g.: # remember the failed attempt; you could also store the sha1 in an # environment variable, or even store individual commits. git tag failed # now abort and retry the rebase git rebase --abort git rebase -i # we should have stopped on commit A with conflicts. Now we can say # "make the tree just like the other time we saw A". git checkout failed~4 -- . # or if you want to review the changes, try this: git checkout -p failed~4 And so on. You visit each commit as before, but you can always grab your previous work (or parts of it, with pathspec limiting or "-p") instead of repeating the work. I often do something like this for complicated merges (e.g., of two long-running branches). I try the merge first, only to find that some preparatory steps would make it a lot easier. So I commit the merged result (even sometimes half-finished), "reset --hard" back to the original, do the early steps, and then re-merge. And then I "checkout -p" to pick my work out of the earlier failed attempt. > (Surely, it's not as simple as doing a "git reset --hard > sha-of-commit-C" is it?) Not quite that simple, but that's another approach. If you "git reset --hard" you'll lose D and E, since the rebase has already applied them. But you could then replay them manually, like this: # go back to C and fix it git reset --hard C fix fix fix git commit --amend -m 'fixed C' # now replay the other commits on top git cherry-pick D git cherry-pick E # and then continue on with any other rebased patches git rebase --continue -Peff
Re: Is there any way to "interrupt" a rebase?
On Mon, Feb 19, 2018 at 2:36 PM, brian m. carlsonwrote: > On Mon, Feb 19, 2018 at 11:35:25AM -0800, Hilco Wijbenga wrote: >> So a scenario like this: >> >> my-branch : X -> A -> B -> C -> D -> E -> F -> G >> base-branch : X -> Y >> >> git rebase --onto base-branch HEAD~7 >> commit A --> conflicts >> ... lots of work ... >> commit B --> conflicts >> ... lots of work ... >> commit C (Git handles conflicts) >> commit D (no conflict) >> commit E --> conflicts >> ... er, that should have been fixed in commit C >> >> How do I keep all the work I did for commits A and B? I get the >> impression that rerere does not help here because I did not finish the >> rebase succesfully (and that makes perfect sense, of course). Is there >> a way at this point in the rebase to "go back" to commit C (so without >> "git rebase --abort")? > > What I do in this case is I unstage all the changes from the index, make > the change that should have gone into commit C, use git commit --fixup > (or --squash), and then restage the rest of the changes and continue > with the rebase. I can then use git rebase -i --autosquash afterwards > to insert the commit into the right place. Yes, that's essentially what I end up doing too. Obviously, in cases like this, Murphy likes to drop by so commit D will have made changes to the same files as commit C and you can't cleanly move the fix-up commit to commit C. :-( I had hoped there might be an easier/cleaner way to do it.
Re: Is there any way to "interrupt" a rebase?
On Mon, Feb 19, 2018 at 11:35:25AM -0800, Hilco Wijbenga wrote: > So a scenario like this: > > my-branch : X -> A -> B -> C -> D -> E -> F -> G > base-branch : X -> Y > > git rebase --onto base-branch HEAD~7 > commit A --> conflicts > ... lots of work ... > commit B --> conflicts > ... lots of work ... > commit C (Git handles conflicts) > commit D (no conflict) > commit E --> conflicts > ... er, that should have been fixed in commit C > > How do I keep all the work I did for commits A and B? I get the > impression that rerere does not help here because I did not finish the > rebase succesfully (and that makes perfect sense, of course). Is there > a way at this point in the rebase to "go back" to commit C (so without > "git rebase --abort")? What I do in this case is I unstage all the changes from the index, make the change that should have gone into commit C, use git commit --fixup (or --squash), and then restage the rest of the changes and continue with the rebase. I can then use git rebase -i --autosquash afterwards to insert the commit into the right place. -- brian m. carlson / brian with sandals: Houston, Texas, US https://www.crustytoothpaste.net/~bmc | My opinion only OpenPGP: https://keybase.io/bk2204 signature.asc Description: PGP signature