Git에서 계정 別 권한을 주기 위해서 대부분의 경우에 사용하는 SSH,
하지만 push 등을 할 때마다 패스워드를 입력하기가 상당히 귀찮다.

편하게 할 수 있는 방법이 없을까? 당연히 있다!


1. .ssh 확인

   - ~/.ssh/ 경로를 확인하자.


$ ls -al ~/.ssh/

   - ssh 키 파일이 없다.



2. ssh-keygen

   - ssh 키 파일을 생성하자.


$ ssh-keygen

   - [ ssh-keygen ] 명령을 실행하고 그냥 다 엔터를 치면 된다.


$ ls -al ~/.ssh/

   - 다시 한 번 [ ~/.ssh/ ] 디렉토리 안의 파일을 확인하면 위 스크린샷과 같이 파일 2개가 생겼다.
   - 확장자를 보면 알겠지만 [ id_rsa.pub ] 파일이 바로 공개키다.



3. authorized_keys

   - 등록된 사람이라면 그냥 바로 접근을 허용하기 위해서는 authorized_keys 파일을 만들어야 한다.


$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

   - 위의 샘플의 경우는 git repository의 owner가 "chani"이고 사용자 역시 "chani"인 경우다.

   - "chani" 계정에 ssh로 접근하는 사용자가 등록된 사용자인지 확인을 하기 위해서는 "authorized_keys"을 참조한다.



   - [ authorized_keys ]가 등록되어 있으면, 위와 같이 패스워드를 묻지 않고 바로 진행이 된다.



   - 그런데, 위와같이 [ authorized_keys ] 파일을 만들어주는 것은 대단히 원시적인 방법이다.

$ ssh-copy-id -i ~/.ssh/id_rsa.pub chani@localhost

   - 위와 같이 하면 뒷쪽에 있는 타겟에 [ authorized_key ] 파일을 생성한다!
   - 참고!



곧 알아보려고 하는 gitolite를 활용한 Git 계정관리에서도
기본적으로 활용하는 "SSH 공개키" 사용 방법에 대해서 알아보았다.

이 부분은 Git에서만 적용되는 것이 아니라, SSH 환경에서 기본적으로 적용하는 방식이다.

잘 알아두면 리눅스 환경에서 많은 도움이 될 것이다.
반응형

지금까지 remote repository에서의 branch에 대해서 살펴보면서,
remote repository에 branch를 추가하는 것에 대해서 아직도 알아보지를 않았다.


1. remote branch 생성

   - remote repository 內에 branch를 생성해보자.


$ git branch develop
$ git checkout develop
$ git push origin develop

   - remote repository에 원하는 branch를 만들고 싶다면 원하는 이름으로 push를 해버리면 된다.
   - 위와 같이 궂이 꼭 별도의 branch를 만들어서 push를 할 필요는 없지만,
     예시와 같이 같은 이름의 branch를 만들어서 그 이름으로 push를 통해서 remote branch를 생성하는 것이 편하다.


2. clone

   - remote repository가 제대로 되어있는지 확인하기 위해서 새로 clone을 한 번 해보자.


$ git clone chani@localhost:/srv/repository/bare1repo.git
$ cd bare1repo
$ git branch -a



3. tracking

   - local master branch에서 작업을 한 뒤에 [ git push ]를 하면 알아서 'origin/master'에 들어간다.
   - 이는 서로간에 tracking 관계가 설정되어 있기에 그냥 되는 것이다.


$ git checkout --track origin/develop
$ git config -l

   - remote에 위치하고 있는 'origin/develop' branch를 따르는 branch를 local에 생성을 하고 싶다면,
     [ git checkout --track origin/develop ] 명령을 사용하면 된다.
   - 그러면, local 에서 'develop' 이라는 이름으로 branch를 생성하고, checkout 까지 한다.
   - 더불어, 핵심 포인트인 'develop ~ origin/develop' 사이에 tracking 기능이 설정이 된다.

   - tracking 관계를 확인하기 위해서는 [ git config -l ] 명령을 사용하면 된다. 뒤의 옵션은 "L"이다.
   - 밑 부분의 branch 를 살펴보면 연결관계를 확인할 수 있다.



4. checkout track

   - [ --track ] 옵션으로 생성을 하게 되면 local branch 이름을 remote branch 이름과 똑같이 설정을 한다.


