วิธีนำเข้าที่เก็บ Git ที่มีอยู่ไปยังที่อื่น


476

ฉันมีพื้นที่เก็บข้อมูล Git ในโฟลเดอร์ที่เรียกว่าXXXและฉันมีสอง Git พื้นที่เก็บข้อมูลที่เรียกว่าYYY

ฉันต้องการที่จะนำเข้าXXXพื้นที่เก็บข้อมูลลงในYYYพื้นที่เก็บข้อมูลเป็นไดเรกทอรีย่อยชื่อZZZและเพิ่มทุกXXXประวัติการเปลี่ยนแปลง 'เพื่อYYY

โครงสร้างโฟลเดอร์ก่อน:

├── XXX
│   ├── .git
│   └── (project files)
└── YYY
    ├── .git
    └── (project files)

โครงสร้างโฟลเดอร์หลัง:

YYY
├── .git  <-- This now contains the change history from XXX
├──  ZZZ  <-- This was originally XXX
│    └── (project files)
└──  (project files)

สามารถทำได้หรือไม่ฉันต้องใช้โมดูลย่อย?


2
ใน Github ตอนนี้คุณสามารถทำได้จากเว็บอินเตอร์เฟสเมื่อคุณสร้าง repo ใหม่
bgcode

คำตอบ:


430

น่าจะเป็นวิธีที่ง่ายที่สุดในการดึงสิ่งXXXเข้าสู่สาขาในYYYแล้วผสานเข้ากับต้นแบบ:

ในYYY :

git remote add other /path/to/XXX
git fetch other
git checkout -b ZZZ other/master
mkdir ZZZ
git mv stuff ZZZ/stuff                      # repeat as necessary for each file/dir
git commit -m "Moved stuff to ZZZ"
git checkout master                
git merge ZZZ --allow-unrelated-histories   # should add ZZZ/ to master
git commit
git remote rm other
git branch -d ZZZ                           # to get rid of the extra branch before pushing
git push                                    # if you have a remote, that is

จริง ๆ แล้วฉันแค่ลองกับ repos ของฉันสองสามตัวและใช้งานได้ ต่างจากคำตอบของJörgมันจะไม่ยอมให้คุณใช้ repo อื่นต่อไป แต่ฉันไม่คิดว่าคุณจะระบุไว้

หมายเหตุ: เนื่องจากสิ่งนี้ถูกเขียนขึ้นครั้งแรกในปี 2009 git ได้เพิ่มการรวมทรีย่อยที่ระบุไว้ในคำตอบด้านล่าง ฉันอาจจะใช้วิธีนี้ในวันนี้ แต่แน่นอนว่าวิธีนี้ยังใช้งานได้


1
ขอบคุณ ฉันใช้เทคนิคของคุณในเวอร์ชันดัดแปลงเล็กน้อย: ฉันสร้างสาขา 'การจัดเตรียม' ใน XXX ที่ฉันสร้างโฟลเดอร์ ZZZ และย้าย 'สิ่ง' ลงในนั้น จากนั้นฉันรวม XXX เป็น YYY
Vijay Patel

1
มันใช้งานได้ดีสำหรับฉัน การเปลี่ยนแปลงเดียวที่ฉันทำคือ: 1) "สาขา git -d ZZZ" ก่อนการผลักเพราะฉันไม่ต้องการให้สาขาอุณหภูมินี้แขวนอยู่รอบ ๆ 2) "git push" ทำให้ฉันมีข้อผิดพลาด: "ไม่มี refs ทั่วไปและไม่ได้ระบุไม่ทำอะไรบางทีคุณควรระบุสาขาเช่น 'master' (ต้นกำเนิดที่ฉันผลักไปคือพื้นที่เก็บข้อมูลเปล่า) แต่ "git push - ทั้งหมด" ทำงานเหมือนแชมป์
CrazyPyro

1
ฉันต้องการที่จะจบลงด้วยโฟลเดอร์ ZZZ รวมถึงประวัติใน repo YYY: ฉันต้องการที่จะลบ repo XXX เดิมและสาขา ZZZ ใน repo YYY ฉันพบการลบสาขา ZZZ เนื่องจาก @CrazyPyro แนะนำให้ลบประวัติ - เพื่อให้ฉันรวมสาขา ZZZ เป็นหลักก่อนที่จะลบ
Oli Studholme

