Tạo một kho lưu trữ mô-đun con từ một thư mục và giữ lịch sử cam kết git của nó


111

Tôi có một ứng dụng web khám phá các ứng dụng web khác theo một cách cụ thể. Nó chứa một số bản trình diễn web trong một demosthư mục và một trong các bản trình diễn này bây giờ phải có kho lưu trữ riêng. Tôi muốn tạo một kho lưu trữ riêng cho ứng dụng demo này và làm cho nó trở thànhgói phụ mô-đun con từ kho lưu trữ chính mà không làm mất lịch sử cam kết của nó.

Có thể giữ lịch sử cam kết từ các tệp trong thư mục của kho lưu trữ và tạo một kho lưu trữ từ đó và sử dụng nó như một mô-đun con thay thế không?


Tôi đã tìm kiếm cách di chuyển thư mục 1 từ kho lưu trữ Git A sang kho lưu trữ Git B. +1 cho liên kết đến bài viết.
Chetabahana


Có điều này thực sự là rất giống nhau, các giải pháp khác nhau một chút, cảm ơn vì chia sẻ này
GabLeRoux

Câu trả lời:


191

Giải pháp chi tiết

Xem ghi chú ở cuối câu trả lời này (đoạn cuối) để biết một giải pháp thay thế nhanh chóng cho mô-đun con git sử dụng npm;)

Trong câu trả lời sau đây, bạn sẽ biết cách trích xuất một thư mục từ một kho lưu trữ và tạo một kho lưu trữ git từ nó và sau đó bao gồm nó như một mô-đun con thay vì một thư mục.

Lấy cảm hứng từ bài viết của Gerg Bayer Di chuyển tệp từ Kho lưu trữ Git này sang Kho lưu trữ Git khác, Lưu giữ lịch sử

Ở phần đầu, chúng ta có một cái gì đó như thế này:

<git repository A>
    someFolders
    someFiles
    someLib <-- we want this to be a new repo and a git submodule!
        some files

Trong các bước dưới đây, tôi sẽ gọi điều này someLib<directory 1>.

Cuối cùng, chúng ta sẽ có một cái gì đó như thế này:

<git repository A>
    someFolders
    someFiles
    @submodule --> <git repository B>

<git repository B>
    someFolders
    someFiles

Tạo một kho lưu trữ git mới từ một thư mục trong một kho lưu trữ khác

Bước 1

Nhận một bản sao mới của kho lưu trữ để chia nhỏ.

git clone <git repository A url>
cd <git repository A directory>

Bước 2

Thư mục hiện tại sẽ là kho lưu trữ mới vì vậy hãy xóa điều khiển từ xa hiện tại.

git remote rm origin

Bước 3

Trích xuất lịch sử của thư mục mong muốn và cam kết nó

git filter-branch --subdirectory-filter <directory 1> -- --all

Bây giờ bạn sẽ có một kho lưu trữ git với các tệp từ directory 1gốc của kho lưu trữ của bạn với tất cả lịch sử cam kết liên quan.

Bước 4

Tạo kho lưu trữ trực tuyến của bạn và đẩy kho lưu trữ mới của bạn!

git remote add origin <git repository B url>
git push

Bạn có thể cần thiết lập upstreamnhánh cho lần đẩy đầu tiên của mình

git push --set-upstream origin master

Sạch sẽ <git repository A>(tùy chọn, xem nhận xét)

Chúng tôi muốn xóa dấu vết (tệp và lịch sử cam kết) <git repository B>khỏi <git repository A>lịch sử để thư mục này chỉ có một lần.

Điều này dựa trên việc Xóa dữ liệu nhạy cảm khỏi github.

Chuyển đến một thư mục mới và

git clone <git repository A url>
cd <git repository A directory>
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch <directory 1> -r' --prune-empty --tag-name-filter cat -- --all

Thay thế <directory 1>bằng thư mục bạn muốn xóa. -rsẽ làm điều đó một cách đệ quy bên trong thư mục được chỉ định :). Bây giờ đẩy đến origin/mastervới--force

git push origin master --force

Giai đoạn Boss (Xem Ghi chú bên dưới)

Tạo một mô-đun con từ <git repository B>thành<git repository A>

git submodule add <git repository B url>
git submodule update
git commit

