Checkout-Unterverzeichnisse in Git?


160

Ist es möglich, Unterverzeichnisse eines Repositorys in Git auszuchecken?

Stellen Sie sich vor, ich richte eine neue WordPress-Installation ein. Ich werde zwei neue Verzeichnisse für meine Plugin- und Theme-Anpassung erstellen:

  • wordpress/wp-content/plugins/myplugins/
  • wordpress/wp-content/themes/mytheme/

Ich möchte diese Verzeichnisse über Git pflegen. In Subversion, würde ich dies erreichen , indem er trunk/myplugins/und trunk/mytheme/Verzeichnisse und Unterverzeichnisse heraus überprüfen. Hat Git eine Möglichkeit, dieselbe Aufgabe mit einem einzigen Repository zu erledigen?

Ich könnte das Boot in einem Git-Paradigma einfach vermissen, als langjähriger SVN-Benutzer mit wenig Kontakt zu Git.

Bearbeiten: Mehrere Zweige, in denen unterschiedliche Inhalte gespeichert sind, sind eine interessante Möglichkeit, damit umzugehen.


2
Warum checken Sie nicht das gesamte Repo aus und stellen einen symbolischen Link zu den Unterverzeichnissen her, mit denen Sie arbeiten möchten?
Zufälligkeit2077



Ist es möglich, spärliches Auschecken und Referenzieren des Git-Repositorys durchzuführen?
luka5z

Antworten:


121

Sparse Checkout gibt es jetzt in Git 1.7 .

Siehe auch die Frage „ Ist es möglich, eine spärliche Prüfung durchzuführen, ohne zuerst das gesamte Repository auszuchecken? ”.

Beachten Sie, dass Sie bei spärlichen Kassen immer noch das gesamte Repository herunterladen müssen , auch wenn einige der von Git heruntergeladenen Dateien nicht in Ihrem Arbeitsbaum landen.


1
Wo de git cloneeinfacher Befehl? Nun, ich benutze diese Antwort und arbeite!
Peter Krauss

4
Und gibt es eine Möglichkeit, diese Ordner umzubenennen? Wenn ich nur spärlich auschecke /foo/bar/foobar, kann ich es nur wie /foobarin meinem lokalen Repository sehen?
Graywolf

17

Es gibt keinen wirklichen Weg, dies in Git zu tun. Und wenn Sie nicht als einzelne Arbeitseinheit Änderungen vornehmen, die beide Bäume gleichzeitig betreffen, gibt es keinen guten Grund, für beide ein einziges Repository zu verwenden. Ich dachte, ich würde diese Subversion-Funktion vermissen, aber ich stellte fest, dass das Erstellen von Repositorys so wenig Verwaltungsaufwand verursacht (einfach aufgrund der Tatsache, dass Repositorys direkt neben ihrer Arbeitskopie gespeichert werden, anstatt dass ich explizit einen Ort außerhalb von auswählen muss Arbeitskopie), an die ich mich gewöhnt habe, nur viele kleine Einzweck-Repositories zu erstellen.

Wenn Sie darauf bestehen (oder es wirklich brauchen), können Sie ein Git-Repository mit just mythemeund mypluginsVerzeichnissen erstellen und diese aus der WordPress-Installation heraus verknüpfen.


MDCore schrieb:

Wenn Sie sich beispielsweise auf mytheme festlegen, wird die Revisionsnummer für myplugin erhöht

Beachten Sie, dass dies für git kein Problem darstellt, wenn Sie sich entscheiden, beide Verzeichnisse in einem einzigen Repository abzulegen, da git das Konzept der monoton steigenden Versionsnummern jeglicher Form vollständig aufhebt.

Das einzige Kriterium für die Zusammenstellung von Dingen in einem einzigen Repository in Git ist, ob es sich um eine einzelne Einheit handelt, d. H. in Ihrem Fall, ob es Änderungen gibt, bei denen es nicht sinnvoll ist, die Änderungen in jedem Verzeichnis isoliert zu betrachten. Wenn Sie Änderungen haben, bei denen Sie Dateien in beiden Verzeichnissen gleichzeitig bearbeiten müssen und die Änderungen zusammengehören, sollten sie ein Repository sein. Wenn nicht, dann glom sie nicht zusammen.

Git möchte wirklich, dass Sie separate Repositorys für separate Entitäten verwenden.

Submodule