4
@ เซบาสเตียนบลาสก์ฉันเพิ่งยุ่งกับสิ่งนี้ด้วยสอง repos ของฉันและตระหนักว่ามีขั้นตอนที่ขาดหายไปที่ไม่มีใครเคยดูเหมือนจะสังเกตเห็นแม้จะมี upvotes นี้มานานหลายปี :-) ฉันพูดถึงการรวมมันเป็นหลัก แต่ไม่ได้แสดงจริง กำลังแก้ไขในขณะนี้ ...
ebneter

2
คุณสามารถเพิ่มสิ่งนี้เมื่อย้ายไฟล์ไปยังโฟลเดอร์ย่อยของคุณ: git mv $(ls|grep -v <your foldername>) <your foldername>/ นี่จะเป็นการคัดลอกไฟล์และโฟลเดอร์ทั้งหมดไปยังโฟลเดอร์ใหม่ของคุณ
serup

366

หากคุณต้องการเก็บประวัติการส่งที่แน่นอนของที่เก็บข้อมูลที่สองและดังนั้นจึงยังคงความสามารถในการรวมการเปลี่ยนแปลงต้นน้ำในอนาคตได้อย่างง่ายดายนี่คือวิธีที่คุณต้องการ มันส่งผลในประวัติที่ไม่ได้แก้ไขของทรีย่อยที่ถูกนำเข้าไปยัง repo ของคุณรวมกับการรวมหนึ่งคอมมิทเพื่อย้ายที่เก็บที่ผสานไปยังไดเรกทอรีย่อย

git remote add XXX_remote <path-or-url-to-XXX-repo>
git fetch XXX_remote
git merge -s ours --no-commit --allow-unrelated-histories XXX_remote/master
git read-tree --prefix=ZZZ/ -u XXX_remote/master
git commit -m "Imported XXX as a subtree."

คุณสามารถติดตามการเปลี่ยนแปลงต้นน้ำดังนี้:

git pull -s subtree XXX_remote master

Git เป็นตัวของตัวเองโดยที่รากอยู่ก่อนทำการผสานดังนั้นคุณไม่จำเป็นต้องระบุคำนำหน้าในการรวมที่ตามมา

ข้อเสียก็คือว่าในประวัติศาสตร์ที่ผสานไฟล์ที่อยู่ unprefixed (ไม่อยู่ในไดเรกทอรีย่อย) ผลลัพธ์git log ZZZ/aจะแสดงการเปลี่ยนแปลงทั้งหมด (ถ้ามี) ยกเว้นการเปลี่ยนแปลงในประวัติการผสาน คุณทำได้:

git log --follow -- a

แต่นั่นจะไม่แสดงการเปลี่ยนแปลงอื่น ๆ ในประวัติศาสตร์ที่ถูกผสาน

กล่าวอีกนัยหนึ่งถ้าคุณไม่เปลี่ยนZZZไฟล์ในที่เก็บXXXคุณต้องระบุ--followและพา ธ ที่ไม่มีการแก้ไข หากคุณเปลี่ยนแปลงในที่เก็บทั้งสองแสดงว่าคุณมี 2 คำสั่งซึ่งไม่แสดงการเปลี่ยนแปลงทั้งหมด

รุ่น Git ก่อนที่ 2.9 : คุณไม่จำเป็นต้องผ่านการเลือกที่จะ--allow-unrelated-historiesgit merge

วิธีการในคำตอบอื่น ๆ ที่ใช้read-treeและข้ามmerge -s oursขั้นตอนอย่างมีประสิทธิภาพไม่แตกต่างจากการคัดลอกไฟล์ด้วย cp และยอมรับผลลัพธ์

ต้นฉบับมาจาก"ทรีย่อยผสาน" บทความช่วยเหลือ และอีกลิงค์ที่มีประโยชน์


9
ดูเหมือนว่ามันจะไม่ได้เก็บประวัติไว้ ... ถ้าฉันทำgit logไฟล์ใด ๆ ที่ฉันดึงเข้าไปฉันก็เห็นว่ามีการคอมมิชชันเดียวและไม่มีอะไรจากชีวิตก่อนหน้านี้ใน repo อื่น ๆ Git 1.8.0
Anentropic

8
AHA! ถ้าผมใช้เส้นทางเก่าของไฟล์ที่นำเข้าคืองด subdir ก็ถูกนำเข้ามาแล้วเข้าสู่ระบบคอมไพล์จะให้ฉันกระทำประวัติศาสตร์เช่นgit log -- myfileแทนที่จะgit log -- rack/myfile
Anentropic

