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

1,726 阅读1分钟

最近公司的代码仓库由gitlab切换到了gerrit,推送代码不能直接使用sourcetree自带的推送按钮了。看了下网上基本都是基于自定义操作来执行以下脚本,的确好使,不过就是每次操作有点累。

#!/bin/sh
# 获取当前分支名
branch=`git symbolic-ref --short -q HEAD`
# push review
git push origin HEAD:refs/for/${branch}

找到个大佬写的文章,好当给力。

在Sourcetree中的review code仓库页面中的Toolbar上加入几个按钮,替代CustomAction中的快捷键

还是放不下心爱的sourcetree,让我们来看看sourcetree的推送跟脚本的推送差别到底在哪里。

一般仓库推送时,是这样的,

git push origin refs/heads/main:refs/heads/main

可以看到sourcetree也是这样的推送 image.png 而gerrit推送时是这样的

git push origin HEAD:refs/for/main

如果能在sourcetree里替换下最后个参数就行。 让我直接上hook代码

#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();
    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 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];
        // 这里可以判断.git/hooks/commit-msg文件存在不
        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) {

// 这里我们可以直接添加上要指令review的人邮箱,不然每次都要在网页上选
                mutableArgs[mutableArgs.count - 1] = [NSString stringWithFormat:@"HEAD:refs/for/%@%@", branchName, @"%r=xing@run.com,r=xing@test.com"];
            } 
            
            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);
    }
}

@end

大概思路是:在STRepoWindowControllerwindowDidLoad时我们需要获取到仓库的路径。点推送时,我们判断下这个仓库是不是gerrit的,可以判断.git/hooks/下的目标文件存在不,也可以判断url包含gerrit等。如果是就把STTaskargs中的最后个参数换成目标字符串,如果不是还是走之前的逻辑。至此已经无感使用了

MonkeyDev创建的mac工程在m1上老是报错,就重新创建了个工程用的Aspects来hook,感觉还是挺好用的,dylib注入用的optool,也还能正常使用。 有时不能用修改二进制的方式注入,可以换个方式如

#!/bin/bash
DYLD_INSERT_LIBRARIES=$SCRIPT_DIR/TestMac/libTestMac.dylib /Applications/Sourcetree.app/Contents/MacOS/Sourcetree

关于定位到类的方法用的frida

# 跟踪方法
frida-trace -m "+[STTaskWindowController *]" "Sourcetree"
frida-trace -m "-[*STTask* *beginTasks*]" "Sourcetree"
#忽略 class
frida-trace -m "*[STTaskWindowController *]" "Sourcetree" -M "*[STTaskWindowController class]"