Can I show specific commits in git log without filter?

Answered

I'm creating a tool window that shows log UI views and I'd like to know if it's possible to customize which commits appear in the log without relying on the filter provided. The commits I'd like to show are obtained through a git log command using specific options and parameters that I can't replicate using the filter with the log UI.

Any help and tips appreciated!

0
2 comments
Official comment

Hi,

This is not going to be easy, as only a predetermined set of filters is currently supported by default. So you'd have to implement a new kind of log tab with a new kind of filters for this. This is definitely possible, this is roughly what you'd need:

1. A `VcsLogFilter` that defines your custom command filter.

2. A `VcsLogFilterUi` that represent the ui for editing your command. It does not have to actually edit anything, only to return the `VcsLogFilterCollection` with your filter.

3. A `VcsLogUi` that represents the ui of your custom tab. Basically this is the class that creates and connects all the components in the tab. The easiest would be to implement `VcsLogUiImpl` (the default log tab) and override `VcsLogUiImpl#createFilterUi`, but then your filter ui would have to implement `VcsLogFilterUiEx`.

4. A `VcsLogFilterer` that would actually run the git command, read hashes and create a `VisiblePack` with the filtered commits graph.

5. A `VcsLogUiFactory` that would create your `VcsLogUi` instance. The factory needs to be passed to `VcsLogManager.createLogUi` method to actually create the `VcsLogUi`. To use `VcsLogUi` instance you should wrap it into `VcsLogPanel` and place this panel to the component you're going to use it on.

6. In order to avoid memory leaks, created instance of `VcsLogUi` should be disposed with the `VcsLogManager`. To do, subscribe to `VcsProjectLog#VCS_PROJECT_LOG_CHANGED` topic and remove the component and dispose `VcsLogUi` in the `ProjectLogListener#logDisposed`.

As you can see, this requires some work to implement. Also I'd like to point out that some of the mentioned classes aren't intended to be used externally and may change in the future.

There is also a dumb hacky and limited solution. What you could do is run your git command, then create a `VcsLogHashFilter` from the hashes that the command returns. Then you can use `VcsLogManager#createLogUi` with the `MainVcsLogUi` instance, provided by `VcsLogManager#getMainLogUiFactory` to create a log ui with your hash filter.

The main disadvantage of the last solution is that the log tab won't be refreshed when new commits arrive as the hash filter has a fixed set of hashes. You'll have to rerun the command yourself and set another filter if you want to refresh it. The second disadvantage is that hashes are represented as strings, so the filter instance could take up a lot of memory if your command returns a lot of hashes. But you could manually limit the number of shown commits to some sane amount to overcome this problem.

Roughly, the code would look like this (but I don't recommend to use this approach):

// run git command in background, collect hashes
// val hashes = ...

invokeLater {
val hashFilter = VcsLogFilterObject.fromHashes(hashes)
// VcsProjectLog.runWhenLogIsReady could be used here to try to start log initialization if log is available but not ready yet
val logManager = VcsProjectLog.getInstance(project).logManager ?: return

val factory = logManager.getMainLogUiFactory("CustomLog${UUID.randomUUID()}", // logId should be unique
VcsLogFilterObject.collection(hashFilter))
// LogWindowKind.STANDALONE is used here as LogWindowKind.TOOL_WINDOW only supports "Git" toolwindow
val logUi = logManager.createLogUi(factory, VcsLogManager.LogWindowKind.STANDALONE)
// actions in the log tab wont work as VcsLogPanel acts as a data context
val panel = VcsLogPanel(logManager, logUi)

// add the panel to wherever it is needed

val connectionDisposable = Disposer.newDisposable()
project.getMessageBus().connect(connectionDisposable).subscribe(VcsProjectLog.VCS_PROJECT_LOG_CHANGED, object : ProjectLogListener {
override fun logCreated(manager: VcsLogManager) {}
override fun logDisposed(manager: VcsLogManager) {
Disposer.dispose(connectionDisposable)
// remove log panel from where it was placed
Disposer.dispose(logUi)
}
})
}

This is a very general description and I can explain everything in more detail if needed, so feel free to ask more questions. We are working on our api and looking at the real use cases would be a great help in designing it.

--

Julia

Hi Julia. Thanks for going through the steps needed to make this happen (nice to know that it's possible at least). Greatly appreciated. Will definitely come back here if I have any more questions.

0

Please sign in to leave a comment.