无感顺滑地使用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,也还能正常使用。

相关推荐
森叶37 分钟前
利用本地 Express Web 服务解决复杂的 Electron 通信链路的问题
前端·electron·express
冴羽1 小时前
SvelteKit 最新中文文档教程(18)—— 浅层路由和 Packaging
前端·javascript·svelte
哀木1 小时前
聊聊前端埋点 clarity:我会一直视监你... 永远...
前端
墨渊君2 小时前
Expo 入门指南:让 React Native 开发更轻松(含环境搭建)
前端·javascript·react native
xnian_2 小时前
策略模式实际用处,改吧改吧直接用,两种方式
java·服务器·前端
31535669132 小时前
一文带你了解二维码扫码的全部用途
前端·后端
七月shi人2 小时前
用claude3.7,不到1天写了一个工具小程序(11个工具6个游戏)
前端·小程序·ai编程
Billy Qin2 小时前
Rollup详解
前端·javascript·rollup
夜寒花碎3 小时前
前端自动化测试一jest基础使用
前端·单元测试·jest
小徐_23333 小时前
uni-app工程实战:基于vue-i18n和i18n-ally的国际化方案
前端·微信小程序·uni-app