Gibt es in git eine einfache Möglichkeit, einen nicht verwandten Zweig in ein Repository einzuführen?


328

Während ich heute einem Freund mit einem Git-Problem half, musste ich einen Zweig einführen, der völlig vom masterZweig getrennt sein musste. Der Inhalt dieses Zweigs hatte wirklich einen anderen Ursprung als der, der auf dem masterZweig entwickelt worden war, aber sie sollten masterzu einem späteren Zeitpunkt in den Zweig integriert werden.

Ich erinnerte mich, als ich John Wiegleys Git von unten nach oben las , wie Zweige im Wesentlichen eine Bezeichnung für ein Commit sind, das einer bestimmten Konvention folgt, und wie ein Commit an einen Baum von Dateien und optional an übergeordnete Commits gebunden ist. Wir haben ein übergeordnetes Commit für das vorhandene Repository mithilfe der Installation von git erstellt:

Also haben wir alle Dateien im Index entfernt ...

$ git rm -rf .

... Verzeichnisse und Dateien aus einem Tarball extrahiert, diese dem Index hinzugefügt ...

$ git add .

... und ein Baumobjekt erstellt ...

$ git write-tree

( git-write-treeerzählte uns die sha1sum des erstellten Baumobjekts.)

Dann haben wir den Baum festgeschrieben, ohne übergeordnete Festschreibungen anzugeben ...

$ echo "Imported project foo" | git commit-tree $TREE

( git-commit-treehat uns die sha1sum des erstellten Commit-Objekts mitgeteilt.)

... und einen neuen Zweig erstellt, der auf unser neu erstelltes Commit verweist.

$ git update-ref refs/heads/other-branch $COMMIT

Schließlich kehrten wir in die masterNiederlassung zurück, um dort weiterzuarbeiten.

$ git checkout -f master

Dies scheint wie geplant funktioniert zu haben. Aber dies ist eindeutig nicht die Art von Verfahren, die ich jemandem empfehlen würde, der gerade erst anfängt, Git zu verwenden, um es milde auszudrücken. Gibt es eine einfachere Möglichkeit, einen neuen Zweig zu erstellen, der in keiner Beziehung zu allem steht, was bisher im Repository passiert ist?

Antworten:


510

Es gibt eine neue Funktion (seit V1.7.2), die diese Aufgabe etwas übergeordneter macht als die anderen Antworten.

git checkoutunterstützt jetzt die --orphanOption. Von der Manpage :

git checkout [-q] [-f] [-m] --orphan <new_branch> [<start_point>]

Erstellen Sie einen neuen verwaisten Zweig mit dem Namen <new_branch>, der von <start_point> aus gestartet wurde, und wechseln Sie zu diesem Zweig. Das erste Commit für diesen neuen Zweig hat keine Eltern und ist die Wurzel einer neuen Geschichte, die von allen anderen Zweigen und Commits völlig getrennt ist.

Dies macht nicht genau das , was der Fragesteller wollte, da es den Index und den Arbeitsbaum ausfüllt <start_point>(da dies schließlich ein Checkout-Befehl ist). Die einzige andere Aktion, die erforderlich ist, besteht darin, unerwünschte Elemente aus dem Arbeitsbaum und dem Index zu entfernen. Funktioniert leider git reset --hardnicht, git rm -rf .kann aber stattdessen verwendet werden (ich glaube, dies entspricht den rm .git/index; git clean -fdxAngaben in anderen Antworten).


Zusammenfassend:

git checkout --orphan newbranch
git rm -rf .
<do work>
git add your files
git commit -m 'Initial commit'

Ich habe <start_point>nicht angegeben, weil es standardmäßig HEAD ist, und es ist uns sowieso egal. Diese Sequenz macht im Wesentlichen dasselbe wie die Befehlssequenz in Artems Antwort , nur ohne auf beängstigende Installationsbefehle zurückzugreifen.


Wissen Sie, ob es möglich ist, einen verwaisten Zweig zu erstellen, der sichtbar bleibt, wenn Sie einen Zweig aus dem anderen "Baum" auschecken?
JJD

1
@JJD: Ich denke, was Sie wollen, ist Git Merge. ( git checkout master && git merge --no-commit "orphan-branch" ) Einige ähnliche Tricks funktionieren mit Git-Reset oder dem Spielen mit dem Index. Dies hängt jedoch von Ihrem gewünschten Workflow ab.
Phord

