레거시 코드를 리베이스하고 있는데 스크립트(보통 코드 포맷터)로 인해 발생하는 많은 충돌을 발견했습니다. 변경 사항은 간단하고 예측 가능하므로 스크립트를 쉽게 다시 실행하여 코드에 변경 사항을 적용할 수 있습니다. 그러면 일반적으로 git의 diff 마커로 둘러싸인 3개의 동일한 "스니펫"이 남게 됩니다.
세 개의 일치하는 조각과 차이점 마커를 찾아 단일 조각으로 변환하는 스크립트를 어떻게 작성합니까?
예:
git rebase
다음을 생성합니다.
<<<<<<< HEAD
ACA
BCB
||||||| parent of 0cfd85b8e3... Beautify.
AAA
BBB
=======
AAC
BBC
>>>>>>> 0cfd85b8e3... Beautify.
포맷터를 다시 실행하면 다음과 같은 결과가 나타납니다.
<<<<<<< HEAD
ACC
BCC
||||||| parent of 0cfd85b8e3... Beautify.
ACC
BCC
=======
ACC
BCC
>>>>>>> 0cfd85b8e3... Beautify.
나는 그것을 다음과 같이 변환하고 싶습니다 :
ACC
BCC
나는 grep(새 줄로 어려움을 겪음)과 pcregrep(역참조/캡처 그룹으로 어려움을 겪었음)을 포함하여 아주 많은 것을 시도했습니다. 어떤 아이디어가 있나요?
답변1
이 문제에 대해 상태 저장 awk 프로그램을 사용하면 다음과 같이 명확하게 알 수 있습니다.
awk '/^<<<<<<</ { state = 1; next }
/^=======/ { state = 2; next }
/^>>>>>>>/ { state = 0; next }
state == 0 || state == 2' data
foo
prolog
AAC
BBC
epilog
bar
여기에는 다음이 data
포함됩니다.
foo
prolog
<<<<<<< HEAD
ACA
BCB
||||||| parent of 0cfd85b8e3... Beautify.
AAA
BBB
=======
AAC
BBC
>>>>>>> 0cfd85b8e3... Beautify.
epilog
bar
state == 0
아직 초기화되지 않은 경우 true이므로 처음에는 상태 0에 있습니다 . state
우리는 그것을 볼 때 상태 1로 전환하고 <<<<<<
, 그것을 볼 때 상태 2로 전환 =======
한 다음, 종료 마커를 볼 때 상태 0으로 다시 전환합니다 >>>>>>>
. 매우 간단합니다.
초기 상태 0(충돌 토큰 외부) 또는 상태 2(충돌 토큰의 마지막 부분)에 있는 경우에만 줄을 인쇄합니다.
모든 경우에 명령은 next
실패 동작이 발생하지 않도록 보장합니다. 마지막 사례만 아무것도 인쇄하므로 처음 세 사례는 아무 것도 인쇄하지 않고 충돌 표시를 식별하고 상태를 변경하는 것 외에는 아무것도 수행하지 않는다는 것을 알고 있습니다. 따라서 어떤 상태에 있든 충돌 플래그는 인쇄되지 않습니다. 상태 2에서 다시 상태 0으로 돌아가면 실수로 =======
or >>>>>>>
줄이 인쇄되지 않습니다.
첫 번째 작업에서는 인쇄되지 않는 상태 1로 전환되므로 꼭 필요한 것은 아니지만 next
포함하는 것이 일관성을 유지하는 데 좋습니다. 또한 효율성: 충돌 마커는 상호 배타적입니다. =======
이미 감지한 경우 테스트해야 합니다 <<<<<<<
.
답변2
TxRLisp의 awk
매크로는 전통적인 Awk보다 더 강력한 범위 표현을 가지고 있기 때문에 상태 변수 없이 이 문제를 해결할 수 있습니다.
범위 표현식은 다른 표현식과 결합될 수 있습니다. 예를 들어, 범위 표현식은 다른 범위의 시작이나 끝일 수 있습니다.
awk
범위 표현식은 주절 조건뿐만 아니라 매크로의 어느 곳에서나 사용할 수 있습니다 .범위 표현식이 들어옵니다.아홉 가지 맛시작과 끝에서 레코드를 제외하거나 더 많은 레코드를 포함하도록 범위를 확장하는 데 사용됩니다.
data
Awk의 답변과 동일한 파일을 사용합니다 .
$ txr awk.tl data
foo
prolog
AAC
BBC
epilog
bar
코드는 다음 위치에 있습니다 awk.tl
.
(awk
(:let (beg (f^ #/<<<<<<</))
(mid (f^ #/=======/))
(end (f^ #/>>>>>>>/)))
((-rng- mid end)) ; print lines between ===...>>>, exclusive
((not (rng beg end)))) ; print lines outside <<<...>>>
분해:
먼저(:let ...)
양식 범위 내에서 일부 어휘 변수를 바인딩하는 절입니다 awk
. 단일 인수 익명 함수인 변수 beg
, mid
및 를 소개합니다. end
예를 들어(f^ /abc/)
abc
문자열을 가져 와서 문자열의 시작 부분에 고정된 정규식과 일치하는 함수를 생성합니다 . diff3 충돌 마커의 시작, 중간, 끝을 일치시키는 함수를 정의하고 있습니다.
뒷면에는 :let
두 개의 절이 있습니다 (condition action)
. 절도 없으므로 action
기본 작업은 인쇄입니다. 이는 Awk의 한 형태이므로 동일한 규칙이 적용됩니다.
범위는 태그에서 까지의 (-rng- mid end)
레코드 와 일치합니다. 이 변형은 범위의 내부 레코드에 대해서만 true를 생성하며 첫 번째 및 마지막 레코드에 대해서는 false를 생성합니다. 이것이 바로 우리가 원하는 것입니다. 우리는 이 줄을 포함하지 않고 와 사이의 모든 줄을 인쇄하고 싶습니다. awk에는 범위에서 끝점을 제외하는 기능이 없습니다.mid
end
-rng-
======
>>>>>>>
두 번째 규칙의 조건은 입니다 (not (rng beg end))
. 이 조건은 beg
to end
범위 내에 있지 않은 레코드 , 즉 diff3 충돌 주석 내에 있지 않은 경우에 해당됩니다. 우리는 그것들을 모두 인쇄하고 싶습니다. awk에는 그러한 기능도 없습니다. 범위 표현식은 다른 연산자(예: !
부정)와 결합할 수 없습니다.
TXR Lisp의 범위 기능으로 인해 awk
이 솔루션에는 상태 변수가 필요하지 않으며 다음 레코드 처리를 계속하기 위해 암시적 루프에서 명시적으로 분기해야 하는 규칙이 없습니다.