Git işareti silinmiş ve yeni bir dosyayı dosya taşıma olarak nasıl yapabilirim?


505

Bir dosyayı el ile taşıdım ve sonra değiştirdim. Git'e göre, yeni bir dosya ve kaldırılmış bir dosyadır. Git'i dosya taşıma gibi davranmaya zorlamanın bir yolu var mı?


3
Belirli bir dosya için old_file.txt, o git mv old_file.txt new_file.txteşdeğerdir git rm --cached old_file.txt, mv old_file.txt new_file.txt, git add new_file.txt.
Jarl

3
Jarl: hayır değil. Dosyada da değişiklikler varsa, git mvbunları önbelleğe eklemez, ancak ekler git add. Kullanabilmek için dosyayı geri taşımayı ve git mvsonra git add -pdeğişiklik kümemi gözden geçirmeyi tercih ederim .
dhardy



Git son 8 yılda düzeldi, eğer tek bir dosyaysa, en iyi yanıt stackoverflow.com/a/433142/459 hiçbir şey yapmadı… ama rm almak / güncellemek için stackoverflow.com/a/1541072/459 adresini takip edebilirsiniz mv / değiştir durumu.
dlamblin

Yanıtlar:


435

Değişikliğiniz çok şiddetli değilse Git otomatik olarak taşı / yeniden adlandırmayı algılar. Sadece git addyeni dosya ve git rmeski dosya. git statusdaha sonra yeniden adlandırmayı algılayıp algılamadığını gösterir.

ek olarak, dizinler etrafında hareket etmek için şunları yapmanız gerekebilir:

  1. cd'yi bu dizin yapısının en üstüne yerleştirin.
  2. Çalıştırmak git add -A .
  3. git status"Yeni dosyanın" artık "yeniden adlandırılmış" bir dosya olduğunu doğrulamak için çalıştırın

Git durumu hala "yeni dosya" gösteriyorsa ve "yeniden adlandırılmamış" ise, Hank Gay’in tavsiyelerini izlemeniz ve taşıma ve iki ayrı işlemde değişiklik yapmanız gerekir .


75
Açıklama: 'çok şiddetli değil', yeni dosyanın ve eski dosyanın, git tarafından kullanılan bazı benzerlik dizinlerine göre>% 50 'benzer' anlamına gelir.
pjz

151
Beni birkaç dakika kapatan bir şey: yeniden adlandırılan dosyalar ve silinen dosyalar işlemek için hazırlanmazsa, bir silme ve yeni bir dosya olarak görünürler. Onları hazırlama dizinine ekledikten sonra yeniden adlandırma olarak tanıyacaktır.
marczych

19
" Git otomatik olarak taşıma / yeniden adlandırma tespit edecek " den bahsetmeye değer . Bu kullandığınız zaman yapar git status, git logya da git diff, değil yapmanız anda git add, git mvya da git rm. Yeniden adlandırmayı saptamanın daha fazla konuşması, yalnızca aşamalı dosyalar için mantıklıdır. Bu nedenle git mv, dosyada yapılan bir değişiklik dosyayı a git statusolarak kabul edebilir rename, ancak dosyayı git stage(ile aynı git add) kullandığınızda, değişikliğin yeniden adlandırma olarak algılanamayacak kadar büyük olduğu anlaşılır.
Jarl

5
Gerçek anahtar git add, @ jrhorn424'ün önerdiği gibi, muhtemelen sadece bir kerede tüm repo olması gereken yeni ve eski yerleri aynı şekilde eklemek gibi görünüyor .
brianary

4
Bu, özellikle dosya değiştiğinde ve küçükse, yanılmaz değildir. Git'i özellikle hareket hakkında anlatmak için bir yöntem var mı?
Rebs

112

Hareketi ve değişikliği ayrı taahhütlerde yapın.


12
Git'in hamle ve modifikasyonları aynı anda yapabildiğini anlıyorum. Java'da programlama yaparken ve bir IDE kullanırken, bir sınıfı yeniden adlandırmak hem değişiklik hem de harekettir. Git'in, bir taşıma (kaldırma ve oluşturma işleminden) olduğunda otomatik olarak çözebilmesi gerektiğini anlıyorum.
pupeno

1
Perl de bunu gerektirir ve Git'i hiçbir zaman taşıma / yeniden adlandırma tespit etmedim.
15'de jrockway

