Kết hợp nhiều kho git


207

Hãy nói rằng tôi đã có một thiết lập trông giống như

phd/code/
phd/figures/
phd/thesis/

Vì lý do lịch sử, tất cả đều có kho git riêng. Nhưng tôi muốn kết hợp chúng thành một thứ duy nhất để đơn giản hóa mọi thứ một chút. Ví dụ, ngay bây giờ tôi có thể thực hiện hai bộ thay đổi và phải làm một cái gì đó như

cd phd/code
git commit 
cd ../figures
git commit

Thật tốt khi được biểu diễn

cd phd
git commit

Dường như có một vài cách để làm điều này bằng cách sử dụng các mô hình con hoặc lấy từ kho lưu trữ phụ của tôi, nhưng điều đó phức tạp hơn một chút so với tôi đang tìm kiếm. Ít nhất, tôi sẽ hạnh phúc với

cd phd
git init
git add [[everything that's already in my other repositories]]

nhưng nó không giống như một lớp lót. Có bất cứ điều gì trong gitđó có thể giúp tôi ra?


Cũng xem xét cách tiếp cận tuyệt vời này: stackoverflow.com/questions/1425892/ Kẻ
Johan Sjöberg

Đồng thời xem xét: Saintgimp.org/2013/01 / 22/iêu
ptim

Các join-git-repos.py kịch bản làm một công việc tốt đẹp nếu bạn có kho riêng biệt, mỗi với các ngành chủ mà bạn muốn kết hợp.
Đánh dấu

Câu trả lời:


149

Đây là một giải pháp tôi đã đưa ra ở đây :

  1. Trước tiên hãy sao lưu toàn bộ thư mục phd của bạn: Tôi không muốn chịu trách nhiệm cho những năm tháng vất vả của bạn! ;-)

    $ cp -r phd phd-backup
    
  2. Di chuyển nội dung phd/codeđến phd/code/codevà sửa lịch sử sao cho có vẻ như nó vẫn luôn ở đó (thao tác này sử dụng lệnh bộ lọc nhánh của git ):

    $ cd phd/code
    $ git filter-branch --index-filter \
        'git ls-files -s | sed "s#\t#&code/#" |
         GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
         git update-index --index-info &&
         mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' HEAD
    
  3. Tương tự cho nội dung của phd/figuresphd/thesis(chỉ cần thay thế codebằng figuresthesis).

    Bây giờ cấu trúc thư mục của bạn sẽ trông như thế này:

    phd
      |_code
      |    |_.git
      |    |_code
      |         |_(your code...)
      |_figures
      |    |_.git
      |    |_figures
      |         |_(your figures...)
      |_thesis
           |_.git
           |_thesis
                |_(your thesis...)
    
  4. Sau đó tạo một kho lưu trữ git trong thư mục gốc, kéo mọi thứ vào đó và xóa các kho lưu trữ cũ:

    $ cd phd
    $ git init
    
    $ git pull code
    $ rm -rf code/code
    $ rm -rf code/.git
    
    $ git pull figures --allow-unrelated-histories
    $ rm -rf figures/figures
    $ rm -rf figures/.git
    
    $ git pull thesis --allow-unrelated-histories
    $ rm -rf thesis/thesis
    $ rm -rf thesis/.git
    

    Cuối cùng, bây giờ bạn sẽ có những gì bạn muốn:

    phd
      |_.git
      |_code
      |    |_(your code...)
      |_figures
      |    |_(your figures...)
      |_thesis
           |_(your thesis...)
    

Một khía cạnh tốt đẹp của thủ tục này là nó sẽ để lại các tệp và thư mục không được phiên bản .

Hi vọng điêu nay co ich.


Chỉ cần một từ cảnh báo: nếu codethư mục của bạn đã có codethư mục con hoặc tệp, mọi thứ có thể rất sai (tương tự figuresthesistất nhiên). Nếu đúng như vậy, chỉ cần đổi tên thư mục hoặc tệp đó trước khi thực hiện toàn bộ quy trình này:

$ cd phd/code
$ git mv code code-repository-migration
$ git commit -m "preparing the code directory for migration"

Và khi thủ tục kết thúc, hãy thêm bước cuối cùng này:

$ cd phd
$ git mv code/code-repository-migration code/code
$ git commit -m "final step for code directory migration"

Tất nhiên, nếu codethư mục con hoặc tệp không được phiên bản, chỉ cần sử dụng mvthay vì git mvvà quên đi git commits.


13
Cảm ơn vì đoạn trích này - nó đã làm chính xác những gì tôi cần (một khi tôi đã tính đến Mac OS X sed không xử lý "\ t" (tôi phải sử dụng ^ V ^ Tôi thay vào đó).
Craig Trader

6
Lúc đầu tôi không thể làm việc này và cuối cùng đã tìm ra giải pháp cho vấn đề trên một bảng tin cũ khác. Trên dòng cuối cùng, tôi đã phải đặt dấu ngoặc kép xung quanh tên tệp như vậy: mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEADvà sau đó nó hoạt động rất tốt!
Jorin

3
Lệnh nhánh bộ lọc thú vị là từ các trang man nhánh bộ lọc của git. Bạn nên nói rằng: a) nó nên được quy cho chính xác b) Tôi sẽ không chạy một lệnh như vậy chỉ vì ai đó, ngay cả với danh tiếng cao, đã đăng nó trên StackOverflow. Biết nó từ trang người đàn ông tôi sẽ.
tymtam

