#!/bin/sh
# ------------------------------------------------------------------------------
#
# git-qrename.sh: Implement the Git-MQ "git qrename" extension command.
#
# Rename the current patch, or any specifically named patch, which has been
# registered within the Git-MQ patch series; with one argument, the current
# patch is renamed accordingly; with two arguments, the first named becomes
# renamed as the second.
#
# $Id$
#
# ------------------------------------------------------------------------------
#
mq_facility="git qrename"
#
# I'd have liked to call this a "SYNOPSIS", (which is what it is), but git's
# git-sh-setup script requires the much less appropriate name "OPTIONS_SPEC",
# (which describes only a small subset of its actual content).
#
OPTIONS_SPEC="\
git qrename [<old-name>] <new-name>

Rename the current patch, or a specific patch named by <old-name>,
to <new-name>.
--"
#
# ------------------------------------------------------------------------------
#
# $Id$
#
# Written by Keith Marshall <keith@users.osdn.me>
# Copyright (C) 2018-2020, 2022, Keith Marshall
#
#
# This file is part of the Git-MQ program suite.
#
# The Git-MQ program suite is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public Licence
# as published by the Free Software Foundation, either version 3 of
# the Licence, or (at your option) any later version.
#
# The Git-MQ program suite is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public Licence for more details.
#
# You should have received a copy of the GNU General Public Licence
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
# ------------------------------------------------------------------------------
#
# Although they may not always have any effect, all Git-MQ commands, like
# their hg counterparts, are expected to accept the global verbosity, and
# colour control options; ensure that they are declared:
#
OPTIONS_SPEC="$OPTIONS_SPEC
colour?*     generic output colour control -- may have no effect"
${OPTION_VERBOSE_DEFINED-false} || OPTIONS_SPEC="$OPTIONS_SPEC
v,verbose!*  generic verbosity selector -- may have no effect"

# For Git-MQ options, such as "--colour", we prefer a spelling convention
# which conforms to "World English" standards; however, git itself adopts
# "US English" convention.  Thus, we must also accommodate users who will
# specify "--color" instead of "--colour", without creating any ambiguity
# in the possible abbreviations; to achieve this, we check for "--color",
# among the command-line arguments, replacing it with "--colour", BEFORE
# we allow git to parse them.
#
for mq_argv
do case "$mq_argv" in
     --color*) mq_argv=`echo $mq_argv | sed 's/^--colo/&u/'` ;;
     --no-color) mq_argv="--no-colour" ;;
   esac
   ${mq_argv_init-true} && { set -- "$mq_argv"; mq_argv_init=false
   } || set -- "$@" "$mq_argv"
done

# Now, we may let git parse the command line, and set up its shell script
# processing environment, for use within a git working directory tree.
#
SUBDIRECTORY_OK=true . "`git --exec-path`/git-sh-setup" && require_work_tree

libexecdir=`dirname "$0"`
test `basename "$libexecdir"` = bin && libexecdir=`dirname "$libexecdir"`
libexecdir="$libexecdir/libexec/git-mq/${VERSION=1.0}"

mq_require(){ test -f "$1" || die "fatal: '$1' not found."; . "$1"; }
mq_require "$libexecdir/git-mq-setup.sh"

# There are no options specifically defined for this command, but
# we still need to handle (and ignore) the standard Git-MQ options.
#
while git_mq_getopt "$@"
do shift
done

# At least one patch name argument is required.  Thus, after option
# processing, at least one argument must remain; OTOH, more than two
# arguments makes no sense.
#
test $# -gt 0 || mq_abort 2 "error: must specify at least one patch name"
test $# -le 2 || mq_abort 2 "error: must specify at most two patch names"

# When a patch is renamed, not only must we rename the patch file
# itself, but we must also update all references to it, in both the
# series file, and maybe also in the status file; thus, we require
# temporary files, in which to capture the updated references.
#
mq_require mq-tmpfile
mq_tmpfile mq_series_file_tmp '`mktemp --tmpdir="$mq_patchdir"` ||
  $fatal "cannot establish series file update cache"'
mq_tmpfile mq_status_file_tmp '`mktemp --tmpdir="$mq_patchdir"` ||
  $fatal "cannot establish status file update cache"'

# To verify that each argument specifies an existing, registered, and
# maybe applied patch, we need to parse both series and status files.
#
mq_map_control_file_refs "$mq_patchdir" series status
printf "%s\n" "$@" | awk >&2 "$mq_series $mq_status "'

# For convenience, define an abort handler function, to bothe emit
# a diagnostic message, and set the exit code.
#
  function abort( severity, message )
  { print facility ":", (severity > 1) ? "fatal:" : "error:", message;
    exit severity;
  }

# As we parse the status file, in addition to noting which patches
# have been applied, we need to cross-reference their patch names to
# their commit identification hashes.
#
  FILENAME ~ /status$/ { ref[$2] = $1; }

# After parsing the series and status file records, we verify each
# specified patch name, reading each in turn from the stdin pipe.
#
  FILENAME == "-" {
    if( newname ) oldname = newname;
    newname = $0;
  }
# Having parsed the series and status files, and identified the
# arguments for the rename, we proceed with the rename action.
#
  END {
    if( ! oldname )
    { if( applied < 0 )
	abort( 1, "cannot rename unnamed unapplied patch" );
      $0 = series[applied];
      oldname = $1;
    }
    if( ! (oldname in entry) )
      abort( 1, "there is no \47" oldname "\47 in the series" );
    if( newname in entry )
      abort( 1, "\47" newname "\47 is already in the series" );
    if( system( "test -f '"$mq_patchdir"'/" newname ) == 0 )
      abort( 1, "patch file \47" newname "\47 already exists" );
    for( idx = 0; idx < entries; idx++ )
    { $0 = series[idx];
      if( (key = $1) == oldname ) $1 = newname;
      if( state[key] == "A" ) print ref[key]":"$1 > "'"$mq_status_file_tmp"'";
      print > "'"$mq_series_file_tmp"'";
    }
    if( system( "cd \42'"$mq_patchdir"'\42 && mv " oldname " " newname ) != 0 )
      abort( 2, "failed to rename patch file \47" oldname "\47" );
  }' facility="$mq_facility" "$mq_series_file" "$mq_status_file" - && {

# Finally, on successfully renaming the patch file, and recording
# the associated reference changes, me must save the reference data
# updates to the appropriate control files.
#
  mq_update mq_series_file
  mq_update mq_status_file
}
# ------------------------------------------------------------------------------
# $RCSfile$: end of file
