1. 먼저, 병합하려는 대상 브랜치로 체크아웃합니다 (예: main 또는 master).

git checkout main

2. 대상 브랜치를 최신 상태로 업데이트합니다.

git pull origin main

3. 병합하려는 소스 브랜치로 체크아웃합니다 (예: feature-branch).

git checkout feature-branch

4. 대상 브랜치로 다시 병합합니다.

git merge main

5. 충돌이 발생하면, yarn.lock 파일을 삭제합니다.

git rm yarn.lock

6. yarn install을 실행하여 새로운 yarn.lock 파일을 생성합니다.

yarn install

7. 생성된 새 yarn.lock 파일을 커밋합니다.

git add yarn.lock
git commit -m "Resolve yarn.lock merge conflict by regenerating it"

8. 이제 안전하게 병합을 계속 진행할 수 있습니다.

git merge main
 

왕초보 깃허브 시작하기 : 1. 계정 생성부터 개념, 저장소(repository) 생성까지
상단에 글과 이어집니다.
(깃허브 계정생성, Repository 생성, 깃 설치, 깃허브 사전 지식 등에 관한 내용)

브랜치란?

브랜치(Branch)

브랜치는 깃에서 코드를 분리하고 독립적으로 관리할 수 있도록 하는 개념입니다.
브랜치를 사용하면 여러 개발자가 동시에 작업하거나 특정 기능을 개발할 때 각자 독립적인 작업 환경에서 작업할 수 있습니다.
각 브랜치는 프로젝트의 특정 상태를 나타내며, 변경 사항을 다루기 위해 사용됩니다.

주요 브랜치 관련 용어와 개념

메인 브랜치(Main/Branch):
프로젝트의 기본이 되는 브랜치로서, 주로 "main" 또는 "master"라는 이름으로 사용됩니다.
일반적으로 안정된 코드를 유지하고 배포하기 위해 사용됩니다.

피처 브랜치(Feature Branch):
특정 기능을 개발하기 위해 메인 브랜치에서 파생된 브랜치입니다.
개발자는 피처 브랜치에서 해당 기능을 구현하고 테스트한 후, 완료되면 메인 브랜치로 병합(merge)할 수 있습니다.

브랜치 생성(Create a Branch):
새로운 브랜치를 만들어 기존 브랜치에서 독립적으로 작업을 수행할 수 있습니다.

git branch [브랜치 이름]

브랜치 전환(Switch Branch):

작업 중인 브랜치를 변경합니다.

git checkout [브랜치 이름]

또는

git switch [브랜치 이름]

브랜치 병합(Merge Branch):
브랜치를 현재 브랜치로 병합합니다.

git merge [브랜치 이름]

브랜치를 사용하면 여러 작업이 동시에 진행되거나 새로운 기능을 안전하게 개발할 수 있으며, 각 브랜치는 독립적인 커밋 히스토리를 가지게 됩니다. 이는 협업과 프로젝트 관리를 더욱 효과적으로 할 수 있도록 도와줍니다.

GitHub 작업 플로우와 용어, 명령어정리


