Delegate와 Event의 이해(2)

Posted by 나에요임마
2017. 12. 10. 11:21 Program/C#

저번 글에 이어 "

Event

" 에 대해서 알아도보록 하자.

 

2.)   Event(이벤트)란 무엇일까~?

 

    - C#에 있어서 Event란 개념을 단순하게 정의한다면 '자신의 타입 혹은 자신의 인스턴스가 다른 객체에 특별한 일이 일어났음을 통지' 한다라

      고  볼수 있다. MSDN에서는 대략 '한 객체에 뭔가 흥미로운 사건이 발생했을때 특정 Client에 알림을 제공하는 방법'이라고 나와있다.

 

        (본인은 영어에 약해 맘?대로 의역하는 버릇이 있으므로 딴지걸지 마시고 직접가서 확인하실분은 주저말고 아래 링크를 클릭하도록 하자 )

        http://msdn.microsoft.com/en-us/library/aa645739(VS.71).aspx)

 

이벤트를 사용하는 이유는 위에서의 'Event'  정의와 같다고 볼 수 있다. 대표적인 예가 C# 의 'Event Handler' 다.

간단하게 다아는 Winform 내에서 'Button'의 Click 이벤트를 등록하는 부분을 보도록 하자.

 

[Source] 

Code Snippet
  1. this.button1.Click += new System.EventHandler(this.button1_Click);

 

 

 

 

Code Snippet
  1. // Summary:
  2.         //     Occurs when the control is clicked.
  3.         [SRCategory("CatAction")]
  4.         [SRDescription("ControlOnClickDescr")]
  5.         public event EventHandler Click;

 

우리가 보통 화면상 Button 의 Click 이벤트를 더블클릭하게 되면

위와같이 해당 Button 클래스가 가지고 있는 Event 객체인 Click 'EventHandler' 에 등록이 되게 된다.

 

해당 'EventHandler' 란 것도 사실 별것 아닌 앞서 설명한 'Delegate'로 이루어져 있다.

 

[Source]

Code Snippet
  1. public delegate void EventHandler(object sender, EventArgs e);

 

 

눈치채신 분들도 있겠지만

위에 언급된 코드에서  'event' 와 'delegate' 의 차이는 차이는

보통의 앞선 설명에서 사용되던 delegate의 'Type Field' 방식에 'event'란 단어가 붙어있다는 점이다.

 

'event'  'Delegate Type Field'에 대한

하나의 'modifier' 개념과 비슷하다.

(실제 event가 내부적으로 'access moidifer' 라는 건 아니다)

 

즉 위의 'Delegate Type Field' 에 대한 'Access Modifier' 와 같은 개념으로 쓰이는 것이지

'Delegate Type Field' 그 자체를 의미하는 건 아니다.

(

 'event'

 'delegate type field' 를 캡슐화 해준다)

 

'event' 하나로 어떠한 변화가 생기는지 아래의 코드로 확인 해 보도록 하자.

 

[Source]

Code Snippet
  1. class EventTestClass
  2.     {
  3.         public event EventHandler Test;
  4.  
  5.         public void Prn(object sender, EventArgs e)
  6.         {
  7.             Console.WriteLine("{0} Property Changed!", sender.GetType().ToString());
  8.         }
  9.  
  10.         public void PropertyChanged(object sender, EventArgs e)
  11.         {
  12.             if (Test != null)
  13.                 Test(sender, e);
  14.         }
  15.     }

 

 

Code Snippet
  1. class Program
  2.     {
  3.         private int num = 0;
  4.         public EventTestClass eTestClass = new EventTestClass();
  5.  
  6.         public int NUM
  7.         {
  8.             get
  9.             {
  10.                 return num;
  11.             }
  12.             set
  13.             {
  14.                 if (value != num)
  15.                     eTestClass.PropertyChanged(this, EventArgs.Empty);
  16.  
  17.                 num = value;
  18.             }
  19.         }
  20.  
  21.         static void Main(string[] args)
  22.         {
  23.             Program pgm = new Program();
  24.  
  25.             pgm.eTestClass.Test += new EventHandler(pgm.eTestClass.Prn);
  26.  
  27.             pgm.NUM = 4;
  28.         
  29.         }
  30.     }

 

먼저 위와같이 하나의 event 객체를 가진  클래스가 내부적으로 어떤 변화를 거치는지 살펴보자.

 

[Reflector]

 

 

[ILDASM]

 

여기서 잠깐 주목해볼 중요한 점은

 public event EventHandler Test

라고 선언했던 부분의 Eventhandler의 Field가 private 한정자로 바뀌었다는 점이다.

 

'Reflector' 로 까여진 소스내에 보다싶이

컴파일러에 의해서

 

(1) 'Private EventHandler Type Field'

(2) 'Event Handler Property'

 

위 두 가지 코드가 새롭게 생성되었다는 점도 주목해봐야할 곳이다.

 

즉 우리가 'event' 라는 명칭을 붙여주게 되면

위의 (1), (2) 과정이 내부 컴파일러에 의해서 생성되고

 

실제로 우리가 'event handler' 에 등록하기 위해서 '+=' 같은 동작을 하게되면

private 접근자인 'delegate type field' 에 접근하는게 아니라

'event handler property' 에 접근하게 되는 것이다.

 

그 내부 에서는 다시금 각각 '+=', '-=' Operator 에 맞게끔

add, remove 동작이 실행된다.

(코드에서도 보이듯이 각 add, remove 내에서는 private 으로 변경된 'EventHander' 에 대한 조작을 가한다.)

 