@ Matthew Hier ist das Changelog für Git 1.7.2 .
JJD

@phord Ich dachte mehr oder weniger daran, dass die Waise Dinge wie Komponententests oder Dokumentation enthält, die vom Quellcode des Projekts getrennt sind.
JJD

2
@JJD: Das Skript, das ich gegeben habe, sollte dir geben, was du willst, es sei denn, ich habe dich missverstanden. Wenn Sie "bleibt sichtbar" sagten, meinen Sie damit, dass die Dateien im Arbeitsverzeichnis bleiben, obwohl Sie einen anderen Zweig ausgecheckt haben? Mit --no-commiton git mergewird dies erreicht. Möglicherweise müssen git reset origin/masterSie eine nachverfolgen, damit Ihr nächstes Commit dort abläuft, wo Sie möchten. Die Dateien aus Ihrem Orphan-Zweig werden jedoch als "nicht verfolgte Dateien" angezeigt, sofern Sie sie nicht auch in Ihre .gitignore-Datei aufnehmen.
Phord

32

Aus dem Git Community Book :

git symbolic-ref HEAD refs/heads/newbranch 
rm .git/index 
git clean -fdx 
<do work> 
git add your files 
git commit -m 'Initial commit'

1
Die nächste Antwort ist besser für moderne Versionen von Git.
Kikito

14
@kikito: Re: "Nächste Antwort ist besser" ... Die Bestellung hier auf SO ist nicht stabil. Könnten Sie einen Link hinzufügen, der auf eine Ihrer Meinung nach bessere Antwort verweist?
David J.

2
Ich bezog mich auf stackoverflow.com/a/4288660/312586 . Sie haben Recht, ich hätte nicht "weiter" sagen sollen. Es hatte weniger Punktzahl als diese Antwort, als ich kommentierte.
Kikito

1
rm .git/indexist hässlich :-)
Ciro Santilli 法轮功 冠状 病 六四 事件 28

Dieser funktioniert besser für den Fall "Ich möchte mich für den Zweig
festlegen

22

Obwohl die Lösung mit git symbolic-refund das Entfernen von Index funktioniert, ist es möglicherweise konzeptionell sauberer , ein neues Repository zu erstellen

$ cd /path/to/unrelated
$ git init
[edit and add files]
$ git add .
$ git commit -m "Initial commit of unrelated"
[master (root-commit) 2a665f6] Initial commit of unrelated
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 foo

dann holen Sie daraus

$ cd /path/to/repo
$ git fetch /path/to/unrelated master:unrelated-branch
warning: no common commits
remote: Counting objects: 3, done.
Unpacking objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
From /path/to/unrelated
 * [new branch]      master     -> unrelated-branch

Jetzt können Sie / path / to / ohne Bezug löschen


Meiner Meinung nach würde ein sauberes Konzept eine Option auf git branchoder beinhalten git checkout. Ich bin froh, dass Git so etwas möglich macht, aber warum sollte es nicht einfacher sein?
Hillu

3
Weil es eine seltene Sache ist und sein sollte. Wenn Sie einen nicht verwandten Zweig haben, gehört dieser normalerweise zum nicht verwandten Repository, anstatt ihn in einen vorhandenen zu stopfen (obwohl es Ausnahmen gibt).
Jakub Narębski

1
+1. Für andere Git-Neulinge können Sie anscheinend dann die Zweige über auflisten git branchund zwischen ihnen über wechseln git checkout BRANCH_NAME.
akavel

Danke dafür, ich hätte nicht alleine darüber nachgedacht. Dies ist besonders hilfreich, da der Verlauf (falls vorhanden) für das andere Repo beibehalten wird.
BM5k

13

Github verfügt über eine Funktion namens Projektseiten, mit der Sie einen bestimmten benannten Zweig in Ihrem Projekt erstellen können, um Dateien bereitzustellen, die von Github bereitgestellt werden. Ihre Anweisungen lauten wie folgt:

$ cd /path/to/fancypants
$ git symbolic-ref HEAD refs/heads/gh-pages
$ rm .git/index
$ git clean -fdx

