I use git-svn to interact with an existing SVN repository that contains some C++ projects. subwcrev.exe is used as a pre-build event to update some strings in a C++ header (svnversion.h). These strings get hardcompiled to form some version information for the resulting binary.
Since subwcrev requires .svn metadata to work, the pre-build event is going to fail when used on the git-svn working copy. So I came up with the following bash script which I use as post-commit and post-checkout hooks for my git repository. The script tries to do the same thing as subwcrev based on the output of git svn info (cached in a local file).
#!/bin/sh
if [ ! -f svninfo ] ; then
git svn info > svninfo
fi
revision=`sed -e "/Revision/!d" -e "s/Revision: \(.*\)/\1/" svninfo`
lastchange=`sed -e "/Last Changed Rev/!d" -e "s/Last Changed Rev: \(.*\)/\1/" svninfo`
# Get the last changed date, extract timestamp, replaces dashes with slashes
changedate=`sed -e "/Last Changed Date/!d" -e "s/Last Changed Date: \(.\{19\}\).*/\1/" -e "s!-!\\\\\\/!g" svninfo`
now=`date "+%Y\/%m\/%d %H:%M:%S"`
gitcommit=`git show --abbrev-commit | sed -n -e "s/commit //p"`
for entry in $( find -name svnversion_template.h ); do
newname=`echo $entry|sed -e "s/_template//"`
sed -e "s/\\\$WCRANGE\\\$/${revision}/" \
-e "s/\\\$WCREV\\\$/${lastchange}-${gitcommit}/" \
-e "s/\\\$WCDATE\\\$/${changedate}/" \
-e "s/\\\$WCNOW\\\$/${now}/" \
-e "s/\\\$WCURL\\\$/local git repo/" \
-e "s/\\\$WCMODS.*\\\$/(true)/" \
-e "s/\\\$WCMIXED.*\\\$/(false)/" \
$entry > `echo $entry|sed -e "s/_template//"`
done
What I cannot really emulate so far is the automatic detection of a local uncommitted changes (based on the last checked out SVN revision) that makes subwcrev so useful.
I am replacing $WCREV$ with the revision number of the SVN repository (as subwcrev would do) but this time I add my abbreviated git commit hash to identify the code I compiled. My question now is: Is there a way to distinguish in a shell script whether my current HEAD differs from the last fetched SVN revision so that I could omit adding the -${gitcommit} part and set $WCMODS$ to false?
If there were some thing like a post-"git svn dcommit" hook, my problem would be solved, too, since then that special hook would create the svnversion.h differently. Can such hook be added somehow?
I don't really get your points, but start to improve your script first.
revision=$(grep -Po "(?<=Revision: ).*" svninfo)
lastchange=$(grep -Po "(?<=Last Changed Rev: ).*" svninfo)
# Get the last changed date, extract timestamp, replaces dashes with slashes
changedate=$(grep -Po "(?<=Last Changed Date: ).{19}" svninfo)
changedate=${changedate//-//}
now=$(date "+%Y\/%m\/%d %H:%M:%S")
Then, in for loop, could you please explain detail, what result you need?
Can you show one sample of svnversion_template.h ?
So it looks like you might have to parse the contents of the git svn info query yourself to get what is normally stored in WCREV. The example results look like this for me:
git svn info
Path: .
URL: http://myurl.com/trunk/myrepo
Repository Root: http://myurl.com
Repository UUID: 15fed3e9-81ce-ef4a-a7da-fc36e3df1edc
Revision: 14106
Node Kind: directory
Schedule: normal
Last Changed Author: myusername
Last Changed Rev: 14106
Last Changed Date: 2015-05-29 10:23:10 -0400 (Fri, 29 May 2015)
Now for the second part of your question, whether or not you can tell if your git HEAD matches the latest svn checkout, you'll need to use the command git diff git-svn command. "git-svn" here is the name of the branch that the git-svn program is maintaining, and if everything is up to date, the results will be empty.
Related
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.
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
I'm making a Pre-Receive Hook on BitBucket that is supposed to confirm that all pushes made in a branch are up-to-date with parent Branches.
I mean, in a temporal evolution, we have several branches creations:
Branch creation during time
With the above example os 3 branches, Dev, Feature1, and my Local, i want to, before making push of Local to remote/origins/Feature1, make git merge from the latest Feature1 with the recent on-push Local Code. In this way, i can confirm that, whoever is making the push, is using the latest version of feature1, and there will be no conflict.
If it were any conflict, i would return 1, to avoid making the push! and obligate the Developer to pull from Feature before push is code.
This is my script on Pre-Receive Hook.
while read from_ref to_ref ref_name; do
echo "Ref update:"
echo " Old value: $from_ref"
echo " New value: $to_ref"
echo " Ref name: $ref_name"
echo " Diff:"
git clone --progress -v $GIT_URL $CLONE_DIR1
cd $CLONE_DIR1
git checkout -b test remotes/origin/Feature1
git merge --no-commit -m "Merging feature with local on-push code" $ref_name
(....)
done
I've tried with ref_name, to_ref, and having no success.
Anyone can help me?
How can I access the recent pushed code, and merge by parent branch with this code?
This seems like a very odd thing to do, and it is probably doomed to failure. It will certainly be complicated and you will want to change your test behavior based on which ref(s) are being updated and whether these update add merge commit(s).
That said, there are some special rules for pre-receive and update hooks, and if you obey them you will get somewhat further:
Do not chdir or cd away from the current directory. Or, if you do, make sure you chdir back, but usually it's not too difficult to make sure that operations that must run in another directory, run as a separate process: either a sub-shell, or another script.
Remove $GIT_DIR from the environment before attempting git commands that need to use a different repository. The reason is that the hook is run in the top level directory with $GIT_DIR set to either .git (non-bare repo) or . (bare repository).
Putting those two together, you might move all your verifier code into a separate script and do something like this:
exitstatus=0
while read from_ref to_ref ref_name; do
... maybe some setup code here to see if $ref_name
is being created or destroyed ...'
case "$ref_name" in
... add cases as needed to choose action based on ref ...
if (unset GIT_DIR; /path/to/check_script arg1 arg2 ...); then
echo "push being rejected because ..."
exitstatus=1
fi
...
esac
...
done
exit $exitstatus
There is still one very big problem here. You want check_script to be able to access any proposed new commits that would become reachable from $ref_name if the hook script exits 0 so that the proposed update to it is allowed. That update has not yet occurred: $ref_name still points to the old SHA-1 $from_ref. Meanwhile, the new SHA-1 in $to_ref might not have any name pointing to it (though it does still exist in the underlying repository).
Among other things, if $to_ref points to new commits (the usual case), any clone you make at this point, via normal git operations, will not contain those commits, so you will not be able to use them.
There are two obvious ways to handle this:
Make a new (temporary) reference that points to $to_ref. You can then see the proposed commits in the clone.
Don't use a clone. Copy the repository some other way, or use the original repository itself directly, e.g., as an "alternate", or by creating a temporary work tree directory and pointing $GIT_WORK_TREE there, or using some of the new git worktree features that have appeared in git 2.6+. (If you choose the manual temporary work-tree method, be sure to think about the normal shared $GIT_INDEX_FILE as well.)
Remember also to check for forced pushes that remove commits from a branch, or even remove-some-and-add-others all in one push.
UPDATE:
This question is resolved for me.
The final code is this:
#!/bin/bash
DIR=xpto/external-hooks/code_review
CLONE_DIR=$DIR/test_conflict_push-$(date +%s)
GIT_URL=myGitUrl
exitStatus=0
read oldrev newrev refname
feature_branch=${refname##refs/heads/}
echo "Feature branch-> $feature_branch"
#Clone feature branch from remote repo to be update via merged.
git clone --progress -v $GIT_URL $CLONE_DIR
currentDir=$PWD
cd $CLONE_DIR
#create branch named 'latest' to put new and modify files
git checkout -b latest remotes/origin/$feature_branch
#go back to PWD otherwise cant make git diff
cd $currentDir
# Get the file names, without directory, of the files that have been modified
# between the new revision and the old revision
echo "Getting files"
files=`git diff --name-only ${oldrev} ${newrev}`
echo "Files -> $files"
# Get a list of all objects in the new revision
echo "Getting objects"
objects=`git ls-tree --full-name -r ${newrev}`
echo "objects -> $objects"
# Iterate over each of these files
for file in ${files}; do
# Search for the file name in the list of all objects
object=`echo -e "${objects}" | egrep "(\s)${file}\$" | awk '{ print $3 }'`
# If it's not present, then continue to the the next itteration
if [ -z ${object} ];
then
continue;
fi
# Otherwise, create all the necessary sub directories in the new temp directory
mkdir -p "${CLONE_DIR}/`dirname ${file}`" &>/dev/null
# and output the object content into it's original file name
git cat-file blob ${object} > ${CLONE_DIR}/${file}
done;
echo "Ready for start merging."
cd $CLONE_DIR
#add new files to branch
echo $(git add .)
#commit added and modify files to branch
echo $(git commit -a -m "Merge latest to original feature")
#get generated commit id
echo $(git log -1)
#create branch named 'merged' to merge above commited files
echo $(git checkout -b merged remotes/origin/$feature_branch)
#merge only occurs for madded and modify files!
echo "Merging committed files to 'merged' branch with from 'latest' branch."
mergeResult=$(git merge --no-commit latest)
echo "Merge Result -> $mergeResult"
##to lower case
if [[ "${mergeResult,,}" == *"conflict"* ]]
then
echo "Merge contains conflicts."
echo "Update your $feature_branch branch!"
exitStatus=1
else
echo "Merge don't contains conflict."
echo "Push to $feature_branch can proceed."
exitStatus=0
fi
#remove temporary branches
echo $(git checkout master)
echo $(git branch -D latest)
echo $(git branch -D merged)
#delete temporary clone dir
rm -rf $CLONE_DIR
exit $exitStatus
Many Thanks.
I have to accept or not accept the commit on a particular repository based on the comments with the commit (using hooks). I don't know how to do it. I have to do it on a Windows device. I read somewhere that I should modify the pre-commit.tmpl file to accept just that word as the commit so I did modify this statement:
SVNLOOK=/usr/local/bin/svnlook
$SVNLOOK log -t "$TXN" "$REPOS" | \
grep ""[a-zA-Z0-9]"" > /dev/null || exit 1
into this:
SVNLOOK=/usr/local/bin/svnlook
$SVNLOOK log -t "$TXN" "$REPOS" | \
grep "^.*hello.*$" > /dev/null || exit 1
Also, it says to change the .tmpl extension for windows. But I don't know if a grep search is right also, what is the other alternative to doing the same task?
The exampels inside the .tmpl files are made for unix and using unix commands. You need to install the appropriate unix tools and adapt the scripts to your architecture(modifying paths etc..)
On windows you also need to rename the file to .bat so it is executable.
Note that no environment variables are available in hook scripts.
I would recommend to use python as a platform independent way of providing hook scripts. There are tons of python hook scripts available.
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