posted by REDFORCE 2017. 4. 9. 21:29

출처 : git-scm.com

도움말 보기

명령어에 대한 도움말이 필요할 때 도움말을 보는 방법은 세 가지다:

$ git help <verb>
$ git <verb> --help
$ man git-<verb>

예를 들어 아래와 같이 실행하면 config 명령에 대한 도움말을 볼 수 있다:

$ git help config

도움말은 언제 어디서나 볼 수 있다. 오프라인으로도 볼 수 있다. 도움말과 이 책으로 부족하면 다른 사람의 도움을 받는 것이 필요하다. Freenode IRC 서버(irc.freenode.net)에 있는 #git이나 #github 채널로 찾아가라. 이 채널에는 보통 수백 명의 사람이 접속해 있다. 이 사람들은 모두 Git에 대해 잘 알고 있다. 기꺼이 도와줄 것이다.

'Programming > Git' 카테고리의 다른 글

Git_#2.2_수정하고 저장소에 저장하기  (0) 2017.04.09
Git_#2.1_Git 저장소 만들기  (0) 2017.04.09
Git_#1.5_최초 설정  (0) 2017.04.09
Git_#1.4_Git 설치  (0) 2017.04.09
Git_#1.3_Git 기초  (0) 2017.04.09
posted by REDFORCE 2017. 4. 9. 21:29

출처 : git-scm.com

Git 최초 설정

Git을 설치하고 나면 Git의 사용 환경을 적절하게 설정해 주어야 한다. 한 번만 설정하면 된다. 설정한 내용은 Git을 업그레이드해도 유지된다. 언제든지 다시 바꿀 수 있는 명령어가 있다.

'git config'라는 도구로 설정 내용을 확인하고 변경할 수 있다. Git은 이 설정에 따라 동작한다. 이때 사용하는 설정 파일은 세 가지나 된다.

  • /etc/gitconfig 파일: 시스템의 모든 사용자와 모든 저장소에 적용되는 설정이다. git config --system 옵션으로 이 파일을 읽고 쓸 수 있다.
  • ~/.gitconfig 파일: 특정 사용자에게만 적용되는 설정이다. git config --global 옵션으로 이 파일을 읽고 쓸 수 있다.
  • .git/config: 이 파일은 Git 디렉토리에 있고 특정 저장소(혹은 현재 작업 중인 프로젝트)에만 적용된다. 각 설정은 역순으로 우선시 된다. 그래서 .git/config가 /etc/gitconfig보다 우선한다.

윈도용 Git은 $HOME 디렉토리(%USERPROFILE% 환경변수)에 있는 .gitconfig 파일을 찾는다. 보통 C:\Documents and Settings\$USER 또는 C:\Users\$USER 이다(윈도우에서는 $USER 대신 %USERNAME%를 사용한다). 그리고 msysGit도 /etc/gitconfig를 가지고 있다. 경로는 MSys 루트에 따른 상대 경로다. 인스톨러로 msysGit을 설치할 때 설치 경로를 선택할 수 있다.

사용자 정보

Git을 설치하고 나서 가장 먼저 해야 하는 것은 사용자 이름과 이메일 주소를 설정하는 것이다. Git은 커밋할 때마다 이 정보를 사용한다. 한 번 커밋한 후에는 정보를 변경할 수 없다:

$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com

다시 말하자면 --global 옵션으로 설정한 것은 딱 한 번만 하면 된다. 해당 시스템에서 해당 사용자가 사용할 때에는 이 정보를 사용한다. 만약 프로젝트마다 다른 이름과 이메일 주소를 사용하고 싶으면 --global옵션을 빼고 명령을 실행한다.

편집기

사용자 정보를 설정하고 나면 Git에서 사용할 텍스트 편집기를 고른다. 기본적으로 Git은 시스템의 기본 편집기를 사용하고 보통 Vi나 Vim이다. 하지만, Emacs 같은 다른 텍스트 편집기를 사용할 수 있고 아래와 같이 실행하면 된다:

$ git config --global core.editor emacs

Diff 도구

Merge 충돌을 해결하기 위해 사용하는 Diff 도구를 설정할 수 있다. vimdiff를 사용하고 싶으면 아래와 같이 실행한다:

$ git config --global merge.tool vimdiff

이렇게 kdiff3, tkdiff, meld, xxdif, emerge, vimdiff, gvimdiff, ecmerge, opendiff를 사용할 수 있다. 물론 다른 도구도 사용할 수 있다. 자세한 내용은 7장에서 다룬다.

설정 확인

git config --list 명령을 실행하면 설정한 모든 것을 보여준다:

$ git config --list
user.name=Scott Chacon
user.email=schacon@gmail.com
color.status=auto
color.branch=auto
color.interactive=auto
color.diff=auto
...

Git은 같은 키를 여러 파일(/etc/gitconfig와 ~/.gitconfig 같은)에서 읽기 때문에 같은 키가 여러개 있을 수도 있다. 이러면 Git은 나중 값을 사용한다.

git config {key} 명령으로 Git이 특정 Key에 대해 어떤 값을 사용하는지 확인할 수 있다:

$ git config user.name
Scott Chacon


'Programming > Git' 카테고리의 다른 글

Git_#2.1_Git 저장소 만들기  (0) 2017.04.09
Git_#1.6_도움말 보기  (0) 2017.04.09
Git_#1.4_Git 설치  (0) 2017.04.09
Git_#1.3_Git 기초  (0) 2017.04.09
Git_#1.2_짧게 보는 Git의 역사  (0) 2017.04.09
posted by REDFORCE 2017. 4. 9. 21:28

출처 : git-scm.com

Git 설치

Git을 사용하려면 우선 설치해야 한다. 다양한 방법으로 Git을 설치할 수 있지만 두 가지 방법이 가장 일반적이다. 하나는 소스코드로 컴파일하여 설치하는 방법이고 다른 하나는 각 운영체제(혹은 플랫폼)의 패키지를 사용하여 설치하는 방법이다.

소스코드로 설치하기

소스코드로 설치하면 Git의 가장 최신 버전을 설치할 수 있기 때문에 컴파일하여 설치할 시간이 있으면 소스코드로 Git을 설치하는 것이 좋다. Git은 계속 UI를 개선하고 있기 때문에 최신 버전을 사용하면 좋은 기능을 빨리 사용할 수 있다. 리눅스 패키지는 보통 최신 버전이 아니고 예전 버전이다. 그래서 Backport를 사용하거나 소스코드로 설치하는 것도 좋은 대안이다.