$ git checkout -b hotfix origin/develop

   - 위와 같이 'hotfix'라는 이름으로 'origin/develop'을 tracking 하는 local branch를 생성하였다.



5. delete

   - 만들었으면 지우는 방법을 알아볼 차례다.


$ git push origin :develop

   - [ push ] 명령을 이용하여 remote branch를 삭제하면 되는데, branch 이름 앞에 [ : ]를 붙이면 된다.
   - 지금까지의 명령 방법과는 조금 다른 방식이니 주의해야할 것 같다.



tracking 관계에 있게 되면 기본적으로 둘 사이에 밀접한 관계를 맺고 있다는 의미가 된다.
해당 branch에서 작업한 것은 그 관계에 있는 remote branch에 반영을 하겠다라는 의미가 되기 때문이다.

이는 잘 이용하면 상당히 편하게 작업을 할 수 있을 것이다.


아름다운 근로자의 날이다.
근로자 여러분 모두 파이팅~!!!
반응형

'SCM > Git-GitHub' 카테고리의 다른 글

GitWeb + Nginx  (0) 2012.05.09
SSH Public Key - SSH 공개키  (0) 2012.05.04
Git Branch (브랜치) - Remote Ⅱ (Pull)  (0) 2012.04.30
Git Branch (브랜치) - Remote Ⅰ (Fetch)  (0) 2012.04.30
Git Branch (브랜치) - Local Ⅴ (ETC)  (0) 2012.04.24

Remote Repository의 변경 사항이 있을 때마다 fetch 받아서 merge를 하는 것은
왠지 비효율적인 것 같고 불편한 것 같이 느껴진다.

Git이 그런 아이가 아닌데....

물론 이와는 다른 방법이 있다. 편한 방법이...
이에 대해서 하나씩 살펴보자.



1. After merge

   - 앞에서 진행한 내용이후의 상황을 살펴보자.


$ git status
$ git log -4

   - remote의 변경 사항을 fetch 받아서 지금 작업하고 있는 내용에 merge를 한 상황이다.
   - 그런데, [ git status ]를 하면 "Your branch is ahead of 'origin/master' by 2 commits" 라고 보인다.

   - 하나는 local master branch에서 push 하지 않은 commit 이고,
   - 다른 하나는 merge로 인해서 발생한 commit 이다.



2. clean

   - 일단, 다른 것을 확인해보기 위해서 위와 같이 확인을 했다면, 잠시 정리 작업을 진행하자.


$ git push
$ git status

   - local에 남아있는 commits을 remote로 밀어 넣자.
   - [ git status ] 실행 결과가 위와 같이 깔끔해야 한다.



3. pull

   - 앞의 포스팅을 따라했다면, "2nd/bare1repo" 를 만들었을 것이다.
   - 만약에 없다면, 그냥 무시~ ^^


$ git pull


   - remote repository에서 소스를 받아온 후, 아무런 작업이 없었다면 (push 안된 commit이 없는 경우)
   - 위와 같이 그냥 소스코드를 업데이트 하곤 끝이다.



4. remote repository update

   - 오늘 살펴볼 내용을 만들기 위해서 별도의 local에서 작업 후 push하여 remote를 업데이트 해보자.


   - 일단은 모두 정리를 했다면, 위 그림과 같다.


   - 작업을 한 다음에 commit을 하나 했다면 위 그림과 같다.


 

$ git status
$ nano ./readme.txt
$ git commit -a -m "modify readme.txt in master, 2nd"
$ git push


   - 파일 하나 수정 후 commit을 하고, 마지막으로 [ git push ]를 해주자.

   - 위 그림과 같이 "A" commit 상태에서 "B" commit 상태로 'master'와 'origin/master' 모두 변경되었다.



5. git pull

   - 이제 원래 작업하던 local로 변경하여보자.



   - "A" commit 상태였지만, 저 위의 작업으로 인해서 'origin/master'의 실제 상태는 "B" commit 이다.

   - 그런데, master branch에서 소스 수정 후 commit을 하게 되면 어떻게 될까?



   - 이제는 [ git pull ]을 해 볼 시간인데... 어떻게 될까?


   - [ git pull ]을 하게 되면 바로 앞의 포스팅과 같이 'merge commit'을 생성하면서 코드를 섞어준다.


   - 그림으로 살펴보면 위와 같다.