10
@jrockway, vardı. Küçük dosyalar ile kolayca olur, sanırım "bir hamle için" çok farklı "olurlar.
ANeves

2
Bunu otomatik hale getirmenin bir yolu var mı? Dizinde birçok dosya var, önce hareketi ve sonra değişikliği taahhüt etmek istiyorum, ancak elle yapmak zor
ReDetection

5
Unutulmaması gereken bir nokta, git diffbir taahhütteki yeniden adlandırmayı tanımazsa, iki taahhütte tanımayacağıdır. Bunu yapmak için -Maka kullanmanız gerekir --find-renames. Bu nedenle, sizi bu soruya yönlendiren motivasyon, bir çekme isteğinde (deneyimden bahsederek) yeniden adlandırmayı görmekse, bunu iki taahhüde bölmek sizi bu hedefe doğru ilerletmeyecektir.
mattliu

44

Hepsi algısal bir şey. Git genellikle hamleleri tanımakta oldukça iyidir, çünkü GIT bir içerik izleyicidir

Gerçekten tek bağlı "stat" bunu nasıl görüntüler. Buradaki tek fark -M bayrağı.

git log --stat -M

commit 9c034a76d394352134ee2f4ede8a209ebec96288
Author: Kent Fredric
Date:   Fri Jan 9 22:13:51 2009 +1300


        Category Restructure

     lib/Gentoo/Repository.pm                |   10 +++++-----
     lib/Gentoo/{ => Repository}/Base.pm     |    2 +-
     lib/Gentoo/{ => Repository}/Category.pm |   12 ++++++------
     lib/Gentoo/{ => Repository}/Package.pm  |   10 +++++-----
     lib/Gentoo/{ => Repository}/Types.pm    |   10 +++++-----
     5 files changed, 22 insertions(+), 22 deletions(-)

git log --stat

commit 9c034a76d394352134ee2f4ede8a209ebec96288
Author: Kent Fredric
Date:   Fri Jan 9 22:13:51 2009 +1300

    Category Restructure

 lib/Gentoo/Base.pm                |   36 ------------------------
 lib/Gentoo/Category.pm            |   51 ----------------------------------
 lib/Gentoo/Package.pm             |   41 ---------------------------
 lib/Gentoo/Repository.pm          |   10 +++---
 lib/Gentoo/Repository/Base.pm     |   36 ++++++++++++++++++++++++
 lib/Gentoo/Repository/Category.pm |   51 ++++++++++++++++++++++++++++++++++
 lib/Gentoo/Repository/Package.pm  |   41 +++++++++++++++++++++++++++
 lib/Gentoo/Repository/Types.pm    |   55 +++++++++++++++++++++++++++++++++++++
 lib/Gentoo/Types.pm               |   55 -------------------------------------
 9 files changed, 188 insertions(+), 188 deletions(-)

git yardım günlüğü

   -M
       Detect renames.

   -C
       Detect copies as well as renames. See also --find-copies-harder.

76
Bu biraz bilgiçlikçik geliyorsa üzgünüm, ama "Git genellikle hamleleri tanımakta oldukça iyidir, çünkü GIT bir içerik izleyicisidir" benim için sıralı değil gibi görünüyor. Bu bir içerik izleyici, evet ve belki de hareketleri algılamada iyi olur, ancak bir ifade diğerinden gerçekten takip etmez. Bir içerik izleyici olduğu için hareket algılama mutlaka iyi değildir. Aslında, bir içerik izleyicinin hiç hareket algılaması olmayabilir.
Laurence Gonsalves

1
Bir dizini yeniden adlandırdığınızda @WarrenSeine, bu dizindeki dosyaların SHA1'leri değişmez. Tüm sahip olduğunuz aynı SHA1'lere sahip yeni bir AĞAÇ nesnesi. Bir dizinin yeniden adlandırılmasının önemli veri değişikliklerine neden olmasının bir nedeni yoktur.
Kent Fredric

1
@KentFredric "git log" ile -M kullanarak dosyanın yeniden adlandırılıp adlandırılmadığını kontrol edebildiğimizi gösterdim ve denedim ve iyi çalışıyor, ancak kodumu incelenmek üzere gönderdiğimde ve gerrit ( gerritcodereview.com ) dosyasını gördüğümde orada dosyanın yeni eklendiğini ve önceki dosyanın silindiğini gösterir. Yani ben git taahhüt ve gerrit düzgün gösterir kullanarak "git commit" bir seçenek var.
Patrick

