Bash conditional based on whether a file has changed since last pull - bash

I'm writing a script for automatically updating a system of ours. Basically I want to do a pull and update from a remote hg repository, and then run some update scripts. Now the problem is that these update scripts takes a while to run and most of them only has to be run if there has been changes to their configurations.
Now my script looks like following:
if hg pull -u
then
run scripts
fi
What I want is something like
if hg pull -u && 'some changes was introduces in my/configuration/directory/*'
then
run scripts
fi
Any idea how to do this?

With templated output of hg incoming your can check before pull, which files will be modified on pull (if any will be) and will act accordingly
hg incoming --template "{files % '{file}\n'}" | grep SOMESTRING

You can use hg status to get a list of files that have been changed between revisions, for example files modified between tip and it's parent(tip^) that are in my\configuration\directory:
hg status my\configuration\directory\** -m --rev "tip^:tip"
I would recommend to pull, check if those files have been altered from the current revset, update, and then run your scripts if your config has changed. To me that looks easier than trying to store which revset you started with and figure it out after the update. (Note that I'm not great with bash/grep, so this is approximate and untested):
hg pull
cfgChngd = hg status -m my\config\dir\** -m --rev tip | grep "M my\config\"
hg update
if cfgChngd
runAllTheScripts
fi

You can use the status command to get a list of changed files -- also between different revisions:
HERE=`hg log --template '{node}' -r.`
hg pull -u
if hg st --rev $HERE:. | grep -q .
then
run scripts
fi

Related

Consider only files being pushed in pre-push hook git

I want to run lint on code for pre-push without considering the local changed files the user has. for example change in file A is being pushed and the same got changed in the local changes I want to consider the code being pushed by user.
How to implement this using hit-hooks.
Alternate ways I tried:
Limiting the user to reset the changes in pre-push - The functionality is limited in this case
There are several ways to get files from git storage and write them to disk, but I don't know of a direct command to say straight away "checkout files A, B and C from commit xxx to that directory on disk".
The simplest way is probably to use git worktree add (but this checks out all files, not just the ones you want) :
git worktree add /tmp/myhook.xyz <commit-sha>
The most direct way is to use git --work-tree=... (or GIT_WORK_TREE=...) to target some other directory on disk :
git --work-tree=/tmp/myhook.xyz checkout <commit-sha> -- file1 file2 path/to/file3
How to use this in a pre-push hook:
for each pushed reference, you can :
compare the local commit and remote commit to list files that were modified,
use the above trick to checkout the files from local commit in a specific destination on disk :
# pre-push:
#!/bin/bash
zero=$(git hash-object --stdin </dev/null | tr '[0-9a-f]' '0')
list_modified_files () {
local $local_commit=$1
local $remote_commit=$2
if [ "$local_commit" = "$zero" ]; then
return
fi
if [ "$remote_oid" = "$zero" ]; then
git ls-tree -r --name-only $local_oid
else
git diff --no-renames --diff-filter=AM --name-status $remote_oid $local_oid
fi
}
while read local_ref local_oid remote_ref remote_oid
do
echo "'$local_ref' '$local_oid' '$remote_ref' '$remote_oid'"
tmpdir=$(mktemp -d /tmp/myprepushhook.XXXXXX)
list_modified_files | xargs -r git --work-tree "$tmpdir" checkout "$local_oid" --
# run linter on files in $tmpdir ...
rm -rf "$tmpdir"
done

Git short command for checking out, pulling, prune fetching and deleting

Is there a shortcut for:
Checking out on master
Pulling from master
Prune Fetch (check which remote branches are removed)
Delete those local branches
Scenario:
Let's say I'm on master and I checkout on the branch foo, I do some commits and publish foo to remote and push to it as well. Next I merge that branch to master on GitHub and delete the online version of foo since it's complete. Now in the offline environment, I have to do the following:
$ git checkout master
$ git pull
$ git fetch -p
$ git branch -d foo
or shorthand:
git checkout master && git pull && git fetch -p && git branch -D foo
Is there a command I can execute to make this much shorter? Like
$ git complete foo
or something along those lines..?
aliases can be used for solving this problem.
An alias can be created by running:
$ alias cpfb="git checkout master && git pull && git fetch -p && git branch -D"
Now, you can execute
$ cpfb foo
which will execute those commands specified in the alias.
Setting alias through terminal lasts for only that particular terminal instance.
Hence, save them in ~/.bashrc to make the alias permanent.

Git hook on Ubuntu broken

I recently got a git hook from someone that aims to add the issue number, which is in a specific location of the branch name, to the beginning of all commits. The goal is to take the #number from feature/#number-issue. Here is some info:
➜ .githooks pwd
/home/luctia/.githooks
➜ .githooks git config --global --list
user.name=luctia
user.email=myemail
core.hookspath=/home/luctia/.githooks
➜ .githooks cat commit-msg
#!/bin/sh
WI=$(git status --branch | grep -iPo "(feature|bug)\/#\d+" | head -1)
WI=$(echo "($WI)" | grep -Po "\d+")
if [[ ! -z "$WI" ]]; then
WI="#$WI"
CM=$(cat "$1")
if [[ ! $CM == *"$WI "* ]]; then
echo "$WI $CM" > "$1"
fi
fi
This doesn't seem to work, though. The script is executable for every user, so that's not the issue. I have tried switching from sh to bash, and with that edit I've executed the script on a file in a repo, which added the number to the beginning of the file, so I know it works. I'm not sure if git hooks can execute bash files, but it doesn't make a difference whether I use sh or bash, though I would like to know if it can run bash scripts.
I'm using Webstorm for my IDE right now, and it doesn't work in there, and it also doesn't work on CLI git. I have no idea how to proceed.
Edit: I am pretty sure the script is not executed. When I add data > /tmp/hook to the script, no file appears. I do have to change from sh to bash though.
The problem was that I was trying to make this work on a pre-existing project, with an existing .git directory. I thought changing the config with the --global flag would just work, but apparently the config inside the .git directory of the project did not change, and the old hookspath was still there. When I changed it, the script started working.

Error when creating all branches at once

I have created this bash script to create all branches at once
#!/bin/bash
git fetch -vp
for b in $(git branch -a | grep remotes | grep -v HEAD)
do
branchname=${b##*/}
remote=${b#*/}
command="git branch --track $branchname $remote"
echo "$command"
$($command)
done
but I am always having the same error:
fatal: 'master' is not a valid branch name.
If I run the same command without the script the it is executed successfully.
What am I doing wrong ?
I have created this script because I need push all my branches to another remote repo, so first I need create the all local branches from original repository...
You don't.
You can use the remote tracking branches which you already have from git fetch. That's like origin/master. git fetch has already downloaded all the commits from the remote, and branches in Git are just labels on commits. Even remote branches.
You can get a list of all your remote tracking branches from git branch -r but that's from all remotes. To get the branches for just one remote use git ls-remotes --heads <remote>. The format is a little funny, so you have to do some massaging.
$ git ls-remote --heads $REMOTE | cut -f2 | sed -E "s/refs\/heads/$REMOTE/"
origin/80_method_methods
origin/gh-pages
origin/io-all
origin/issue/217
origin/issue/255
origin/master
origin/rewrite_cmd_wrapper
Then you can push from those branches.
Though wanting to push all the branches from one repo to another is very odd. There's probably a simpler way to solve whatever problem you're trying to solve. I suspect you have an XY Problem.
I would suggest asking a question about that problem instead.
As it is well explained in #Schwern's answer, wanting to "checkout all branches" of a remote repo is probably a XY problem, and it is unneeded in practice because git fetch $REMOTE is enough to retrieve the whole history of the considered repo.
However the proposed command git ls-remote ... does not seem to be appropriate because:
it needs a network connection
it doesn't display the list of remote branches
it just queries the remote repo (without relying on fetched data) to know if the commits corresponding to local branches happen to be known in the remote.
In order to efficiently get the list of all branches from a given remote, I propose instead:
git for-each-ref --format "%(refname:strip=2)" "refs/remotes/$REMOTE/" | grep -v -e '/HEAD$'

List most recent mercurial hashes

Does hg have a nice way to list just the hashes of the most recent commits in the repo on separate lines? hg id -i only gives me the current commit.
Something equivalent to the -l parameter in hg log such that I could do something like:
$ hg id -i -l 3
1eb0c5a3002e <--- youngest commit
321c6693361e <--- 2nd youngest commit
647724838c03 <--- 3rd youngest commit
To give some context, I have a bash script which I pass a mercurial hash to and it builds a docker image,
./build_image.sh 647724838c03
I am trying to get command line completion for the hash argument so I need a command to generate all the hashes which I can drop into compgen:
local HASHES=$(cd $REPO && hg id -i -l 10)
COMPREPLY=( $(compgen -W "$HASHES" -- $cur) )
Most of the time the hash I'm wanting to build will be the checked out commit so hg id -i will work okay, but getting a full list of the last 10 hashes would be nice!
Thanks in advance for any help!
EDIT
I got a work around using grep and awk:
$ hg log -l 5 | grep changeset | awk -vn=12 '{print substr($0,length($0)-n+1)}'
1eb0c5a3002e
ec0db890a6c3
321c6693361e
4a1e2ca2df06
2d1ef7d57485
This isn't great though:
makes assumptions about how hg log outputs (which might change in different versions)
that awk command is a bit too mystical for me :)
doesn't work for Windows (assuming you're trying to solve a different problem to bash tab completion)
That's what you have log for. Template the output to your needs, e.g.
hg log -l10 --template="{node|short} {date} {author}\n"
gives you output like
0c4520bbf388 2017-06-07 12:25 +0200 username <user#example.com>
Checkout hg help template for more templating options. You can basically apply templating to all relevant commands which generate these kind of outputs.
Mind: if you do not sort log explicitly by commit date, it will return you the 10 newest commits in your repository - which does not necessarily reflect order of commit time. You can do that by giving additionally a revset and sorting by it:
hg log --rev="sort(date('<now'))" --template="{node}\n" -l5

Resources