diff does not work with subdirectories - diff

I want to patch whole directory tree. Although diff finds all differences, patch does apply these to relevant files. When I change a file in subdir I can see patch crate that file one level above it should in the directory tree being patched.
I use command:
diff -Nur extern/ local/ | patch -d extern
what is wrong with that?

Since you're passing -d dir ("Change to the directory dir immediately, before doing anything else.") you also need to tell patch to strip off one level of directories with -p:
diff -Nur extern/ local/ | patch -d extern -p1
That's because diff's output will look something like this:
+++ extern/foo.x
--- local/foo.x
## -21,7 +21,9 ##
- yyy
+ xxx
... so you need to get rid of that first prefix in the path.

Related

Autocomplete directories in a subfolder with the Fish shell

I'm having trouble getting the 'complete' function in the fish shell to behave as I would like and I've been searching for an answer for days now.
Summary
Essentially I need to provide tab directory auto-completion as if I was in a different directory to the one I am currently in. It should behave exactly as 'cd' and 'ls' do, but with the starting point in another directory. It seems like such a trivial thing to be able to do but I can't find a way to make it work.
Explanation
Example folder structure below
- root
- foo
- a
- dir1
- subdir1
- dir2
- subdir2
- b
- dir3
- subdir3
- dir4
- subdir4
I am running these scripts whilst in the 'root' directory, but I need tab auto-complete to behave as if I was in the 'foo' directory.
testfunc -d a/dir2/subdir2
Instead of
testfunc -d foo/a/dir2/subdir2
There are a lot of directories inside 'foo' and a lot of sub-directories within them, and this auto-complete behaviour is necessary to speed our process (this script is used extensively throughout the day).
Attempted Solution
I've tried using the 'complete' builtin to get this working by specifying the directory to use, but all this managed to do was auto-complete the first level of directories with a space after the argument instead of continuing to auto-complete like 'cd' would.
complete -x -c testfunc -a "(__fish_complete_directories ./foo/)"
Working bash version
I have already got this working in Bash and I am trying to port it over to fish. See below for the Bash version.
_testfunc()
{
local cur prev words cword
_init_completion || return
compopt +o default
case $prev in
testfunc)
COMPREPLY=( $( compgen -W '-d' -- "$cur" ) )
compopt +o nospace
return
;;
-d)
curdir=$(pwd)
cd foo/ 2>/dev/null && _filedir -d
COMPREPLY=( $( compgen -d -S / -- "$cur" ) )
cd $curdir
return
;;
esac
} &&
complete -o nospace -F _testfunc testfunc
This is essentially stepping into the folder that I want, doing the autocompletion, then stepping back into the original folder that the script was run in. I was hoping this would be easier in Fish after getting it working in Bash (I need to support these two shells), but I'm just pulling my hair out.
Any help would be really appreciated!
I am not a bash completions expert, but it looks like the bash completions are implemented by changing directories, running completions, and then changing back. You can do the same in fish:
function complete_testfunc
set prevdir $PWD
cd foo
__fish_complete_directories
cd $prevdir
end
complete -x -c testfunc -a "(complete_testfunc)"
does that work for you?

Standalone Shell script working fine but when used is srcs of sh_binary its not working

