|
|
| [Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] |
Hi, on IRC, "cehteh" requested that I send an updated version of the script. It was submitted when a short-lived option of git-diff was used, so the first publicised version no longer works. This has seen a tremendous amount of work by Jason Chu, which made it work more like darcs when called as "bash git-hunk-commit.bash --darcs". It still relies heavily on bash arrays. Ciao, Dscho
#!/bin/bash
# Copyright (C) 2006 Johannes E. Schindelin
# Distributed under the same license as git.
# Use this command to commit just a few hunks of the current output
# of "git diff". For your security, it only works when the index matches
# HEAD.
# ensure that this is a git repository
. git-sh-setup
TMP_FILE=$GIT_DIR/tmp.$$.txt
trap "rm -f $TMP_FILE; exit" 0 1 2 3 15
# the index must match the HEAD
index_check="$(git diff --index --name-only HEAD 2>&1)"
if [ $? -ne 0 ]; then
index_check="$(git diff --cached --name-only HEAD 2>&1)"
fi
if [ -n "$index_check" ]; then
echo "The staging area (AKA index) is already dirty."
exit 1
fi
# read the names of all modified files into the array "modified"
declare -a modified
filenr=1
git ls-files --modified -z > $TMP_FILE
while read -d $'\0' file; do
modified[$filenr]="$file"
filenr=$(($filenr+1))
done < $TMP_FILE
if [ ${#modified[*]} = 0 ]; then
echo "No modified files."
exit 1
fi
declare -a hunks
declare -a darcs_hunk_state
# interactively show the hunks of a file and ask if they should be committed.
# 1st parameter is the index into the modified file list.
# Darcs mode means that all hunks are presented one after another.
# Normal mode means user can specify hunks interactively.
select_hunks () {
local index=$1
local filename=${modified[$index]}
local -a diff
local -a hunk_start
local current_hunks=${hunks[$index]}
local lineno
local hunkno
local action
local i
local active
lineno=1
hunkno=0
git diff "$filename" > $TMP_FILE
while read -r line; do
diff[$lineno]="$line"
case "$line" in
@@*)
hunk_start[$hunkno]=$lineno
hunkno=$(($hunkno+1))
;;
esac
lineno=$(($lineno+1))
done < $TMP_FILE
hunk_start[$hunkno]=$lineno
action=""
while [ "$action" != commit -a "$action" != abort ]; do
echo
echo "Current hunks: ($current_hunks) of $hunkno hunks"
echo "To show (and decide on) a hunk type in the number."
echo "To commit the current hunks, say 'commit', else 'abort'."
echo
echo -n "Your choice? "
read action
case "$action" in
c) action=commit;;
q|a) action=abort;;
commit|abort) ;;
[1-9]*)
echo
for ((i=${hunk_start[$(($action-1))]}; i<${hunk_start[$action]}; i++)); do
echo ${diff[$i]}
done | less -FS
active=$(echo $current_hunks,$action | tr , '\n' | sort | uniq -u | tr '\n' , | sed -e "s/^,//" -e "s/,$//")
if [ ${#active} -lt ${#current_hunks} ]; then
i=yes
else
i=no
fi
echo
while [ -n "$action" -a "$action" != yes -a "$action" != no -a -n "$action" ]; do
echo -n "Commit this hunk (default is $i)? "
read action
case "$action" in
y) action=yes;;
n) action=no;;
esac
done
if [ -n "$action" -a $i != "$action" ]; then
current_hunks=$active
fi
;;
*) echo "Unknown command: $action";;
esac
done
if [ "$action" = commit ]; then
hunks[$index]=$current_hunks
fi
}
darcs_select_hunks () {
local index=1
local filename=${modified[$index]}
local -a diff
local -a hunk_start
local lineno
local hunkno
local i
local current_hunkno=1
local current_hunk_state
local input=''
local done=0
# Hunk number over all files
local absolute_hunkno=1
# Was the input we just had something that would finish recording
# if we hit the end of the patches?
local quiter
lineno=1
hunkno=0
git diff "$filename" > $TMP_FILE
while read -r line; do
diff[$lineno]="$line"
case "$line" in
@@*)
hunk_start[$hunkno]=$lineno
hunkno=$(($hunkno+1))
;;
esac
lineno=$(($lineno+1))
done < $TMP_FILE
hunk_start[$hunkno]=$lineno
while [ $done -eq 0 ]; do
quiter=0
# Darcs hunk preamble
echo "hunk $filename"
# Display hunk
for ((i=${hunk_start[$(($current_hunkno-1))]}; i<${hunk_start[$current_hunkno]}; i++)); do
echo ${diff[$i]}
done
current_hunk_state=$(get_hunk_state $index $current_hunkno)
echo -n "Shall I record this change? ($absolute_hunkno/?) ["
case "$current_hunk_state" in
y) echo -n Ynwsfqadjkc ;;
n) echo -n yNwsfqadjkc ;;
w) echo -n ynWsfqadjkc ;;
esac
echo -n "], or ? for help: "
read -n 1 input
echo
case "$input" in
n|y|w) set_hunk_state $index $current_hunkno $input
current_hunkno=$((current_hunkno+1))
absolute_hunkno=$((absolute_hunkno+1))
quiter=1 ;;
j) current_hunkno=$((current_hunkno+1))
absolute_hunkno=$((absolute_hunkno+1)) ;;
k) current_hunkno=$((current_hunkno-1))
absolute_hunkno=$((absolute_hunkno-1)) ;;
q) done=2
continue ;;
' ') current_hunkno=$((current_hunkno+1))
absolute_hunkno=$((absolute_hunkno+1))
quiter=1 ;;
d) done=1
continue ;;
?|h) print_darcs_help
continue ;;
esac
if [ $((current_hunkno+1)) -gt ${#hunk_start[@]} ]; then
index=$(($index+1))
if [ $index -gt ${#modified[@]} ]; then
if [ $quiter -eq 1 ]; then
done=1
else
current_hunkno=$((current_hunkno-1))
absolute_hunkno=$((absolute_hunkno-1))
index=$(($index-1))
fi
continue
fi
filename=${modified[$index]}
unset hunk_start
lineno=1
hunkno=0
git diff "$filename" > $TMP_FILE
while read -r line; do
diff[$lineno]="$line"
case "$line" in
@@*)
hunk_start[$hunkno]=$lineno
hunkno=$(($hunkno+1))
;;
esac
lineno=$(($lineno+1))
done < $TMP_FILE
hunk_start[$hunkno]=$lineno
current_hunkno=1
elif [ $current_hunkno -lt 1 ]; then
index=$(($index-1))
if [ $index -lt 1 ]; then
index=1
current_hunkno=1
absolute_hunkno=1
continue
fi
filename=${modified[$index]}
unset hunk_start
lineno=1
hunkno=0
git diff "$filename" > $TMP_FILE
while read -r line; do
diff[$lineno]="$line"
case "$line" in
@@*)
hunk_start[$hunkno]=$lineno
hunkno=$(($hunkno+1))
;;
esac
lineno=$(($lineno+1))
done < $TMP_FILE
hunk_start[$hunkno]=$lineno
current_hunkno=$((hunkno))
fi
done
case $done in
# q -- don't do anything
2) echo "Commit cancelled."
exit 0 ;;
# d -- commit everything that was a y
1) set_darcs_hunks
return ;;
esac
}
set_darcs_hunks () {
local i
local active
for ((i=1; i<$filenr; i++)); do
active=$(echo ${darcs_hunk_state[$i]} | tr , '\n' | grep :y$ | sed -e 's/:y$//' | sort | tr '\n' , | sed -e "s/^,//" -e "s/,$//")
hunks[$i]=$active
done
}
print_darcs_help () {
cat << EOF
How to use git-hunk-commit...
y: record this patch
n: don't record it
w: wait and decide later, defaulting to no
s: don't record the rest of the changes to this file
f: record the rest of the changes to this file
d: record selected patches, skipping all the remaining patches
a: record all the remaining patches
q: cancel record
j: skip to next patch
k: back up to previous patch
c: calculate number of patches
h or ?: show this help
<Space>: accept the current default (which is capitalized)
EOF
}
# Given the index ($1) and hunkno ($2) return the current darcs choice
get_hunk_state () {
local index=$1
local hunkno=$2
local current_darcs_state=${darcs_hunk_state[$index]}
local value=$(echo $current_darcs_state | sed -e "s/.*\(^\|,\)$hunkno:\([^,]*\)\(,\|$\).*/\2/")
if [ "$value" = "$current_darcs_state" ]; then
echo w
else
echo $value
fi
}
# Given the index ($1), hunkno ($2), and new state ($3) set the entry in darcs_hunk_state
set_hunk_state () {
local index=$1
local hunkno=$2
local state=$3
local current_darcs_state=${darcs_hunk_state[$index]}
local newstates=''
newstates=$(echo $current_darcs_state | sed -e "s/\(^\|,\)$hunkno:[^,]*\(,\|$\)//")
newstates="$newstates,$hunkno:$state"
darcs_hunk_state[$index]=$(echo $newstates | sed -e "s/^,//" -e "s/,$//")
}
# Apply the hunks saved in the array hunks for the specified file.
# This means that the diff is rewritten to skip the unwanted hunks.
apply_hunks () {
local index=$1
local filename=${modified[$index]}
local -a current_hunks
local lineno
local lineno2
local linediff
local hunkno
local i
local active
i=0
echo ${hunks[$index]} | tr , '\n' > $TMP_FILE
while read hunkno; do
current_hunks[$i]=$hunkno
i=$(($i+1))
done < $TMP_FILE
linediff=0
hunkno=0
i=0
active=true
git diff "$filename" > $TMP_FILE
while read -r line
do
case "$line" in
@@*)
hunkno=$(($hunkno+1))
if [ $hunkno = "${current_hunks[$i]}" ]; then
active=true
i=$(($i+1))
if [ $linediff -ne 0 ]; then
lineno=$(echo "$line" | sed "s/^.*+\([0-9]*\)[, ].*$/\1/")
lineno2=$(($lineno+$linediff))
line="$(echo "$line" | sed "s/+$lineno/+$lineno2/")"
fi
else
active=
lineno=$(echo "$line" | sed -n "s/^.*-[0-9]*,\([0-9]*\) .*$/\1/p")
if [ -z "$lineno" ]; then
lineno=1
fi
lineno2=$(echo "$line" | sed -n "s/^.*+[0-9]*,\([0-9]*\) .*$/\1/p")
if [ -z "$lineno2" ]; then
lineno2=1
fi
linediff=$(($linediff+$lineno-$lineno2))
fi
;;
esac
if [ -n "$active" ]; then
echo "$line"
fi
done < $TMP_FILE
}
darcs_mode=
case "$1" in
--darcs) darcs_mode=true;;
esac
IFS=''
action=
i=
if [ ! -n "$darcs_mode" ]; then
while [ "$action" != commit -a "$action" != abort ]; do
echo
for ((i=1; i<$filenr; i++)); do
echo -n "$i ${modified[$i]}"
if [ -n "${hunks[$i]}" ]; then
echo " (${hunks[$i]})"
else
echo
fi
done | less -FS
echo
echo "To put one or more hunks of a file into the staging area (AKA"
echo "index), type in the number of the file."
echo "To commit, say 'commit', to abort, say 'abort'."
echo
echo -n "Your choice? "
read action
case "$action" in
c) action=commit;;
q|a) action=abort;;
commit|abort) ;;
[0-9]*) select_hunks "$action";;
*) echo "Unknown command." ;;
esac
done
else
darcs_select_hunks
action="commit"
fi
if [ "$action" = commit ]; then
for ((i=1; i<$filenr; i++)); do
if [ -n "${hunks[$i]}" ]; then
apply_hunks $i
fi
done | tee a123 | git apply --cached || exit
echo git commit
fi
[Newbies FAQ] [Kernel List] [Site Home] [Gcc Help] [IETF Annouce] [DCCP] [Netdev] [Networking] [Security] [V4L] [Bugtraq] [Rubini] [Photo] [Yosemite] [MIPS Linux] [ARM Linux] [Linux Security] [Linux RAID] [Linux SCSI] [DDR & Rambus] [Linux Resources]