14 . GitHub 원격작업

버젼 제어(version control)는 다른 사람과 협업할 때 진정으로 다가온다. 우리는 이미 버젼 제어를 위해 필요한 작업 대부분을 수행했다; 한가지 빠진 것은 한 저장소에서 다른 저장소로 변경사항을 복사하는 것이다.

Git 같은 시스템은 임의 두 저장소 사이에 작업을 옮길 수 있는 기능을 제공한다. 하지만, 실무에서 다른 사람의 노트북이나 PC보다는 중앙 허브에 웹 방식으로 하나의 원본을 두고 사용하는 것이 가장 쉽다. 대부분의 프로그래머는 프로그램 마스터 원본을 GitHub, BitBucket, GitLab 호스팅 서비스에 두고 사용한다; 이번 학습 마지막 부분에서 이러한 접근법의 장점과 단점을 살펴본다.

세상 사람들과 현재 프로젝트에서 변경한 사항을 공유하는 것에서부터 시작해보자. GitHub에 로그인하고 나서, 우측 상단 아이콘을 클릭해서 planets 이름으로 신규 저장소를 생성한다:

(1단계) GitHub 저장소 생성

저장소 이름을 “planets”으로 만들고 “Create Repostiory”를 클릭한다:

(2단계) GitHub 저장소 생성

저장소가 생성되자 마자, GitHub는 URL을 가진 페이지와 로컬 저장소 환경설정 방법에 대한 정보를 화면에 출력한다:

(3단계) GitHub 저장소 생성

다음 명령어가 실제로 GitHub 서버에서 자동으로 수행된 것이다:

$ mkdir planets
$ cd planets
$ git init

mars.txt 파일을 추가하고 커밋한 이전 학습을 상기한다면, 로컬 저장소는 다음과 같이 도식적으로 표현할 수 있다:

Git 준비영역(Staging) 로컬 저장소

이제 저장소가 두개로 늘어서, 도식적으로 표현하면 다음과 같다:

신선한 신규 GitHub 저장소

현재 로컬 저장소는 여전히 mars.txt 파일에 대한 이전 작업정보를 담고 있다. 하지만, GitHub의 원격 저장소에는 아직 어떠한 파일도 담고 있지는 않다:

다음 단계는 두 저장소를 연결하는 것이다. 로컬 저장소를 위해서 GitHub 저장소를 원격(remote)으로 만들어 두 저장소를 연결한다. GitHub의 저장소 홈페이지에 식별하는데 필요한 문자열이 포함되어 있다:

GitHub 저장소 URL 발견장소

SSH에서 HTTPS로 프로토콜(protocol)을 변경하려면 ‘HTTPS’ 링크를 클릭한다.

HTTPS vs. SSH

부가적인 설정이 필요하지 않아서 여기서는 HTTPS를 사용한다. 워크샵 후에 SSH 접근 설정을 원할지도 모른다. SSH 접근이 좀더 안전하다. GitHub, Atlassian/BitBucket, GitLab의 훌륭한 지도서 중 하나를 따라하는 것도 좋다. GitLab은 온라인 동영상도 제공한다.

GitHub 저장소 URL 변경

웹 브라우져에서 URL을 복사하고 나서, 로컬 컴퓨터 planets 저장소로 가서 다음 명령어를 실행한다:

$ git remote add origin https://github.com/vlad/planets.git

Make sure to use the URL for your repository rather than Vlad’s: the only difference should be your username instead of vlad.

Vlad가 아니고 여러분 저장소의 URL을 사용했는지 확인한다: 유일한 차이점은 vlad 대신에 여러분의 사용자이름(username)이다.

git remote -v 실행해서 명령어가 제대로 작동했는지 확인한다:

$ git remote -v

origin   https://github.com/vlad/planets.git (push)
origin   https://github.com/vlad/planets.git (fetch)

origin 이름이 원격 저장소에 대한 로컬 별명이다. 원한다면 다른 명칭을 사용할 수도 있지만, origin 이름이 가장 일반적인 선택이다.

별명이 origin으로 설정되면, 다음 명령어가 변경사항을 로컬 저장소에서 GitHub 원격 저장소로 밀어 넣어 푸쉬(push)한다:

$ git push origin master