Git을 설치하려면 아래와 같은 라이브러리들이 필요하다. Git은 curl, zlib, openssl, expat, libiconv를 필요로 한다. 예를 들어 Fedora처럼 yum을 사용하는 시스템이나 apt-get이 있는 데비안류 시스템이면 아래 명령어를 실행하여 의존 패키지를 설치할 수 있다:

$ yum install curl-devel expat-devel gettext-devel \
  openssl-devel zlib-devel

$ apt-get install libcurl4-gnutls-dev libexpat1-dev gettext \
  libz-dev libssl-dev

필요한 라이브러리를 모두 설치하고 다음 단계를 진행한다. Git 웹 사이트에서 최신 스냅샷을 가져온다:

http://git-scm.com/download

그리고 컴파일하고 설치한다:

$ tar -zxf git-1.7.2.2.tar.gz
$ cd git-1.7.2.2
$ make prefix=/usr/local all
$ sudo make prefix=/usr/local install

설치한 다음부터는 Git을 사용하여 Git 소스코드를 수정할 수 있다:

$ git clone git://git.kernel.org/pub/scm/git/git.git

리눅스에 설치

리눅스에서 패키지로 Git을 설치할 때에는 보통 각 배포판에서 사용하는 패키지 관리도구를 사용하여 설치한다. Fedora에서는 아래와 같이 한다:

$ yum install git-core

Ubuntu같은 데비안류 배포판에서는 apt-get을 사용한다:

$ apt-get install git

Mac에 설치하기

Mac에 Git을 쉽게 설치하는 방법은 두 가지가 있다. GUI 인스톨러가 가장 쉽게 사용할 수 있다. SourceForge 페이지에서 내려받는다:

http://sourceforge.net/projects/git-osx-installer/


그림 1-7. OS X Git 인스톨러

