Git에서 프로젝트 하위 트리의 이름을 바꾸거나 이동하고 싶습니다.
/project/xyz
에
/components/xyz
plain을 사용 git mv project components
하면 xyz project
가져 오기에 대한 모든 커밋 기록 이 사라집니다. 역사가 유지되도록 이것을 옮기는 방법이 있습니까?
Git에서 프로젝트 하위 트리의 이름을 바꾸거나 이동하고 싶습니다.
/project/xyz
에
/components/xyz
plain을 사용 git mv project components
하면 xyz project
가져 오기에 대한 모든 커밋 기록 이 사라집니다. 역사가 유지되도록 이것을 옮기는 방법이 있습니까?
답변:
힘내 커밋으로 사용 그래서 여부, 이름을 바꾸지 않고 작업을 지속를 감지 git mv
하거나 mv
중요하지 않습니다.
이 log
명령은 --follow
이름 바꾸기 작업 전에 기록을 계속 하는 인수를 취합니다 . 즉, 추론을 사용하여 유사한 내용을 검색합니다.
http://git-scm.com/docs/git-log
전체 기록을 조회하려면 다음 명령을 사용하십시오.
git log --follow ./path/to/file
git config alias.logf "log --follow"
작성하는 것 git logf ./path/to/file
입니다.
파일의 이름을 바꾸고 히스토리를 그대로 유지하는 것이 가능 하지만 저장소의 전체 히스토리에서 파일 이름이 변경됩니다. 이것은 아마도 강박적인 git-log-lovers에게만 해당되며 다음을 포함하여 몇 가지 심각한 의미를 갖습니다.
지금, 당신은 여전히 나와 함께 있기 때문에, 당신은 아마도 완전히 고립 된 파일의 이름을 바꾸는 솔로 개발자 일 것입니다. filter-tree
!를 사용하여 파일을 이동합시다 .
파일 old
을 폴더로 이동 dir
하고 이름을 지정 한다고 가정하십시오.new
이것은로 할 수 git mv old dir/new && git add -u dir/new
있지만 역사를 깨뜨립니다.
대신 :
git filter-branch --tree-filter 'if [ -f old ]; then mkdir dir && mv old dir/new; fi' HEAD
것이다 다시 각 반복에 대한 틱으로 명령을 실행하는 모든이 지점에서 커밋. 이렇게하면 많은 것들이 잘못 될 수 있습니다. 나는 보통 파일이 존재하는지 (그렇지 않으면 아직 움직이지 않는지) 테스트 한 다음 필요한 단계를 수행하여 나무를 내 마음에 들게합니다. 여기에서 파일에 대한 참조를 변경하기 위해 파일을 sed 할 수 있습니다. 몸을 녹여 라! :)
완료되면 파일이 이동되고 로그가 그대로 유지됩니다. 당신은 닌자 해적처럼 느껴집니다.
또한; mkdir 디렉토리는 파일을 새 폴더로 이동하는 경우에만 필요합니다. 경우 역사의 앞부분이 폴더의 생성을 피할 파일이 존재보다.
--index-filter
모든 커밋마다 트리를 체크 아웃하고 다시 체크인 할 필요가 없으므로 for 이름 바꾸기를 사용하는 것이 훨씬 빠릅니다. --index-filter
각 커밋 인덱스에서 직접 작동합니다.
짧은 대답은 NO 입니다. Git에서 파일 이름을 바꾸고 기록을 기억할 수는 없습니다. 그리고 그것은 고통입니다.
소문은 효과가 git log --follow
--find-copies-harder
있지만 파일 내용이 전혀 변경되지 않았고로 이동 한 경우에도 작동하지 않습니다 git mv
.
(처음에 나는 이름을 변경하고 힘내 혼란 수도 업데이트 패키지 하나의 작업에서, 이클립스를 사용했다. 그러나 그것은 할 수있는 매우 일반적인 일이다. --follow
A는 경우에만 작업에 보인다 mv
수행 한 후 commit
와는 mv
너무 멀리하지 않습니다.)
Linus는 개별 파일을 추적 할 필요없이 소프트웨어 프로젝트의 전체 내용을 전체적으로 이해해야한다고 말합니다. 슬프게도 내 작은 뇌는 그렇게 할 수 없습니다.
많은 사람들이 Git이 자동으로 움직임을 추적한다는 진술을 무의식적으로 반복했다는 것은 정말 성가신 일 입니다. 그들은 내 시간을 낭비했다. 힘내는 그런 일을하지 않습니다. 의도적으로 (!) Git은 움직임을 전혀 추적하지 않습니다.
내 해결책은 파일 이름을 원래 위치로 다시 바꾸는 것입니다. 소스 제어에 맞게 소프트웨어를 변경하십시오. Git을 사용하면 처음으로 "git"해야 할 것 같습니다.
불행히도, 그것은 Eclipse를 깨뜨립니다 --follow
. git log --follow
복잡한 이름 변경 내역이있는 파일의 전체 기록을 표시하지 않는 경우도 git log
있습니다. (왜 그런지 모르겠습니다.)
(뒤로 돌아가서 오래된 작업을 다시 시작하는 영리한 해킹이 있지만 다소 무섭습니다. GitHub-Gist : emiller / git-mv-with-history 참조 )
git log --follow [file]
이름 변경을 통해 기록을 표시합니다.
git mv
기본적으로 git rm && git add
. 파일 이름이 90 % 일 때 파일 이름을 바꾸는 것으로 간주하는 -M90
/ 와 같은 옵션이 있습니다 --find-renames=90
.
나는한다:
git mv {old} {new}
git add -u {new}
-A
대신 행동을 원 하십니까? 다시 여기를 참조 : git-scm.com/docs/git-add
git add -u
있습니다. Git 문서는 도움이되지 않는 경향이 있으며 마지막으로보고 싶은 곳입니다. 다음 git add -u
은 실제로 작동하는 게시물입니다 : stackoverflow.com/a/2117202 .
Git에서 프로젝트 하위 트리의 이름을 바꾸거나 이동하고 싶습니다.
/project/xyz
에
/ components / xyz
plain을 사용
git mv project components
하면xyz
프로젝트의 모든 커밋 기록 이 손실됩니다.
Git은 디렉토리 rename 을 감지 하므로 더 잘 문서화 되므로 아니오 (8 년 후, Git 2.19, 2018 년 3 분기 2018) .
참조 b00bf1c 커밋 , 1,634,688 커밋 , 0661e49 커밋 , 4d34dff 커밋 , 983f464 커밋 , c840e1a 커밋 , 9,929,430 커밋 (2018 6월 27일), 및 d4e8062 커밋 , 5dacd4a 커밋 에 의해 (2018 6월 25일를) 엘리야 Newren ( newren
) .
( Junio C gitster
Hamano 에 의해 병합 - 커밋 0ce5a69 , 2018 년 7 월 24 일)
이제 설명되어 있습니다 Documentation/technical/directory-rename-detection.txt
:
예:
때 모든
x/a
,x/b
및x/c
이동 한z/a
,z/b
그리고z/c
이 가능성이 있는지,x/d
그 사이에 추가도로 이동할 것이다z/d
전체 디렉토리 '라는 힌트를 취함으로써x
'이동 '을z
'.
그러나 그들은 다음과 같은 다른 많은 경우입니다.
히스토리의 한쪽은 이름이 바뀌고
x -> z
다른 쪽은 일부 파일의 이름이로 바뀌어x/e
병합에서 전이적인 이름 바꾸기를 수행해야합니다.
디렉토리 이름 변경 탐지를 단순화하기 위해 Git은 이러한 규칙을 시행합니다.
디렉토리 이름 바꾸기 감지가 적용되는 경우 몇 가지 기본 규칙이 제한됩니다.
- 주어진 디렉토리가 여전히 병합의 양쪽에 존재하는 경우, 이름이 변경된 것으로 간주하지 않습니다.
- 이름을 바꾼 파일 중 일부에 파일이나 디렉토리가있는 경우 (또는 서로 방해가되는 경우) 해당 하위 경로의 디렉토리 이름을 "끄기"하고 충돌을 사용자에게보고하십시오. .
- 히스토리의 다른 쪽에서 디렉토리의 이름을 히스토리의 이름이 바뀐 경로로 바꾼 경우, 내재 된 디렉토리 이름 바꾸기에 대해 다른 쪽의 히스토리에서 해당 이름 바꾸기를 무시하십시오 (그러나 사용자에게 경고).
에서 많은 테스트를 볼 수 t/t6043-merge-rename-directories.sh
있으며 다음을 지적합니다.
- a) 이름 변경으로 디렉토리를 둘 이상의 다른 디렉토리로 분할하면 이름이 가장 많은 디렉토리가 "wins"입니다.
- b) 경로가 병합의 양쪽에있는 이름 변경의 소스 인 경우 경로에 대한 디렉토리 이름 감지를 피하십시오.
- c) 히스토리의 다른 쪽이 이름 바꾸기를 수행하는 것이면 내재적 디렉토리 이름 변경 만 디렉토리에 적용하십시오.
git am
git log --pretty=email -p --reverse --full-index --binary
cat extracted-history | git am --committer-date-is-author-date
예 : 추출물의 역사 file3
, file4
그리고file5
my_repo
├── dirA
│ ├── file1
│ └── file2
├── dirB ^
│ ├── subdir | To be moved
│ │ ├── file3 | with history
│ │ └── file4 |
│ └── file5 v
└── dirC
├── file6
└── file7
대상 설정 / 청소
export historydir=/tmp/mail/dir # Absolute path
rm -rf "$historydir" # Caution when cleaning the folder
이메일 형식으로 각 파일의 히스토리 추출
cd my_repo/dirB
find -name .git -prune -o -type d -o -exec bash -c 'mkdir -p "$historydir/${0%/*}" && git log --pretty=email -p --stat --reverse --full-index --binary -- "$0" > "$historydir/$0"' {} ';'
불행하게도 옵션 --follow
또는 --find-copies-harder
함께 사용할 수 없습니다 --reverse
. 파일 이름이 변경 될 때 (또는 상위 디렉토리 이름이 변경 될 때) 히스토리가 잘리는 이유입니다.
이메일 형식의 임시 기록 :
/tmp/mail/dir
├── subdir
│ ├── file3
│ └── file4
└── file5
Dan Bonachea 는 첫 번째 단계에서 git log 생성 명령의 루프를 뒤집을 것을 제안합니다. 파일 당 한 번 git log를 실행하는 대신 명령 행에서 파일 목록으로 정확히 한 번 실행하고 단일 통합 로그를 생성하십시오. 이 방법으로 여러 파일을 수정하면 결과에서 단일 커밋이 유지되고 모든 새 커밋은 원래의 상대 순서를 유지합니다. (현재 통일 된) 로그에 파일 이름을 다시 쓸 때 아래 두 번째 단계의 변경도 필요합니다.
이 다른 리포지토리에서이 세 파일을 이동한다고 가정합니다 (동일한 리포지토리 일 수 있음).
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB # New tree
│ ├── dirB1 # from subdir
│ │ ├── file33 # from file3
│ │ └── file44 # from file4
│ └── dirB2 # new dir
│ └── file5 # from file5
└── dirH
└── file77
따라서 파일을 재구성하십시오.
cd /tmp/mail/dir
mkdir -p dirB/dirB1
mv subdir/file3 dirB/dirB1/file33
mv subdir/file4 dirB/dirB1/file44
mkdir -p dirB/dirB2
mv file5 dirB/dirB2
귀하의 임시 이력은 다음과 같습니다.
/tmp/mail/dir
└── dirB
├── dirB1
│ ├── file33
│ └── file44
└── dirB2
└── file5
기록 내에서 파일 이름도 변경하십시오.
cd "$historydir"
find * -type f -exec bash -c 'sed "/^diff --git a\|^--- a\|^+++ b/s:\( [ab]\)/[^ ]*:\1/$0:g" -i "$0"' {} ';'
다른 저장소는 다음과 같습니다.
my_other_repo
├── dirF
│ ├── file55
│ └── file56
└── dirH
└── file77
임시 히스토리 파일에서 커밋을 적용하십시오.
cd my_other_repo
find "$historydir" -type f -exec cat {} + | git am --committer-date-is-author-date
--committer-date-is-author-date
원래 커밋 타임 스탬프를 유지합니다 ( Dan Bonachea 의 의견).
다른 레포는 지금 :
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB
│ ├── dirB1
│ │ ├── file33
│ │ └── file44
│ └── dirB2
│ └── file5
└── dirH
└── file77
git status
푸시 될 준비가 된 커밋의 양을 보는 데 사용 하십시오 :-)
이름이 바뀐 파일을 나열하려면 다음을 수행하십시오.
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow {} ';' | grep '=>'
추가 사용자 정의 : git log
옵션 --find-copies-harder
또는을 사용 하여 명령 을 완료 할 수 있습니다 --reverse
. cut -f3-
완전한 패턴 '{. * =>. *}'를 사용 하고 grepping 하여 처음 두 열을 제거 할 수도 있습니다 .
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow --find-copies-harder --reverse {} ';' | cut -f3- | grep '{.* => .*}'
이 다단계 프로세스를 따라 코드를 상위 디렉토리로 이동하고 기록을 유지했습니다.
0 단계 : 안전한 보관을 위해 '마스터'에서 '역사'분기 생성
1 단계 : git-filter-repo 도구를 사용하여 기록을 다시 씁니다. 아래의이 명령은 폴더 'FolderwithContentOfInterest'를 한 레벨 위로 이동하고 관련 커밋 히스토리를 수정했습니다.
git filter-repo --path-rename ParentFolder/FolderwithContentOfInterest/:FolderwithContentOfInterest/ --force
2 단계 :이 시점까지 GitHub 리포지토리의 원격 리포지토리 경로가 손실되었습니다. 원격 참조 추가
git remote add origin git@github.com:MyCompany/MyRepo.git
3 단계 : 저장소에서 정보 가져 오기
git pull
4 단계 : 로컬 손실 지점과 원점 지점 연결
git branch --set-upstream-to=origin/history history
5 단계 : 메시지가 표시되면 폴더 구조에 대한 주소 병합 충돌
6 단계 : 푸시 !!
git push
참고 : 수정 된 기록 및 이동 된 폴더는 이미 커밋 된 것으로 나타납니다. enter code here
끝난. 코드는 기록을 그대로 유지하면서 부모 / 원하는 디렉토리로 이동합니다!
Git의 핵심 인 Git 배관은 이름 변경을 추적하지 않지만, Git 로그 "porcelain"으로 표시 한 기록은 원하는 경우 이름을 감지 할 수 있습니다.
주어진 git log
경우 -M 옵션을 사용하십시오.
자식 로그 -p -M
현재 버전의 Git.
이것은 다른 명령에서도 작동합니다 git diff
.
비교를 다소 엄격하게하는 옵션이 있습니다. 파일을 크게 변경하지 않고 파일 이름을 바꾸면 Git 로그 및 친구가 이름을 쉽게 검색 할 수 있습니다. 이러한 이유로 일부 사람들은 한 커밋에서 파일 이름을 바꾸고 다른 커밋에서 파일을 변경합니다.
Git에 파일 이름이 바뀐 위치를 찾을 때마다 파일 사용 여부와 시간에 따라 CPU 사용 비용이 발생합니다.
특정 리포지토리에서 이름 변경 검색으로 기록을 항상보고 싶다면 다음을 사용할 수 있습니다.
자식 설정 diff.renames 1
한 디렉토리에서 다른 디렉토리로 이동하는 파일 이 감지됩니다. 예를 들면 다음과 같습니다.
commit c3ee8dfb01e357eba1ab18003be1490a46325992
Author: John S. Gruber <JohnSGruber@gmail.com>
Date: Wed Feb 22 22:20:19 2017 -0500
test rename again
diff --git a/yyy/power.py b/zzz/power.py
similarity index 100%
rename from yyy/power.py
rename to zzz/power.py
commit ae181377154eca800832087500c258a20c95d1c3
Author: John S. Gruber <JohnSGruber@gmail.com>
Date: Wed Feb 22 22:19:17 2017 -0500
rename test
diff --git a/power.py b/yyy/power.py
similarity index 100%
rename from power.py
rename to yyy/power.py
이 기능은 with뿐만 아니라 diff를 사용할 때마다 작동합니다 git log
. 예를 들면 다음과 같습니다.
$ git diff HEAD c3ee8df
diff --git a/power.py b/zzz/power.py
similarity index 100%
rename from power.py
rename to zzz/power.py
시험판으로 기능 분기에서 한 파일을 약간 변경하고 커밋 한 다음 마스터 분기에서 파일 이름을 바꾸고 커밋 한 다음 파일의 다른 부분을 조금 변경하여 커밋했습니다. 피처 브랜치에 가서 마스터에서 병합 할 때 병합은 파일 이름을 바꾸고 변경 사항을 병합했습니다. 병합 결과는 다음과 같습니다.
$ git merge -v master
Auto-merging single
Merge made by the 'recursive' strategy.
one => single | 4 ++++
1 file changed, 4 insertions(+)
rename one => single (67%)
그 결과 파일 이름이 바뀌고 텍스트가 변경되는 작업 디렉토리가 생성되었습니다. 따라서 Git이 이름 변경을 명시 적으로 추적하지 않더라도 올바른 작업을 수행 할 수 있습니다.
이것은 오래된 질문에 대한 늦은 답변이므로 다른 답변은 당시 Git 버전에 맞았을 수 있습니다.
먼저 이름 바꾸기만으로 독립 실행 형 커밋을 만듭니다.
그런 다음 최종적으로 파일 내용이 변경되면 별도의 커밋이 적용됩니다.
디렉토리 또는 파일의 이름을 바꾸려면 (복잡한 경우에 대해 잘 모르므로 몇 가지주의 사항이있을 수 있습니다) :
git filter-repo --path-rename OLD_NAME:NEW_NAME
언급 된 파일의 디렉토리 이름을 바꾸려면 (콜백을 사용할 수 있지만 방법을 모르겠습니다) :
git filter-repo --replace-text expressions.txt
expressions.txt
같은 줄로 채워진 파일입니다 literal:OLD_NAME==>NEW_NAME
(Python의 RE를 사용 regex:
하거나 with를 사용할 수 있습니다 glob:
).
커밋 메시지에서 디렉토리 이름을 바꾸려면 :
git-filter-repo --message-callback 'return message.replace(b"OLD_NAME", b"NEW_NAME")'
파이썬의 정규 표현식도 지원되지만 수동으로 파이썬으로 작성해야합니다.
리포지토리가 원격 인 원본 저장소 인 경우 --force
다시 쓰려면 강제 로 추가해야합니다 . (이 작업을 수행하기 전에 리포지토리의 백업을 만들 수 있습니다.)
참조를 유지하지 않으려면 (Git GUI의 분기 히스토리에 표시됨)을 추가해야합니다 --replace-refs delete-no-add
.
다음 과 같이 파일을 이동 하고 스테이지하십시오.
git add .
커밋하기 전에 상태를 확인할 수 있습니다.
git status
표시됩니다 :
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: old-folder/file.txt -> new-folder/file.txt
Git 버전 2.26.1로 테스트했습니다.
GitHub 도움말 페이지 에서 추출되었습니다 .
git mv
: stackoverflow.com/questions/1094269/whats-the-purpose-of-git-mv