Counting objects: 9, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (9/9), 821 bytes, done.
Total 9 (delta 2), reused 0 (delta 0)
To https://github.com/vlad/planets
 * [new branch]      master -> master
Branch master set up to track remote branch master from origin.

프록시(Proxy)

만약 연결된 네트워크가 프록시를 사용한다면, “Could not resolve hostname” 오류 메시지로 인해서 마지막 명령어가 실패할 가능성이 있다. 이 문제를 해결하기 위해서, 프록시에 대한 정보를 Git에 전달할 필요가 있다:

$ git config --global http.proxy http://user:password@proxy.url
$ git config --global https.proxy http://user:password@proxy.url

프록시를 사용하지 않는 또다른 네트워크에 연결될 때, Git에게 프록시 기능을 사용하지 않도록 다음 명령어를 사용하여 일러준다:

$ git config --global --unset http.proxy
$ git config --global --unset https.proxy

비밀번호 관리자(password manager)

운영체제 위에 비밀번호 관리자(password manager)가 설정되어 있다면, 사용자이름(username)과 비밀번호(passord)가 필요로 할 때, git push 명령어가 이를 사용하려 한다. “Git Bash on Windows”를 사용하면 기본 디폴트 행동이다. 관리자 비밀번호를 사용하는 대신에, 터미널에서 사용자이름과 비밀번호를 입력하려면, git push를 실행하기 전에 터미널에서 다음과 같이 타이핑한다:

$ unset SSH_ASKPASS

git uses SSH_ASKPASS for all credential entry에도 불구하고, SSH나 HTTPS를 경유하여 Git을 사용하든 SSH_ASKPASS을 설정하고 싶지 않을 수도 있다.

~/.bashrc 파일 하단에 unset SSH_ASKPASS을 추가해서 Git으로 하여금 사용자명과 비밀번호를 사용하도록 기본설정으로 둘 수도 있다.

이제 로컬 저장소와 원격 저장소는 다음과 같은 상태가 된다:

첫번째 푸쉬(Push) 다음 GitHub 저장소

‘-u’ 플래그(flag)

Git 문서에서 git push과 함께 사용되는 -u 옵션을 볼 수 있다. git branch 명령어에 대한 --set-upstream-to 옵션과 동의어에 해당되는 옵션이다. 원격 브랜치를 현재 브랜치와 연결시키는데 사용된다. 그래서 git pull 명령어가 아무런 인자없이 사용될 수 있다. 원격 저장소가 설정되면, git push -u origin master 명령어만 실행시키면 연결작업이 완료된다.

또한, 원격 저장소에서 로컬 저장소로 변경사항을 풀(pull)해서 가져올 수도 있다:

$ git pull origin master

From https://github.com/vlad/planets
 * branch            master     -> FETCH_HEAD
Already up-to-date.

이 경우 가져오기 하는 풀(pull)은 아무런 결과가 없는데, 이유는 두 저장소가 이미 동기화가 되어서다. 하지만, 만약 누군가 GitHub 저장소에 변경사항을 푸쉬했다면, 상기 명령어는 변경된 사항을 로컬 저장소로 다운로드한다.

GitHub GUI

GitHub 웹사이트에서 planets 저장소를 찾아간다. Code 탭아래 “XX commits”(“XX”는 숫자) 텍스트를 클릭한다. 각 커밋 우측의 버튼 세개 여기 저기 둘러보고, 클릭해 본다. 버튼을 눌러서 어떤 정보를 모을 수 있거나 탐색할 수 있는가? 쉘에서 동일한 정보를 어떻게 얻을 수 있을까?

해답 (클립보드 그림을 갖는) 가장 좌측 버튼은 클립보드에 커밋 식별자 전체를 복사한다. 쉘에서 git log 명령어가 각 커밋에 대한 전체 커밋 식별자를 보여준다.

중간 버튼을 클릭하게 되면, 특정 커밋으로 변경한 내용 전체를 확인할 수 있다. 녹색 음영선은 추가를 붉은색 음영선은 삭제를 의미한다. 쉘에서 동일한 작업을 git diff로 할 수 있다. 특히, git diff ID1..ID2(ID1와 ID2은 커밋 식별자다) 명령어(즉, git diff a3bf1e5..041e637)는 두 커밋 사이 차이를 보여준다.

