Git의 루트 커밋 앞에 커밋을 삽입하시겠습니까?
Git 저장소에서 처음 두 커밋을 지우는 방법에 대해 이전에 물어 본 적이 있습니다.
해결책이 다른 것들처럼 흥미롭고 실제로 마음을 뒤틀지는 않지만, 프로젝트 개발 과정에서 절차를 여러 번 반복해야 하는 경우에는 여전히 약간의 상처가 됩니다.
그래서, 저는 차라리 한 번만 고통을 겪고, 그리고 나서 표준적인 상호작용 리베이스를 영원히 사용할 수 있는 것이 낫습니다.
그렇다면, 제가 하고 싶은 것은 오직 첫 번째가 되는 것만을 목적으로 존재하는 빈 초기 약속을 갖는 것입니다.코드도 없고 아무것도 없습니다.기지를 다시 설치할 수 있도록 공간을 차지하는 것뿐입니다.
그렇다면 제 질문은 기존 저장소를 사용하는 경우 첫 번째 저장소 앞에 새로운 빈 커밋을 삽입하고 다른 모든 사용자를 앞으로 이동시키는 방법은 무엇입니까?
이를 달성하기 위한 두 단계는 다음과 같습니다.
- 새 빈 커밋 만들기
- 이 빈 커밋에서 시작하도록 기록 다시 쓰기
빈 커밋을 지점인 배 커 분 에 니 기 다 시 밋 임 을 ▁commitnewroot편의상
새 빈 커밋 만들기
여러 가지 방법으로 이 작업을 수행할 수 있습니다.
배관만 사용할 경우
가장 깨끗한 방법은 Git의 배관을 사용하여 커밋을 직접 생성하는 것으로, 작업 복사본이나 인덱스 또는 체크아웃된 분기 등을 건드리지 않습니다.
빈 디렉토리에 대한 트리 개체 만들기:
tree=`git hash-object -wt tree --stdin < /dev/null`다음과 같이 커밋을 둘러쌉니다.
commit=`git commit-tree -m 'root commit' $tree`해당 항목에 대한 참조를 만듭니다.
git branch newroot $commit
당신이 당신의 껍질을 충분히 알고 있다면 물론 전체 절차를 한 줄로 다시 정렬할 수 있습니다.
배관 미포함
일반적인 도자기 명령을 사용하면 다음을 확인하지 않고 빈 커밋을 만들 수 없습니다.newroot인덱스 및 작업 복사본을 이유 없이 반복적으로 분기 및 업데이트합니다.하지만 일부는 이것을 더 쉽게 이해할 수 있습니다.
git checkout --orphan newroot
git rm -rf .
git clean -fd
git commit --allow-empty -m 'root commit'
매우 오래된 버전의 Git에서 그것이 부족하다는 것에 주목하세요.--orphan로바다로 합니다.checkout은 첫 번째 같이 해야 합니다.
git symbolic-ref HEAD refs/heads/newroot
이 빈 커밋에서 시작하도록 기록 다시 쓰기
여기에는 두 가지 옵션, 즉 기본 재배치 또는 기록 새로 고침이 있습니다.
리베이스
git rebase --onto newroot --root master
이것은 간단하다는 장점이 있습니다.그러나 분기에서 마지막으로 커밋할 때마다 커밋 이름과 날짜도 업데이트합니다.
또한 일부 에지 사례 기록에서는 아무것도 포함하지 않는 커밋을 기반으로 하지만 병합 충돌로 인해 실패할 수도 있습니다.
기록 다시 쓰기
더 깨끗한 방법은 지점을 다시 작성하는 것입니다.과 git rebase어떤 지점에서 시작하는지 확인해야 합니다.
git replace <currentroot> --graft newroot
git filter-branch master
다시 쓰는 것은 두 번째 단계에서 발생합니다. 설명이 필요한 첫 번째 단계입니다.무엇을git replace대체할 개체에 대한 참조를 볼 때마다 Git가 대신 해당 개체의 대체를 살펴봐야 한다고 Git에게 말합니다.
과 --graft스위치, 당신은 그것을 평소와 약간 다른 것으로 말하고 있습니다.아직 하시는데, 할 개체가 경우에는 할 개체가 있습니다.<currentroot>교체의 부모 커밋이 나열된 커밋이어야 한다는 것을 제외하고 정확한 사본을 가진 커밋 개체(즉,newroot범하다).그리고나서git replace사용자를 위해 이 커밋을 만든 다음 해당 커밋을 원래 커밋의 대체 항목으로 선언합니다.
이제 당신이 한다면,git log당신은 이미 당신이 원하는 대로 보이는 것을 보게 될 것입니다: 지점은 다음에서 시작합니다.newroot.
그나참고는 다음과 .git replace 기록을 실제로 수정하지 않으며 저장소 밖으로 전파되지도 않습니다.이것은 단지 한 개체에서 다른 개체로 저장소에 로컬 리디렉션을 추가할 뿐입니다.즉, 아무도 이 교체의 효과를 보지 못한다는 것입니다. 오직 당신뿐입니다.
그것이 그 이유입니다.filter-branch단계가 필요합니다.와 함께git replace커밋에 .git filter-branch그런 다음 다음 모든 커밋에 대해서도 이 프로세스를 반복합니다.그것이 바로 여러분이 그것을 공유할 수 있도록 역사가 실제로 다시 쓰여지는 곳입니다.
아리스토텔레스 파갈치스와 우웨 클라인-쾨니히의 답변과 리처드 브로노스키의 논평의 병합.
git symbolic-ref HEAD refs/heads/newroot
git rm --cached -r .
git clean -f -d
# touch .gitignore && git add .gitignore # if necessary
git commit --allow-empty -m 'initial'
git rebase --onto newroot --root master
git branch -d newroot
(모든 것을 한 곳에 놓기 위해)
아리스토텔레스의 대답이 마음에 듭니다.그러나 대규모 저장소(커밋 수 5,000개 이상)의 경우 필터 분기가 몇 가지 이유로 기본 재배치보다 더 잘 작동한다는 것을 발견했습니다. 1) 더 빠릅니다. 2) 병합 충돌이 발생할 때 사람의 개입이 필요하지 않습니다.태그를 다시 작성할 수 있습니다. 태그를 보존합니다.각 커밋의 내용에 대한 질문이 없으므로 필터 분기가 작동합니다. 이는 이 '기본값' 이전과 정확히 동일합니다.
내 단계는 다음과 같습니다.
# first you need a new empty branch; let's call it `newroot`
git symbolic-ref HEAD refs/heads/newroot
git rm --cached -r .
git clean -f -d
# then you apply the same steps
git commit --allow-empty -m 'root commit'
# then use filter-branch to rebase everything on newroot
git filter-branch --parent-filter 'sed "s/^\$/-p <sha of newroot>/"' --tag-name-filter cat master
'--tag-name-filter cat' 옵션은 태그가 새로 만든 커밋을 가리키도록 다시 작성됨을 의미합니다.
"git init" 직후에 빈 커밋을 만드는 것을 잊은 경우 저장소 시작 부분에 빈 커밋을 추가하려면:
git rebase --root --onto $(git commit-tree -m 'Initial commit (empty)' 4b825dc642cb6eb9a060e54bf8d69288fbee4904)
제 생각에 사용하는 것입니다.git replace그리고.git filter-branch사용하는 것보다 더 나은 솔루션입니다.git rebase:
- 더 나은 성과
- 더 쉽고 덜 위험합니다(각 단계에서 결과를 확인하고 수행한 작업을 실행 취소할 수 있습니다...).
- 결과가 보장된 여러 지점에서 잘 작동
그 이면에 있는 아이디어는 다음과 같습니다.
- 과거의 빈 커밋을 새로 만듭니다.
- 새 루트 커밋이 상위로 추가된다는 점을 제외하고는 이전 루트 커밋을 정확히 유사한 커밋으로 바꿉니다.
- 하고 실행합니다.
git filter-branch - 다시 한 번 모든 것이 정상인지 확인하고 더 이상 필요하지 않은 git 파일을 치료합니다.
다음은 두 가지 첫 번째 단계에 대한 스크립트입니다.
#!/bin/bash
root_commit_sha=$(git rev-list --max-parents=0 HEAD)
git checkout --force --orphan new-root
find . -path ./.git -prune -o -exec rm -rf {} \; 2> /dev/null
git add -A
GIT_COMMITTER_DATE="2000-01-01T12:00:00" git commit --date==2000-01-01T12:00:00 --allow-empty -m "empty root commit"
new_root_commit_sha=$(git rev-parse HEAD)
echo "The commit '$new_root_commit_sha' will be added before existing root commit '$root_commit_sha'..."
parent="parent $new_root_commit_sha"
replacement_commit=$(
git cat-file commit $root_commit_sha | sed "s/author/$parent\nauthor/" |
git hash-object -t commit -w --stdin
) || return 3
git replace "$root_commit_sha" "$replacement_commit"
위험 이 할 수 하지 않은 하기 전에 백업을 수행하는 것이 ;) 만 하면 ..git/refs/replace 다시
리포지토리의 상태가 예상대로인지 확인했으면 다음 명령을 실행하여 모든 분기의 기록을 업데이트합니다.
git filter-branch -- --all
것,의 역사를 ( 참조).filter-branch자세한 내용은)을 참조하십시오.당신은 두 가지를 비교하고 모든 것이 괜찮은지 다시 확인할 수 있습니다.원하는 경우 더 이상 필요하지 않은 파일을(를) 삭제합니다.
rm -rf ./.git/refs/original
rm -rf ./.git/refs/replace
은 당신의 음단계돌수다있니습으로 돌아갈 수 있습니다.master분기 및 임시 분기 삭제:
git checkout master
git branch -D new-root
이제 모든 것이 완료되어야 합니다 ;)
저는 아리스토텔레스와 켄트의 답을 성공적으로 사용했습니다.
# first you need a new empty branch; let's call it `newroot`
git checkout --orphan newroot
git rm -rf .
git commit --allow-empty -m 'root commit'
git filter-branch --parent-filter \
'sed "s/^\$/-p <sha of newroot>/"' --tag-name-filter cat -- --all
# clean up
# pre- git 2.28...
git checkout master
# or git 2.28 and later...
git checkout $(git config --get init.defaultBranch)
git branch -D newroot
# make sure your branches are OK first before this...
git for-each-ref --format="%(refname)" refs/original/ | \
xargs -n 1 git update-ref -d
이것은 또한 모든 분기를 다시 작성합니다(단순히).master또는init.defaultBranch태그 외에도 있습니다.
git rebase --root --onto $emptyrootcommit
쉽게 속임수를 써야 합니다.
저는 흥분했고 이 멋진 대본의 'idempotent' 버전을 썼습니다.항상 동일한 빈 커밋을 삽입하고, 두 번 실행해도 커밋 해시가 매번 변경되지 않습니다.git-insert-empty-root에 대한 저의 견해는 다음과 같습니다.
#!/bin/sh -ev
# idempotence achieved!
tmp_branch=__tmp_empty_root
git symbolic-ref HEAD refs/heads/$tmp_branch
git rm --cached -r . || true
git clean -f -d
touch -d '1970-01-01 UTC' .
GIT_COMMITTER_DATE='1970-01-01T00:00:00 +0000' git commit \
--date='1970-01-01T00:00:00 +0000' --allow-empty -m 'initial'
git rebase --committer-date-is-author-date --onto $tmp_branch --root master
git branch -d $tmp_branch
이것이 추가적인 복잡성을 가질 가치가 있습니까? 그렇지 않을 수도 있지만, 저는 이것을 사용할 것입니다.
또한 이를 통해 repo의 복제된 여러 복사본에 대해 이 작업을 수행하고 동일한 결과를 얻을 수 있으므로 호환성이 유지됩니다. 테스트...네, 작동합니다. 하지만 원격을 삭제하고 다시 추가해야 합니다. 예:
git remote rm origin
git remote add --track master user@host:path/to/repo
루트 커밋을 전환하는 방법
먼저 첫 번째로 원하는 커밋을 만듭니다.
둘째, 다음을 사용하여 커밋 순서를 전환합니다.
gitrebase -i --root
편집자는 루트 커밋까지 커밋과 함께 다음과 같이 나타납니다.
1234 오래된 루트 메시지 선택
0294 중간에 커밋을 선택합니다.
루트에 넣고 싶은 5678 커밋을 선택합니다.
그런 다음 첫 번째 줄에 배치하여 원하는 커밋을 먼저 배치할 수 있습니다.예제:
루트에 넣고 싶은 5678 커밋을 선택합니다.
1234 오래된 루트 메시지 선택
0294 중간에 커밋을 선택합니다.
편집기를 종료하면 커밋 순서가 변경됩니다.
PS: 편집기에서 사용하는 편집기를 변경하려면 다음을 실행합니다.
git config --global core.dll name_of_the_dll_program_you_dll_to_use
제가 생각해낸 것은 다음과 같습니다.
# Just setting variables on top for clarity.
# Set this to the path to your original repository.
ORIGINAL_REPO=/path/to/original/repository
# Create a new repository…
mkdir fun
cd fun
git init
# …and add an initial empty commit to it
git commit --allow-empty -m "The first evil."
# Add the original repository as a remote
git remote add previous $ORIGINAL_REPO
git fetch previous
# Get the hash for the first commit in the original repository
FIRST=`git log previous/master --pretty=format:%H --reverse | head -1`
# Cherry-pick it
git cherry-pick $FIRST
# Then rebase the remainder of the original branch on top of the newly
# cherry-picked, previously first commit, which is happily the second
# on this branch, right after the empty one.
git rebase --onto master master previous/master
# rebase --onto leaves your head detached, I don't really know why)
# So now you overwrite your master branch with the newly rebased tree.
# You're now kinda done.
git branch -f master
git checkout master
# But do clean up: remove the remote, you don't need it anymore
git remote rm previous
제 여기내입니다.bash켄트의 답변을 기반으로 한 개선된 스크립트:
- 그것은 단순한 것이 아니라 원래의 지점을 확인합니다.
master완료되면; - 임시 지점을 피하려고 했지만,
git checkout --orphan분리된 헤드 상태가 아닌 분기 상태에서만 작동하므로 새 루트를 커밋할 수 있을 정도로 충분히 오래 체크아웃된 다음 삭제됩니다. - 그것은 새로운 루트 커밋의 해시를 사용합니다.
filter-branch(Kent는 수동 교체를 위해 자리 표시자를 그 안에 남겨두었습니다); - 그자리의
filter-branch는 다시 작성하지 않습니다. - 작성자 및 커밋 메타데이터는 저장소 간에 루트 커밋이 동일하도록 표준화됩니다.
#!/bin/bash
# Save the current branch so we can check it out again later
INITIAL_BRANCH=`git symbolic-ref --short HEAD`
TEMP_BRANCH='newroot'
# Create a new temporary branch at a new root, and remove everything from the tree
git checkout --orphan "$TEMP_BRANCH"
git rm -rf .
# Commit this empty state with generic metadata that will not change - this should result in the same commit hash every time
export GIT_AUTHOR_NAME='nobody'
export GIT_AUTHOR_EMAIL='nobody@example.org'
export GIT_AUTHOR_DATE='2000-01-01T00:00:00+0000'
export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"
export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
git commit --allow-empty -m 'empty root'
NEWROOT=`git rev-parse HEAD`
# Check out the commit we just made and delete the temporary branch
git checkout --detach "$NEWROOT"
git branch -D "$TEMP_BRANCH"
# Rewrite all the local branches to insert the new root commit, delete the
# original/* branches left behind, and check out the rewritten initial branch
git filter-branch --parent-filter "sed \"s/^\$/-p $NEWROOT/\"" --tag-name-filter cat -- --branches
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
git checkout "$INITIAL_BRANCH"
최신 제품과 최고 제품을 결합하는 것입니다.부작용도 없고, 충돌도 없고, 태그도 없어요.
git log --reverse
tree=`git hash-object -wt tree --stdin < /dev/null`
commit=`git commit-tree -m 'Initialize empty repository' $tree`
echo $commit # copy below, interpolation didn't work for me
git filter-branch --parent-filter 'sed "s/^\$/-p <commit>/"' --tag-name-filter cat master
git log --reverse
GitHub에서는 CI 실행 데이터가 손실되고 다른 분기도 수정하지 않으면 PR이 엉망이 될 수 있습니다.
아리스토텔레스 파갈치스와 다른 사람들의 대답을 따르지만 더 간단한 명령어를 사용합니다.
zsh% git checkout --orphan empty
Switched to a new branch 'empty'
zsh% git rm --cached -r .
zsh% git clean -fdx
zsh% git commit --allow-empty -m 'initial empty commit'
[empty (root-commit) 64ea894] initial empty commit
zsh% git checkout master
Switched to branch 'master'
zsh% git rebase empty
First, rewinding head to replay your work on top of it...
zsh% git branch -d empty
Deleted branch empty (was 64ea894).
보고서에 커밋 대기 중인 로컬 수정 사항이 포함되어 있으면 안 됩니다.
모git checkout --orphan새로운 버전의 깃에서 작동할 것 같습니다.
대부분의 시간을 기록합니다.git status유용한 힌트를 줍니다.
새 리포지토리를 시작합니다.
날짜를 원하는 시작 날짜로 다시 설정합니다.
원하는 방식으로 모든 작업을 수행하고 시스템 시간을 조정하여 원하는 방식으로 작업을 수행할 수 있습니다.필요에 따라 기존 리포지토리에서 파일을 꺼내 많은 불필요한 입력을 방지합니다.
오늘에 도달하면 저장소를 교체하면 완료됩니다.
만약 여러분이 단지 미친(확립된) 사람이지만 합리적으로 지능적이라면(아마도, 이런 미친 아이디어를 생각하기 위해서는 어느 정도의 스마트가 있어야 하기 때문에) 그 과정을 스크립팅할 것입니다.
그것은 또한 여러분이 과거가 지금으로부터 일주일 후에 다른 방식으로 일어났으면 좋겠다고 결정했을 때 더 좋게 만들 것입니다.
이 게시물이 오래된 것은 알지만 이 페이지는 구글에서 "커밋을 삽입"하는 첫 번째 게시물입니다.
왜 간단한 것들을 복잡하게 만드나요?
당신은 A-B-C를 가지고 있고 A-B-Z-C를 원합니다.
git rebase -i trunkB 의 어떤 (또는 B 이전모것든의것)모▁(- 선택을 B라인에서 편집하도록 변경
- 내용: 변:
git add .. git commit(git commit --amendB를 편집하고 Z를 생성하지 않음)
[당신은 얼마든지 만들 수 있습니다.git commit더 많은 커밋을 삽입할 수 있습니다.물론 5단계에 문제가 있을 수 있지만 git와의 충돌을 해결하는 것은 당신이 가져야 할 기술입니다.그렇지 않다면, 연습하세요!
git rebase --continue
간단하죠, 그렇죠?
당신이 이해한다면,git rebase가 되지 'root' 커가는것은문제되않지습다니가밋추하을▁adding다않니.
깃을 가지고 재미있게 놀아요!
언급URL : https://stackoverflow.com/questions/645450/insert-a-commit-before-the-root-commit-in-git
'programing' 카테고리의 다른 글
| 키별로 해시 정렬, Ruby에서 해시 반환 (0) | 2023.06.03 |
|---|---|
| 응용 프로그램에 매니페스트 권한을 추가하는 방법은 무엇입니까? (0) | 2023.06.03 |
| C#에서 URL에 대한 쿼리 문자열을 작성하는 방법은 무엇입니까? (0) | 2023.06.03 |
| bodyParser는 express 4에서 사용되지 않습니다. (0) | 2023.06.03 |
| Ruby on Rails를 사용하여 HTTP 요청을 작성하는 방법은 무엇입니까? (0) | 2023.06.03 |