Submodule erfüllen nicht den Wunsch, beide Verzeichnisse in einem Repository zu behalten, da sie tatsächlich ein separates Repository für jedes Verzeichnis erzwingen würden , das dann mithilfe von Submodulen in einem anderen Repository zusammengeführt wird. Schlimmer noch, da die Verzeichnisse in der WordPress-Installation keine direkten Unterverzeichnisse desselben Verzeichnisses sind und auch Teil einer Hierarchie mit vielen anderen Dateien sind, würde die Verwendung der Verzeichnis-Repositorys als Submodule in einem einheitlichen Repository keinerlei Vorteile bieten, da die einheitlichen Das Repository würde keinen Anwendungsfall / Bedarf widerspiegeln.


Wo de git cloneeinfache Befehlsfolge? Nun, ich benutze diese Antwort und arbeite!
Peter Krauss

16

Eine Sache, die ich an spärlichen Checkouts nicht mag, ist, dass Ihre Verzeichnisstruktur alle Verzeichnisse enthalten muss, die dazu führen, wenn Sie ein Unterverzeichnis auschecken möchten, das einige Verzeichnisse tief ist.

Um dies zu umgehen, klone ich das Repo an einem Ort, der nicht mein Arbeitsbereich ist, und erstelle dann in meinem Arbeitsbereichsverzeichnis einen symbolischen Link zum Unterverzeichnis im Repository. Git funktioniert so ganz gut, weil Dinge wie der Git-Status die Änderungsdateien relativ zu Ihrem aktuellen Arbeitsverzeichnis anzeigen.


Dies funktioniert nur in einem Betriebssystem, das symbolische Links unterstützt. Sie müssen die Funktionsweise spärlicher Kassen ändern.
Anders Lindén

1
+1 für die Idee mit einem symbolischen Link im ausgecheckten Verzeichnis. Eine spärliche Kaufabwicklung und ein symbolischer Link schließen sich jedoch nicht gegenseitig aus: Sie benötigen keinen vollwertigen Klon.
Apitsch

10

Tatsächlich befinden sich "enge" oder "teilweise" oder "spärliche" Kassen derzeit in einer starken Entwicklung für Git. Beachten Sie, dass Sie immer noch das vollständige Repository unter haben .git. Die anderen beiden Beiträge sind für den aktuellen Status von Git aktuell, aber es sieht so aus, als könnten wir irgendwann spärliche Kassen durchführen. Überprüfen Sie die Mailinglisten, wenn Sie an weiteren Details interessiert sind - sie ändern sich schnell.


Gut zu wissen! Ich mag es, solche eng verwandten Verzeichnisse unter einem Repository zu haben, und würde es tun, wenn es überhaupt möglich ist.
Annika Backstrom

5

git clone --filter ab Git 2.19

Diese Option überspringt das Abrufen nicht benötigter Objekte vom Server:

git clone --depth 1 --no-checkout --filter=blob:none \
  "file://$(pwd)/server_repo" local_repo
cd local_repo
git checkout master -- mdir/

Der Server sollte konfiguriert sein mit:

git config --local uploadpack.allowfilter 1
git config --local uploadpack.allowanysha1inwant 1

Ab Version 2.19.0 gibt es keine Serverunterstützung, diese kann jedoch bereits lokal getestet werden.

file://$(path)ist erforderlich, um git cloneProtokoll-Spielereien zu überwinden : Wie kann ein lokales Git-Repository mit einem relativen Pfad flach geklont werden ?

Denken Sie daran, dass dies --depth 1bereits impliziert --single-branch, siehe auch: Wie klone ich einen einzelnen Zweig in Git?

TODO: --filter=blob:noneÜberspringt alle Blobs, ruft aber trotzdem alle Baumobjekte ab. Bei einem normalen Repo sollte dies jedoch im Vergleich zu den Dateien selbst winzig sein, sodass dies bereits gut genug ist. Gefragt unter: https://www.spinics.net/lists/git/msg342006.html Entwickler antworteten --filter=tree:0, dass dies in Arbeit ist.

Das Format von --filterist am dokumentiert man git-rev-list.

Das Git-Remote-Protokoll wurde erweitert, um diese Funktion zu unterstützen.

Dokumente auf Git-Baum:

Probieren Sie es aus

#!/usr/bin/env bash
set -eu