가장 우측 버튼은 커밋 당시에 저장소 모든 파일을 보여준다. 쉘로 이런 작업을 수행하려면, 해당 시점의 저장소를 checkout 해야 한다. 쉘에서 git checkout ID(여기서 ID는 살펴보려고 하는 커밋 식별자) 명령어를 실행하면 된다. checkout 하게 되면, 나중에 저장소를 올바른 상태로 되돌려 놓아야 된다는 것을 기억해야 됩니다.

GitHub 시간도장(Timestamp)

GitHub에 원격저장소를 생성하라. 로컬 저장소의 콘텐츠를 원격 저장소로 푸쉬하라. 로컬 저장소에 변경사항을 만들고, 변경사항을 푸쉬하라.

방금 생성한 GitHub 저장소로 가서 GitHub 변경사항에 대한 시간도장(timestamps)을 살펴본다. GitHub이 시간정보를 어떻게 기록하는가? 왜 그런가?

해답 GitHub은 시간도장을 사람이 읽기 쉬운 형태로 표시한다(즉, “22 hours ago” 혹은 “three weeks ago”). 하지만, 시간도장을 이리저리 살펴보면, 파일의 마지막 변경이 발생된 정확한 시간을 볼 수 있다.

푸쉬(Push) vs. 커밋(Commit)

이번 학습에서, “git push” 명령어를 소개했다. “git push” 명령어가 “git commit” 명령어와 어떻게 다른가?

해답

변경 사항을 푸쉬하면, 로컬에서 변경한 사항을 원격 저장소와 상호협의하여 최신 상태로 갱신한다. (흔히 다른 사람 변경시킨 것을 공유하는 것도 이에 해당된다.) 커밋은 로컬 저장소만 갱신한다는 점에서 차이가 난다.

원격 설정 고치기

원격 URL에 오탈자가 발생되는 일이 실무에서 흔히 발생된다. 이번 연습문제는 이런 유형의 이슈를 어떻게 고칠 수 있느냐에 대한 것이다. 먼저 잘못된 URL을 원격(remote)에 추가하면서 시작해 보자.

git remote add broken https://github.com/this/url/is/invalid

git remote로 추가할 때 오류를 받았나요? 원격 URL을 적법한지 확인해 주는 명령어를 생각해 낼 수 있나요? URL을 어떻게 수정할 수 있을까요? (팁: git remote -h를 사용한다.) 이번 연습문제를 수행한 다음에 원격(remote)를 지워버리는 것을 잊지말자.

해답 원격(remote)를 추가할 때 어떤 오류 메시지도 볼 수 없다. (원격 remote 를 추가하는 것은 Git에게 알려주기만 할 뿐 아직 사용하지는 않았기 때문이다.) git push 명령어를 사용하자마자, 오류 메시지를 보게 된다. git remote set-url 명령어를 통해서 잘못된 원격 URL을 바꿔 문제를 해결하게 된다.

GitHub 라이선스와 README 파일

이번 학습에서 GitHub에 원격 저장소를 생성하는 방법을 학습했다. 하지만, GitHub 저장소를 초기화할 때 README.md 혹은 라이선스 파일을 추가하지 않았다. 로컬 저장소와 원격 저장소를 연결시킬 때 두 파일을 갖게 되면 무슨 일이 발생될 것으로 생각하십니까?

해답 이런 경우, 관련없는 이력때문에 병합 충돌(merge conflict)이 발생된다. GitHub에서 README.md 파일을 생성시키고 원격 저장소에서 커밋작업을 수행한다. 로컬 저장소로 원격 저장소를 풀(pull)로 땡겨오면, Git이 origin과 공유되지 않는 이력을 탐지하고 병합(merge)를 거부해 버린다.

$ git pull origin master

From https://github.com/vlad/planets
 * branch            master     -> FETCH_HEAD
 * [new branch]      master     -> origin/master
fatal: refusing to merge unrelated histories

--allow-unrelated-histories 옵션으로 두 저장소를 강제로 병합(merge)시킬 수 있다. 이런 옵션을 사용할 때 주의한다. 병합하기 전에 로컬저장소와 원격저장소의 콘텐츠를 면밀히 조사한다.

$ git pull --allow-unrelated-histories origin master

From https://github.com/vlad/planets
 * branch            master     -> FETCH_HEAD
Merge made by the 'recursive' strategy.
 README.md | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 README.md