* ILDASM에서 보이는 add_, remove_ 이하의 명칭은 컴파일러에 의해 생성된

 'Event Handler Property' 의 add, remove에 대한 Method이다.

 

즉, 이 말이 무슨말인지 알아채신 분도 있겠지만

add, remove내에서 해당 delegate에 대한 조작을 한다는 건

다시말해 event 역시 내부적으로는 delegate 와 같은 방식으로 동작을 하게 된 다는 것이다.

(전에 설명된 Combine, Remove 같은 방식으로 Delegate list를 관리)

 

단지 Delegate 를 쓰는것과 달리 위와 같은 'Event' 를 사용하게 되면

기존의 Delegate의 통지기능에 더해

클래스의 외부 코드가 해당 'EventHandler' Field 에 대한 부적절한 사용을 막아줌과 동시에

이로인해 필드의 값이 변경되 구독한 메서드에 대한 List 들 값에 대한 부적절한 변경을 막아줄 수 있다.

(이는 OOP적인 관점에서 'event' 가 내부적으로 Delegate Type Field 에 대한 'modifier' 적인 개념의 내부 처리를 한다고 볼수있다.)

 

여기에 더해서 한가지 더 특징은

위의 'EventHandler Property' 에 의해 제공되는

add_, remove_이하 메서드는 '쓰레드 안정적' 인 기능을 더해준다는 점이다.

 

[MethodImpl(MethodImplOptions.Synchronized)]

public void add_Test(블러블러......){...}

 

컴파일러 내부에 의해서 add_ 이하 메소드는 위와같은 Attribute 를 지니게 되는데

이는 여러 객체가 Event에 동시에 등록하거나 해제하려 할 경우에 순차적인 접근을 보장해 준다.

 

이상 'Event'에 대한 몇가지 내용을 'Delegate'에 이어서 살펴보았다.

 

위에 언급된 add_, remove_ 이하 메서드인

'Event Handler Property' 는 재정의 해서 쓸수 있으며

이는 위에 언급된 쓰레드 안정적인 Attribute가 가져다 줄 또다른 성능 문제점등의

문제를 해결하기 위해서 사용되나 이글에서는 따로 다루진 않는다.

 

(참고 : http://www.switchonthecode.com/tutorials/csharp-snippet-tutorial-custom-event-handlers)

 

 

Delegate와 Event의 이해(1)

Posted by 나에요임마
2017. 12. 10. 11:19 Program/C#

이제부터 얘기해 주제는  파리~~바게트!” .. 아니다.

바로 C# Delegate And Event

Delegate Event 도대체 무엇일까?

가지에 대해서 가장 아는 방법은 차이점을 따져보면서 쓰이게 됐는지에

대해서 살펴보는 가장 좋다.

(개발에 있어 가지는 "Why?" 는 상당히 중요하다)

그러기에 앞서 가장먼저 파리바게트 글자수마져도 같은

델리게이트(Delegate)” 녀석에 해서 먼저 알아보도록 하자.

 

1.)     Delegate(델리게이트) 무엇일까~?

 -  C/C++ 을 접해본 사람들은 함수포인터(Func Pointer)에 대해서 들어봤을 거라 생각한다.  접해보지 못했다는 분들을 위해 간략하게 "stdlib.h" 내에 있는 qsort함수를 살펴보면 아래와 같다.

 

Code Snippet
  1. void qsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *));

 

먼가 복잡해 보이는 듯 하지만 다른 건 볼 필요 없고 끝의

 

int(*compar)(const void *, const void *));

 

형태만 보자. 이 형태는 int형을 리턴하고 인자는 2개의 void형 포인터를 갖는 함수형태를 파라미터로 받는 다는 말이다. 이게 바로 함수 포인터의 정체다.(C/C++상에서)

 

일반적으로는

void (*pf)(int *);

위와같이 Definition 을 사용한다.

(함수포인터를 콜하는 등의 예는 다루고자 하는 내용과 거리가 멀어 생략합니다.)

 

즉, 아주 간략히 말하자면 C#에서의 "Delegate" 는 C/C++의 함수포인터와 매우 유사한 면을 지니고 있다. C#에서 "Delegate" 는 간접 접근의 한 방법으로 사용되며 이는 "Delegate"의 사전적의미인 아래의 내용과 동일하게 사용된다.

 

 
delegate   [delig?t, -geit] [US]
  • n.
    • 대표, 사절, 파견 위원, 대리인(deputy)
 
예를 들어, 글쓴이가 저녁에 요리를 해먹기 위해 4:30분에 슈퍼에 가서 두부 한모, 파슬린, 국간장 등을 사오는 행위를 해야하는데 이를 메모지에 적어놓기만 하고 사정상 다른사람에게 부탁을 했다고 치면 부탁받음 사람은 이 일련의 작업을 메모지를 들고가서 글쓴이 대신 처리하게 되는 과정과 비슷하다.
(날더운데 대신 가줄사람이 있겠냐만은....그런 착한 사람이 있다고 치자......)
 
여기서 이 메모장이 하는 역할이 일종의 "Delegate" 즉 대리자와 비슷하다.
그럼 이 "델리게이트" 란 녀석을 왜 쓰고 어디에 쓰는 걸까??
 
위에서 잠깐 언급한 메모지를 다시 생각해보자.
 
"예를 들어 글쓴이가 위 메모지의 내용대로 자주 음식을 해먹기 때문에 슈퍼에 자주 가야하는 상황이 생기고 또 우연히? 항상 슈퍼에 자주 갈수 없는 상황이 생겨서 누군가에게 대신 이러한 작업을 위임해야 한다고 하면 그 대상이 누가됐던간에 글쓴이는 이 메모지를 대신가줄 불쌍한 이?에게 전달만해주면 된다."
 
