最近代码仓库由gitlab切换到了gerrit,推送代码不能直接使用sourcetree自带的推送按钮了。看了下网上基本都是基于自定义操作来执行以下脚本,的确好使,不过就是每次操作有点累。
sh
#!/bin/sh
# 获取当前分支名
branch=`git symbolic-ref --short -q HEAD`
# push review
git push origin HEAD:refs/for/${branch}
找到个大佬写的文章,好当给力。
在Sourcetree中的review code仓库页面中的Toolbar上加入几个按钮,替代CustomAction中的快捷键
基于此,我有一些新的想法:
1.改造终端事件:我想在点终端时,如果是gerrit仓库就再追加执行脚本命令。
2.改造推送事件,如果是gerrit仓库就使用新命令。
一般仓库推送时,是这样的
sh
git push origin refs/heads/main:refs/heads/main
而gerrit推送时是这样的
sh
git push origin HEAD:refs/for/main
只要替换下最后个参数就行。 让我直接上hook代码
objc
#import "TestMac.h"
#import "Aspects.h"
@import Cocoa;
@interface TestMac()
@property(nonatomic, copy) NSString *repoPath;
@end
@implementation TestMac
static TestMac *testMacObj;
+(void)load {
NSLog(@"插件注入成功");
testMacObj = [TestMac new];
hookSTRepoWindowControllerWindowDidLoad();
hookNSToolbarItemSetAction(testMacObj);
hookSTTaskInitWithCommand();
}
void hookSTRepoWindowControllerWindowDidLoad(void) {
[NSClassFromString(@"STRepoWindowController") aspect_hookSelector:@selector(windowDidLoad) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo){
NSString *repoPath = [aspectInfo.instance performSelector:@selector(repoPath)];
if ([repoPath isKindOfClass:[NSString class]] && repoPath.length > 0) {
// 这里我们存下仓库路径
testMacObj.repoPath = repoPath;
}
} error: nil];
}
void hookNSToolbarItemSetAction(TestMac *obj) {
NSError *error = nil;
[NSToolbarItem aspect_hookSelector:@selector(setAction:)
withOptions:AspectPositionInstead
usingBlock:^(id<AspectInfo> aspectInfo, SEL action) {
NSToolbarItem *toolbarItem = aspectInfo.instance;
if ([NSStringFromSelector(action) containsString:@"openTerminal"] && ![toolbarItem.target isEqualTo:obj]) {
// 这里我们重置打开终端的target和action
toolbarItem.target = obj;
toolbarItem.action = @selector(openTerminal:);
} else {
[aspectInfo.originalInvocation invoke];
}
} error:&error];
if (error) {
NSLog(@"Error hookNSToolbarItemSetAction: %@", error);
}
}
- (void)openTerminal:(id)fff {
NSString *msgFilePath = [NSString stringWithFormat:@"%@/%@", testMacObj.repoPath, @".git/hooks/commit-msg"];
BOOL isGerrit = [[NSFileManager defaultManager] fileExistsAtPath:msgFilePath];
NSString *command = [NSString stringWithFormat:@"cd %@;", testMacObj.repoPath];
if (isGerrit) {
NSString *shellScriptPath = @"~/gerritPush.sh";
command = [NSString stringWithFormat:@"%@ sh %@", command, shellScriptPath];
}
NSString *scriptSource = [NSString stringWithFormat:@"tell application \"Terminal\"\n"
" activate\n"
" delay 0.25\n"
" do script \"%@\"\n"
"end tell\n", command];
NSAppleScript *appleScript = [[NSAppleScript alloc] initWithSource:scriptSource];
NSDictionary *errorInfo = nil;
NSAppleEventDescriptor *result = [appleScript executeAndReturnError:&errorInfo];
if (!result) {
showMsg([errorInfo objectForKey:NSAppleScriptErrorMessage]);
return;
}
}
void hookSTTaskInitWithCommand(void) {
NSError *error = nil;
[NSClassFromString(@"STTask") aspect_hookSelector:@selector(initWithCommand:workingDir:args:)
withOptions:AspectPositionInstead
usingBlock:^(id<AspectInfo> aspectInfo, NSString *command, NSString *workingDir, NSArray *args) {
NSString *msgFilePath = [NSString stringWithFormat:@"%@/%@", testMacObj.repoPath, @".git/hooks/commit-msg"];
BOOL isGerrit = [[NSFileManager defaultManager] fileExistsAtPath:msgFilePath];
if (!isGerrit) {
[aspectInfo.originalInvocation invoke];
return;
}
BOOL showChange = NO;
NSString *lastCMD = args.lastObject;
NSString *branchName = @"";
if ([lastCMD isKindOfClass:[NSString class]]) {
if ([lastCMD hasPrefix:@"refs/heads"]) {
showChange = YES;
NSArray *components = [[[lastCMD componentsSeparatedByString:@":"] firstObject] componentsSeparatedByString:@"/"];
branchName = components.lastObject;
}
} else {
NSLog(@"类型不是字符串: %@", NSStringFromClass([lastCMD class]));
}
if (showChange && branchName.length > 0) {
NSMutableArray *mutableArgs = [args mutableCopy];
if (mutableArgs.count > 0) {
mutableArgs[mutableArgs.count - 1] = [NSString stringWithFormat:@"HEAD:refs/for/%@", branchName];
}
id<AspectInfo> strongAspectInfo = aspectInfo;
SEL selector = aspectInfo.originalInvocation.selector;
NSInvocation *invocation = strongAspectInfo.originalInvocation;
[invocation setSelector:selector];
[invocation setArgument:&mutableArgs atIndex:4];
[invocation invoke];
} else {
[aspectInfo.originalInvocation invoke];
}
} error:&error];
if (error) {
NSLog(@"hook STTask initWithCommand 错误: %@", error);
}
}
void showMsg(NSString *msg) {
NSAlert *alert = [[NSAlert alloc] init];
alert.messageText = @"日志";
alert.informativeText = msg;
[alert addButtonWithTitle:@"OK"];
[alert beginSheetModalForWindow:[[NSApplication sharedApplication] mainWindow] completionHandler:nil];
}
@end
大概思路是:在STRepoWindowController
的windowDidLoad
时我们需要获取到仓库的路径,然后我们重置打开终端NSToolbarItem
的的target
和action
。点推送时,我们重新设置下最后个参数值就是如果是gerrit就替换下目标字符串就是。MonkeyDev
创建的mac工程在m1上老是报错,就重新创建了个工程用的Aspects
来hook,感觉还是挺好用的,dylib注入用的optool
,也还能正常使用。