Cara memindahkan file dari satu repo git ke yang lain (bukan klon), menjaga sejarah

484

Repositori Git kami dimulai sebagai bagian dari repositori monster SVN tunggal di mana masing-masing proyek masing-masing memiliki pohon sendiri seperti:

project1/branches
        /tags
        /trunk
project2/branches
        /tags
        /trunk

Jelas, cukup mudah untuk memindahkan file dari satu ke yang lain dengan svn mv. Tetapi di Git, setiap proyek berada dalam repositori sendiri, dan hari ini saya diminta untuk memindahkan subdirektori dari project2ke project1. Saya melakukan sesuatu seperti ini:

$ git clone project2 
$ cd project2
$ git filter-branch --subdirectory-filter deeply/buried/java/source/directory/A -- --all
$ git remote rm origin  # so I don't accidentally overwrite the repo ;-)
$ mkdir -p deeply/buried/different/java/source/directory/B
$ for f in *.java; do 
>  git mv $f deeply/buried/different/java/source/directory/B
>  done
$ git commit -m "moved files to new subdirectory"
$ cd ..
$
$ git clone project1
$ cd project1
$ git remote add p2 ../project2
$ git fetch p2
$ git branch p2 remotes/p2/master
$ git merge p2 # --allow-unrelated-histories for git 2.9+
$ git remote rm p2
$ git push

Tapi itu sepertinya berbelit-belit. Apakah ada cara yang lebih baik untuk melakukan hal semacam ini secara umum? Atau sudahkah saya mengadopsi pendekatan yang benar?

Perhatikan bahwa ini melibatkan menggabungkan sejarah ke dalam repositori yang ada, daripada hanya membuat repositori mandiri baru dari bagian yang lain ( seperti dalam pertanyaan sebelumnya ).

ebneter
sumber
1
Itu terdengar seperti pendekatan yang masuk akal bagi saya; Saya tidak bisa memikirkan cara yang jelas untuk meningkatkan metode Anda secara signifikan. Sangat menyenangkan bahwa Git sebenarnya membuat ini mudah (saya tidak ingin mencoba untuk memindahkan direktori file antara repositori yang berbeda di Subversion, misalnya).
Greg Hewgill
1
@ebneter - Saya sudah melakukan ini (memindahkan riwayat dari satu repo svn ke yang lain) secara manual, menggunakan skrip shell. Pada dasarnya saya memutar ulang histori (diff, komit pesan log) dari file / dir tertentu ke repositori kedua.
Adam Monsen
1
Aku bertanya-tanya mengapa Anda tidak melakukannya git fetch p2 && git merge p2bukan git fetch p2 && git branch .. && git merge p2? Sunting: baik-baik saja, sepertinya Anda ingin mendapatkan perubahan di cabang baru bernama p2, bukan cabang saat ini.
Lekensteyn
1
Apakah tidak ada cara untuk mencegah --filter-branch menghancurkan struktur direktori? Langkah "git mv" ini menghasilkan komit besar yang penuh dengan penghapusan file dan pembuatan file.
Edward Falk
1
Perhatikan bahwa pada git 2.9 menggabungkan sejarah yang tidak terkait tidak diizinkan secara default. Untuk membuatnya berfungsi, tambahkan --allow-unrelated-historiesyang terakhir git mergeuntuk membuatnya berfungsi.
Scott Berrevoets

Jawaban:

55

Yap, memukul pada --subdirectory-filterdari filter-branchitu kunci. Fakta bahwa Anda menggunakannya pada dasarnya membuktikan bahwa tidak ada cara yang lebih mudah - Anda tidak punya pilihan selain menulis ulang sejarah, karena Anda ingin berakhir hanya dengan subset (berganti nama) dari file, dan ini dengan definisi mengubah hash. Karena tidak ada perintah standar (misalnya pull) penulisan ulang riwayat, tidak mungkin Anda dapat menggunakannya untuk mencapai ini.

Anda bisa memperbaiki detailnya, tentu saja - beberapa kloning dan percabangan Anda tidak sepenuhnya diperlukan - tetapi pendekatan keseluruhannya bagus! Ini memalukan, rumit, tapi tentu saja, tujuan dari git bukanlah untuk membuatnya mudah untuk menulis ulang sejarah.

