Domanda

Ho un repository Git in una cartella chiamata XXX, e ho il secondo repository Git chiamato AAAA.

Voglio importare il XXX repository nel AAAA repository come sottodirectory denominata ZZZ e aggiungi tutto XXXcambia la cronologia in AAAA.

Struttura delle cartelle prima:

XXX
 |- .git
 |- (project files)
YYY
 |- .git
 |- (project files)

Struttura delle cartelle dopo:

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

È possibile farlo o devo ricorrere all'utilizzo di sottomoduli?

È stato utile?

Soluzione

Probabilmente il modo più semplice sarebbe quella di tirare la XXX roba in una filiale a YYY e quindi unire in Master:

Nel 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

In realtà ho appena provato questo con un paio di miei pronti contro termine e funziona. A differenza di risposta di Jörg non lascerà si continua a utilizzare l'altra pronti contro termine, ma non credo è stato specificato che in ogni caso.

Nota: Dal momento che questo è stato originariamente scritto nel 2009, git ha aggiunto l'unione sottostruttura menzionato nella risposta qui sotto. Io probabilmente utilizzare questo metodo oggi, anche se naturalmente questo metodo non funziona ancora.

Altri suggerimenti

Se si desidera mantenere la storia esatta commit del secondo repository e quindi anche mantenere la possibilità di unire facilmente le modifiche a monte in futuro, allora qui è il metodo che si desidera. Essa si traduce nella storia non modificata della sottostruttura importato nel tuo repo più un merge commit per spostare il repository risultante dalla concentrazione di sottodirectory.

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."

È possibile tenere traccia delle modifiche a monte in questo modo:

git pull -s subtree XXX_remote master

Git capisce da solo, dove le radici sono prima di fare l'unione, quindi non è necessario specificare il prefisso sulle unioni successive.

versioni di Git prima di 2.9 :. Non è necessario per passare l'opzione --allow-unrelated-histories a git merge

Il metodo in altra risposta che utilizza read-tree e salta la fase merge -s ours è effettivamente differente che la copia dei file con cp e commettendo il risultato.

Fonte originale era di di GitHub "sottostruttura Merge" assistenza .

git-subtree è uno script progettato per esattamente questo caso l'uso di la fusione più repository in uno mantenendo la cronologia (e / o la storia la scissione di sottostrutture, anche se questo è sembra essere irrilevante a questa domanda). E 'distribuito come parte della struttura git dalla release 1.7.11 .

Per unire un <repo> repository alla revisione <rev> come sottodirectory <prefix>, utilizzare git subtree add come segue:

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

git-sottostruttura implementa la strategia di merge sottostruttura in un più user friendly modo.

Per il vostro caso, all'interno di repository YYY, si dovrebbe eseguire:

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

C'è un esempio ben noto di questo nel repository Git stesso, che è collettivamente noto nella comunità Git come " il più cool si fondono mai " (dopo la linea di soggetto Linus Torvalds utilizzato nella e-mail alla mailinglist Git che descrive questa fusione). In questo caso, il gitk Git GUI che ora fa parte del Git corretta, in realtà era un progetto separato. Linus è riuscito a fondere quel repository nel repository Git in un modo che

  • appare nel repository Git, come se fosse stato sempre sviluppato come parte del Git,
  • tutta la storia è mantenuto intatto e
  • può ancora essere sviluppato indipendentemente nel vecchio magazzino, con modifiche semplicemente essere git pulled.

L'e-mail contiene i passaggi necessari per riprodurre, ma non è per i deboli di cuore: in primo luogo, Linus ha scritto Git, quindi probabilmente conosce un po 'più su di esso di te o di me , e in secondo luogo, questo è stato quasi 5 anni fa e Git ha migliorato molto , da allora, quindi forse è ora molto più semplice.

In particolare, credo che al giorno d'oggi si potrebbe usare un modulo gitk, in questo caso specifico.

Il modo più semplice per farlo è quello di utilizzare git format-patch.

Supponiamo di avere 2 repository git foo e bar .

foo contiene:

  • foo.txt
  • .git

bar contiene:

  • bar.txt
  • .git

e vogliamo finire-up con foo che contiene il bar storia e questi file:

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

