#TIL #git 충돌(conflict) 났다. 한쪽 변경 사항을 적용하고 싶은데, ours야 theirs야?

충돌(conflict)이 발생했을 때, 한쪽 변경 사항을 적용하고 싶다. 주로 테스트 데이터 커밋이다. 코드 변경 사항과 섞여 들어가지 않게 데이터만 따로 커밋을 만든다. push 전에 걸러내면 되니깐. 중앙 저장소에 있는 커밋을 로컬로 가져왔는데, 데이터에서 충돌이 발생했다. 이걸 머지? 노노 양 많으면 토한다. 그럴 땐, 한쪽을 그냥 사용하면 된다.

$ git pull
$ git checkout feature-1
$ git rebase master

충돌이 발생했다.

$ git checkout --ours  <file>
$ git checkout --theirs  <file>

둘 중 어떤 걸 사용하면 되나? 난 이거 할 때마다 헷갈린다. 왜냐면 feature-1은 내가 작업한 거니깐 이게 ours 처럼 느껴지기 때문이다.

하지만 땡! rebase를 할 때, master가 부모고 feature-1 브랜치의 커밋을 하나씩 자식으로 연결한다. 즉 master가 ours고 feature-1이 theirs가 된다.

정리한다고 했지만, 막상 사용할 때가 되면 또 헷갈린다. rebase –continue 명령을 하기 전에 확인하는 습관만 들이면 좀 헷갈려도 괜찮다. 확인하고 진행하니깐.

참고

#TIL #git head 커밋 아이디?

$ cat .git/HEAD
ref: refs/heads/master
$ cat .git/refs/heads/master
246e5b84c1492682434d22448a22273b60e958aa

재미로 한두 번 이렇게 찾지 매번 이렇게 찾아야 하나?

$ git rev-parse HEAD
246e5b84c1492682434d22448a22273b60e958aa
$ git rev-parse --short HEAD
246e5b8
$ git rev-parse --short HEAD~1
42389d9

rev-parse 명령을 사용하면 된다.

참고 - https://git-scm.com/docs/git-rev-parse

#TIL #git gitignore에 / 문자가 앞에 있고 없고

$ cat .gitignore
*.c
$ git status --ignored --short
!! main.c
!! test/main_test.c

/ 문자가 없다. 모든 디렉터리를 대상으로 glob 패턴에 일치하는 파일을 무시한다.

