Ta odpowiedź dostarcza ciekawych poleceń opartych na git am
przykładach i krok po kroku.
Cel
- Chcesz przenieść niektóre lub wszystkie pliki z jednego repozytorium do drugiego.
- Chcesz zachować ich historię.
- Ale nie dbasz o utrzymanie tagów i gałęzi.
- Akceptujesz ograniczoną historię plików o zmienionych nazwach (i plików w katalogach o zmienionych nazwach).
Procedura
- Wyodrębnij historię w formacie wiadomości e-mail za pomocą
git log --pretty=email -p --reverse --full-index --binary
- Zreorganizuj drzewo plików i zaktualizuj zmianę nazwy pliku w historii [opcjonalnie]
- Zastosuj nową historię za pomocą
git am
1. Wyodrębnij historię w formacie wiadomości e-mail
Przykład: Historia Ekstrakt file3
, file4
ifile5
my_repo
├── dirA
│ ├── file1
│ └── file2
├── dirB ^
│ ├── subdir | To be moved
│ │ ├── file3 | with history
│ │ └── file4 |
│ └── file5 v
└── dirC
├── file6
└── file7
Wyczyść tymczasowy katalog docelowy
export historydir=/tmp/mail/dir # Absolute path
rm -rf "$historydir" # Caution when cleaning
Wyczyść źródło repozytorium
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
Wyodrębnij historię każdego pliku w formacie e-mail
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"' {} ';'
Niestety opcja --follow
lub --find-copies-harder
nie można jej połączyć --reverse
. Dlatego historia jest wycinana, gdy nazwa pliku jest zmieniana (lub gdy nazwa katalogu nadrzędnego jest zmieniana).
Po: historia tymczasowa w formacie wiadomości e-mail
/tmp/mail/dir
├── subdir
│ ├── file3
│ └── file4
└── file5
2. Zreorganizuj drzewo plików i zaktualizuj zmianę nazwy pliku w historii [opcjonalnie]
Załóżmy, że chcesz przenieść te trzy pliki w tym drugim repozytorium (może to być to samo repo).
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
Dlatego zreorganizuj swoje pliki:
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
Twoja tymczasowa historia jest teraz:
/tmp/mail/dir
└── dirB
├── dirB1
│ ├── file33
│ └── file44
└── dirB2
└── file5
Zmień także nazwy plików w historii:
cd "$historydir"
find * -type f -exec bash -c 'sed "/^diff --git a\|^--- a\|^+++ b/s:\( [ab]\)/[^ ]*:\1/$0:g" -i "$0"' {} ';'
Uwaga: Przepisuje historię, aby odzwierciedlić zmianę ścieżki i nazwy pliku.
(tj. zmiana nowej lokalizacji / nazwy w ramach nowego repozytorium)
3. Zastosuj nową historię
Twoje inne repo to:
my_other_repo
├── dirF
│ ├── file55
│ └── file56
└── dirH
└── file77
Zastosuj zatwierdzenia z tymczasowych plików historii:
cd my_other_repo
find "$historydir" -type f -exec cat {} + | git am
Twoje drugie repo to teraz:
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB ^
│ ├── dirB1 | New files
│ │ ├── file33 | with
│ │ └── file44 | history
│ └── dirB2 | kept
│ └── file5 v
└── dirH
└── file77
Użyj, git status
aby zobaczyć liczbę zatwierdzeń gotowych do wypchnięcia :-)
Uwaga: Ponieważ historia została przepisana w celu odzwierciedlenia zmiany ścieżki i nazwy pliku:
(tj. W porównaniu do lokalizacji / nazwy w poprzednim repozytorium)
- Nie trzeba
git mv
zmieniać lokalizacji / nazwy pliku.
- Nie ma potrzeby
git log --follow
uzyskiwania dostępu do pełnej historii.
Dodatkowa sztuczka: wykryj pliki o zmienionej nazwie / przeniesione w repozytorium
Aby wyświetlić listę plików, których nazwy zostały zmienione:
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow {} ';' | grep '=>'
Więcej dostosowań: Możesz wykonać polecenie, git log
używając opcji --find-copies-harder
lub --reverse
. Możesz także usunąć pierwsze dwie kolumny, używając cut -f3-
i grepując pełny wzór „{. * =>. *}”.
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow --find-copies-harder --reverse {} ';' | cut -f3- | grep '{.* => .*}'