无感顺滑地使用Sourcetree推送code review 到gerrit

最近代码仓库由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

大概思路是:在STRepoWindowControllerwindowDidLoad时我们需要获取到仓库的路径,然后我们重置打开终端NSToolbarItem的的targetaction。点推送时,我们重新设置下最后个参数值就是如果是gerrit就替换下目标字符串就是。MonkeyDev创建的mac工程在m1上老是报错,就重新创建了个工程用的Aspects来hook,感觉还是挺好用的,dylib注入用的optool,也还能正常使用。

相关推荐
CHH54319 分钟前
3D立体卡片动效(附源码)
前端·css·3d·玩转css
戏拈秃笔10 分钟前
前端——在本地搭建Vue单页应用
前端·javascript·vue.js
Elena_Lucky_baby11 分钟前
vue项目创建+eslint+Prettier+git提交规范(commitizen+hooks+husk)
前端·javascript·vue.js
m0_6442226125 分钟前
HarmonyOS开发探索:自定义键盘-webview
前端·华为·计算机外设·移动开发·harmonyos·鸿蒙开发
铁匠匠匠37 分钟前
django学习入门系列之第三点《案例 小米商城二级菜单》
前端·css·经验分享·python·学习·django·前端框架
呀呀呀雅楠?38 分钟前
vue3(nuxt3)中使用ref获取不到子组件变量
前端·javascript·vue.js·nuxtjs
一颗苹果OMG1 小时前
三栏布局,到底哪种最好用呢
前端·css
小秦学加瓦chen1 小时前
使用docker创建nginx容器部署前端项目
前端·nginx·docker
小怪瘦791 小时前
Vue3使用PDFJS将后端查到的二进制数据转为图片
java·前端·javascript
m0_742887731 小时前
分享一个超级实用的东西——巴比达远程访问
前端·巴比达·免费内网穿透