즉, "Delegate" 는 주로 코드에서
"어떠한 작업을 하고 싶은데 그 작업이 명확치 않은경우" 에 주로 쓰인다.
 
"간단히 말하자면 위의 예에서 글쓴이가 어떤사람이건 아무나 붙잡고 심부름을 시킨다고 쳤을 때 이사람은 아직 뭘 해야 하는지 모르는 상태고 이때 글쓴이가 준 메모지가 이 심부름 하는 사람에게 어떠한 작업을 해야한다...라고 알려줄 수 있는 역할을 한다는 것이다."
 
C#에서 자주쓰이는 대표적인 예가 "Thread" 이다.
아래 코드를 살펴보자.
 
Code Snippet
  1. class Program
  2.     {
  3.         static void Main(string[] args)
  4.         {
  5.             Thread thread = new Thread(new ThreadStart(Prn));
  6.             thread.Start();
  7.         }
  8.         static void Prn()
  9.         {
  10.             Console.WriteLine("Printing.......Anywhere");
  11.         }
  12.     }
EndFragment
EndFragment
 
위 코드에서 Thread가 시작됨과 동시에 Prn이라는 함수를 호출하고
"Printing........Anywhere"
라는 문자를 찍는 작업을 할 수 있는 건 ThreadStart라는 Delegate 때문이다.
 
* Thread는 그 자체가 무엇을 실행해야 할지는 모르지만
  Delegate 를 통해 해당 작업을 수행하게 된다. *
 
-------------
ThreadStart라는 Delegate는 아래와 같이 선언되있다.
 
Code Snippet
  1. namespace System.Threading
  2. {
  3.     // Summary:
  4.     //     Represents the method that executes on a System.Threading.Thread.
  5.     [ComVisible(true)]
  6.     public delegate void ThreadStart(); ->델리게이트타입
  7. }
EndFragment
-------------
 
Delegate라는 Type은 위에서 설명한 것과 같은 대리자, 역할위임의 수행을 하기
때문에 특정 동작에 대한 정보를 담고있는 Type이라고 볼 수 있다.
(Type은 특정 형태를 의미하는 말이며, Instance를 의미하는 건 아니다.)
 
Delegate의 사용법은 위의 Thread 샘플과 같은 방식으로 사용된다.
해당 Delegate Type 과 return 및 파라미터 인자들까지 그 형태가 같아야 된다.
 
Delegate의 또다른 특징은 여러 작업을 동시에 담아둘수도 있으며
동시에 여러번의 작업을 실행 할 수도 있다는 점이다.
 
아래 코드를 보자.
 
 
Code Snippet
  1. class Program
  2. {
  3.     public delegate void Printer();
  4.     static void Main(string[] args)
  5.     {
  6.         Printer prn = new Printer(Prn);
  7.         //Printer prn = Prn; 동일
  8.         prn += Prn2;
  9.         if (prn != null)
  10.             prn();
  11.     }
  12.     static void Prn()
  13.     {
  14.         Console.WriteLine("Printing.......Anywhere");
  15.     }
  16.     static void Prn2()
  17.     {
  18.         Console.WriteLine("Printing2.......Anywhere");
  19.     }
  20. }
EndFragment
 
 
위 결과를 실행하면 당연한 얘기겠지만
Prn(), Prn2()
함수의 내용이 콘솔에 출력된다.
 
물론 -= 연산도 가능하다.
 
본 이야기의 시작점이었던 "왜 쓰이고 어디에 쓰이는지" 에 대한 본론으로 들어가기 전에 조금 더 Deleagate의 내부 동작을 이해하고자 삼천포에 좀더 빠져보도록 하자
 
위 코드를 잠깐 ILDasm 으로 열어보도록 하자.
 
 
모르는 분을 위해 잠깐 언급하자면 IL Dasm은 "IL(중간언어)를 볼수있게 디스어셈"을 해주는 도구로 "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\" 경로 밑에 숨어 살고있다. V7.0A는 상황에 따라 버전명이 다를 수 있다.
 
 
[ILDASM]
 
자세히 볼것 없이 간략히 훑어 본다면
1,2,3 번은 각각 해당 함수에 따른 Delegate 객체를 생성하는 부분이다.
 
위에 언급된 소스대로라면 1,3번만 나와야 맞지만 소스에 동작방식이 동일하다고 주석처리 한 부분을 확인 해 볼수 있게 실제 컴파일시에는 주석을 풀었다.
 
아무튼, 눈여겨 볼 곳은 
 
System.Delegate::Combine(...,...) 과 
(CombineRemove와 짝을 이룬다. 소스에서 "-=" 연산자로 실행 후 확인해보자)
Printer::Invoke() 부분이다.
 
 
즉, 위 소스에서 Delegate Instance 에 함수를 "+=" 연산자를 통해 호출을 하게 되면
Combine메소드가 불려지면서
 
Code Snippet
  1. prn = (Printer) Delegate.Combine(prn, new Printer(Program.Prn2));
EndFragment
 
위와 같이 컴파일러는 재 해석 하게 된다.
 
ILDASM에서도 보이듯이rValue에 대한 Delegate Instance를 내부적으로
생성한 다음 기존의 Delegate와 결합시켜준다. 
 
그 후 호출된 Delegate Instance
 
prn() -> prn.Invoke() -> Prn() -> Prn2()
 
식으로 호출과정을 거치게 된다.
 
컴파일하면 내부적으로 Invoke를 호출해서 하나씩 꺼내와 실행하게 되지만
직접 소스에 prn.Invoke() 라고 쳐도 실행에는 상관이 없다.
 