MacPorts(http://www.macports.org)를 사용하는 방법도 있다. MacPorts가 설치돼 있으면 아래와 같이 Git을 설치한다:

$ sudo port install git-core +svn +doc +bash_completion +gitweb

이제 설치는 했다. 만약 Subversion 저장소를 Git과 함께 사용해야 하면 svn도 필요하다.

윈도에 설치

윈도에서도 Git을 쉽게 설치할 수 있다. 그저 구글 코드 페이지에서 msysGit 인스톨러를 내려받고 실행하면 된다:

http://msysgit.github.com/

설치가 완료되면 CLI 프로그램과 GUI 프로그램을 둘 다 사용할 수 있다. CLI 프로그램에는 SSH 클라이언트가 포함돼 있기 때문에 유용하다.

Windows 사용자 필독:이 책에서 소개하는 다양한 명령어를 사용하려면 유닉스 스타일의 msysGit 쉘을 사용하는 것이 좋다. 어쩔 수 없이 Windows에 포함된 기본 쉘(Command Prompt, 명령 프롬프트)을 꼭 써야 하면 공백이 포함된 파라미터를 Git 명령어에 넘길 때 작은 따옴표(' ') 대신 큰 따옴표(" ")를 사용해야 한다. 파라미터 끝에 ^ 기호가 있을 때도 큰 따옴표로 파라미터를 감싸야 한다. Windows 쉘에서 ^ 기호는 다음 줄로 명령어가 이어짐을 나타낸다.

'Programming > Git' 카테고리의 다른 글

Git_#1.6_도움말 보기  (0) 2017.04.09
Git_#1.5_최초 설정  (0) 2017.04.09
Git_#1.3_Git 기초  (0) 2017.04.09
Git_#1.2_짧게 보는 Git의 역사  (0) 2017.04.09
Git #1.1 버전 관리란?  (0) 2017.04.08
posted by REDFORCE 2017. 4. 9. 21:27

출처 : git-scm.com

Git 기초

Git의 핵심은 뭘까? 이 질문은 Git을 이해하는데 굉장히 중요하다. Git이 무엇이고 어떻게 동작하는지 이해한다면 쉽게 Git을 효과적으로 사용할 수 있다. Git을 배우려면 Subversion이나 Perforce 같은 다른 VCS를 사용하던 경험을 지워버려야 한다. Git은 미묘하게 달라서 다른 VCS에서 쓰던 개념으로는 헷갈릴 거다. 사용자 인터페이스는 매우 비슷하지만, 정보를 취급하는 방식이 다르다. 이런 차이점을 이해하면 Git을 사용하는 것이 어렵지 않다.

델타가 아니라 스냅샷

Subversion과 Subversion 비슷한 놈들과 Git의 가장 큰 차이점은 데이터를 다루는 방법에 있다. 큰 틀에서 봤을 때 대부분의 VCS 시스템이 관리하는 정보는 파일들의 목록이다. CVS, Subversion, Perforce, Bazaar 등의 시스템은 파일의 집합으로 정보를 관리한다. 각 파일의 변화를 그림 1-4처럼 시간순으로 관리한다.


그림 1-4. 각 파일에 대한 변화(델타)를 저장하는 시스템들

Git은 이런 식으로 데이터를 저장하지도 취급하지도 않는다. 대신 Git의 데이터는 파일 시스템의 스냅샷이라 할 수 있으며 크기가 아주 작다. Git은 커밋하거나 프로젝트의 상태를 저장할 때마다 파일이 존재하는 그 순간을 중요하게 여긴다. 파일이 달라지지 않았으면 Git은 성능을 위해서 파일을 저장하지 않는다. 단지 이전 상태의 파일에 대한 링크만 저장한다. Git은 그림 1-5처럼 동작한다.


그림 1-5. Git은 시간순으로 프로젝트의 스냅샷을 저장한다

이것이 Git이 다른 VCS와 구분되는 점이다. 이점 때문에 Git는 다른 시스템들이 과거로부터 답습해왔던 버전 컨트롤의 개념과 다르다는 것이고 많은 부분을 새로운 관점에서 바라본다. Git은 강력한 도구를 지원하는 작은 파일시스템이다. Git은 단순한 VCS가 아니다. 이제 3장에서 설명할 Git 브랜치를 사용하면 얻게 되는 이득이 무엇인지 설명한다.

거의 모든 명령을 로컬에서 실행

거의 모든 명령이 로컬 파일과 데이터만 사용하기 때문에 네트워크에 있는 다른 컴퓨터는 필요 없다. 대부분의 명령어가 네트워크의 속도에 영향을 받는 CVCS에 익숙하다면 Git이 매우 놀라울 것이다. Git의 이런 특징에서 나오는 미칠듯한 속도는 오직 Git느님만이 구사할 수 있는 초인적인 능력이다. 프로젝트의 모든 히스토리가 로컬 디스크에 있기 때문에 모든 명령을 순식간에 실행된다.

예를 들어 Git은 프로젝트의 히스토리를 조회할 때 서버 없이 조회한다. 그냥 로컬 데이터베이스에서 히스토리를 읽어서 보여 준다. 그래서 눈 깜짝할 사이에 히스토리를 조회할 수 있다. 어떤 파일의 현재 버전과 한 달 전의 상태를 비교해보고 싶을 때도 Git은 그냥 한 달 전의 파일과 지금의 파일을 로컬에서 찾는다. 파일을 비교하기 위해 리모트에 있는 서버에 접근하고 나서 예전 버전을 가져올 필요가 없다.

즉 오프라인 상태에서도 비교할 수 있다. 비행기나 기차 등에서 작업하고 네트워크에 접속하고 있지 않아도 커밋할 수 있다. 다른 VCS 시스템에서는 불가능한 일이다. Perforce는 서버에 연결할 수 없을 때 할 수 있는 일이 별로 없다. Subversion이나 CVS에서도 마찬가지다. 데이터베이스에 접근할 수 없어서 파일을 편집할 수는 있지만, 커밋할 수 없다. 매우 사소해 보이지만 실제로 이 상황에 부닥쳐보면 느껴지는 차이가 매우 크다.

Git의 무결성

Git은 모든 데이터를 저장하기 전에 체크섬(또는 해시)을 구하고 그 체크섬으로 데이터를 관리한다. 체크섬 없이 어떠한 파일이나 디렉토리도 변경할 수 없다. 체크섬은 Git에서 사용하는 가장 기본적인(Atomic) 데이터 단위이자 Git의 기본 철학이다. Git 없이는 체크섬을 다룰 수 없어서 파일의 상태도 알 수 없고 심지어 데이터를 잃어버릴 수도 없다.

Git은 SHA-1 해시를 사용하여 체크섬을 만든다. 만든 체크섬은 40자 길이의 16진수 문자열이다. 파일의 내용이나 디렉토리 구조를 이용하여 체크섬을 구한다. SHA-1은 아래처럼 생겼다:

24b9da6552252987aa493b52f8696cd6d3b00373

Git은 모든 것을 해시로 식별하기 때문에 이런 값은 여기저기서 보인다. 실제로 Git은 파일을 이름으로 저장하지 않고 해당 파일의 해시로 저장한다.

Git은 데이터를 추가할 뿐

Git으로 무얼 하든 데이터를 추가한다. 되돌리거나 데이터를 삭제할 방법이 없다. 다른 VCS처럼 Git도 커밋하지 않으면 변경사항을 잃어버릴 수 있다. 하지만, 일단 스냅샷을 커밋하고 나면 데이터를 잃어버리기 어렵다.

Git을 사용하면 프로젝트가 심각하게 망가질 걱정 없이 매우 즐겁게 여러 가지 실험을 해 볼 수 있다. 9장을 보면 Git이 데이터를 어떻게 저장하고 손실을 어떻게 복구해야 할지 알 수 있다.

세 가지 상태

이 부분은 중요하기에 집중해서 읽어야 한다. Git을 공부하기 위해 반드시 짚고 넘어가야 할 부분이다. Git은 파일을 Committed, Modified, Staged 이렇게 세 가지 상태로 관리한다. Committed란 데이터가 로컬 데이터베이스에 안전하게 저장됐다는 것을 의미한다. Modified는 수정한 파일을 아직 로컬 데이터베이스에 커밋하지 않은 것을 말한다. Staged란 현재 수정한 파일을 곧 커밋할 것이라고 표시한 상태를 의미한다.

이 세 가지 상태는 Git 프로젝트의 세 가지 단계와 연결돼 있다. Git 디렉토리, 워킹 디렉토리, Staging Area 이렇게 세 가지 단계를 이해하고 넘어가자.


그림 1-6. 워킹 디렉토리, Staging Area, Git 디렉토리

Git 디렉토리는 Git이 프로젝트의 메타데이터와 객체 데이터베이스를 저장하는 곳을 말한다. Git 디렉토리가 Git의 핵심이다. 다른 컴퓨터에 있는 저장소를 Clone 할 때 Git 디렉토리가 만들어진다.

워킹 디렉토리는 프로젝트의 특정 버전을 Checkout한 것이다. Git 디렉토리는 지금 작업하는 디스크에 있고 그 디렉토리에 압축된 데이터베이스에서 파일을 가져와서 워킹 디렉토리를 만든다.

Staging Area는 Git 디렉토리에 있다. 단순한 파일이고 곧 커밋할 파일에 대한 정보를 저장한다. 종종 인덱스라고 불리기도 하지만, Staging Area라는 명칭이 표준이 되어가고 있다.

Git으로 하는 일은 기본적으로 아래와 같다:

  • 워킹 디렉토리에서 파일을 수정한다.
  • Staging Area에 파일을 Stage해서 커밋할 스냅샷을 만든다.
  • Staging Area에 있는 파일들을 커밋해서 Git 디렉토리에 영구적인 스냅샷으로 저장한다.

Git 디렉토리에 있는 파일들은 Committed 상태이다. 파일을 수정하고 Staging Area에 추가했다면 Staged이다. 그리고 Checkout하고 나서 수정했지만, 아직 Staging Area에 추가하지 않았으면 Modified이다. 2장에서 이 상태에 대해 좀 더 자세히 배운다. 특히 Staging Area를 어떻게 이용하는지 혹은 아예 생략하는 방법도 설명한다.

'Programming > Git' 카테고리의 다른 글

Git_#1.6_도움말 보기  (0) 2017.04.09
Git_#1.5_최초 설정  (0) 2017.04.09
Git_#1.4_Git 설치  (0) 2017.04.09
Git_#1.2_짧게 보는 Git의 역사  (0) 2017.04.09
Git #1.1 버전 관리란?  (0) 2017.04.08
posted by REDFORCE 2017. 4. 9. 21:25
출처 : git-scm.com

짧게 보는 Git의 역사

인생을 살다 보면 여러 가지 일들이 벌어지듯이 Git의 삶 또한 창조적인 파괴와 모순 속에서 시작되었다. 리눅스 커널은 굉장히 규모가 큰 오픈소스 프로젝트다. 리눅스 커널의 일생에서 대부분 시절은 패치와 단순 압축 파일로만 관리했다. 2002년에 드디어 리눅스 커널은 BitKeeper라고 불리는 상용 DVCS를 사용하기 시작했다.

2005년에 커뮤니티가 만드는 리눅스 커널과 이익을 추구하는 회사가 개발한 BitKeeper의 관계는 틀어졌다. BitKeeper의 무료 사용이 제고된 것이다. 이 사건은 리눅스 개발 커뮤니티(특히 리눅스 창시자 리누스 토발즈)가 자체 도구를 만드는 계기가 됐다. Git은 BitKeeper를 사용하면서 배운 교훈을 기초로 아래와 같은 목표를 세웠다:

  • 빠른 속도
  • 단순한 구조
  • 비선형적인 개발(수천 개의 동시 다발적인 브랜치)
  • 완벽한 분산
  • 리눅스 커널 같은 대형 프로젝트에도 유용할 것(속도나 데이터 크기 면에서)

Git은 2005년 탄생하고 나서 아직도 초기 목표를 그대로 유지하고 있다. 그러면서도 사용하기 쉽게 진화하고 성숙했다. Git은 미친 듯이 빨라서 대형 프로젝트에 사용하기도 좋다. Git은 동시다발적인 브랜치에도 끄떡없는 슈퍼 울트라 브랜칭 시스템이다(3장 참고).

'Programming > Git' 카테고리의 다른 글

Git_#1.6_도움말 보기  (0) 2017.04.09
Git_#1.5_최초 설정  (0) 2017.04.09
Git_#1.4_Git 설치  (0) 2017.04.09
Git_#1.3_Git 기초  (0) 2017.04.09
Git #1.1 버전 관리란?  (0) 2017.04.08
posted by REDFORCE 2017. 4. 8. 19:26

출처 : git-scm.com


버전 관리란?

버전 관리란 무엇이며, 왜 이것을 알아야 할까? 버전 관리 시스템은 파일의 변화를 시간에 따라 기록하여 과거 특정 시점의 버전을 다시 불러올 수 있는 시스템이다. 이 책에는 소프트웨어의 소스 코드를 버전 관리하는 예만 나오지만 실제로는 모든 컴퓨터 파일이 버전 관리의 대상이 될 수 있다.

이미지나 레이아웃을 수정할 때마다 각각의 형태를 모두 보존하고 싶은 그래픽 디자이너나 웹 디자이너라면 버전 관리 시스템(Version Control System; VCS)을 사용하는 것이 현명할 수 있다. VCS를 사용하면 개별 파일 혹은 프로젝트 전체를 이전 상태로 되돌리거나 시간에 따른 변경 사항을 검토할 수 있으며, 문제가 되는 부분을 누가 마지막으로 수정했는지, 누가 언제 이슈를 만들어냈는지 등을 알 수 있다. 또한 파일을 잃어버리거나 무언가 잘못되어도 대개 쉽게 복구할 수 있다. 그리고 이 모든 장점을 누리는 데는 큰 노력이 들지 않는다.

로컬 버전 관리 시스템

대부분의 사람들이 버전 관리를 위해 쓰는 방법은 파일을 다른 디렉토리에 복사하는 것이다(똑똑한 사람이라면 디렉토리 이름에 시간을 넣을 것이다). 이 방법은 간단하고 자주 사용되는 방법이지만 실수가 발생하기 쉽다. 어느 디렉토리에서 작업하고 있었는지 잊어버리고 엉뚱한 파일을 덮어쓰거나 의도하지 않았던 위치로 복사할 수도 있다.

이 문제를 해결하기 위해 오래전에 프로그래머들은 간단한 데이터베이스에 파일의 변경 사항을 기록하는 로컬 버전 관리 시스템을 만들었다(그림 1-1 참조).


그림 1-1. 로컬 버전 관리 다이어그램

유명했던 VCS 도구들 중 현재에도 널리 쓰이는 것으로 RCS라 불리는 시스템이 있다. 그 예로 Mac OS X 운영체제에서는 개발 도구를 설치하면 RCS가 딸려온다. RCS의 기본적인 동작 방식은 각 리비전들 간의 패치 세트(patch set)라고 하는 데이터의 차이점들을 특별한 형식의 파일에 저장, 특정 시점의 파일 내용을 보고 싶을 때 해당 시점까지의 패치들을 모두 더하여 파일을 만들어내는 것이다.

중앙집중식 버전 관리 시스템

또 다른 문제는 시스템 외부에 있는 개발자들과 함께 작업하는 것이다. 중앙집중식 버전 관리 시스템(Centralized Version Control System; CVCS)은 이 문제를 해결하기 위해 개발됐다. CVS, Subversion, Perforce와 같은 시스템들이 여기에 속한다. CVCS에서는 버전 관리되는 모든 파일을 저장하는 하나의 서버와, 이 중앙 서버에서 파일들을 가져오는(checkout) 다수의 클라이언트가 존재한다. 오랫동안 사용된 이 방식은 지금까지도 버전 관리의 대표적인 방식이다(그림 1-2 참조).


그림 1-2. 중앙집중식 버전 관리 다이어그램

CVCS는 로컬 VCS에 비해 장점이 많다. 누구나 다른 사람들이 무엇을 하고 있는지 알 수 있고, 관리자는 누가 무엇을 할 수 있는지 꼼꼼하게 관리할 수 있다. CVCS를 관리하는 것은 수많은 클라이언트의 로컬 데이터베이스를 관리하는 것보다 훨씬 쉽다.

그러나 CVCS는 심각한 단점이 있다. 중앙 서버가 잘못되면 모든 것이 잘못된다는 점이다. 서버가 다운될 경우 서버가 다시 복구될 때까지 다른 사람과의 협업도, 진행 중이던 작업을 버전 관리하는 것도 불가능해진다. 중앙 데이터베이스가 저장된 하드디스크에 오류가 발생하고 백업도 없다면, 사람들이 각자 자신의 컴퓨터에 가지고 있던 스냅샷 외에는 그동안 쌓인 프로젝트의 이력을 모두 잃게 된다. 로컬 VCS 시스템도 같은 문제가 있다. 프로젝트의 모든 이력이 한곳에만 있을 경우 이것은 피할 수 없는 문제다.

분산 버전 관리 시스템

분산 버전 관리 시스템(Distributed Version Control System; DVCS)은 앞서 말한 문제를 해결하기 위해 개발되었다. Git, Mecurial, Bazaar, Darcs 등 DVCS에서는 클라이언트가 파일들의 마지막 스냅샷을 가져오는 대신 저장소(repository)를 통째로 복제한다. 따라서 서버에 문제가 생겨도 어느 클라이언트든 복제된 저장소를 다시 서버로 복사하면 서버가 복구된다. 체크아웃(checkout)을 할 때마다 전체 백업이 일어나는 셈이다(그림 1-3 참조).


그림 1-3. 분산 버전 관리 시스템 다이어그램

게다가 대부분의 DVCS에서는 다수의 원격 저장소(remote repository)를 갖는 것이 가능하기 때문에 동시에 여러 그룹과 여러 방법으로 함께 작업할 수 있다. 이로 인해 계층 모델(hierarchical model) 등 중앙집중 시스템에서는 할 수 없는 다양한 작업 방식(workflow)들을 사용해볼 수 있다.

'Programming > Git' 카테고리의 다른 글

Git_#1.6_도움말 보기  (0) 2017.04.09
Git_#1.5_최초 설정  (0) 2017.04.09
Git_#1.4_Git 설치  (0) 2017.04.09
Git_#1.3_Git 기초  (0) 2017.04.09
Git_#1.2_짧게 보는 Git의 역사  (0) 2017.04.09
posted by REDFORCE 2017. 3. 23. 11:02

이번 글에서는 std::thread 의 공유자원에 대한 사용 방법과


mutex / lock 에 대해서 정리해보도록 하겠습니다.




공유 자원 사용하기

  • 멀티스레드 프로그래밍에서는 공유 리소스 관리가 가장 큰 문제
  • 너무 빡빡하게 관리하면 성능 하락의 위험
  • 너무 느슨하게 관리하면 언제 터질지 모름
  • mutex를 사용하여 공유 리소스를 관리하는 것이 가장 일반적인 방법
  • mutex는 크리티컬 섹션 (Windows에서는)을 사용한다.



#include <mutex> 헤더가 필요하고~


std::mutex mtx_lock를 선언하여

lock() / unlock() 함수를 통해 공유 자원에 접근하고 관리 할 수 있습니다.


( [&] 람다가 등장해 있군요. 다시 복습차원에서... [&] 캡쳐를 했다는 것은 스코프 영역의 모든 변수를 참조 한다는 뜻 입니다! )



공유 자원을 사용 할 수 있는지 조사


그럼 공유 자원을 mutex를 통해 lock / unlock만 하면 되냐~?

당연히 그걸론 부족하조.


다른 쓰레드가 lock을 걸어서 자원을 쓰고 있는지 안쓰고 있는지 검사를 해야겠지요.


순서를 정리해보자면 다음과 같습니다.

  • (1) 스레드 A 가 mutex의 lock 을 호출 했을 때,
    (2) 이미 스레드 B에서 lock을 호출 했다면?
    (3) A는 B가 lock을 풀어줄 때 까지 대기한다.

  • 스레드 A는 일을 하고 싶어도 할 수가 없다.
    이때 try_lock() 을 사용한다!

  • 다른 스레드가 먼저 락을 걸었다면 대기하지않고 즉시 false를 반환,
    반대로 true를 반환한 경우 공유 리소스의 소유권을 가진다.


자동으로 lock 풀기


그럼 이번에는 혹여나 실수로 unlock을 호출하지 않은 경우는 어떻게 되는가?


당연히 실수로 unlock을 호출하지않고 나와버렸으니 스레드는 데드락에 빠지는 상황에 놓입니다.

이 상황은 프로그래머의 코드 상의 실수 보다는

스레드가 코드 수행 중 예외 상황이 발생하여 빠져 나가버리는 경우가 큽니다.

(프로그래머가 이런 실수를 했다면 그건...야근을 너무 많이 한 탓일 수도 = _=;;)


해서 이런 문제가 발생할 경우를 대비해


lock_guard 라는 유틸리티 클래스를 사용합니다.


scope를 벗어날 때 자동으로 unlock을 호출하는 녀석인데요.




std::lock_guard<std::mutex> guard(mtx_lock) 부분을 보시면


먼저 std::lock_guard<std::mutex> 타입형으로 guard 라는 변수와 함께 파라미터로 ( mtx_lock ) 을 받습니다.


따라서 뮤텍스를 받는 녀석이라는 건데


이녀석을 알아서 스레드 안에 넣어두면 guard가 함수의 스코프가 벗어날 때 unlock을 호출하여 줍니다.





이번엔 데드락 상황을 한번 연출 해보고 


빠져나가는 방법을 적어보도록 하겠습니다.



상황은 위 코드와 같이 isCheck( ) 안에서 guard를 통해 lock1 이 만들어졌을 상황인데


아래 다른 쓰레드가 Add ( )를 호출하는 바람에 또 다시 Guard가 만들어져 lock2 만들어지게 되면


결국 데드락 상황에 빠지게 됩니다.



해서 이런 반복적인 락으로 인해 빠지는 데드락을 회피 하고자 쓰는 mutex가 있는데요.


바로 std::recursive_mutex 를 이용 하시면 됩니다.




m_Mutex 선언 부를 보시면 타입이 std::recursive_mutex로 바뀐 것을 볼 수 있습니다.


아직 저도 정확히 recursive_mutex는 어떤식으로 회피를 하는지는 모르겠습니다만 원초적인 원리를 알게 되면 따로 또 포스트를 적겠습니다.




쓰레드를 딱 한번만 실행

  • 멀티스레드 환경에서 프로그램 실행중 단 한번만 코드 실행이 필요 할 때
  • 보통은 이중 조사로 구현하지만...(실수가 발생 할 수 있음)
  • std::call_once 를 사용하면 쉽게 가능~



사용 방법은 위와 같이


(1) std::once_flag 를 이용하여 플래그 값을 먼저 세워두시고 

(2) std::call_once 를 통해 플래그 값과 펑션을 던져 주시면 됩니다.


만약 이 플래그 값을 통해 펑션을 수행했다면 flag 값이 변경되고 나 사용했음~~ 하고 표기할 수 있게 됩니다.


이를 응용하여 스레드에서 flag값을 보고 이거 한번 해야되는데...했었나? 안했었나?? 하는걸 알 수 있게 됩니다.




여기까지 Modern C++ 12편으로 정리한 내용을 마치겠습니다.


부족한 내용이나 설명이 부실 한것도 많습니다만.



= _=)..아몰랑 일단 나만 알면되...

