code

Git에서 파일 이동/이름 변경 및 이력 유지가 가능합니까?

starcafe 2023. 4. 18. 23:09
반응형

Git에서 파일 이동/이름 변경 및 이력 유지가 가능합니까?

Git에서 프로젝트 서브트리의 이름을 변경/이동하고 싶다.

/project/xyz

로.

/components/xyz

git mv project components후, 「」의 커밋 이력을 나타냅니다xyz project?력을유 유지 ?는 는? ?? ???

하는 것이 이름 을 검출하기 에 Git을 합니다.git mv ★★★★★★★★★★★★★★★★★」mv상관없어.

log명령어는 이름 변경 작업 전에 이력을 계속하는 인수를 사용합니다.즉, 휴리스틱스를 사용하여 유사한 내용을 검색합니다.

전체 이력을 검색하려면 다음 명령을 사용합니다.

git log --follow ./path/to/file

아니요.

간단한 대답은 NO입니다.Git에서 파일 이름을 변경하고 이력을 기억하는 것은 불가능합니다.그리고 그것은 고통이다.

에 의하면 문 rumor rumor rumor rumor rumor rumor rumorgit log --follow --find-copies-harder 는 동작합니다만, 파일 내용에 변경이 없고, 로 이동해도 동작하지 않습니다.

의 이름을 는 Git을 수 ).을 사용하다 --follow동작하는 것 같습니다.mv를 실행하고 , 「」를 실행합니다.commitmv그리 멀지 않다.)

Linus는 소프트웨어 프로젝트의 전체 내용을 전체적으로 이해해야 하며 개별 파일을 추적할 필요가 없다고 말합니다.음, 슬프게도, 내 작은 뇌는 그렇게 할 수 없어.

많은 사람들이 Git이 자동으로 움직임을 추적한다는 말을 무심코 반복하는 것은 정말 짜증난다.그들은 내 시간을 낭비했다.Git은 그런 짓 안 해요.의도적으로(!) Git은 움직임을 전혀 추적하지 않습니다.

해결 방법은 파일 이름을 원래 위치로 다시 바꾸는 것입니다.소스 컨트롤에 맞게 소프트웨어를 변경합니다.Git에서는 처음에 제대로 "기팅"하면 되는 것 같습니다.

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★--follow. 복잡한 이름 변경 이력을 가진 파일의 전체 이력이 표시되지 않을 수 있습니다.git log(이것들)

(과거로 돌아가서 옛날 일을 다시 하는 똑똑한 해커들도 있지만, 그것들은 꽤 무섭다.GitHub-Gist: emiller/git-mv-with-history를 참조하십시오.)

요컨대, 이것을 하는 서브버전이 잘못되었다면 Git도 잘못되었다.이것을 하는 것은 어떤 (잘못된) 기능이 아니라 실수이다.

저장소의 전체 기록에서 파일 이름이 변경되더라도 파일 이름을 변경하고 기록을 그대로 유지할 수 있습니다.이것은 아마도 git-log 애호가만을 위한 것으로, 다음과 같은 심각한 영향을 미칩니다.

  • Git을 사용하는 동안 하지 말아야 할 가장 중요한 공유 이력을 다시 쓸 수 있습니다.다른 사용자가 저장소를 복제한 경우 이렇게 하면 리포지토리가 손상됩니다.두통을 피하기 위해 복제를 다시 해야 합니다.이름 변경이 충분히 중요한 경우라면 이 방법은 괜찮지만, 신중하게 검토해야 합니다. 오픈소스 커뮤니티 전체를 화나게 할 수도 있습니다.
  • 저장소 기록에서 이전 이름을 사용하여 파일을 참조한 경우 이전 버전을 위반하는 것입니다.이것을 고치려면, 당신은 후프 점프를 조금 더 해야 할 것이다.그것은 불가능한 것이 아니라 단지 지루하고 그럴 가치가 없을 수도 있다.

아아 、 [ a file using ]를 사용하여 파일을 이동합시다.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

는 브랜치 내의 모든 커밋을 재실행하고 각 반복에 대해 명령어를 틱으로 실행합니다.이렇게 하면 많은 것들이 잘못될 수 있어요.저는 보통 파일이 있는지 확인하고(그렇지 않으면 아직 이동하지 않고) 필요한 단계를 수행하여 원하는 대로 트리를 장식합니다.여기서는 파일에 대한 참조를 변경하기 위해 파일을 차례로 입력할 수 있습니다.실컷 먹어! :)

완료되면 파일이 이동되고 로그는 그대로 유지됩니다.닌자 해적처럼 느껴져요.

또한 mkdir dir는 파일을 새 폴더로 이동하는 경우에만 필요합니다.그러면 파일이 존재하는 것보다 이전에 이 폴더가 생성되지 않습니다.

