{"thread":{"id":"52304","subject":"A helper for interactive rebases with conflicts","startedAt":"2019-11-21T08:54:18Z","lastAt":"2019-11-21T08:54:18Z","messageCount":1,"participants":["Johannes Schindelin"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"386708","messageId":"nycvar.QRO.7.76.6.1911210941570.31080@tvgsbejvaqbjf.bet","threadId":"52304","inReplyTo":null,"subject":"A helper for interactive rebases with conflicts","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-11-21T08:54:02Z","receivedAt":"2019-11-21T08:54:18Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Team,\n\nI frequently find my interactive rebases being interrupted with conflicts\ndue to already-upstreamed patches. Typically, these patches should be left\nout from the todo list (because it uses the `right_only` flag of the\nrevision walker), but that logic can fail when passing an `--onto`\nparameter: the upstreamed patch might not be part of the symmetric range\nused to generate the todo list.\n\nFor these cases, I wrote this little script, which looks at the oneline of\nthe commit which could not be cherry-picked, tries to find it in the\ncommits reachable from HEAD (but not from the cherry-picked commit), and\nonce found, runs a range-diff:\n\n-- snip --\n#!/bin/sh\n\nstring2regex () {\n\techo \"$1\" | sed 's/[]\\\\\\/[*?]/\\\\&/g'\n}\n\ncompare_to_upstream_commit () { # [<tip-commit>] [<upstream-branch>] [<commit-count>]\n\ttip=HEAD\n\tupstream=upstream/master\n\tcount=1\n\n\ttest $# -gt 0 ||\n\ttest ! -f \"$(git rev-parse --git-path rebase-merge/stopped-sha)\" ||\n\tset -- stopped-sha\n\n\tcase \"$1\" in\n\tstopped|stopped-sha)\n\t\ttip=\"$(cat \"$(git rev-parse --git-path rebase-merge/stopped-sha)\")\" &&\n\t\tgit rev-parse -q --verify \"$tip\" ||\n\t\tdie \"Could not get stopped-sha; Not in a rebase?\\n\"\n\t\tupstream=HEAD\n\t\tshift\n\t\t;;\n\t*[^0-9a-f]*) ;; # not a tip commit\n\t?*) tip=\"$(git rev-parse --verify \"$1\"^0)\" || die \"Could not parse $1\"; shift;;\n\tesac\n\n\tcase \"$1\" in\n\t*[^0-9]*) upstream=\"$1\"; shift;;\n\tesac\n\n\tcase \"$1\" in\n\t''|*[^0-9]*) ;; # not a count\n\t*) count=$1; shift;;\n\tesac\n\n\ttest 0 = $# ||\n\tdie \"Unhandled argument(s): $*\"\n\n\toneline=\"$(git show -s --format=%s $tip)\" &&\n\tregex=\"$(string2regex \"$oneline\")\" &&\n\tupstream_commit=\"$(git rev-list --no-merges -1 --grep=\"$regex\" $tip..\"$upstream\")\" &&\n\t{ test -n \"$upstream_commit\" ||\n\t\tupstream_commit=\"$(git rev-list --no-merges -1 -i --grep=\"$regex\" $tip..\"$upstream\")\"; } &&\n\t{ test -n \"$upstream_commit\" ||\n\t\tupstream_commit=\"$(git range-diff -s $tip^..$tip $tip..\"$upstream\" |\n\t\t\tsed -n 's/^[^:]*:[^:]*=[^:]*: *\\([^ ]*\\).*/\\1/p')\"; } &&\n\t{ test -n \"$upstream_commit\" ||\n\t\tupstream_commit=\"$(git range-diff --creation-factor=95 -s $tip^..$tip $tip..\"$upstream\" |\n\t\t\tsed -n 's/^[^:]*:[^:]*=[^:]*: *\\([^ ]*\\).*/\\1/p')\"; } &&\n\ttest -n \"$upstream_commit\" ||\n\tdie \"Could not find upstream commit for '$oneline'\"\n\n\tgit range-diff --creation-factor=95 \"$tip~$count..$tip\" \"$upstream_commit~$count..$upstream_commit\"\n}\n\ncompare_to_upstream_commit \"$@\"\n-- snap --\n\nAs you can see, this script does more than just handle interrupted\nrebases: it also allows you to specify a tip commit, an upstream branch\nand a commit count to conveniently find the tip commit in the upstream\nbranch and then do a range-diff of <commit-count> patches.\n\nObviously, the described logic fails badly when the oneline has been\nchanged, so the script falls back to running a full range-diff and seeing\nwhether it can identify the upstream commit that way. If that fails, too,\nit runs the range-diff again with a looser net.\n\nMaybe this script will prove useful to somebody else than me, too?\n\nCiao,\nJohannes\n"}]}