1
Hayýr. Bunu hiç göstermedim. Git'in içeriğin aynı olması nedeniyle bir add + delete adının bir yeniden adlandırma olduğunu iddia edebildiğini gösterdim . Hangisinin olduğunu bilmenin bir yolu yok ve umursamıyor. Tüm git hatırlar "eklenir" ve "silinir". "Yeniden adlandırıldı" hiçbir zaman kaydedilmez.
Kent Fredric

1
Örneğin, son zamanlarda bir repo üzerinde çok sayıda "Kopyala + Düzenle" yaptım. Görüntülemenin çoğu yolu yalnızca "yeni dosya" git log -M1 -C1 -B1 -D --find-copies-harderyı görür, ancak geçerseniz , git yeni dosyanın önce kopyalanmış olabileceğini "keşfedebilir". Bazen bunu doğru yapar, diğer zamanlarda, aynı içeriğe sahip olan tamamen ilgisiz dosyaları bulur.
Kent Fredric

36

git diff -Mveya git log -Mbu değişiklikleri otomatik olarak, oldukları sürece küçük değişikliklerle yeniden adlandırma olarak algılamalıdır . Senin Eğer küçük değişiklikler minör değilsin, benzerlik threashold azaltabilir, örneğin

$ git log -M20 -p --stat

varsayılan% 50'den% 20'ye düşürmek için.


13
Konfigürasyonda eşik tanımlamak mümkün mü?
Yeniden Tespit

34

İşte bir veya birkaç, yeniden adlandırılmış ve değiştirilmemiş dosya için hızlı ve kirli bir çözüm.

Diyelim ki dosya isimlendirildi foove şimdi isimlendirildi bar:

  1. barGeçici adla yeniden adlandır :

    mv bar side
    
  2. Ödeme foo:

    git checkout HEAD foo
    
  3. Rename fooiçin barGit ile:

    git mv foo bar
    
  4. Şimdi geçici dosyanızı yeniden adlandırın bar.

    mv side bar
    

Bu son adım, değiştirilen içeriğinizi dosyaya geri getiren şeydir.

Bu işe yarayabilir, ancak taşınan dosya orijinal git içeriğinden çok farklıysa, bunun yeni bir nesne olduğuna karar vermenin daha verimli olacağını düşünür. Göstereyim:

$ git status
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    renamed:    README -> README.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   README.md
    modified:   work.js

$ git add README.md work.js # why are the changes unstaged, let's add them.
$ git status
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    deleted:    README
    new file:   README.md
    modified:   work.js

$ git stash # what? let's go back a bit
Saved working directory and index state WIP on dir: f7a8685 update
HEAD is now at f7a8685 update
$ git status
On branch workit
Untracked files:
  (use "git add <file>..." to include in what will be committed)

    .idea/

nothing added to commit but untracked files present (use "git add" to track)
$ git stash pop
Removing README
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    new file:   README.md

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    deleted:    README
    modified:   work.js

Dropped refs/stash@{0} (1ebca3b02e454a400b9fb834ed473c912a00cd2f)
$ git add work.js
$ git status
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    new file:   README.md
    modified:   work.js

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    deleted:    README

$ git add README # hang on, I want it removed
$ git status
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    deleted:    README
    new file:   README.md
    modified:   work.js

$ mv README.md Rmd # Still? Try the answer I found.
$ git checkout README
error: pathspec 'README' did not match any file(s) known to git.
$ git checkout HEAD README # Ok the answer needed fixing.
$ git status
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    new file:   README.md
    modified:   work.js

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    deleted:    README.md
    modified:   work.js

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

    Rmd

$ git mv README README.md
$ git status
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    renamed:    README -> README.md
    modified:   work.js

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   work.js

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

    Rmd

$ mv Rmd README.md
$ git status
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    renamed:    README -> README.md
    modified:   work.js

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   README.md
    modified:   work.js

$ # actually that's half of what I wanted; \
  # and the js being modified twice? Git prefers it in this case.