Von dort aus haben Sie ein leeres Repository, zu dem Sie dann Ihren neuen Inhalt hinzufügen können.


10

Die aktuell ausgewählte Antwort ist richtig, ich würde das nur zufällig hinzufügen ...

Genau so können Benutzer mit github.com Github-Seiten für ihre Repos über einen verwaisten Zweig namens erstellen gh-pages. Die schönen Schritte werden hier gegeben und erklärt:

https://help.github.com/articles/creating-project-pages-manually

Grundsätzlich lauten die git-Befehle zum Einrichten wie folgt:

  1. git checkout --orphan gh-pages (Erstellen Sie einen übergeordneten Zweig namens gh-pages in Ihrem Repo.)
  2. git rm -rf . (Entfernt alle Dateien aus dem Zweigbaum)
  3. rm '.gitignore' (sogar der Gitignore)
  4. Fügen Sie nun den Inhalt der Website hinzu (fügen Sie index.html usw. hinzu) und legen Sie fest und drücken Sie.
  5. Profitieren.

Beachten Sie, dass Sie auch einen / docs-Ordner festlegen können Sie in Ihrem Repo als Quelle für die "Project Site" festlegen können, die Github zum Erstellen der Website verwendet.

Hoffe das hilft!


3

Manchmal möchte ich einfach sofort einen leeren Zweig im Projekt erstellen und dann mit der Arbeit beginnen. Ich werde nur den folgenden Befehl ausführen:

git checkout --orphan unrelated.branch.name
git rm --cached -r .
echo "init unrelated branch" > README.md
git add README.md
git commit -m "init unrelated branch"

1

Wenn Ihr vorhandener Inhalt bereits festgeschrieben wurde, können Sie ihn jetzt (Git 2.18 Q2 2018) in einen eigenen neuen Orphan-Zweig extrahieren, da "git rebase -i --root " aktualisiert wurde, um die Sequenzer-Maschinerie besser nutzen zu können.

Dieser Sequenzer ermöglicht es nun , die gesamte Topologie des Commit-Graphen an eine andere Stelle zu übertragen .

Siehe Commit 8fa6eea , Commit 9c85a1c , Commit ebddf39 , Commit 21d0764 , Commit d87d48b , Commit ba97aea (03. Mai 2018) von Johannes Schindelin ( dscho) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit c5aa4bc , 30. Mai 2018)

Sequenzer: Ermöglicht die Einführung neuer Root-Commits

Im Kontext des neuen --rebase-mergesModus, der speziell entwickelt wurde, um die vorhandene Zweigstellentopologie großzügig zu ändern, möchte ein Benutzer möglicherweise Commits in einen vollständig neuen Zweig extrahieren, der mit einem neu erstellten Root-Commit beginnt .

Dies ist jetzt möglich, indem Sie den Befehl reset [new root]vor dem pickFestschreiben einfügen, das ein Root-Festschreiben werden soll. Beispiel:

reset [new root]
pick 012345 a commit that is about to become a root commit
pick 234567 this commit will have the previous one as parent

Dies steht nicht im Widerspruch zu anderen Verwendungen des resetBefehls, da [new root]es sich nicht um einen gültigen Referenznamen handelt: Sowohl die öffnende Klammer als auch das Leerzeichen sind in Referenznamen unzulässig.


0

Fand dieses Skript unter http://wingolog.org/archives/2008/10/14/merging-in-unrelated-git-branches und es funktioniert sehr gut!

#!/bin/bash

set -e

if test -z "$2" -o -n "$3"; then
    echo "usage: $0 REPO BRANCHNAME" >&2
    exit 1
fi

repo=$1
branch=$2

git fetch "$repo" "$branch"

head=$(git rev-parse HEAD)
fetched=$(git rev-parse FETCH_HEAD)
headref=$(git rev-parse --symbolic-full-name HEAD)

git checkout $fetched .

tree=$(git write-tree)

newhead=$(echo "merged in branch '$branch' from $repo" | git commit-tree $tree -p $head -p $fetched)
git update-ref $headref $newhead $head
git reset --hard $headref

1
Ich glaube, dass der gleiche Effekt mit erzielt werden kann git fetch $REMOTE $REMOTE_BRANCH:$LOCAL_BRANCH. Vermisse ich etwas
Hillu
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.