Gorio Tech Blog search

행렬의 N 거듭제곱 빠르게 구하기

|

참조

분류 URL
문제 행렬 제곱
응용 문제 스포일러 1
참조 라이브러리 sharifa_header.h, bit_library.h
이 글에서 설명하는 라이브러리 matrix.h

개요

시간복잡도: $ O(M^3 log N) $

공간복잡도: $ O(M^2) $

  • M은 행렬의 크기, N은 거듭제곱할 수를 나타낸다. 물론 행렬은 정사각행렬이다.

이 글에서는 행렬의 N 거듭제곱을 빠르게 구하는 방법을 설명한다. 사실 행렬의 N승은 정수의 N 거듭제곱 빠르게 구하기을 구하는 것과 근본적으로 동일하다.
다만 단순 정수의 곱이 아닌 행렬곱을 사용할 뿐이다.


알고리즘

먼저 예를 하나 들어보자. 행렬 $A$의 11승을 구하고 싶다고 하자. 어떻게 계산하는 것이 빠르겠는가?

단순무식한 방법을 적자면, $A^2$부터 구하면 된다. 그리고 $A^3$을 구한다. … 마지막으로 $A^{11}$을 구한다.
그러면 계산량은? 행렬곱 10번이다.

나쁘지 않은데? 라고 생각한다면, N이 크면 당연히 시간 초과임을 생각하라. N이 10억쯤 한다면? 10억 번을 1초 안에 계산할 수 있겠는가?
문제처럼 N(문제에서는 B이다.)이 $10^{11}$이나 한다면?

당연히 이 방법으로는 어림도 없다. 그럼 조금 더 좋은 방법을 생각해야 한다.

이제 $A^{11} = A \cdot A \cdot A \cdot A \cdot A \cdot A \cdot A \cdot A \cdot A \cdot A \cdot A$를 조금 다르게 써 보자.

[A^{11} = (A^5)^2 \cdot A]

만약에 여러분이, $A^5$를 알고 있다고 하자. 그러면 계산을 몇 번이나 해야 할까?
행렬곱은 딱 두 번 뿐이다.

조금 전 단순무식하게 구할 때를 떠올려보자. $A^5$로부터 $A^{11}$을 구하는 것은 행렬곱 연산이 6번이나 필요했다.
그러나 지금은 단 두 번만에 해결이 된다.

이제 $A^5$를 구하는 방법을 생각해보자. 다음을 생각할 수 있을 것이다.

[A^5 = (A^2)^2 \cdot A]

[A^2 = (A)^2]

그러면 이것을 어떻게 코드로 옮길 것인가?
고려할 것이 몇 가지 있다. 하나씩 살펴보자.

  • A의 N승을 위의 방법으로 구할 때 반드시 고려해야 하는 부분은, 현재 구하고자 하는 N이 짝수인지 홀수인지이다.
    • 만약 홀수라면, 다음과 같다. $ A^{2k+1} = (A^k)^2 \cdot A$이다.
    • 만약 짝수라면, 조금 더 간단하다. $ A^{2k} = (A^k)^2 $이다.
  • $A^N$을 구하기 전에, 먼저 2진수로 나타내 본다. N=11인 경우, N=$1011_2$이다.
  • $A^{11}$을 종이에 쓰고 천천히 생각해보라. 다음 두 가지 중 맞는 것은 무엇인가? N=$1011_2$이다.
    • 가장 끝자리 비트(LSB)부터 고려하여 위의 거듭제곱 알고리즘을 따른다.
    • 가장 앞자리 비트(MSB)부터 고려하여 위의 거듭제곱 알고리즘을 따른다.
    • 답은 MSB부터 고려하는 것이다. N=11로 놓고 종이에 써보면, MSB를 고려하는 것은 $A^{11}$을 구하지만, LSB를 고려하는 것은 $A^{13}$을 구하게 될 것이다.
    • 이것이 바로 matrix.h에서 bit_reverse 함수를 사용하는 이유이다.
    • 한 가지 더 주의할 점은, 비트 반전만 해서는 안된다. 100이 001로 바뀌어 그냥 1이 되기 때문이다. 따라서 자리수를 기억해 두어야 한다.

이제 $A^{11}$는 다음과 같은 순서로 구하면 된다는 것을 알 수 있을 것이다. 11=$1011_2$임을 기억하라.
물론 $A^0 = 1$이다.

이진수
1 $ (A^0)^2 \cdot A = A^1 $
0 $ (A^1)^2 = A^2 $
1 $ (A^2)^2 \cdot A = A^5 $
1 $ (A^5)^2 \cdot A = A^{11} $

조금 더 복잡한 예를 들어보겠다. 46=$101110_2$이다.

이진수
1 $ (A^0)^2 \cdot A = A^1 $
0 $ (A^1)^2 = A^2 $
1 $ (A^2)^2 \cdot A = A^5 $
1 $ (A^5)^2 \cdot A = A^{11} $
1 $ (A^{11})^2 \cdot A = A^{23} $
0 $ (A^{23})^2 = A^{46} $