27
Bu işlemin bir amacı yoktur. git, yeniden adlandırma için herhangi bir meta veri eklemez git mv, sadece bir git rm/ git addçift için kolaylıktır . Zaten 'mv bar foo' yaptıysanız, yapmanız gereken tek şey taahhütte bulunmadan önce git add foove emin olmaktır git rm bar. Bu, tek bir git add -Akomut veya muhtemelen bir git add foo; git commit -adizi gibi yapılabilir .
CB Bailey

17
Tek bildiğim, bunu yapmadan önce Git'in bunu bir hamle olarak tanımadığı. Bunu yaptıktan sonra Git bunu bir hamle olarak tanıdı.

8
Bunu bir hamle olarak tanır. Ama şimdi değişmemiş bir değişiklik olarak da değişime sahip. Size bir kez adddosya daha, git sonları taşı / silinmiş / tekrar eklendi içine değiştirin.
Michael Piefel

4
Bu işlem, git commit3. adımdan sonra çalışırsanız çalışır , aksi takdirde yanlıştır. Ayrıca, @CharlesBailey doğrudur, mv blah foo3. adımda kolayca normal bir işlem ve ardından bir taahhüt yapabilir ve aynı sonuçları alabilirsiniz.
DaFlame

5
"Bu süreç bir amaca hizmet etmiyor." - Git'in kafası karıştığında ve bir dosyanın silindiğini ve başka bir dosyanın eklendiğini düşündüğü bir amaca hizmet eder. Bu, Git'in karışıklığını giderir ve ara işlem yapmadan dosyayı açıkça taşınmış olarak işaretler.
Danack

20

Yeniden git statusadların gösterilmemesinden bahsediyorsanız git commit --dry-run -abunun yerine deneyin


11

TortoiseGit kullanıyorsanız, Git'in otomatik yeniden adlandırma algılamasının işlem sırasında gerçekleştiğini, ancak bunun gerçekleşeceği gerçeğinin yazılım tarafından önceden görüntülenmediğini unutmayın. İki dosyayı farklı bir dizine taşıdım ve bazı küçük düzenlemeler yaptım. Taahhüt aracım olarak TortoiseGit kullanıyorum ve Değişiklik listesi silindi ve taşındı değil, eklenen dosyaları gösterdi. Git durumunu komut satırından çalıştırmak da benzer bir durum gösterdi. Ancak dosyaları işledikten sonra, günlükte yeniden adlandırıldığını ortaya çıkardılar. Dolayısıyla sorunuzun cevabı, çok sert bir şey yapmadığınız sürece Git'in yeniden adlandırmayı otomatik olarak alması gerekir.

Düzenleme: Görünüşe göre yeni dosyaları ekler ve sonra komut satırından bir git durumu yaparsanız, yeniden adlandırma taahhüt vermeden önce görünmelidir.

Düzenleme 2: Ek olarak, TortoiseGit'te yeni dosyaları taahhüt iletişim kutusuna ekleyin, ancak bunları taahhüt etmeyin. Sonra Günlüğü Göster komutuna gidip çalışma dizinine bakarsanız, Git işlemeden önce yeniden adlandırmayı tespit edip etmediğini göreceksiniz.

Aynı soru burada ortaya çıktı: https://tortoisegit.org/issue/1389 ve burada düzeltmek için bir hata olarak günlüğe kaydedildi: https://tortoisegit.org/issue/1440 TortoiseGit'in taahhüdü ile ilgili bir ekran sorunu olduğu ortaya çıktı iletişim kutusu ve ayrıca yeni dosyaları eklemediyseniz git durumunda da vardır.


2
Doğru, TortoiseGit delete + add gösteriyor olsa bile git git --ry-run, delete + add gösteriyor olsa bile git durumu delete + add göstermiş olsa bile git git komutundan sonra yeniden adlandırmayı ve silme + ekleme görmüyorum.
Tomas Kubes

1
Geçmişe erişim sırasında otomatik yeniden adlandırma algılamasının gerçekleşeceğinden eminim ; taahhütte her zaman ekle + sil. Bu aynı zamanda tanımladığınız şizofrenik davranışı da açıklar. Bu nedenle buradaki seçenekler: stackoverflow.com/a/434078/155892
Mark Sowul

10

Veya bu sorunun cevabını denemek coud burada tarafından Amber ! Tekrar alıntı yapmak için:

Öncelikle, manuel olarak taşınan dosya için aşamalı eklemenizi iptal edin:

$ git reset path/to/newfile
$ mv path/to/newfile path/to/oldfile