Cascabel
sumber
1
bagaimana jika file Anda telah berpindah melalui beberapa direktori, dan sekarang berada dalam satu - apakah subdirektori-filter masih berfungsi? (yaitu saya berasumsi bahwa jika saya hanya ingin memindahkan satu file, saya dapat memindahkannya ke subdirektori sendiri dan ini akan berhasil?)
rogerdpack
1
@rogerdpack: Tidak, ini tidak akan mengikuti file melalui penggantian nama. Saya percaya itu akan tampaknya telah dibuat pada titik itu dipindahkan ke subdirektori yang dipilih. Jika Anda ingin memilih hanya satu file, lihat --index-filterdi halaman filter-branchmanual.
Cascabel
8
Apakah ada resep bagaimana saya bisa mengikuti penggantian nama?
Night Warrier
Saya pikir mempertahankan dan mengkurasi sejarah adalah salah satu poin utama dari git.
artburkart
288

Jika riwayat Anda waras, Anda bisa mengambil komit sebagai tambalan dan menerapkannya di repositori baru:

cd repository
git log --pretty=email --patch-with-stat --reverse --full-index --binary -- path/to/file_or_folder > patch
cd ../another_repository
git am --committer-date-is-author-date < ../repository/patch 

Atau dalam satu baris

git log --pretty=email --patch-with-stat --reverse -- path/to/file_or_folder | (cd /path/to/new_repository && git am --committer-date-is-author-date)

(Diambil dari dokumen Exherbo )

Smar
sumber
21
Untuk tiga atau 4 file yang saya perlukan untuk memindahkan ini adalah solusi yang jauh lebih sederhana daripada jawaban yang diterima. Saya akhirnya memotong jalan keluar di file tambalan dengan find-replace agar sesuai dengan struktur direktori repo baru saya.
Rian Sanderson
8
Saya telah menambahkan pilihan sehingga file biner (seperti gambar) juga benar bermigrasi: git log --pretty=email --patch-with-stat --full-index --binary --reverse -- client > patch. Bekerja tanpa masalah AFAICT.
Emmanuel Touzery
35
Pada langkah terapkan, saya menggunakan --committer-date-is-author-dateopsi untuk mempertahankan tanggal komit asli, bukan tanggal file dipindahkan.
darrenmc
6
menggabungkan komit dalam riwayat melanggar perintah "am". Anda dapat menambahkan "-m --first-parent" ke perintah git log di atas, lalu bekerja untuk saya.
Gábor Lipták
6
@Daniel Golden Saya telah berhasil memperbaiki masalah dengan file yang telah dipindahkan (yang merupakan konsekuensi dari bug git log, sehingga tidak berfungsi dengan baik --followdan --reversebenar). Saya menggunakan jawaban ini , dan di sini adalah naskah lengkap yang saya gunakan sekarang untuk memindahkan file
tsayen
75

Setelah mencoba berbagai pendekatan untuk memindahkan file atau folder dari satu repositori Git ke yang lain, satu-satunya yang tampaknya berfungsi dengan baik dijelaskan di bawah ini.

Ini melibatkan kloning repositori yang ingin Anda pindahkan file atau folder, memindahkan file atau folder itu ke root, menulis ulang sejarah Git, mengkloning repositori target dan menarik file atau folder dengan sejarah langsung ke repositori target ini.

Tahap satu

  1. Buat salinan repositori A sebagai langkah-langkah berikut membuat perubahan besar pada salinan ini yang tidak boleh Anda dorong!

    git clone --branch <branch> --origin origin --progress \
      -v <git repository A url>
    # eg. git clone --branch master --origin origin --progress \
    #   -v https://username@giturl/scm/projects/myprojects.git
    # (assuming myprojects is the repository you want to copy from)
    
  2. masuk ke dalamnya

    cd <git repository A directory>
    #  eg. cd /c/Working/GIT/myprojects
    
  3. Hapus tautan ke repositori asli untuk menghindari perubahan jarak jauh yang tidak disengaja (mis. Dengan mendorong)

    git remote rm origin
    
  4. Telusuri riwayat dan file Anda, hapus semua yang tidak ada di direktori 1. Hasilnya adalah isi dari direktori 1 yang dimuntahkan ke dasar repositori A.

    git filter-branch --subdirectory-filter <directory> -- --all
    # eg. git filter-branch --subdirectory-filter subfolder1/subfolder2/FOLDER_TO_KEEP -- --all
    
  5. Hanya untuk satu file bergerak: telusuri apa yang tersisa dan hapus semuanya kecuali file yang diinginkan. (Anda mungkin perlu menghapus file yang tidak Anda inginkan dengan nama dan komit yang sama.)

    git filter-branch -f --index-filter \
    'git ls-files -s | grep $'\t'FILE_TO_KEEP$ |
    GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
    git update-index --index-info && \
    mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE || echo "Nothing to do"' --prune-empty -- --all
    # eg. FILE_TO_KEEP = pom.xml to keep only the pom.xml file from FOLDER_TO_KEEP
    

