#! /bin/sh # tla-pure-merge: apply un-merged patches on one branch to another # Copyright (C) Cameron Patrick, 2005. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. # # TODO: should allow applying patches to existing project tree set -e usage() { echo "usage: tla-pure-merge [options] SOURCE DESTINATION" echo "Replays pure changesets from the SOURCE version or revision to" echo "the DESTINATION version." echo echo "Options:" echo " -h --help Display this message" echo " -a --all Merge all patches without asking" echo "* -i --interactive Ask which patches to merge" echo " -p PATCH --patches PATCH Merge the listed patches" echo " -l --pull Use current tree version as DESTINATION" echo " -s --push Use current tree version as SOURCE" echo " --edit-summary Add source revision to summary" echo "* --no-edit-summary Use the same log summaries as the source" echo "* --skip-present Use tla missing --skip-present" echo " --no-skip-present Disable the --skip-present behaviour" echo echo "Options indicated with a * are on by default." echo echo "If --pull or --push is specified, and no revision name is given, tla-pure-merge" echo "will use use {arch}/+upstream-version as the SOURCE or DESTINATION." exit 0 } die() { echo "$@" 1>&2 echo "Try tla-pure-merge --help for command-line options." 1>&2 exit 1 } find_upstream_vers() { local tree_root="$(tla tree-root)" if [ -f "${tree_root}/{arch}/+upstream-version" ]; then upstream_vers="$(cat ${tree_root}/{arch}/+upstream-version)" else upstream_vers="" fi } clean_up() { cd .. rm -rf $temp_dir } if [ "$PAGER" = "" ]; then PAGER=less fi edit_summary=no skip_present=yes interactive=yes mode=normal patches_missing="" source_vers="" dest_vers="" # parse command line arguments while [ "$#" != "0" ]; do case "$1" in -h|--help) usage ;; -a|--all) interactive=no ;; -i|--interace) interactive=yes ;; -p|--patches) if [ "$2" = "" ]; then die "--patches specified but no patch list given" fi patches_missing="$2" interactive=no echo "* patches to merge: $2" shift ;; -l|--pull) mode=pull if ! tla tree-root >/dev/null; then die "--pull given but not in a project tree." fi dest_vers="$(tla tree-version)" find_upstream_vers echo "* using tree version ${dest_vers} as destination" ;; -s|--push) mode=push if ! tla tree-root >/dev/null; then die "--push given but not in a project tree." fi find_upstream_vers source_vers="$(tla tree-version)" echo "* using tree version ${source_vers} as source" ;; --edit-summary) edit_summary=yes ;; --no-edit-summary) edit_summary=no ;; --skip-present) skip_present=yes ;; --no-skip-present) skip_present=no ;; -*) die "Unknown option argument $1" ;; *) if [ "$source_vers" = "" ]; then source_vers="$1" elif [ "$dest_vers" = "" ]; then dest_vers="$1" else die "Don't know what to do with argument $1" fi ;; esac shift done # default to upstream versions if possible if [ "$mode" = "pull" ] && [ "$source_vers" = "" ] && [ "$upstream_vers" != "" ]; then echo "* using upstream version ${upstream_vers} as source" source_vers="$upstream_vers" fi if [ "$mode" = "push" ] && [ "$dest_vers" = "" ] && [ "$upstream_vers" != "" ]; then echo "* using upstream version ${upstream_vers} as destination" dest_vers="$upstream_vers" fi # make sure we have a source and a destination if [ "$source_vers" = "" ]; then die "No source version specified to merge from." fi if [ "$dest_vers" = "" ]; then die "No destination version specified to merge into." fi # grab a copy of the destination revision temp_dir=",,pure-merge-$$-$(tla parse-package-name --non-arch $dest_vers)" tla get --silent $dest_vers $temp_dir cd $temp_dir trap "clean_up; exit 1" INT EXIT # decide which patches to consider merging if [ "$patches_missing" != "" ]; then : elif tla parse-package-name -l $source_vers >/dev/null 2>/dev/null; then patches_missing="$(tla parse-package-name -l $source_vers)" else skip_present_flag=--skip-present [ $skip_present = no ] && skip_present_flag='' patches_missing="$(tla missing $skip_present_flag $source_vers | grep -v ^base- || true)" fi source_vers="$(tla parse-package-name -a $source_vers)/$(tla parse-package-name --package-version $source_vers)" # maybe there are no missing patches? if [ "$patches_missing" = "" ]; then echo echo "No changes to merge." exit 0 fi # fetch patches and log entries for x in $patches_missing; do patch="$source_vers--$x" echo "* fetching patch $patch" tla get-changeset $patch ,tmp-cset-$x tla cat-archive-log $patch >,tmp-log-$x done # decide which patches to merge if [ "$interactive" = "no" ]; then act="a" else act="?" fi patches_to_merge="" for x in $patches_missing; do summary="$(grep '^Summary: ' ,tmp-log-$x | sed 's/^Summary: //g')" creator="$(grep '^Creator: ' ,tmp-log-$x | sed 's/^Creator: //g')" date="$(grep '^Date: ' ,tmp-log-$x | sed 's/^Date: //g')" echo echo "$x" echo " $date $creator" echo " $summary" while true; do if [ "$act" = "a" ] || [ "$act" = "d" ]; then break fi echo -n "Merge this patch? [ynvxdaq?] " read act case $act in y|Y|n|N|d|D|a|A) break ;; v|V) tla show-changeset --diffs ,tmp-cset-$x | $PAGER ;; x|X) tla show-changeset ,tmp-cset-$x ;; q|Q) echo "Cancelling merge." exit 0 ;; *) echo "Your choices are:" echo " y - merge this patch" echo " n - don't merge this patch" echo " v - view this patch and ask again" echo " x - view a summary of this patch and ask again" echo " d - skip this patch and all after it" echo " a - merge this patch and all after it" echo " q - cancel, don't merge anything" echo ;; esac done if [ "$act" = "y" ] || [ "$act" = "a" ]; then patches_to_merge="$patches_to_merge $x" fi done echo # merge selected patches for x in $patches_to_merge; do patch="$source_vers--$x" if ! tla changes -q; then echo "PANIC: Gah, changes have been made to source tree." echo "This shouldn't happen :(" # remove trap now, we don't want to remove the temp dir trap - EXIT exit 2 fi rm -rf ,tmp-log cp ,tmp-log-$x ,tmp-log echo "* pure merge: applying $patch" log_file="$(tla make-log)" merge_of="$patch" merge_of_pure=no if grep -q '^Pure-merge-of: ' ,tmp-log; then merge_of="$(sed -n -e '/^Pure-merge-of: / { s/^Pure-merge-of: \(.*\)/\1/g; p }' ,tmp-log)" merge_of_pure=yes fi cat /dev/null >$log_file if [ "$merge_of_pure" = "yes" ]; then cat ,tmp-log | \ sed -n -e '/^Original-/ p' -e '/^Summary: / Q' >>$log_file else cat ,tmp-log | \ sed -e 's/^\(Creator\|Date\|Standard-date\): /Original-\1: /g' \ -e t -e d -e '/^Summary: / Q' \ >>$log_file fi echo "Direct-ancestor: $patch" >>$log_file echo "Pure-merge-of: $merge_of" >>$log_file if [ "$edit_summary" = "yes" ]; then cat ,tmp-log | sed -n \ -e '/^Summary: / { s/ [merge of .*]$//g; s%$% [merge of '"$merge_of"']%g; p; q }' >>$log_file else cat ,tmp-log | sed -n \ -e '/^Summary: / { p; q }' >>$log_file fi cat ,tmp-log | sed -e '1,/^Summary: / d' >>$log_file rm -f ,tmp-log if ! tla apply-changeset ,tmp-cset-$x .; then echo "* conflicts during pure merge of $patch" echo "Please fix conflicts by hand and run \`tla commit\`" echo "in directory $temp_dir" # remove trap now, we don't want to remove the temp dir trap - EXIT exit 1 fi tla commit done exit 0 # arch-tag: a36bd7cf-88b4-4ef9-9d6f-944d18ac6600