728x90

 

Class


ES6에서 class 라는 문법이 추가 되었고, 기존의 prototype 기반으로 클래스를 만드는 것보다 명료하게 클래스를 만들 수 있게 되었습니다.

 

 

1. 클래스 정의

ES6에서 클래스는 특별한 함수입니다. 그렇기 때문에, 함수와 동일하게 클래스 선언(Class declarations)과 클래스 표현(Class expressions)으로 클래스를 만들 수 있습니다 ([자바스크립트] 함수(Function), 즉시실행함수(Immediately-invoked function expression) 참고)

 

클래스 선언(Class declarations)

class Polygon { constructor(height, width) { this.height = height; this.width = width; } }

클래스 선언은 함수 선언과 달리 호이스팅 되지 않습니다. ([자바스크립트] 유효범위(Scope)와 호이스팅(Hoisting) 참고)

var p = new Polygon(); // ReferenceError class Polygon {}

클래스 선언은 호이스팅 되지 않습니다.

 

클래스 표현(Class expressions)

익명 클래스와 기명 클래스를 만들 수 있습니다.

// unnamed var Polygon = class { constructor(height, width) { this.height = height; this.width = width; } }; // named var Polygon = class Polygon { constructor(height, width) { this.height = height; this.width = width; } };

클래스 표현(Class expressions) 또한 클래스 선언(Class declarations)와 같이 호이스팅(hoisting)이 되지 않습니다.

 

 

2. 메소드 정의

ES6의 class에서, 클래스의 몸통(body)는 {} 이며, class 메소드는 class의 {} 안에 정의해야 합니다.

 

Strict mode

클래스 몸통(body)는 strict mode로 실행됩니다. ([자바스크립트] 엄격 모드(strict mode) 참고)

 

생성자(Constructor)

생성자 메소드는 객체의 생성과 초기화를 하는 특별한 메소드입니다. 클래스에서 constructor 이름을 갖는 메소드는 하나여야 합니다.

var Polygon = class { constructor(height, width) { this.height = height; this.width = width; } constructor(height2, width2) { this.height = height2 * 2 ; this.width = width2 * 2; } };

constructor는 하나여야 합니다.

 

생성자 메소드에서 super 키워드를 통해 상위 클래스의 생성자 메소드를 호출 할 수 있습니다.

