Missed Team ’24? Catch up on announcements here.

×
Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

verifying commiter and content of a change in repository hook

Michael Schnupp January 20, 2014

A repository hook get a "refChanges" argument. With the help of "historyService" it is possible to list the changes involved in the push. Each change is a com.atlassian.stash.content.Changeset, which provides very basic informion about that change. It provides access to the author and the parents, but not to the committer and the actual change.

How do I get access to the committer and the actual change of a given change/commit from inside a pre receive repository hook?

What is the correct documentation I should read?

(I already found ScmService and GitCommandBuilderSupport. They look kind of promising, but I could not find out how to use them.)

3 answers

1 accepted

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

0 votes
Answer accepted
cofarrell
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 20, 2014

Hi Michael,

Apologies, I didn't read your question properly (at all apparently). You want the _Git_ committer, which is definitely stored but just not available. As you've hinted at you will unfortunately have to use one of the low-level Builders to retrieve that information by calling revList() and manually:

// Injected into the constructor
private GitCommandBuilderFactory gitBuilderFactory;

...
gitBuilderFactory.builder(repository).revList().build()
    .format("TODO")
    // Add your flags here
    .build(new MyCommandOutputHandler() {
        // TODO Parse the lines
    }).call();

And follow the types in your IDE is probably the best bet at that point. If you have access to the Stash source I would recommend looking at com.atlassian.stash.scm.git.common.ChangesetReader.

The fact that we don't parse the commits is baked into the very low levels of Stash I'm afraid and there isn't any configuration. You will have to parse the output of 'git rev-list' manually yourself. :(

I might see if this something we can make easier, but I can't promise anything.

Regarding the files themselves, you can use HistoryService.getDetailedChangeset() quite happily after figuring out which specific changesets you are interested in. This is a plugin that I wrote that checks the file size of a push, which might be useful?

https://bitbucket.org/atlassianlabs/stash-filesize-hook-plugin/src/b520fe77a2a5a2596d559f3358607721bcf6398a/src/main/java/com/atlassian/stash/plugin/filesize/FilesizeHook.java?at=master

Cheers,

Charles

Michael Schnupp January 22, 2014

Could you give an example for a suitable output handler? (I tried to find an example in the source by myself, but it is a pain without IDE support.)

cofarrell
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 22, 2014

Hi Michael,

You will want to look in the :

plugins/scm-git/src/main/java/com/atlassian/stash/internal/scm/git/command/revlist folder for the relevant code.

How important is the commiter (and not just author) to your plugin by the way?

Charles

Michael Schnupp January 22, 2014

Well in many cases author and commiter are the same, therefore it is fine to just check the author. But in case they are different usually the committer is the more important one, as the author only wrote the change, whereas the committer added it to the branch and is therefore responsible for that change. - I was a bit surprised to see the one available and the other missing. (Especially as they are treated equally at the lower layers.)
The plugin should make sure that all commits contain correct names and email addresses, i.e. there should be a matching entry in crowd.

cofarrell
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 22, 2014

Thanks Michael, I was just curious.

Let me know if you get stuck on the OutputHandler.

Michael Schnupp January 27, 2014

This really gets nasty... I probably need something like that:

private Person getCommitter(Changeset changeset) {
		return gitBuilderFactory.builder(changeset.getRepository()).revList()
				.format("%cn%x02%ce").limit(1).rev(changeset.getId())
				.build(new CommitterHandler()).call();
	}

	class MyPerson implements Person {
		private String name;
		private String email;

		public MyPerson(String name, String email) {
			this.name = name;
			this.email = email;
		}

		@Override
		public String getEmailAddress() {
			return email;
		}

		@Override
		public String getName() {
			return name;
		}
	}

	class CommitterHandler implements CommandOutputHandler<Person> {
		private Person committer;

		@Override
		public void process(InputStream output) throws ProcessException {
			BufferedReader reader = new BufferedReader(new InputStreamReader(
					output));
			try {
				String line = reader.readLine();
				if (line == null) {
					return;
				}
				if (!line.startsWith("commit")) {
					throw new IllegalStateException( "[" + line + "]: Unexpected output; expected a 'commit' object");
				}
				line = reader.readLine();
				if (line == null) {
					throw new IllegalStateException("Unexpected end of output; no changeset details were present");
				}
				String[] pieces = StringUtils.splitPreserveAllTokens(line, '\u0002');
				if (pieces.length != 2) {
					throw new IllegalStateException("Unexpected number of pieces found.");
				}
				committer=new MyPerson(pieces[0],pieces[1]);
			} catch (IOException e) {
				throw new IllegalStateException("read error.");
			}
		}

		@Override
		@Nullable
		public Person getOutput() {
			return committer;
		}

		@Override
		public void complete() throws ProcessException {}

		@Override
		public void setWatchdog(Watchdog watchdog) {}
	}

This reimplements much of the existing stash code. A better API would really be great.

Michael Schnupp January 28, 2014
BTW, why do I need to explicitly use <component-import> for GitCommandBuilderFactory, but don't need this for HistoryService?
cofarrell
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 28, 2014

Positive. Have you imported the component?

<component-import key="gitCommandBuilderFactory" interface="com.atlassian.stash.scm.git.GitCommandBuilderFactory"/>

I'm actually not sure what the best way to find a comprehensive list of components.

Any stateless interfaces in our API javadocs are probably a fairly good bet.

https://developer.atlassian.com/stash/docs/latest/reference/java-api.html

https://developer.atlassian.com/static/javadoc/stash/latest/scm-git-api/reference/com/atlassian/stash/scm/git/GitCommandBuilderFactory.html

In the case of the Git plugin, if you have the source look at plugins/scm-git/src/main/resources/META-INF/pring/atlassian-plugins-components.xml and anything that is exported as osgi:service. In other cases it's normally in the relevant atlassian-plugin.xml as:

<component class="..." public="true" />

I know that isn't ideal, and I'll talk to the team internally to see if there is a better way to discover them.

Charles

Michael Schnupp January 28, 2014
Oh, I see.
Why do I need to explicitly use <component
-import> for GitCommandBuilderFactory, but don't need this for HistoryService?
Michael Schnupp January 28, 2014

(oops, just rewrote my comment in the same minute you added your answer.)

BTW, why do I need to explicitly use <component-import> for GitCommandBuilderFactory, but don't need this for HistoryService?

With the component properly imported my plugin seems to work fine. Does the above code make sense to you? Do you have any ideas for improvements?

cofarrell
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 28, 2014

Basically anything that's exported to OSGI via the webapp doesn't need to be imported. In Stash's case that's anything in the API/SPI modules.

0 votes
Sarp Kaya March 8, 2015

Sorry to bump, have you found how to use them?

0 votes
cofarrell
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 20, 2014

Hi Michael,

EDIT: Please IGNORE this comment - it doesn't answer the question at all...

I'm sorry but we don't store anything about who made the push yet, there really isn't any way to retrieve that information from Git. We are definitely going to add this functionality, but I couldn't say when.

At the moment the only option would be to write a post-receive hook and store that information yourself.

You might be interested in the following:

https://jira.atlassian.com/browse/STASH-2715

https://jira.atlassian.com/browse/STASH-2852

Cheers,

Charles

TAGS
AUG Leaders

Atlassian Community Events