Tahap Dua

  1. Langkah pembersihan

    git reset --hard
    
  2. Langkah pembersihan

    git gc --aggressive
    
  3. Langkah pembersihan

    git prune
    

Anda mungkin ingin mengimpor file-file ini ke repositori B dalam direktori bukan root:

  1. Buat direktori itu

    mkdir <base directory>             eg. mkdir FOLDER_TO_KEEP
    
  2. Pindahkan file ke direktori itu

    git mv * <base directory>          eg. git mv * FOLDER_TO_KEEP
    
  3. Tambahkan file ke direktori itu

    git add .
    
  4. Komit perubahan Anda dan kami siap untuk menggabungkan file-file ini ke dalam repositori baru

    git commit
    

Tahap Tiga

  1. Buat salinan repositori B jika Anda belum memilikinya

    git clone <git repository B url>
    # eg. git clone https://username@giturl/scm/projects/FOLDER_TO_KEEP.git
    

    (dengan asumsi FOLDER_TO_KEEP adalah nama repositori baru yang Anda salin)

  2. masuk ke dalamnya

    cd <git repository B directory>
    #  eg. cd /c/Working/GIT/FOLDER_TO_KEEP
    
  3. Buat koneksi jarak jauh ke repositori A sebagai cabang di repositori B

    git remote add repo-A-branch <git repository A directory>
    # (repo-A-branch can be anything - it's just an arbitrary name)
    
    # eg. git remote add repo-A-branch /c/Working/GIT/myprojects
    
  4. Tarik dari cabang ini (hanya berisi direktori yang ingin Anda pindahkan) ke dalam repositori B.

    git pull repo-A-branch master --allow-unrelated-histories
    

    Tarikan menyalin file dan riwayat. Catatan: Anda bisa menggunakan gabungan alih-alih tarikan, tetapi tarikan bekerja lebih baik.

  5. Terakhir, Anda mungkin ingin membersihkan sedikit dengan menghapus koneksi jarak jauh ke repositori A

    git remote rm repo-A-branch
    
  6. Dorong dan Anda sudah siap.

    git push
    
mcaran
sumber
Saya telah melalui sebagian besar langkah-langkah yang diuraikan di sini namun tampaknya hanya menyalin sejarah komit file atau dir dari master (dan bukan dari cabang lain). Apakah itu benar?
Bao-Long Nguyen-Trong
Saya pikir itu benar dan bahwa Anda harus melalui langkah-langkah serupa untuk setiap cabang dari mana Anda ingin memindahkan file atau folder. beralih ke cabang misalnya. MyBranch di repositori A, filter-branch dll. Anda kemudian akan "git pull repo-A-branch MyBranch" di repositori B.
mcarans
Terima kasih balasannya. Apakah Anda tahu jika tag di cabang juga akan dimigrasi?
Bao-Long Nguyen-Trong
Saya khawatir saya tidak tahu, tetapi akan menebak bahwa mereka akan tahu.
mcarans
1
@ mcarans Sayangnya, ini BUKAN cara yang dapat diandalkan, meskipun tampaknya begitu. Itu menderita masalah yang sama dengan semua solusi lainnya - Tidak mempertahankan riwayat nama lama. Dalam kasus saya, komit pertama adalah ketika saya mengganti nama direktori / file. Segala sesuatu di luar itu hilang.
xZero
20

Saya menemukan ini sangat berguna. Ini adalah pendekatan yang sangat sederhana di mana Anda membuat tambalan yang diterapkan pada repo baru. Lihat halaman tertaut untuk lebih jelasnya.

Itu hanya berisi tiga langkah (disalin dari blog):

# Setup a directory to hold the patches
mkdir <patch-directory>

# Create the patches
git format-patch -o <patch-directory> --root /path/to/copy

