본문 바로가기
C.W.K.
Stream
Lesson 04 of 05 · published

빚기 전에 베이스를 골라

~12 min · version-control, convergence, operations, design

Level 0젖은 흙
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

푸시 거부는 너무 늦게 한 발견이야

기본 순서는 이래. 작업하고, 커밋하고, 밀어 올리고, 원격이 움직였는지 알아내고. 고통 대부분이 그 마지막 단계에 살아. 답을 알게 될 때쯤엔 커밋이 이미 틀린 걸로 드러난 베이스 위에 존재하니까. 되감는 건 수동 작업이고, 쫓기는 상황이고, 다들 기다리는 잠금을 쥔 채고.

대신 먼저 가져와. 뭘 딛고 지을지 정하기 전에 원격이 어디 있는지 물어봐. 그러면 경우 넷이 나와. 각각 명백히 맞는 행동이 있고, 네 번째는 멈추는 거야.

경우 넷

같거나 원격 브랜치가 없음. 로컬 끝 위에 지어. 맞출 게 없어.

뒤처짐. 원격에 네가 없는 커밋이 있어. 낡은 로컬이 아니라 원격 끝 위에 직접 지어. 틀리면 "내 변경이 왜 사라졌지"를 조용히 만들어내는 경우고, 해볼 값어치 있는 추가 검사가 하나 있어. 원격의 새 커밋이 네가 착지시키려는 그 경로를 건드렸으면 멈추고 손으로 맞춰. 같은 자료를 편집하는 기록자 둘은 자동 규칙이 해소할 일이 아냐.

앞섬. 원격에 없는 로컬 커밋이 있어. 로컬 끝 위에 지어서 그걸 지키고, 통째로 밀어 올려.

갈라짐. 양쪽 다 상대한테 없는 커밋이 있어. 거부해. 자동으로 맞는 답이 없어. 병합이 맞을 수도 있고 재적용이 맞을 수도 있고, 어느 쪽인지는 도구가 못 가진 사실에 달렸어. 여기서 멈추는 게 기능이야.

거부도 결과야

멈춰서 "이력이 갈라졌어요, 사람이 봐야 해요"라고 말하는 자동 작업은 제 일을 하고 있는 거야. 영리하게 만들고 싶은 유혹이 오지. 병합 시도, 안 되면 재적용, 병합이 사소하면 강제. 그리고 영리함이 한 칸 늘 때마다 사람이 필요했던 결정이 아무도 안 읽는 코드 경로로 옮겨가. 그게 조용히 틀린 짓을 하기 전까진 아무도 몰라.

적용할 기준. 맞는 행동이 상태로 정해지는 경우는 자동화하고, 의도로 정해지는 경우는 거부해.

이 기준의 좋은 점은 논쟁이 짧아진다는 거야. "이것도 자동화하자"가 나오면 물을 게 하나뿐이거든. 이 상태만 보고 답이 정해져? 정해지면 하고, 안 정해지면 어떤 사실이 더 필요한지 말해봐. 그 사실을 도구가 가질 수 있으면 그걸 먼저 갖게 하고, 못 가지면 거기가 거부할 자리야.

공유 자원이 끼어 있으면 "푸시 시점에 발견"을 "빌드 전에 결정"으로 바꿔. 정보는 어느 쪽이든 있어. 차이는 전적으로, 답이 불편하게 돌아왔을 때 되감아야 할 뭔가를 이미 만들어놨느냐야.

Code

경우 넷, 결정 셋, 거부 하나·python
def choose_base(repo) -> str:
    """Decide what to build on, BEFORE building anything."""
    branch = current_branch(repo)
    run(["git", "fetch", "origin"], cwd=repo)      # ask first

    local = rev(repo, branch)
    remote = rev(repo, f"origin/{branch}", missing_ok=True)

    if remote is None or local == remote:
        return local                                # level

    if is_ancestor(repo, local, remote):            # BEHIND
        touched = run(["git", "diff", "--name-only", local, remote,
                       "--", *land_paths], cwd=repo).stdout.strip()
        if touched:
            raise SystemExit(
                "the remote moved THIS unit's paths while the claim "
                f"was held - reconcile by hand:\n{touched}")
        return remote            # build on the remote tip, not the
                                 # stale local one

    if is_ancestor(repo, remote, local):            # AHEAD
        return local             # keep the unpushed local commits

    raise SystemExit(               # DIVERGED
        "local and remote have both moved. no automated answer is "
        "correct here - merge or rebase depends on intent. "
        "reconcile by hand, then re-run.")


# Four branches, three of them decided by state alone.
# The refusal is the feature. Every increment of cleverness here
# moves a decision that needed a human into a code path nobody
# reads until it quietly does the wrong thing.

External links

Exercise

충돌을 끝에 가서 발견하는 자동 프로세스를 하나 찾아봐. 푸시든, 배포든, 업로드든, 판 확인이 붙은 쓰기든. 그 확인을 맨 앞으로 옮기고 거기서 나올 수 있는 상태를 나열해. 그다음 상태마다 맞는 행동이 상태만으로 따라 나오는지 정해. 안 따라 나오는 상태는 전부 거부고, 그 거부를 쓰는 건 대개 두 줄짜리 변경인데 보상이 커.
Hint
자동화하고 싶어질 상태는 답이 열에 아홉 맞는 그 상태야. 정확히 그게 거부할 대상이지. 나머지 하나가 조용히 틀리게 처리될 거고, 그걸 만나는 실행을 아무도 안 보고 있을 테니까.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.