posted by REDFORCE 2017. 3. 23. 09:31

와...드디어 마지막 목차 인 thread로 왔습니다.


이틀 만에 이걸 싹다 다시 공부하며 정리하면서 올렸는데


다시 상기가 많이 된 것 같군요.


(근데 왜 정작 쓸 때는 또 다시 켜서 보는걸까.....= _=난 빠가야로 였단말인가)



목차


1. Auto

2. range based for

3. enum class

4. non-static data member initializers

5. initializer lists

6. default / delete definition

7. override / final

8. emplacement

9. constexpr

10. lambda

11. random

12. thread



12. thread


이번 글은 마지막인 thread에 대해서 정리해보도록 하겠습니다.


프로그래밍의 꽃이라 부를 수 있는 thread ( 쓰 뤠 두으우엑 우엑...공부할 때 마다 극혐 토나온닷..)



여기서 정리하는 thread는 C++ 표준 thread 라이브 러리 입니다. (std::thread)


#include <thread> 헤더가 필요하며


각 OS API의 쓰레드보다 사용하기가 쉽다는 장점이 있습니다.



std::thread의 사용 법은 아래와 같이 간단합니다.



std::thread 타입으로 thread를 선언하고 ( ) 안에 함수 내용을 적으시면 됩니다.

위와 같이 람다로 구현하는 방법도 가능합니다.