$ cat .gitignore
/*.c
$ git status --ignored --short
?? test/main_test.c
!! main.c

/ 문자가 있다. 그것도 제일 앞에. pathname 시작과 일치해야 한다. 그 뒤에 따라오는 * 문자는 / 문자 같은 경로 구분자(path separator)와는 매치가 안 된다. 그래서 main.c 파일은 매치되고 test/main_test.c 파일은 매치 안 된다. 즉, 매치되는 main.c 파일만 무시된다.

참고 - https://git-scm.com/docs/gitignore

#TIL #elixir 컴파일 경고를 에러로 취급

def project do
  [
    # ...
    aliases: aliases(),
  ]
end

defp aliases do
  [
    "compile": ["compile --warnings-as-errors"]
  ]
end

커맨드라인 옵션은 찾았다. 디폴트 옵션이 있으면 좋겠다. 바로 실행하거나 테스트를 할 때, 코드가 변경되면 컴파일부터 한다. 이때에도 경고를 에러로 취급하는 옵션을 적용하고 싶다. 컴파일만 하면 경고가 잘 보이지만 실행하거나 테스트를 하면 빠르게 올라가서 경고 메시지를 못 볼 때가 있기 때문이다.

디폴트 옵션은 못 찾았다. 대신 alias를 사용한 방법을 찾았다. 똑똑한 방법이다.

#TIL #git 리모트 브랜치 조회

$ git fetch

가져온다.

$ git branch -r
  origin/feature-1234

리모트 브랜치만 조회. -v 옵션을 추가하면 커밋 메시지도 같이 볼 수 있다.

$ git branch -a
* feature-1234
  master
  remotes/origin/feature-1234

모두 조회. 마찬가지로 -v 옵션 사용할 수 있다.

$ git branch -vv
* feature-1234 1234567890 [origin/feature-1234] commit message 1
  master       123456789a [origin/master      ] commit message 2

트래킹 정보를 출력하려면 -vv 옵션을 사용한다.

$ git remote show origin

따끈따끈한 정보를 바로 조회할 수도 있다. 그냥 구경만 하는 게 아니고 뭔가 하려면 결국 git fetch 명령을 해야 한다.

참고

#TIL #batch #bash 제어 연산자로 만드는 명령어 리스트

제어 연산자(control operator)를 사용하면 한 줄에 여러 명령을 실행할 수 있다. 간단히 if 문(statement)을 쓴 효과를 누릴 수 있다.

command1 ; command2
command2 & command2

첫 번째 명령 성공 여부와 상관없이 연속으로 실행. 이것만 bash와 batch 스크립트가 서로 다르다. bash 스크립트는 ; 문자 사용. batch 스크립트는 & 문자 사용. 나머지는 똑같다.

command1 && command2

command1 성공하면 command2 실행.

command1 || command2

command1 실패하면 command2 실행.

short-circuit evaluation으로 이해하면 된다. exit code가 0이면 true인 게 좀 헷갈리겠지만.

cmd.exe /k ""%ConEmuBaseDir%\CmdInit.cmd" & "%ProgramFiles%\Redis\redis-cli.exe""

conemu 앱에서 redis 클라이언트 띄울 때, 사용했다.

참고

#TIL #app conemu로 git bash와 cmd를 한 화면에

/ddiary/assets/2017-11-08-til-app-conemu-show-git-bash-and-cmd-in-one-screen.jpg

> "C:\git-sdk-64\git-cmd.exe" --no-cd --command=usr/bin/bash.exe -l -i
"C:\git-sdk-64\git-cmd.exe" --no-cd --command=usr/bin/bash.exe -l -i -new_console:s
cmd.exe /k ""%ConEmuBaseDir%\CmdInit.cmd" & "%ProgramFiles%\Redis\redis-cli.exe"" -new_console:s1TVn

Startup > Tasks > Predefined tasks 메뉴로 하나 새로 등록. 그냥 emacs로 띄우려다가 탭이 너무 많아서 개발 로컬 서버는 conemu를 사용하고 있다.

홈페이지 - http://conemu.github.io/

#TIL #elixir 비어있는 map 패턴 매칭

defmodule MapUtil do
  def empty?(%{}), do: true
  def empty?(map), do: false
end
iex> MapUtil.empty?(%{hello: "elixir"})
true

기대했던 것과 달리 true가 나온다. %{} 변수에는 비어있는 map이 아니라 모든 map이 패턴 매칭된다.

def empty?(map) when map == %{}, do: true

빈 map과 같은지 검사하는 가드 절(guard clause)을 추가하거나

def empty?(map) when map_size(map) == 0, do: true

가드 절에서 호출할 수 있는 함수를 사용해서 검사하면 된다. 호출할 수 있는 함수는 제한되어 있다.

리스트도 그런가 테스트를 해봤는데, 비어있는 list에만 패턴 매칭되네. 순서가 있고 없고의 차이인가? 외우라면 외우겠지만 아직 왜 저렇게 설계했는지는 이유를 잘 모르겠다.

참고 - Pattern match function against empty map - stackoverflow.com

#TIL #elixir keyword list를 map으로 변환

iex> [key1: "value1", key2: "value2"] |> Enum.into(%{})
%{key1: "value1", key2: "value2"}

Enum.into/2 함수를 사용하면 된다. Enum.map_reduce/3 함수를 써서 복잡하게 난리부르스를 추다가 구글링으로 간편한 방법을 알아냈다.

iex> [{:key1, "value1"}, {:key2, "value2"}] == [key1: "value1", key2: "value2"]
true

special syntax를 써서 편하게 keyword list를 만들 수 있다. 실제론 tuple의 list. Enum.into/2 함수는 원소를 하나씩 변환하는데, tuple의 원소 개수가 2개면 key와 value로 해석해서 map에 추가한다.

참고 - Elixir: How to convert a keyword list to a map? - stackoverflow.com

#TIL #git 리모트 브랜치 체크 아웃

$ git checkout --track origin/serverfix

--track 옵션을 사용하면 된다. 로컬 브랜치 이름은 리모트 브랜치 이름에서 만들어준다.

$ git checkout -b sf origin/serverfix

이름을 다르게 하고 싶거나 --track 옵션을 사용했을 때, git이 이름 추정을 못 해서 체크아웃을 실패할 때, 사용한다.

참고 - 3.5 Git 브랜치 - 리모트 브랜치 - Pro Git

#TIL #vim 중괄호(braces)를 넘어 다니자

1 { 2 { 3 { |4 } A } B }

[{ 입력

1 { 2 { 3 |{ 4 } A } B }

[{ 입력

1 { 2 |{ 3 { 4 } A } B }

대괄호(square brackets)는 방향이다. 즉 [{ 키를 입력하면 이전 { 문자로 이동한다.

1 { 2 |{ 3 { 4 } A } B }

그럼 여기서 ]} 키를 입력하면 어떻게 되나?

1 { 2 { 3 { 4 |} A } B }

여기가 아니라

1 { 2 { 3 { 4 } A |} B }

여기로 입력한다. 왜냐면 unmatched 문자를 찾아서 이동하기 때문이다. {} 쌍은 무시한다.

이게 {} 문자를 사용하는 프로그래밍 언어에서는 상위 level brace로 이동하거나 현재 level의 닫거나 여는 brace로 이동할 수 있다. 하위 level은 안 된다. 왜냐면 unmatched 문자를 찾기 때문에 {} 쌍은 무시한다.

if (a == 0)
{
    if (b == 0)
    {
        if (c == 0)
        {
            |call_body();
        }
    }
}

[{ 입력

if (a == 0)
{
    if (b == 0)
    {
        if (c == 0)
        |{
            call_body();
        }
    }
}

[{ 입력

if (a == 0)
{
    if (b == 0)
    |{
        if (c == 0)
        {
            call_body();
        }
    }
}

]} 입력

if (a == 0)
{
    if (b == 0)
    {
        if (c == 0)
        {
            call_body();
        }
    |}
}

참고 - various-motions - vimdoc

#TIL #linux 왜 cannot set LC_CTYPE locale?

svn: warning: cannot set LC_CTYPE locale
svn: warning: environment variable LANG is KOR
svn: warning: please check that your locale name is correct

msys2에서 svn 명령어를 입력하니 이런 warning이 나온다. 왜?

$ locale
LANG=KOR
LC_CTYPE="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_COLLATE="C"
LC_MONETARY="C"
LC_MESSAGES="C"
LC_ALL=

locale 이름이 이상해서 발생한 것 같다. 한글 메시지는 폰트 때문에 너무 작게 나오니 영문으로 변경.

$ export LANG=en_US.UTF-8
$ export LC_ALL=en_US.UTF-8

.bashrc 파일에 추가.

$ locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_ALL=en_US.UTF-8

끝.

#TIL #windows #powershell utf-8로 파일 인코딩 변경

C:\> PowerShell -Command "(Get-Content input.txt) | Set-Content -Encoding utf8 output.txt"

powershell을 쓰는 게 가장 간단하다. 괄호로 감싸야지 다 읽고 파일 핸들을 release 하므로 같은 파일 encoding을 바로 변경할 수 있다.

참고 - Converting text file to UTF-8 on Windows command prompt - superuser.com

#TIL pacman 패키지 매니저 기본 명령어

$ pacman -Sy

로컬 패키지 DB와 원격 리포지토리 동기화.

$ pacman -Su

설치된 모든 패키지 업데이트.

$ pacman -Ss ${package_name}

패키지 검색.

$ pacman -S ${package_name}

설치 또는 업데이트

참고 - pacman(한글) - wiki.archlinux.org

#TIL #batch 확장자 bat, cmd는 어떤 차이가 있는 걸까?

cmd가 최신. 호환성을 끊고 새로운 기능을 추가하려고 bat가 아닌 새로운 확장자를 추가한 것 같다. %ERRORLEVEL% 변수 세팅 정책이 다른 건 주의해야 한다.

@ECHO off
dir __ghost__ 1>2 2>NUL
ECHO error level #1 = %errorlevel%
SET a=10
ECHO error level #2 = %errorlevel%
C:\>test.bat
error level #1 = 1
error level #2 = 1
C:\>test.cmd
error level #1 = 1
error level #2 = 0

bat 확장자 스크립트는 에러일 때만 %ERRORLEVEL% 변수에 값을 할당한다. 반면 cmd 확장자 스크립트는 에러가 아니라도 %ERRORLEVEL% 변수에 값을 할당한다.

cmd 확장자 방식이 맞는 것 같다. 바로 전 명령어 에러값을 반영하기 때문. 하지만 bash처럼 에러 발생 시 바로 리턴해버리는 set -e 명령어가 없다. 그래서 %ERRORLEVEL% 값을 처리 안 하면 계속 남아있는 bat 확장자 방식이 에러 스노우볼링 방지에 유리하다. 특히 jenkins에서 batch script 빌드 스텝을 사용한 경우.

지금은 bat 확장자 스크립트로도 아무런 불편함이 없지만, 추가 기능이 필요한 때가 올 것이다. 그때 cmd로 가느니 지금 cmd로 통일해서 간다. 주의할 점을 알았으니 가도 괜찮다.

참고 - Windows batch files: .bat vs .cmd? - stackoverflow.com

#TIL #css pre 태그에 자동 줄바꿈을 제거하고 x축 스크롤 바 추가

 pre {
+    white-space: pre;
-    white-space: pre-wrap;
-    word-wrap: break-word;
+    word-wrap: normal;
+    overflow-x: auto;
 }

모바일에서 소스 코드 보기가 힘들다. 더 편하게 읽으라고 줄 바꿈을 자동으로 해주는데, 오히려 읽기가 더 힘들다. 자동 줄 바꿈을 없애고 x축 스크롤 바를 추가했다.

word-warp 프로퍼티 수정을 안 했었다. 사파리로 열어보기 전까지는. normal 값으로 바꾸니 사파리에서도 잘 보인다. 파이어폭스도 알아서 잘 보여주겠지. 크롬과 사파리만 테스트했다. 브라우저마다 다르게 보이면 어떤 짜증이 나는지 짧게나마 체험했다.

참고

#TIL #git 센스 돋는 stash aliases

$ git config --global alias.stsh 'stash --keep-index'
$ git config --global alias.staash 'stash --include-untracked'

Git Aliases of the Gods! - Git Merge 2017’ 발표보고 추가했다. 네이밍 센스 부럽다. --include-untracked 옵션 생각이 왜 그리 안 나는지. 이제 git staash 명령어로 해결

#TIL #jenkins build summary에 정보를 보여주려면 groovy postbuild

/ddiary/assets/2017-06-28-til-jenkins-groovy-postbuild-plugin.gif

build summary에 error 문자열을 보여주고 싶다. console output을 눌러서 보면 되지만 버튼 클릭을 하나 없애고 싶다. 분명 있을법한 플러그인인데, 못 찾겠다.

groovy postbuild 6번째 사용 예제를 보니 딱 원하는 게 있다. 결국 groovy를 쓰게 되는구나. 클릭 한번 줄이기가 이렇게 힘드네.

#TIL #git remote 저장소에서 지워진 브랜치를 로컬에 반영

$ git fetch --prune
$ git pull --prune

--prune 옵션을 사용하면 된다.

$ git config --global fetch.prune true

리모트 저장소에서 지워진 branch를 로컬에 유지할 필요가 없다. 쌓여서 문제지. 이제 git fetch, git pull 명령을 입력할 때, 옵션을 안 붙여도 된다.

--prune

Before fetching, remove any remote-tracking references that no longer exist on the remote. [.]

- https://git-scm.com/docs/git-fetch

branch는 ‘나뭇가지’란 뜻도 있으니 ‘가지 치다’란 뜻을 가진 prune을 사용했나 보다. 네이밍 센스 좋다.

참고

#TIL #git 알아서 upstream을 설정해주는 push alias

[alias]
  pu = "!f() { \
         [[ $(git config \"branch.$(git symbolic-ref --short HEAD).merge\") = '' ]] && \
         git push -u origin $(git symbolic-ref --short HEAD) $@ || \
         git push $@; \
       }; f"

.gitconfig 파일에 추가한다. 커맨드라인 명령어로 추가하려고 했다. 에러 메시지가 뜨는데, 뭐가 문제인지 모르겠다. 에라이. 그냥 config 파일을 직접 수정했다.

두가지 전제 조건이 있다. remote는 origin 고정이다. remote와 local 브랜치 이름이 같다.

$ git checkout feature-1
$ git pu

feature-1 브랜치 upstream 저장소 설정 여부에 따라 둘 중 하나로 실행된다.

$ git push -u origin feature-1

또는

$ git push

참고