git log --follow [file]

는 이름 변경을 통해 이력을 보여줍니다.

하고 있습니다.

git mv {old} {new}
git add -u {new}

Git에서 프로젝트 서브트리의 이름을 변경/이동하고 싶다.

/project/xyz

로.

/컴포넌트/xyz

git mv project components후, 「」의 커밋 이력을 나타냅니다xyz프로젝트가 없어집니다.

아니요(8년 후, Git 2.19, 2018년 3분기), Git은 디렉토리 이름을 검출할 것이고, 이것은 더 잘 문서화되어 있습니다.

Eliza Newren()newren의 commit b00bf1c, commit 1634688, commit 0661e49, commit 4d34dff, commit 983f464, commit c840e1a, commit 9929430(2018년 6월 27일) 및 commit dacd4a(2018년 6월 25일)을 참조하십시오.
(2018년 7월 24일, Junio C Hamano에 의해 병합됨--commit 0ce5a69에서)

그 것에 대해서는, 에서 설명합니다.

예:

of 、 [ ]의 x/a,x/b ★★★★★★★★★★★★★★★★★」x/cz/a,z/b ★★★★★★★★★★★★★★★★★」z/c 그럴 x/d그 사이에 추가된 것은, 로의 이동도 희망합니다.z/d가 「Directory)」라고 하는 를 얻음으로써,x는 ''로했습니다.z

하지만 다음과 같은 경우가 많습니다.

의 을 개명하다x -> z을 '하다'로 x/e그 mar 、 Marge 、 transitive rename 。

디렉토리 이름 변경 검출을 단순화하기 위해 Git에 의해 다음과 같은 규칙이 적용됩니다.

디렉토리 이름 변경 검출이 적용되는 경우의 몇 가지 기본 규칙 제한:

  1. 특정 디렉토리가 Marge 양쪽에 아직 존재하는 경우 이름이 변경된 것으로 간주되지 않습니다.
  2. 이름 변경 예정 파일의 서브셋에 파일 또는 디렉토리가 방해되고 있는 경우(또는 서로 방해되고 있는 경우), 이러한 특정 서브패스에 대해 디렉토리 이름을 "끄고" 충돌을 사용자에게 보고합니다.
  3. 이력 상대측이 디렉토리명을 변경한 경로로 변경했을 경우는, 이력 상대측의 특정의 이름을 암묵적인 디렉토리명에 대해서 무시해 주세요(단, 사용자에게 경고합니다.

에서는 많은 테스트를 볼 수 있습니다.또, 다음과 같은 것도 나타내고 있습니다.

  • a) 디렉토리를 2개 이상의 다른 디렉토리로 분할하는 경우 이름이 가장 많은 디렉토리는 "wins"입니다.
  • b) 패스가 머지 양쪽의 이름 변경의 소스인 경우, 패스의 디렉토리 이름 검출을 피합니다.
  • c) 디렉토리의 암묵적인 디렉토리명의 변경은, 이력의 다른 쪽이 이름을 변경하는 경우만 실시해 주세요.

네.

  1. 을 전자 하려면 , 「」를 사용합니다.git log --pretty=email
  2. 이러한 파일을 새 디렉토리로 재구성하고 이름을 변경합니다.
  3. 이러한 파일(이메일)을 Git 커밋으로 다시 변환하여 이력을 유지합니다.

제한

  • 태그 및 분기는 유지되지 않습니다.
  • 경로 파일 이름 변경 시 기록이 잘립니다(디렉토리 이름 변경).

예를 들어 단계별 설명

1.이력을 이메일 형식으로 추출

" " 의 을 추출합니다.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 generation 명령어 루프를 반전할 것을 제안합니다: 파일당 한 번 git log를 실행하는 대신 명령줄에서 파일 목록과 함께 정확히 한 번 실행하고 단일 통합 로그를 생성합니다.이 방법으로 여러 파일을 수정하는 커밋은 결과에서 단일 커밋으로 유지되며 모든 새로운 커밋은 원래 상대적인 순서를 유지합니다.또한 (현재 통합된) 로그에 파일 이름을 다시 쓸 때 다음 두 번째 단계에서 변경해야 합니다.


2. 파일 트리 재구성 및 파일 이름 업데이트

이 다른 repo(같은 repo일 수도 있음)에서 이들 3개의 파일을 이동한다고 가정합니다.

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"' {} ';'

3. 새로운 이력의 적용

다른 답변은 다음과 같습니다.

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-.'{.* => .*}'.

find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow --find-copies-harder --reverse {} ';' | cut -f3- | grep '{.* => .*}'

