Gitolite Configure post-push hook and perform operation on committed content - gitolite

I am using the Gitolite to create the git repo.
I have requirement that when user push something to repo it must have some specific file (eg. .md file) otherwise don't let push the code.
So now I need to configure a post-push hook and do some operation on pushed content.
Can any one please help me to do the same?

First, I would not recommend gitosis (old and obsolete).
Second, this is a job for a VREF (update hooks in Gitolite lingo).
You can use in that VREF a diff --name-only or diff --name-status:
git diff --name-only <sha-old> <sha-new>
(See shell explained)
If the list doesn't include your .md file, you would exit with a non-zero status, and the git push would be denied.

Related

How do I make git branch work when some git file or directory lost?

One day I change a vim configuration from Github(https://github.com/ma6174/vim) on Linux(Visualbox Ubuntu), then some git orders doesn't work.e.g.:
git diff
diff-so fancy | less --tabs = 4 RFX: 1: diff-so fancy | less -- tabs = 4 RFX: diff-so:not found 4: No such file or directory
RFX: No such file or directory
Also when I input git branch, git log, it's doesn't work.Just give me something wrong.
But I can use git add ,git commit -m "", git push to push my file to the remote repository.I also can create a new branch, while can't check the information about dev or master.
Please tell me how to let git work normally.

`ssh -vT git#github.com` works fine, and agent is in github ssh keys. Where is the issue?

I just bought a new machine, and I'm working on a new github repo. I clone it into my machine using
git clone git#github.com:<username>/<exact-repo-name>.git
and it clones fine:
Cloning into '<exact-repo-name>'...
remote: Counting objects: ###, done.
remote: Total ### (delta 0), reused 0 (delta 0), pack-reused ###
Receiving objects: 100% (###/###), ### KiB | 0 bytes/s, done.
Resolving deltas: 100% (###/###), done.
Checking connectivity... done.
And, I add all of the remote branches locally by doing:
for remote in git branch -r; do git checkout -b $remote; done
Then, when I try to pull from any of the branches, using
git pull origin/<branch-name>
I get the ever-so-common error:
origin/<branch-name> does not appear to be a git repository
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
So, I go through all of the steps:
ssh -vT git#github.com
ssh-add -l
eval "$(ssh-agent -s)"
These all succeed, and I check the output of ssh-add -l and I see that it is in the list ssh keys on my github account. I go into Settings -> ssh keys, and I see the agent there.
Are there certain user permissions that are on top of those for ssh?
I agree with #Felix that this most likely is not an SSH problem, though it might be masquerading as this. Instead, I think you have a problem with your path. You can try running git clone like this:
git clone https://username#github.com/username/exact-repo-name.git
Here the username is your GitHub username. Git will prompt you for a password so you don't have to enter it as plain text. Please read this SO article for more information.
It's probably not an ssh issue, as an ssh problem would normally result in a message like
fatal: The remote end hung up unexpectedly
Most likely it's a path problem: either you have an extra slash or space in the remote path (or if there's supposed to be a space, you're missing quotation marks in your command line), or some letter is the wrong case. Double-check the rest of the URL. It could also be exactly what it says, a permissions problem -- are you sure you're connecting as the right user for this repository?
Edit: from your added details, it looks like you're just getting the syntax of the pull command mixed up. Does it work if you do this?:
git checkout <branch-name>
git pull # Edit: don't do this without reading all of 'git help pull'
? Also, does git fetch --all work? (git pull is just git fetch followed by git merge.)
Further edit: I can reproduce your error; it is a command syntax problem. Here are more examples:
"source" is a git repository in a local folder; I clone it into a folder called "dest":
$ git clone source dest
$ cd dest
Now I do the git branch command in your for loop:
$ git branch -r
origin/HEAD -> origin/master
origin/branch1
origin/branch2
origin/master
I would expect that first line to cause problems, since that would result in these commands being run in your for loop:
git checkout -b "origin/HEAD"
git checkout -b "->"
...
(Note that your example is missing backticks around `git branch -r`, so if what you posted is literally what you're running, you'll end up with branches called "git", "branch", and "-r", which would really not be what you want... if you had trouble putting backticks into inline code blocks, see https://meta.stackexchange.com/questions/55437/how-can-the-backtick-character-be-included-in-code)
Then if I try your pull command, I get the same error as you:
$ git pull origin/branch1
fatal: 'origin/branch1' does not appear to be a git repository
fatal: The remote end hung up unexpectedly
This is because git pull takes the remote repository name (origin) and the remote branch name (branch1) as two separate parameters, separated by a space. So this works:
$ git pull origin branch1
From /tmp/source
* branch branch1 -> FETCH_HEAD
Already up-to-date.
But, that's probably not quite what you want, because it sounds like you wanted to create a bunch of local branches to track the same named branches on origin (e.g. you want a local branch named "branch1" that tracks "origin/branch1"). Your local branch creation commands didn't include setting up tracking, so what you've actually got is a local branch called "origin/branch1" and a branch on remote repository "origin" also called "branch1", i.e. after running your for loop, the output of git branch -a is:
$ git branch -a
master
origin/HEAD
origin/branch1
* origin/branch2
origin/master
remotes/origin/HEAD -> origin/master
remotes/origin/branch1
remotes/origin/branch2
remotes/origin/master
...So there are two branches called "origin/branch1", and the only thing that differentiates them is that in one case, the repository is your local one and the branch's literal full name is "origin/branch1", and in the other case, the repository is the remote one (named origin) and the branch's literal full name is "branch1", which is notated as "origin/branch1" -- this will probably be very confusing to work with (and in some commands, git will complain and say, "origin/branch1 is ambiguous" and you'll have problems).
Note also that each of the new branches your for loop created is actually just a copy of master (or whatever the default/selected branch was on origin), because you didn't specify a remote branch for them to start from. That is, in your clone command, your local repository was set up with one branch selected (probably master). You then told git "make new branch from where I am now" for each line in your for loop, so each of those new branches, despite having all the different names of the branches on remote, are references to the first branch selected with your clone. This is probably not what you wanted.
I think what you actually wanted was this command, run for each remote branch:
$ git checkout --track origin/branch1
Branch branch1 set up to track remote branch branch1 from origin.
Switched to a new branch 'branch1'
Actually, if you've just done a fresh clone, a git checkout branch1 [without the -b] will have the same effect I think -- since there is no local branch called branch1, git will look for one in the repository you cloned from and set up tracking if it finds one.
...So I guess what this whole post boils down to is:
Leave out the -b in your initial checkout commands
Use a space instead of a slash in your pull command
:)
See https://git-scm.com/book/en/v2/Git-Branching-Remote-Branches and the output of git help pull for more about remote tracking and pulling; some of it is a bit subtle (even more so than all the caveats I mentioned :) ).

How can we backup or clone GitoLite server on every commit

My firm is setting up Gitolite on Linux and we would like to setup a backup for the server just in case of a crash on every commit to a 2nd Linux server.
How can we backup a Gitolite server on every commit to it? Is anyone doing this?
Another way to generate backups is to ask your post-receive hook to create a bundle (a bit like in this question)
!/bin/sh
git bundle create "/path/to/backup/$(basename "$PWD").bundle" --branches --tags
This is based on the fact that hook runs in a bare repo: see "how to get project path in hook script post-commit?".
The interest in bundle and git bundle is that is generates only one file, which is easier to manage/copy around.
And that fact acts as a (mostly read-only) repo, meaning you can clone from that file.
This would work:
git clone myrepo.bundle myrepo
See also:
"git bundle: bundle tags and heads",
"Backup a Local Git Repository", and
"Git with Dropbox".
First you should not worry too much about git backups. - Everyone who is working in your project will have a complete clone on his box. - Hence more than enough backups. ;)
But you want to have another official repository updated after each push. In this case probably the easiest way is writing a small server side hook, that runs after each push and itself pushes the changes to the second repository.
You probably want to use a post-receive hook. For details have a look at here or here.
Example:
#create repositories
git init a
git init --bare b
git init --bare c
#add the hook in "b"
echo -e '#!/usr/bin/bash\nread old new ref\ngit push ../c $ref' >>b/hooks/post-receive
chmod +x b/hooks/post-receive
#create a commit in "a"
cd a
echo foo >test
git add .
git commit -m testcommit
#push it to "b"
git push ../b master
#notice the "remote:..." output of the hook
#find the commit in "c"
cd ../c
git log
This creates three repositories. When you have a commit in a and push it to b the hook will push it to c, too.

gitolite_admin hooks and mirroring

I'm wondering is there a simple way to install hooks for certain repo using gitolite_admin.
Let's imagine i want to have post-update hook for repo awesome using gitolite_admin repo cloned to my workstation...
#conf/gitolite_conf
repo awesome
RW+ = deployer
contents of post-update:
#!/bin/sh
echo "Post receive-hook => updating Redmine repository"
sudo -u deployer perl -we '`cd /home/deployer/repo/awesome.git && git fetch -q --all`'
You could also look at "repo-specific environment variables"
A special form of the option syntax can be used to set repo-specific environment variables that are visible to gitolite triggers and any git hooks you may install.
For example, let's say you installed a post-update hook that initiates a CI job. By default, of course, this hook will be active for all gitolite-managed repos. However, you only want it to run for some specific repos, say r1, r2, and r4.
To do that, first add this to the gitolite.conf:
repo r1 r2 r4
option ENV.CI = 1
This creates an environment variable called GL_OPTION_CI with the value 1, before any trigger or hook is invoked.
Note: option names must start with ENV., followed by a sequence of characters composed of alphas, numbers, and the underscore character.
Now the hook running the CI job can easily decide what to do:
# exit if $GL_OPTION_CI is not set
[ -z $GL_OPTION_CI ] && exit
... rest of CI job code as before ...
Of course you can also do the opposite; i.e. decide that the listed repos should not run the CI job but all other repos should:
repo #all
option ENV.CI = 1
repo r1 r2 r4
option ENV.CI = ""
That feature is fairly recent (started in commit 999f9cd39, but in this case, completed in commit 63865a16 June 2013 for 3.5.2).
But even you don't have that version, there are other ways to do this using option variables, as the last part of that section explains.
Before this feature was added, you could still do this, by using the gitolite git-config command inside the hook code to test for options and configs set for the repo, like:
if gitolite git-config -q reponame gitolite-options.option-name
then
...
And you can use git config variables in the same way.
Or you can use group membership -- see the comments against function "in_group" in "Easy.pm" for details.
# in_group()
# return true if $ENV{GL_USER} is set and is in the given group
# shell equivalent
# if gitolite list-memberships $GL_USER | grep -x $GROUPNAME >/dev/null; then ...
In addition to sitaram's answer, the recent (August 29th, 2013) commit 62fb31755a formerly introduce repo specific hooks:
it's basically just creating a symlink in <repo.git>/hooks pointing to some file inside $rc{LOCAL_CODE}/hooks/repo-specific (except the gitolite-admin repo)
You cannot specific a hook for gitolite-admin though.
And you hook is only one of the three following authorized hooks:
pre-receive
post-receive
post-update
That means you can:
store your repo specific hooks in your gitolite-admin/hooks/repo-specific/xx
declare those your in the gitolite-admin local options on the server.
First enable those hooks:
ENABLE => [
# allow repo-specific hooks to be added
# 'repo-specific-hooks',
Then declare the hooks on the server gitolite-admin repo:
gitolite git-config gitolite-options.hook=reponame hookname scriptname
(with a tab or \t between reponame hookname scriptname)
Original answer:
As mention in the gitolite man page on hooks
if you want to install a hook in only a few specific repositories, do it directly on the server.
(otherwise, you would be managing hooks for all git repos through gitolite-admin/common/hooks)
That being said, you could take advantage of VREF in gitolite V3.x, which are update hooks: those can be set for some repos and for some user, like any other rule.
You could then:
make your VREF script leave a 'flag' (a file) in the appropriate bare git repo being updated
make a common 'deploy' post-update hook, which would first look for that flag, and if found, deploy the repo (and remove the flag).
Again:
a post-update hook managed through gitolite-admin can only be common to all git repos (not what you want)
only VREFs can be associated to repos and users through gitolite.conf
The solution above tries to take those two facts into account to achieve what you are looking for: a deploy script running only for certain repos and managed through the gitolite.conf config file of the gitolite-admin repo.
Here are step by step instructions to complement #VonC's answer
gitolite hook for specific repository

Git merge branch of another remote

Nowadays, I see a lot of commits from Linus Torvalds and/or Gitster that look like so:
Merge branch 'maint' of git://github.com/git-l10n/git-po into maint …
* 'maint' of git://github.com/git-l10n/git-po:
l10n: de.po: fix a few minor typos
or:
Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upst… …
…ream-linus
Pull MIPS update from Ralf Baechle:
...
* 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus: (22 commits)
MIPS: SNI: Switch RM400 serial to SCCNXP driver
...
I have no idea how would one do that, although, I know about the git remote and git checkout and git merge that I use to merge forks (or "pull requests") but it does not generate such message, why? and how would someone do that (provide examples please)?
P.S: I'm a fan of Linus Torvalds commits etc, like how detailed the description of his merges look ;P
P.S: This is how I used to merge stuff:
git remote add anotherremoot
git checkout -b anotherbranch
git pull remoteshortcut
...do tests...
git checkout master
git merge anotherbranch
git push
The above example I learned from: http://viget.com/extend/i-have-a-pull-request-on-github-now-what
Just use git pull to pull the remote branch into master:
git remote add something git://host.example.com/path/to/repo.git
git checkout master
git pull something master
Or do it without adding a remote:
git pull git://host.example.com/path/to/repo.git
git pull fetches the remote branch, and merges it into the local branch. If possible, it does a fast-forward merge, which just means that it updates the current master to the latest commit without generating a merge commit. But if there are changes on both sides, it will do a merge, like the ones you see Linus and Junio doing, with the remote URL included.
If you want to guarantee that you get a merge commit, even if it could fast forward, do git pull -no-ff. If you want to make sure that pull never creates a merge commit (so fails if there are changes on both sides), do git pull --ff-only.
If you want to include a more detailed message, like the full log that Linus provides, do git pull --log. If you want to edit the message, instead of just using the automatically created message, use git pull --edit. See documentation for git pull for more options.

Resources