Mercurial Queues Emulation for Git
修訂. | 1a53f0d998b3fb0ed2b9ba877996ad57a22b9783 |
---|---|
大小 | 11,215 bytes |
時間 | 2022-03-31 22:05:16 |
作者 | Keith Marshall |
Log Message | Promote git-mq-1.0-rc-3 to final release status,
* configure.ac (AC_INIT): Set version number to 1.0
|
# git-qnew.shar
# ------------------------------------------------------------------------------
# Shell archive to deliver the implementation for the "git qnew" command.
# ------------------------------------------------------------------------------
#
# $Id$
#
# Written by Keith Marshall <keith@users.osdn.me>
# Copyright (C) 2018, 2019, 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/>.
#
# ------------------------------------------------------------------------------
#
cat <<\ETX
#!/bin/sh
# ------------------------------------------------------------------------------
#
# git-qnew.sh: Implement the Git-MQ "git qnew" extension command.
#
# Create a new patch, and push it on to the patch series stack. Essentially,
# this emulates Mercurial's "hg qnew" command; however, it provides additional
# capabilities to accommodate complexity arising from Git's index.
#
# $Id$
#
# ------------------------------------------------------------------------------
#
mq_facility="git qnew"
#
# 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 qnew [-e] [[[-m <text>] ...] | [-F <file>]] <patch> [<file> ...]
Create a new patch, and push it on to the top of the patch series stack.
--
e,edit! edit commit message
F,file!= read commit message from nominated file
l,logfile!=* hg compatible alias for --file option
m,message!= specify text for commit message
D,currentdate! record current date within patch, as commit date
d,date!= record specified date as patch commit date
U,currentuser! record current user as patch author
u,user!= record specified user as patch author
a,all! include all modified tracked files"
ETX
# Incorporate the universal set-up code, which is common to all of the
# Git-MQ command implementations.
#
. ./git-mq-setup.shar
cat <<\ETX
# Regardless of any options specified by the user, we must be prepared
# to commit an empty patch.
#
mq_commit_opts="--allow-empty"
# The commit message definition options, "--message", "--logfile", or
# "--file", require a special validating interpreter, to ensure that no
# invalid combination is specified; this collects any valid sequence of
# such options into the "mq_msgopt" shell variable.
#
mq_require mq-message-opts
# Process any options which the user may have specified on the command
# line; shift them out of the way, leaving only non-option arguments.
#
while git_mq_getopt "$@"
do case $1 in
-e) mq_collect mq_commit_opts "--edit" ;;
-a) mq_commit_all=true; mq_collect mq_commit_opts --all ;;
-F) mq_collect_valid_msgopt --file "$2"; shift ;;
-l) mq_collect_valid_msgopt --logfile "$2"; shift ;;
-m) mq_collect_valid_msgopt --message "$2"; shift ;;
-d) mq_commit_date="--date='$2'"; shift ;;
-D) mq_commit_date="--date='`date '+%s %z'`'" ;;
-u) mq_commit_user="--author='$2'"; shift ;;
-U) mq_commit_user="" ;;
esac; shift
done
# After processing any command line options, there must be at least
# one argument remaining, representing the patch name...
#
test $# -gt 0 || $fatal "no patch name specified
`usage | sed 1q`"
# ...and no patch, with that name, may yet exist in the currently
# active patch queue directory.
#
mq_patchname="$1" mq_patchfile="$mq_patchdir/$1"
test -f "$mq_patchfile" && $fatal "patch '$1' already exists."
# Furthermore, no entry for the named patch may exist within the
# series file; (any such entry would represent an inconsistency in
# the patch series meta-data).
#
test -f "$mq_patchdir/series" && {
awk '$1 == "'"$1"'" {exit 1}' "$mq_patchdir/series" ||
$fatal "entry for patch '$1' exists in series file."
}
# When any patches have been applied already...
#
if git qtop > /dev/null 2>&1
then
# ...we should already have a 'qbase' tag, representing the commit for
# the first applied patch, and similarly, a 'qtip' tag representing the
# current topmost patch, on top of which we will now push this new one.
#
git_mq_require_tag() {
eval mq_$1'_hash=`git rev-parse --quiet --verify '$1'`' ||
eval "$fatal \"cannot resolve '$1' reference for '`eval $2`'\""
}
git_mq_require_tag qbase 'git qapplied | sed 1q'
git_mq_require_tag qtip 'git qtop'
else
# Conversely, when no patches have been applied, neither the preceding
# tags, nor any 'qparent' tag, should have been defined...
#
for mq_tag in qparent qbase qtip
do git rev-parse --quiet --verify $mq_tag > /dev/null &&
$fatal "tag '$mq_tag' exists, but no patches applied."
done
# ...but any existing 'HEAD' reference will become the effective 'qtip',
# on top of which the new patch will be pushed, and to which the 'qparent'
# tag will then point; (if no such 'HEAD' reference exists, then the patch
# will become the first commit on its unborn branch, and no 'qparent' tag
# will be assigned).
#
mq_qtip_hash=`git rev-parse --quiet --verify HEAD` &&
mq_qparent_hash=$mq_qtip_hash || mq_qtip_hash=`printf %040d 0`
fi
# Before actually changing anything, within the git repository, ensure
# that we can create an intermediate file, in the appropriate directory,
# in which to store the associated patch content.
#
mq_require mq-tmpfile
test -d "$mq_patchdir" || mkdir -p "$mq_patchdir" || exit 2
mq_tmpfile mq_patchfile_tmp '`mktemp --tmpdir="$mq_patchdir"` ||
$fatal "cannot create intermediate patch file"'
# Also, ensure that we can create a temporary file, in which to cache
# updates to the patch queue series file.
#
mq_tmpfile mq_series_file_tmp '`mktemp --tmpdir="$mq_patchdir"` ||
$fatal "cannot establish series file update cache"'
# If any further arguments are specified, following the patch name,
# they must be interpreted as a list of files to be included in the
# patch; drop the patch name argument itself, and any "--" separator
# which may have been included to conform to git convention, leaving
# just the list of files, which becomes the basis for patch content.
#
# Note: this is not compatible with use of the "--all" option; when
# allowed, all specified files must be accessible under "$GIT_ROOT",
# and must already be tracked by git. We may use "git ls-files" to
# detect exceptions to the former requirement, but, by default, it
# will silently ignore exceptions to the latter; thus, in addition
# to the "ls-files" check, we loop over the argument list to check
# for accessibility of all specified files. Having performed these
# checks, we then run "git ls-files" again, to silently filter out
# any files which are not tracked.
#
shift
while test "x${1}x" = "x--x"; do shift; done
test $# -gt 0 && {
${mq_commit_all-false} && {
mq_abort 2 "error: named files not allowed with '--all' option"
}
mq_status=`git ls-files "$@" 2>&1 > /dev/null | awk '{ print $1, $3 }'`
test "$mq_status" && mq_abort 2 "$mq_status is not under '$GIT_ROOT'"
for mq_arg
do test -r "$mq_arg" || $fatal "'$mq_arg' is not accessible"
done
eval set -- `git ls-files "$@" | awk '{ print "\47" $0 "\47" }'`
# After checking accessibility of specified files, under "$GIT_ROOT",
# and reducing the list to include only tracked files, there may now
# be none left. This requires special handling, since it implies a
# request to create an empty patch: to achieve this, we must back up
# and clear any already staged changes, from git's index, allowing
# us to commit the empty patch content, and subsequently restore
# the index state when we have finished.
#
test $# -gt 0 || {
git update-index -q --ignore-submodules --refresh
git diff --quiet --cached --ignore-submodules || {
mq_cache_backup=`mktemp` || $fatal "cannot create index backup cache"
git diff --cached --ignore-submodules > "$mq_cache_backup"
git reset --quiet -- `git diff --cached --name-only --ignore-submodules`
mq_require mq-atexit "mq_cache_restore '$mq_cache_backup'"
mq_cache_restore() { git apply --cached "$1"; rm "$1"; }
}
}
}
# Fold any user specified log message options into the primary
# commit options set; if no log message specified, substitute a
# suitable default.
#
test ${mq_msgopt+set} && mq_collect mq_commit_opts "$mq_msgopt" ||
mq_collect mq_commit_opts "--message '[Git-MQ]: $mq_patchname'"
# Delegate the commit, and subsequent patch generation, to the patch
# writing module, advising that any passed arguments are to be used in
# the commit command invocation.
#
mq_argv=commit mq_require git-mq-write-patch ${1+"$@"}
# Assign tags to facilitate tracking of applied patches; (note that each
# patch is individually tagged with its own name, and also the "qtip" tag,
# which will always move to the patch at the top of the series stack; the
# "qbase" tag is also assigned, if it doesn't yet exist, on the basis of
# an inference that the current patch is the first to be applied, and in
# this case, the "qparent" tag is also assigned to the preceding "HEAD",
# if any such commit exists).
#
git tag "$mq_patchname"
test "x$mq_qparent_hash" = x || git tag qparent $mq_qparent_hash
git rev-parse --quiet --verify qbase > /dev/null || git tag qbase
git tag --force qtip > /dev/null
# Update the series file, inserting an entry for the new patch; (notice
# that we had previously verified that no such entry existed beforehand).
# We capture the update in the previously created temporary cache file,
# then, regardless of where we have mapped the location of the original,
# we store the update to $mq_patchdir/series, overwriting any existing
# file with this name.
#
mq_map_control_file_refs "$mq_patchdir" series status
awk "$mq_series $mq_status"' END {
if( entries > 0 )
{ if( applied < 0 )
print "'"$mq_patchname"'" > "'"$mq_series_file_tmp"'";
for( idx = 0; entries > idx; idx++ )
{ print series[idx] > "'"$mq_series_file_tmp"'";
if( idx == applied )
print "'"$mq_patchname"'" > "'"$mq_series_file_tmp"'";
}
}
else print "'"$mq_patchname"'" > "'"$mq_series_file_tmp"'";
}' "$mq_series_file" "$mq_status_file"
mq_series_file="$mq_patchdir/series" mq_update mq_series_file
# Finally, update the status file, in place, to reflect that the new
# patch has been applied, and is at the top of the patch queue stack.
#
git show --no-patch --format="%H:$mq_patchname" >> "$mq_patchdir/status"
#
# ------------------------------------------------------------------------------
# $RCSfile$: end of file
ETX