이진수로 나타냈을 때 해당 자리가 1이면 제곱한 후 A를 추가로 곱하고, 0이면 그냥 제곱만 하면 된다.

행렬의 거듭제곱은 아주 복잡하지는 않다. 헷갈린다면 정수의 N 거듭제곱 빠르게 구하기을 참조하라.

구현

거듭제곱이 구현된 행렬 클래스는 다음과 같다. 필자의 편의를 위해, re_define.h#define을 활용한 많은 단축 선언들을 사용했다.

#include "sharifa_header.h"
#include "bit_library.h"

vector<vector<int> > mat_mul(vector<vector<int> > matrix_A, vector<vector<int> > matrix_B, int mod) {
    int m = matrix_A.size();
    vector<vector<int> > ret(m, vector<int>(m));
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < m; j++) {
            for (int k = 0; k < m; k++) {
              ret[i][j] += ((ll)matrix_A[i][k] * matrix_B[k][j]) % mod;
              ret[i][j] %= mod;
            }
        }
    }
    return ret;

}

vector<vector<int> > matrix_power_N(vector<vector<int> > matrix, int N, int mod, bool print) {
    int m = matrix.size(), len = binary_len(N);
    vector<vector<int> > original = matrix;
    vector<vector<int> > ret = vector<vector<int> >(m, vector<int>(m));
    for (int i = 0; i < m; i++)
        ret[i][i] = 1;
    
	N = bit_reverse(N);
    while (len--) {
        ret = mat_mul(ret, ret, mod);
        if (N & 1) {
            ret = mat_mul(ret, original, mod);
        }
        N >>= 1;
    }
    if (print) {
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < m; j++)
                printf("%d ", ret[i][j]);
            puts("");
        }
    }
    return ret;
}

문제 풀이

사용법은 어렵지 않다. 행렬을 2차원 벡터로 만든다.
그리고 행렬을 N승을 취한 후, print 인자를 true로 주어 matrix_power_N 함수를 호출하면 문제는 풀린다.

#include "../library/matrix.h"

#define mod 1000

int main_10830() {
    int m, N;
    scanf("%d%d", &m, &N);

    vector<vector<int> > original = vector<vector<int> >(m, vector<int>(m));
    for (int i = 0; i < m; i++)
        for (int j = 0; j < m; j++)
            scanf("%d", &original[i][j]);

    matrix_power_N(original, N, mod, true);
    return 0;
}

주의: 이 코드를 그대로 복붙하여 채점 사이트에 제출하면 당연히 틀린다. 못 믿겠다면, 저런 헤더 파일이 채점 사이트에 있을 것이라 생각하는가?

Comment  Read more

Markdown 사용법

|

마크다운을 쓸 때는 메모장으로도 되지만 JetBrains의 Webstorm에 Markdown support plugins을 설치하는 것이 도움이 된다.

참조: simhyejin.github.io


Python Code:

# coding=utf-8 
# 둥

import torch
from torch.utils.data import Dataset

MSCOCO_IMGFEAT_ROOT = 1048576
SPLIT2NAME = {
    'train': 'train2014',
    'valid': 'val2014',
}

class VQADataset:
    """
    A VQA data example in json file:
            "answer_type": "other",
    """
    def __init__(self, splits: str):
        self.name = splits
        print('self.name = splits:', self.name, sep='\t')
        self.splits = splits.split(',')

        # Loading datasets
        self.data = []
        for split in self.splits:
            self.data.extend(json.load(open("data/vqa/comn_sents_%s.json" % split)))
        print("Load %d data from split(s) %s." % (len(self.data), self.name))

        # Convert list to dict (for evaluation)
        self.id2datum = {
            datum['question_id']: datum
            for datum in self.data
        }

        # Answers
        self.ans2label = json.load(open("data/vqa/trainval_ans2label.json"))
        self.label2ans = json.load(open("data/vqa/trainval_label2ans.json"))
        assert len(self.ans2label) == len(self.label2ans)

    @property
    def num_answers(self):
        return len(self.ans2label)

    def __len__(self):
        return len(self.data)
print('what?', end='\t')

위의 글에 잘 설명되어 있지만, 복사해 놓고 쓰기 편하도록 본 글에 정리해 두었다.

참고로 넓은 개행을 하려면 한 줄을 띄우고 작성해야 한다.

좁은 개행은 문장 끝에 공백을 두 개 붙이면 된다.

그러나 여러 줄을 띄워도 효과는 똑같다. 공백 문자 (스페이스바) 도 마찬가지이다.

들여쓰기는 &nbsp;을 사용한다. 하나당 하나의 공백이다.

가장 큰 제목

적당한 제목

이 글은 보고 쓰기 위해 작성되었다.