thread를 생성하고 특정한 시기에 동작하게 하려면 다음과 같이 수행하면 됩니다.



위 코드와 같이 myThread를 선언만 해놓고

정의는 추후에 하는 방식으로 내가 원하는 타이밍에 동작하게 할 수 있습니다.


다음, thread의 파라미터 값을 넣는 것도 아래와 같이 수행하면 넣을 수 있습니다.



위 코드의 마지막 줄에 람다 함수가 끝나고 " , 4" 를 넣어준것 과 같이 파라미터를 전달 하면 됩니다~



이번에는 클래스의 멤버 함수를 스레드에서 사용하는 방법입니다.


아마 이 방법이 제일 궁금하셨을텐데요.


위에 적어둔 방법들과 똑같이 thread의 매개변수로 펑션만 던져주시면 됩니다.




쓰레드 대기


thread 클래스의 join 함수를 사용하여 스레드가 종료 할 때 까지 대기하는 방법입니다.


 - join() 함수를 호출하면 blocking이 된다.

 - join() 함수를 호출할 수 있는 지 알기 위해서는 joinable() 함수를 사용한다.



쓰레드 식별자


쓰레드를 식별 하는 방법은 get_id() 함수를 사용하면 해당 쓰레드의 식별자를 얻을 수 있습니다.