Delegate에 대한 내부적인 이해를 돕기위한 삼천포는 여기 까지다.!!
 
왜 삼천포로 빠졌는지는 후반 2부에서 언급 될 "Event" 에 대한 설명시
비교를 위해 되새김질 해봐야 하기 때문이므로 이정도면 충분하다.
(충분하지 않다면 당신은 이미.....엘리뜨?!)
 
다시 본론으로 들어와
그렇다면 이런 Delegate를 왜 사용하는 걸까?
그냥 아싸리 Func(); 이런식으로 호출하면 만사 땡 아닌가??
 
Delegate는 위에서 보여진 Combine을 통한 다수의 func에 대한
편리한? 절차적인 호출등의 이점 말고도
 
코드의 유연성을 도와준다는 데에 그 목적이 있다.
즉 코드간의 강한 결합도를 낮춰줄 수 있다는 말이다.
 
예를 들어
A 클래스 안에 항상 Data를 받으면 콘솔로 뿌려주는
Console.Write("블러") 와 같이 코드가 박혀있는 메소드가 있다고 치자.
 
A클래스를 만일 콘솔 Main함수에서 가져다 쓴다면 문제는 없겠지만
콘솔이 아닌 순수한 윈폼이라면(이는 프로젝트 설정에서 Console도 가능하게 한 경우는 제외한다.) 위 함수자체는 쓸모없게 되버릴 것이다.
 
만일 저 출력부를 따로 Delegate 가능하게끔 참조만을 연결해 두었다면 따로 A클래스
쓸대 해당 프로젝트에 따라서 참조로 연결해준 위임자에게 역할만 바꿔주면 될 것이다.
 
이 얼마나 유용성있는가? (예가 좀 억지성이 있겠지만서도..)
 
여기서 드러나는 또 하나의 장점은 메소드 그 자체의 캡슐화(Encapsulating) 기능이다.
호출하는 쪽에서는 그 자체 내부의 메소드 기능이 뭐든간에 그냥 대리자만 불러주면 된다. 이처럼 Delegate는 코드 구조에 따라서 참 유용하게 쓰일 수가 있다.
 
