실제로 작업을 하다보면 저장소 특정파일의 특정부위를 누가 언제 왜 수정했는지,

커밋 메세지를 확인하거나 물어봐야 하는 경우가 생길 수 있으므로, 

hg annotate를 빈번하게 사용하게 된다.


근데 이 때, 

TortoiseHg같은 GUI 프로그램이든 콘솔창이든 따로 프로그램을 실행시킨 다음 파일내용 전체에 대한 annotate를 뒤적거려야 하는 등 조금은 귀찮음이 있다.

(물론 grep이나 findstr 등을 이용해서 스마트하게 할 수도 있긴 하지만 이것도 타이핑이 귀찮음)


내가 하고 싶은 건,

에디터에서 단축키를 누르면 현재 파일의 현재 라인 위아래로 몇라인 정도 hg annotate 정보(누가,언제)가 나오도록 하는 것이었다.



[조건]

1. 에디터에서 외부 프로그램 실행 시키는 기능이 있어야 함. 

    - EditPlus의 사용자 도구나 Eclipse의 Externel Tools 같은.

2. 외부 프로그램 실행 시킬 때 파일경로/현재라인번호를 args로 넘겨줄 수 있어야 함.

    - EditPlus의 경우엔 문제 없는데 Eclipse의 경우 현재라인번호를 넘겨줄 수 있도록 변수가 제공되지 않음...;

3. 외부 프로그램은 hg 커맨드를 실행시킬 수 있는것이면 뭐든 OK.



[순서]

1. 에디터에서 외부 프로그램 실행 -> 이때 args로 파일경로/현재라인번호를 넘겨줌

2. 외부 프로그램이 넘겨받은 파일경로 정보로 hg annotate 실행. 결과 문자열에서 현재라인번호 앞뒤로 몇라인 정도 잘라서 출력.

3. 출력 정보를 에디터 내부의 콘솔창 등으로 받아 뿌려줌.


에디터는 EditPlus를 선택했고, (Eclipse는 args로 현재라인번호를 넘겨줄 수가 없다!)
외부 프로그램은 그냥 PHP 스크립트 파일로 만들었음.



[EditPlus 사용자 도구 설정]
실행되는 php.exe는 C:\PATH\TO\php.exe이고, 스크립트 파일 경로는 C:\SCRIPT_PATH\hg.php 라고 가정함.
1. '도구 > 사용자 도구 구성'을 선택하여 설정창 띄움.

2. 우측의 '추가(D) >>' 버튼을 눌러 새로운 사용자 도구 만들기

3. 설정은 아래와 같이 하면 됨.
    메뉴 제목: hg annotate // 그냥 아무거나
    명령(O): "C:\PATH\TO\php.exe" // 나는 외부 프로그램으로 PHP 스크립트를 실행시킬것이므로
    인수(E): "C:\SCRIPT_PATH\hg.php" -f $(FilePath) -l $(CurLine) -n 10 // 일종의 예제
        // 위 인수부분은 만드는 외부 프로그램에 따라 취향껏 지정하면 됨.
        // 아래는 위에서 설정한, 내가 만든 스크립트에서 받아먹는 인수에 대한 설명
        "C:\PATH\TO\hg.php" : 내가 만든 스크립트 파일
        -f $(FilePath) : 현재 파일경로
        -l $(CurLine) : 현재 라인번호
        -n 10            : 현재 라인번호 기준으로 위아래 10줄씩 annotate 정보 출력
    동작: 출력 내용 캡쳐



[실행 스크립트 소스코드]
EditPlus 사용자 도구 설정에서 C:\SCRIPT_PATH\hg.php 로 설정한 그 스크립트 파일.


<?PHP
$options = getopt('f:l:n:');

// 필수옵션
// 파일 경로
$filePath = isset($options['f']) ? trim($options['f']) : null;
if (!$filePath) {
    echo "파일 경로가 필요합니다.\n";
    exit;
}
// 기준 라인번호
$lineNo = isset($options['l']) ? (int)trim($options['l']) : null;
if (!$lineNo) {
    echo "라인 번호가 필요합니다.\n";
    exit;
}

// 선택옵션
// 위아래 출력라인수 (기본값=5)
$defaultLinePad = 5;
$linePad = isset($options['n']) ? (int)trim($options['n']) : $defaultLinePad;
if (!$linePad) $linePad = $defaultLinePad;


// hg annotate 실행 
// 나는 그냥 옵션을 -uc 로 했지만, 보고싶은 내용에 따라 취향껏 하면 됨.
$cmd = 'hg anno -uc '.$filePath;
$exeResult = exe($cmd);

// exitcode 체크
if ($exeResult['exitcode'] !== 0) {
    printf("[ERROR] %s (%d)\n", $exeResult['stderr'], $exeResult['exitcode']);
    exit;
}

// 출력 문자열 가공
$arrAnnoate = explode("\n", $exeResult['stdout']);
$totalLine = count($arrAnnoate);
$posStart = $lineNo-$linePad;
if ($posStart<1) $posStart = 1;
$posEnd = $lineNo+$linePad;
if ($posEnd>=$totalLine) $posEnd = $totalLine;

$resultStr = '';
for ($i=0; $i<$totalLine; $i++) {
    $currentLineNo = $i+1;

    if ($currentLineNo<$posStart) continue;
    if ($currentLineNo>$posEnd) break;

    $resultStr .= sprintf("[%4d] %s\n", $currentLineNo, $arrAnnoate[$i]);
}

// 출력
echo $resultStr;


// 명령을 실행시키고 stdout, stderr, exitcode 반환
function exe($cmd)
{
    $DescriptorSpec = array(
        // STDIN
        0 => array('pipe', 'r'),
        // STDOUT
        1 => array('pipe', 'w'),
        // STDERR
        2 => array('pipe', 'w')
    );

    $proc = proc_open($cmd, $DescriptorSpec, $pipes);
    if (is_resource($proc) === false) return null;

    $result = array(
        'stdout' => rtrim(stream_get_contents($pipes[1])),
        'stderr' => rtrim(stream_get_contents($pipes[2]))
    );
    $result['exitcode'] = proc_close($proc);

    return $result;
}







거지같은 소스코드지만 쓰면 편하다.













Posted by bloodguy
,