get_id() 함수를 통해서 멀티쓰레드에서 각각의 쓰레드를 구분 할 수 있지요.


get_id() 함수를 사용하면 멀티스레드에서 공용 리소스에 접근하는 스레드를 알 수 있고,

특정 스레드만 접근할 수 있게 하는게 가능합니다.



thread 오브젝트와 (커널)스레드 분리

  • detach 함수를 사용하면 thread 오브젝트와 스레드 연결 고리를 떼어낸다.
  • detach 이후에는 thread 오브젝트는 제어 할 수 없다.
  • detach 와 스레드의 종료는 상관 없다.



thread를 한번 detach 한 순간 부터는 그 쓰레드는 완전 독립이 되기 때문에 join과 같은 함수를 사용 할 수가 없습니다.


따라서 그 쓰레드가 언제 어떻게 될진 그 쓰레드한테 달려있다고 볼 수 있지요.


저는 개인적으로 이런식으로 detach를 쓸일이 있나? 하는 생각도 듭니다만...


= _=뭐 언젠가 필요한 때도 있겠지요.



일시중지와 양보

  • sleep_for 와 sleep_until을 사용하면 스레드를 일시 중지 시킬 수 있다.
  • sleep_for 는 지정한 시간 동안 ( 예: 100 밀리 세컨드 동안만 정지)
  • sleep_until 은 지정 시간까지 ( 예: 16시 10분까지 정지)
  • yield를 사용하여 자신(스레드)의 활동을 포기하고 다른 스레드에게 양보한다.
    std::this_thread::yield();


thread 종료
  • 스레드가 실행 중에 프로그램이 종료되면 crash가 발생 할 수 있다.
  • 프로그램 종료 전에 꼭 스레드를 먼저 종료 시키고 프로그램을 종료하도록 한다.



여기 까지가 기본적인 std::thread에 대한 정리였습니다.


그리고 더 이제 심화적이고 중요한 thread의 공유자원 사용하는 방법과 mutex / lock 에 대해서 다음 글을 통해 살펴보도록 하겠습니다.


posted by REDFORCE 2017. 3. 23. 08:46

목차


1. Auto

2. range based for

3. enum class

4. non-static data member initializers

5. initializer lists

6. default / delete definition

7. override / final

8. emplacement

9. constexpr

10. lambda

11. random

12. thread


대망의 11편 랜덤 입니다!


11. Random


이번 글은 random에 대해서 정리해보도록 하겠습니다.



과거 C++ 03 까지만 해도


우리는 랜덤한 난수를 쓰기 위해


이런 코드를 작성했었지요...



음...참 그냥 무식한 난수분포기 였습니다.


srand...오랜만에보는군요 = _=)..




