code: plan9front

Download patch

ref: cfebf83947550f3ae730cef4a9b1ab6bf6b8d4f7
parent: e524e8d65a7573c46d7beb49e77bfc2d55a5563d
author: Ori Bernstein <ori@eigenstate.org>
date: Tue Aug 17 00:31:15 EDT 2021

git: better handling of absolute paths, regex metachars

Git currently gets a bit confused if you try to
manipulate files by absolute path.  There were also a
number of places where user-controlled file paths ended
up getting passed to regex interpretation, which could
confuse things.

This change mainly does 2 things:

	- Adds a 'drop' function which drops
	  a non-regex prefix from a string, and uses
	  that to manipulate paths, simplifies 'subst',
	  and removes 'subst -g', which was only used
	  with fixed regexes; sed does this job fine.
	- When getting a path from a user, we
	  make it absolute and then strip out the head

Along the way it cleans up a couple of stupids:

	- 'for(f in $list) if(! ~ $#f 0) use $f:
	  $f can't be a nil list because of
	  list flattening.
	- removes a useless substitution here:

	 	all=`$nl{{git/query -c $1 $2; git/query -c $2 $3} | sed 's/^..//' | \
			gsubst '^('$ourbr'|'$basebr'|'$theirbr')/*' | sort | uniq}

	  where git/query -c doesn't produce
	  paths prefixed with the query.

--- a/sys/lib/git/common.rc
+++ b/sys/lib/git/common.rc
@@ -11,23 +11,22 @@
 	exit 'usage'
 }
 
-# subst [-g] this [that]
-fn subst{
-	awk 'BEGIN{
-		global = 0
-		for(i = 1; ARGV[i] ~ /^-/; i++){
-			if(ARGV[i] == "-g")
-				global = 1
-			ARGC--
-		}
-		this = ARGV[i++]; ARGC--
-		that = ARGV[i++]; ARGC--
-	}
+fn subst {
+	awk '
+	BEGIN{ARGC=0}
+	{sub(ARGV[1], ARGV[2]); print}
+	' $*
+}
+
+fn drop {
+	awk '
+	BEGIN{ARGC=0}
 	{
-		if(global) gsub(this, that)
-		else sub(this, that)
+		if(index($0, ARGV[1]) == 1)
+			$0=substr($0, length(ARGV[1])+1)
 		print
-	}' $*
+	}
+	' $*
 }
 
 fn present {
@@ -77,7 +76,7 @@
 	if(~ $#gitroot 0)
 		die 'not a git repository'
 	gitfs=$gitroot/.git/fs
-	gitrel=`{pwd | subst '^'$"gitroot'/?'}
+	gitrel=`{pwd | drop $gitroot | sed 's@^/@@'}
 	if(~ $#gitrel 0)
 		gitrel='.'
 	cd $gitroot
--- a/sys/src/cmd/git/add
+++ b/sys/src/cmd/git/add
@@ -16,11 +16,11 @@
 if(~ $#* 0)
 	exec aux/usage
 
-paths=`$nl{cleanname -d $gitrel $*}
+paths=`$nl{cleanname -d $gitrel $* | drop $gitroot}
 if(~ $add tracked)
-	files=`$nl{walk -f $paths}
+	files=`$nl{walk -f ./$paths}
 if not
-	files=`$nl{cd .git/index9/tracked/ && walk -f $paths}
+	files=`$nl{cd .git/index9/tracked/ && walk -f ./$paths}
 
 for(f in $files){
 	if(! ~ `$nl{cleanname $f} .git/*){
--- a/sys/src/cmd/git/clone
+++ b/sys/src/cmd/git/clone
@@ -7,7 +7,7 @@
 if(~ $debug 1)
 	debug=(-d)
 
-remote=`{echo $1 | subst -g '/*$'}
+remote=`{echo $1 | sed  's@/*$@@'}
 local=$2
 
 if(~ $#remote 0)
@@ -79,7 +79,7 @@
 
 	tree=.git/fs/HEAD/tree
 	lbranch=`{git/branch}
-	rbranch=`{echo $lbranch | subst '^heads' 'remotes/origin'}
+	rbranch=`{echo $lbranch | subst 'heads' 'remotes/origin'}
 	echo checking out repository...
 	if(test -f .git/refs/$rbranch){
 		cp .git/refs/$rbranch .git/refs/$lbranch
@@ -86,12 +86,10 @@
 		git/fs
 		@ {builtin cd $tree && tar cif /fd/1 .} | @ {tar xf /fd/0} \
 			|| die 'checkout failed:' $status
-		for(f in `$nl{walk -f $tree | subst '^'$tree'/*'}){
-			if(! ~ $#f 0){
-				idx=.git/index9/tracked/$f
-				mkdir -p `$nl{basename -d $idx}
-				walk -eq $f > $idx
-			}
+		for(f in `$nl{walk -f $tree | drop $tree}){
+			idx=.git/index9/tracked/$f
+			mkdir -p `$nl{basename -d $idx}
+			walk -eq ./$f > $idx
 		}
 	}
 	if not{
--- a/sys/src/cmd/git/merge
+++ b/sys/src/cmd/git/merge
@@ -7,13 +7,12 @@
 	basebr=$gitfs/object/$2/tree
 	theirbr=$gitfs/object/$3/tree
 
-	all=`$nl{{git/query -c $1 $2; git/query -c $2 $3} | sed 's/^..//' | \
-		subst -g '^('$ourbr'|'$basebr'|'$theirbr')/*' | sort | uniq}
+	all=`$nl{{git/query -c $1 $2; git/query -c $2 $3} | sed 's/^..//' | sort | uniq}
 	for(f in $all){
 		ours=$ourbr/$f
 		base=$basebr/$f
 		theirs=$theirbr/$f
-		merge1 $f $theirs $base $ours
+		merge1 ./$f $theirs $base $ours
 	}
 }
 
--- a/sys/src/cmd/git/pull
+++ b/sys/src/cmd/git/pull
@@ -75,8 +75,8 @@
 # The remote is directly ahead of the local, and we have
 # no local commits that need merging.
 if(~ $#quiet 0)
-	git/log -s -e $local'..'$remote >[1=2]
+	git/log -s -e $local'..'$remote
 echo
-echo $remote':' `{git/query $local} '=>' `{git/query $remote}  >[1=2]
+echo $remote':' `{git/query $local} '=>' `{git/query $remote}
 git/branch -mnb $remote $local
 exit ''
--- a/sys/src/cmd/git/revert
+++ b/sys/src/cmd/git/revert
@@ -11,8 +11,8 @@
 if(~ $#query 1)
 	commit=`{git/query -p $query}
 
-files=`$nl{cleanname -d $gitrel $*}
-for(f in `$nl{cd $commit/tree/ && walk -f $files}){
+files=`$nl{cleanname -d $gitrel $* | drop $gitroot}
+for(f in `$nl{cd $commit/tree/ && walk -f ./$files}){
 	mkdir -p `{basename -d $f}
 	cp -x -- $commit/tree/$f $f
 	git/add $f