Trong trường hợp của tôi, tôi đã có một my-plugin
kho lưu trữ và một main-project
kho lưu trữ, và tôi muốn giả vờ rằng nó my-plugin
luôn được phát triển trong plugins
thư mục con của main-project
.
Về cơ bản, tôi viết lại lịch sử của my-plugin
kho lưu trữ để nó xuất hiện tất cả sự phát triển diễn ra trong plugins/my-plugin
thư mục con. Sau đó, tôi thêm lịch sử phát triển my-plugin
vào main-project
lịch sử và hợp nhất hai cây lại với nhau. Vì không có plugins/my-plugin
thư mục nào có trong main-project
kho lưu trữ, đây là một sự hợp nhất không xung đột tầm thường. Kho lưu trữ kết quả chứa tất cả lịch sử từ cả hai dự án ban đầu và có hai gốc.
TL; DR
$ cp -R my-plugin my-plugin-dirty
$ cd my-plugin-dirty
$ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|plugins) plugins/my-plugin || true)'" -- --all
$ cd ../main-project
$ git checkout master
$ git remote add --fetch my-plugin ../my-plugin-dirty
$ git merge my-plugin/master --allow-unrelated-histories
$ cd ..
$ rm -rf my-plugin-dirty
Phiên bản dài
Đầu tiên, tạo một bản sao của my-plugin
kho lưu trữ, bởi vì chúng ta sẽ viết lại lịch sử của kho lưu trữ này.
Bây giờ, điều hướng đến thư mục gốc của my-plugin
kho lưu trữ, kiểm tra nhánh chính của bạn (có thể master
) và chạy lệnh sau. Tất nhiên, bạn nên thay thế my-plugin
và plugins
bất kể tên thật của bạn là gì.
$ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|plugins) plugins/my-plugin || true)'" -- --all
Bây giờ để giải thích. git filter-branch --tree-filter (...) HEAD
chạy (...)
lệnh trên mỗi cam kết có thể truy cập từ HEAD
. Lưu ý rằng điều này hoạt động trực tiếp trên dữ liệu được lưu trữ cho mỗi lần xác nhận, vì vậy chúng tôi không phải lo lắng về các khái niệm "thư mục làm việc", "chỉ mục", "dàn dựng", v.v.
Nếu bạn chạy một filter-branch
lệnh không thành công, nó sẽ để lại một số tệp trong .git
thư mục và lần sau bạn thử, filter-branch
nó sẽ phàn nàn về điều này, trừ khi bạn cung cấp -f
tùy chọn này filter-branch
.
Đối với lệnh thực tế, tôi không có nhiều may mắn bash
để làm những gì tôi muốn, vì vậy thay vào đó tôi sử dụng zsh -c
để thực zsh
hiện một lệnh. Đầu tiên tôi đặt extended_glob
tùy chọn, đó là cái cho phép ^(...)
cú pháp trong mv
lệnh, cũng như glob_dots
tùy chọn, cho phép tôi chọn dotfiles (chẳng hạn như .gitignore
) với một global ( ^(...)
).
Tiếp theo, tôi sử dụng mkdir -p
lệnh để tạo cả hai plugins
và plugins/my-plugin
cùng một lúc.
Cuối cùng, tôi sử dụng tính năng zsh
"tiêu cực toàn cầu" ^(.git|plugins)
để khớp với tất cả các tệp trong thư mục gốc của kho lưu trữ ngoại trừ .git
và my-plugin
thư mục mới được tạo . (Không bao gồm .git
có thể không cần thiết ở đây, nhưng cố gắng di chuyển một thư mục vào chính nó là một lỗi.)
Trong kho lưu trữ của tôi, cam kết ban đầu không bao gồm bất kỳ tệp nào, vì vậy mv
lệnh đã trả về lỗi trên cam kết ban đầu (vì không có gì để di chuyển). Vì vậy, tôi đã thêm một || true
để git filter-branch
không phá thai.
Các --all
tùy chọn bảo filter-branch
phải viết lại lịch sử cho tất cả các chi nhánh trong kho, và thêm --
là cần thiết để nói git
để giải thích nó như là một phần của danh sách lựa chọn cho các chi nhánh để viết lại, thay vì như một tùy chọn để filter-branch
bản thân.
Bây giờ, điều hướng đến main-project
kho lưu trữ của bạn và kiểm tra bất kỳ chi nhánh nào bạn muốn hợp nhất vào. Thêm bản sao cục bộ của my-plugin
kho lưu trữ (với lịch sử đã được sửa đổi) dưới dạng từ xa main-project
với:
$ git remote add --fetch my-plugin $PATH_TO_MY_PLUGIN_REPOSITORY
Bây giờ bạn sẽ có hai cây không liên quan trong lịch sử cam kết của mình, bạn có thể hình dung độc đáo bằng cách sử dụng:
$ git log --color --graph --decorate --all
Để hợp nhất chúng, sử dụng:
$ git merge my-plugin/master --allow-unrelated-histories
Lưu ý rằng trong Git trước 2.9.0, --allow-unrelated-histories
tùy chọn không tồn tại. Nếu bạn đang sử dụng một trong những phiên bản này, chỉ cần bỏ qua tùy chọn: thông báo lỗi --allow-unrelated-histories
ngăn chặn cũng được thêm vào 2.9.0.
Bạn không nên có bất kỳ xung đột hợp nhất. Nếu bạn làm như vậy, điều đó có thể có nghĩa là filter-branch
lệnh không hoạt động chính xác hoặc đã có một plugins/my-plugin
thư mục trong đó main-project
.
Hãy chắc chắn nhập một thông điệp cam kết giải thích cho bất kỳ người đóng góp nào trong tương lai tự hỏi không biết tin tặc nào đang diễn ra để tạo một kho lưu trữ có hai gốc.
Bạn có thể hình dung biểu đồ cam kết mới, cần có hai xác nhận gốc, sử dụng git log
lệnh trên . Lưu ý rằng chỉ có master
chi nhánh sẽ được hợp nhất . Điều này có nghĩa là nếu bạn có công việc quan trọng trên các my-plugin
nhánh khác mà bạn muốn hợp nhất vào main-project
cây, bạn nên hạn chế xóa my-plugin
điều khiển từ xa cho đến khi bạn thực hiện các kết hợp này. Nếu bạn không, thì các cam kết từ các nhánh đó sẽ vẫn nằm trong main-project
kho lưu trữ, nhưng một số sẽ không thể truy cập được và dễ bị thu gom rác cuối cùng. (Ngoài ra, bạn sẽ phải tham khảo chúng bằng SHA, vì việc xóa một điều khiển từ xa sẽ loại bỏ các nhánh theo dõi từ xa của nó.)
Tùy chọn, sau khi bạn đã hợp nhất mọi thứ bạn muốn giữ my-plugin
, bạn có thể xóa my-plugin
điều khiển từ xa bằng cách sử dụng:
$ git remote remove my-plugin
Bây giờ bạn có thể xóa bản sao của my-plugin
kho lưu trữ có lịch sử bạn đã thay đổi một cách an toàn . Trong trường hợp của tôi, tôi cũng đã thêm một thông báo phản đối vào my-plugin
kho lưu trữ thực sau khi hợp nhất hoàn tất và được đẩy.
Đã thử nghiệm trên Mac OS X El Capitan với git --version 2.9.0
và zsh --version 5.2
. Số dặm của bạn có thể thay đổi.
Người giới thiệu: