plan9fox/sys/src/cmd/git/branch
Ori Bernstein 03e5d9e9e2 git/merge: preserve exec bit correctly
A while ago, qwx noticed that we clobbered the exec
bit when merging files. This is not what we want, so
we changed the operator precedence to avoid merging
dirty files implicitly.

But we do want to merge, because it's convenient for
maintaining permissions. So, instead, we should do a
3 way merge of the exec bit.

This patch does that, as well as reverting the rollback
of that change.

While we're here, we adjust the timestamps correctly
in git/branch.

This requires changes to git/fs, because without an open
handler, lib9p allows opening any file with any mode,
which confuses 'test -x'.
2022-04-16 23:53:19 +00:00

123 lines
2.5 KiB
Bash
Executable file

#!/bin/rc -e
rfork en
. /sys/lib/git/common.rc
gitup
flagfmt='a:listall, b:baseref ref, d:delete, n:newbr, s:stay, m:merge'
args='[branch]'
eval `''{aux/getflags $*} || exec aux/usage
modified=()
deleted=()
if(~ $#* 0){
if(~ $#listall 0)
awk '$1=="branch"{print $2}' < $gitfs/ctl
if not
cd .git/refs/ && walk -f heads remotes
exit
}
if(! ~ $#* 1)
exec aux/usage
branch=$1
if(~ $branch refs/heads/*)
new=$name
if not if(~ $branch heads/*)
new=refs/$branch
if not
new=refs/heads/$branch
orig=`{git/query HEAD}
if (~ $#baseref 1)
base=`{git/query $baseref} || exit 'bad base'
if not if(~ $#newbr 0)
base=`{git/query $new}
if not
base=`{git/query HEAD}
if(~ $#newbr 0){
if(! ~ $#baseref 0)
die update would clobber $branch with $baseref
baseref=`$nl{echo -n $new | sed s@refs/heads/@refs/remotes/origin/@}
if(! test -e .git/$new)
if(! base=`{git/query $baseref})
die could not find branch $branch
}
modified=`$nl{git/query -c HEAD $base | grep '^[^-]' | subst '^..'}
deleted=`$nl{git/query -c HEAD $base | grep '^-' | subst '^..'}
# if we're not merging, don't clobber existing changes.
if(~ $#merge 0){
if(! ~ $#modified 0 || ! ~ $#deleted 0){
git/walk -fRMA $modified $deleted ||
die 'uncommitted changes would be clobbered'
}
}
if(~ $delete 1){
rm -f .git/$new
echo 'deleted branch' $new
exit
}
commit=`{git/query $base} || die 'branch does not exist:' $base
if(~ $new */*)
mkdir -p .git/`{basename -d $new}
if(! ~ $#stay 0){
echo $commit > .git/$new
exit
}
basedir=`{git/query -p $base}
dirtypaths=()
if(! ~ $#modified 0 || ! ~ $#deleted 0)
dirtypaths=`$nl{git/walk -cfRMA $modified $deleted}
if(~ $#dirtypaths 0)
cleanpaths=($modified $deleted)
if not {
cleanpaths=()
for(p in $modified $deleted)
if(! ~ $p $dirtypaths)
cleanpaths=($cleanpaths $p)
}
echo $commit > .git/$new
for(m in $cleanpaths){
d=`$nl{basename -d $m}
mkdir -p $d
mkdir -p .git/index9/tracked/$d
# Modifications can turn a file into
# a directory, or vice versa, so we
# need to delete and copy the files
# over.
a=dir
b=dir
if(test -f $m)
a=file
if(test -f $basedir/tree/$m)
b=file
if(! ~ $a $b){
rm -rf $m
rm -rf .git/index9/tracked/$m
}
if(~ $b file){
cp -x -- $basedir/tree/$m $m
walk -eq $m > .git/index9/tracked/$m
touch $m
}
}
for(ours in $dirtypaths){
common=$gitfs/object/$orig/tree/$ours
theirs=$gitfs/object/$base/tree/$ours
merge1 $ours $ours $common $theirs
}
if(! ~ $#deleted 0){
rm -f $deleted
rm -f .git/index9/tracked/$deleted
}
echo ref: $new > .git/HEAD
echo $new: `{git/query $new}
exit ''