git - bash script to automatically reserve merge conflict - bash

My team routinely goes through merge conflicts because of a versioning file being updated on every build. The file is version.txt.
This is where I've got to so far. I only want it to resolve the version.txt conflict by accepting either the current branch version or the incoming branch. When I use the below script, my PRs show as empty which I assume means it has discarded all local changes by using the ours strategy.
I would like the script to keep all local changes–except for the ones causing the conflict, resolve the conflict and push to the current feature branch. What can I do to achieve this?
#!/bin/bash
set -e
FEATURE_BRANCH=$1
DESTINATION_BRANCH=$2
read -p "Would you like to accept incoming or outgoing changes? {in/out}" OP
if [ $OP = "in" ]; then
MERGE_STRATEGY="Xtheirs"
else
MERGE_STRATEGY="Xours"
fi
echo "Pulling current branch"
git pull
echo "Checking out current branch"
git checkout $FEATURE_BRANCH
echo "Pulling destination branch"
git pull origin $DESTINATION_BRANCH -${MERGE_STRATEGY} <<-EOF
:wq #this part is necessary because vim text editor is automatically opened to display a merge message.
EOF
echo "Conflict resolved, pushing back to feature branch"
git push origin $FEATURE_BRANCH
echo "PR ready for merge"

Related

Husky pre commit hook and squashing commits

I am using "husky": "^7.0.4".
My team squashes their commits before opening a PR.
I have a pre-commit file to automate this workflow. Every other time I run the commit function, the pre-commit flow works perfectly. So the 1st, 3rd, 5th, etc. works. The 2nd, 4th, 6th, etc time prints this error
fatal: cannot lock ref 'HEAD': is at 766hdjoXXX but expected 766e11XXX
I thought it might be because I wasn't changing the file, however when I tried changing something, that didn't work either(it succeeds and fails every other time regardless). Any idea what's wrong?
Here is the pre-commit file:
read -n1 -p "Do you want to squash commits? [n/Y]" SHOULD_SQUASH < /dev/tty
case $SHOULD_SQUASH in
n|N)
echo
echo Skipping squash, now linting files...
;;
y|Y)
[ -z "$SQUASH_BRANCH" ] && SQUASH_BRANCH=develop
branch=$(git symbolic-ref HEAD)
echo
echo Squashing all commits from $branch
git reset $(git merge-base $SQUASH_BRANCH $branch)
echo ------SUCCESS!------
echo Commits successfully squashed.
git add .
echo Added all files successfully.
;;
*)
echo
echo Skipping squash, now linting files...
;;
esac
npx pretty-quick --staged
npm run lint
The squash function is from a custom function, that works with no problem, we created that lives in .zshrc.
Pre-commit files in general should not use git reset and git add. It is possible to make this work sometimes, but you get odd effects, including the one you're seeing (and sometimes worse ones). A pre-commit script should limit itself to testing whether the commit is OK, and if so, exiting zero; if not, the script should exit nonzero, without attempting to make any changes.1
Instead of calling your script .git/pre-commit and invoking it with git commit, call it makecommit and invoke it as makecommit. Or, call it git-c and invoke it as git c. Have the script do its thing—including run npm lint—and if all looks good, have it run git commit. You won't need a pre-commit hook at all, but if you like, you can have one that reads as follows:
[ "$RUN_FROM_OUR_SCRIPT" = yes ] && exit 0
echo "don't run git commit directly, run our script instead"
exit 1
Then instead of just git commit, have your script do:
RUN_FROM_OUR_SCRIPT=yes git commit
which will set the variable that the pre-commit hook tests to make sure git commit was run from your script.
Note that you will no longer need to redirect the read from /dev/tty. (You probably should also consider using git reset --soft, and/or verifying the that index content matches the working tree content.)
1If you like to live dangerously, and really want to have a script that can update files, make sure that $GIT_INDEX_FILE is unset or set to .git/index, to make sure you have not been invoked as git commit --only.

git hook pre-push bash script validate tag