결론은, 아래와 같다.

[ git pull ] = [ git fetch ] + [ git merge origin/master ]

즉, 최신 변경된 내역을 내가 작업하고 있는 것에 반영하기 위해서는 'git pull'을 사용하는 것이 편하다!!!


더불어서, git의 merge는 "3-way merge"라는 방식이다.




헥헥~ 오늘은 내일 '근로자의 날'을 앞두고,
일요일에 진행한 "두산 vs KIA" 경기를 다시보기로 보면서 역전승이라는 드라마에 감동하면서 포스팅하고 있다.
두산 파이팅~!!!

반응형

'Work Flow'에 대해서 살펴보기 전에,
'Remote Repository'에 존재하는 'branch'와 'Local Repository'에 존재하는 'branch'의 관계에 대해서 알아보겠다.


조금 다른 일반적인(?) 용어로 이야기를 하자면,
서버에서 소스를 가져와서 작업을 하고 있던 중 서버의 내용이 변경이 되어,
이것을 내가 작업하고 있는 곳에 반영을 하고 싶은 경우에 어떻게 할 것인가? 에 대한 설명을 해보고자 한다.


1. branch -a

   - 현재 작업 공간을 한 번 점검해보도록 하겠다.



$ git branch -a


   - 오늘 진행할 내용을 위해서 잡다한 것 모두 지우고, 정리하고, push하고 해 놓았다.

   - 여기서 확인하고 싶은 것은 기본적인 branch의 내역이다. "origin/master"의 존재 !!



2. commit in master branch

   - local에서 즉, master에서 commit을 하나 해보자.



$ nano ./readme.txt
$ git commit -a -m "modify readme.txt in master br"
$ git status


   - 여기에서 잘 살펴볼 부분은 [ git status ]를 했을 때 나오는 메시지이다.
   - "Your branch is ahead of 'origin/master' by 1 commit."
   - 최근의 git은 정말 안내 메시지가 너무 잘 되어 있다~!!! 짱~!!!


3. commit in origin/master

   - remote repository에 commit을 하나 추가된 상황을 만들고자 한다.




$ cd /srv/workspace/2nd
$ git clone {계정}@localhost:/srv/repository/bare1repo
$ cd ./bare1repo


   - 별도의 commit을 만들어 remote에 넣기 위해서 다른 곳에 별도의 local repository를 clone 하자


$ nano ./readme1.txt
$ git commit -a -m "modify readme1.txt in master br, but other master"
$ git push


   - 파일 하나를 수정하고 commit 한 다음에, remote repository로 밀어넣자(push).



4. fetch & merge

   - remote repository의 변경된 내역을 local로 받아오기 위한 작업을 해보자.



$ git fetch
$ git status

   - remote repository의 정보를 얻어오기 위한 명령어는 [ git fetch ]이다.
   - [ git status ]를 해보면 이전과는 또 다른 메시지가 보일 것이다.

   - " Your branch and 'origin/master' have diverged,
        and have 1 and 1 different commit each, respectively. "

   - " diverge " 뜻을 모르는 저같은 분들을 위한 단어의 의미 :
1. (다른 방향으로) 갈라지다   2. 나뉘다, 갈리다   3. (예상・계획 등에서) 벗어나다


$ git merge origin/master

   - [ git log ]를 보면, 위의 그림대로 구성되어진 것을 확인할 수 있을 것이다.




별 것 아닌 것으로 보일 수도 있는데,
git을 혼자서가 아니라 팀으로 작업하는 경우라면 의외로 자주 발생하는 상황일 것이다.

Git에서 보여주는 메시지들을 다시 한 번 잘 살펴보고, 흐름을 잘 느끼고 생각하면 많은 것을 배울 수 있을 것이다.



이런 에잇~ 또 12시를 넘겨버렸네.... ㅠㅠ
오늘 자전거 타이어 바꾸려고 편도 15km가 넘는 거리를 달려갔지만 결국 허탕치고 오늘 자전거만 30km가 넘게 타서...
지금 허리아프고 손목아프고, 목 아프고..... 이런 상황에서 12시를 넘겨버리다니... ㅠㅠ
내일 출근해야하는데.... 흑흑.... 어여 잠자야겠다.

