Reputation: 14825
I'm trying to retrieve the most recent commit who modified/edited a list of files I provide.
In practice, if I have a.txt
and b.txt
, where a.txt
has been modified yesterday and b.txt
has been modified 2 hours ago, I want to get the latest commit which edited b.txt
.
This is my script so far:
for file in components/*/index.jsx
do
# 1. Get the dirname of the current file
dir=`dirname $file`
# 2. Get a space separated list of files to check in $dir
files=`find $dir -type f | grep -E -v "demo.jsx|.test.jsx|meta.json|__snapshots__" | tr '\n' ' '`
# 3. Get the most recent commit that touched any of the listed files
commit=`git log -n 1 --pretty=format:%H -- $files | cat`
# 4. Get the version of the package.json at the specific $commit
version=`git show $commit:packages/react-components/package.json | cat | grep -m 1 version`
# 5. Insert the version in the meta.json of the component
sed -i'' -e "2s/.*/$version/" $dir/meta.json
# 6. Remove the backup file generated by sed
rm $dir/meta.json-e
done
The problem is in step #3, it doesn't return any commit at all if I provide multiple files.
This is the log of the problematic step:
❯ echo $files
components/Foobar/index.jsx components/Foobar/index.sss
❯ git log -n 1 --pretty=format:%H -- $files | cat
# nothing
If instead I run:
❯ git log -n 1 --pretty=format:%H -- components/Foobar/index.jsx components/Foobar/index.sss | cat
93012738e776f4ffe920161ba61501ed84b815a5
I get the commit hash...
Ideas?
update: If I run:
`echo "git log -n 1 --pretty=format:%H -- $files"`
it prints the commit hash, why?
Upvotes: 1
Views: 1338
Reputation: 22969
This extremely weird. Here are some more things to try.
Instructions:
for
loop from running several times and producing lots of output, temporarily replace components/*/index.jsx
with components/Foobar/index.jsx
(or whichever file triggers the problem).git
commands).Snippets:
Check which shell is running the script
echo "$SHELL"
ls -l "$SHELL"
Expected output: /bin/sh
, which might be a symbolic link to bash
or dash
. If it's a symbolic link to another shell (e.g. zsh
), that would be a problem because other shells expand variables differently.
Double check that you don't have quotes ("
) around $files
in the original command.
Check that $IFS
is sane.
echo -n "$IFS" | xxd -p
Expected output: 20090a
(hex for <space><tab><newline>
).
(I know I've already asked you to do this, but please do it again from the script.)
$IFS
controls word splitting, and would cause problems if it was a strange value.
Check that the files exist:
ls components/Foobar/index.jsx components/Foobar/index.sss
Expected output: ls
should list the two files.
If you get a "No such file or directory" error, check the working directory of the script at that point and the git branch that is checked out.
Try using the files
variable, set to a string:
files="components/Foobar/index.jsx components/Foobar/index.sss"
ls $files
Expected output: ls
should list the two files.
If you get an error such as
ls: cannot access components/Foobar/index.jsx components/Foobar/index.sss: No such file or directory
then something is wrong with your shell, because it doesn't do word splitting on unquoted variables.
Try running ls
inside backticks.
files="components/Foobar/index.jsx components/Foobar/index.sss"
output=`ls $files`
echo "$output"
Expected output: The two files.
If you get a "No such file or directory" error then your shell is broken and doesn't do variable expansion inside backticks properly.
Use find
instead of a literal string:
ls $files
Unlike snippet 5, this snippet allows $files
to keep its value from step 2 in your script.
Expected output: ls
should list the same two files as the previous snippet.
If you get an error (no such file or directory), something is wrong with step 2 of your script. Check the value of $files
using echo -n "$files" | od -c
and post it.
Try the git
command with literal file arguments:
git log -n 1 --pretty=format:%H -- components/Foobar/index.jsx components/Foobar/index.sss | cat
echo # output of previous command doesn't have a trailing newline
Expected output: The commit hash.
Try using the $files
variable, set to a string:
files="components/Foobar/index.jsx components/Foobar/index.sss"
git log -n 1 --pretty=format:%H -- $files | cat
echo # output of previous command doesn't have a trailing newline
Expected output: The commit hash.
Use find
instead of a literal string:
git log -n 1 --pretty=format:%H -- $files | cat
echo # output of previous command doesn't have a trailing newline
Expected output: The commit hash.
Try the original command from the script:
commit=`git log -n 1 --pretty=format:%H -- $files | cat`
echo "$commit"
Expected output: The commit hash.
Upvotes: 2
Reputation: 14825
Posting this as an answer, but it's mostly a workaround:
for file in components/*/index.jsx
do
dir=`dirname $file`
files=`find $dir -type f | grep -E -v "demo.jsx|meta.json|.test.jsx|__snapshots__" | tr '\n' ' '`
# We basically print the command as string, and evaluate it with backtricks
# Use only if you have complete control over the project structure!
commit=`$(echo git log -n 1 --pretty=format:%H -- $files) | cat`
version=`git show $commit:packages/react-components/package.json | cat | grep -m 1 version`
sed -i'' -e "2s/.*/$version/" $dir/meta.json
rm $dir/meta.json-e
done
I don't know why this is needed, but it seems to work at least.
Upvotes: 1