작업 복사본을 수정하지 않고 Git 브랜치를 리베이스 할 수 있습니까?
"마스터"브랜치를 체크 아웃했다고 가정 해 봅시다. 일부 프로덕션 변경 사항을 "master"에 적용했으며 이제 "실험용"분기를 최신 마스터로 리베이스하려고합니다. 그러나 작업 복사본의 파일을 수정하지 않고이 작업을 수행하고 싶습니다. 기본적으로 작업 복사본을 건드리지 않고 .git 디렉토리 내에서 모든 마법이 일어나기를 원합니다.
"내 작업 복사본을 수정하지 마십시오"요구 사항이 아니라면 다음을 수행하면됩니다.
# current branch is master
git checkout experimental
git rebase master
git checkout master
내 진짜 문제는 내가 시작한 것과 똑같은 내용을 확인하여 끝내더라도 작업 복사본의 타임 스탬프가 수정된다는 것입니다. "git checkout experiment"를 실행하자마자 실험 브랜치에 변경 사항이 포함 된 모든 파일은 mtime이 현재 시간으로 설정되며 마지막으로 실험을 다시 기반으로 한 이후 마스터에서 변경된 모든 파일도 마찬가지입니다. mtimes가 변경 되었기 때문에 빌드 도구와 같은 것들은 내가 완료 될 때까지 파일의 내용이 실제로 변경되지 않았음에도 불구하고 다시해야 할 작업이 있다는 아이디어를 얻습니다. (제 경우에는 프로젝트 파일의 타임 스탬프가 변경되면 Visual Studio에서 프로젝트를 언로드하고 다시로드하는 데 많은 시간을 소비해야한다고 생각합니다.) 저는이를 피하고 싶습니다.
작업 복사본에서 아무것도 수정하지 않고 한 번에 위의 모든 작업을 수행 할 수있는 방법이 있습니까 (리베이스 중에 충돌이 없다고 가정)?
(이 경우 있는 충돌을, 내 취향은 오류를 표시하는 것입니다 후 전체 작업 중단, 과거의 어떤 타임 스탬프를 수정하지만 그건 그냥 내 취향이 아니라 어려운 요구 사항이다없이 -. 나는 모든이 가능 모르겠어요.)
물론 mtimes를 캡처하고 git을 실행 한 다음 mtimes를 재설정하는 스크립트를 작성할 수 있습니다. 그러나 리베이스는 파일의 실제 내용이 아닌 델타에 관한 것이기 때문에 작업 복사본을 귀찮게하지 않고 리베이스와 같은 작업을 수행 할 수있는 방법이 이미 Git에있을 것 같습니다.
작업 복사본에서 아무것도 수정하지 않고 위의 모든 작업을 한 단계로 수행 할 수있는 방법이 있습니까?
git은 작업 트리에서 모든 병합 작업 (실제 병합, 체리 선택, 리베이스, 패치 응용 프로그램)을 수행하기 때문에 불행히도 불가능합니다 (작업 복사본의 수정 가능한 복사본을 만들지 않고 -Petr의 답변 참조 ). 예를 들어 지식이 풍부한 Jakub Narębski의 답변 중 하나 에서 이전에 여러 번 언급되었습니다 .
작업 디렉토리 (및 / 또는 색인)를 사용하여 해결해야하는 병합 충돌이있을 수 있으므로 작업 디렉토리 (및 색인)를 건드리지 않고 병합 (또는 리베이스) 할 수있는 방법은 없습니다.
예, 이것은 디자인 결정이지만 꽤 이해할 수있는 결정입니다. 메모리에서 병합을 시도하는 데 필요한 모든 구조를 구축 한 다음 충돌이 발생하자마자 모든 것을 작업 트리 대신 작업 트리에서 간단히 수행 할 수 있습니다. (저는 git 개발자가 아닙니다. 이것을 완전한 진실로 받아들이지 마십시오. 다른 이유가있을 수 있습니다.)
모든 mtime 조작을 수행하는 스크립트를 작성하는 것보다 내 제안은 단순히 저장소를 복제하고 복제본에서 리베이스를 수행 한 다음 원래 저장소로 다시 푸시하는 것입니다.
git clone project project-for-rebase
cd project-for-rebase
git branch experimental origin/experimental
git rebase master experimental
git push origin experimental
물론 실험이 원래 저장소에서 체크 아웃되지 않았다고 가정합니다. 이 경우, 대신 푸시, 당신은 같은 것을 할 거라고 git fetch ../project-for-rebase experimental; git reset --hard FETCH_HEAD
, 또는 더 읽기를 git remote add for-rebase ../project-for-rebase; git fetch for-rebase; git reset --hard for-rebase/experimental
. 그것은 원래의 실험적 브랜치와 리베이스 된 실험 브랜치 사이의 어떤 파일이든 자연스럽게 건드리지 만, 확실히 올바른 동작입니다. (물론 당신이 제시 한 예는 아니지만이 지침이 일반적 이길 바랍니다!)
이후 자식 2.5 , 더 나은 솔루션은 제 사용하는 worktree를 .
git 저장소는 여러 작업 트리를 지원할 수 있으므로 한 번에 두 개 이상의 브랜치를 확인할 수 있습니다.
$ git worktree add ../second-copy experimental
$ cd ../second-copy/
$ git rebase master experimental
그리고 그게 다야. 나중에 rm -rf second-copy
원하면 나중에 더 많은 리베이스를 위해 유지할 수 있습니다.
$ git rebase master experimental
Linux에서이 작업을 수행하기 위해 작은 스크립트를 만들었습니다. Jefromi의 답변과 몇 가지 추가 사항을 기반으로합니다 (주로 개체 데이터베이스가 복사되지 않도록 대체 항목을 설정하고 필요한 분기 만 가져옴). 여러분 중 일부는 유용 할 것입니다 : https://github.com/encukou/bin/blob/master/oot-rebase
당신이 좋아하는 것을별로하지 않는다면 풀 리퀘스트를 환영합니다. :)
저장소의 복제본을 만드는 것과 비슷하게 여러 작업 디렉터리를 사용하여 이와 같은 작업을 수행하는 것이 훨씬 더 깔끔하다는 것을 알았습니다. 또한 클론은 많은 디스크 공간을 사용하므로 거의 사용하지 않습니다.
https://github.com/git/git/blob/master/contrib/workdir/git-new-workdir
다음과 같이 새 workdir을 만듭니다.
git-new-workdir project-dir new-workdir branch
그런 다음 원래 작업 디렉토리의 가져 오기 및 커밋이 여기에 반영된다는 점을 제외하고는 복제 본인 것처럼 처리 할 수 있습니다 (재 체크 아웃없이 작업 분기에는 적용되지 않음). 유일한 예외는 기본적으로 각 작업 디렉토리에 대해 개별적으로 수행되는 하위 모듈이있는 경우입니다. 솔직히 나는 서브 모듈을 피하려고 시도했기 때문에 그것을 들여다 본 적이 없습니다.
그래서 기본적으로 :
cd new-workdir
git checkout experimental
git rebase master
정확히 하나의 명령은 아니지만 매우 간단합니다.
아래의 숨김 접근 방식에 비해이 접근 방식 (예 : 복제 접근 방식)의 장점은 작업 디렉터리에서 현재 실행중인 (또는 일부 프로세스에서 사용중인) 코드가있는 경우 중단되지 않는다는 것입니다.
여기에 언급되지 않은 다른 옵션은 현재 작업 디렉토리에서 수행하는 것이지만 변경 사항을 숨기면 작업 디렉토리 상태를 즉시 복원 할 수 있습니다.
# current branch is master (with changes to working state)
git stash -u
git checkout experimental
git rebase master
git checkout master
git stash pop
stash -u
새 파일이 있으면 사용하십시오. 그렇지 않으면 숨겨지지 않습니다. 다시 말하지만 한 단계가 아니라 매우 깨끗하고 간단합니다.
나는 또한 그것을 좋아하지만 이것은 나에게 희망을 남기지 않습니다.
경우
<branch>
지정, 자식 REBASE는 다른 작업을 수행하기 전에 자동 자식 체크 아웃을 수행합니다. 그렇지 않으면 현재 분기에 남아 있습니다.
http://git-scm.com/docs/git-rebase
다른 사람들이 말했듯이 작업 디렉토리를 건드리지 않고 브랜치를 리베이스하는 것은 불가능합니다 (새 클론 또는 작업 트리 생성과 같은 제안 된 대안조차도이 사실을 변경할 수 없습니다. 이러한 대안은 실제로 현재 작업 디렉토리를 건드리지 않고 새 작업 트리 생성).
업데이트하려는 분기가 현재 작업 트리 (또는 그 부모)를 기반으로하는 특수한 경우에는 불필요하게 파일을 건드리지 않고 다른 분기를 "리베이스"할 수 있습니다.
이 특별한 경우는 메인 "마스터"브랜치 (원격 마스터 브랜치로 정기적으로 업데이트되는)에서 모두 브랜치 된 많은 브랜치에서 작업하는 git 워크 플로가있는 경우에 자주 발생합니다.
설명을 위해 다음 구조의 Git 저장소를 가정합니다.
repo
- commitA
- commitB
- commitC <-- master <-- OtherBranch based on master
- commitD <-- First commit in otherBranch
- commitE <-- Second commit in OtherBranch
- commitD <-- Unrelated commit in current working tree
예제를 위해 "OtherBranch"가 "master"에서 분기되고 현재 작업 트리도 "master"를 기반으로한다고 가정합니다. 워크 플로는 일반적으로 원격 버전으로 로컬 마스터 브랜치를 업데이트하는 것으로 시작됩니다.
# Fetch commits from remote "origin" and update the master branch:
# If your current branch is identical to master
git pull origin master
# If your current branch has extra commits on top of master
git pull --rebase origin master
# If you don't want to touch your current branch
git fetch origin master:master
... and then you fiddle with the current branch and do some time-consuming compilations. Eventually, you decide that you want to work on OtherBranch
. This OtherBranch
should be rebased on master
(preferably with minimal filesystem operations). The following section will show how.
Rebasing other branch (reference example - do NOT do this)
The following solution is the git way to do it:
git checkout OtherBranch
git rebase master # or git rebase origin/master
The disadvantage of that is that the first command changes the dates of the current worktree, even though the files are going to be restored by the second command.
Rebasing other branch with minimal changes
To minimize the number of touched files, you need to check out to the new base branch and then apply all extra commits in OtherBranch
on top of the base branch using git cherry-pick
.
Before doing anything, you need to identify the commits in OtherBranch
.
git log OtherBranch
shows the commits on OtherBranch (mainly useful if you haven't changedOtherBranch
yet)git reflog
shows the changes to branches in your local repository (useful if you have already updated branches and made a mistake).
In the current example, you will discover that the last commit on OtherBranch
is commitE
. You can see a list of commits before that with git log commitE
(or if you want a shorter list, git log --oneline commitE
). If you look through the list, you will see that the base commit is commitC
.
Now you know that the base commit is commitC
and the last commit is commitE
, you can rebase OtherBranch (from its previous "master" to the new "master") as follows:
# Replace the old OtherBranch with "master" and switch to it.
git checkout -B OtherBranch master
# Cherry-pick commits starting from commitC and ending at commitE.
cherry-pick commitC^..commitE
Alternatively (if you want to successfully complete the "rebase" before replacing OtherBranch
):
# Create new branch NewOtherBranch based off "master" and switch to it.
git checkout -b NewOtherBranch master
# Cherry-pick commits starting from commitC and ending at commitE.
cherry-pick commitC^..commitE
# Replace the old branch with the current branch (-M = --move --force)
git branch -M OtherBranch
Why does this work?
Rebasing branches in git requires one to switch the current branch to the branch that you want to update (OtherBranch
).
With the git rebase
workflow, the following happens:
- Switch to
OtherBranch
(potentially branched off a very old base branch). - Rebase (internal step 1): Save commits that are not in the upstream branch.
- Rebase (internal step 2): Reset current branch to the (new) base branch.
- Rebase (internal step 3): Restore commits from step 2.
Step 1 and step 3 touch many files, but ultimately many of the touched files have not actually changed.
My method combines step 1 and 3 into step 3, and as a result the number of touched files is minimal. The only files that are touched are:
- Files that were changed between the base branch and the current commit in the current working tree.
- Files that are changed by the commits in the
OtherBranch
.
So you want a rebase done on for a branch before you checkout that branch? I really can't see the reason for that, since if you don't checkout that branch you can't work on it. Why do you want to rebase a branch that you don't work on? Do the checkout, it will change your mtime and then do the rebase. The rebase will touch files that are changed and of course you need to rebuild them.
However, a simple way to solve this is to use an other worktree for the rebase. Just set the enviroment variable GIT_WORK_TREE to an other worktree. Just don't forget to have your HEAD match your worktree.
Depending on which branch he is at and what's pushed, a push to a non-bare repo can be dangerous. A much better solution is to fetch from the repo with the precious worktree instead. Example:
` orgbranch=$(git rev-parse HEAD)
mkdir /tmp/tmp_wd
cp -r !(.git) /tmp/tmp_wd
export GIT_WORK_TREE=/tmp/tmp_wd
git checkout branch1
git rebase master
git checkout $orgbranch
export GIT_WORK_TREE=
rm -rf /tmp/tmp_wd`
ReferenceURL : https://stackoverflow.com/questions/4913360/can-i-rebase-a-git-branch-without-modifying-my-working-copy
'developer tip' 카테고리의 다른 글
전체 분기가 아닌 git 부분 병합 (0) | 2021.01.08 |
---|---|
JavaScript로 옵션 텍스트 / 값 얻기 (0) | 2021.01.08 |
파이썬 : 함수에 대한 정보를 얻는 방법? (0) | 2021.01.08 |
시그널 핸들러에 인자 제공 / 전달 (0) | 2021.01.08 |
CoffeeScript는 JavaScript 스타일 == 같음 의미를 허용합니까? (0) | 2021.01.08 |