5
COI CHỪNG! MacOS X không sử dụng phần mở rộng GNU của sed, vì vậy nó không biết trình tự \ t. Kết quả là một lịch sử sai lầm! Giải pháp của tôi là dán mã vào tệp tập lệnh viết một ký tự <TAB> thực trong đó. Từ Terminal, một tab có thể được nhập bằng cách nhấn ctrl + v và sau đó viết <TAB>. Tôi chưa thử giải pháp của Craig
Gil Vegliach

4
XEM RA (2)! Cũng lưu ý rằng nếu một số tệp hoặc thư mục chứa dấu gạch nối ('-') thì lệnh sed sẽ thất bại. Trong trường hợp đó, bạn có thể thay thế nó bằng một cái gì đó như 's ~ \ t ~ & code / ~'. Ở đây, áp dụng logic tương tự, coi chừng '~' trong tên
Gil Vegliach

75

git-stitch-reposẽ xử lý đầu ra của git-fast-export --all --date-orderkho git được cung cấp trên dòng lệnh và tạo một luồng phù hợp với git-fast-importnó sẽ tạo ra một kho lưu trữ mới chứa tất cả các xác nhận trong một cây cam kết mới tôn trọng lịch sử của tất cả các kho lưu trữ nguồn.


33
Uh, nó là một công cụ của bên thứ ba, không phải là một phần của git
:)

1
Thật vậy, bây giờ bạn nói với tôi :) Ồ, tôi cho rằng tôi phải học cách cài đặt các gói CPAN vào một ngày nào đó
Will Robertson

1
Cảm ơn đã chỉ ra lệnh đó. Chỉ cần sử dụng nó để giúp chuyển một vài repos từ SVN sang Git.
đăng nhập

1
CẢNH BÁO có thể không hoạt động nếu bạn có chi nhánh / sáp nhập! Từ trang git-stich-repo : "git-stich-repo hoạt động hoàn hảo với các kho lưu trữ có lịch sử tuyến tính (không hợp nhất). Các cải tiến cho thuật toán ghép được thêm vào trong phiên bản 0.06 nên phù hợp để làm việc với các kho lưu trữ chi nhánh và sáp nhập. "
Bryan P

6
Đây là một tập lệnh bên ngoài, câu trả lời quá ngắn và không thực sự hữu ích, tập lệnh này có vấn đề với các cam kết hợp nhất, không nhiều người sẽ xử lý Perl hoặc CPAN và điều này không được giải thích rõ trong câu trả lời. Vì vậy, ... -1, xin lỗi.
Haralan Dobrev