먼저 C++03과 C++11의 난수분포기 차이를 비교해보겠습니다.


  C++03 난수분포기

  C++11 난수분포기

 

  • C런타임 난수를 사용

  • 전역 함수 사용

  • 의사 난수 주기가 짧음 ( 최대 32767 )

  • 균등하게 분포되지 않음

  • 기능적으로 빈약함

 

  • 고품질의 난수 생성기와
    분포 클래스를 사용

  • 난수의 형/범위/분포 형태를
    세세하게 조절 가능


사용 방법은 먼저 아래와 같이 간단합니다.




#include <random>에 만들어져있는 std::mt19937 을 이용하는 것인데요.


std::mt19937은 난수 생성기로


Mersenne twister(32비트 버전) 과 std::mt19937_64(64비트 버전) 이 있습니다.



난수를 생성할 때 한번 seed 값을 이용하여 생성해보도록 할까요.





위와 같이 chrono를 이용하여 seed 값을 토대로 난수를 생성할 수 있습니다.


아..혹시나 seed가 뭔말임? 하고 이해 못하시는 분들이 계실까봐.



위쪽에 0 ~100 까지의 난수를 뽑아내기 위해 rand() % 101 을 한 경우가 있지요?


그와 같은 경우라 보시면 될 것 같습니다.

(아니야! 잘못됐어! 라면 지적 해주세요. 사실 저도 정확히 이게 맞나 긴가민가합니다)


[seed 뜻 수정 : 어떤 숫자를 가지고 특정한 수식을 적용 시켜 나온 숫자를 랜덤이라 할 때 필요한 수식에 대입할 숫자를 seed라 부름]



예측 불가능한 난수 생성


혹여나 보안 목적과 같이 절대 예측할 수 없는 난수를 생성하고 싶다면 비 결정적 난수 생성기를 사용해야합니다.


지금 우리가 본 mt19937은 의사 난수라 해서 우리가 일정범위를 지정하고 주관적인 의사가 들어간 난수 입니다.



그럼...예측 불가능한 난수를 어케만드냐?



std::random_device 를 이용하시면 됩니다.


난수 생성을 위해 화면상의 마우스 커서를 이리저리 움직여주세요~ 라는 프로그램같은걸 본적이 있으신가요?


예전에 저는 이런 것을 tortoiseGit 을 사용해 볼 때 난수 생성을 통해 암호알고리즘을 돌리는 것을 본적이 있습니다.



사실 소프트웨어적으로 난수를 생성하는 것은 비결정적 난수를 생성함에 한계가 있습니다.


따라서 이 난수를 하드웨어적으로 움직여서 난수 생성중인 본인도 알기 어려운 (마우스 움직임과 같은) 하드웨어를 이용하는 것이지요.


(난 마우스를 1px 우측으로 움직이고 있어! 라고 미세하게 조정이 가능한 신급 감각을 가진 인물이 라면 가능할지도..)



그래서 다음과 같이 랜덤한 디바이스를 통해 아래 코드 방식대로


비 결정적 난수를 생성 할 수 있습니다.



범위안의 난수 생성

  • 특정 조건 안에 만족하는 난수를 원한다 ( 예 : 1~ 100 사이 )
  • 난수를 특정 타입과 특정 범위 안에서 생성하기 위해서는
    난수 생성기에 난수 분포기를 더하여 난수를 생성한다.
  • 정수 타입의 난수를 분포 할 때는 uniform_int_distribution,
    실수 타입의 난수를 분포 할 때는 uniform_real_distribution 을 사용


위 설명대로 어떤 범위의 난수를 생성 할 떄는 다음과 같이 사용 할 수 있습니다.



차례대로 설명 드리자면 


먼저 난수 생성기! mt19937을 선언하여 rng1(3244) 라고 값을 넣어 아무 숫자나 생성합니다~


그 다음 난수 분포기 uniform_int_distribution 을 이용하여 난수의 분포범위를 결정합니다. ( -3, 3 ) 사이~



해서 난수 분포기 안에 난수 생성기를 넣지요!  // dist1( rng1 )



주의할 점.


std::uniform_intdistribution dist와 std::uniform_real_distribution은 포함 범위가 다르므로 주의!



std::uniform_int_distribution dist( -3, 3 )   // -3 이상,  3까지.


std::uniform_real_distribution<double> dist ( 0.0, 1.0 ) // 0.0 이상, 1.0 미만!



그 외 다양한 난수분포기 들이 있습니다만


대표적으로 사용 되는 녀석들만 적어보도록 하겠습니다.



bernoulli_distribution  분포기

 - 확률을 지정하면 이 확률에 근거하여 true 와 false 를 반환

(예: 몬스터 잡으면 n% 확률로 xx 아이템을 드롭!)


binomial_distribution 난수 분포기

 - 특정 확률로 n 회 실시 했을 때 몇 번 성공 할 것인가를 반환

(예: 사망 가능성(확률)이 있는 백신을 N 사람에게 투여하여 성공할 횟수는?)


normal_distribution 난수 분포기

 - 평균과 표준편차로 정규 분포 난수를 생성한다.

(예: 평균 키 173cm, 표준 편차 5cm 의 신장 데이터를 생성!)



위에 적힌 3가지 외에도 엄청나게 많은 난수 생성기와 분포기가 #include <random> 안에 들어가 있습니다만..


필요한건 그때 그때 찾아서 쓰시거나 직접 로직을 만드시는게 편할거라 봅니다.




결론. 개인적으로 srand() 보단정말 세밀하군요. |(O_ o)/

'Programming > C++' 카테고리의 다른 글

[12편-2] Modern C++ 정리: thread  (0) 2017.03.23
[12편-1] Modern C++ 정리: thread  (0) 2017.03.23
[10편] Modern C++ 정리: lambda  (3) 2017.03.23
[9편] Modern C++ 정리: constexpr  (0) 2017.03.23
[8편] Modern C++정리: emplacement  (0) 2017.03.23
posted by REDFORCE 2017. 3. 23. 07:43

목차


1. Auto

2. range based for

3. enum class

4. non-static data member initializers

5. initializer lists

6. default / delete definition

7. override / final

8. emplacement

9. constexpr

10. lambda

11. random

12. thread


와...드디어 10편까지 왔네요. 이틀 동안 열심히 적었습니다만


솔직히 많이 부족하고 너무 설명이 빈약하고 그냥 이런식으로 하면되!


라는 어조로 적어버린 것 같아 죄송합니다.



계속 노력해서 더 좋은 설명을 곁들이도록 할게요!