list-objects() (
  git rev-list --all --objects
  echo "master commit SHA: $(git log -1 --format="%H")"
  echo "mybranch commit SHA: $(git log -1 --format="%H")"
  git ls-tree master
  git ls-tree mybranch | grep mybranch
  git ls-tree master~ | grep root
)

# Reproducibility.
export GIT_COMMITTER_NAME='a'
export GIT_COMMITTER_EMAIL='a'
export GIT_AUTHOR_NAME='a'
export GIT_AUTHOR_EMAIL='a'
export GIT_COMMITTER_DATE='2000-01-01T00:00:00+0000'
export GIT_AUTHOR_DATE='2000-01-01T00:00:00+0000'

rm -rf server_repo local_repo
mkdir server_repo
cd server_repo

# Create repo.
git init --quiet
git config --local uploadpack.allowfilter 1
git config --local uploadpack.allowanysha1inwant 1

# First commit.
# Directories present in all branches.
mkdir d1 d2
printf 'd1/a' > ./d1/a
printf 'd1/b' > ./d1/b
printf 'd2/a' > ./d2/a
printf 'd2/b' > ./d2/b
# Present only in root.
mkdir 'root'
printf 'root' > ./root/root
git add .
git commit -m 'root' --quiet

# Second commit only on master.
git rm --quiet -r ./root
mkdir 'master'
printf 'master' > ./master/master
git add .
git commit -m 'master commit' --quiet

# Second commit only on mybranch.
git checkout -b mybranch --quiet master~
git rm --quiet -r ./root
mkdir 'mybranch'
printf 'mybranch' > ./mybranch/mybranch
git add .
git commit -m 'mybranch commit' --quiet

echo "# List and identify all objects"
list-objects
echo

# Restore master.
git checkout --quiet master
cd ..

# Clone. Don't checkout for now, only .git/ dir.
git clone --depth 1 --quiet --no-checkout --filter=blob:none "file://$(pwd)/server_repo" local_repo
cd local_repo

# List missing objects from master.
echo "# Missing objects after --no-checkout"
git rev-list --all --quiet --objects --missing=print
echo

echo "# Git checkout fails without internet"
mv ../server_repo ../server_repo.off
! git checkout master
echo

echo "# Git checkout fetches the missing directory from internet"
mv ../server_repo.off ../server_repo
git checkout master -- d1/
echo

echo "# Missing objects after checking out d1"
git rev-list --all --quiet --objects --missing=print

GitHub stromaufwärts .

Ausgabe in Git v2.19:

# List and identify all objects
c6fcdfaf2b1462f809aecdad83a186eeec00f9c1
fc5e97944480982cfc180a6d6634699921ee63ec
7251a83be9a03161acde7b71a8fda9be19f47128
62d67bce3c672fe2b9065f372726a11e57bade7e
b64bf435a3e54c5208a1b70b7bcb0fc627463a75 d1
308150e8fddde043f3dbbb8573abb6af1df96e63 d1/a
f70a17f51b7b30fec48a32e4f19ac15e261fd1a4 d1/b
84de03c312dc741d0f2a66df7b2f168d823e122a d2
0975df9b39e23c15f63db194df7f45c76528bccb d2/a
41484c13520fcbb6e7243a26fdb1fc9405c08520 d2/b
7d5230379e4652f1b1da7ed1e78e0b8253e03ba3 master
8b25206ff90e9432f6f1a8600f87a7bd695a24af master/master
ef29f15c9a7c5417944cc09711b6a9ee51b01d89
19f7a4ca4a038aff89d803f017f76d2b66063043 mybranch
1b671b190e293aa091239b8b5e8c149411d00523 mybranch/mybranch
c3760bb1a0ece87cdbaf9a563c77a45e30a4e30e
a0234da53ec608b54813b4271fbf00ba5318b99f root
93ca1422a8da0a9effc465eccbcb17e23015542d root/root
master commit SHA: fc5e97944480982cfc180a6d6634699921ee63ec
mybranch commit SHA: fc5e97944480982cfc180a6d6634699921ee63ec
040000 tree b64bf435a3e54c5208a1b70b7bcb0fc627463a75    d1
040000 tree 84de03c312dc741d0f2a66df7b2f168d823e122a    d2
040000 tree 7d5230379e4652f1b1da7ed1e78e0b8253e03ba3    master
040000 tree 19f7a4ca4a038aff89d803f017f76d2b66063043    mybranch
040000 tree a0234da53ec608b54813b4271fbf00ba5318b99f    root