20

Có lẽ, chỉ đơn giản (tương tự như câu trả lời trước, nhưng sử dụng các lệnh đơn giản hơn) tạo ra trong mỗi kho lưu trữ cũ riêng biệt một cam kết chuyển nội dung thành một thư mục con có tên phù hợp, ví dụ:

$ cd phd/code
$ mkdir code
# This won't work literally, because * would also match the new code/ subdir, but you understand what I mean:
$ git mv * code/
$ git commit -m "preparing the code directory for migration"

và sau đó hợp nhất ba repos riêng biệt thành một repos mới, bằng cách thực hiện smth như:

$ cd ../..
$ mkdir phd.all
$ cd phd.all
$ git init
$ git pull ../phd/code
...

Sau đó, bạn sẽ lưu lịch sử của mình, nhưng sẽ tiếp tục với một repo duy nhất.


Điều này là ổn, nhưng nếu bạn đang hợp nhất một repo này với một repo khác (ví dụ phd không phải là một repo đã tồn tại) thì nếu phd có các thư mục có tên giống như các thư mục con trong thư mục mã, bạn sẽ gặp vấn đề như 'git pull .. / phd / code 'kéo tất cả các xác nhận bằng các đường dẫn gốc và chỉ khi kết thúc, nó mới áp dụng cam kết mv.
tymtam

1
@Tymek: nhưng điều này vẫn sẽ hoạt động trong tình huống đó, không có vấn đề. Điều không hay là các đường dẫn trong lịch sử sẽ không "chính xác" (tương ứng với các đường dẫn mới).
imz - Ivan Zakharyaschev

19

Bạn có thể thử chiến lược hợp nhất cây con . Nó sẽ cho phép bạn hợp nhất repo B thành repo A. Ưu điểm hơn git-filter-branchlà nó không yêu cầu bạn viết lại lịch sử repo A của bạn (phá vỡ tổng SHA1).


Liên kết không hoạt động và điều này sẽ không bảo tồn lịch sử, phải không?
tymtam

3
@Tymek (Xin lỗi các phần của kernel.org vẫn không hoạt động sau khi vi phạm bảo mật). Nó phá vỡ SHA1 của repo B. Nhưng A vẫn nguyên vẹn.
Leif Gruenwoldt

2
Bây giờ là một tấm gương của tài liệu đó ftp.sunet.se/pub/Linux/kernel.org/software/scm/git/docs/howto/iêu
Leif Gruenwoldt

1
@LeifGruenwoldt Liên kết đầu tiên đang hoạt động. Và liên kết gương đã biến mất, bạn nên loại bỏ nó.
Vadim Kotov

9

Giải pháp nhánh git-lọc-hoạt động tốt, nhưng lưu ý rằng nếu git repo của bạn đến từ nhập SVN, nó có thể thất bại với một thông báo như:

