Mercurial Queues Emulation for Git
修訂. | 1a53f0d998b3fb0ed2b9ba877996ad57a22b9783 |
---|---|
大小 | 8,157 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-rev-import.sh
# ------------------------------------------------------------------------------
#
# Script to handle the "git qimport" operation, when moving existing commit
# objects under Git-MQ patch queue control; module to be invoked exclusively
# within the scope of an "mq_require" function call.
#
# ------------------------------------------------------------------------------
#
# $Id$
#
# Written by Keith Marshall <keith@users.osdn.me>
# Copyright (C) 2019, 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/>.
#
# ------------------------------------------------------------------------------
#
# Before proceeding, initialize temporary files in which to capture our
# rewrites of the patch queue series and status files.
#
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"'
# Since this is always invoked within a function call scope, we may safely
# localize variables, which are used exclusively within this scope.
#
{ local mq_rev_spec mq_ref mq_brk
# When invoked, via mq_require, this module is passed an over-quoted list of
# commit object and/or rev-list specifications for the prior commit objects,
# which are to be brought under Git-MQ control; reduce it to a normal vector
# of arguments, for later iterative processing.
#
eval set -- $@
# Define a variation on "mq_abort", so that we can forward abort requests
# through the awk pipeline, forcing it to terminate abnormally.
#
mq_reject() { mq_complain "$@"; echo "abort"; }
# Initially, in what is implicitly designated as "phase 0", we compile a top
# down list of all commit object hashes which represent existing patches.
#
git rev-parse --quiet --verify qbase && {
mq_brk="qbase~1" mq_ref=`git rev-parse --quiet --verify $mq_brk` ||
mq_ref=`git hash-object -t tree /dev/null`
git rev-list $mq_ref..qtip
}
# Progressing to "phase 1", we now compile a list of existing commit objects
# which match any "--rev" argument specified revision, or revision range.
#
echo phase 1; for mq_rev_spec
do mq_ref=`git rev-parse --quiet --verify $mq_rev_spec` &&
echo $mq_ref $mq_rev_spec ||
{ mq_ref=`eval git rev-list $mq_rev_spec 2> /dev/null` &&
for mq_ref in `git rev-list $mq_ref`
do echo $mq_ref $mq_rev_spec; done ||
mq_reject "fatal: '$mq_rev_spec' does not match any revision"
}
done
# Additionally, in "phase 2", we compile a bottom up list of those commit
# objects which are viable for conversion to patches; this list comprises
# the set of commit objects which is reachable from the "qparent" commit,
# without traversing any merge, and excluding any commit which is already
# published in a remote repository.
#
echo phase 2
mq_brk=`git rev-parse --quiet --verify ${mq_brk-"HEAD"}` &&
{ mq_exclude_merges() {
for mq_ref in `git rev-list --merges --all`; do echo "^$mq_ref"; done
}
git rev-list --reverse $mq_brk `mq_exclude_merges` --not --remotes
}
# Having generated this sequence of three lists, we filter and analyse
# them, verifying their consistency, and reducing them to the updated
# content for insertion into the Git-MQ series and status files.
#
} | awk >&2 'BEGIN { phase = avail = req = 0; }
$1 == "abort" { exit 2 } phase == 0 { patch[$1] = 1; }
$1 == "phase" { if( (phase = $2) == 2 ) delete patch; next; }
function trim(text){ sub(/^ /,"",text); return gensub(/ $/,"",1,text); }
function complain(msg){ print "git qimport: error:", msg; }
function errout(msg){ complain( msg ); exit 2; }
phase == 1 { if( patch[$1] )
{ ref = substr( $1, 1, 7 ); $1 = ""; spec = trim($0);
complain( "\47" spec "\47 refers to commit \47" ref "\47" );
errout( "this commit represents an existing Git-MQ patch" );
}
sched[req++] = $1;
}
phase == 2 { accept[seq[avail++] = $1] = 1; }
END { idx0 = -1;
for( idx = 0; idx < req; idx++ ) {
if( accept[sched[idx]] ) import[sched[idx]] = 1;
else errout( "cannot import \47" sched[idx] "\47" );
}
for( idx = 0; idx < avail; idx++ ) {
if( import[seq[idx]] ) { if( idx0 < 0 ) idx0 = idx; }
else if( idx0 >= 0 ) {
seq[idx] = substr( seq[idx], 1, 7 );
complain( "omitting \47" seq[idx] "\47 creates discontinuity" );
errout( "cannot import commits preceding \47" seq[idx] "\47" );
}
}
patchname = "'"$mq_patchname"'";
for( idx = idx0; idx < avail; idx++ ) {
seq_patchname = patchname ? patchname : substr( seq[idx], 1, 7 )".diff";
print seq[idx]":"seq_patchname > "'"$mq_status_file_tmp"'";
print seq_patchname > "'"$mq_series_file_tmp"'";
}
}
' || exit $?
# We've now captured the list of existing commits, which are to be
# converted to patches, in the series and status file update caches;
# before actually converting them, recheck that the "git qimport"
# argument limit, with specified patch name, is not exceeded.
#
mq_check_arg_limit `sed 2q $mq_series_file_tmp`
# Before proceeding further, check that no proposed name for any new
# patch conflicts with any already registered patch, or, unless the
# "--force" option is in effect, with any file which already exists
# in the Git-MQ patch directory.
#
for mq_destname in ${mq_patchname-`cat $mq_series_file_tmp`}
do test -f "$mq_series_file" && {
awk '$1 == "'"$mq_destname"'" {exit 1}' "$mq_series_file" || {
mq_abort 2 "error: '$mq_destname' is already in the patch series"
}
}
${mq_allow_overwrite-false} || {
test -f "$mq_patchdir/$mq_destname" && {
mq_abort 2 "error: patch '$mq_destname' already exists"
}
}
done
# If we're still here, then reprocess the sequence of status file
# additions, update the patch series tags (except for "qparent"),
# and write the associated patch files.
#
awk -F: '{ system( "git tag "(patchname = $2)" "(patchid = $1) );
if( NR == 1 ) { system( "git tag --force qbase "$1" > /dev/null" );
"sh -c \47printf %040d 0\47" | getline;
"git rev-parse --quiet --verify qbase~1" | getline;
parentid = $0;
}
fmt = "--pretty=format:\47# Git-MQ patch%n# Parent "parentid"%+B\47";
system( "git show "fmt" "patchid" > '"$mq_patchdir"'/"patchname );
parentid = patchid;
}' $mq_status_file_tmp
# Since the preceding step did not update the "qparent" tag, we do
# this now, or delete it if all available commits are now represented
# by Git-MQ patches.
#
{ git tag --force qparent qbase~1 2>&1 || git tag --delete qparent
} > /dev/null
# Note that, if we are importing existing commits into an originally
# empty patch series, then nothing above guarantees that a "qtip" tag
# is created; if a "qbase" tag exists, ensure that "qtip" does too.
#
{ git rev-parse --quiet --verify qbase && git tag qtip 2>&1
} > /dev/null
# Finally, we fold the captured update records into the heads of the
# Git-MQ series and status files, creating these if necessary.
#
mq_series_file="$mq_patchdir/series" mq_status_file="$mq_patchdir/status"
test -f "$mq_series_file" && cat "$mq_series_file" >> "$mq_series_file_tmp"
test -f "$mq_status_file" && cat "$mq_status_file" >> "$mq_status_file_tmp"
mq_update mq_series_file; mq_update mq_status_file
#
# ------------------------------------------------------------------------------
# $RCSfile$: end of file