반응형

직전 포스팅 서두에 '잡다한 정보'를 알려준다고 해놓고는,
branch에서 가장 중요하다고 할 수 있는 conflict 이야기를 해버렸다. ㅋㅋ

이번에는 정말 branch에 대한 정말 잡스러운 이것 저것들에 대해서 포스팅하기로 하겠다.
이번 포스팅으로 branch 이야기를 마무리 지으려고 하는데... 안되면 한 두번 더 하지 뭐~ ^^



1. git branch & checkout

   - hotfix/patch branch 만들고 'hotfix branch'로 checkout을 하자.


 

$ git branch hotfix
$ git branch patch
$ git checkout hotfix



2. git branch -v

   - 각 branch 別 마지막 commit을 확인하고 싶으면 [ -v ] 옵션을 사용하면 된다.


$ git branch -v



3. commit in hotfix branch

   - 'hotfix branch'에서 파일 하나를 수정하고 commit 해보자.




$ nano ./readme.txt
$ git commit -a -m "modify readme.txt in hotfix br"


$ git checkout patch
$ nano ./readme.txt
$ git commit -a -m "modify readme.txt in patch br"



4. commit in master branch

   - 'master branch'에서 commit을 하나 해보자.



$ git checkout master
$ nano ./readme.txt
$ git commit -a -m "modify readme.txt in master br"




5. merge

   - 이번에는 드디어 merge 작업을 해보자.



$ git merge hotfix
$ git mergetool
$ rm -rf ./readme.txt.orig
$ git commit -m "merge between master and hotfix"


   - merge를 하게 되면 'hotfix branch'에 있던 commit이 'master branch'에 합쳐진다.
   - 위 그림과 위 스크린샷을 잘 살펴보기 바란다.


6. git branch --merged / --no-merged

   - branch와 관련한 잡스런(?) 정보를 보여주는 옵션들이 있다.

 
   - [ git branch --merged ] merge가 이루어진 branch 목록을 보여준다.

   - [ git branch --no-merged ] merge가 행해지지 않은 branch 목록을 보여준다.

   - 어떤 branch를 만들어서 commit이 한 번 이상 이루어졌을 경우,
     별도의 merge를 하지 않으면 위 스크린샷과 같이 그냥 삭제가 이루어지지 않는다.
   - 강제로 지우고 싶으면 [ -D ] 옵션을 사용하라고 알려주고 있다. 


   - 5번 항목의 그림을 보면 알겠지만, 'patch branch'를 삭제하게 되면,
     "3a45533" commit은 붕~뜨게 된다. 그러기에 그냥 삭제하라고 하면 경고를 하게 되는 것이다.

   - 5번 항목의 그림을 다시 한 번 보게되면, "hotfix branch"의 경우,
     기존에 가지고 있던 "7770966" commit은 "master branch"의 흐름에 같이 섞여버렸다. 

 

 



여기까지해서 Local 에서의 branch 에 대해서는 대강 다 살펴본 것 같다.

앞으로는 이러한 Git 에서의 branch 특성을 이용한 Work Flow에 대해서 간단히 알아보고,
그 다음에는 Remote Repository 를 사용하는 환경에서의 branch에 대해서 알아보도록 하겠다.


오늘은 여기까지.... (우와~ 오늘은 자정 안넘겼다~~~!!! 금일 칼퇴근의 힘~!!!)

반응형

조금은 잡스러울 수 있는 부분들에 대해서 알아보자.


1. uncommit


$ git branch patch
$ nano ./readme.txt
$ git checkout patch
$ nano ./readme.txt

   - 'patch branch'를 생성하고, 그냥 'master branch'에서 파일을 수정한 다음에,
   - commit 하지 않고 'patch branch'로 checkout을 하면...
   - 좀 전에 'master branch'에서 수정한 파일의 내용이 어떻게 될까!?

   - 결론은 변경된 내용이 그대로 'patch branch'에서도 유지가 되어 있다.
   - 위 스크린샷에서 보면, checkout을 한 뒤 나오는 메시지를 보면 [ M    readme.txt ]라는 내용이 보일 것이다.

   - 이는 변경된 파일 내용을 유지하지 않을 경우 그 변경된 내역을 저장할 방법이 없기 때문으로 보인다.
   - 파일의 변경된 사항은 commit 단위로만 기억하는 Git이라는 것을 잘 알고 있어야겠다.