(출처 : https://phoenixnap.com/kb/how-git-works)

Working Tree : 작업 트리
Working Directory : 작업 디렉터리
Staging Area(Index) : 스테이징 영역
Repository : 저장소
-Local Repository : 로컬 저장소
-Remote Repository : 원격 저장소

포크(Fork):
프로젝트의 원본 저장소를 개인 계정으로 포크합니다.
이를 통해 원본 프로젝트를 수정하지 않고 개인 계정에서 변경을 가할 수 있습니다.

클론(Clone):
개인 계정에서 포크한 저장소를 로컬 환경으로 복제합니다.
포크를 거치지 않더라도 저장소 URL을 입력하여 로컬 환경으로 복제할 수 있습니다.

git clone [저장소 URL]

변경 사항을 스테이징 영역에 추가(add):
변경 사항을 스테이징 영역에 추가합니다.

git add [파일명]

또는

git add .

로컬에서 변경사항 커밋(Commit Locally):
로컬에서 작업하고 변경한 내용을 로컬 저장소에 커밋합니다.

git commit -m "커밋 메세지"

원격 저장소에 푸쉬(Push to Remote):

로컬 변경 사항을 원격 저장소로 푸쉬합니다.

git push [원격 저장소 이름] [브랜치 이름]

병합하기(merge)
브랜치를 현재 브랜치로 병합합니다.

git merge [브랜치 이름]

풀 리퀘스트(Pull Request, PR):
로컬에서 작업한 변경 사항을 원격 저장소에 푸쉬하고 풀 리퀘스트를 생성합니다.
PR은 기능 추가, 버그 수정 등의 변경 사항을 원본 저장소로 병합하도록 요청하는 것입니다.

git pull-request

리뷰와 머지(Review and Merge):

풀 리퀘스트를 생성한 프로젝트 관리자 또는 협업자가 코드 변경 사항을 확인하고 필요한 리뷰를 진행합니다.
리뷰가 완료되면 풀 리퀘스트가 원본 저장소에 머지됩니다.

원격 저장소에서 최신 변경 사항을 현재 브랜치에 가져오기(pull):
원격 저장소에서 최신 변경 사항을 가져와 현재 브랜치에 병합합니다.

git pull [원격 저장소 이름] [가져올 브랜치 이름]

이러한 작업플로우는 여러 개발자가 동시에 협업하고 변경 사항을 관리할 때 효과적으로 사용됩니다.
특히 풀 리퀘스트를 통한 코드 리뷰는 프로젝트의 코드 품질을 유지하고 협업을 원활하게 합니다.

Git이란

깃(Git)은 소프트웨어 개발 프로젝트를 관리하기 위한 분산 버전 관리 시스템(DVCS) 중 하나입니다. 소프트웨어 개발자들이 소스 코드의 변경 내용을 효과적으로 추적하고 협업하는 데 사용됩니다.

깃의 주요 특징

분산 버전 관리: 깃은 프로젝트의 모든 이력과 변경 내용을 로컬 저장소에 저장하므로 네트워크에 연결되지 않은 상태에서도 작업이 가능하며, 중앙 서버에 의존하지 않습니다.

브랜치: 브랜치를 지원하여 여러 개발자가 동시에 작업하고 서로 다른 기능을 개발할 수 있습니다. 이를 통해 동시에 다양한 작업을 진행할 수 있습니다.

병합(Merging): 브랜치에서 개발한 내용을 다시 통합하는 기능이 있어 여러 사람이 개발한 내용을 하나로 합칠 수 있습니다.

이력 관리: 소스 코드의 변경 이력이 상세하게 기록되어 누가 언제 어떤 변경을 했는지를 쉽게 추적할 수 있습니다.

원격 저장소: 깃허브(GitHub)와 같은 원격 저장소를 통해 여러 사용자 간에 협업이 용이하며, 프로젝트의 백업과 공유가 가능합니다.

 

많은 개발자와 프로젝트에서 깃을 사용하여 소스 코드를 효과적으로 관리하고 협업합니다.

GitHub란

깃허브(GitHub)는 깃(Git)을 기반으로 하는 웹 기반의 버전 관리 플랫폼입니다. 개발자들이 소스 코드를 저장하고 관리하는데 사용되며, 프로젝트의 협업과 커뮤니케이션을 간편하게 도와줍니다. 깃허브는 코드 호스팅, 협업 도구, 이슈 트래킹, 코드 검토, 빌드 및 배포 등 다양한 개발 관련 기능을 제공합니다.

깃허브의 주요 특징

코드 호스팅: 개발자들은 깃허브를 사용하여 소스 코드를 저장하고 관리할 수 있습니다. 다양한 프로젝트가 깃허브에 호스팅되어 있으며, 다른 사용자들이 소스 코드를 참고하고 기여할 수 있습니다.

협업과 이슈 트래킹: 사용자들은 프로젝트에 이슈를 제기하고 토론할 수 있으며, 작업을 추적하고 문제를 해결하기 위한 기능들이 제공됩니다.

브랜치와 병합: 깃허브는 깃의 브랜치와 병합 기능을 활용하여 여러 개발자가 동시에 작업하고 소스 코드를 관리할 수 있도록 도와줍니다.

코드 검토: 코드 검토 기능을 통해 여러 사용자가 제안된 변경 사항을 확인하고 피드백을 주고받을 수 있습니다.

빌드 및 배포: 깃허브는 CI/CD(Continuous Integration/Continuous Deployment)를 지원하여 소프트웨어를 자동으로 빌드하고 테스트하며, 배포하는 프로세스를 구축할 수 있습니다.

오픈 소스 생태계: 많은 오픈 소스 프로젝트가 깃허브를 통해 관리되며, 전 세계의 개발자들이 이를 활용하고 기여합니다.

깃허브는 개발 생태계에서 널리 사용되며, 개발자들 간의 협업과 소스 코드 관리를 효과적으로 지원하는 중요한 도구 중 하나입니다.

GitHub 계정 생성

깃허브 링크 https://github.com/
상단에 링크를 클릭하여 깃허브 사이트에 접속합니다.


접속 후 우측 상단에 SignUp을 클릭하여 회원가입을 진행합니다.


사용할때 이메일을 입력 후 continue를 클릭해주세요
이메일은 가입 인증번호, repository 초대, 토큰, 비밀번호 재설정 등의 이메일이 전송될때 사용됩니다.


이후 사용하실 비밀번호를 입력해주세요. 사용가능한 비밀번호라면 continue 버튼이 활성화됩니다. 정책에 맞는 비밀번호를 입력 후 continue를 눌러주세요.


사용하실 이름을 설정해주세요. username은 계정의 이름이라고 생각하시면 됩니다. 추후에 변경이 가능하나 중복은 안됩니다. 입력 후 continue를 눌러주세요.


깃허브와 관련된 업데이트 및 공지사항을 전송받을지에 대한 여부입니다. 선택사항이니 저는 체크하지 않고 넘어가겠습니다.


사람인지 판단하는 퍼즐을 풀면 계정생성 버튼이 활성화됩니다. create해줍니다.


입력한 이메일로 인증번호가 전송됩니다. 메일함에 없다면 스팸메일함을 확인해주세요. 이메일로 전송된 번호를 입력해줍니다.


인증이 완료되면 개인 설정 페이지로 넘어갑니다. 설정을 해주셔도 되고 아래 skip을 눌러 넘어갈 수 있습니다. 저는 skip을 눌렀습니다.


대쉬보드 페이지로 넘어가면 가입완료입니다!

GitHub 저장소(Repository) 생성


상단에 +버튼을 눌러 New Repository를 눌러주세요.


Repository name에 원하는 저장소 이름을 입력해주세요. 프로젝트와 관련된 이름이나 작업의 이름을 적어주시면 됩니다.

저장소를 다른 이들에게 공개하려면 public, 비공개하려면 private을 설정해주세요. 나중에 변경이 가능합니다.

README file은 해당 저장소나 프로젝트에 대한 설명을 작성할 수 있는 파일입니다. 설정시 자동으로 생성되며 안하더라도 나중에 추가가 가능합니다.

각종 설정 후 페이지 하단에 create repository를 누르면 repository 생성이 완료됩니다.

Git 설치

Git을 설치하기 위해서 아래의 링크에 접속해줍니다.
https://git-scm.com/downloads

운영체제에 맞게 다운로드한 후 안내에 따라 설치해주시면됩니다.

맥북은 터미널에 하단과 같이 입력

brew install git

윈도우는 설치 완료 시 설치된 Git bash를 이용하면 리눅스 환경과 같이 터미널을 이용할 수 있습니다. 이곳에서 git 명령을 수행할 수 있습니다.

GitHub 첫걸음을 위한 사전 지식

1. GitHub 기초 작업 필수 개념: commit, push

커밋(Commit)
커밋은 깃에서 변경 내용을 로컬 저장소에 저장하는 작업을 말합니다.
개발자는 작업을 완료했을 때 해당 변경 내용을 커밋하여 로컬 저장소에 기록합니다.
각각의 커밋은 프로젝트의 특정 시점의 스냅샷으로서, 커밋 메시지를 통해 어떤 변경이 있었는지 명확하게 기록합니다.
커밋을 통해 소스 코드의 이력을 추적하고 필요한 경우 특정 시점으로 되돌릴 수 있습니다.

커밋의 기본 구조

git commit -m "커밋 메시지"

푸쉬(Push)

푸쉬는 로컬 저장소에 있는 변경 내용을 원격 저장소로 업로드하는 작업을 의미합니다.
로컬 저장소의 커밋들은 개발자가 작업을 마무리하고 다른 개발자들과 변경 사항을 공유할 때 원격 저장소로 푸쉬됩니다.
이를 통해 여러 개발자가 동시에 작업하고 있는 경우 다른 개발자들도 최신 변경 내용을 확인하고 자신의 로컬 저장소에 반영할 수 있습니다.

푸쉬의 기본 구조

git push [원격 저장소 이름] [브랜치 이름]

예를 들어, 일반적으로는 다음과 같이 사용됩니다

git push origin main

이 명령은 현재 작업 중인 브랜치의 변경 내용을 "origin"이라는 원격 저장소의 "main" 브랜치로 푸쉬하는 코드입니다.

커밋과 푸쉬를 함께 사용하면 개발자는 소스 코드의 변경 내용을 효과적으로 관리하고 협업할 수 있습니다.

2. 로컬 저장소와 원격 저장소

로컬 저장소(Local Repository)

  • 로컬 저장소는 개발자의 개인 컴퓨터에 위치한 저장소를 의미합니다.
  • 개발자는 자신의 로컬 컴퓨터에서 깃을 사용하여 프로젝트를 관리하고 변경 사항을 추적할 수 있습니다.
  • 로컬 저장소에서는 브랜치를 생성하고 변경 내용을 커밋할 수 있습니다.

원격 저장소(Remote Repository)

  • 원격 저장소는 인터넷이나 네트워크 상의 다른 위치에 위치한 중앙 서버 저장소를 의미합니다.
  • 깃허브(GitHub)는 가장 널리 사용되는 원격 저장소 중 하나입니다.
  • 원격 저장소는 로컬 저장소와 변경 사항을 주고받을 수 있습니다. 개발자는 로컬에서 작업한 내용을 원격 저장소에 푸시(push)하여 공유하거나, 원격 저장소에서 변경된 내용을 풀(pull)하여 자신의 로컬에 반영할 수 있습니다.
  • 여러 개발자가 동시에 프로젝트에 참여하고 협업할 때 원격 저장소는 중앙화된 저장소 역할을 합니다.
  • 로컬 저장소와 원격 저장소 간의 상호 작용을 통해 개발자들은 프로젝트의 변경 내용을 효과적으로 추적하고 여러 사람이 동시에 작업할 수 있게 됩니다.

로컬 저장소는 작업을 하고 변경 사항을 기록하는 개발자의 개인 작업 공간이며, 원격 저장소는 여러 개발자들이 공유하고 협업하는 중앙 저장소입니다.

3. 브랜치

브랜치(Branch)

브랜치는 깃에서 코드를 분리하고 독립적으로 관리할 수 있도록 하는 개념입니다.
브랜치를 사용하면 여러 개발자가 동시에 작업하거나 특정 기능을 개발할 때 각자 독립적인 작업 환경에서 작업할 수 있습니다.
각 브랜치는 프로젝트의 특정 상태를 나타내며, 변경 사항을 다루기 위해 사용됩니다.

주요 브랜치 관련 용어와 개념

메인 브랜치(Main/Branch):
프로젝트의 기본이 되는 브랜치로서, 주로 "main" 또는 "master"라는 이름으로 사용됩니다.
일반적으로 안정된 코드를 유지하고 배포하기 위해 사용됩니다.

피처 브랜치(Feature Branch):
특정 기능을 개발하기 위해 메인 브랜치에서 파생된 브랜치입니다.
개발자는 피처 브랜치에서 해당 기능을 구현하고 테스트한 후, 완료되면 메인 브랜치로 병합(merge)할 수 있습니다.

 

이어서...

브랜치 사용과 깃허브 작업플로우 및 명령어는 다음 글에서 이어집니다.
왕초보 깃허브 시작하기 : 2. 브랜치 사용과 GitHub 용어 및 명령어 정리

 

배운점

- 생각보다 너무 오래걸렸다....

생각했던 논리는 맞는데 코드 작성 실수 때문에 계속 오류가 났었다.

더 깔끔하게 풀 수 있을 것 같은데 다시 한 번 풀어보면 좋을 것 같다

 

import java.util.*;
class Solution {
    public int[] solution(String[] park, String[] routes) {
        int[] startIdx = {0,0};
        int h = park.length;
        int w = park[0].length();
        
        for(int i = 0; i < park.length; i++){
            if(park[i].indexOf('S') != -1){
                startIdx[1] = park[i].indexOf('S');
                startIdx[0] = i;
                break;
            }
        }
        
        for(int i = 0; i < routes.length; i++){
            boolean flag = true;
            
            switch(routes[i].charAt(0)){
                case 'E' :
                    if(startIdx[1] + (routes[i].charAt(2) - '0') >= w)
                        break;
                    for(int j = 1; j <= (routes[i].charAt(2) - '0'); j++)
                    {
                        if(park[startIdx[0]].charAt(startIdx[1]+j) == 'X'){
                            flag = false;
                            break;
                        }
                    }
                    if(flag)
                        startIdx[1] += (routes[i].charAt(2) - '0');
                    break;
                case 'W' :
                    if(startIdx[1] - (routes[i].charAt(2) - '0') < 0)
                        break;
                    for(int j = 1; j <= (routes[i].charAt(2) - '0'); j++)
                    {
                        if(park[startIdx[0]].charAt(startIdx[1]-j) == 'X'){
                            flag = false;
                            break;
                        }
                    }
                    if(flag)
                        startIdx[1] -= (routes[i].charAt(2) - '0');
                    break;
                case 'S' :
                    if(startIdx[0] + (routes[i].charAt(2) - '0') >= h)
                        break;
                    for(int j = 1; j <= (routes[i].charAt(2) - '0'); j++)
                    {
                        if(park[startIdx[0]+j].charAt(startIdx[1]) == 'X'){
                            flag = false;
                            break;
                        }
                    }
                    if(flag)
                        startIdx[0] += (routes[i].charAt(2) - '0');
                    break;
                case 'N':
                    if(startIdx[0] - (routes[i].charAt(2) - '0') < 0)
                        break;
                    for(int j = 1; j <= (routes[i].charAt(2) - '0'); j++)
                    {
                        if(park[startIdx[0]-j].charAt(startIdx[1]) == 'X'){
                            flag = false;
                            break;
                        }
                    }
                    if(flag)
                        startIdx[0] -= (routes[i].charAt(2) - '0');
                    break;
                    
            }
        }
        return startIdx;
    }
}

배운점

- 달리기 경주를 풀고 나니 Map에 조금 익숙해져서 Map을 사용하였다. 조건을 제대로 안읽어서 그리움 점수가 없는 경우를 고려하지 않아 null 에러가 발생했지만 에러 처리를 해주니 코드가 정상 작동 하였다.

import java.util.*;
class Solution {
    public int[] solution(String[] name, int[] yearning, String[][] photo) {
        int[] answer = new int[photo.length];
        Map<String, Integer> score = new HashMap<String, Integer>();
        
        for(int i = 0; i < name.length; i++){
            score.put(name[i], yearning[i]);
        }
        
        for(int i = 0; i < photo.length; i++)
        {
            for(int j = 0; j < photo[i].length; j++){
                if(score.get(photo[i][j]) != null)
                    answer[i] += score.get(photo[i][j]);
            }
        }
        return answer;
    }
}

배운점

처음에는 players를 List로 바꿔서 indexOf("")로 idx를 찾아 값을 바꿔서 테스트를 했다.

근데 테스트케이스 몇개에서 시간 초과가 발생해서 코드를 엎어야 했다.

찾아보니 indexOf 사용 시 시간복잡도가 O(n)이어서 테스트케이스가 많으면 1번씩 다 탐색을 하여 시간초과가 발생하였다.

따라서 players를 Map에 복사하여 callings를 돌면서 Map에서 인덱스를 찾아 players를 변경해주는 방식으로 코드를 구성하였다.

Map을 이용하니 시간초과가 발생하지 않았다.

 

내가 쓴 코드

import java.util.*;
class Solution {
    public String[] solution(String[] players, String[] callings) { 
        String[] answer = new String[players.length];
        Map<String,Integer> map = new HashMap();
        
        for(int i = 0; i < players.length; i++){
            map.put(players[i], i);
        }
        for(String call : callings)
        {
            int idx = map.get(call);
            
            String temp  = players[idx-1];
            
            map.put(call, idx-1);
            map.put(temp, idx);
            
            players[idx-1] = call;
            players[idx] = temp;
        
        }
        
        return players;
    }
}

 

class Solution {
    public String solution(String new_id) {
        String answer = "";
        answer = new_id;
        
        answer = upperToLower(answer);
        answer = change_correct_char(answer);
        answer = change_period(answer);
        answer = chanegFirstOrLastPeriod(answer);
        answer = isEmptyAdda(answer);
        answer = lengthCorrect(answer);
        answer = minChar(answer);
        return answer;
    }
    public String upperToLower(String new_id){
        return new_id.toLowerCase();
    }
    public String change_correct_char(String new_id){
        String answer = "";
        for(int i = 0 ; i < new_id.length(); i++){
            char ch = new_id.charAt(i);
            if(Character.isDigit(ch) || Character.isAlphabetic(ch) || ch == '-'|| ch == '_' || ch == '.')
                answer += ch;
        }
        return answer;
    }
    public String change_period(String new_id){
        while(new_id.indexOf("..") != -1)
        {
            new_id = new_id.replace("..", ".");
        }
        return new_id;
    }
    public String chanegFirstOrLastPeriod(String new_id){
        if(!new_id.isEmpty() && new_id.charAt(0) == '.')
            new_id = new_id.substring(1);
        if(!new_id.isEmpty() && new_id.charAt(new_id.length()-1)=='.')
            new_id = new_id.substring(0, new_id.length()-1);
        return new_id;
    }
    public String isEmptyAdda(String new_id){
        if(new_id.isEmpty())
            new_id = "a";
        return new_id;
    }
    public String lengthCorrect(String new_id){
        if(new_id.length() >= 16){
            new_id = new_id.substring(0,15);
            if(new_id.charAt(new_id.length()-1) == '.')
                new_id = new_id.substring(0,new_id.length()-1);
        }
        return new_id;
    }
    public String minChar(String new_id){
            while(new_id.length() < 3){
                new_id += new_id.charAt(new_id.length()-1);
            }
        return new_id;
    }
}

import java.util.*;

class Solution {
    public int solution(int[][] board, int[] moves) {
        int answer = 0;
        List<Integer> basket = new ArrayList<>();
        int size = board.length;
        for(int i = 0; i < moves.length; i++){
            for(int j = 0; j < size; j++){
                if(board[j][moves[i]-1] == 0)
                    continue;
                else{
                    basket.add(board[j][moves[i]-1]);
                    board[j][moves[i]-1] = 0;
                    break;
                }
            }
            if(basket.size() >= 2) {
                for (int k = 0; k < basket.size() - 1; k++)
                    if (basket.get(k) == basket.get(k + 1)) {
                        answer += 2;
                        basket.remove(k + 1);
                        basket.remove(k);
                    }
            }
        }
        return answer;
    }
}

+ Recent posts