Rewrite 422a38a0e9d2c61098b98e6c56213ac83b7bacc2 (1/42)mv: cannot stat `/home/.../wikis/nodows/.git-rewrite/t/../index.new': No such file or directory

Trong trường hợp này, bạn cần loại trừ sửa đổi ban đầu khỏi nhánh bộ lọc - tức là thay đổi HEADở cuối thành [SHA of 2nd revision]..HEAD- xem:

http://www.git.code-experiment.com/blog/2010/03/merging-git-repose khu.html


2
Cảm ơn bạn! Tôi đã gãi đầu tại sao điều này không hiệu quả! Các repo đã thực sự đến từ SVN.
Arthur Maltson

1
Lỗi tương tự khi tôi làm điều đó. Có hy vọng của tôi lên. Ngoài ra, liên kết bây giờ bị hỏng.
Ryan

Bạn có thể giải thích ý của bạn bằng cách "thay đổi đầu tại ...", repo của tôi xuất phát từ việc nhập SVN và tôi đang đối mặt chính xác với vấn đề này, sẽ đánh giá cao sự giúp đỡ rất nhiều!

5

Giải pháp @MiniQuark đã giúp tôi rất nhiều, nhưng thật không may, nó không tính đến các thẻ tài khoản trong kho lưu trữ nguồn (Ít nhất là trong trường hợp của tôi). Dưới đây là cải tiến của tôi để trả lời @MiniQuark.

  1. Đầu tiên tạo thư mục sẽ chứa repo tổng hợp và repos hợp nhất, tạo thư mục cho mỗi thư mục được hợp nhất.

    $ mkdir new_phd
    $ mkdir new_phd / code
    $ mkdir new_phd / số liệu
    $ mkdir new_phd / Luận án

  2. Thực hiện kéo từng kho lưu trữ và tìm nạp tất cả các thẻ. (Chỉ trình bày hướng dẫn cho codethư mục con)

    $ cd new_phd / code
    $ git init
    $ git pull ../../origen_phd/code master
    $ git fetch ../../origen_phd/code refs / tags / *: refs / tags / *

  3. (Đây là cải tiến cho điểm 2 trong câu trả lời của MiniQuark) Di chuyển nội dung new_phd/codeđến new_phd/code/codevà thêm code_tiền giả trước mỗi thẻ

    $ git bộ lọc-nhánh --index-filter 'git ls-files -s | sed "s- \ t \" * - & code / - "| GIT_INDEX_FILE = $ GIT_INDEX_FILE.new git update-index --index-thông tin -. * - mã _ & - "'TRỤ

  4. Sau khi làm như vậy, sẽ có gấp đôi số thẻ so với trước khi thực hiện bộ lọc nhánh. Các thẻ cũ vẫn còn trong repo và các thẻ mới có code_tiền tố được thêm vào.

    $ git thẻ
    mytag1
    code_mytag1

    Xóa các thẻ cũ theo cách thủ công:

    $ ls .git / refs / tags / * | grep -v "/ code_" | xargs rm

    Lặp lại điểm 2,3,4 cho các thư mục con khác

  5. Bây giờ chúng tôi có cấu trúc của các thư mục như trong @MiniQuark anwser điểm 3.

  6. Thực hiện như ở điểm 4 của MiniQuark anwser, nhưng sau khi thực hiện thao tác kéo và trước khi xóa .gitdir, hãy tìm nạp các thẻ:

    $ git tìm nạp danh mục refs / tags / *: refs / tags / *

    Tiếp tục..

Đây chỉ là một giải pháp khác. Hy vọng nó sẽ giúp được ai đó, nó đã giúp tôi :)


5

git-Stitch-repo từ câu trả lời của Aristotle Pagaltzis chỉ hoạt động cho các kho lưu trữ với lịch sử tuyến tính đơn giản.

Câu trả lời của MiniQuark hoạt động cho tất cả các kho lưu trữ, nhưng nó không xử lý các thẻ và chi nhánh.

Tôi đã tạo một chương trình hoạt động giống như MiniQuark mô tả, nhưng nó sử dụng một cam kết hợp nhất (với N cha mẹ) và cũng tạo lại tất cả các thẻ và nhánh để trỏ đến các cam kết hợp nhất này.

Xem kho git-merge-repos để biết ví dụ về cách sử dụng nó.



3

Trên thực tế, git-Stitch-repo hiện hỗ trợ các nhánh và thẻ, bao gồm các thẻ chú thích (tôi thấy có một lỗi mà tôi đã báo cáo và nó đã được sửa). Những gì tôi thấy hữu ích là với các thẻ. Vì các thẻ được gắn vào các cam kết và một số giải pháp (như phương pháp của Eric Lee) không thể xử lý các thẻ. Bạn cố gắng tạo một nhánh từ một thẻ đã nhập và nó sẽ hoàn tác bất kỳ sự hợp nhất / di chuyển git nào và gửi lại cho bạn giống như kho lưu trữ hợp nhất gần giống với kho lưu trữ mà thẻ xuất phát. Ngoài ra, có vấn đề nếu bạn sử dụng cùng một thẻ trên nhiều kho lưu trữ mà bạn 'đã hợp nhất / hợp nhất'. Ví dụ: nếu bạn có quảng cáo B của repo, cả hai đều có thẻ rel_1.0. Bạn hợp nhất repo A và repo B thành repo AB. Vì các thẻ rel_1.0 nằm trên hai cam kết khác nhau (một cho A và một cho B), Thẻ nào sẽ hiển thị trong AB? Thẻ từ repo A đã nhập hoặc từ repo B đã nhập, nhưng không phải cả hai.

git-Stitch-repo giúp giải quyết vấn đề đó bằng cách tạo các thẻ rel_1.0-A và rel_1.0-B. Bạn có thể không thể kiểm tra thẻ rel_1.0 và mong đợi cả hai, nhưng ít nhất bạn có thể thấy cả hai, và theo lý thuyết, bạn có thể hợp nhất chúng vào một nhánh cục bộ chung sau đó tạo thẻ rel_1.0 trên nhánh được hợp nhất đó (giả sử bạn chỉ hợp nhất và không thay đổi mã nguồn). Tốt hơn là làm việc với các chi nhánh, vì bạn có thể hợp nhất như các chi nhánh từ mỗi repo thành các chi nhánh địa phương. (dev-a và dev-b có thể được hợp nhất thành một nhánh dev cục bộ, sau đó có thể được đẩy về nguồn gốc).


2

Trình tự bạn đề xuất

git init
git add *
git commit -a -m "import everything"

sẽ làm việc, nhưng bạn sẽ mất lịch sử cam kết của bạn.


Mất lịch sử không phải là quá tệ, nhưng vì kho lưu trữ là cho công việc của riêng tôi (nghĩa là nó là riêng tư) nên có rất nhiều thứ trong đó tôi không muốn phiên bản hoặc chưa được phiên bản.
Will Robertson

1

Để hợp nhất một Dự án thứ hai trong một Dự án chính:

A) Trong Dự án thứ hai

git fast-export --all --date-order > /tmp/secondProjectExport

B) Trong Dự án chính:

git checkout -b secondProject
git fast-import --force < /tmp/secondProjectExport

Trong nhánh này thực hiện tất cả các chuyển đổi nặng nề bạn cần làm và cam kết chúng.

C) Sau đó trở lại tổng thể và hợp nhất cổ điển giữa hai nhánh:

git checkout master
git merge secondProject

Điều này sẽ hợp nhất tất cả các tệp và thư mục gốc của cả hai dự án git vào một dự án. Tôi nghi ngờ _anyone_would muốn điều này xảy ra.
Clintm

0

Tôi cũng sẽ ném giải pháp của mình vào đây. Về cơ bản, nó là một trình bao bọc kịch bản bash khá đơn giản git filter-branch. Giống như các giải pháp khác, nó chỉ di chuyển các nhánh chính và không di chuyển các thẻ. Nhưng toàn bộ lịch sử cam kết chính được di chuyển và nó là một tập lệnh bash ngắn nên người dùng có thể dễ dàng xem lại hoặc chỉnh sửa.

https://github.com/Oakleon/git-join-repose


0

Tập lệnh bash này hoạt động xung quanh vấn đề ký tự tab sed (ví dụ trên MacOS) và vấn đề thiếu tệp.

export SUBREPO="subrepo"; # <= your subrepository name here
export TABULATOR=`printf '\t'`;
FILTER='git ls-files -s | sed "s#${TABULATOR}#&${SUBREPO}/#" |
  GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
  git update-index --index-info &&
  if [ -f "$GIT_INDEX_FILE.new" ]; then mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE; else echo "git filter skipped missing file: $GIT_INXEX_FILE.new"; fi'

git filter-branch --index-filter "$FILTER" HEAD

Đây là sự kết hợp của các bài đăng của miniquark , marius-butucryan . Chúc mừng cho họ!

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.