2
@FrancescoFrassinelli ไม่เป็นที่ต้องการใช่ไหม การนำประวัติมาเป็นคุณลักษณะของวิธีนี้
patrickvacek

4
@FrancescoFrassinelli หากคุณไม่ต้องการประวัติทำไมไม่ทำสำเนาปกติ? ฉันกำลังพยายามหาวิธีที่จะนำคุณเข้าสู่วิธีการนี้หากไม่ใช่สำหรับประวัติ - นั่นเป็นเหตุผลเดียวที่ฉันใช้วิธีนี้!
patrickvacek

7
ตั้งแต่ Git 2.9 คุณต้องมีตัวเลือก--allow-unrelated-historiesเมื่อทำการผสาน
stuXnet

112

git-subtreeเป็นสคริปต์ที่ออกแบบมาสำหรับกรณีการใช้งานนี้อย่างสมบูรณ์ในการรวมหลาย ๆ ที่เก็บไว้ในที่เดียวในขณะที่รักษาประวัติ (และ / หรือแยกประวัติของ subtrees แม้ว่าที่ดูเหมือนจะไม่เกี่ยวข้องกับคำถามนี้) มันมีการกระจายเป็นส่วนหนึ่งของต้นไม้คอมไพล์ตั้งแต่รุ่น 1.7.11

ในการรวมที่เก็บ<repo>ที่การแก้ไข<rev>เป็นไดเรกทอรีย่อย<prefix>ให้ใช้git subtree addดังนี้:

git subtree add -P <prefix> <repo> <rev>

git-subtree ใช้กลยุทธ์การรวมทรีย่อยในลักษณะที่เป็นมิตรกับผู้ใช้มากขึ้น

สำหรับกรณีของคุณภายในที่เก็บ YYY คุณต้องเรียกใช้:

git subtree add -P ZZZ /path/to/XXX.git master

ข้อเสียก็คือว่าในประวัติศาสตร์ที่ผสานไฟล์ที่อยู่ unprefixed (ไม่อยู่ในไดเรกทอรีย่อย) ผลลัพธ์git log ZZZ/aจะแสดงการเปลี่ยนแปลงทั้งหมด (ถ้ามี) ยกเว้นการเปลี่ยนแปลงในประวัติการผสาน คุณทำได้:

git log --follow -- a

แต่นั่นจะไม่แสดงการเปลี่ยนแปลงอื่น ๆ ในประวัติศาสตร์ที่ถูกผสาน

กล่าวอีกนัยหนึ่งถ้าคุณไม่เปลี่ยนZZZไฟล์ในที่เก็บXXXคุณต้องระบุ--followและพา ธ ที่ไม่มีการแก้ไข หากคุณเปลี่ยนแปลงในที่เก็บทั้งสองแสดงว่าคุณมี 2 คำสั่งซึ่งไม่แสดงการเปลี่ยนแปลงทั้งหมด

เพิ่มเติมเกี่ยวกับมันนี่


4
หากคุณมีไดเรกทอรีที่จะรวมแทนที่จะเป็นพื้นที่เก็บข้อมูลเปล่าหรือห่างไกลgit subtree add -P name-of-desired-prefix ~/location/of/git/repo-without-.git branch-name
Tatsh

2
ประสบการณ์ Noob: git (รุ่น 2.9.0.windows.1) ตอบสนอง "ร้ายแรง: อาร์กิวเมนต์ที่คลุมเครือ 'HEAD': การแก้ไขที่ไม่รู้จักหรือเส้นทางที่ไม่ได้อยู่ในแผนผังการทำงาน" เมื่อฉันลองสิ่งนี้ในพื้นที่เก็บข้อมูลที่เพิ่งเปิดใหม่ในพื้นที่ แต่มันก็ทำงานได้ดีหลังจากที่ฉันจริงๆมีพื้นที่เก็บข้อมูลใหม่ไปคือหลังจากเพิ่มไฟล์ธรรมดาและกระทำด้วยวิธีปกติ
สไตน์

ทำงานได้อย่างสวยงามสำหรับสถานการณ์ของฉัน
Johnny Utahh

โอ้นี่วิเศษมาก
dwjohnston