# Missing objects after --no-checkout
?f70a17f51b7b30fec48a32e4f19ac15e261fd1a4
?8b25206ff90e9432f6f1a8600f87a7bd695a24af
?41484c13520fcbb6e7243a26fdb1fc9405c08520
?0975df9b39e23c15f63db194df7f45c76528bccb
?308150e8fddde043f3dbbb8573abb6af1df96e63

# Git checkout fails without internet
fatal: '/home/ciro/bak/git/test-git-web-interface/other-test-repos/partial-clone.tmp/server_repo' does not appear to be a git repository
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

# Git checkout fetches the missing directory from internet
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (1/1), 45 bytes | 45.00 KiB/s, done.
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (1/1), 45 bytes | 45.00 KiB/s, done.

# Missing objects after checking out d1
?8b25206ff90e9432f6f1a8600f87a7bd695a24af
?41484c13520fcbb6e7243a26fdb1fc9405c08520
?0975df9b39e23c15f63db194df7f45c76528bccb

Schlussfolgerungen: Alle Blobs von außerhalb d1/fehlen.

Beachten Sie, dass root/rootund mybranch/mybranchauch fehlen, --depth 1verbirgt dies jedoch in der Liste der fehlenden Dateien. Wenn Sie entfernen --depth 1, werden sie in der Liste der fehlenden Dateien angezeigt.


1

Wie Ihre Bearbeitung zeigt, können Sie zwei separate Zweige verwenden, um die beiden separaten Verzeichnisse zu speichern. Dadurch bleiben beide im selben Repository, aber Sie können immer noch keine Commits für beide Verzeichnisbäume haben. Wenn Sie eine Änderung in einer haben, die eine Änderung in der anderen erfordert, müssen Sie diese als zwei separate Commits ausführen, und Sie eröffnen die Möglichkeit, dass zwei Kassen der beiden Verzeichnisse nicht mehr synchron sind.

Wenn Sie das Verzeichnispaar als eine Einheit behandeln möchten, können Sie 'wordpress / wp-content' als Stammverzeichnis Ihres Repos verwenden und die .gitignore-Datei auf der obersten Ebene verwenden, um alles außer den beiden interessierenden Unterverzeichnissen zu ignorieren. Dies ist derzeit wahrscheinlich die vernünftigste Lösung.

Angeblich gibt es seit zwei Jahren spärliche Kassen, aber es gibt immer noch keine Anzeichen dafür im Git Development Repo und auch keine Hinweise darauf, dass die notwendigen Änderungen jemals dort eintreffen werden. Ich würde nicht auf sie zählen.


1

Sie können kein einzelnes Verzeichnis eines Repositorys auschecken, da das gesamte Repository vom einzelnen .git-Ordner im Stammverzeichnis des Projekts anstelle der unzähligen .svn-Verzeichnisse von subversion verwaltet wird.

Das Problem bei der Arbeit an Plugins in einem einzelnen Repository besteht darin, dass durch das Festschreiben von z. B. mytheme die Versionsnummer für myplugin erhöht wird. Daher ist es auch bei Subversion besser, separate Repositorys zu verwenden.

Das Subversion-Paradigma für Teilprojekte lautet svn: externals, was sich etwas in Submodule in git übersetzt (aber nicht genau für den Fall, dass Sie zuvor svn: externals verwendet haben.)


0

Hier gibt es eine Inspiration. Einfach nutzen shell regexoder git regex.

git checkout commit_id */*.bat  # *.bat in 1-depth subdir exclude current dir, shell regex  
git checkout commit_id '*.bat'  # *.bat in all subdir include current dir, git regex

Verwenden Sie das Zitat, um der Shell-Regex-Interpretation zu entgehen und Platzhalter an git zu übergeben.

Die erste ist nicht rekursiv, sondern nur Dateien in einer Tiefe subdir. Aber der zweite ist rekursiv.

In Bezug auf Ihre Situation kann Folgendes ausreichen.

git checkout master */*/wp-content/*/*
git checkout master '*/wp-content/*'

Hacken Sie einfach die Zeilen nach Bedarf.


0

Sie können nicht festgeschriebene Änderungen nur auf eine bestimmte Datei oder ein bestimmtes Verzeichnis zurücksetzen:

git checkout [some_dir|file.txt]
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.