# Apply the patches in the new repo using a 3 way merge in case of conflicts
# (merges from the other repo are not turned into patches). 
# The 3way can be omitted.
git am --3way <patch-directory>/*.patch

Satu-satunya masalah yang saya miliki adalah bahwa saya tidak bisa menerapkan semua tambalan sekaligus menggunakan

git am --3way <patch-directory>/*.patch

Di Windows saya mendapat kesalahan InvalidArgument. Jadi saya harus menerapkan semua tambalan satu demi satu.

anhoppe
sumber
Tidak bekerja untuk saya karena pada beberapa titik sha-hashes hilang. Ini membantu saya: stackoverflow.com/questions/17371150/…
dr0i
Berbeda dengan pendekatan "git log", opsi ini bekerja sempurna untuk saya! Terima kasih!
AlejandroVD
1
Mencoba berbagai pendekatan untuk memindahkan proyek ke repo baru. Ini adalah satu-satunya yang bekerja untuk saya. Tidak dapat percaya bahwa tugas bersama seperti itu pasti rumit.
Chris_D_Turk
Terima kasih telah berbagi blog Ross Hendrickson . Pendekatan ini berhasil bagi saya.
Kaushik Acharya
1
Ini adalah solusi yang sangat elegan, namun, sekali lagi, ia mengalami masalah yang sama dengan semua solusi lainnya - Ini TIDAK akan mempertahankan sejarah yang telah diubah namanya.
xZero
6

MENJAGA NAMA DIREKTORI

Subdirektori-filter (atau subtree perintah git pendek) berfungsi dengan baik tetapi tidak berfungsi untuk saya karena mereka menghapus nama direktori dari info komit. Dalam skenario saya, saya hanya ingin menggabungkan bagian dari satu repositori ke yang lain dan menyimpan sejarah DENGAN nama path lengkap.

Solusi saya adalah menggunakan tree-filter dan hanya menghapus file dan direktori yang tidak diinginkan dari klon sementara repositori sumber, kemudian tarik dari klon itu ke repositori target saya dalam 5 langkah sederhana.

# 1. clone the source
git clone ssh://<user>@<source-repo url>
cd <source-repo>
# 2. remove the stuff we want to exclude
git filter-branch --tree-filter "rm -rf <files to exclude>" --prune-empty HEAD
# 3. move to target repo and create a merge branch (for safety)
cd <path to target-repo>
git checkout -b <merge branch>
# 4. Add the source-repo as remote 
git remote add source-repo <path to source-repo>
# 5. fetch it
git pull source-repo master
# 6. check that you got it right (better safe than sorry, right?)
gitk
Joachim Nilsson
sumber
Ini naskah tidak akan membuat modifikasi untuk repo asli Anda. Jika desto repo yang ditentukan dalam file peta tidak ada, maka skrip ini akan mencoba membuatnya.
Chetabahana
1
Saya pikir juga bahwa menjaga nama direktori tetap sangat penting. Kalau tidak, Anda akan mendapatkan komitmen penggantian nama tambahan ke repositori target.
ipuustin
6

Yang saya selalu gunakan ada di sini http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/ . Sederhana dan cepat.

Untuk kepatuhan dengan standar stackoverflow, berikut adalah prosedurnya:

mkdir /tmp/mergepatchs
cd ~/repo/org
export reposrc=myfile.c #or mydir
git format-patch -o /tmp/mergepatchs $(git log $reposrc|grep ^commit|tail -1|awk '{print $2}')^..HEAD $reposrc
cd ~/repo/dest
git am /tmp/mergepatchs/*.patch
Hugh Perkins
sumber
5

Jawaban ini memberikan perintah menarik berdasarkan git amdan disajikan menggunakan contoh, langkah demi langkah.

Objektif

  • Anda ingin memindahkan beberapa atau semua file dari satu repositori ke yang lain.
  • Anda ingin menyimpan sejarah mereka.
  • Tetapi Anda tidak peduli tentang menyimpan tag dan cabang.
  • Anda menerima riwayat terbatas untuk file berganti nama (dan file dalam direktori berganti nama).

Prosedur

  1. Ekstrak riwayat dalam format email menggunakan
    git log --pretty=email -p --reverse --full-index --binary
  2. Atur ulang pohon file dan perbarui perubahan nama file dalam riwayat [opsional]
  3. Terapkan riwayat baru menggunakan git am

1. Ekstrak histori dalam format email

Contoh: Ekstrak riwayat file3, file4danfile5

my_repo
├── dirA
│   ├── file1
│   └── file2
├── dirB            ^
│   ├── subdir      | To be moved
│   │   ├── file3   | with history
│   │   └── file4   | 
│   └── file5       v
└── dirC
    ├── file6
    └── file7

Bersihkan tujuan direktori sementara

export historydir=/tmp/mail/dir  # Absolute path
rm -rf "$historydir"             # Caution when cleaning

Bersihkan sumber repo Anda

git commit ...           # Commit your working files
rm .gitignore            # Disable gitignore
git clean -n             # Simulate removal
git clean -f             # Remove untracked file
git checkout .gitignore  # Restore gitignore

Ekstrak riwayat setiap file dalam format email

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

Sayangnya opsi --followatau --find-copies-hardertidak dapat digabungkan dengan --reverse. Inilah sebabnya mengapa sejarah dipotong ketika file diganti namanya (atau ketika direktori induk diubah namanya).

Setelah: Riwayat sementara dalam format email

/tmp/mail/dir
    ├── subdir
    │   ├── file3
    │   └── file4
    └── file5

2. Atur ulang susunan file dan perbarui perubahan nama file dalam riwayat [opsional]

Misalkan Anda ingin memindahkan ketiga file ini di repo lain ini (bisa menjadi repo yang sama).

my_other_repo
├── dirF
│   ├── file55
│   └── file56
├── dirB              # New tree
│   ├── dirB1         # was subdir
│   │   ├── file33    # was file3
│   │   └── file44    # was file4
│   └── dirB2         # new dir
│        └── file5    # = file5
└── dirH
    └── file77

Karena itu atur ulang file Anda:

cd /tmp/mail/dir
mkdir     dirB
mv subdir dirB/dirB1
mv dirB/dirB1/file3 dirB/dirB1/file33
mv dirB/dirB1/file4 dirB/dirB1/file44
mkdir    dirB/dirB2
mv file5 dirB/dirB2

Riwayat sementara Anda sekarang:

/tmp/mail/dir
    └── dirB
        ├── dirB1
        │   ├── file33
        │   └── file44
        └── dirB2
             └── file5

Ubah juga nama file dalam riwayat:

cd "$historydir"
find * -type f -exec bash -c 'sed "/^diff --git a\|^--- a\|^+++ b/s:\( [ab]\)/[^ ]*:\1/$0:g" -i "$0"' {} ';'

Catatan: Ini menulis ulang sejarah untuk mencerminkan perubahan path dan nama file.
      (yaitu perubahan lokasi / nama baru dalam repo baru)


3. Terapkan sejarah baru

Repo Anda yang lain adalah:

my_other_repo
├── dirF
│   ├── file55
│   └── file56
└── dirH
    └── file77

Terapkan komit dari file riwayat sementara:

cd my_other_repo
find "$historydir" -type f -exec cat {} + | git am 

Repo Anda yang lain sekarang:

my_other_repo
├── dirF
│   ├── file55
│   └── file56
├── dirB            ^
│   ├── dirB1       | New files
│   │   ├── file33  | with
│   │   └── file44  | history
│   └── dirB2       | kept
│        └── file5  v
└── dirH
    └── file77

Gunakan git statusuntuk melihat jumlah komit yang siap didorong :-)

Catatan: Karena riwayat telah ditulis ulang untuk mencerminkan jalur dan perubahan nama file:
      (yaitu dibandingkan dengan lokasi / nama dalam repo sebelumnya)

  • Tidak perlu git mvmengubah lokasi / nama file.
  • Tidak perlu git log --followmengakses riwayat lengkap.

Trik tambahan: Mendeteksi file yang diubah namanya / dipindahkan dalam repo Anda

Untuk membuat daftar file yang telah diubah namanya:

find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow {} ';' | grep '=>'

Kustomisasi lainnya: Anda dapat menyelesaikan perintah git logmenggunakan opsi --find-copies-harderatau --reverse. Anda juga dapat menghapus dua kolom pertama menggunakan cut -f3-dan menangkap pola lengkap '{. * =>. *}'.

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

Setelah memiliki gatal serupa dengan goresan (walaupun hanya untuk beberapa file dari repositori yang diberikan) skrip ini terbukti sangat membantu: git-import

Versi singkatnya adalah ia membuat file tambalan dari file atau direktori yang diberikan ( $object) dari repositori yang ada:

cd old_repo
git format-patch --thread -o "$temp" --root -- "$object"

yang kemudian diterapkan ke repositori baru:

cd new_repo
git am "$temp"/*.patch 

Untuk detailnya silakan lihat:

ViToni
sumber
2

Coba ini

cd repo1

Ini akan menghapus semua direktori kecuali yang disebutkan, menjaga sejarah hanya untuk direktori ini

git filter-branch --index-filter 'git rm --ignore-unmatch --cached -qr -- . && git reset -q $GIT_COMMIT -- dir1/ dir2/ dir3/ ' --prune-empty -- --all

Sekarang Anda dapat menambahkan repo baru Anda di remote git Anda dan mendorongnya ke sana

git remote remove origin <old-repo>
git remote add origin <new-repo>
git push origin <current-branch>

tambahkan -funtuk menimpa

Chetan Basutkar
sumber
PERINGATAN: git-filter-branch memiliki kekenyangan gotcha yang menghasilkan penulisan ulang riwayat yang rusak. Tekan Ctrl-C sebelum melanjutkan untuk membatalkan, kemudian gunakan alat pemfilteran alternatif seperti 'git filter-repo' ( github.com/newren/git-filter-repo ) sebagai gantinya. Lihat halaman manual cabang-filter untuk detail lebih lanjut; untuk memadamkan peringatan ini, setel FILTER_BRANCH_SQUELCH_WARNING = 1.
Colin
1

Menggunakan inspirasi dari http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/ , saya membuat fungsi Powershell ini untuk melakukan hal yang sama, yang memiliki bekerja sangat baik untuk saya sejauh ini:

# Migrates the git history of a file or directory from one Git repo to another.
# Start in the root directory of the source repo.
# Also, before running this, I recommended that $destRepoDir be on a new branch that the history will be migrated to.
# Inspired by: http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/
function Migrate-GitHistory
{
    # The file or directory within the current Git repo to migrate.
    param([string] $fileOrDir)
    # Path to the destination repo
    param([string] $destRepoDir)
    # A temp directory to use for storing the patch file (optional)
    param([string] $tempDir = "\temp\migrateGit")

    mkdir $tempDir

    # git log $fileOrDir -- to list commits that will be migrated
    Write-Host "Generating patch files for the history of $fileOrDir ..." -ForegroundColor Cyan
    git format-patch -o $tempDir --root -- $fileOrDir

    cd $destRepoDir
    Write-Host "Applying patch files to restore the history of $fileOrDir ..." -ForegroundColor Cyan
    ls $tempDir -Filter *.patch  `
        | foreach { git am $_.FullName }
}

Penggunaan untuk contoh ini:

git clone project2
git clone project1
cd project1
# Create a new branch to migrate to
git checkout -b migrate-from-project2
cd ..\project2
Migrate-GitHistory "deeply\buried\java\source\directory\A" "..\project1"

Setelah melakukan ini, Anda dapat mengatur ulang file pada migrate-from-project2cabang sebelum menggabungkannya.

crimbo
sumber
1

Saya menginginkan sesuatu yang kuat dan dapat digunakan kembali (fungsi one-command-and-go + undo) jadi saya menulis skrip bash berikut. Bekerja untuk saya pada beberapa kesempatan, jadi saya pikir saya akan membagikannya di sini.

Ia dapat memindahkan folder arbitrer /path/to/foodari repo1ke /some/other/folder/barke repo2(path folder bisa sama atau berbeda, jarak dari folder root mungkin berbeda).

Karena hanya membahas komit yang menyentuh file dalam folder input (tidak semua komit dari repo sumber), itu harus cukup cepat bahkan pada repo sumber besar, jika Anda hanya mengekstrak subfolder bersarang dalam yang tidak disentuh di setiap melakukan.

Karena yang dilakukan adalah membuat cabang yatim dengan semua sejarah repo lama dan kemudian menggabungkannya ke HEAD, bahkan akan berfungsi jika terjadi bentrokan nama file (maka Anda harus menyelesaikan penggabungan pada akhir saja) .

Jika tidak ada bentrokan nama file, Anda hanya perlu git commitdi akhir untuk menyelesaikan penggabungan.

Kelemahannya adalah bahwa ia kemungkinan tidak akan mengikuti penggantian nama file (di luar REWRITE_FROMfolder) dalam permintaan repo - pull sumber diterima di GitHub untuk mengakomodasi hal itu.

Tautan GitHub: git-move-folder-between-repos-keep-history

#!/bin/bash

# Copy a folder from one git repo to another git repo,
# preserving full history of the folder.

SRC_GIT_REPO='/d/git-experimental/your-old-webapp'
DST_GIT_REPO='/d/git-experimental/your-new-webapp'
SRC_BRANCH_NAME='master'
DST_BRANCH_NAME='import-stuff-from-old-webapp'
# Most likely you want the REWRITE_FROM and REWRITE_TO to have a trailing slash!
REWRITE_FROM='app/src/main/static/'
REWRITE_TO='app/src/main/static/'

verifyPreconditions() {
    #echo 'Checking if SRC_GIT_REPO is a git repo...' &&
      { test -d "${SRC_GIT_REPO}/.git" || { echo "Fatal: SRC_GIT_REPO is not a git repo"; exit; } } &&
    #echo 'Checking if DST_GIT_REPO is a git repo...' &&
      { test -d "${DST_GIT_REPO}/.git" || { echo "Fatal: DST_GIT_REPO is not a git repo"; exit; } } &&
    #echo 'Checking if REWRITE_FROM is not empty...' &&
      { test -n "${REWRITE_FROM}" || { echo "Fatal: REWRITE_FROM is empty"; exit; } } &&
    #echo 'Checking if REWRITE_TO is not empty...' &&
      { test -n "${REWRITE_TO}" || { echo "Fatal: REWRITE_TO is empty"; exit; } } &&
    #echo 'Checking if REWRITE_FROM folder exists in SRC_GIT_REPO' &&
      { test -d "${SRC_GIT_REPO}/${REWRITE_FROM}" || { echo "Fatal: REWRITE_FROM does not exist inside SRC_GIT_REPO"; exit; } } &&
    #echo 'Checking if SRC_GIT_REPO has a branch SRC_BRANCH_NAME' &&
      { cd "${SRC_GIT_REPO}"; git rev-parse --verify "${SRC_BRANCH_NAME}" || { echo "Fatal: SRC_BRANCH_NAME does not exist inside SRC_GIT_REPO"; exit; } } &&
    #echo 'Checking if DST_GIT_REPO has a branch DST_BRANCH_NAME' &&
      { cd "${DST_GIT_REPO}"; git rev-parse --verify "${DST_BRANCH_NAME}" || { echo "Fatal: DST_BRANCH_NAME does not exist inside DST_GIT_REPO"; exit; } } &&
    echo '[OK] All preconditions met'
}

# Import folder from one git repo to another git repo, including full history.
#
# Internally, it rewrites the history of the src repo (by creating
# a temporary orphaned branch; isolating all the files from REWRITE_FROM path
# to the root of the repo, commit by commit; and rewriting them again
# to the original path).
#
# Then it creates another temporary branch in the dest repo,
# fetches the commits from the rewritten src repo, and does a merge.
#
# Before any work is done, all the preconditions are verified: all folders
# and branches must exist (except REWRITE_TO folder in dest repo, which
# can exist, but does not have to).
#
# The code should work reasonably on repos with reasonable git history.
# I did not test pathological cases, like folder being created, deleted,
# created again etc. but probably it will work fine in that case too.
#
# In case you realize something went wrong, you should be able to reverse
# the changes by calling `undoImportFolderFromAnotherGitRepo` function.
# However, to be safe, please back up your repos just in case, before running
# the script. `git filter-branch` is a powerful but dangerous command.
importFolderFromAnotherGitRepo(){
    SED_COMMAND='s-\t\"*-\t'${REWRITE_TO}'-'

    verifyPreconditions &&
    cd "${SRC_GIT_REPO}" &&
      echo "Current working directory: ${SRC_GIT_REPO}" &&
      git checkout "${SRC_BRANCH_NAME}" &&
      echo 'Backing up current branch as FILTER_BRANCH_BACKUP' &&
      git branch -f FILTER_BRANCH_BACKUP &&
      SRC_BRANCH_NAME_EXPORTED="${SRC_BRANCH_NAME}-exported" &&
      echo "Creating temporary branch '${SRC_BRANCH_NAME_EXPORTED}'..." &&
      git checkout -b "${SRC_BRANCH_NAME_EXPORTED}" &&
      echo 'Rewriting history, step 1/2...' &&
      git filter-branch -f --prune-empty --subdirectory-filter ${REWRITE_FROM} &&
      echo 'Rewriting history, step 2/2...' &&
      git filter-branch -f --index-filter \
       "git ls-files -s | sed \"$SED_COMMAND\" |
        GIT_INDEX_FILE=\$GIT_INDEX_FILE.new git update-index --index-info &&
        mv \$GIT_INDEX_FILE.new \$GIT_INDEX_FILE" HEAD &&
    cd - &&
    cd "${DST_GIT_REPO}" &&
      echo "Current working directory: ${DST_GIT_REPO}" &&
      echo "Adding git remote pointing to SRC_GIT_REPO..." &&
      git remote add old-repo ${SRC_GIT_REPO} &&
      echo "Fetching from SRC_GIT_REPO..." &&
      git fetch old-repo "${SRC_BRANCH_NAME_EXPORTED}" &&
      echo "Checking out DST_BRANCH_NAME..." &&
      git checkout "${DST_BRANCH_NAME}" &&
      echo "Merging SRC_GIT_REPO/" &&
      git merge "old-repo/${SRC_BRANCH_NAME}-exported" --no-commit &&
    cd -
}

# If something didn't work as you'd expect, you can undo, tune the params, and try again
undoImportFolderFromAnotherGitRepo(){
  cd "${SRC_GIT_REPO}" &&
    SRC_BRANCH_NAME_EXPORTED="${SRC_BRANCH_NAME}-exported" &&
    git checkout "${SRC_BRANCH_NAME}" &&
    git branch -D "${SRC_BRANCH_NAME_EXPORTED}" &&
  cd - &&
  cd "${DST_GIT_REPO}" &&
    git remote rm old-repo &&
    git merge --abort
  cd -
}

importFolderFromAnotherGitRepo
#undoImportFolderFromAnotherGitRepo
jakub.g
sumber
0

Dalam kasus saya, saya tidak perlu menyimpan repo tempat saya bermigrasi atau menyimpan sejarah sebelumnya. Saya memiliki patch dari cabang yang sama, dari remote yang berbeda

#Source directory
git remote rm origin
#Target directory
git remote add branch-name-from-old-repo ../source_directory

Dalam dua langkah itu, saya bisa membuat cabang repo lainnya muncul di repo yang sama.

Akhirnya, saya mengatur cabang ini (yang saya impor dari repo lain) untuk mengikuti garis utama repo target (sehingga saya bisa membedakannya secara akurat)

git br --set-upstream-to=origin/mainline

Sekarang berperilaku seolah-olah itu hanya cabang lain yang saya dorong melawan repo yang sama.

Jason D
sumber
0

Jika jalur untuk file yang dimaksud adalah sama di kedua repo dan Anda ingin membawa hanya satu file atau satu set kecil file terkait, satu cara mudah untuk melakukan ini adalah dengan menggunakan git cherry-pick.

Langkah pertama adalah membawa komit dari repo lain ke repo lokal Anda sendiri git fetch <remote-url>. Ini akan meninggalkan FETCH_HEADmenunjuk komit kepala dari repo lain; jika Anda ingin menyimpan referensi ke komit itu setelah Anda melakukan pengambilan lain, Anda mungkin ingin memberi tag dengannya git tag other-head FETCH_HEAD.

Anda kemudian perlu membuat komit awal untuk file itu (jika tidak ada) atau komit untuk membawa file ke keadaan yang dapat ditambal dengan komit pertama dari repo lain yang ingin Anda bawa. Anda dapat dapat melakukan ini dengan git cherry-pick <commit-0>jika commit-0memperkenalkan file yang Anda inginkan, atau Anda mungkin perlu membangun komit 'dengan tangan'. Tambahkan -nke opsi pick-ceri jika Anda perlu memodifikasi komit awal untuk, misalnya, drop file dari komit yang Anda tidak ingin bawa

Setelah itu, Anda dapat melanjutkan ke git cherry-pickkomitmen berikutnya, menggunakan lagi -njika perlu. Dalam kasus yang paling sederhana (semua komit adalah apa yang Anda inginkan dan menerapkan bersih) Anda dapat memberikan daftar lengkap komit pada baris perintah ceri-memilih: git cherry-pick <commit-1> <commit-2> <commit-3> ....

cjs
sumber
0

Ini menjadi lebih sederhana dengan menggunakan git-filter-repo.

Untuk pindah project2/sub/dirke project1/sub/dir:

# Create a new repo containing only the subdirectory:
git clone project2 project2_subdir
cd project2_subdir
git filter-repo --force --path sub/dir

# Merge the new repo:
cd ../project1
git remote add project2_subdir ../project2_subdir/
git merge remotes/project2_subdir/master --allow-unrelated-histories
git remote remove project2_subdir

Untuk menginstal alat ini cukup: pip3 install git-filter-repo ( lebih detail dan opsi di README )

# Before: (root)
.
|-- project1
|   `-- 3
`-- project2
    |-- 1
    `-- sub
        `-- dir
            `-- 2

# After: (project1)
.
├── 3
└── sub
    └── dir
        └── 2
Tapuzi
sumber
-2

Metode di bawah ini untuk memigrasi GIT Stash saya ke GitLab dengan mempertahankan semua cabang dan melestarikan sejarah.

Kloning repositori lama ke lokal.

git clone --bare <STASH-URL>

Buat repositori kosong di GitLab.

git push --mirror <GitLab-URL>

Di atas saya lakukan ketika kami memigrasi kode kami dari simpanan ke GitLab dan itu bekerja dengan sangat baik.

Roopkumar Akubathini
sumber