ฉันใช้ @Tatsh ข้อเสนอแนะและใช้งานได้สำหรับฉัน
Carmine Tambascia

49

มีอินสแตนซ์ที่รู้จักกันดีของสิ่งนี้ในที่เก็บ Git เองซึ่งเป็นที่รู้จักกันในชุมชน Git ว่า " การรวมที่ยอดเยี่ยมที่สุดที่เคยมี " (หลังจากหัวเรื่องบรรทัด Linus Torvalds ที่ใช้ในอีเมลไปยัง Git mailinglist ซึ่งอธิบายสิ่งนี้ ผสาน) ในกรณีนี้gitkGit GUI ซึ่งขณะนี้เป็นส่วนหนึ่งของ Git เหมาะสมจริง ๆ แล้วเคยเป็นโครงการแยกต่างหาก ไลนัสจัดการรวมที่เก็บนั้นเข้ากับที่เก็บ Git ด้วยวิธีการนั้น

  • มันจะปรากฏในพื้นที่เก็บข้อมูล Git ราวกับว่ามันได้รับการพัฒนาเป็นส่วนหนึ่งของ Git เสมอ
  • ประวัติทั้งหมดจะถูกเก็บไว้เหมือนเดิมและ
  • มันยังคงสามารถพัฒนาได้อย่างอิสระในพื้นที่เก็บข้อมูลเก่าที่มีการเปลี่ยนแปลงเพียงแค่ถูกgit pulled

อีเมลมีขั้นตอนที่จำเป็นในการทำซ้ำ แต่มันไม่ได้เกิดขึ้นเพราะใจคนแรก: ไลนัสเขียน Git ดังนั้นเขาอาจจะรู้มากกว่าคุณหรือฉันเล็กน้อยและที่สองนี่เกือบ 5 ปีที่แล้ว และ Git ได้รับการปรับปรุงอย่างมากตั้งแต่นั้นมาดังนั้นบางทีตอนนี้ก็อาจจะง่ายขึ้นมาก

โดยเฉพาะอย่างยิ่งฉันเดาว่าทุกวันนี้จะใช้ gitk submodule ในกรณีเฉพาะนั้น


3
BTW กลยุทธ์ที่ใช้สำหรับการรวมภายหลัง (ถ้ามี) เรียกว่าsubtree merge และมีgit-subtreeเครื่องมือของบุคคลที่สามที่สามารถช่วยคุณได้: github.com/apenwarr/git-subtree
Jakub Narębski

ขอบคุณฉันลืมไปแล้ว subtreeกลยุทธ์การผสานโดยเฉพาะอย่างยิ่งในการร่วมกับgit-subtreeเครื่องมือที่เป็นคนดีบางทีทางเลือกที่เหนือกว่าแม้จะ submodules
Jörg W Mittag

12

วิธีง่ายๆในการทำเช่นนั้นคือการใช้ git format-patch

สมมติว่าเรามี 2 คอมไพล์ที่เก็บfooและบาร์

fooประกอบด้วย:

  • foo.txt
  • .git

แถบประกอบด้วย:

  • bar.txt
  • .git

และเราต้องการจบลงด้วยfooที่มีประวัติบาร์และไฟล์เหล่านี้:

  • foo.txt
  • .git
  • foobar / bar.txt