나는 "이력을 잃지 않고 폴더 이름을 바꾼다"라는 문제에 직면했다.수정하려면 다음을 수행합니다.

$ git mv oldfolder temp && git mv temp newfolder
$ git commit
$ git push

이 다단계 프로세스에 따라 코드를 부모 디렉토리로 이동하고 이력을 유지했습니다.

순서 0: 보관을 위해 'master'에서 분기 'history'를 만듭니다.

1단계: git-filter-repo 도구를 사용하여 이력을 다시 씁니다.아래 명령은 'Folder with Content Of' 폴더를 이동했습니다.'관심'을 한 단계 올려 관련 커밋 이력을 수정했습니다.

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 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: ''가 붙어 있는glob:를 참조해 주세요.

커밋 메시지에서 디렉토리 이름을 변경하려면:

git-filter-repo --message-callback 'return message.replace(b"OLD_NAME", b"NEW_NAME")'

Python의 정규 표현식도 지원되지만 Python으로 수동으로 작성해야 합니다.

가 리모트 " " " 를 .--force작업을 하기 전에 것이 좋습니다.

를 보존하지 GUI 됩니다), 「」(Git GUI」(Git GUI) 「」(Git GUI」), 「Git GUI」(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의 핵심인 Git 배관은 이름을 추적하지 않지만, Git 로그 "포셀레인"과 함께 표시되는 이력은 원한다면 그것들을 검출할 수 있다.

의 ★★★★★에 대해서git log 옵션을 -M 을 사용하세요.

git log -p -M

현재 버전의 Git을 사용합니다.

명령어는 다른 할 수 있습니다.git diff뿐만 아니라.

어느 정도 엄격한 비교를 할 수 있는 옵션이 있습니다.파일을 크게 변경하지 않고 파일 이름을 바꾸면 Git 로그와 친구들이 이름을 쉽게 찾을 수 있습니다.이러한 이유로 일부 사용자는 한 커밋에서 파일 이름을 변경하고 다른 커밋에서 파일을 변경합니다.

Git에 파일명이 변경된 장소, 사용 여부, 사용 시기 등을 물어보면 CPU 사용 비용이 발생합니다.

특정 저장소에서 이름 변경 탐지와 함께 항상 기록을 보고하려면 다음을 사용할 수 있습니다.

git config diff.rename 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

은 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

시험삼아 기능 브랜치 내의 1개의 파일에 작은 변경을 가하여 커밋한 후 마스터 브랜치에서는 파일 이름을 변경하고 커밋한 다음 파일의 다른 부분을 작게 변경하여 커밋했습니다.feature branch로 이동하여 master에서 merge 했을 때 merge는 파일 이름을 변경하고 변경 사항을 merge했습니다.Marge의 출력은 다음과 같습니다.

 $ 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 버전에 대해 정확했을 수 있습니다.

먼저 이름 변경만으로 스탠드아론 커밋을 만듭니다.

그 후 파일 내용에 대한 최종적인 변경은 별도의 커밋에 포함됩니다.

제 경우, 'resources' 디렉토리에서 'src/main/resources'로 두 개의 파일을 이동했습니다.다음 코드와 같이 '삭제됨'으로 표시됩니다.

그러나 재배치된 파일을 스테이징 영역에 추가한 후 삭제된 파일을 추가한 후 시스템에서 "이름 변경"으로 인식했습니다.

두 파일의 이력을 확인해보니 온전하고 영구적인 링크는 여전히 작동하고 있었습니다.모든 게 우리가 바라던 대로 되는군

myaddress (master *)$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        deleted:    resources/myaddress-schemas.sql
        deleted:    resources/select-sangdo-ro-60.sql

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        .gitignore
        pom.xml
        src/

myaddress (master *)$ git add src/main/resources/*.sql
myaddress (master *+)$ git add `git ls-files --deleted`

myaddress (master +)$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        renamed:    resources/myaddress-schemas.sql -> src/main/resources/myaddress-schemas.sql
        renamed:    resources/select-sangdo-ro-60.sql -> src/main/resources/select-sangdo-ro-60.sql

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        .gitignore
        pom.xml
        src/main/java/
        src/main/webapp/

myaddress (master +)$
myaddress (master +)$ git commit -m "two resource files moved to src/main"
[master 0832839] two resource files moved to src/main
 2 files changed, 0 insertions(+), 0 deletions(-)
 rename {resources => src/main/resources}/myaddress-schemas.sql (100%)
 rename {resources => src/main/resources}/select-sangdo-ro-60.sql (100%)
myaddress (master)$

언급URL : https://stackoverflow.com/questions/2314652/is-it-possible-to-move-rename-files-in-git-and-maintain-their-history

반응형