I'm trying to validate a git tag using the pre-push git hook but whenever I execute git push origin <tag-name> it takes the previous tag as the latest from /refs/tags.
To replicate the issue:
Step 1. git commit
step 2. git tag V1.0.0-US123-major
step 3. git push origin V1.0.0-US123-major
So when step 3 executes the pre-push script should take "V1.0.0-US123-major" tag and validates against the below regex. If the tag matches with regex then it is a valid tag else abort git push.
#!/bin/sh
read_tag="$(git describe --abbrev=0 --tags)"
if [[ $read_tag =~ (^v[0-9]{1}.[0-9]{1}.[0-9]{1}-[a-zA-Z]+-[a-zA-Z]+$) ]]; then
echo "matched"
else
echo "not matched"
exit 1
fi
My expectation is when I use git push origin 2.2.2.2, the pre-push script does not return exit1 rather it accepts the tag and pushing to origin which is not correct.
git push origin 2.2.2
latest tag: v5.5.5-abcd-tues
matched
Can someone help me with this, please?
Your pre-push hook is checking the current revision, not the tag you're pushing, because git describe describes HEAD if you don't specify otherwise.
When you use a pre-push hook, the references being pushed are passed in on standard input. Assuming the thing you want to check is the name of the remote reference (that is, the one that's going to end up on the server), then it could look something like this (using POSIX syntax):
#!/bin/sh
set -e
while read lref new rref old
do
case $rref in
refs/tags/*)
if echo "$rref" | \
grep -qsE '(^refs/tags/v[0-9]{1}.[0-9]{1}.[0-9]{1}-[a-zA-Z]+-[a-zA-Z]+$)'
then
echo "matched"
else
echo "not matched"
exit 1
fi;;
*)
;;
esac
done
Do note that while a pre-push hook can help the developer make good choices and avoid mistakes, it's not an effective control, because it can be trivially bypassed. If you need to restrict what gets pushed to the server, you need to do that either with a pre-receive hook, your server implementation, or a CI system. See the relevant Git FAQ entry for more.

How to add Server-side Custom Hooks in Gitlab?

What I am trying to do is that i want to add my hooks to repo in server, so that whoever has cloned the repo, passes through this before pushing to Gitlab server.
So far what i have done is, created pre-receive file in custom_hooks folder in /.git/custom_hooks and added some script to it.
Below is my pre-receive file.
#!/bin/bash
zero_commit="0000000000000000000000000000000000000000"
excludeExisting="--not --all"
while read oldrev newrev refname; do
# echo "payload"
echo $refname $oldrev $newrev
# branch or tag get deleted
if [ "$newrev" = "$zero_commit" ]; then
continue
fi
# Check for new branch or tag
if [ "$oldrev" = "$zero_commit" ]; then
span=`git rev-list $newrev $excludeExisting`
else
span=`git rev-list $oldrev..$newrev $excludeExisting`
fi
for COMMIT in $span;
do
for FILE in `git log -1 --name-only --pretty=format:'' $COMMIT`;
do
echo "rejecting all pushes"
exit 1
done
done
done
exit 0
Then I cloned the repo in my local Windows machine and tried pushing it. But it didnt create the intended effect. It still got pushed to server.
I'm new to Gitlab and Git Hooks. I don't know whether my pre-receive file is wrong or where I am going wrong. Please let me how to add hooks to server so that it validates/works for whoever cloned my repo.
Please help.
Thanks in advance.
See https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks which starts with:
Like many other Version Control Systems, Git has a way to fire off custom scripts when certain important actions occur. There are two groups of these hooks: client-side and server-side. Client-side hooks are triggered by operations such as committing and merging, while server-side hooks run on network operations such as receiving pushed commits. You can use these hooks for all sorts of reasons.
See also https://docs.gitlab.com/ee/administration/server_hooks.html

How to run a script on every commit in a github repo? [duplicate]

Let's say I have two scripts in server /hooks folder:
First one initiates logging and writes essential information about push: (post-receive)
#!/bin/sh
read oldrev newrev refname
LOGFILE=post-receive.log
echo " push - Old SHA: $oldrev -> $newrev >> $LOGFILE
sh ./post-receive-logic >> $LOGFILE
Second one does actual deploying: (post-receive-logic)
#!/bin/sh
cd ~/proj
pm2 stop ~/proj/main.js
git --git-dir ~/proj/.git --work-tree ~/proj pull
npm install
pm2 restart ~/proj/main.js
echo "finished"
When I push a commit, second script never gets called: no changes in working tree, no server being killed and restarted, no output specific to second script.
If I call ./post-receive-logic by hand, everything goes fine, server stops, files being pulled, server starts again.
I tried call it without sh, like this:
./post-receive-logic >> $LOGFILE
but no luck.
What am I doing wrong?
Exercise: where is . (or $PWD) during the post-receive hook operation?
When you run it, it's whatever your $PWD is. What about when it's run automatically? (Have a look in the receiving system's bare git repository: your $LOGFILE output will be in that directory.)
(There's a missing close quote in the hook text in your posting, so presumably you hand-copied some part(s) of the script and perhaps there's something else missing. Also, be sure the hook has execute-permission. But my guess is that you're being bitten by the fact that git runs hooks with $PWD set to the .git directory, not the hook directory.)
(Side note: your hook is probably incomplete, as it only reads one oldrev newrev refname, but a git push can push many refs. Normally you should loop: while read oldrev newrev refname; do ...; done. If you have a pre-receive hook that rejects pushes that push more than one ref, though, this particular post-receive hook could be correct.)

shell script to display overview of steps instead of printing all logs to console

i am writing script which does git release branch merge to master then develop branch and deletes current branch and creates new branch off of develop branch. which involves 4 steps
release branch to master
master to develop
delete current branch
create new branch off of develop
how to do in shell script which displays only 4 steps and step status instead of printing whole output in console.
My desired output is
release branch to master Pass
master to develop Pass
delete current branch Pass
create new branch off of develop Pass
SUCCESS
You could redirect stdout and stderr of each individual command to /dev/null or, better, to a log file, and only echo a description to your steps to the console, i.e.:
echo Deleting current branch
git branch -d fluffy 2>&1 >$LOG
if (($?==0)) then
echo passed
else
echo failed
fi
I personally would however do it like this:
echo Deleting current brach
git branch -d fluffy >$LOG
if (($?==0)) then
echo passed
fi
The reason is that when a git command fails, we usually do get error information on stderr, and in this case, we want to see the error message.
Another way (which I would not recommend, but still would fit your requirement) is to redirect the output of the whole script to a log file, and print your status messages explicitly to the console:
echo Deleting current branch >/dev/tty
This means, however, that this script can only be sensibly used interactively, i.e. when you do have a console.

Resources