2. conflict

   - branch를 만들어서 각각 다른 부분들을 수정을 했다면, merge에 문제가 없으나,
   - 같은 부분을 서로 수정을 했다면 merge에 문제가 발생을 한다.


 $ git branch patch
 $ nano ./merge.txt
 $ git commit -a -m "modify 1st line in master br"

   - 'patch branch'를 만들어 놓고,
   - 'master branch'에서 'merge.txt'라는 파일의 1번째 줄을 수정하고 commit 한다.


 $ git checkout patch
 $ nano ./merge.txt
 $ git commit -a -m "modify 1st line in patch br"

   - 'patch branch'로 checkout을 한 후 'merge.txt' 파일을 수정 후 commit 한다.
   - 'master branch' 때와 마찬가지로 1번째 줄을 수정한 것이다.


 $ git checkout master
 $ git merge patch
 $ git status

   - 'master branch'로 다시 checkout을 한 뒤에 [ git merge patch ]를 해보자.
   - 충돌이 난 것을 볼 수 있을 것이다 (같은 줄을 각자 수정을 했으니...)

   - 충돌이 난 것을 다시 한 번 보기 위해서는 [ git status ]를 해보면 된다.


 $ git mergetool

   - 머지 도구를 활용하기 위해서는 [ git mergetool ]이라고 실행하면 된다.
   - 나의 경우에는 'meld'를 사용한다고 설정을 해놓아서 기본 도구로 (meld)가 보인다.


   - 'meld'의 실행화면이다.
   - 왼쪽이 'master branch'의 파일 내용이고, 오른쪽이 'patch branch' 파일의 내용이다.


   - 수정한 내역이 있으면 위와 같은 화면이 나온다.
   - 저장하고 빠져나오자.


   - 'meld'를 빠져나오면 위 스크린샷과 같이 이상한 파일이 하나 생긴다. "merge.txt.orig"
   - 그 내용을 살펴보면 위와 같이 뭐가 어떻게 충돌이 나는지를 보여준다.


   - 뭐 별 문제가 없다면 그냥 삭제하면 된다.

   - 이제는 merge를 할 때에 충돌이 난 부분을 해결을 했으니, commit을 하면 된다.


   - 그런데, 위 스크린샷을 보면 상당히 특이한 것을 확인할 수 있다.
   - commit 하나만 했을 뿐인데, log를 보면 1개가 아닌 2개의 log가 추가된 것을 볼 수 있다.
   - 즉, patch branch에서 작성한 commit이 자동으로 따라 붙어버린 것이다.




여기까지 해서 conflict 해결하는 것까지 살펴보았다.
이제 자야겠다. 일자가 바뀌었다.
눈알이 빡~빡~
반응형

또 다시 간만에 작성하는 Git 이야기...^^

branch의 경우 설명하는 글들을 읽어도 알기 힘든 부분들이 많다.



오늘 살펴보고자 하는 것은 지난 번 포스팅한 내용 중,
merge를 하게 되면 마지막 commit을 한 내용을 그대로 가져온다고 했었다는 내용에 대해서다.


Git의 branch를 그냥 막 사용한다면 모르겠지만,
하나 하나 그 내용을 분석이 필요하다고 하면 아래 내용을 잘 따라와보면 도움이 될 것이다.



1. 준비

   - 지금 branch를 활용한 작업을 하기 전에 준비를 하자.



 $ git branch -a

   - [ git branch -a ] 명령을 통해 모든 branch 상태에 대해서 알아보자.
   - [ * ] 표시가 되어있는 branch가 지금 현재 작업을 하고 있는 branch이다.


2. branch & commit

   - branch를 하나 만들고, 'master branch'에서 commit을 하나 해보자. 



 $ git branch patch1
 $ nano ./readme.txt
 $ git commit -a -m "nano readme.txt in master br"

   - "A" 시점에서 'patch1 branch'를 생성을 하고,
   - 'master branch'에서 파일 수정 후 commit을 해서 "B" 시점으로 갔다.
   - 그래서 'master branch'는 지금 현재 "B" 위치에 있는 것이다.


