I am trying to write a bash script which checks if there are any commits of a repository and returns a message. Here is the script so far.
MY_PATH="C:/test"
cd "$MY_PATH"
git clone https://github.com/test-repo/docker-react.git
cd "docker-react"
git checkout master
if [[ -n "`git log --pretty=format: --name-only --since="200 days ago" | sort | uniq`" ]]
then
echo -e No Commits last 2 days
else
echo -e Commits available
fi
When I execute this code it always show commits available even when I change the days.
Where am I going wrong?
That looks like a bash script, which would be interpreted by the Git for Windows bash shell.
It means it should be checked with ShellCheck, which would then recommend:
#!/bin/bash
MY_PATH="C:/test"
cd "$MY_PATH" || exit
git clone https://github.com/test-repo/docker-react.git
cd "docker-react" || exit
git checkout master
if [[ -n "$(git log --pretty=format: --name-only --since="200 days ago" | sort | uniq)" ]]
then
echo -e No Commits last 2 days
else
echo -e Commits available
fi
Not yet ideal: ideally, it would check if the repository is not already cloned. But it should work better.
Related
We use a gh-pages branch in our repository to host a static website and frequently commit new information to this branch. Those files often get stale, as we push to a subdirectory per feature branch in the same repository.
The directory structure in my gh-pages branch is similar to the following:
.
|-- README.md
|-- JIRA-1234-feature
| `-- graph
|-- JIRA-4567-bugfix
| `-- graph
|-- JIRA-7890-branch-name
| `-- testing
I want to remove directories via a GitHub actions for which the last update was more than 5 days ago.
I naively tried to remove them via find /path/to/files* -mtime +5 -exec rm {} ;, but the operating system obviously uses the clone date as the last modified time.
I also found
git ls-tree -r --name-only HEAD | while read filename; do
echo "$(git log -1 --format="%ad" --date="short" -- $filename) $filename"
done
which prints the last git update and the file name like this:
2023-01-12 JIRA-1234-test/index.html
2023-01-12 JIRA-1234-test/static/test.css
I don't know how to trigger file removal commands from this list, though.
How would I have to modify the following action to remove the old files?
name: Prune GH Pages branch
on:
workflow_dispatch:
jobs:
upload:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout#v3
with:
ref: gh-pages
- name: Cleanup
run: |
# TODO: REMOVE FILES HERE
git ls-tree -r --name-only HEAD | while read filename; do
echo "$(git log -1 --format="%ad" --date="short" -- $filename) $filename"
done
- name: Commit & Push
run: |
if [ $(git status --porcelain | wc -l) -eq "0" ]; then
echo "git repo is clean."
else
git add -A
git commit -m "branch cleanup"
git push
fi
Unfortunately, I didn't find a way to make a nice one-liner for the requirement. We need the following bash script. I have commented all the important steps.
#!/bin/bash
# Validate if $1 is a positive number days
[[ $1 != +([[:digit:]]) ]] &&
echo "$1: The script has to be run with positive number argument" && exit 1
# Get the X days ago timestamp
X_DAYS_AGO_TIMESTAMP=$(date -d "$1 days ago" +%s)
# Iterate over all files in the repository
for file in $(git ls-files); do
echo -n "."
# Get the timestamp of the last commit that modified this file
LAST_MODIFIED_TIMESTAMP=$(git log -1 --format="%at" -- "$file")
# If the file hasn't been modified within the last $1 days
if [ "$LAST_MODIFIED_TIMESTAMP" -lt "$X_DAYS_AGO_TIMESTAMP" ]; then
# Remove the file from the repository
echo -e "\nRemoving $file last modified at $(date -d "#$LAST_MODIFIED_TIMESTAMP")"
git rm --quiet "$file"
fi
done
# Commit the changes (if any)
if ! git diff --exit-code --quiet --staged; then
git commit -m "Remove files not modified within the last $1 days"
else
echo "No files removed"
fi
I can elaborate if something is not clear enough.
This question already has answers here:
How do I set a variable to the output of a command in Bash?
(15 answers)
Closed 5 months ago.
This is probably a simple one for a bash scripter, which I am not.
I'm running a cron job that downloads some data, and then depending on that data, may or may not modify a second file. After the job, I want to git commit one or both files. For the conditional commit, I tried this in a .sh script:
# attempt to capture whether MyNotes.txt was changed
# by counting lines in git status output
mywc=(git status -s MyNotes.txt | wc -l)
echo $mywc found!
if [ $mywc = 1 ]; then
echo Add file for commit
else
echo Nothing to add
fi
I'm pretty much getting nowhere; this thing seems to fail on the first line with syntax error near unexpected token '|'. If I run git status -s MyNotes.txt | wc -l on the command line, I get the numeric output I expect.
What am I doing wrong and how can I make this work?
If there's a more elegant way to determine whether a file changed, feel free to share.
Also, for my edification, how could I get this to work without the interim mywc variable? I.e., if I wanted to just do the command within the if, something like this:
if [[ $(git status -s MyNotes.txt | wc -l) = 1 ]]; then
...
Thanks!
What am I doing wrong and how can I make this work?
put a dollar before parenthesis.
foo=$(command)
The thing you are using looks like a bash array
declare -a letters=(a b c d)
If there's a more elegant way to determine whether a file changed, feel free to share.
Consider this:
$ git diff -s --exit-code README.md || echo has changed
has changed
$ git checkout README.md
Updated 1 path from the index
$ git diff -s --exit-code README.md || echo has changed
The OR (||) runs if the first command exits with a non-zero code.
Same thing essentially:
$ false || echo false exits with 1
false exits with 1
$ true || echo will not trigger
An aspect of bash that people overlook is that [[, ]], [ and ] are separate commands. They have return codes too. With this knowledge, you can leverage the return codes with if and any other command.
$ if true; then echo yes; else echo no; fi
yes
$ if false; then echo yes; else echo no; fi
no
So for detecting changes in a tracked file:
$ if git diff -s --exit-code README.md; then echo same as in index; else echo changed; fi
same as in index
$ echo 123 >> README.md
$ if git diff -s --exit-code README.md; then echo same as in index; else echo changed; fi
changed
With all of that said...
Just add the file. You don't need to check anything. If it hasn't changed, nothing will happen.
$ echo foo >> myfile
$ git add myfile
$ git commit -m 'maybe changed' myfile
[master b561cc1] maybe changed
1 file changed, 1 insertion(+), 1 deletion(-)
$ git add myfile
$ git commit -m 'maybe changed' myfile
no changes added to commit (use "git add" and/or "git commit -a")
if you need to avoid a non-zero exit code (such as with set -e), just put a || true after the command that you want to ignore the exit status of:
$ cat foo.sh
#!/bin/basho
set -e
echo foo >> myfile
git add myfile
git commit -m 'maybe changed' myfile
git add myfile
git commit -m 'maybe changed' myfile > /dev/null || true
echo no error here. it\'s fine..
false
echo fill never reach this.
Try running that script and see what happens
I search for a way for checking if file changed.
git diff --exit-code -s <path>
Now the bash scripter knows that every command returns a status code which can be checked with $?. In case everything went smoothly, 0 is returned. In that case we get 0 if file is not changed.
Every bash scripter knows too that you can use that with && and || operators (because of lazy evaluation) to write such construct:
git diff --exit-code -s <path> && echo "should add file"
About your edification, what you wrote is perfectly fine!
As CryptoFool pointed out in a comment, I failed to include a $ in my variable assignment. Simple fix in the first line of my script:
mywc=$(git status -s MyNotes.txt | wc -l)
As matt pointed out in a subsequent comment, doing a git add on a file that hasn't changed has no effect. It won't stage the file for commit. So instead of doing conditional logic to determine whether to git add myfile.txt, I'll just blindly execute git add myfile.txt, which will either stage the file if there are changes, or do nothing if there are no changes. Therefore, my entire script can be replaced with one line:
git add MyNotes.txt
It seems like 1836 branches exist in one of the company's repos and I've been given a task to first display and then delete all branches that have not been committed to for 6 months.
I found this SO question and tried running (with both --until and --before and "month"):
#!/bin/bash
branches_to_delete_count=0
for k in $(git branch -a | sed /\*/d); do
if [ -n "$(git log -1 --before='6 month ago' -s $k)" ]; then
echo "NOT REALLY DELETING, git branch -D $k"
fi
((branches_to_delete_count=branches_to_delete_count+1))
done
echo "Found $branches_to_delete_count branches to delete!"
But to no avail, I get the same number of branches to delete each time which is 1836.
What am I doing wrong? How can I list all branches that haven't been committed for more than 6 months?
The reason why all your branches show up : git log branch does not look at branch's head only, it looks at its whole history.
git log -1 --before='6 month ago' branch will :
unroll the history of branch
keep only commits older than 6 month
keep the first of these commits
Since (in your company's repo) all branches have a commit that is at least 6 month old in their history, git log -1 --before='6 month ago' branch will always show one line.
You can either restrict the range of commits to "a range which contains only the head commit" :
git log -1 --before='6 month ago' branch^..branch
or use git for-each-ref as #phd suggested in his comment :
git for-each-ref --format="%(refname) %(creatordate)" --sort='-creatordate' refs/heads/
and keep the branches with old enough dates.
we have not direct get the branch name which has last commit in 6 month ago so we combine git command and make shell script
Here we are passing two git command
first was git branch | sed s/^..// here get branch and remove two space
second was git log -1 --before='6 month ago' <branch-name>
pass following command in terminal
copy and past in terminal give branch name
for branch in `git branch | sed s/^..//` ; do log=`git log -1 --before='6 month ago' $branch`; if [ ${#log} -gt 0 ] ; then echo $branch; fi; done
this is shell script along with git command same as above
save shell script with test.sh,change mode chmod +x test.sh then run bash test.sh
month=6 #check how many year ago
for branch in `git branch | sed s/^..//` #get branch one by one
do
log=`git log -1 --before='%s month ago'$month $branch` #getting log of the branch last commit base on month
if [ ${#log} -gt 0 ] #check if log has output then it has branch commit before specify month ago
then
echo $branch # print branch name which is in our project
fi
done
let me know does it work or not
I am trying to navigate through all existing all branches and lookup if files with a certain extension such as (.zip or .exe exist)
I tried to write a bash script to achieve this task.
for branch in $(git branch);
do
echo "I am in: $branch"
git ls-files *.exe
done
I would like to see the file path when it is detected.
You are not changing to the branch so you are always checking the last branch you checked out. Try this:
# In the repo's working directory
for branch in $(git branch -a|grep -v remotes|sed 's/\*//g'); do
echo "I am in branch: ${branch}"
git checkout ${branch}
find . -type f -name '*.md'
done
Following is how I solved my problem:
read -p "Extension to lookup [example: .zip]: " extensionType
for branch in $(git branch);
do
if [[ $branch == *"Release"* ]]; then
echo "----------------------------------"
echo ">>Navigating to: $branch"
echo ">>$branch..."
git checkout $branch
git ls-files "*$extensionType"
echo "----------------------------------"
fi
done
I hope this helps.
Is there a way in Git Bash to check if the working tree is clean, that is no uncommitted changes or untracked files?
I'm working on a bash script for my group to automate the process of daily rebasing working branches. Unclean working trees is a common problem. I can manually correct the problem by executing git checkout .. This would have the desired result most of the time, but not always, so I need to be able to have my script programatically check that the working directory/tree is clean.
The git-sh-setup script included with git contains a number of useful functions for working with git repositories. Among them is require_clean_work_tree:
require_clean_work_tree () {
git rev-parse --verify HEAD >/dev/null || exit 1
git update-index -q --ignore-submodules --refresh
err=0
if ! git diff-files --quiet --ignore-submodules
then
echo >&2 "Cannot $1: You have unstaged changes."
err=1
fi
if ! git diff-index --cached --quiet --ignore-submodules HEAD --
then
if [ $err = 0 ]
then
echo >&2 "Cannot $1: Your index contains uncommitted changes."
else
echo >&2 "Additionally, your index contains uncommitted changes."
fi
err=1
fi
if [ $err = 1 ]
then
test -n "$2" && echo >&2 "$2"
exit 1
fi
}
This is in addition to being able to check the output from git status --porcelain and/or git status -z if you need to be more specific about what the state currently is.