[링크](https://google.com/)

링크

[참조 링크][1]

[1]:  https://greeksharifa.github.io/references/2018/06/29/markdown-usage/ "YW & YY's blog: markdown-usage"

참조 링크

빈 줄을 넣는 것을 추천한다.

url 링크: <https://google.com/>

url 링크: https://google.com/

어쩐지 처음으로 돌아가고 싶은가? 내부 링크는 이렇게 사용한다. [가장 큰 제목](#가장-큰-제목)

어쩐지 처음으로 돌아가고 싶은가? 내부 링크는 이렇게 사용한다. 가장 큰 제목

내부 링크의 #id 규칙은 다음과 같다.

  1. 가능하면 괄호는 쓰지 않는다. 제대로 작동하지 않는다.
  2. 영문자는 lowercase로, 한글은 그대로 둔다.
  3. 문자/숫자/space/하이픈 외의 문자는 모두 제거한다.
  4. space는 하이픈으로 대체한다.
  5. 만약 제목이 유일한 이름이 아니라면, -1 -2 -3 등을 붙인다.

> 인용하려면 이와 같이 한다.
>> 인용을 안에 또 하고 싶으면 이렇게 한다.
>>> 더 할 수 있다.

인용하려면 이와 같이 한다.

인용을 안에 또 하고 싶으면 이렇게 한다.

더 할 수 있다.

코드 블럭이다. ```을 써도 되고 ~~~을 써도 된다.
greetings = input()
print('Hello, Markdown!') # greetings는 안썼음

조그맣게 쓰고 싶다면 이렇게

*기울여 쓰기*     _기울여 쓰기_

**Bold로 쓰기**     __Bold로 쓰기__

***기울이고 Bold로 쓰기*** ___기울이고 Bold로 쓰기___

~~취소하기~~

기울여 쓰기 기울여 쓰기

Bold로 쓰기 Bold로 쓰기

기울이고 Bold로 쓰기 기울이고 Bold로 쓰기

취소하기


---

수평선. 앞뒤로 빈 줄을 하나씩 넣는 것이 좋다.

***

다시 수평선

___

-------------

* * *


수평선. 앞뒤로 빈 줄을 하나씩 넣는 것이 좋다.


다시 수평선





- [ ] 체크리스트
- [x] 완료 리스트

1. 순서 있는 리스트
2. 순서 있는 리스트
0. 사실 숫자는 순서가 없어도 된다.
-1232. 물론 음수는 안 된다.
11111111. 뭐 그래도 숫자는 맞춰 주는 것이 좋긴 하다.

* 순서를 없애고 싶으면 이렇게
  * 탭을 누르고 치면 이렇게 보인다.
  - 사실 *, -, + 를 섞어서 써도 잘 보인다.
  + 하지만 굳이 그렇게 할 필요는 없다.
       * 탭은 적당히 쳐야 잘 보인다.
                          * 너무 많이 하면 효과가 없다.
                          - 이미 효과가 없다.
  • 체크리스트
  • 완료 리스트
  1. 순서 있는 리스트
  2. 순서 있는 리스트
  3. 사실 숫자는 순서가 없어도 된다. -1232. 물론 음수는 안 된다.
  4. 뭐 그래도 숫자는 맞춰 주는 것이 좋긴 하다.
  • 순서를 없애고 싶으면 이렇게
    • 탭을 누르고 치면 이렇게 보인다.
    • 사실 *, -, + 를 섞어서 써도 잘 보인다.
    • 하지만 굳이 그렇게 할 필요는 없다.
      • 탭은 적당히 쳐야 잘 보인다. * 너무 많이 하면 효과가 없다. - 이미 효과가 없다. ```
Header 1 Header 2
Content 1 —의 개수는 상관없음.
Content 2 Content 4

| Header 1 | Header 2 | Header 3 | | :——– | :———-: | ——–: | | Left | Center | —의 개수가 달라도 됨. |


Header 1 | Header 2
------------------ | --------------------
Content 1 | ---의 개수는 상관없음.
Content 2 | Content 4

| Header 1 | Header 2 | Header 3 |
| :-------- | :----------: | --------: |
| Left | Center | ---의 개수가 달라도 됨. |

이미지는 이렇게(링크랑 비슷)

alt text alt text alt text 1: /test3.png

예시:

01_new_repository

``` 이미지는 이렇게(링크랑 비슷)

alt text alt text alt text 1: /test3.png

예시:

01_new_repository

편하게 하려면,

  • /public/img directory 안에 /categories_name/post_file_name directory를 만든다.
  • 만든 directory 안에 이미지를 붙여 넣는다.
  • WebStorm에 보이는 이미지에서 Ctrl + Shift + Alt + C 를 눌러 상대 경로를 복사한다.
  • 그리고 위의 예시의 src 항목에다 붙여넣기 하면 된다. 이때 반드시 /public/img/으로 시작해야 한다.
Comment  Read more

GitHub 사용법 - 02. 프로젝트와 repository 생성

|

주의: 이 글을 읽는 여러분이, 만약 git을 많이 써 봐서 익숙한 것이 아니라면, 반드시 손으로 직접 따라 칠 것을 권한다. 눈으로만 보면 100% 잊어버린다.


Remote Repository 생성

우선 https://github.com/에 접속하여 로그인한다(회원가입은 되어 있어야 한다). 그러면 다음과 비슷한 화면이 보인다.

01_new_repository

New repository를 클릭한 후, 프로젝트 이름을 git_tutorial으로 입력한다. 원하는 이름으로 해도 상관없다.
Description을 성심성의껏 잘 작성한다.

그리고 Initialize this repository with a README 체크박스에 체크한다.
체크하지 않고 만든다면 git repository를 처음 만든 이후 local repository와 연결하는 방법을 알려주는 안내 글이 뜬다. 이를 읽어 봐도 괜찮다.

지금은 체크하지 않는 것이 간단하므로 체크하지 않겠다. 그림에 체크되어 있는 것은 무시하자.

02_create_a_new_repository

마지막으로 Create repository를 누른다.

그러면 이제 여러분의 GitHub 계정에 git_tutorial이란 이름의 remote repository가 생성될 것이다.

02_create_a_new_repository


Local Repository 생성

그리고 이제 local에서 directory를 하나 생성한다. Directory 이름은 프로젝트와 같은 이름으로 하고, 생성하는 위치는 본인 마음대로 하면 된다.

02_create_a_new_repository

다음으로 명령창(터미널 또는 cmd 창)을 하나 띄운다. cd '경로명'/git_tutorial으로 하면 된다. 예시는 절대 경로이지만, 상대 경로로 해도 무방하다.
필자는 윈도우 환경(cmd)에서 진행하였다.

cd C:\Users\Sharifa-D\WebstormProjects\git_tutorial

그리고 다음 명령들을 수행해 본다. 만약에 git이 유효한 명령이 아니라는 error가 뜬다면, 본인의 운영체제이 맞는 git을 설치해야 한다.
윈도우의 경우 여기에서 다운받아 설치하면 된다(64bit 기준).

git init
git status

똑같은 과정을 거쳤다면, 다음 그림과 같은 화면이 나올 것이다.

02_create_a_new_repository

여기까지 했다면, local repository를 성공적으로 생성한 것이다. 오류가 뜬다면, 복잡해 보이는 에러 메시지를 그대로 복붙하여 구글링하면 된다.
여러분이 겪는 문제는 다른 사람들도 겪어 본 문제라는 것을 해결법과 함께 알 수 있다.


프로젝트 수정

파일 생성하고 수정하기

이제 여러분은 1) remote repository와 2) local repository를 모두 생성했다. 그러면 이제 두 repo(repository)를 연결하는 부분이 필요할 것이다.

연결은 어렵지 않다. 하지만 그 전에 파일을 조금 작성해보자. 빈 프로젝트 갖고 뭘 하기엔 심심하지 않은가?

first.py란 파일을 하나 생성한다. 그리고 다음과 같은 내용을 작성해보자.

print("Hello, git!") # instead of "Hello, World!"

심심하다면 python first.py를 명령창에 입력해 보자.

그리고 git status를 다시 입력한다. 조금 전과는 다른 화면을 볼 수 있다.

02_create_a_new_repository

여기서 중요한 정보를 몇 개 찾을 수 있다. 앞으로 git을 사용할 때에는 무슨 메시지가 뜨는지 (전부) 살펴보는 것이 굉장히 중요하다. 뭔가 잘못되었는지 아닌지를 볼 수 있기 때문이다.

위에서부터 하나씩 살펴보면 아래와 같다.

  • On branch master
    • 지금 작업하는 branch가 master라는 의미이다. 본인이 어떤 branch에서 작업 중인지 확인하는 습관을 반드시 가지도록 한다. branch를 잘못 옮긴 줄 모르고 작업을 이어갔다가는 큰일 날 수 있다.
  • No commits yet
    • 아직 생성한 commit이 없다는 뜻이다. 이후에 commit을 추가하면, 이 부분이 다르게 보일 것이다.
  • Untracked files: (use “git add ..." to include in what will be committed)
    • 여러분이 수정하긴 했지만 staging area에 올라가지 않은 파일의 목록이다. staging area에 올라갔다는 말은 track한다는 말과 같다.
  • first.py
    • 여러분은 아직 git add 명령을 사용하지 않았기 때문에 수정/생성/삭제한 유일한 파일인 first.py가 tracking되지 않고 있다.
  • nothing added to commit but untracked files present (use “git add” to track)
    • tracking하려면 git add를 쓰라고 한다. 메시지에는 도움이 되는 내용이 많다.

조금 더 자세히 설명하기 위해, second.py 파일을 생성한다.

print("Why don't you answer me, git?")

git add

그리고 조금 전 메시지가 친절히 알려줬던 git add 명령을 사용하려고 한다. 명령창에 다음과 같이 입력한다.

git add first.py

이제 다시 한번 git status를 입력하면, 아까보다 메시지가 더 많은 것을 확인할 수 있다.

02_create_a_new_repository

그림을 보면 tracking되고 있는 파일은 초록색, untracked file은 빨간색으로 되어 있음을 알 수 있다. 여러분은 first.pygit add로 추가했기 때문에 초록색으로, second.py는 그러지 않았기 때문에 빨간색으로 남아 있음을 확인할 수 있다.

이번에는 다른 명령을 연습해보자. git add .을 입력한다. git status로 확인해보면?

02_create_a_new_repository

.의 의미는 모든 파일과 디렉토리이다. 즉, 여러분은 프로젝트에 존재하는 모든 파일(first.pysecond.py)를 staging area에 추가한 것이다.

옵션으로, git add의 다양한 버전을 표로 정리해 두었다.

명령어 Description
git add first.py first.py 파일 하나를 staging area에 추가한다.
git add my_directory/                               my_directory라는 이름의 디렉토리와 그 디렉토리 안의 모든 파일과 디렉토리를 staging area에 추가한다.
git add . 현재 폴더의 모든 파일과 디렉토리, 하위 디렉토리에 든 전부를 staging area에 추가한다. 규모가 큰 프로젝트라면 써서는 안 된다.
git add -p [<파일>] 파일의 일부를 staging하기
git add -i Git 대화 모드를 사용하여 파일 추가하기
git add -u [<경로>] 수정되고 추적되는 파일의 변경 사항 staging하기

옵션: 같은 파일이 changes to be committedUntracked files 모두에 있는 경우

이 부분은 옵션이다. git이 아직 잘 이해가 되지 않는다면, 반드시 할 필요는 없다. 여기로 바로 넘어가면 된다.

first.py 파일을 다음과 같이 수정한다.

print("Hello, git!") # instead of "Hello, World!"
print("Don't you hear me, git?")

그리고 다른 명령을 하지 않은 채로 git status를 명령창에 입력한다. 그럼 다음과 같을 것이다.

02_create_a_new_repository

여러분이 한 것을 되짚어 보면 다음과 같다.

  1. first.py를 생성 및 수정하였다.
  2. first.pygit add 명령으로 staging area에 추가하였다.
  3. first.py를 또 수정하였다.
  4. 다른 명령(git addgit commit 등)을 하지 않는 채로 git status로 상태를 확인하였다.

이런 과정을 거쳤을 때 여러분은 동일한 파일이 changes to be committedUntracked files에 모두 있는 광경을 볼 수 있는 것이다.

즉, 이는 오류가 아니라,

  • 이미 git add로 추가한 적이 있으니 changes to be committed에 있는 것이고
  • 그 이후에 수정한 사항은 staging area에 올라가지 않았으니 Untracked files에도 있는 것이다.

어렵지 않게 이해할 수 있을 것이다.

옵션: git add 취소, git rm –cached <file>

여러분이 메시지를 꼼꼼히 읽어봤다면, 다음과 같은 문구를 보았을 것이다.

(use “git rm –cached ..." to unstage)

이는 staging area에 올라간 파일을 unstage하겠다는 뜻으로, git add를 취소하는 것과 같은 효과를 가진다. 즉 cached<file>을 (staging area에서) rm(remove)하겠다는 의미이다.

무슨 일을 하는지 알았으니, git rm --cached first.py를 명령창에 입력한다. 그리고 git status를 쳐보자.

02_create_a_new_repository

에러가 뜬다. 메시지를 의역하면, first.py가 실제 파일 내용이랑 git이 인식하는 파일 내용이 달라서 staging area에서 제거할 수 없다는 뜻이다.

어차피 여러분은 이 파일을 unstage하는 것이 목적이었으므로, git add first.py이후 git rm --cached first.py를 입력해주면 그만이다. git status로 상태를 확인해주자.

02_create_a_new_repository

이제 옵션을 안 한 상태로 되돌리기 위해, first.py에 추가한 내용을 지우고 git add .를 입력한다.

git commit

현재 다음과 같은 상태일 것이다.

02_create_a_new_repository

이제 commit을 할 차례이다. 커밋이란 수정사항들을 하나로 묶는 것이라 보면 된다.
실제 프로젝트에서 하나의 커밋이란 하나의 기능이라 보면 된다. 하니의 기능이란 새 기능일 수도 있고, 버그 수정일 수도 있고, 단순 개선 사항일 수 있다.

커밋은 여러 종류가 있지만, 가장 간단한 버전은 다음과 같다.
주의: 아직 명령창에 적지 않는다.

git commit -m “commit-message”

명령어를 입력할 때 -에 알파벳을 붙여 쓰는 경우가 있다. 이는 옵션을 주겠다는 의미이다.
-m 옵션을 주면서 "로 묶은 메시지를 전달하면, commit-message라는 description으로 커밋을 하나 만든다라는 의미가 된다.

옵션: 좋은 commit message 작성법

왜 명령창에 적지 말라고 했냐면, 이 방식으로 하는 것은 큰 프로젝트를 다룰 때 굉장히 안 좋은 습관이다. 고작 한 문장짜리로 커밋의 모든 내용을 설명할 수 있겠는가? 만약에 커밋 내용이 다음과 같은 일을 한다고 하자.

이 커밋은 #101번과 #104번 이슈를 해결하기 위해 작성된 것이다. 이 문제에 관한 자세한 내용은 #203번과 #223번을 참조하라.
해당 문제를 해결하기 위해 다음과 같은 방법을 사용하였다. 블라블라
문제는 해결되었지만, 아직 다음과 같은 (사소한) 문제가 남아 있다. #401번을 참조하라.

한 문장에 실수 없이 적을 수 있겠는가?
자신이 있다면 말리진 않겠지만, 별로 좋은 습관이 아니란 것은 명확하다.

좋은 commit message 작성법에 관한 내용은 여기를 참조하라.

다시 commit하기

아무튼 다음과 같이 입력한다.

git commit

그럼 뭔가 화려한 편집 창이 보인다. vi 편집기라고 보면 된다.

02_create_a_new_repository

i를 누른다. insert를 한다는 뜻이다.
그리고 commit message를 최대한 상세히 입력한다.

다 입력했으면, ESC를 누른다. 그리고, :wq를 입력한 후 Enter를 누른다.

02_create_a_new_repository

vi 편집기에서는, 입력 모드와 명령 모드가 있다.
입력 모드는 일반적인 텍스트 편집기와 같다.
명령 모드는 옵션을 주고 여러 조작을 할 수 있다. 자세한 설명은 Vim 사용법를 참조하면 된다. 여기서는 w는 저장이고 q는 quit을 의미한다는 것만 알아도 된다.

이제 commit에 대한 간단한 설명이 끝났다.


Remote & Local Repository 연결

이제 연결을 할 차례이다. 근데 할 것이 한 가지 더 남았다. 사용자 등록 과정이다.

등록 과정은 두 가지 방법이 있다. 전역 설정을 하느냐, repo 별로 설정을 하느냐이다.

  • 전역 설정
    • git config --global user.name "Your name"
      • 여러분의 github 아이디를 적으면 된다.
    • git config --global user.email "Your email"
      • 여러분의 github 이메일 계정을 적으면 된다.
  • repo별 설정
    • git config user.name "Your name"
      • –global 옵션이 없다. 여러분의 github 아이디를 적으면 된다.
    • git config user.email "Your email"
      • 역시 –global 옵션이 없다. 여러분의 github 이메일 계정을 적으면 된다.

전역 설정과 repo별 설정의 차이를 굳이 설명할 필요는 없을 것이다.

일단은 global 설정부터 시작하자. Your name/email은 여러분 스스로 입력하길 바란다.

git config –global user.name “Your name” git config –global user.email “Your email”

추가로 해야만 하는 것은 없지만, 등록된 사용자를 확인하는 방법도 알아야 하지 않겠는가?

명령어 Description
git config –global –list 전역 설정 정보 조회
git config –list repo별 설정 정보 조회

02_create_a_new_repository

이제 진짜로 연결하는 과정이다. 딱 한 문장으로 끝난다.

git remote add <remote repo 이름> <repo url>
ex) git remote add origin https://github.com/greeksharifa/git_tutorial.git

url은 여기서 확인할 수 있다.

02_create_a_new_repository

일반적으로 remote repo의 이름은 origin으로 둔다.

옵션: git 설정하기

다음 명령은 Git의 출력결과 색상을 활성화하는 명령이다.

git config --global color.ui “auto”

앞으로 git push를 할 때마다 git 비밀번호를 입력해야 하는데, 이것이 매우 귀찮다면 비밀번호를 저장해 둘 수 있다.

git config --global credential.helper store

단 보안상의 이슈가 걱정된다면, store 대신 cache 옵션을 주어 15분간 저장되게 할 수 있다. 15분 대신 다른 시간을 지정하려면 다음과 갈이 한다.

git config --global credential.helper cache –timeout 86400

git pull, git push

이제 정말로 연결한 remote repo에 local repo의 수정사항을 올릴 때가 되었다. push 명령은 어렵지 않다.

git push origin master

한동안 origin이라는 이름만 쓰고 master branch만 이용할 계획이라면, 위의 명령에 -u 옵션을 붙인다. 즉, git push -u origin master라고 입력하면 된다.
그러면 앞으로 git push만 입력해도 origin master에 push가 이루어진다.

그런데 이때 그냥 push를 하면 error가 뜰 수 있다. 이는 remote repo를 만들 때 README.md 파일을 생성했기 때문이다.
GitHub Instruction에서 간략히 설명한 대로, git pull origin master(혹은 그냥 git pull)으로 remote repo의 변경사항을 local repo로 받아온다.

그리고 현재의 master branch와 remote repo의 branch를 연결하기 위해, 다음과 같이 입력한다.
여기서 upstream branch란 remote repo의 branch를 가리키는 말이라 봐도 좋다.

git push –set-upstream origin master

02_create_a_new_repository

이제 브라우저에서 git_tutorial repo를 확인해 본다.

02_create_a_new_repository

끝났다!


다음 글에서는 프로젝트 clone, status, .gitignore에 대해서 알아본다.


Git 명령어

GitHub 사용법 - 00. Command List에서 원하는 명령어를 찾아 볼 수 있다.

Comment  Read more

GitHub 사용법 - 01. 소개

|

Git이란?

Git은 버전 관리 시스템으로, 파일의 변경 내용을 계속 추적하도록 개발된 것이다. 즉 Git은 분산 버전 관리 시스템으로, 모든 사람이 프로젝트의 현재와 과거 모두의 전체 history를 갖고 있는 것이다.

GitHub이란?

GitHub은 Git repository를 업로드하는 웹사이트이다. 여러분이 알고 있는 그 깃헙 맞다.

Git을 사용하는 이유까지 설명하지는 않도록 하겠다.


Git에서 사용되는 개념

Repository 저장소

저장소는 당연히, 프로젝트 파일을 모아둔 곳이다. 하나의 root directory와 비슷한 개념이다.

저장소에는 크게 세 가지 종류가 있다고 생각해도 무방하다. 이 중 두 개는 거의 비슷한데, 소유자가 다를 뿐이다.

  1. 나의 remote repository
  2. 다른 사람의 remote repository
  3. 나의 local repository

4. 다른 사람의 local repository

물론 4번은 여러분이 신경쓸 부분은 아니다. 따라서 세 가지만 생각하면 된다.

각 repository 사이에서 상호작용하는 과정은 다음과 같은 것들이 있다. 다른 사람이 하는 것은 생각하지 않도록 하자.

  1. 다른 사람의 remote repository(2)를 나의 remote repository(1)로 가져오는 것(fork)
  2. 나의 remote repository(1)을 나의 local repository(3)으로 가져오는 것(clone)
  3. 나의 local repository(3)의 변경사항을 나의 remote repository(1)에 반영하는 것(push)
  4. Fork로 가져온 프로젝트인 나의 local repository(3)의 변경사항을 나의 remote repository(1)에 반영시킨 후(push), 다른 사람의 remote repository(2)에 반영하는 것(pull request). 이를 GitHub 프로젝트에 기여했다고 한다.

Init, Clone

프로젝트를 시작하는 과정은 다음과 같다.

  1. 먼저 local에서 directory를 하나 생성한다. 이름은 프로젝트 이름으로 한다.
  2. 생성한 directory에서 git init 명령을 입력한다.
  3. 브라우저에서 git repository를 하나 생성한다.
  4. 브라우저에 보이는 안내를 따르면 된다. git remote add origin ... 명령을 입력한다.
  5. 다음 Add, Commit, Push과정을 따르면 된다.

이미 일부 혹은 전체가 만들어져 있는 프로젝트를 local에 받아와서 하고 싶을 때가 있다. 이는 다음 과정을 따른다.

  1. git clone ... 명령으로 remote repository를 나의 local repository(3)으로 받아온다.
  2. 끝. 이제 개발을 시작하면 된다.

Add, Commit, Push

여러분이 혼자서 간단한 프로젝트를 진행하게 된다면 가장 많이 쓰게 되는 명령들이다.

앞에서 말한 repository 상호작용 과정 중 3번을 가장 많이 하게 되는데, 이는 총 4단계로 이루어진다. 물론 경우에 따라 다른 과정이 추가될 수도 있다.

  1. 파일을 프로젝트 목적에 맞게 수정하고 저장한다. 즉 개발 과정이다.
  2. Add: 이제 파일을 cache에 잠시 올려 놓는다. 이 과정이 필요한 이유는, 하나의 commit(한번에 반영할 수정사항)에 여러분이 원하는 파일만 반영할 수 있도록 하기 위함이다. 만약에 반강제적으로 모든 파일이 반영되어야만 한다면, commit이 제 역할을 하지 못하게 될 수 있다.
  3. Commit: 이제 원하는 만큼의 수정사항을 하나의 commit으로 묶는다.
  4. Push: 이제 commit을 진짜로 나의 remote repository에 반영하는 과정이다.

Commit에는 단지 수정사항을 정리하는 것 외에 해주어야 하는 것이 두 가지 더 있다.

  • Commit message: commit할 때 같이 작성한다. Commit message란 이 commit이 어떤 수정사항을 담고 있는지를 알려주는 것이다. 자세히 쓸수록 좋다.
  • Tag: 여러분이 생각하는 그 태그 맞다. 블로그의 글에 달려 있는 태그랑 같은 기능을 한다. 포스팅 대신 commit을 참조하는 것이 다를 뿐이다.

Branch 브랜치

새로운 기능을 개발하거나 테스트를 할 때 사용하는 독립적인 commit history이다. 나무에서 메인 줄기가 아닌 옆으로 빠져나온 나뭇가지를 생각하면 된다.

Branch가 필요한 이유는 무엇인가? 혼자서 간단한 것을 할 때라면 사실 branch를 새로 만들 것도 없이 그냥 진행해도 별 문제는 없다. 하지만 프로젝트의 규모가 커지거나 여러 사람이 협업해야 한다면 branch는 필수이다. 모두가 master branch(메인 줄기)를 직접 수정하려고 들면 큰일난다.

모든 Git 프로젝트는 기본적으로 master branch를 갖는다. master branch가 나무의 메인 줄기로서, 검증이 끝난 프로젝트의 결과물이라 할 수 있다.

branch 간 상호작용은 꽤 종류가 많지만, 여기서는 몇 가지만 간략히 소개한다.

  1. branch에서 새 branch를 생성하는 것(git branch ...)
  2. 어떤 branch에서 다른 특정 branch로 옮겨가는 것(checkout)
  3. 검증이 끝난 branch를, 그 branch를 생성한 주 branch에 합치는 것(merge)

새로운 기능을 개발하여 추가하는 과정은 대개 위의 세 과정을 따른다. 물론 2번과 3번 사이에 개발 과정이 있을 것이다.

이렇게 새로운 기능을 개발하는 branch를 feature(topic) branch라 부른다. 또 구버전 소프트웨어 지원 등의 이유로 별도의 branch가 필요한 경우 이를 release branch라 부른다.

Issue

  • 기능에 대해 논의하거나
  • 버그 수정사항을 알리거나
  • todoList로 활용한다.

협업할 때는 당연히 필요하고, 혼자 할 때도 bugList와 todoList로 쓰면 유용하다.


Git 명령어

GitHub 사용법 - 00. Command List에서 원하는 명령어를 찾아 볼 수 있다.

Comment  Read more

GitHub 사용법 - 00. Command List

|
명령어 설명
git init local repo를 생성한다.
git status [-s] 현재 local branch의 git 상태를 확인한다(수정 파일, cache, commit 등) -s 옵션은 간략히 표시한다.
   
git add <file | directory> stage에 파일이나 디렉토리 혹은 전체(*)를 올린다.
git add -p|-i|-u 각각 부분/대화형/수정 사항을 stage에 추가
git rm --cached <file | directory> 파일이나 디렉토리를 cache에서 제거한다.
git commit [-m “commit message”\ 수정사항들을 하나의 커밋으로 묶고 커밋 메시지를 작성한다.
git commit -v git diff 명령을 포함하는 커밋 메시지 편집창을 열어 커밋한다.
git diff [HEAD] 마지막 커밋과 현재 수정사항 사이의 차이를 보여준다.
git diff [<branch1>] <branch2> 다른 브랜치와의 차이를 보여준다.
git diff [<commit1>] <commit1> 다른 커밋과의 차이를 보여준다.
   
git tag <tag> [-a [-m] ] 마지막 커밋에 태그를 붙인다. -a 옵션은 Annotated 태그를 가리킨다. -m 옵션은 메시지를 작성한다.
git tag <tag> <commit> 지정한 코드에 해당하는 커밋에 태그를 붙인다.
git tag 태그 목록을 보여준다.
git show <tag> 해당 태그에 대한 자세한 설명을 보여준다.
   
git remote add origin <remote repo 주소> local branch를 remote branch와 연결시킨다.
git clone <remote repo 주소> remote repo의 파일 복제본을 local로 가져온다. local repo가 생성된다.
   
git log [--oneline] 현재 브랜치의 commit log를 표시한다. --oneline 옵션은 한줄로 간략히 표시한다.
git log origin/master..[HEAD] remote repo에는 없고 HEAD에는 있는 커밋을 표시한다.
git log --graph 현재 브랜치의 commit log를 그래프 형태로 보여준다.
   
git branch [--list | -r -a] local/remote/전체 repo의 branch 목록 조회
git checkout [-b] <branch> 선택한 branch로 이동. -b 옵션은 브랜치를 생성하면서 이동
git checkout -t [-b] <origin\/branch> 선택한 remote branch의 파일을 다운로드하면서 checkout
git branch -d[-D] <branch> 선택한 local branch 삭제, -D 옵션은 강제 삭제
git push -d origin <branch> remote branch 삭제
git fetch remote branch 목록 업데이트
   
git merge <branch> 현재 브랜치에서 해당 브랜치의 수정사항을 가져온다.
git merge <branch1> <branch2> branch2의 변경사항을 branch1로 가져온다.
git rebase <branch> 현재 브랜치의 base를 해당 브랜치의 tip으로 설정하여, 해당 브랜치의 변경사항을 가져온다.

기타 명령어 설명
git help [<command>]
git [<command>] --help
man git-[<commmand>]
명령어에 대한 도움말을 볼 수 있다.
git config [--global] user.name “Your name” local repo의 git id(name)을 Your name으로 설정한다. --global 옵션을 주면 모든 local repo에 적용한다.
git config [--global] user.name “Your name” local repo의 git email을 Your email으로 설정한다. --global 옵션을 주면 모든 local repo에 적용한다.
git config [--list | <config settings> \ config 세팅 상태를 볼 수 있다. --list 옵션은 config 세팅 전부를 보여준다.
git config --global color.ui “auto” Git의 출력결과 색상을 활성화
git config --global credential.helper store 비밀번호 저장
git config --global credential.helper cache –timeout “seconds” seconds 초 동안 비밀번호 저장
Comment  Read more