{"thread":{"id":"50324","subject":"[PATCH] rebase: move state_dir to tmp prior to deletion","startedAt":"2019-01-26T07:41:26Z","lastAt":"2019-01-26T13:59:21Z","messageCount":3,"participants":["Ben Woosley","Alban Gruin"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"367700","messageId":"01020168891b1a3e-140bd175-a8cb-4379-a114-de68b1cac5d6-000000@eu-west-1.amazonses.com","threadId":"50324","inReplyTo":null,"subject":"[PATCH] rebase: move state_dir to tmp prior to deletion","fromName":"Ben Woosley","fromEmail":"ben.woosley@gmail.com","sentAt":"2019-01-26T07:41:21Z","receivedAt":"2019-01-26T07:41:26Z","isPatch":true,"sender":{"key":"ben.woosley@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5470?v=4"},"body":"From: Ben Woosley <ben.woosley@gmail.com>\n\nTo avoid partial deletion / zombie rebases.\n\nExample behavior under partial deletion, after\nCtrl-Cing out of a standard rebase:\n\n    $ git rebase target\n    First, rewinding head to replay your work on top of it...\n    Applying: [...]\n    ^C\n    $ git status\n    rebase in progress; onto (null)\n    You are currently rebasing.\n      (all conflicts fixed: run \"git rebase --continue\")\n\n    Changes to be committed:\n      (use \"git reset HEAD <file>...\" to unstage)\n    [...]\n    $ git rebase --continue\n    error: could not read '.git/rebase-apply/head-name': No such file or directory\n    $ git rebase --abort\n    error: could not read '.git/rebase-apply/head-name': No such file or directory\n\nOthers report this issue here:\nhttps://stackoverflow.com/questions/3685001/git-how-to-fix-corrupted-interactive-rebase\n---\n git-legacy-rebase.sh           | 17 ++++++++++++++---\n git-rebase--preserve-merges.sh |  2 +-\n 2 files changed, 15 insertions(+), 4 deletions(-)\n\ndiff --git a/git-legacy-rebase.sh b/git-legacy-rebase.sh\nindex b4c7dbfa575d3..878c0e42054d7 100755\n--- a/git-legacy-rebase.sh\n+++ b/git-legacy-rebase.sh\n@@ -128,11 +128,22 @@ read_basic_state () {\n \t}\n }\n \n+remove_rebase_state () {\n+  state_tmpdir=$(mktemp -d -t \"git-rebase-state-XXXXXX\")\n+  if test -d state_tmpdir\n+  then\n+    exec mv \"$state_dir\" \"$state_tmpdir\"\n+    exec rm -rf \"$state_tmpdir\"\n+  else\n+    exec rm -rf \"$state_dir\"\n+  fi\n+}\n+\n finish_rebase () {\n \trm -f \"$(git rev-parse --git-path REBASE_HEAD)\"\n \tapply_autostash &&\n \t{ git gc --auto || true; } &&\n-\trm -rf \"$state_dir\"\n+\tremove_rebase_state\n }\n \n run_interactive () {\n@@ -194,7 +205,7 @@ run_specific_rebase () {\n \telif test $ret -eq 2 # special exit status for rebase -p\n \tthen\n \t\tapply_autostash &&\n-\t\trm -rf \"$state_dir\" &&\n+\t\tremove_rebase_state &&\n \t\tdie \"Nothing to do\"\n \tfi\n \texit $ret\n@@ -439,7 +450,7 @@ abort)\n \texit\n \t;;\n quit)\n-\texec rm -rf \"$state_dir\"\n+\tremove_rebase_state\n \t;;\n edit-todo)\n \trun_specific_rebase\ndiff --git a/git-rebase--preserve-merges.sh b/git-rebase--preserve-merges.sh\nindex afbb65765d461..146b52df14928 100644\n--- a/git-rebase--preserve-merges.sh\n+++ b/git-rebase--preserve-merges.sh\n@@ -226,7 +226,7 @@ Once you are satisfied with your changes, run\n \n die_abort () {\n \tapply_autostash\n-\trm -rf \"$state_dir\"\n+\tremove_rebase_state\n \tdie \"$1\"\n }\n \n\n--\nhttps://github.com/git/git/pull/569\n"},{"id":"367702","messageId":"0102016889a9145a-83604d85-036b-409b-a3d2-2eb1ef2c5b95-000000@eu-west-1.amazonses.com","threadId":"50324","inReplyTo":"01020168891b1a3e-140bd175-a8cb-4379-a114-de68b1cac5d6-000000@eu-west-1.amazonses.com","subject":"[PATCH v2] rebase: move state_dir to tmp prior to deletion","fromName":"Ben Woosley","fromEmail":"ben.woosley@gmail.com","sentAt":"2019-01-26T10:16:26Z","receivedAt":"2019-01-26T10:16:29Z","isPatch":true,"sender":{"key":"ben.woosley@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5470?v=4"},"body":"From: Ben Woosley <ben.woosley@gmail.com>\n\nTo avoid partial deletion / zombie rebases.\n\nExample behavior under partial deletion, after\nCtrl-Cing out of a standard rebase:\n\n    $ git rebase target\n    First, rewinding head to replay your work on top of it...\n    Applying: [...]\n    ^C\n    $ git status\n    rebase in progress; onto (null)\n    You are currently rebasing.\n      (all conflicts fixed: run \"git rebase --continue\")\n\n    Changes to be committed:\n      (use \"git reset HEAD <file>...\" to unstage)\n    [...]\n    $ git rebase --continue\n    error: could not read '.git/rebase-apply/head-name': No such file or directory\n    $ git rebase --abort\n    error: could not read '.git/rebase-apply/head-name': No such file or directory\n\nOthers report this issue here:\nhttps://stackoverflow.com/questions/3685001/git-how-to-fix-corrupted-interactive-rebase\n---\n git-legacy-rebase.sh           | 17 ++++++++++++++---\n git-rebase--preserve-merges.sh |  2 +-\n 2 files changed, 15 insertions(+), 4 deletions(-)\n\ndiff --git a/git-legacy-rebase.sh b/git-legacy-rebase.sh\nindex b4c7dbfa575d3..832a211c925c3 100755\n--- a/git-legacy-rebase.sh\n+++ b/git-legacy-rebase.sh\n@@ -128,11 +128,22 @@ read_basic_state () {\n \t}\n }\n \n+remove_rebase_state () {\n+  removal_dir=$(mktemp -d -t \"git-rebase-state-XXXXXX\")\n+  if test -d \"$removal_dir\"\n+  then\n+    mv \"$state_dir\" \"$removal_dir\"\n+  else\n+    removal_dir=\"$state_dir\"\n+  fi\n+  rm -rf \"$removal_dir\"\n+}\n+\n finish_rebase () {\n \trm -f \"$(git rev-parse --git-path REBASE_HEAD)\"\n \tapply_autostash &&\n \t{ git gc --auto || true; } &&\n-\trm -rf \"$state_dir\"\n+\tremove_rebase_state\n }\n \n run_interactive () {\n@@ -194,7 +205,7 @@ run_specific_rebase () {\n \telif test $ret -eq 2 # special exit status for rebase -p\n \tthen\n \t\tapply_autostash &&\n-\t\trm -rf \"$state_dir\" &&\n+\t\tremove_rebase_state &&\n \t\tdie \"Nothing to do\"\n \tfi\n \texit $ret\n@@ -439,7 +450,7 @@ abort)\n \texit\n \t;;\n quit)\n-\texec rm -rf \"$state_dir\"\n+\tremove_rebase_state\n \t;;\n edit-todo)\n \trun_specific_rebase\ndiff --git a/git-rebase--preserve-merges.sh b/git-rebase--preserve-merges.sh\nindex afbb65765d461..146b52df14928 100644\n--- a/git-rebase--preserve-merges.sh\n+++ b/git-rebase--preserve-merges.sh\n@@ -226,7 +226,7 @@ Once you are satisfied with your changes, run\n \n die_abort () {\n \tapply_autostash\n-\trm -rf \"$state_dir\"\n+\tremove_rebase_state\n \tdie \"$1\"\n }\n \n\n--\nhttps://github.com/git/git/pull/569\n"},{"id":"367707","messageId":"5c40a594-d23c-dfb5-18fe-2ac6fa9b3865@gmail.com","threadId":"50324","inReplyTo":"0102016889a9145a-83604d85-036b-409b-a3d2-2eb1ef2c5b95-000000@eu-west-1.amazonses.com","subject":"Re: [PATCH v2] rebase: move state_dir to tmp prior to deletion","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-26T13:59:10Z","receivedAt":"2019-01-26T13:59:21Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Hi Ben,\n\nLe 26/01/2019 à 11:16, Ben Woosley a écrit :\n> From: Ben Woosley <ben.woosley@gmail.com>\n> \n> To avoid partial deletion / zombie rebases.\n> \n> Example behavior under partial deletion, after\n> Ctrl-Cing out of a standard rebase:\n> \n>     $ git rebase target\n>     First, rewinding head to replay your work on top of it...\n>     Applying: [...]\n>     ^C\n>     $ git status\n>     rebase in progress; onto (null)\n>     You are currently rebasing.\n>       (all conflicts fixed: run \"git rebase --continue\")\n> \n>     Changes to be committed:\n>       (use \"git reset HEAD <file>...\" to unstage)\n>     [...]\n>     $ git rebase --continue\n>     error: could not read '.git/rebase-apply/head-name': No such file or directory\n>     $ git rebase --abort\n>     error: could not read '.git/rebase-apply/head-name': No such file or directory\n> \n> Others report this issue here:\n> https://stackoverflow.com/questions/3685001/git-how-to-fix-corrupted-interactive-rebase\n> ---\n>  git-legacy-rebase.sh           | 17 ++++++++++++++---\n>  git-rebase--preserve-merges.sh |  2 +-\n>  2 files changed, 15 insertions(+), 4 deletions(-)\n> \n\nThis patch does not cover the new builtin rebase, even though it’s also\naffected by this bug.  I ran in two bugs trying to reproduce this issue\nwith the builtin:\n\n 1. Right after invoking `git rebase $target', $target was checked out\nbut the state directory has not been written yet, leaving the user on\nthe target without any possibility of aborting the rebase.  I think it’s\nbecause the checkout is done by builtin/rebase.c, whereas the state\ndirectory is created by git-rebase--am -- and it might not be created at\nall!\n\n 2. If I wait a little bit, I have the same bug as yours.\n\nI tried to reproduce the issue on master (16a465bc01, \"Third batch after\n2.20\") and builtin.userebase set to false, without success.  It seems\nthis issue has been fixed in the shell version, but not in the builtin\nversion -- and it seems that you are using the latter, so it won’t solve\nyour problem anyway.  Try `git -c rebase.usebuiltin=false rebase\n--abort', and the state directory will be removed without any errors.\n\nThings are a bit different with interactive rebase: it checks out the\ntarget branch, then create its state directory.  Only the first issue\ncan happen, and you have to be very unlucky to run into it.  The same\ngoes for rebase -p.\n\n> diff --git a/git-legacy-rebase.sh b/git-legacy-rebase.sh\n> index b4c7dbfa575d3..832a211c925c3 100755\n> --- a/git-legacy-rebase.sh\n> +++ b/git-legacy-rebase.sh\n> @@ -128,11 +128,22 @@ read_basic_state () {\n>  \t}\n>  }\n>  \n> +remove_rebase_state () {\n> +  removal_dir=$(mktemp -d -t \"git-rebase-state-XXXXXX\")\n> +  if test -d \"$removal_dir\"\n> +  then\n> +    mv \"$state_dir\" \"$removal_dir\"\n> +  else\n> +    removal_dir=\"$state_dir\"\n> +  fi\n> +  rm -rf \"$removal_dir\"\n> +}\n> +\n>  finish_rebase () {\n>  \trm -f \"$(git rev-parse --git-path REBASE_HEAD)\"\n>  \tapply_autostash &&\n>  \t{ git gc --auto || true; } &&\n> -\trm -rf \"$state_dir\"\n> +\tremove_rebase_state\n>  }\n>  \n>  run_interactive () {\n> @@ -194,7 +205,7 @@ run_specific_rebase () {\n>  \telif test $ret -eq 2 # special exit status for rebase -p\n>  \tthen\n>  \t\tapply_autostash &&\n> -\t\trm -rf \"$state_dir\" &&\n> +\t\tremove_rebase_state &&\n>  \t\tdie \"Nothing to do\"\n>  \tfi\n>  \texit $ret\n> @@ -439,7 +450,7 @@ abort)\n>  \texit\n>  \t;;\n>  quit)\n> -\texec rm -rf \"$state_dir\"\n> +\tremove_rebase_state\n>  \t;;\n>  edit-todo)\n>  \trun_specific_rebase\n> diff --git a/git-rebase--preserve-merges.sh b/git-rebase--preserve-merges.sh\n> index afbb65765d461..146b52df14928 100644\n> --- a/git-rebase--preserve-merges.sh\n> +++ b/git-rebase--preserve-merges.sh\n> @@ -226,7 +226,7 @@ Once you are satisfied with your changes, run\n>  \n>  die_abort () {\n>  \tapply_autostash\n> -\trm -rf \"$state_dir\"\n> +\tremove_rebase_state\n>  \tdie \"$1\"\n>  }\n>  \n> \n> --\n> https://github.com/git/git/pull/569\n>\nIt seems that you tried to implement the solution proposed in the\nstackoverflow thread you linked.  Unfortunately, if it fixed something,\nit has a problem: it won’t bring you back to the commit where you called\n`git rebase', unlike a `git rebase --abort' on a uncorrupted rebase\nstate.  This is what happens when aborting a corrupted rebase with the\nshell version of rebase.  I think it’s because git-rebase--am will only\ncreate a state directory if it has a problem (ie. a conflict or the user\nhits ^C).\n\nSo, there is definitely a problem with git-rebase--am, but this does not\naddress it.\n\n-- Alban\n\n"}]}