Last active
August 29, 2015 13:56
-
-
Save dekarrin/8887229 to your computer and use it in GitHub Desktop.
Creates incremental patches containing only the desired changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
cd ~ | |
apply () | |
{ | |
cd "$1" | |
files=$(echo *) | |
for f in $files | |
do | |
append= | |
if [ "$2" ] | |
then | |
append="$2/" | |
fi | |
if [ -f "$f" -a "$(echo $(basename $f) | rev | cut -d '.' -f1 | rev)" = 'patch' ] | |
then | |
echo "$append$1/$f" | |
elif [ -d "$f" ] | |
then | |
apply "$f" "$append$1" | |
fi | |
done | |
cd .. | |
} | |
while read file | |
do | |
patch -d silvermine -p 1 < $file | |
done <<< "$(apply patches)" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Below are potential script params; specific to project | |
DIR_WORKING=~ | |
DIR_OLDFILE_ROOT=silvermine | |
DIR_NEWFILE_ROOT=ready | |
PATCH_PATTERN_SOURCE=features.txt | |
PATCH_START_MARK="DEKKY MOD START" | |
PATCH_END_MARK="DEKKY MOD END" | |
# Below are constants, NOT params | |
DIR_TEMP=tmpchanges # Relative to working dir | |
DIR_PATCHES=patches # Output patches to this dir | |
# Below this, constants should be fixed | |
SUFFIX_PATCH=.patch | |
SUFFIX_PROGRESS_OLD=.old | |
SUFFIX_PROGRESS_NEW=.new | |
SUFFIX_PROGRESS_PATCH=${SUFFIX_PATCH}.gen | |
SUFFIX_PROGRESS_REMOVAL=${SUFFIX_PROGRESS_PATCH}.rem | |
SUFFIX_PROGRESS_FINAL=${SUFFIX_PROGRESS_NEW}.patched | |
SUFFIX_PATCH_ERR=.others | |
# get hunk header info | |
# | |
# $1 - hunk header | |
# $2 - name of array to put info into | |
get_hunk_info () | |
{ | |
local get_hunk_info_var=$(echo $1 | sed 's/ +/:/' | sed s/[^0-9,:]//g) | |
echo $get_hunk_info_var | cut -d : -f1 | cut -d , -f1 | |
echo $get_hunk_info_var | cut -d : -f1 | cut -d , -f2 | |
echo $get_hunk_info_var | cut -d : -f2 | cut -d , -f1 | |
echo $get_hunk_info_var | cut -d : -f2 | cut -d , -f2 | |
} | |
# checks if the first hunk in a given file is wanted in the final output | |
# | |
# $1 - file to check | |
# $2 - pattern for mod starts that mark desired hunks | |
# $3 - filename to write unwanted hunk section headers to. can be omitted for none | |
# $4 - What to put before each line writen to $3. Only applicable if $3 is used. | |
# | |
# Returns | |
# '1' - hunk is wanted in final output | |
# '0' - hunk is not wanted in final output | |
# '-1' - hunk is wanted, but is mixed with additional hunks. | |
hunk_wanted () | |
{ | |
exit_status=0 | |
echo_status=0 | |
bad_lines= | |
while read -r line | |
do | |
if [ "$(echo $line | grep -i "$PATCH_START_MARK")" ] | |
then | |
if [ "$(echo $line | grep "$2")" ] | |
then | |
echo_status=1 | |
else | |
exit_status=1 | |
bad_lines="$bad_lines | |
$4$line" | |
fi | |
fi | |
done < "$1" | |
echo $echo_status | |
if [ $echo_status = 1 -a $exit_status = 1 -a -n "$3" ] | |
then | |
echo "$bad_lines" >> "$3" | |
fi | |
return $exit_status | |
} | |
lpad () | |
{ | |
orig=$1 | |
len=$2 | |
printf "%"$len"s" $orig | |
} | |
# $1 - status progress message | |
# $2 - rest of the message | |
pad_out () | |
{ | |
desired=80 | |
have=$(expr ${#2} + 1) | |
needed=$(expr $desired - $have) | |
printf "%-"$needed"s%s" "$1" " $2" | |
} | |
# Outputs without a newline and overwrites anything that was already on the last line of | |
# the terminal | |
overwrite () | |
{ | |
output="$1" | |
if [ -z "$output" ] | |
then | |
read output | |
fi | |
printf "\r" | |
if [ -n "$LAST_OVERWRITE_LENGTH" ] | |
then | |
if [ "$LAST_OVERWRITE_LENGTH" -gt 0 ] | |
then | |
clear_space_count=0 | |
while [ $clear_space_count -lt $LAST_OVERWRITE_LENGTH ] | |
do | |
printf " " | |
let clear_space_count++ | |
done | |
printf "\r" | |
fi | |
fi | |
# only save length of last line | |
while read -r line | |
do | |
LAST_OVERWRITE_LENGTH=${#line} | |
printf "$line" | |
done <<< "$output" | |
} | |
# Outputs with a newline and overwrites anything that was already on the last line of | |
# the terminal | |
overwriteln () | |
{ | |
overwrite "$@" | |
printf "\n" | |
LAST_OVERWRITE_LENGTH=0 | |
} | |
cd "$DIR_WORKING" | |
rm -rf "$DIR_TEMP" "$DIR_PATCHES" | |
#prepare files by patching out all changes but what we want | |
file_pattern=$(head -n 1 "$PATCH_PATTERN_SOURCE" | sed 's/, /\\|/g') | |
all_files=$(grep -lR "$file_pattern" "$DIR_NEWFILE_ROOT"/*) | |
total_files=$(echo "$all_files" | wc -l) | |
current_file=0 | |
for file in $all_files | |
do | |
current_file=$(expr $current_file + 1) | |
base=$(echo "$file" | sed s@^"$DIR_NEWFILE_ROOT"\/@@) | |
status_dir_progress="[$current_file/$total_files] '$base':" | |
overwrite <<< "$(pad_out "$status_dir_progress" "scanning...")" | |
if [ ! -f "$DIR_OLDFILE_ROOT/$base" ] | |
then | |
overwriteln <<< "$(pad_out "$status_dir_progress" "New file; add manually")" | |
continue | |
fi | |
mkdir -p "$DIR_TEMP/$(dirname $base)" | |
temp_base=$DIR_TEMP/$base | |
temp_old=$temp_base$SUFFIX_PROGRESS_OLD | |
temp_new=$temp_base$SUFFIX_PROGRESS_NEW | |
temp_patch=$temp_base$SUFFIX_PROGRESS_PATCH | |
temp_removal=$temp_base$SUFFIX_PROGRESS_REMOVAL | |
temp_final=$temp_base$SUFFIX_PROGRESS_FINAL | |
patches_err=$DIR_PATCHES/$base$SUFFIX_PATCH_ERR | |
temp_err=$temp_base$SUFFIX_PATCH_ERR | |
cp "$file" "$temp_base" | |
dos2unix -q "$temp_base" | |
total_hunks=$(diff -u "$temp_base" "$DIR_OLDFILE_ROOT/$base" | grep '^@@ -[0-9]\+,[0-9]\+ +[0-9]\+,[0-9]\+ @@$' | wc -l) | |
current_hunk=0 | |
kept_hunks=0 | |
new_file_start=1 | |
old_file_start=1 | |
rm -f "$temp_final" | |
while true | |
do | |
let current_hunk++ | |
tail -n +$old_file_start "$DIR_OLDFILE_ROOT/$base" > "$temp_old" | |
tail -n +$new_file_start "$temp_base" > "$temp_new" | |
diff -u "$temp_new" "$temp_old" > "$temp_patch" | |
if [ "$(cat "$temp_patch" | wc -l)" = 0 ] | |
then | |
if [ $total_hunks = 0 ] | |
then | |
overwriteln <<< "$(pad_out "$status_dir_progress" "none found; shouldn't happen...")" | |
else | |
overwriteln <<< "$(pad_out "$status_dir_progress" "complete; kept $(lpad $kept_hunks 3) of $(lpad $total_hunks 3) hunks")" | |
fi | |
break | |
else | |
overwrite <<< "$(pad_out "$status_dir_progress" "analyzing diff hunk $current_hunk of $total_hunks...")" | |
fi | |
second_hunk_start=$(grep -n '^@@' < "$temp_patch" | cut -f1 -d: | tail -n +2 | head -n 1) | |
if [ "$second_hunk_start" ] | |
then | |
head -n $(expr $second_hunk_start - 1) "$temp_patch" > "$temp_removal" | |
else | |
cp "$temp_patch" "$temp_removal" | |
fi | |
hunk_header=$(tail -n +3 "$temp_removal" | head -n 1) | |
read orig_start orig_num mod_start mod_num <<<$(get_hunk_info "$hunk_header") | |
orig_hunk_length=$(expr $orig_start + $orig_num - 1) | |
mod_hunk_length=$(expr $mod_start + $mod_num - 1) | |
new_file_start=$(expr $new_file_start + $orig_hunk_length) | |
old_file_start=$(expr $old_file_start + $mod_hunk_length) | |
desired_lines=0 | |
wanted_status=$(hunk_wanted "$temp_removal" "$file_pattern" "$temp_err" "$kept_hunks: ") | |
wanted_exit=$? | |
if [ $wanted_status = 1 ] | |
then | |
kept_hunks=$(expr $kept_hunks + 1) | |
if [ $wanted_exit = 1 ] | |
then | |
overwriteln "WARNING: Multiple mods present in hunk #$kept_hunks in '$base'" | |
fi | |
desired_lines=$orig_hunk_length | |
else | |
patch -s -p 1 -d "$DIR_TEMP" < "$temp_removal" | |
desired_lines=$mod_hunk_length | |
fi | |
head -n $desired_lines "$temp_new" | grep -iv "$PATCH_START_MARK\\|$PATCH_END_MARK" >> "$temp_final" | |
done | |
cat "$temp_new" >> "$temp_final" | |
mv "$temp_final" "$temp_base" | |
if [ "$(diff -q "$DIR_OLDFILE_ROOT/${base}" "$temp_base")" ] | |
then | |
mkdir -p "$DIR_PATCHES/$(dirname $base)" | |
if [ -f "$temp_err" ] | |
then | |
mv "$temp_err" "$patches_err" | |
fi | |
diff -u "$DIR_OLDFILE_ROOT/${base}" "$temp_base" > "$DIR_PATCHES/${base}.patch" | |
fi | |
done | |
rm -rf $DIR_TEMP | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment