Message ID | 20250220164933.GP21808@frogsfrogsfrogs (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | libxfs-apply: allow stgit users to force-apply a patch | expand |
On 2025-02-20 08:49:33, Darrick J. Wong wrote: > From: Darrick J. Wong <djwong@kernel.org> > > Currently, libxfs-apply handles merge conflicts in the auto-backported > patches in a somewhat unfriendly way -- either it applies completely > cleanly, or the user has to ^Z, find the raw diff file in /tmp, apply it > by hand, resume the process, and then tell it to skip the patch. > > This is annoying, and I've long worked around that by using my handy > stg-force-import script that imports the patch with --reject, undoes the > partially-complete diff, uses patch(1) to import as much of the diff as > possible, and then starts an editor so the caller can clean up the rest. > > When patches are fuzzy, patch(1) is /much/ less strict about applying > changes than stg-import. Since Carlos sent in his own workaround for > guilt, I figured I might as well port stg-force-import into libxfs-apply > and contribute that. > > Signed-off-by: "Darrick J. Wong" <djwong@kernel.org> > --- > per maintainer request > --- > tools/libxfs-apply | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++-- > 1 file changed, 62 insertions(+), 2 deletions(-) > > diff --git a/tools/libxfs-apply b/tools/libxfs-apply > index 097a695f942bb8..9fb31f74d5c9af 100755 > --- a/tools/libxfs-apply > +++ b/tools/libxfs-apply > @@ -297,6 +297,64 @@ fixup_header_format() > > } > > +editor() { > + if [ -n "${EDITOR}" ]; then > + ${EDITOR} "$@" > + elif [ -n "${VISUAL}" ]; then > + ${VISUAL} "$@" > + elif command -v editor &>/dev/null; then > + editor "$@" > + elif command -v nano &>/dev/null; then > + nano "$@" > + else > + echo "No editor available, aborting messily." > + exit 1 > + fi > +} > + > +stg_force_import() > +{ > + local patch_name="$1" > + local patch="$2" > + > + # Import patch to get the metadata even though the diff application > + # might fail due to stg import being very picky. If the patch applies > + # cleanly, we're done. > + stg import --reject -n "${patch_name}" "${patch}" && return 0 > + > + local tmpfile="${patch}.stgit" > + rm -f "${tmpfile}" > + > + # Erase whatever stgit managed to apply, then use patch(1)'s more > + # flexible heuristics. Capture the output for later use. > + stg diff | patch -p1 -R > + patch -p1 < "${patch}" &> "${tmpfile}" > + cat "${tmpfile}" > + > + # Attach any new files created by the patch > + grep 'create mode' "${patch}" | sed -e 's/^.* mode [0-7]* //g' | while read -r f; do > + git add "$f" > + done > + > + # Remove any existing files deleted by the patch > + grep 'delete mode' "${patch}" | sed -e 's/^.* mode [0-7]* //g' | while read -r f; do > + git rm "$f" > + done > + > + # Open an editor so the user can clean up the rejects. Use readarray > + # instead of "<<<" because the latter picks up empty output as a single > + # line and does variable expansion... stupid bash. > + readarray -t rej_files < <(grep 'saving rejects to' "${tmpfile}" | \ > + sed -e 's/^.*saving rejects to file //g') > + rm -f "${tmpfile}" > + if [ "${#rej_files[@]}" -gt 0 ]; then > + echo "Opening editor to deal with rejects. Changes commit when you close the editor." > + editor "${rej_files[@]}" > + fi > + > + stg refresh > +} > + > apply_patch() > { > local _patch=$1 > @@ -385,11 +443,13 @@ apply_patch() > stg import -n $_patch_name $_new_patch.2 > if [ $? -ne 0 ]; then > echo "stgit push failed!" > - read -r -p "Skip or Fail [s|F]? " response > - if [ -z "$response" -o "$response" != "s" ]; then > + read -r -p "Skip, force Apply, or Fail [s|a|F]? " response > + if [ -z "$response" -o "$response" = "F" -o "$response" = "f" ]; then > echo "Force push patch, fix and refresh." > echo "Restart from commit $_current_commit" > fail "Manual cleanup required!" > + elif [ "$response" = "a" -o "$response" = "A" ]; then > + stg_force_import "$_patch_name" "$_new_patch.2" > else > echo "Skipping. Manual series file cleanup needed!" > fi > Thanks! Works great, lgtm Reviewed-by: Andrey Albershteyn <aalbersh@kernel.org> Sending fix for stgit detection in reply to this patch
diff --git a/tools/libxfs-apply b/tools/libxfs-apply index 097a695f942bb8..9fb31f74d5c9af 100755 --- a/tools/libxfs-apply +++ b/tools/libxfs-apply @@ -297,6 +297,64 @@ fixup_header_format() } +editor() { + if [ -n "${EDITOR}" ]; then + ${EDITOR} "$@" + elif [ -n "${VISUAL}" ]; then + ${VISUAL} "$@" + elif command -v editor &>/dev/null; then + editor "$@" + elif command -v nano &>/dev/null; then + nano "$@" + else + echo "No editor available, aborting messily." + exit 1 + fi +} + +stg_force_import() +{ + local patch_name="$1" + local patch="$2" + + # Import patch to get the metadata even though the diff application + # might fail due to stg import being very picky. If the patch applies + # cleanly, we're done. + stg import --reject -n "${patch_name}" "${patch}" && return 0 + + local tmpfile="${patch}.stgit" + rm -f "${tmpfile}" + + # Erase whatever stgit managed to apply, then use patch(1)'s more + # flexible heuristics. Capture the output for later use. + stg diff | patch -p1 -R + patch -p1 < "${patch}" &> "${tmpfile}" + cat "${tmpfile}" + + # Attach any new files created by the patch + grep 'create mode' "${patch}" | sed -e 's/^.* mode [0-7]* //g' | while read -r f; do + git add "$f" + done + + # Remove any existing files deleted by the patch + grep 'delete mode' "${patch}" | sed -e 's/^.* mode [0-7]* //g' | while read -r f; do + git rm "$f" + done + + # Open an editor so the user can clean up the rejects. Use readarray + # instead of "<<<" because the latter picks up empty output as a single + # line and does variable expansion... stupid bash. + readarray -t rej_files < <(grep 'saving rejects to' "${tmpfile}" | \ + sed -e 's/^.*saving rejects to file //g') + rm -f "${tmpfile}" + if [ "${#rej_files[@]}" -gt 0 ]; then + echo "Opening editor to deal with rejects. Changes commit when you close the editor." + editor "${rej_files[@]}" + fi + + stg refresh +} + apply_patch() { local _patch=$1 @@ -385,11 +443,13 @@ apply_patch() stg import -n $_patch_name $_new_patch.2 if [ $? -ne 0 ]; then echo "stgit push failed!" - read -r -p "Skip or Fail [s|F]? " response - if [ -z "$response" -o "$response" != "s" ]; then + read -r -p "Skip, force Apply, or Fail [s|a|F]? " response + if [ -z "$response" -o "$response" = "F" -o "$response" = "f" ]; then echo "Force push patch, fix and refresh." echo "Restart from commit $_current_commit" fail "Manual cleanup required!" + elif [ "$response" = "a" -o "$response" = "A" ]; then + stg_force_import "$_patch_name" "$_new_patch.2" else echo "Skipping. Manual series file cleanup needed!" fi