Mục tiêu
- Sử dụng (lấy cảm hứng từ Smar , mượn từ Exherbo )
git am
- Thêm lịch sử cam kết của các tập tin sao chép / di chuyển
- Từ thư mục này sang thư mục khác
- Hoặc từ kho này sang kho khác
Giới hạn
- Thẻ và chi nhánh không được lưu giữ
- Lịch sử bị cắt trên tên đường dẫn đổi tên (đổi tên thư mục)
Tóm lược
- Trích xuất lịch sử ở định dạng email bằng cách sử dụng
git log --pretty=email -p --reverse --full-index --binary
- Sắp xếp lại cây tập tin và cập nhật tên tệp
- Nối lịch sử mới bằng cách sử dụng
cat extracted-history | git am --committer-date-is-author-date
1. Trích xuất lịch sử ở định dạng email
Ví dụ: Trích xuất lịch sử của file3
, file4
vàfile5
my_repo
├── dirA
│ ├── file1
│ └── file2
├── dirB ^
│ ├── subdir | To be moved
│ │ ├── file3 | with history
│ │ └── file4 |
│ └── file5 v
└── dirC
├── file6
└── file7
Đặt / làm sạch đích
export historydir=/tmp/mail/dir # Absolute path
rm -rf "$historydir" # Caution when cleaning the folder
Trích xuất lịch sử của từng tệp ở định dạng 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"' {} ';'
Thật không may tùy chọn --follow
hoặc --find-copies-harder
không thể được kết hợp với --reverse
. Đây là lý do tại sao lịch sử bị cắt khi tập tin được đổi tên (hoặc khi thư mục cha được đổi tên).
Lịch sử tạm thời ở định dạng email:
/tmp/mail/dir
├── subdir
│ ├── file3
│ └── file4
└── file5
Dan Bonachea đề nghị đảo ngược các vòng lặp của lệnh tạo nhật ký git trong bước đầu tiên này: thay vì chạy nhật ký git một lần cho mỗi tệp, chạy chính xác một lần với danh sách các tệp trên dòng lệnh và tạo một nhật ký thống nhất. Cách này cam kết sửa đổi nhiều tệp vẫn là một cam kết duy nhất trong kết quả và tất cả các cam kết mới duy trì thứ tự tương đối ban đầu của chúng. Lưu ý điều này cũng yêu cầu thay đổi ở bước thứ hai bên dưới khi viết lại tên tệp trong nhật ký (hiện đã thống nhất).
2. Sắp xếp lại cây tập tin và cập nhật tên tệp
Giả sử bạn muốn di chuyển ba tệp này trong repo khác này (có thể là cùng một repo).
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
Do đó, sắp xếp lại các tệp của bạn:
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
Lịch sử tạm thời của bạn là bây giờ:
/tmp/mail/dir
└── dirB
├── dirB1
│ ├── file33
│ └── file44
└── dirB2
└── file5
Thay đổi tên tập tin trong lịch sử:
cd "$historydir"
find * -type f -exec bash -c 'sed "/^diff --git a\|^--- a\|^+++ b/s:\( [ab]\)/[^ ]*:\1/$0:g" -i "$0"' {} ';'
3. Áp dụng lịch sử mới
Repo khác của bạn là:
my_other_repo
├── dirF
│ ├── file55
│ └── file56
└── dirH
└── file77
Áp dụng các cam kết từ các tệp lịch sử tạm thời:
cd my_other_repo
find "$historydir" -type f -exec cat {} + | git am --committer-date-is-author-date
--committer-date-is-author-date
giữ nguyên dấu thời gian cam kết ban đầu ( bình luận của Dan Bonachea ).
Repo khác của bạn bây giờ là:
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB
│ ├── dirB1
│ │ ├── file33
│ │ └── file44
│ └── dirB2
│ └── file5
└── dirH
└── file77
Sử dụng git status
để xem số lượng cam kết sẵn sàng được đẩy :-)
Thêm mẹo: Kiểm tra các tập tin được đổi tên / di chuyển trong repo của bạn
Để liệt kê các tập tin đã được đổi tên:
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow {} ';' | grep '=>'
Thêm tùy chỉnh: Bạn có thể hoàn thành lệnh git log
bằng các tùy chọn --find-copies-harder
hoặc --reverse
. Bạn cũng có thể xóa hai cột đầu tiên bằng cách sử dụng cut -f3-
và lấy mẫu hoàn chỉnh '{. * =>. *}'.
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow --find-copies-harder --reverse {} ';' | cut -f3- | grep '{.* => .*}'
git mv
: stackoverflow.com/questions/1094269/whats-the-purpose-of-git-mv