3. checkout & commit

   - 작업하고 있는 branch를 바꿔보자.



 $ git checkout patch1

   - [ git checkout patch1 ]을 통해 작업하고 있는 branch를 변경했다.



 $ nano ./readme1.txt
 $ git commit -a -m "nano readme1.txt in patch1 br"

   - 'readme1.txt' 파일을 수정 후 commit 하자.

   - commit을 추가로 한 번 더 해보자.



 $ nano ./readme1.txt
 $ git commit -a -m "1 more, nano readme1.txt in patch1 br"

   - 'patch1 branch'에서 commit 2건을 추가한 것이다.



4. checkout & merge

   - 각 branch 別 상황을 좀 살펴보자.


 $ git branch -a
 $ git log -2
 $ git checkout master
 $ git log -2

   - 'patch1 branch'의 log들과 'master branch'의 log들을 잘 살펴보기 바란다.

   - 이제 merge를 실행해 보자.



 $ git merge patch1
 $ git log -4

   - 위 그림과 스크린샷을 잘 봐야 한다!!! 많은 것들이 녹아 있다.

   - "E" 지점(commit)은 별도로 만든 것이 아니라 'merge'로 만들어진 것이다.
   - 그래서 log를 보면 "Merge branch 'patch1'"이 보일 것이다.

   - 그런데, 또 하나 특이한 것은 'patch1'에서 이루어진 commit까지 모두 따라왔다.
   - 그러면, 'patch1 branch'를 지워버리면 어떻게 될까?


 $ git branch -d patch1

   - 'patch1 branch'를 그냥 지워버리면 어떻게 될까?!
   - 위에서 보는 바와 같이 그냥 'branch'만 지워지고 변화는 없다.


   - branch를 지운다고 하여도 그 branch에서 작업을 했던 commit들은 지워지지 않는다는 말 같은데...
   - merge가 되었다면 의미가 있지만, merge가 없는 상태에서 branch를 지웠다면...
   - 의미 없는 commit들이 그냥 살아있다는 말이 되는데...
   - 이 부분에 대해서는 꼭 한 번 깊이있게 살펴볼 예정이다. 개인적으로 관심있는 부분이라서....^^



여하튼, 이번 포스팅에서 확인을 한 것은,
branch에서 만든 commit들이 merge 후에도 계속 따라온다는 것이다.

commit 하나 하나에 더욱 더 신경을 써야 한다는 결론이 나온다.

반응형

Git Branch에 대해서 계속 살펴보자.


1. git checkout -b


 $ git checkout -b hotfix

   - 브랜치를 만들고 새로 만든 브랜치로 작업 영역을 변경하기 위해서는 2단계의 과정을 거쳐야 한다.
      . [ git branch 블라블라 ] + [ git checkout 블라블라 ]

   - 이걸 한 묶음으로 처리하기 위해서는 아래와 같이 하면 된다.
      . [ git checkout -b 블라블라 ]


2. commit


   - 현재 어느 브랜치에서 작업을 하고 있는지 확인을 하고... [ git branch ]
   - 파일을 하나 수정 후 commit을 하자. [ git commit -a -m "블라블라" ]

   - 지금 긴급히 수정할 꺼리가 있어서 hotfix를 한다고 가정을 해보는 것이다.


3. merge


 $ git branch
 $ git checkout master
 $ git merge hotfix

   - 변경 사항을 가지고 올 브랜치로 checkout을 먼저 하고선, [ git checkout master ]
   - 어느 브랜치의 것을 가지고 와서 merge를 할지 명령을 하면 된다. [ git merge hotfix ]

   - 여기서 궁금한 것은 merge를 한 것은 알겠는데, 파일만 합친 것인지...?!


   - merge를 한 이후 [ git log ]를 해보면, 본래 없던 commit이 추가되어 있는 것이 보일 것이다.
   - 즉, 위의 경우는 "hotfix" 브랜치의 마지막 commit을 "master" 브랜치로 복사해버린 것이다.



4. delete


 $ git branch -d hotfix

   - 용도가 없는 branch를 지울 때엔 '-d' 옵션을 이용하면 된다.




브랜치에 대해서 공부를 하려면 꼭 나타나는 '머지 (merge)'다.
일단 가볍게 맛만 보는 정도에서 마무리 짓겠다.


앞으로도 몇 번 더 포스팅하면서 더 알아보도록 하겠다.

반응형

+ Recent posts