03e5d9e9e2
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'.
123 lines
2.5 KiB
Bash
Executable file
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 ''
|