Quindi, per fare questo:

 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/*

E se vogliamo riscrivere tutti i messaggi impegna da bar che possiamo fare, ad esempio, su Linux:

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

Questo aggiungerà "[bar]" all'inizio di ogni messaggio di commit.

Basato su questo articolo, l'utilizzo di subtree è ciò che ha funzionato per me ed è stata trasferita solo la cronologia applicabile.Pubblica qui nel caso in cui qualcuno abbia bisogno dei passaggi (assicurati di sostituire i segnaposto con valori applicabili a te):

nel tuo repository di origine dividi la sottocartella in un nuovo ramo

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

nel repository di destinazione unisciti nel ramo dei risultati suddivisi

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

verifica le modifiche e conferma

git status
git commit

Non dimenticartelo

Pulisci eliminando il file subtree-split-result ramo

git branch -D subtree-split-result

Rimuovi il telecomando che hai aggiunto per recuperare i dati dal repository di origine

git remote rm merge-source-repo

Questa funzione clonare repo remoto nella dir repo locale, dopo la fusione tutti i commit saranno salvati, git log sarà mostrare i commit originali e percorsi adeguati:

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"
}

Come usare:

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

Se fare un po 'le modifiche si può anche spostare i file / dirs di pronti contro termine incorporata in percorsi diversi, ad esempio:

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"

Annunci
Percorsi sostituisce via sed, in modo da assicurarsi che si trasferisce nei percorsi corretti dopo la fusione.
Il parametro --allow-unrelated-histories esiste solo in quanto git> = 2.9.

L'aggiunta di un'altra risposta, come credo che questo sia un po 'più semplice. Un tiro di repo_dest è fatto in repo_to_import e quindi una spinta --set-monte url:. Repo_dest maestro è fatto

Questo metodo ha funzionato per me l'importazione di diversi pronti contro termine più piccoli in uno più grande.

Come importare: repo1_to_import a 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

rinominare o spostare file e dirs nella posizione desiderata in pronti contro termine originale prima di fare l'importazione. per esempio.

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

Il metodo descritto al seguente link ispirato questa risposta. Mi è piaciuto come sembrava più semplice. Ma attenzione! There Be Dragons! https://help.github.com/articles/importing-an-external -git-repository git push --mirror url:repo_dest spinge la vostra storia repo locale e statale a distanza (url: repo_dest). MA cancella la storia antica e lo stato del telecomando. Ensues divertimento! : -E

ho voluto importare solo alcuni file dall'altro repository (XXX) nel mio caso. Il sotto-albero era troppo complicato per me e le altre soluzioni non ha funzionato. Questo è quello che ho fatto:

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

Questo vi dà un elenco separato da spazi di tutti i commit che interessano i file che ho voluto importare (ZZZ) in ordine inverso (potrebbe essere necessario aggiungere --follow alla cattura rinomina pure). Allora sono andato nella repository di destinazione (YYY), ha aggiunto l'altro repository (XXX) come a distanza, ha un prendere da esso e, infine:

git cherry-pick $ALL_COMMITS

che aggiunge tutti i commit al ramo, avrete quindi avere tutti i file con la loro storia e si può fare quello che vuoi con loro come se fossero sempre stati in questo repository.

mi trovavo in una situazione in cui ero alla ricerca di -s theirs ma, naturalmente, questa strategia non esiste. La mia storia era che avevo biforcuta un progetto su GitHub, e ora per qualche ragione, il mio master locale non potrebbe essere fusa con upstream/master anche se avevo apportate modifiche locali a questo ramo. (Davvero non so cosa sia successo lì? - Credo che a monte avevo fatto alcune spinte sporchi dietro le quinte, forse)

Quello che ho finito per fare era

# 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

Così ora il mio master è di nuovo in sintonia con upstream/master (e si potrebbe ripetere quanto sopra per qualsiasi altro ramo che si desidera anche sincronizzare in modo simile).

Vedere Esempio di base questo articolo e prendere in considerazione tale mappatura sulla repository:

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

Dopo ogni attività descritte in questo capitolo (dopo la fusione), rimuovere B-master ramo:

$ git branch -d B-master

Quindi, spingere modifiche.

Si lavora per me.

posso suggerire un'altra soluzione (in alternativa a git-sottomoduli ) per il vostro problema - gil (collegamenti Git) strumento

Consente di descrivere e gestire complessi repository Git dipendenze.

Inoltre fornisce una soluzione al git ricorsiva sottomoduli dipendenza problema .

Considerate si hanno le seguenti dipendenze del progetto: campione git repository grafico delle dipendenze

Quindi è possibile definire il file con la descrizione .gitlinks repository relazione:

# 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

Ogni riga descrive collegamento git nel seguente formato:

  1. Nome univoco del repository
  2. Percorso relativo del repository (iniziato dal percorso di .gitlinks file)
  3. repository Git che sarà utilizzato al comando clone git ramo Repository alla cassa
  4. linea o linea vuota iniziato con #, non sono analizzati (trattati come commento).

Infine è necessario aggiornare il repository del campione root:

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

# The same result with a single command
gil update

Come risultato si clonare tutti i progetti necessari e collegare gli uni agli altri in modo corretto.

Se si vuole impegnare tutte le modifiche in alcuni repository con tutte le variazioni di bambino legato repository si può fare con un singolo comando:

gil commit -a -m "Some big update"

Pull, i comandi di spinta funziona in modo simile:

gil pull
gil push

Gil (collegamenti Git) strumento supporta i seguenti comandi:

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

Di più su git ricorsiva sottomoduli problema di dipendenza .

Non so di un modo semplice per farlo. Si potrebbe fare questo:

  1. Usa git filter-branch per aggiungere un super-directory ZZZ sul repository XXX
  2. Spingere la nuova filiale al repository YYY
  3. Unisci il ramo spinto con tronco di YYY.

Posso modificare con i dettagli, se questo suona attraente.

Penso che si possa fare questo usando 'git mv' e 'git pull'.

Sono un noob git fiera - quindi state attenti con il repository principale - ma ho appena provato questo in una directory temp e sembra funzionare

.

In primo luogo - rinominare la struttura del XXX da abbinare come si desidera guardare quando è all'interno di YYY:

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

Ora XXX si presenta così:

XXX
 |- ZZZ
     |- ZZZ

Ora usare 'git pull' per andare a prendere i cambiamenti attraverso:

cd ../YYY
git pull ../XXX

Ora YYY si presenta così:

YYY
 |- ZZZ
     |- ZZZ
 |- (other folders that already were in YYY)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top