I have project structure as follows-
PROJECT_STRUCTURE
Now my_shbin.sh is as below -
#!/bin/bash
find ../../ \( -name "*.java" -o -name "*.xml" -o -name "*.html" -o -name "*.js" -o -name "*.css" \) | grep -vE "/node_modules/|/target/|/dist/" >> temp-scan-files.txt
# scan project files for offensive terms
IFS=$'\n'
for file in $(cat temp-scan-files.txt); do
grep -iF -f temp-scan-regex.txt $file >> its-scan-report.txt
done
This script works completely fine when invoked individually and gives required results.But when I add the below sh_binary in my BUILD file I do not see anything in temp-scan-files.txt file and thus nothing in its-scan-report.txt file
sh_binary(
name = "findFiles",
srcs = ["src/test/resources/my_shbin.sh"],
data = glob(["temp-scan-files.txt", "temp-scan-regex.txt", "its-scan-report.txt"]),
)
I ran sh_binary from intellij using the play icon and also tried running it from terminal using bazel run :findFiles. No error is shown but I cannot see data in temp-scan-files.txt.
Any help on this issue.The documentation of bazel is very confined with approx no information whatsoever except the use case.
When a binary is run using bazel run, it's run from the "runfiles tree" for that binary. The runfiles tree is a directory tree that bazel creates that contains symlinks to the binary's inputs. Try putting pwd and tree at the beginning of the shell script to see what this looks like. The reason that the runfiles tree doesn't contain any of the files in src/main is that they're not declared as inputs to the sh_binary (e.g. using the data attribute). See https://docs.bazel.build/versions/master/user-manual.html#run
Another thing to note is that the glob in data = glob(["temp-scan-files.txt", "temp-scan-regex.txt", "its-scan-report.txt"]), won't match anything, because those files are in src/test/resources relative to the BUILD file. However, the script tries to modify these files, and it's not typically possible to modify input files (if this sh_binary were being run as a build action, the inputs would be effectively read-only. This would work only because bazel run is similar to running the final binary by itself outside bazel, e.g. like bazel build //target && bazel-bin/target)
The most straight-forward way to do this might be something like this:
genrule(
name = "gen_report",
srcs = [
# This must be the first element of srcs so that
# the regex file gets passed to the "-f" of grep in cmd below.
"src/test/resources/temp-scan-regex.txt",
] + glob([
"src/main/**/*.java",
"src/main/**/*.xml",
"src/main/**/*.html",
"src/main/**/*.js",
"src/main/**/*.css",
],
exclude = [
"**/node_modules/**",
"**/target/**",
"**/dist/**",
]),
outs = ["its-scan-report.txt"],
# The first element of $(SRCS) will be the regex file, passed to -f.
cmd = "grep -iF -f $(SRCS) > $#",
)
$(SRCS) are the files in srcs delimited by a space, and $# means "the output file, if there's only one". $(SRCS) will contain the temp-scan-regex.txt file, which you probably don't want to include as part of the scan, but if it's the first element, then it will be the parameter to -f. This is maybe a bit hacky and a little fragile, but it's also kind of annoying to try to separate the file out (e.g. using grep or sed or array slicing).
Then bazel build //project/root/myPackage:its-scan-report.txt

How do I diff only certain files?

I have a list of files (a subset of the files in a directory) and I want to generate a patch that includes only the differences in those files.
From the diff manual, it looks like I can exclude (-x), but would need to specify that for every file that I don't want to include, which seems cumbersome and difficult to script cleanly.
Is there a way to just give diff a list of files? I've already isolated the files with the changes into a separate directory, and I also have a file with the list of filenames, so I can present it to diff in whichever way works best.
What I've tried:
cd filestodiff/
for i in `*`; do diff /fileswithchanges/$i /fileswithoutchanges/$i >> mypatch.diff; done
However patch doesn't see this as valid input because there's no filename header included.
patchutils provides filterdiff that can do this:
diff -ur old/ new/ | filterdiff -I filelist > patchfile
It is packaged for several linux distributions

What order does find(1) list files in?

On extfs, if there are only file-creations and no -deletions in a directory, I expect that find . -type f would list the files either in their chronological order of creation (or mtime), or if not, at least in their reverse chronological order... depending on how a directory's contents are traversed.
But that isn't the behavior I'm seeing.
The following code, eg, creates a fresh set of directories and files:
#!/bin/bash -u
for i in a/ a/{1,2,3,4,5} b/ b/{1,2,3,4,5}; do
if echo "$i" | egrep -q "/$"; then
echo "Creating dir $i"
mkdir -p "$i"
else
echo "Creating file $i"
touch "$i"
fi
sleep 0.500
done
Output of the above snippet:
Creating dir a/
Creating file a/1
Creating file a/2
Creating file a/3
Creating file a/4
Creating file a/5
Creating dir b/
Creating file b/1
Creating file b/2
Creating file b/3
Creating file b/4
Creating file b/5
However, find lists files in somewhat random order. For example, a/2 doesn't follows a/1, and b/2 doesn't follow b/1:
$ find . -type f
./a/1
./a/3
./a/4
./a/2 <----
./a/5
./b/1
./b/3
./b/4
./b/2 <----
./b/5
Any idea why this should happen?
My main problem is: I have a very large volume storing 100s of 1000s of files. I need to traverse these files and directories in the order of their creation/modification (mtime) and pipe each file to another process for further processing. But I don't necessarily want to first create a temporary list of this large set of files and then sort it based on mtime before piping it to my process.
find lists objects in the order that they are reported by the underlying filesystem implementation. You can tell ls to show you this "raw" order by passing it the -f option.
The order could be anything at all -- alphabetical, by mtime, by atime, by length of name, by permissions, or something completely different. The ordering can even vary from one listing to the next.
It's common for filesystems to report in an order that reflects the filesystem's strategy for allocating directory slots to files. If this is some sort of hash-based strategy based on filename then the order can appear nonsensical. This is what happens with widely-used Linux and BSD filesystem implementations. Since you mention extfs this is probably what causes the ordering you're seeing.
So, if you need the output from find to be ordered in a particular way then you'll have to create that order yourself. Maybe based on something like:
find . -type f -exec ls -ltr --time-style=+%s {} \; | sort -n -k6

Perforce -- generate a diff including added files?

When I use "p4 diff", it ignores files that I have open for add. Is it possible to generate a diff that includes added files as well as changed files?
Considering the diff in Perforce compare the files in the client workspace to revisions in the depot, the short answer is "no".
There is no recorded history to compare to for the added file (not committed to the depot before a submit)
The longer answer would involve a script to complete the diff with the full content of the added files. (kind of like in this SO question)
This is a known missing feature of p4 diff that lots of tools dealing with Perforce have to work around.
The code review tool Reviewboard comes with the tool post-review. It is used to create diffs for a a codereview, but it will just do a diff if you want.
It's in python, you could pull out the diff section for your own use.
I wrote this to help me on cygwin:
#!/bin/bash
if [ $# -ne 1 ]; then
echo "usage: $0 <pathSpec>"
exit 1
fi
pathSpec=$1
doIt () {
p4 opened "$pathSpec" | egrep "#.* - add" | while read f; do
# figure out the workspace path
depotPath=${f%#*}
clientFileLine=$(p4 fstat "$depotPath" | grep clientFile)
workspacePathWin=${clientFileLine#... clientFile }
# diff output
echo ==== $depotPath#1 - $workspacePathWin ====
workspacePath=$(cygpath "$workspacePathWin")
lineCount=$(wc -l < "$workspacePath")
echo ## -0,0 +1,$lineCount ##
# the actual diff contents
sed -e 's/^/+/' "$workspacePath"
echo
done
}
doIt
I just worked around this issue and got my added files included in a Fisheye pre-commit review.
What I did was copy the edited files' diff hunk descriptor header. This includes one line which lists the depot and local paths - pretty straightforward - and another line with the range information, formatted as described here.
So, I added each appended each file's contents to the diff, each preceded by
==== //path/to/depot/file#1 - /path/to/workspace/file ====
## -1,1 +1,LEN ##
where LEN is the number of lines in the added file.
This worked for me; Your Mileage May Vary.
I just got this to work, branching off Aaron's answer. Instead of
==== //path/to/depot/file#1 - /path/to/workspace/file ====
I used
--- //path/to/file [TIMESTAMP GENERATED BY NORMAL DIFF]
+++ //path/to/file [TIMESTAMP GENERATED BY NORMAL DIFF]
## -1,1 +1,LEN ##
Then make sure to have a + at the beginning of each added line.
Just p4 print the added files (I know, that's not great) - but its definitely one way to go.