Ardından, dosyayı taşımak için Git'i kullanın:

$ git mv path/to/oldfile path/to/newfile

Elbette, manuel hareketi zaten taahhüt ettiyseniz, hareketten önce revizyona sıfırlamak ve daha sonra oradan mv'yi gitmek isteyebilirsiniz.


7

git mvİşletim sistemi taşıma komutları yerine dosyaları taşımak için komut kullanın : https://git-scm.com/docs/git-mv

git mvKomutun yalnızca Git 1.8.5 ve sonraki sürümlerinde bulunduğunu lütfen unutmayın . Bu yüzden Git'i bu komutu kullanmak için güncellemeniz gerekebilir.


3

Bazı dosyaları taşırken (ancak değiştirmeden) son zamanlarda bu sorunu yaşadım.

Sorun, Git'in dosyaları taşıdığımda bazı satır sonlarını değiştirmesi ve daha sonra dosyaların aynı olduğunu söyleyememesi.

kullanma git mv sorunu çözdü, ama sadece tek bir dosya / dizin üzerinde çalışır ve ben yapmak için deponun kökünde çok fazla dosya vardı.

Bunu düzeltmenin bir yolu bazı bash / toplu sihirdir.

Başka bir yol aşağıdaki gibidir

  • Dosyaları taşıyın ve git commit . Bu satır sonlarını günceller.
  • Dosyaları yeni satır sonlarına sahip oldukları için orijinal konumlarına geri taşıyın ve git commit --amend
  • Dosyaları tekrar taşıyın ve git commit --amend. Bu kez satır sonlarında değişiklik yapılmadığından Git mutlu oluyor

1

Benim için taahhütten önce tüm değişiklikleri saklamak ve tekrar patlatmak için çalıştı. Bu, git eklenen / silinen dosyaları yeniden analiz ettirdi ve doğru bir şekilde taşınmış olarak işaretledi.


0

Bunu yapmanın muhtemelen daha iyi bir “komut satırı” yolu var ve bunun bir hack olduğunu biliyorum, ama asla iyi bir çözüm bulamadım.

TortoiseGIT'i kullanma: Bazı dosya taşıma işlemlerinin yeniden adlandırmak yerine ekleme / silme yükü olarak gösterildiği bir GIT taahhüdünüz varsa, dosyalar yalnızca küçük değişikliklere sahip olsa da, bunu yapın:

  1. Yerel olarak ne yaptığınızı kontrol edin
  2. 2. taahhütte tek satırlık küçük bir değişiklik yapın
  3. GIT girişine git kaplumbağa git
  4. İki taahhüdü seçin, sağ tıklayın ve “bir taahhütte birleştir” i seçin

Yeni işlem, dosya adlarını düzgün bir şekilde gösterecektir… bu da doğru dosya geçmişinin korunmasına yardımcı olacaktır.


0

Bir dosyayı aynı anda düzenlediğimde, yeniden adlandırdığımda ve taşıdığımda, bu çözümlerin hiçbiri çalışmıyor. Çözüm, bunu iki taahhütte yapmak (ayrı olarak düzenleme ve yeniden adlandırma / taşıma) ve sonra fixupikinci taahhüdün git rebase -ibir taahhütte bulunmasıdır.


0

Bu soruyu anlama şeklim "Git'in eski bir dosyanın silinmesini ve dosya taşıma olarak yeni bir dosyanın oluşturulmasını nasıl fark ettirmesini sağlamaktır".

Evet, eski bir dosyayı sildikten ve eski bir dosyayı ekledikten sonra çalışma dizininde git status" deleted: old_file" ve "Untracked files: ... new_file "

Ancak hazırlama dizinini / düzeyinde git'i kullanarak dosya ekleyip kaldırdığınızda, dosya taşıma olarak tanınır. Bunu yapmak için, İşletim Sisteminizi kullanarak silme ve oluşturma işlemlerini yaptığınızı varsayarak, aşağıdaki komutları verin:

git add new_file
git rm old_file

Dosyanın içeriği% 50 veya daha fazla benzer ise, run git statuskomutu size şunları vermelidir:

renamed: old_file -> new_file

1
git status" deleted: old_file" ve " Untracked files: ... new_file" diyecek : Git 2.18'den beri değil: stackoverflow.com/a/50573107/6309
VonC
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.