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

相关推荐
web1309332039810 分钟前
前端下载后端文件流,文件可以下载,但是打不开,显示“文件已损坏”的问题分析与解决方案
前端
outstanding木槿21 分钟前
react+antd的Table组件编辑单元格
前端·javascript·react.js·前端框架
好名字08211 小时前
前端取Content-Disposition中的filename字段与解码(vue)
前端·javascript·vue.js·前端框架
隐形喷火龙1 小时前
element ui--下拉根据拼音首字母过滤
前端·vue.js·ui
m0_748241121 小时前
Selenium之Web元素定位
前端·selenium·测试工具
风无雨1 小时前
react杂乱笔记(一)
前端·笔记·react.js
前端小魔女2 小时前
2024-我赚到自媒体第一桶金
前端·rust
鑫~阳2 小时前
快速建站(网站如何在自己的电脑里跑起来) 详细步骤 一
前端·内容管理系统cms
egekm_sefg2 小时前
webrtc学习----前端推流拉流,局域网socket版,一对多
前端·学习·webrtc
m0_748234342 小时前
前端工作中问题点拆分
前端