한 가지 빼먹은 점을 더 이야기 하자면 대리자(Delegate)는
BeginInvoke,EndInvoke를 통한 비동기 메소드 호출또한 가능하다.
(MSDN을 통해 테스트 해보도록 하자- http://support.microsoft.com/kb/315582/ko)
 
 
Event Delegate를 한번에 이야기 하려 했으나
주저리 글이 많아지는 바람에 믿거나 말거나 + 헛소리도 많았지만
Delegate에 대한 글은 이정도로 하고 1부를 정리...
 
2부에서 다시 이야기를 시작하도록 하자.
 
 
**
빼먹은 내용이 있어 언급하자면 Delegate InstanceString과 같이 Immutable 하다는 점이다. 즉 위에 ILDASM에서도 보이지만 Combine 자체 기능도 두 개의 각기 다른 Instance를 통해 새로운 Instance를 리턴하는 것이지 기존의 Instance에 대한 변경을 가하는 점이 아니라는 것이다.
**
 
 
Reference : "C# In Depth",
 
=====
 
 


 

귀찮은 분을 위해  Delegate 비동기 호출을 간단히 예제를 통해 확인해 보도록 하자.

 

"BeginInvoke를 호출하게 될 경우 'IAsyncResult' 인터페이스를 리턴하게 되며 이는 EndInvoke시에 다시 사용된다."

 

[Source]

Code Snippet
  1. namespace ConsoleApplication5
  2. {
  3.     class Program
  4.     {
  5.         public delegate int BinaeryOp(int x, int y);
  6.         static public BinaeryOp b;
  7.         static int Add(int x, int y)
  8.         {
  9.             Console.WriteLine("Add() Invoked on thread {0}",
  10.                               System.Threading.Thread.CurrentThread.GetHashCode());
  11.             System.Threading.Thread.Sleep(8000);
  12.             return x + y;
  13.         }
  14.         static void Main(string[] args)
  15.         {
  16.             b = new BinaeryOp(Add);
  17.             IAsyncResult ar = b.BeginInvoke(10, 10, null, null);
  18.             Console.WriteLine("Main Thread {0}",
  19.                              System.Threading.Thread.CurrentThread.GetHashCode());
  20.             //1초마다 해당 Function의 작업이 완료되었는 지를 체크 함
  21.             while (!ar.AsyncWaitHandle.WaitOne(1000, true)) 
  22.             {
  23.                 Console.WriteLine("Doing more work in Main()!");
  24.             }
  25.             int answer = b.EndInvoke(ar);
  26.             Console.WriteLine("10 + 10 is {0}", answer);
  27.         }
  28.     }
  29. }

 


[Result]


'Program > C#' 카테고리의 다른 글

서비스 응용 프로그램 만들기  (0) 2018.01.23
Delegate와 Event의 이해(2)  (0) 2017.12.10
StringBuilder로 문자열 처리를 빠르게  (0) 2017.10.11
Invoke() 이쁘게쓰기!  (0) 2017.10.11
시리얼통신  (1) 2017.10.02

Git 주요 명령어 정리

Posted by 나에요임마
2017. 12. 6. 00:42 Program/GitHub

놀이터흙이제맛 2013.08.09 15:01

관련 문서

목차

add

staging / do track

작업폴더의 파일을 깃이 추적하게 하거나 커밋을 위한 준비상태로 만듦.

git add *
git add .
git add *.java
git add README.TXT

모든 추적 및 추적되지 않는 파일의 변경 내용을 추가

git add -A
git add --all
# git rm을 쓰지 않고 직접 삭제한 파일도 모두 스테이징할 때 쓰면 유용하다.

Git 대화 모드로 파일 staging

git add -i

branch

브랜치 생성

git branch mybranch

다른 시점의 스냅샷에 브랜치 생성

"checkout으로 포인터 이동 후 브랜치 생성"의 단축형. 여기서 위치는 다른브랜치나 커밋체크섬 혹은 태그가 될 수 있다.

git branch 브랜치명 위치

브랜치 확인

git branch
git branch -r   # 리모트 브랜치 목록 보기
git branch -a   # 지역과 리모트 브랜치 모두 보기
git branch -v   # 마지막 커밋 메세지도 함께 출력한다.

merge 여부 확인

merged 상태인 브랜치만 표시하거나 그렇지 않은 브랜치만 표시한다.

git branch --merged
git branch --no-merged

기존 브랜치를 다른 브랜치에 덮어쓰기

새로 브랜치를 생성하거나 기존 브랜치에 현재 브랜치의 데이터를 덮어쓴다.

git branch -f 대상브랜치 [위치]

브랜치 이름변경

git branch -m NAME_FROM NAME_TO

브랜치 삭제

git branch -d mybranch
git branch -D mybranch # 브랜치 강제삭제(보통 non-merged 브랜치를 삭제할 때 사용)

blame

바보같은커밋을비난하기위한명령어 blame은 데이터의 각 줄을 누가 언제 마지막으로 고쳤는지 확인할 수 있으며 디버깅용도로 사용된다.

파일 커밋 정보 줄 단위로 보기

git blame 파일

특정 라인만 보기

git blame -L 시작라인,종료라인 파일

git blame -L 12,22 simplegit.rb

파일의 줄 단위의 복사, 붙여넣기, 이동 정보 보기

git blame -M 파일

파일의 줄 단위의 이동과 원본 파일 정보 보기

git blame -C -C 파일

checkout

브랜치(혹은 HEAD) 이동

다른 커밋으로 이동함을 의미한다. 워킹 디렉토리는 이동한 브랜치의 데이터와 같아진다.

git checkout master
# 만약 master라는 브랜치가 로컬에 존재하지 않고 원격저장소에만 저장한다면 원격의 데이터를 checkout한다.

다른 커밋 이력으로 HEAD 이동

깃이 만든 히스토리의 특정 시점을 명시해서 그 시점의 스냅샷으로 되돌리는 것을 의미한다. 브랜치를 만들지 않고 체크섬이나 태그로 HEAD(포인터)를 이동할 수 있다. 현 작업폴더와 지정한 커밋 이력의 데이터가 같아진다.

이전 커밋으로 이동하면 해당 시점을 포함한 이전 이력만 조회할 수 있는 문제가 있다. 예를 들어 최초의 체크섬으로 이동했다고 가정했을때 이동 한 뒤에는 - log로 조회했을때 - 단 하나의 커밋 이력만 보인다. 하지만 태그를 달아뒀다면 어느 시점이던 상관없이 모두 보인다. 

checkout 으로 특정 체크섬 혹은 이나 태그로 이동했을 때 깃은 이것을 "분리된 HEAD 상태(detached HEAD)"라고 하며 이 상태에서 바로 작업하는 것보다 브랜치를 생성하여 작업할 것을 권장하고 있다.

git checkout 체크섬

git checkout 태그

git checkout 6f9021d4a03586a787ebcef2f94dd2eca1aec941
git checkout v0.3.1.5

브랜치를 새로 만들고 HEAD 이동

git checkout -b 브랜치

Modified 되돌리기

워킹 디렉토리의 파일을 로컬 저장소의 내용대로 복원

git checkout -- .   # 현재 경로의 모든 파일 되돌리기
git checkout HEAD .    # 현재 경로의 모든 파일 되돌리기

fetch 시점으로 HEAD 이동

git fetch 명령을 사용했을때 "FETCH_HEAD"라는 포인터에 스냅샷이 저장된다. 해당 스냅샷을 확인 후 원하는 브랜치로 이동해 머지한다.

git checkout FETCH_HEAD

git checkout 리모트저장소/리모트브랜치

fetch로 받아온 스냅샷을 머지하지 않고 새 브랜치로 생성

git checkout -b FETCH_HEAD

git checkout -b 생성할브랜치 리모트저장소/리모트브랜치

git checkout -b hotFix anotherServer/master   # 이 명령은 해당 브랜치를 트래킹 브랜치로 만든다.

원격 저장소의 추적(tracking)중인 브랜치 바꾸기

git-scm의 메뉴얼에서는 서버가 특정 브랜치를 추적하게 해서 pull, push를 서버/브랜치 명시 없이 쓸 수 있게 한다는데 직접 해보면 에러가 뜨면서 잘 안된다. 어떤 상황에 사용하는건지 설명이 부족함. 몇 번 헤딩해본 결과 서버에 한개 이상의 브랜치가 존재할 때, clone으로 받아오면 로컬에는 master 브랜치밖에 없다. 이때 checkout -b 로 브랜치를 생성하되 원격 저장소가 생성하는 브랜치를 추적하게 한다.

git checkout -b 로컬브랜치 리모트저장소/리모트브랜치

git checkout --track 리모트저장소/리모트브랜치   # 1.6.2 이상

git checkout --track origin/serverfix

Branch serverfix set up to track remote branch refs/remotes/origin/serverfix.
Switched to a new branch 'serverfix'

cherry-pick

cherry-pick은 커밋하나만 rebase 하는 것이다. 다음 페이지에서 자세한 사항을 확인할 수 있다.

Git 프로젝트 운영하기

선택 병합

git cherry-pick 커밋명

커밋하지 않고 선택 병합

git cherry-pick -n 커밋명

clone

저장소 복제

현재 디렉토리의 하위에 깃저장소를 만들고 명시된 원격 저장소의 데이터를 모두 받아온다. 디렉토리명을 명시하지 않으면 원격 저장소의 디렉토리명과 동일하게 만들어진다.

git clone ~/Documents/workspace/ex/cal/src
git clone file://c:/users/noritersand/noriterGit/localServer localGitRepo
git clone https://noritersand@dev.naver.com/git/lernforscm.git naverGitRepo
git clone git://github.com/schacon/grit.git gitHubRepo

bare repository: 깃저장소를 bare 저장소로 복제 

git clone --bare 저장소주소 [디렉토리]

복제 시 데이터 제한하기

git clone --depth 200 ~/Documents/work/   # 마지막 200개의 커밋만 복제한다.

commit

커밋

변경내용을 확정함. 워킹 디렉토리나 스테이징 에어리어에만 있는 파일을 깃저장소에 확정 저장한다.

git commit

에디터 없이 커밋

vim등의 에디터를 실행하지 않고 메세지를 즉시 입력한다.

git commit -m "hello this is test commit"

Signed-off-by 추가

커밋 메시지에 Signed-off-by를 추가한다. -s 혹은 --signoff 옵션을 사용하면 커밋 메시지에 작성자가 자동으로 추가된다.

git commit -s -m 'commit message test'
[master 1204311] commit message test
 1 file changed, 1 insertion(+)

git log -1 HEAD
commit 1204311e62660b46c375c6e4d9a61a48a9adeb1d
Author: noritersand <who@where.com>
Date:   Thu Nov 17 10:07:35 2016 +0900

    commit message test

    Signed-off-by: noritersand <who@where.com>

자동 스테이징(staging)

워킹 디렉토리의 변경한 파일을 스테이징 없이 커밋한다. 단, 추적중인 파일만 가능

git commit -a -m "auto staging"

마지막 커밋 수정

커밋 직후 사용하면 코멘트만 수정한다. 파일을 스테이징 했다면 새로 커밋 이력을 추가하지 않고 이전 커밋에 덮어쓴다. 

git commit --amend
git commit -m 'initial commit'
git add forgotten_file
git commit --amend # forgotten_file이 마지막 커밋에 추가된다.

마지막 커밋 수정하고 커밋 메세지 재사용

git commit -C HEAD --amend

config

config 는 --global 옵션을 빼고 사용할 때 각 저장소마다 각각의 설정정보를 저장한다.

작업자의 이름/이메일 설정

git config --global user.name "이름"
git config --global user.email "이메일"

기본 편집기 설정

git config --global core.editor 편집기

Diff 도구 설정

git config --global merge.tool vimdiff

설정 확인

git config --list
git config -l
git config --global --list
git config core.editor

단축어(alias) 만들기

git config --global alias.사용할키워드 '명령어'

git config --global alias.unstage 'reset HEAD --'
git config --global alias.visual '!gitk'
git config --global alias.ss 'status'

단축어 목록 보기

git config --get-regexp alias
git config --list | grep alias

단축어 삭제

git config --global --unset alias.ss

SSL 검증 비활성화

git config --global http.sslVerify false

diff

Unstaged와 Staged의 비교

git diff

Staged와 Commited의 비교

git diff --cached
git diff --staged

fetch

원격 저장소의 데이터 가져오기

데이터를 모두 가져오지만 머지는 생략한다. 보통 서버의 데이터를 확인하는 용도로 사용한다.

git fetch # origin 저장소에서 받아온다.
git fetch pb master

태그 받아오기

원격 저장소의 태그를 모두 받아온다. 태그'만' 받는다.

git fetch --tags

help

도움말 보기

git help <verb>

git <verb> --help

man git-<verb>

git help config
git config --help

init

기존 디렉토리를 Git 저장소로 만들기

git init

bare repository

작업폴더(working directory 혹은 work tree)가 없는 저장소를 만든다. 이 명령은 로컬 저장소가 아닌 원격 저장소 즉, 서버를 생성할 때 사용한다.

git init --bare

log

커밋 히스토리 조회

git log

log의 옵션

-p  각 커밋에 적용된 패치를 보여준다.

--stat  각 커밋에서 수정된 파일의 통계정보를 보여준다.

--shortstat  `--stat` 명령의 결과 중에서 수정한 파일, 추가된 줄, 삭제된 줄만 보여준다.

--name-only  커밋 정보중에서 수정된 파일의 목록만 보여준다.

--name-status    수정된 파일의 목록을 보여줄 뿐만 아니라 파일을 추가한 것인지, 수정한 것인지, 삭제한 것인지도 보여준다.

--abbrev-commit  40자 짜리 SHA-1 체크섬을 전부 보여주는 것이 아니라 처음 몇 자만 보여준다.

--relative-date  정확한 시간을 보여주는 것이 아니라 `2 주전`처럼 상대적인 형식으로 보여준다.

--graph 브랜치와 머지 히스토리 정보까지 아스키 그래프로 보여준다.

--pretty    지정한 형식으로 보여준다. 이 옵션에는 oneline, short, full, fuller, format이 있다. format은 원하는 형식으로 출력하고자 할 때 사용한다.

git log -p -2  # 2개의 항목과 패치내용만 보인다.
git log --pretty=oneline   # 각 커밋들의 메세지와 체크섬만 한 줄씩 출력된다.
git log -1 HEAD~3   # HEAD보다 세 개 이전의 커밋 로그 보기
git log v1.0 v2.4   # v1.0 태그에서 v2.4 태그 사이의 로그 보기

pretty=format의 placeholder

  • %H Commit hash
  • %h Abbreviated commit hash
  • %T Tree hash
  • %t Abbreviated tree hash
  • %P Parent hashes
  • %p Abbreviated parent hashes
  • %an Author name
  • %ae Author e-mail
  • %ad Author date (format respects the --date= option)
  • %ar Author date, relative
  • %cn Committer name
  • %ce Committer email
  • %cd Committer date
  • %cr Committer date, relative
  • %s Subject
  • 더 많은 내용은 공식 도움말 참고
git log --pretty=format:"%h %s" --graph

조회범위 제한옵션

-(n) 최근 n 개의 커밋만 조회한다.

--since, --after 명시한 날짜 이후의 커밋만 검색한다.

--until, --before 명시한 날짜 이전의 커밋만 조회한다.

--author 입력한 저자의 커밋만 보여준다.

--committer 입력한 커미터의 커밋만 보여준다.

git log --since=2.weeks
git log --pretty="%h - %s" --author=gitster --since="2008-10-01" \ --before="2008-11-01" --no-merges -- t/

merge

현재 브랜치와 다른 브랜치의 데이터를 병합

Git의 합치기(merge)는 두 개의 부모(parent)를 가리키는 특별한 커밋을 만들어 낸다. 두개의 부모가 있는 커밋이라는 것은 "한 부모의 모든 작업내역과 나머지 부모의 모든 작업, 그리고 그 두 부모의 모든 부모들의 작업내역을 포함한다"라는 의미가 있다.[각주:1]

수행하려는 merge가 3way 일때 충돌이 일어나 실패한다면 깃은 자동으로 해당파일에 충돌부분을 추가해 준다(텍스트로 확인할 수 있다).

git merge 브랜치1 [브랜치2]

git merge master   # 현재 HEAD가 가리키는 데이터를 master와 병합
git merge master hotfix   # hotfix를 master에 병합

# 새로운 토픽 브랜치인 iss 에서 작업 후 master와 병합하려고 한다면
git checkout master; git merge iss
# 혹은
git merge iss master

커밋하지 않고 병합

git merge --no-commit

Merge 도구 실행

3way merge가 실패했을때 사용한다. config 옵션에 설정된 도구가 실행되며 실행되기 전 .orig 확장자로 백업파일이 생성된다.

git mergetool

fetch 후 머지

git fetch로 받아온 스냅샷을 현재 브랜치와 머지한다.

git merge FETCH_HEAD

mv

파일명 수정

git mv FILE_FROM FILE_TO

pull

원격 저장소의 데이터 가져오기

fetch와 비슷하지만 pull은 머지를 자동으로 수행한다.

git pull [리모트저장소] [브랜치]

git pull   # 기본적으로 clone으로 생성한 원격 저장소가 origin일 때만 작동하지만, 추적브랜치를 변경해서 사용할 수도 있다.
git pull pb master

push

원격 저장소에 데이터 올리기

로컬의 커밋된 데이터를 서버에 업로드한다. 특정 조건을 충족하지 않으면 불가능할 때가 있다. 

git push [리모트저장소] [브랜치]

git push   # origin 저장소에 존재하는 브랜치에 push
git push origin other   # 서버에 other란 브랜치가 없으면 새로 생성 후 업로드
git push -u origin master  # 입력인자를 기억하는 옵션. 다음에 실행할 git push 혹은 git pull 이 origin/master로 연결되도록 한다.

로컬 브랜치와 원격 저장소의 브랜치 이름이 다를때 (혹은 원격 저장소에 다른 브랜치를 생성할 때)

git push 리모트저장소 로컬브랜치:서버브랜치

git push origin serverfix:awesomebranch    # 현재 로컬브랜치가 serverfix 일때 리모트브랜치 awesomebranch를 생성하고 push

리모트 브랜치 삭제

git push 리모트저장소 (공백):브랜치

git push teamone :other

생성한 태그 공유

push명령은 태그정보를 포함하지 않아서 따로 올려야 한다.

git push 리모트저장소 태그

혹은

git push --tags   # 생성한 태그를 모두 전송

git push origin v1.5

태그 정보 받아오기

클론으로 저장소를 복제하는게 아니라면 받아오는것 역시 추가작업이 필요. 단, 로컬에 있는 태그와 같은 커밋 이력일 경우엔 기존 태그를 삭제해야함.

git fetch 리모트저장소 tag 태그명

git pull 리모트저장소 tag 태그명

업스트림(upstream) 브랜치 설정

로컬 저장소를 init 명령어로 생성했을땐 수동으로 업스트림 브랜치를 설정할 필요가 있다. 업스트림 브랜치란 원격 저장소와 바로 연결된 로컬 저장소를 말하며 push나 pull 명령등에서 원격 저장소 이름을 생략할 수 있게 한다.

git push --set-upstream origin master
Branch master set up to track remote branch master from origin.
Everything up-to-date

새 로컬 저장소 생성하고 원격 저장소에 푸시하기

touch README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin https://noritersand@127.0.0.1:8443/r/test.git
git push --set-upstream origin master

기존의 로컬 저장소를 푸시하기

git remote add origin https://noritersand@127.0.0.1:8443/r/test.git
git push --set-upstream origin master

rebase

브랜치 병합(3way merge의 다른방법)

merge의 경우 두 브랜치의 최종결과만을 기준으로 병합한다면 rebase는 브랜치의 변경사항을 순서대로 다른 브랜치에 적용하며 병합한다. 저장소의 커밋 로그와 이력을 한 줄로 정리해주기 때문에 보통 완료된 브랜치를 마스터에 병합할 때 사용한다.

git rebase testing     # 현재 브랜치를 testing쪽으로 rebase

checkout과 rebase를 동시에

git rebase master testing   # testing을 master쪽으로 rebase, HEAD는 testing으로 변경

#iss123을 master에 rebase 한다고 했을 때:
git rebase master iss123   # iss123을 master에 rebase 하고..
git rebase iss123 master   # master 브랜치를 fast-forward 한다

rebase의 자세한 내용은 다음 페이지를 참고: Git브랜치 Rebase하기

reset

Staged 되돌리기(스테이징 취소)

add로 인덱스에 등록한 파일을 Unstaged 상태로 바꾼다. 파일을 명시하지 않으면 모두 되돌린다.

git reset HEAD [파일]

Commit 되돌리기

이미 커밋이 완료된 상태에서 이전 커밋으로 돌아간다. (커밋 이력도 사라짐)

git reset HEAD~1   # 이전 커밋으로 1회 되돌림

revert

Commit 되돌리기2

reset과 다른점은 이전 커밋내용으로 되돌리되 HEAD가 원래의 커밋을 가리키는게 아니라 새로운 커밋을 생성하여 변경한 내용을 기록한다.

git revert HEAD

rm

파일/폴더 삭제

git rm readme.txt

git rm 명령어는 git이 추적중인 파일 혹은 폴더에만 사용할 수 있다.

깃의 추적을 중단시키기(파일은 남기고 깃디렉토리에서만 삭제)

git rm --cached readme.txt

file-glob 패턴으로 범위삭제

git rm log/\*.log   # log/디렉토리의 확장명이 log인 파일 모두 삭제
git rm \*.~    # ~로 끝나는 파일 모두 삭제

remote

원격 저장소 목록

현재 로컬저장소에 등록되어진 원격 저장소를 모두 출력하되 단축이름만 표시한다.

git remote

원격 저장소 URL 확인

원격 저장소의 단축이름과 URL을 표시한다.

git remote -v

원격 저장소 URL 바꾸기

git remote set-url origin https://github.com/USERNAME/REPOSITORY.git

원격 저장소 추가

git remote add 단축이름 주소

git remote add remoteRepo https://noritersand@dev.naver.com/git/exlernforscm.git

원격 저장소 살펴보기

특정 저장소의 구체적인 정보(URL, 추적중인 브랜치)를 표시한다.

git remote show 리모트저장소

원격 저장소 이름 바꾸기

git remote rename NAME_FROM NAME_TO

원격 저장소 삭제

git remote rm 원격저장소이름

원격 저장소에서 쓸모가 없어진 브랜치 제거하기

git remote prune 리모트저장소

show

커밋 정보 조회

현재 HEAD의 커밋정보를 조회한다.

git show HEAD

태그로 조회

git show v1.1

체크섬(해시)으로 조회

체크섬은 중복이 없을때 앞의 일부분만 명시해도 된다.

git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b
git show 1c002dd4b

status

저장소 상태 확인

현재 깃저장소의 브랜치, 파일들의 상태를 볼 수 있다.

git status

svn

서브버전 저장소 복제

git svn clone SVN저장소주소

표준 레이아웃을 사용하는 서브버전 저장소 복제

표준대로 trunk와 branches 및 tags를 저장소 구조로 사용하는 저장소를 복제할 때 사용한다.

git svn clone -s SVN저장소주소

비표준 레이아웃을 사용하는 서브버전 저장소 복제

git svn clone -T 트렁크경로\

 -b 브랜치경로\

 -t 태그경로\

 svn저장소

표준 레이아웃을 사용하는 서브버전 저장소의 특정 리비전 복제

git svn clone -s -r 2321

표준 레이아웃을 사용하는 서브버전 저장소를 복제하고 모든 리모트 브랜치에 접두어 추가하기

git svn clone -s --prefix svn/ svn저장소

상위의 서브버전 저장소에서 갱신하고 재정렬하기

git svn rebase

상위의 서브버전 저장소에 커밋데이터 올리기

git svn dcommit

상위 서브버전 저장소에 푸싱될 커밋 목록 보기

git svn dcommit -n

서브버전 저장소 로그 보기

git svn log

서브버전 저장소 파일의 커밋정보 줄단위로 보기

git svn blame 파일

tag

태그 조회

git tag

Lightweight 태그 만들기

특정 커밋지점의 포인터를 생성(책갈피와 비슷한 개념)

git tag v2.2

Annotated 태그 만들기

이름, 이메일, 날짜, 메세지를 저장하는 태그를 생성함

git tag -a v1.1 -m "my version 1.1"

지나간 커밋 이력에 태그 만들기

체크섬을 알고 있다면 예전 커밋에도 태그할 수 있다.

git tag -a v0.8 9fceb02

태그에 서명

GPG(GNU Privacy Guard) 개인키로 태그에 서명

git tag -s 태그명 [-m "태그메세지"]

git tag -s v1.5 -m "my signed 1.5 tag"

태그 서명 검증

태그서명에 사용된 키가 공개키인지 검증한다.

git tag -v v1.0

태그 삭제

git tag -d v0.9

원격 저장소의 태그 삭제

git push origin :v0.9


'Program > GitHub' 카테고리의 다른 글

GitHub로 남의 프로젝트에 감놓고 배놓기  (0) 2017.08.05
완전 초보를 위한 깃허브  (0) 2017.08.05