I’ve been recently writing a pre hook for Git to check if any of the committed files violate any policies. I’m using GitBlit and it’s using JGit (an implementation of Git written in Java). You can’t use normal Git pre hooks with GitBlit, but you can write them in Groovy.
GitBlit’s author created an API to ease the use of JGit in pre hooks. I needed to detect if a file was renamed, and have access to its old path, as well as the new one.
Unfortunately, the documentation of PathChangeModel
(which corresponds to a file in a commit) doesn’t state how to obtain the old path – the renamed file object’s path
property contains the new path.
Luckily, it’s open source! I’ve had a look into the source code of the mentioned class and it turned out that, in case of a rename
, the old path is stored in the name
property. Take a look at the following example.
Let’s:
- Create a new repository and:
- add a file and commit,
- push it to the GitBlit server,
- rename the file and commit (but don’t push it yet).
- Define a new pre hook for Git Blit which will print information about committed files and configure the pre hook for our repository.
- Push the commit and check out what our pre hook will tell us.
New repository:
$ mkdir RenamePreHookTest $ cd RenamePreHookTest/ $ git init Initialized empty Git repository in g:/git/RenamePreHookTest/.git/
Adding a file, committing it and pushing the new repository to origin:
$ echo test > a.txt $ git add a.txt warning: LF will be replaced by CRLF in a.txt. The file will have its original line endings in your working directory. $ git commit -m "Initial commit." [master (root-commit) 55add25] Initial commit. warning: LF will be replaced by CRLF in a.txt. The file will have its original line endings in your working directory. 1 file changed, 1 insertion(+) create mode 100644 a.txt $ git remote add origin http://user:pass@localhost/r/RenamePreHookTest.git $ git push origin master Counting objects: 3, done. Writing objects: 100% (3/3), 216 bytes | 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) remote: Updating references: 100% (1/1) To http://user:pass@localhost/r/RenamePreHookTest.git * [new branch] master -> master
Rename the file:
$ git mv a.txt b.txt $ git commit -m "Renaming the file" [master 253a337] Renaming the file warning: LF will be replaced by CRLF in b.txt. The file will have its original line endings in your working directory. 1 file changed, 0 insertions(+), 0 deletions(-) rename a.txt => b.txt (100%)
Now, we create a new script with the pre hook called rename_test.groovy
– it should be put in the GitBlit’s data/groovy
directory:
import com.gitblit.utils.JGitUtils repository = gitblit.getRepository(repository.name) for (command in commands) { // fetch all pushed commits def commits = JGitUtils.getRevLog(repository, command.oldId.name, command.newId.name).reverse() commits.each { commit -> // fetch all changed files in the commit def files = JGitUtils.getFilesInCommit(repository, commit) for (file in files) { println "Change type: ${file.changeType}\n" + "File's path property: ${file.path}\n" + "File's name property: ${file.name}" } } }
The code is fairly simple – we obtain the repository object, loop through each command and then through each commit, and, finally, we print the change type along with the path
and name
properties of each file.
Now we have to configure the pre hook for our repository – to do that, we add the following line to the file named config
placed in GitBlit’s bare copy of our repository stored in data\git\RenamePreHookTest.git
(just to be clear – not in your directory with the repository, but in the GitBlit’s one):
preReceiveScript = rename_test.groovy
Finally, we push the commit which renames the file:
git push origin master
This is the output produced by our pre hook:
2016-01-06 22:39:27 [INFO ] ARF: RenamePreHookTest.git/info/refs?service=git-receive-pack (100) authenticated 2016-01-06 22:39:27 [INFO ] ARF: RenamePreHookTest.git/git-receive-pack (100) authenticated Change type: RENAME File's path property: b.txt File's name property: a.txt 2016-01-06 22:39:27 [INFO ] admin UPDATED refs/heads/master in RenamePreHookTest.git (from a7e951b58c2168b2d826b562818024bd3ae6997e to e33486ddfaa65661c692fb19a8dbc65ae9c2130e)
As you may see, the new path is stored in the path
property, and the old one in the name
property.
Have fun writing GitBlit’s pre hooks!