ดังนั้นในการทำเช่นนั้น:

 1. create a temporary directory eg PATH_YOU_WANT/patch-bar
 2. go in bar directory
 3. git format-patch --root HEAD --no-stat -o PATH_YOU_WANT/patch-bar --src-prefix=a/foobar/ --dst-prefix=b/foobar/
 4. go in foo directory
 5. git am PATH_YOU_WANT/patch-bar/*

และถ้าเราต้องการที่จะเขียนข้อความทั้งหมดที่กระทำจากแถบที่เราสามารถทำได้เช่นบน Linux:

git filter-branch --msg-filter 'sed "1s/^/\[bar\] /"' COMMIT_SHA1_OF_THE_PARENT_OF_THE_FIRST_BAR_COMMIT..HEAD

สิ่งนี้จะเพิ่ม "[แถบ]" ที่จุดเริ่มต้นของข้อความยืนยันแต่ละข้อความ


หากพื้นที่เก็บข้อมูลดั้งเดิมมีสาขาและการรวมgit amอาจจะล้มเหลว
Adam Monsen

1
ผู้เยาว์ gotcha: git am จะแยกอะไร[ ]จากข้อความคอมมิท ดังนั้นคุณควรใช้เครื่องหมายที่แตกต่างจาก[bar]
HRJ

ไม่ได้ผลสำหรับฉัน ได้รับ "ข้อผิดพลาด: foobar / mySubDir / test_host1: ไม่มีอยู่ในดัชนีสำเนาของแพตช์ที่ล้มเหลวพบได้ใน: /home/myuser/src/proj/.git/rebase-apply/patch เมื่อคุณแก้ไขปัญหานี้แล้ว เรียกใช้ "git am - ยุติ" นี่คือหลังจากใช้ 11 แพทช์ (จาก 60)
oligofren

1
บล็อกนี้มีคำตอบคล้ายกับคำถามที่แตกต่างกันเล็กน้อย (ย้ายเฉพาะไฟล์ที่เลือก)
Jesse Glick

ฉันเห็นข้อเสียเดียวการคอมมิททั้งหมดถูกเพิ่มเข้ากับ HEAD ของที่เก็บเป้าหมาย
CSchulz

8

ฟังก์ชั่นนี้จะโคลน repo ระยะไกลเข้าไปใน repo dir ท้องถิ่นหลังจากรวมการกระทำทั้งหมดจะถูกบันทึกไว้git logจะแสดงการกระทำเดิมและเส้นทางที่เหมาะสม:

function git-add-repo
{
    repo="$1"
    dir="$(echo "$2" | sed 's/\/$//')"
    path="$(pwd)"

    tmp="$(mktemp -d)"
    remote="$(echo "$tmp" | sed 's/\///g'| sed 's/\./_/g')"

    git clone "$repo" "$tmp"
    cd "$tmp"

    git filter-branch --index-filter '
        git ls-files -s |
        sed "s,\t,&'"$dir"'/," |
        GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
        mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
    ' HEAD

    cd "$path"
    git remote add -f "$remote" "file://$tmp/.git"
    git pull "$remote/master"
    git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
    git remote remove "$remote"
    rm -rf "$tmp"
}

วิธีใช้:

cd current/package
git-add-repo https://github.com/example/example dir/to/save

หากทำการเปลี่ยนแปลงเพียงเล็กน้อยคุณสามารถย้ายไฟล์ / dirs ของ repo ที่ผสานไปยังพา ธ อื่นเช่น:

repo="https://github.com/example/example"
path="$(pwd)"

tmp="$(mktemp -d)"
remote="$(echo "$tmp" | sed 's/\///g' | sed 's/\./_/g')"

git clone "$repo" "$tmp"
cd "$tmp"

GIT_ADD_STORED=""

function git-mv-store
{
    from="$(echo "$1" | sed 's/\./\\./')"
    to="$(echo "$2" | sed 's/\./\\./')"

    GIT_ADD_STORED+='s,\t'"$from"',\t'"$to"',;'
}

# NOTICE! This paths used for example! Use yours instead!
git-mv-store 'public/index.php' 'public/admin.php'
git-mv-store 'public/data' 'public/x/_data'
git-mv-store 'public/.htaccess' '.htaccess'
git-mv-store 'core/config' 'config/config'
git-mv-store 'core/defines.php' 'defines/defines.php'
git-mv-store 'README.md' 'doc/README.md'
git-mv-store '.gitignore' 'unneeded/.gitignore'

git filter-branch --index-filter '
    git ls-files -s |
    sed "'"$GIT_ADD_STORED"'" |
    GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
    mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
' HEAD

GIT_ADD_STORED=""

cd "$path"
git remote add -f "$remote" "file://$tmp/.git"
git pull "$remote/master"
git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
git remote remove "$remote"
rm -rf "$tmp"

ประกาศ
พา ธ แทนที่ด้วยsedดังนั้นโปรดตรวจสอบให้แน่ใจว่าได้ย้ายในเส้นทางที่เหมาะสมหลังจากรวม พารามิเตอร์มีอยู่เพียงตั้งแต่ Git> = 2.9
--allow-unrelated-histories


2
สำหรับผู้ใช้ OS X ให้ติดตั้งgnu-sedเพื่อให้git-add-repoฟังก์ชั่นใช้งานได้ ขอขอบคุณอีกครั้งอันเดรย์!
ptaylor

7

จากบทความนี้การใช้ทรีย่อยเป็นสิ่งที่ใช้ได้ผลสำหรับฉันและมีการโอนประวัติที่เกี่ยวข้องเท่านั้น การโพสต์ที่นี่ในกรณีที่ทุกคนต้องการขั้นตอน (โปรดแทนที่ตัวยึดตำแหน่งด้วยค่าที่เหมาะสมกับคุณ):

ในที่เก็บซอร์สของคุณแยกโฟลเดอร์ย่อยเป็นสาขาใหม่

git subtree split --prefix=<source-path-to-merge> -b subtree-split-result

ใน repo ปลายทางของคุณรวมในสาขาผลแยก

git remote add merge-source-repo <path-to-your-source-repository>
git fetch merge-source-repo
git merge -s ours --no-commit merge-source-repo/subtree-split-result
git read-tree --prefix=<destination-path-to-merge-into> -u merge-source-repo/subtree-split-result

ตรวจสอบการเปลี่ยนแปลงและกระทำของคุณ

git status
git commit

อย่าลืม

ทำความสะอาดโดยการลบsubtree-split-resultสาขา

git branch -D subtree-split-result

ลบรีโมตที่คุณเพิ่มเพื่อดึงข้อมูลจาก repo ต้นทาง

git remote rm merge-source-repo


3

การเพิ่มคำตอบอีกครั้งเนื่องจากฉันคิดว่านี่ง่ายกว่าเล็กน้อย การดึงของ repo_dest จะทำใน repo_to_import และจากนั้นจะทำการ push - set-upstream url: repo_dest master

วิธีนี้ได้ผลสำหรับฉันที่จะนำเข้า repos ที่มีขนาดเล็กลงหลายอันไปสู่อันที่ใหญ่กว่า

วิธีนำเข้า: repo1_to_import ไปยัง repo_dest

# checkout your repo1_to_import if you don't have it already 
git clone url:repo1_to_import repo1_to_import
cd repo1_to_import

# now. pull all of repo_dest
git pull url:repo_dest
ls 
git status # shows Your branch is ahead of 'origin/master' by xx commits.
# now push to repo_dest
git push --set-upstream url:repo_dest master

# repeat for other repositories you want to import

เปลี่ยนชื่อหรือย้ายไฟล์และ dirs ไปยังตำแหน่งที่ต้องการใน repo ดั้งเดิมก่อนที่คุณจะนำเข้า เช่น

cd repo1_to_import
mkdir topDir
git add topDir
git mv this that and the other topDir/
git commit -m"move things into topDir in preparation for exporting into new repo"
# now do the pull and push to import

วิธีการอธิบายที่ลิงค์ต่อไปนี้เป็นแรงบันดาลใจคำตอบนี้ ฉันชอบมันเพราะมันดูง่ายขึ้น แต่ระวัง! มีมังกร! https://help.github.com/articles/importing-an-external-git-repository git push --mirror url:repo_destผลักดันประวัติ repo ในพื้นที่ของคุณและสถานะเป็นระยะไกล (url: repo_dest) แต่จะลบประวัติเก่าและสถานะของรีโมต สนุกตามมา! : -E


1

ฉันต้องการนำเข้าเฉพาะบางไฟล์จากที่เก็บอื่น (XXX) ในกรณีของฉัน ทรีย่อยซับซ้อนเกินไปสำหรับฉันและโซลูชันอื่นไม่ทำงาน นี่คือสิ่งที่ฉันทำ:

ALL_COMMITS=$(git log --reverse --pretty=format:%H -- ZZZ | tr '\n' ' ')

นี่จะให้รายการที่คั่นด้วยช่องว่างของการกระทำทั้งหมดที่มีผลต่อไฟล์ที่ฉันต้องการนำเข้า (ZZZ) ในลำดับย้อนกลับ (คุณอาจต้องเพิ่ม - ติดตามเพื่อจับการเปลี่ยนชื่อเช่นกัน) จากนั้นฉันไปที่ที่เก็บเป้าหมาย (YYY) เพิ่มที่เก็บอื่น (XXX) เป็นรีโมตทำการดึงข้อมูลจากมันและสุดท้าย:

git cherry-pick $ALL_COMMITS

ซึ่งจะเพิ่มความมุ่งมั่นทั้งหมดในสาขาของคุณคุณจะมีไฟล์ทั้งหมดที่มีประวัติของพวกเขาและสามารถทำสิ่งที่คุณต้องการกับพวกเขาราวกับว่าพวกเขาเคยอยู่ในพื้นที่เก็บข้อมูลนี้เสมอ


1

ดูตัวอย่างพื้นฐานในบทความนี้และพิจารณาการทำแผนที่บนที่เก็บ:

  • A<-> YYY,
  • B <-> XXX

หลังจากกิจกรรมทั้งหมดที่อธิบายไว้ในบทนี้ (หลังจากการรวม) ลบสาขาB-master:

$ git branch -d B-master

จากนั้นผลักดันการเปลี่ยนแปลง

มันใช้งานได้สำหรับฉัน


0

ฉันอยู่ในสถานการณ์ที่ฉันกำลังมองหา-s theirsแต่แน่นอนว่ากลยุทธ์นี้ไม่มีอยู่จริง ประวัติของฉันคือฉันได้แยกโครงการ GitHub และตอนนี้ด้วยเหตุผลบางอย่างท้องถิ่นของฉันmasterไม่สามารถรวมเข้าด้วยกันได้upstream/masterแม้ว่าฉันจะไม่มีการเปลี่ยนแปลงในสาขานี้ (ไม่รู้จริง ๆ ว่าเกิดอะไรขึ้นที่นั่น - ฉันคิดว่าต้นน้ำทำสิ่งสกปรกดันไปด้านหลังหรือเปล่า?

สิ่งที่ฉันทำคือ

# as per https://help.github.com/articles/syncing-a-fork/
git fetch upstream
git checkout master
git merge upstream/master
....
# Lots of conflicts, ended up just abandonging this approach
git reset --hard   # Ditch failed merge
git checkout upstream/master
# Now in detached state
git branch -d master # !
git checkout -b master   # create new master from upstream/master

ดังนั้นตอนนี้ฉันmasterจะซิงค์อีกครั้งด้วยupstream/master(และคุณสามารถทำซ้ำด้านบนสำหรับสาขาอื่น ๆ ที่คุณต้องการซิงค์ในทำนองเดียวกัน)


1
git reset --hard upstream/masterในท้องถิ่นของคุณmasterสาขาจะทำผลงานได้ ด้วยวิธีนี้คุณจะไม่สูญเสียความกังวลสาขาท้องถิ่น - สิ่งต่าง ๆ เช่นค่าเริ่มต้นต้นน้ำ
tomekwi

0

ฉันสามารถแนะนำวิธีแก้ไขปัญหาอื่น (ทดแทนgit-submodules ) สำหรับปัญหาของคุณ - เครื่องมือgil (ลิงค์ git)

ช่วยให้สามารถอธิบายและจัดการการพึ่งพาที่เก็บคอมไพล์ที่ซับซ้อน

นอกจากนี้ยังให้บริการโซลูชั่นที่จะเป็นปัญหาการพึ่งพา submodules recursive คอมไพล์

พิจารณาว่าคุณมีการพึ่งพาโครงการต่อไปนี้: ตัวอย่างกราฟการพึ่งพาที่เก็บข้อมูล git

จากนั้นคุณสามารถกำหนด.gitlinksไฟล์ที่มีคำอธิบายความสัมพันธ์ที่เก็บ:

# Projects
CppBenchmark CppBenchmark https://github.com/chronoxor/CppBenchmark.git master
CppCommon CppCommon https://github.com/chronoxor/CppCommon.git master
CppLogging CppLogging https://github.com/chronoxor/CppLogging.git master

# Modules
Catch2 modules/Catch2 https://github.com/catchorg/Catch2.git master
cpp-optparse modules/cpp-optparse https://github.com/weisslj/cpp-optparse.git master
fmt modules/fmt https://github.com/fmtlib/fmt.git master
HdrHistogram modules/HdrHistogram https://github.com/HdrHistogram/HdrHistogram_c.git master
zlib modules/zlib https://github.com/madler/zlib.git master

# Scripts
build scripts/build https://github.com/chronoxor/CppBuildScripts.git master
cmake scripts/cmake https://github.com/chronoxor/CppCMakeScripts.git master

แต่ละบรรทัดอธิบายลิงก์ git ในรูปแบบต่อไปนี้:

  1. ชื่อเฉพาะของที่เก็บ
  2. เส้นทางสัมพัทธ์ของที่เก็บ (เริ่มจากเส้นทางของไฟล์. gitlinks)
  3. พื้นที่เก็บข้อมูล Git ซึ่งจะใช้ในคำสั่ง git clone สาขาที่เก็บเพื่อชำระเงิน
  4. บรรทัดว่างหรือบรรทัดที่ขึ้นต้นด้วย # จะไม่ถูกแยกวิเคราะห์ (ถือว่าเป็นความคิดเห็น)

ในที่สุดคุณต้องอัปเดตที่เก็บตัวอย่างรากของคุณ:

# Clone and link all git links dependencies from .gitlinks file
gil clone
gil link

# The same result with a single command
gil update

ดังนั้นคุณจะต้องโคลนโครงการที่จำเป็นทั้งหมดและเชื่อมโยงโครงการเหล่านั้นด้วยวิธีที่เหมาะสม

หากคุณต้องการยอมรับการเปลี่ยนแปลงทั้งหมดในที่เก็บบางแห่งด้วยการเปลี่ยนแปลงทั้งหมดในที่เก็บลิงก์รองคุณสามารถทำได้ด้วยคำสั่งเดียว:

gil commit -a -m "Some big update"

คำสั่ง pull และ push ทำงานในลักษณะเดียวกัน:

gil pull
gil push

เครื่องมือ Gil (ลิงก์ git) รองรับคำสั่งต่อไปนี้:

usage: gil command arguments
Supported commands:
    help - show this help
    context - command will show the current git link context of the current directory
    clone - clone all repositories that are missed in the current context
    link - link all repositories that are missed in the current context
    update - clone and link in a single operation
    pull - pull all repositories in the current directory
    push - push all repositories in the current directory
    commit - commit all repositories in the current directory

เพิ่มเติมเกี่ยวกับปัญหาการพึ่งพา submodules recursive คอมไพล์


0

ให้ฉันใช้ชื่อa(แทนXXXและZZZ) และb(แทนที่YYY) เนื่องจากนั่นทำให้การอ่านง่ายขึ้น

สมมติว่าคุณต้องการรวมพื้นที่เก็บข้อมูลaเข้าด้วยกันb(ฉันสมมติว่ามันอยู่ติดกัน):

cd a
git filter-repo --to-subdirectory-filter a
cd ..
cd b
git remote add a ../a
git fetch a
git merge --allow-unrelated-histories a/master
git remote remove a

สำหรับนี้คุณจำเป็นต้องgit-filter-repoติดตั้ง ( filter-branchเป็นกำลังใจ )

ตัวอย่างของการรวม 2 ที่เก็บขนาดใหญ่โดยใส่หนึ่งในนั้นไว้ในไดเรกทอรีย่อย: https://gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731

เพิ่มเติมเกี่ยวกับมันนี่


-1

ฉันไม่รู้วิธีง่ายๆในการทำเช่นนั้น คุณสามารถทำสิ่งนี้:

  1. ใช้ git filter-branch เพื่อเพิ่ม ZZZ super-directory บนที่เก็บ XXX
  2. ผลักสาขาใหม่ไปยังที่เก็บ YYY
  3. ผสานกิ่งที่ถูกผลักเข้ากับลำตัวของ YYY

ฉันสามารถแก้ไขด้วยรายละเอียดหากฟังดูน่าสนใจ


-2

ฉันคิดว่าคุณสามารถทำได้โดยใช้ 'git mv' และ 'git pull'

ฉันเป็น noob คอมไพล์ที่เป็นธรรม - ดังนั้นควรระมัดระวังกับพื้นที่เก็บข้อมูลหลักของคุณ - แต่ฉันแค่ลองมันใน temp dir และดูเหมือนว่าจะทำงาน

ก่อน - เปลี่ยนชื่อโครงสร้างของ XXX เพื่อให้ตรงกับวิธีที่คุณต้องการให้ดูเมื่ออยู่ใน YYY:

cd XXX
mkdir tmp
git mv ZZZ tmp/ZZZ
git mv tmp ZZZ

ตอนนี้ XXX มีลักษณะเช่นนี้:

XXX
 |- ZZZ
     |- ZZZ

ตอนนี้ใช้ 'git pull' เพื่อดึงข้อมูลการเปลี่ยนแปลง:

cd ../YYY
git pull ../XXX

ตอนนี้ YYY มีลักษณะเช่นนี้:

YYY
 |- ZZZ
     |- ZZZ
 |- (other folders that already were in YYY)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.