class Polygon { constructor(height, width) { this.height = height; this.width = width; } } class Square extends Polygon { constructor(length) { // length로 다각형의 넓이와 높이를 정의하기 위해 부모클래스의 생성자를 호출합니다. super(length, length); // Note: 파생 클래스에서, 'this'를 사용하기 전에는 반드시 super()를 // 호출하여야 합니다. 그렇지 않을 경우 참조에러가 발생합니다. this.name = 'Square'; } get area() { return this.height * this.width; } set area(value) { this.area = value; } } var test = new Square(4); console.log(test.area);

생성자 메소드에서 this를 사용하기 위해서 super 메소드를 먼저 호출해야 합니다.

set, get 함수의 선언법은 [자바스크립트] getter, setter 참고바랍니다.

 

프로토타입 메소드(Prototype methods)

class Polygon { constructor(height, width) { this.height = height; this.width = width; } get area() { return this.calcArea(); } calcArea() { return this.height * this.width; } } const square = new Polygon(10, 10); console.log(square.area);

 

ES6는 메소드 정의를 위한 더 짧은 구문이 도입되었습니다. 다음 코드가 주어지면,

var obj = { foo: function() {}, bar: function() {} };

ES6에서는 아래와 같이 줄일 수 있습니다.

var obj = { foo() {}, bar() {} };

 

정적 메소드(static methods)

static 메소드는 클래스의 인스턴스 (var a = new testFunc()) 필요없이 호출 가능합니다. 또한 클래스의 인스턴스에서 static 메소드를 호출 할 수 없습니다.

class Point { constructor(x, y) { this.x = x; this.y = y; } static distance(a, b) { const dx = a.x - b.x; const dy = a.y - b.y; return Math.sqrt(dx*dx + dy*dy); } } const p1 = new Point(5, 5); const p2 = new Point(10, 10); console.log(Point.distance(p1, p2)); var p3 = new Point(1, 1); p3.distance(p1, p2);

class의 static 메소드

 

인스턴스 없이 정상적으로 distance가 호출 되는 것과, 인스턴스를 통해 distance를 호출 할 때 TypeError가 발생하는 것을 확인 할 수 있습니다.

 

 

오토박싱(Autoboxing)

autoboxing이란?

일반 함수에서 this는 window 객체를 가르키게 됩니다. this가 window 객체 (global object)를 가르키는 이유는 autoboxing 덕분입니다.

non-strict 모드에서 ([자바스크립트] this의 정체 참고) this 값이 null 혹은 undefined 일 경우 window 객체(global object)로 자동으로 변환해 주는 것을 autoboxing이라고 합니다.

 

프로토타입 기반의 클래스에서 autoboxing

프로토타입 기반의 class의 경우

function Animal() { } Animal.prototype.speak = function(){ console.log(this); return this; } Animal.eat = function() { console.log(this); return this; } let obj = new Animal(); let speak = obj.speak; speak(); // global object let eat = Animal.eat; eat(); // global object

프로토타입 기반의 클래스의 autoboxing

 

autoboxing이 되어, 일반 함수를 호출하는 것처럼 메소드를 호출 한 경우 window 객체(global object)를 출력하는 것을 확인 할 수 있습니다.

 

ES6의 클래스 기반의 autoboxing

ES6의 class에서는 autoboxing이 되지 않습니다.

class Animal { speak() { console.log(this); return this; } static eat() { console.log(this); return this; } } let obj = new Animal(); let speak = obj.speak; speak(); // undefined obj.speak(); let eat = Animal.eat; eat(); // undefined Animal.eat();

ES6의 class는 autoboxing이 안됨

 

autoboxing이 되지 않아, 일반 함수를 호출하는 것처럼 메소드를 호출 한 경우 undefined가 출력되는 것을 확인 할 수 있습니다.

그렇기 때문에 ES6의 클래스에서 메소드를 변수에 저장하여 사용할 경우, this에 유의해서 사용해야 합니다.

 

 

3. 클래스 상속(sub classing)

ES6의 클래스 상속

extends 키워드를 통하여 클래스를 상속 받아, 자식 클래스를 만들 수 있습니다.

class Animal { constructor(name) { this.name = name; } speak() { console.log(this.name + ' makes a noise.'); } } class Dog extends Animal { speak() { console.log(this.name + ' barks.'); } } var d = new Dog('Mitzie'); d.speak();

ES6의 클래스 상속

 

프로토타입 기반의 클래스 상속

프로토타입 기반의 클래스도 extends 키워드를 통해 상속 할 수 있습니다.

function Animal (name) { this.name = name; } Animal.prototype.speak = function () { console.log(this.name + ' makes a noise.'); } class Dog extends Animal { speak() { console.log(this.name + ' barks.'); } } var d = new Dog('Mitzie'); d.speak();

프로토타입 기반의 클래스 상속

 

일반 객체의 클래스 상속

일반 객체는 extends 키워드를 통해 상속할 수 없습니다. 상속하고 싶다면 Object.setPrototypeOf 메소드를 사용하여 상속해야 합니다.

var Animal = { speak() { console.log(this.name + ' makes a noise.'); } }; class Dog { constructor(name) { this.name = name; } speak() { console.log(this.name + ' barks.'); } } Object.setPrototypeOf(Dog.prototype, Animal); var d = new Dog('Mitzie'); d.speak();

일반 객체의 클래스 상속

 

 

4. Species

class MyArray extends Array { // 부모 Array 생성자로 종류 덮어쓰기 static get [Symbol.species]() { return Array; } } var a = new MyArray(1,2,3); var mapped = a.map(x => x * x); console.log(mapped instanceof MyArray); // false console.log(mapped instanceof Array); // true

Symbol.species

 

Array를 상속받은 MyArray에서 Array의 default 생성자를 덮어 쓰고 싶을 경우, Symbol.species를 사용하면 됩니다. (get []의 문법은 [자바스크립트] getter, setter 참고 바랍니다.)

Symbol.species로 Array의 생성자를 가져오고, get [] 로 Array를 리턴함으로 부모 Array의 생성자를 덮어 쓸 수 있습니다.

 

 

5. super로 부모 클래스 호출하기

super 키워드를 홈하여 부모 클래스의 메소드를 호출 할 수 있습니다.

 

프로토타입 기반의 부모 클래스 메소드 호출

function Cat(name) { this.name = name; } Cat.prototype.speak = function () { console.log(this.name + ' makes a noise.'); }; function Lion(name) { // `super()` 호출 Cat.call(this, name); } // `Cat` 클래스 상속 Lion.prototype = new Cat(); Lion.prototype.constructor = Lion; // `speak()` 메서드 오버라이드 Lion.prototype.speak = function () { Cat.prototype.speak.call(this); console.log(this.name + ' roars.'); }; var lion = new Lion("BIG"); lion.speak();

프로토타입 기반의 부모 클래스 메소드 호출

 

프로토타입 기반의 클래스에서 부모 클래스의 메소드를 호출하기 위해서 call과 같은 함수를 사용해야 합니다. ([자바스크립트] API - call, apply 함수 참고)

 

ES6 클래스 기반의 부모 클래스 메소드 호출

class Cat { constructor(name) { this.name = name; } speak() { console.log(this.name + ' makes a noise.'); } } class Lion extends Cat { speak() { super.speak(); console.log(this.name + ' roars.'); } } var lion = new Lion("BIG"); lion.speak();

ES6 클래스 기반의 부모 클래스 메소드 호출

 

ES6 클래스는 super 메소드를 사용하여 부모 클래스의 메소드를 호출 할 수 있습니다.



출처: https://beomy.tistory.com/15 [beomy]

728x90

728x90

Rebase 하기

 

Git에서 한 브랜치에서 다른 브랜치로 합치는 방법으로는 두 가지가 있다. 하나는 Merge 이고 다른 하나는 Rebase 다. 이 절에서는 Rebase가 무엇인지, 어떻게 사용하는지, 좋은 점은 뭐고, 어떤 상황에서 사용하고 어떤 상황에서 사용하지 말아야 하는지 알아 본다.

Rebase 의 기초

앞의 Merge 의 기초 절에서 살펴본 예제로 다시 돌아가 보자. 두 개의 나누어진 브랜치의 모습을 볼 수 있다.

Figure 35. 두 개의 브랜치로 나누어진 커밋 히스토리

이 두 브랜치를 합치는 가장 쉬운 방법은 앞에서 살펴본 대로 merge 명령을 사용하는 것이다. 두 브랜치의 마지막 커밋 두 개(C3, C4)와 공통 조상(C2)을 사용하는 3-way Merge로 새로운 커밋을 만들어 낸다.

Figure 36. 나뉜 브랜치를 Merge 하기

비슷한 결과를 만드는 다른 방식으로, C3 에서 변경된 사항을 Patch로 만들고 이를 다시 C4 에 적용시키는 방법이 있다. Git에서는 이런 방식을 Rebase 라고 한다. rebase 명령으로 한 브랜치에서 변경된 사항을 다른 브랜치에 적용할 수 있다.

위의 예제는 아래와 같은 명령으로 Rebase 한다.

$ git checkout experiment $ git rebase master First, rewinding head to replay your work on top of it... Applying: added staged command

실제로 일어나는 일을 설명하자면 일단 두 브랜치가 나뉘기 전인 공통 커밋으로 이동하고 나서 그 커밋부터 지금 Checkout 한 브랜치가 가리키는 커밋까지 diff를 차례로 만들어 어딘가에 임시로 저장해 놓는다. Rebase 할 브랜치(역주 - experiment)가 합칠 브랜치(역주 - master)가 가리키는 커밋을 가리키게 하고 아까 저장해 놓았던 변경사항을 차례대로 적용한다.

Figure 37. `C4`의 변경사항을 `C3`에 적용하는 Rebase 과정

그리고 나서 master 브랜치를 Fast-forward 시킨다.

$ git checkout master $ git merge experiment

Figure 38. master 브랜치를 Fast-forward시키기

C4' 로 표시된 커밋에서의 내용은 Merge 예제에서 살펴본 C5 커밋에서의 내용과 같을 것이다. Merge 이든 Rebase 든 둘 다 합치는 관점에서는 서로 다를 게 없다. 하지만, Rebase가 좀 더 깨끗한 히스토리를 만든다. Rebase 한 브랜치의 Log를 살펴보면 히스토리가 선형이다. 일을 병렬로 동시에 진행해도 Rebase 하고 나면 모든 작업이 차례대로 수행된 것처럼 보인다.

Rebase는 보통 리모트 브랜치에 커밋을 깔끔하게 적용하고 싶을 때 사용한다. 아마 이렇게 Rebase 하는 리모트 브랜치는 직접 관리하는 것이 아니라 그냥 참여하는 브랜치일 것이다. 메인 프로젝트에 Patch를 보낼 준비가 되면 하는 것이 Rebase 니까 브랜치에서 하던 일을 완전히 마치고 origin/master 로 Rebase 한다. 이렇게 Rebase 하고 나면 프로젝트 관리자는 어떠한 통합작업도 필요 없다. 그냥 master 브랜치를 Fast-forward 시키면 된다.

Rebase를 하든지, Merge를 하든지 최종 결과물은 같고 커밋 히스토리만 다르다는 것이 중요하다. Rebase 의 경우는 브랜치의 변경사항을 순서대로 다른 브랜치에 적용하면서 합치고 Merge 의 경우는 두 브랜치의 최종결과만을 가지고 합친다.

Rebase 활용

Rebase는 단순히 브랜치를 합치는 것만 아니라 다른 용도로도 사용할 수 있다. 다른 토픽 브랜치에서 갈라져 나온 토픽 브랜치 같은 히스토리가 있다고 하자. server 브랜치를 만들어서 서버 기능을 추가하고 그 브랜치에서 다시 client 브랜치를 만들어 클라이언트 기능을 추가한다. 마지막으로 server 브랜치로 돌아가서 몇 가지 기능을 더 추가한다.

Figure 39. 다른 토픽 브랜치에서 갈라져 나온 토픽 브랜치

이때 테스트가 덜 된 server 브랜치는 그대로 두고 client 브랜치만 master 로 합치려는 상황을 생각해보자. server 와는 아무 관련이 없는 client 커밋은 C8, C9 이다. 이 두 커밋을 master 브랜치에 적용하기 위해서 --onto 옵션을 사용하여 아래와 같은 명령을 실행한다:

$ git rebase --onto master server client

이 명령은 master 브랜치부터 server 브랜치와 client 브랜치의 공통 조상까지의 커밋을 client 브랜치에서 없애고 싶을 때 사용한다. client 브랜치에서만 변경된 패치를 만들어 master 브랜치에서 client 브랜치를 기반으로 새로 만들어 적용한다. 조금 복잡하긴 해도 꽤 쓸모 있다.

Figure 40. 다른 토픽 브랜치에서 갈라져 나온 토픽 브랜치를 Rebase 하기

이제 master 브랜치로 돌아가서 Fast-forward 시킬 수 있다(master 브랜치를 client 브랜치 위치로 진행 시키기 참고).

$ git checkout master $ git merge client

Figure 41. master 브랜치를 client 브랜치 위치로 진행 시키기

server 브랜치의 일이 다 끝나면 git rebase <basebranch> <topicbranch> 라는 명령으로 Checkout 하지 않고 바로 server 브랜치를 master 브랜치로 Rebase 할 수 있다. 이 명령은 토픽(server) 브랜치를 Checkout 하고 베이스(master) 브랜치에 Rebase 한다.

$ git rebase master server

server 브랜치의 수정사항을 master 브랜치에 적용했다. 그 결과는 master 브랜치에 server 브랜치의 수정 사항을 적용 같다.

Figure 42. master 브랜치에 server 브랜치의 수정 사항을 적용

그리고 나서 master 브랜치를 Fast-forward 시킨다.

$ git checkout master $ git merge server

모든 것이 master 브랜치에 통합됐기 때문에 더 필요하지 않다면 client  server 브랜치는 삭제해도 된다. 브랜치를 삭제해도 커밋 히스토리는 최종 커밋 히스토리 같이 여전히 남아 있다.

$ git branch -d client $ git branch -d server

Figure 43. 최종 커밋 히스토리

Rebase 의 위험성

 

Rebase가 장점이 많은 기능이지만 단점이 없는 것은 아니니 조심해야 한다. 그 주의사항은 아래 한 문장으로 표현할 수 있다.

이미 공개 저장소에 Push 한 커밋을 Rebase 하지 마라

이 지침만 지키면 Rebase를 하는 데 문제 될 게 없다. 하지만, 이 주의사항을 지키지 않으면 사람들에게 욕을 먹을 것이다.

Rebase는 기존의 커밋을 그대로 사용하는 것이 아니라 내용은 같지만 다른 커밋을 새로 만든다. 새 커밋을 서버에 Push 하고 동료 중 누군가가 그 커밋을 Pull 해서 작업을 한다고 하자. 그런데 그 커밋을 git rebase 로 바꿔서 Push 해버리면 동료가 다시 Push 했을 때 동료는 다시 Merge 해야 한다. 그리고 동료가 다시 Merge 한 내용을 Pull 하면 내 코드는 정말 엉망이 된다.

이미 공개 저장소에 Push 한 커밋을 Rebase 하면 어떤 결과가 초래되는지 예제를 통해 알아보자. 중앙 저장소에서 Clone 하고 일부 수정을 하면 커밋 히스토리는 아래와 같아 진다.

Figure 44. 저장소를 Clone 하고 일부 수정함

이제 팀원 중 누군가 커밋, Merge 하고 나서 서버에 Push 한다. 이 리모트 브랜치를 Fetch, Merge 하면 히스토리는 아래와 같이 된다.

Figure 45. Fetch 한 후 Merge 함

그런데 Push 했던 팀원은 Merge 한 일을 되돌리고 다시 Rebase 한다. 서버의 히스토리를 새로 덮어씌우려면 git push --force 명령을 사용해야 한다. 이후에 저장소에서 Fetch 하고 나면 아래 그림과 같은 상태가 된다.

Figure 46. 한 팀원이 다른 팀원이 의존하는 커밋을 없애고 Rebase 한 커밋을 다시 Push 함

자 이렇게 되면 짬뽕이 된다. git pull 로 서버의 내용을 가져와서 Merge 하면 같은 내용의 수정사항을 포함한 Merge 커밋이 아래와 같이 만들어진다.

Figure 47. 같은 Merge를 다시 한다

git log 로 히스토리를 확인해보면 저자, 커밋 날짜, 메시지가 같은 커밋이 두 개 있다(C4, C4'). 이렇게 되면 혼란스럽다. 게다가 이 히스토리를 서버에 Push 하면 같은 커밋이 두 개 있기 때문에 다른 사람들도 혼란스러워한다. `C4`와 `C6`는 포함되지 말았어야 할 커밋이다. 애초에 서버로 데이터를 보내기 전에 Rebase로 커밋을 정리했어야 했다.

Rebase 한 것을 다시 Rebase 하기

만약 이런 상황에 빠질 때 유용한 Git 기능이 하나 있다. 어떤 팀원이 강제로 내가 한일을 덮어썼다고 하자. 그러면 내가 했던 일이 무엇이고 덮어쓴 내용이 무엇인지 알아내야 한다.

커밋 SHA 체크섬 외에도 Git은 커밋에 Patch 할 내용으로 SHA-1 체크섬을 한번 더 구한다. 이 값은 “patch-id” 라고 한다.

덮어쓴 커밋을 받아서 그 커밋을 기준으로 Rebase 할 때 Git은 원래 누가 작성한 코드인지 잘 찾아 낸다. 그래서 Patch가 원래대로 잘 적용된다.

예를 들어 앞서 살펴본 예제를 보면 한 팀원이 다른 팀원이 의존하는 커밋을 없애고 Rebase 한 커밋을 다시 Push 함 상황에서 Merge 하는 대신 git rebase teamone/master 명령을 실행하면 Git은 아래와 같은 작업을 한다.

  • 현재 브랜치에만 포함된 커밋을 찾는다. (C2, C3, C4, C6, C7)

  • Merge 커밋을 가려낸다. (C2, C3, C4)

  • 이 중 덮어쓰지 않은 커밋들만 골라낸다. (C2, C3. C4는 C4’와 동일한 Patch다)

  • 남은 커밋들만 다시 teamone/master 바탕으로 커밋을 쌓는다.

결과를 확인해보면 같은 Merge를 다시 한다 같은 결과 대신 제대로 정리된 강제로 덮어쓴 브랜치에 Rebase 하기 같은 결과를 얻을 수 있다.

Figure 48. 강제로 덮어쓴 브랜치에 Rebase 하기

동료가 생성했던 C4와 C4' 커밋 내용이 완전히 같을 때만 이렇게 동작된다. 커밋 내용이 아예 다르거나 비슷하다면 커밋이 두 개 생긴다(같은 내용이 두 번 커밋될 수 있기 때문에 깔끔하지 않다).

git pull 명령을 실행할 때 옵션을 붙여서 git pull --rebase 로 Rebase 할 수도 있다. 물론 git fetch  git rebase teamone/master 이 두 명령을 직접 순서대로 실행해도 된다.

git pull 명령을 실행할 때 기본적으로 --rebase 옵션이 적용되도록 pull.rebase 설정을 추가할 수 있다. git config --global pull.rebase true 명령으로 추가한다.

Push 하기 전에 정리하려고 Rebase 하는 것은 괜찮다. 또 절대 공개하지 않고 혼자 Rebase 하는 경우도 괜찮다. 하지만, 이미 공개하여 사람들이 사용하는 커밋을 Rebase 하면 틀림없이 문제가 생긴다.

나중에 후회하지 말고 git pull --rebase 로 문제를 미리 방지할 수 있다는 것을 같이 작업하는 동료와 모두 함께 공유하기 바란다.

Rebase vs. Merge

 

Merge가 뭔지, Rebase가 뭔지 여러 예제를 통해 간단히 살펴보았다. 지금쯤 이런 의문이 들 거로 생각한다. 둘 중 무엇을 쓰는 게 좋지? 이 질문에 대한 답을 찾기 전에 히스토리의 의미에 대해서 잠깐 다시 생각해보자.

히스토리를 보는 관점 중에 하나는 작업한 내용의 기록으로 보는 것이 있다. 작업 내용을 기록한 문서이고, 각 기록은 각각 의미를 가지며, 변경할 수 없다. 이런 관점에서 커밋 히스토리를 변경한다는 것은 역사를 부정하는 꼴이 된다. 언제 무슨 일이 있었는지 기록에 대해 거짓말 을 하게 되는 것이다. 이렇게 했을 때 지저분하게 수많은 Merge 커밋이 히스토리에 남게 되면 문제가 없을까? 역사는 후세를 위해 기록하고 보존해야 한다.

히스토리를 프로젝트가 어떻게 진행되었나에 대한 이야기로도 볼 수 있다. 소프트웨어를 주의 깊게 편집하는 방법에 메뉴얼이나 세세한 작업내용을 초벌부터 공개하고 싶지 않을 수 있다. 나중에 다른 사람에게 들려주기 좋도록 Rebase 나 filter-branch 같은 도구로 프로젝트의 진행 이야기를 다듬으면 좋다.

Merge 나 Rebase 중 무엇이 나으냐는 질문은 다시 생각해봐도 답이 그리 간단치 않다. Git은 매우 강력한 도구고 기능이 많아서 히스토리를 잘 쌓을 수 있지만, 모든 팀과 모든 이가 처한 상황은 모두 다르다. 예제를 통해 Merge 나 Rebase가 무엇이고 어떤 의미인지 배웠다. 이 둘을 어떻게 쓸지는 각자의 상황과 각자의 판단에 달렸다.

일반적인 해답을 굳이 드리자면 로컬 브랜치에서 작업할 때는 히스토리를 정리하기 위해서 Rebase 할 수도 있지만, 리모트 등 어딘가에 Push로 내보낸 커밋에 대해서는 절대 Rebase 하지 말아야 한다

728x90

지금까지 Git 을 이용하여 로컬의 작업내용을 원격의 repository 에 보관하여 작업하는 방법을 알아보았다.

 

그런데 만약 멀티 유저환경이라면 그래서 누군가가 내가 마지막으로 보관했던 커밋이후에 신규커밋을 추가했다면 내 로컬 커밋들에 그 신규 커밋을 어떻게 반영해야할까?

 

이런 상황에 사용할 수 있는 명령이 git fetch 이다. fetch 명령은 타겟으로 지정한 리포지토리의 변경내역을 로컬로 가지고 와서 그 내역들을 HEAD라는 특수한 branch에 담는다.

 

이 정보를 이용해서 로컬의 작업내역들과 비교할 수 있고 만약 누군가가 생성한 신규 커밋들이 로컬에서 작업한 부분과 중복되는 부분이 있다면 이에 대한 정보를 제공해준다.

 

아래 예제를 참조

 

1. 현재 log 확인

* git ll 은 git log --oneline 을 이용한 alias 이다. 이와 관련한 정보는 'Git 간단한 사용법 - alias' 참조할 것.

[~/itips_2] (master) $ git ll
2afe186 26 minutes ago I Tips Merge branch 'master' of /home/itips/repo (HEAD -> master, origin/master, origin/HEAD) I Tips
f234ec6 27 minutes ago I Tips Add second.txt file I Tips
d7addb2 29 minutes ago I Tips Add new file I Tips
9875c2a 5 days ago I Tips I will make a commit only with first.txt I Tips

현재 HEAD-> master, origin/master, origin/HEAD 모두 commit 2afe186 을 가리키고 있다.

 

2. fetch를 한후 다시 로그를 확인해 보면 아래와 같다.

[~/itips_2] (master) $ git fetch
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 7 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (7/7), done.
From /home/itips/repo
   2afe186..d7c0da0  master     -> origin/master
[~/itips_2] (master) $ git ll
2afe186 26 minutes ago I Tips Merge branch 'master' of /home/itips/repo (HEAD -> master) I Tips
f234ec6 27 minutes ago I Tips Add second.txt file I Tips
d7addb2 29 minutes ago I Tips Add new file I Tips
9875c2a 5 days ago I Tips I will make a commit only with first.txt I Tips

로컬에 있는 가장 최근 커밋인 2afe186 은 변경없이 그대로 있지만 그 커밋을 가리키고 있던 origin/master 와 origin/HEAD 가 사라지고 HEAD-> master 만 남아 있다. 

fetch 실행 후 메세지에서 '2afe186..d7c0da0 master  -> origin/master' 이부분은 로컬의 마스터 브렌치에서 리포지토리 origin 에 있는 master 브렌치 사이에 커밋들이 있음을 알려주는 것이다. 

 

3. 이제 merge 를 실행

[~/itips_2] (master) $ git merge origin/master
Updating 2afe186..d7c0da0
Fast-forward
 second.txt | 1 +
 third.txt  | 1 +
 2 files changed, 2 insertions(+)
 create mode 100644 third.txt

커밋 2afe186에서 커밋 d7c0da0 로 업데이트가 진행 되었음을 보여준다.

 

4. 다시 커밋로그를 확인

[~/itips_2] (master) $ git ll
d7c0da0 2 minutes ago I Tips Merge remote-tracking branch 'origin/master' (HEAD -> master, origin/master, origin/HEAD) I Tips
8c07aa4 25 minutes ago I Tips Add third.txt I Tips
2afe186 27 minutes ago I Tips Merge branch 'master' of /home/itips/repo I Tips
f234ec6 28 minutes ago I Tips Add second.txt file I Tips
d7addb2 30 minutes ago I Tips Add new file I Tips
9875c2a 5 days ago I Tips I will make a commit only with first.txt I Tips

커밋 2afe186 이후로 2개의 커밋이 추가되었고 HEAD-> master, origin/master, origin/HEAD 가 예전처럼 가장 최근의 커밋을 가리키고 있다. 

 

 

정리하면, fetch 가 진행되기 전까지 로컬작업 환경은 리포지토리의 상황을 알지 못하며 fetch 를 통해 repository 의 정보를 가져오며 이때 로컬파일들에 어떠한 변경도 만들지 않는다. 

 

이렇게 업데이트 된 정보를 이용해서 merge, log 등의 작업을 진행한다. 따라서, 특히 리포지토리에 push 등의 작업을 할 경우 내 로컬 작업 환경에 리포지토리의 커밋내역을 정확히 가지고 있지 않으면 정상적으로 push 가 되지 않고 에러가 난다.

 

그래서 일반적(충돌- conflict 이 일어나지 않는 경우)으로 push 할 때 작업수행은 아래와 같이 진행한다. 

 

git add . && git commit -m 'message' && git fetch origin master && git merge origin/master && git push origin master

 

 

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

 

 

+ Recent posts