Xác minh xem mọi thứ có hoạt động như mong đợi hay không và push

git push origin master

Ghi chú

Sau khi thực hiện tất cả những điều này, tôi nhận ra trong trường hợp của mình rằng thay vào đó, sử dụng npm để quản lý các phần phụ thuộc của riêng tôi sẽ thích hợp hơn . Chúng tôi có thể chỉ định các url git và các phiên bản, hãy xem các url git của package.json là các phần phụ thuộc .

Nếu bạn làm theo cách này, các kho lưu trữ mà bạn muốn sử dụng như là một yêu cầu phải có một mô-đun NPM vì vậy nó phải chứa một package.jsontập tin hoặc bạn sẽ nhận được lỗi này: Error: ENOENT, open 'tmp.tgz-unpack/package.json'.

tldr (giải pháp thay thế)

Bạn có thể thấy dễ dàng hơn khi sử dụng npmquản lý các phần phụ thuộc với các url git :

  • Di chuyển thư mục đến một kho lưu trữ mới
  • chạy npm initbên trong cả hai kho
  • chạy npm install --save git://github.com/user/project.git#commit-ishở nơi bạn muốn cài đặt các phụ thuộc của mình

39
Nên tránh bước "Làm sạch <git repository A>". Làm điều này, bạn không thể khôi phục hoàn toàn / kiểm tra các phiên bản cũ hơn / cam kết khỏi lịch sử của mình. Bạn chỉ nên git rm thư mục và thêm mô-đun con. Vì vậy, bạn đảm bảo có một bản sao hoạt động đầy đủ khi kiểm tra các cam kết cũ hơn.
Cybot

Bạn không nên làm cd someLibtrước Bước 2? Bạn nói "Thư mục hiện tại sẽ là kho lưu trữ mới" nhưng thực sự thì không; kho lưu trữ mới (mô-đun con) nằm bên trong thư mục đó.
Jago

1
xác nhận: có, nó hoạt động cho nhiều hơn một mô-đun con. Cảm ơn rất nhiều vì câu trả lời chi tiết. Ngoài ra, không cần phải sử dụng npm.
Breno Inojosa

2
Tôi sẽ thêm thông tin về refs/original/...mà được tạo ra ở bước 3.
Emile Bergeron

6
GitHub đã thực hiện một bài viết về cách để đạt được quá trình chiết của một thư mục vào một kho lưu trữ mới: help.github.com/articles/...
jrobichaud

9

Giải pháp của @GabLeRoux cắt bỏ các nhánh và các cam kết liên quan.

Một cách đơn giản để sao chép và giữ lại tất cả các nhánh và cam kết bổ sung đó:

1 - Đảm bảo bạn có bí danh git này

git config --global alias.clone-branches '! git branch -a | sed -n "/\/HEAD /d; /\/master$/d; /remotes/p;" | xargs -L1 git checkout -t'

2 - Sao chép điều khiển từ xa, kéo tất cả các nhánh, thay đổi điều khiển từ xa, lọc danh bạ của bạn, đẩy

git clone git@github.com:user/existing-repo.git new-repo
cd new-repo
git clone-branches
git remote rm origin
git remote add origin git@github.com:user/new-repo.git
git remote -v
git filter-branch --subdirectory-filter my_directory/ -- --all
git push --all
git push --tags

3

Giải pháp của GabLeRoux hoạt động tốt ngoại trừ trường hợp bạn sử dụng git lfsvà có các tệp lớn trong thư mục mà bạn muốn tách. Trong trường hợp đó, sau bước 3, tất cả các tệp lớn sẽ vẫn là tệp con trỏ thay vì tệp thực. Tôi đoán có thể là do .gitattributestệp bị xóa trong quá trình nhánh bộ lọc.

Nhận ra điều này, tôi thấy giải pháp sau phù hợp với tôi:

cp .gitattributes .git/info/attributes

Sao chép .gitattributesgit lfs nào sử dụng để theo dõi các tệp lớn vào .git/thư mục để tránh bị xóa.

Khi bộ lọc-nhánh hoàn thành, đừng quên đặt lại .gitattributesnếu bạn vẫn muốn sử dụng git lfs cho kho lưu trữ mới:

mv .git/info/attributes .gitattributes
git add .gitattributes
git commit -m 'added back .gitattributes'
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.