2. lambda


이번 글은 lambda에 대해서 정리해보도록 하겠습니다.


람다에 대해서 간단히 설명하자면 다음과 같습니다.


  • 'lambda 함수' 또는 '무명 함수' 라고 부르기도 한다.
  • lambda는 함수 오브젝트 이다.
  • C++의 표현력을 증가시켜 준다.
  • STL의 알고리즘을 더 간편하게 사용할 수 있다.
  • 규격에서는 lambda는 특별한 타입을 가지고 있다고 한다.
  • 단! decltype나 sizeof 에서 사용 할 수 없다.


람다의 사용 방법은 4가지만 잘 익혀두시면 됩니다.



위에서 차례대로 설명드리겠습니다.


먼저 어떤 스코프의 영역에서 람다를 쓰실려면    [ ] 를 이용하여 람다를 캡쳐~! 합니다.


그러면 컴파일러가 어? 람다 쓰넴. 하고 알게됩니다.



그 다음 이용할 ( ) 를 통해 파라미터 인수를 정의합니다.

(만약 딱히 파라미터 받을 거 없는데? 하면 안써도 됩니다)


그리고 { } 안에서 함수의 본체를 정의합니다.


마지막으로 ( ) 를 통해 우리가 만든 람다 함수를 호출하면 끝!



간단하게 위와 같이 사용하시면 됩니다.

(역시나 파라미터 없는데~? 하면 안써도 무방합니다)



그리고 람다 함수본체까지만 정의하고 auto를 이용해 함수를 auto func 에 담아둔 뒤


func( )를 이용한 호출도 가능합니다!


왜??  위에서 언급했다싶이 Lambda 또한 함수 오브젝트! 이기 때문이지요!



그리고 람다 자체를 함수의 파라미터로 이용할 수 있습니다.




람다 자체의 파라미터를 사용해보는 것도 확인해볼까요.



역시나 쉽습니다.


그냥 파라미터 값도 알아서 척척 넣어주게 되네요.




그 다음 lambda를 이용한 반환(리턴)을 써보겠습니다.



반환하는 방식도 여타 함수와 똑같습니다. 그냥 함수 본체에다 리턴만 알아서 하면되네요.




그럼 이 람다를 응용해서 함수안에 람다 함수를 넣어서 써보도록 하조



네...잘들어갑니다 ㅡ_ ㅡ)...그냥 함수나 어떤 스코프범위 안에서 


그냥  [ ] 캡쳐를 이용해서 선언만 하면 어디서든 람다를 바로바로 넣을 수 있습니다.



그리고 lambda 함수 자체를 반환하는 함수로 만들어 사용도 가능합니다.




그런데 개인적으로 이런식으로 쓰고 싶지는 않네요...


함수 포인터를 써야하는 과정에서 람다를 이용해 어딘가에 템플릿 형태로 만들어놓고 그걸 리턴 하는 용도로 쓴다면


나름 괜찮을 것 같습니다.



자 그런데..위 코드에 보면 [ ] (캡쳐) 부분에 [=] 이런식으로 들어간 게 보일 겁니다!


응? 못보던건데 저게 뭐지 할텐데요.



Capture 라 불리는 [ ] 구역의 용도는 정확히 


lambda를 정의한 scope 내의 변수를 capture 하는 용도입니다!


Scope의 모든 변수를 참조로 capture 할 때는 [&], 특정 변수를 참조로 capture 할 때는 [&변수]

Scope의 모든 변수를 복사로 capture 할 때는 [=], 특정 변수를 복사로 capture 할 때는 [변수]


를 적으면 됩니다!  ( 무슨말?? )


바로 한번 써보도록 할까요.




위와 같이 람다 함수내에서 지금 스코프의 영역에 있는 변수를 참조하여 값을 변경하거나 할 때는 [&]를 이용해 x 값을 받아 올 수 있습니다.



그럼 [=]를 이용한 복사는?



당연히 값 복사만 일어나기 때문에 스코프내의 변수를 바꿔버리는 행위는 에러를 뿜게 됩니다.


순전히 값 복사와 람수 함수 내부적으로 일어나는 변환에 대해서만 처리를 할 수 있습니다.



그러나 필요하다면 


mutable을 이용하여 다음과 같이 람다 함수 내에서만 일시적으로 x 값을 변경 할 수 있습니다.




람다 함수를 빠져나오면 다시 x 값은 원래 값으로 보존이 됩니다.



다음은 람다의 capture : default 에 대해서 보도록 하겠습니다. 

capture : default 라는 말은 우리가 람다를 씀에 있어서


capture가 어떻게 돌아가는지 본다는 뜻 입니다.



위와 같이 람다의 캡쳐 내에서 설정을 어떻게 하냐에 따라 스코프영역의 변수들의 참조/복사를 할 수 있습니다.



람다에 대한 특징을 정리해보겠습니다.


클래스에서 lambda 사용

  • 클래스의 멤버 함수에서 lambda 사용 가능
  • public, protected, private 멤버도 접근 가능
  • lambda는 클래스에서 friend로 인식
  • lambda에서 클래스 멤버를 호출 할 때는 this를 사용한다.


Generic lambdas

  • C++ 14
  • 인수 타입으로 auto를 사용할 수 있다.
  • 템플릿 인수와 같이 형 추론된다.
  • [ ] (const auto& x, const auto& y) { return x + y; }

  • 가변 인수로 사용 할 수 있다.
  • [ ] ( auto&&... args) { std::cout << sizeof...(args) << endl; } (1U, 2.1, nullptr, hoge{ } );

  • auto를 사용하는 것은 구현에서 필수는 아니다.
    다만 가독성을 위해 붙여주도록 하자!

마지막으로 generic lambda 사용을 보도록 하겠습니다.





여기까지가 C++ 14의 람다 였습니다.


람다에 대한것은 잘 사용하면 정말 편하고 코드를 간결하게 만들 수도 있으며

알고리즘을 구현하는데 있어서 많은 도움이 될 거라 봅니다.


저도 아직은 자주 쓰는 편은 아니지만


일부 구역에서는 람다를 이용해서 굳이 필요없는 펑션 구현을 줄일 수 있다는 점에서는 애용하고 있습니다.



많은 도움이 되셨기를....


혹시나 보셨다면 리플이라도...(굽신 굽신)