Was ist der Unterschied zwischen HEAD ^ und HEAD ~ in Git?


756

Wenn ich in Git ein Ahnen-Commit-Objekt anführe, verwechsle ich zwischen HEAD^und HEAD~.

Beide haben eine "nummerierte" Version wie HEAD^3und HEAD~2.

Sie scheinen mir sehr ähnlich oder gleich zu sein, aber gibt es Unterschiede zwischen der Tilde und dem Caret?


64
Es ist schlecht, Links einzufügen, ich weiß, aber dies ist die beste Erklärung, die ich gefunden habe, und es ist ein Bild darin. paulboxley.com/blog/2011/06/git-caret-and-tilde
igor

4
Links sind besonders schlecht, wenn sie kaputt sind. Das ist der Grund, warum es sicherer ist, die Frage zu beantworten, die dies verhindert, da einige Erklärungen kopiert und eingefügt werden können :)
Samuel

Antworten:


763

Faustregeln

  • Verwenden Sie die ~meiste Zeit - um eine Reihe von Generationen zurück zu gehen, normalerweise was Sie wollen
  • Bei ^Zusammenführungs-Commits verwenden - weil sie zwei oder mehr (unmittelbare) Eltern haben

Mnemonik:

  • Tilde sieht ~fast linear aus und möchte in einer geraden Linie rückwärts gehen
  • Caret ^schlägt ein interessantes Segment eines Baumes oder einer Weggabelung vor

Tilde

Der Abschnitt "Festlegen von Revisionen" in der git rev-parseDokumentation definiert ~als

<rev>~<n>ZBmaster~3
ein Suffix ~<n>an eine Revisions Parameter bedeutet , dass das Objekt , das die Commit ist n - ten Generation Vorfahr des Objekts commit benannt folgenden nur die ersten Eltern. Zum Beispiel <rev>~3ist äquivalent zu <rev>^^^dem äquivalent zu <rev>^1^1^1...

Sie können Eltern von jedem Commit erreichen, nicht nur HEAD. Sie können auch über Generationen zurückgehen: Dies master~2bedeutet beispielsweise, dass der Großelternteil der Spitze des Hauptzweigs den ersten Elternteil bei Zusammenführungs-Commits bevorzugt.

Caret

Der Git-Verlauf ist nichtlinear: ein gerichteter azyklischer Graph (DAG) oder Baum. Für ein Commit mit nur einem Elternteil rev~und rev^bedeuten dasselbe. Der Caret-Selektor wird bei Merge-Commits nützlich, da jeder das Kind von zwei oder mehr Elternteilen ist - und die aus der Biologie entliehene Sprache belastet.

HEAD^bedeutet das erste unmittelbare Elternteil der Spitze des aktuellen Zweigs. HEAD^ist die Abkürzung für HEAD^1, und Sie können auch adressieren HEAD^2und so weiter nach Bedarf. Der gleiche Abschnitt der git rev-parseDokumentation definiert es als

<rev>^, Zum Beispiel HEAD^ ,v1.5.1^0
ein Suffix ^an einen Revisions Parameter bedeutet die ersten übergeordnet das Objekt begehen. ^<n>bedeutet das n- te Elternteil ([ zB ] <rev>^entspricht <rev>^1). In der Regel <rev>^0bedeutet dies das Festschreiben selbst und wird verwendet, wenn <rev>der Objektname eines Tag-Objekts auf ein Festschreibungsobjekt verweist.

Beispiele

Diese Spezifizierer oder Selektoren können beliebig verkettet werden, z. B. ist topic~3^2in Englisch das zweite übergeordnete Element des Zusammenführungs-Commits das Urgroßelternteil (vor drei Generationen) der aktuellen Spitze des Zweigs topic.

Der oben erwähnte Abschnitt der git rev-parseDokumentation zeichnet viele Pfade durch eine fiktive Git-Geschichte nach. Die Zeit fließt im Allgemeinen nach unten. Commits D, F, B und A sind Merge-Commits.

Hier ist eine Illustration von Jon Loeliger. Beide Festschreibungsknoten B und C sind Eltern des Festschreibungsknotens A. Die übergeordneten Festschreibungen sind von links nach rechts angeordnet.

G   H   I   J
 \ /     \ /
  D   E   F
   \  |  / \
    \ | /   |
     \|/    |
      B     C
       \   /
        \ /
         A

A =      = A^0
B = A^   = A^1     = A~1
C = A^2
D = A^^  = A^1^1   = A~2
E = B^2  = A^^2
F = B^3  = A^^3
G = A^^^ = A^1^1^1 = A~3
H = D^2  = B^^2    = A^^^2  = A~2^2
I = F^   = B^3^    = A^^3^
J = F^2  = B^3^2   = A^^3^2

Führen Sie den folgenden Code aus, um ein Git-Repository zu erstellen, dessen Verlauf mit der angegebenen Abbildung übereinstimmt.

#! /usr/bin/env perl

use strict;
use warnings;
use subs qw/ postorder /;
use File::Temp qw/ mkdtemp /;

my %sha1;
my %parents = (
  A => [ qw/ B C /               ],
  B => [ qw/     D E F /         ],
  C => [ qw/         F /         ],
  D => [ qw/           G H /     ],
  F => [ qw/               I J / ],
);

sub postorder {
  my($root,$hash) = @_;
  my @parents = @{ $parents{$root} || [] };
  postorder($_, $hash) for @parents;
  return if $sha1{$root};
  @parents = map "-p $sha1{$_}", @parents;
  chomp($sha1{$root} = `git commit-tree @parents -m "$root" $hash`);
  die "$0: git commit-tree failed" if $?;
  system("git tag -a -m '$sha1{$root}' '$root' '$sha1{$root}'") == 0 or die "$0: git tag failed";
}

$0 =~ s!^.*/!!;  # / fix Stack Overflow highlighting
my $repo = mkdtemp "repoXXXXXXXX";
chdir $repo or die "$0: chdir: $!";
system("git init") == 0               or die "$0: git init failed";
chomp(my $tree = `git write-tree`);      die "$0: git write-tree failed" if $?;

postorder 'A', $tree;
system "git update-ref HEAD   $sha1{A}"; die "$0: git update-ref failed" if $?;
system "git update-ref master $sha1{A}"; die "$0: git update-ref failed" if $?;

# for browsing history - http://blog.kfish.org/2010/04/git-lola.html
system "git config alias.lol  'log --graph --decorate --pretty=oneline --abbrev-commit'";
system "git config alias.lola 'log --graph --decorate --pretty=oneline --abbrev-commit --all'";

Sie fügt hinzu , Aliase in der neuen Wegwerf - Repo nur für git lolundgit lola schauen Sie sich die Geschichte sehen können , wie in

$ git lol
*   29392c8 (HEAD -> master, tag: A) A
|\
| * a1ef6fd (tag: C) C
| |
|  \
*-. \   8ae20e9 (tag: B) B
|\ \ \
| | |/
| | *   03160db (tag: F) F
| | |\
| | | * 9df28cb (tag: J) J
| | * 2afd329 (tag: I) I
| * a77cb1f (tag: E) E
*   cd75703 (tag: D) D
|\
| * 3043d25 (tag: H) H
* 4ab0473 (tag: G) G

Beachten Sie, dass sich die SHA-1-Objektnamen auf Ihrem Computer von den oben genannten unterscheiden. Mit den Tags können Sie jedoch Commits nach Namen adressieren und Ihr Verständnis überprüfen.

$ git log -1 --format=%f $(git rev-parse A^)
B
$ git log -1 --format=%f $(git rev-parse A~^3~)
I
$ git log -1 --format=%f $(git rev-parse A^2~)
F

Die „Spezifikation von Revisionen“ in der git rev-parseDokumentation enthält viele nützliche Informationen und ist eine eingehende Lektüre wert. Siehe auch Git Tools - Revisionsauswahl aus dem Buch Pro Git .

Reihenfolge der übergeordneten Commits

Das Commit 89e4fcb0dd aus dem eigenen Verlauf von git ist ein Merge-Commit, wie git show 89e4fcb0ddin der Kopfzeile Merge angegeben, in der die Objektnamen der unmittelbaren Vorfahren angezeigt werden.

commit 89e4fcb0dd01b42e82b8f27f9a575111a26844df
Merge: c670b1f876 649bf3a42f b67d40adbb
Author: Junio C Hamano <gitster@pobox.com>
Date:   Mon Oct 29 10:15:31 2018 +0900

    Merge branches 'bp/reset-quiet' and 'js/mingw-http-ssl' into nd/config-split […]

Wir können die Bestellung bestätigen, indem wir darum bitten git rev-parse, die unmittelbaren Eltern von 89e4fcb0dd nacheinander anzuzeigen.

$ git rev-parse 89e4fcb0dd^1 89e4fcb0dd^2 89e4fcb0dd^3
c670b1f876521c9f7cd40184bf7ed05aad843433
649bf3a42f344e71b1b5a7f562576f911a1f7423
b67d40adbbaf4f5c4898001bf062a9fd67e43368

Das Abfragen des nicht vorhandenen vierten übergeordneten Elements führt zu einem Fehler.

$ git rev-parse 89e4fcb0dd^4
89e4fcb0dd^4
fatal: ambiguous argument '89e4fcb0dd^4': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

Wenn Sie nur die Eltern extrahieren möchten, verwenden Sie ein hübsches Format %P für die vollständigen Hashes

$ git log -1 --pretty=%P 89e4fcb0dd
c670b1f876521c9f7cd40184bf7ed05aad843433 649bf3a42f344e71b1b5a7f562576f911a1f7423 b67d40adbbaf4f5c4898001bf062a9fd67e43368

oder %pfür abgekürzte Eltern.

$ git log -1 --pretty=%p 89e4fcb0dd
c670b1f876 649bf3a42f b67d40adbb

es scheint, dass ^ alle Fälle behandeln kann und man sich fragen kann, warum ~ überhaupt aufgetaucht ist. Warum nicht nur daran erinnern, wie ^ funktioniert?
Sbu

Das ist immer noch super verwirrend ... vorausgesetzt, G ist KOPF. Wenn ich also einen KOPF mache, wäre das D ... richtig?
Patoshi

12
@duckx Der Graph geht tatsächlich von oben nach unten, also ist A das letzte Commit und G eines der ältesten. Der Weg von G nach D ist vorwärts und nicht rückwärts, soweit ich das beurteilen kann.
Goldenratio

@SimonBudin Ich denke, es ist nicht sehr bequem zu bedienen , ^^^^^^^anstatt ~7, oder ? Deshalb ~ist nützlich
YakovL

1
@AdityaVikasDevarapalli Das wäre gut als eigene Frage.
Greg Bacon

340

Der Unterschied zwischen HEAD^und HEAD~wird durch die Abbildung (von Jon Loeliger) auf http://www.kernel.org/pub/software/scm/git/docs/git-rev-parse.html gut beschrieben .

Diese Dokumentation kann für Anfänger etwas dunkel sein, daher habe ich die folgende Abbildung wiedergegeben:

G   H   I   J
 \ /     \ /
  D   E   F
   \  |  / \
    \ | /   |
     \|/    |
      B     C
       \   /
        \ /
         A
A =      = A^0
B = A^   = A^1     = A~1
C = A^2
D = A^^  = A^1^1   = A~2
E = B^2  = A^^2
F = B^3  = A^^3
G = A^^^ = A^1^1^1 = A~3
H = D^2  = B^^2    = A^^^2  = A~2^2
I = F^   = B^3^    = A^^3^
J = F^2  = B^3^2   = A^^3^2

12
Nur eine Frage. Wie ist es möglich, dass ein Commit mehr als zwei Eltern hat? (Siehe B - die Eltern sind D, E und F) Ich stelle mir vor, dass ein Commit nur dann zwei Elternteile haben kann, wenn es sich um ein Zusammenführungs-Commit handelt ... aber wie können Sie 3 Commits gleichzeitig zusammenführen?
Tsikov

Wenn ich mich nicht irre, mag dies offensichtlich sein, aber ich denke, es sollte angegeben werden, dass HEAD ~ dem aktuellen Zweig folgt (wie Diego Dias unten erwähnt).
Fibono

2
Zusätzlich F = A^2^.
Mateen Ulhaq

2
So ^ == ^1 == LEFTMOST PARENT, ^2 == SECOND LEFTMOST PARENTund so weiter. Und ~ == ~1 == LEFTMOST PARENT, ~2 == LEFTMOST PARENTS LEFTMOST PARENT == LEFTMOST GRANDPARENT. Im weiteren ~2^2 == LEFTMOST GRANDPARENTS SECOND LEFTMOST PARENT
Sinne

1
@AlexanderTorstling das war sehr hilfreich für mich. Was bedeutet hier links und rechts?
polynomial_donut

287

Beide ~und ^für sich beziehen sich auf das übergeordnete Element des Commits ( ~~und ^^beide beziehen sich auf das Commit der Großeltern usw.). Sie unterscheiden sich jedoch in ihrer Bedeutung, wenn sie mit Zahlen verwendet werden:

  • ~2bedeutet zwei Ebenen in der Hierarchie über das erste übergeordnete Element, wenn ein Commit mehr als ein übergeordnetes Element hat

  • ^2bedeutet das zweite übergeordnete Element, bei dem ein Commit mehr als ein übergeordnetes Element hat (dh weil es sich um eine Zusammenführung handelt).

Diese können kombiniert werden, so dass HEAD~2^3Mittel HEAD‚s Großeltern verpflichten dritt Eltern zu begehen.


2
Dies zu lesen, gefolgt von dem Bild von stackoverflow.com/questions/2221658/…, machte vollkommen Sinn.
Kunigami

23
Dies sollte die akzeptierte Antwort sein, viel prägnanter und nützlicher als die anderen.
RichVel

3
Diese Antwort ließ mich zwischen Caret / Tilde ohne Nummer und mit Nummer unterscheiden! Ich dachte das ^^war das gleiche wie ^2aber es ist nicht.
Alexander Derck

278

Meine zwei Cent...

Geben Sie hier die Bildbeschreibung ein


Und wie H=A~2^2nicht H=A~2^1?
Mohammad Faisal

3
Wenn ich es richtig herausgefunden hatte, die Commits A, B, D, Gsind auf dem gleichen Zweig und der Commit Dist eine Zusammenführung Gund Hdaher mit zwei Eltern. Das commit ( H) aus einem anderen Zweig wird also von referenziert ^2.
Mohammad Faisal

62

Hier ist eine sehr gute Erklärung, die wörtlich von http://www.paulboxley.com/blog/2011/06/git-caret-and-tilde stammt :

ref~ist eine Abkürzung für ref~1und bedeutet das erste Elternteil des Commits. ref~2bedeutet das erste Elternteil des Commits. ref~3bedeutet das erste Elternteil des ersten Elternteils des Commits. Und so weiter.

ref^ist eine Abkürzung für ref^1und bedeutet das erste Elternteil des Commits. ref^2Wenn sich die beiden jedoch unterscheiden, bedeutet dies, dass der zweite Elternteil des Commits (denken Sie daran, dass Commits zwei Elternteile haben können, wenn sie zusammengeführt werden).

Die Operatoren ^und ~können kombiniert werden.

Geben Sie hier die Bildbeschreibung ein


5
Vielen Dank, dass Sie die Unterschiede tatsächlich erklärt haben, anstatt eine Vielzahl von Beispielen zu veröffentlichen.
Kirk Broadhurst

32

Mit dem ^<n>Format können Sie das n-te übergeordnete Element des Commits auswählen (relevant für Zusammenführungen). Mit diesem ~<n>Format können Sie das n-te Vorgänger-Commit auswählen, immer nach dem ersten übergeordneten Element. Siehe git-rev-parse ‚s Dokumentation für einige Beispiele.


21

Es ist erwähnenswert, dass git auch eine Syntax zum Verfolgen von "von wo Sie gekommen sind" / "von jetzt zurück wollen" hat - zum Beispiel HEAD@{1}verweist es auf den Ort, von dem aus Sie zu einem neuen Festschreibungsort gesprungen sind.

Grundsätzlich HEAD@{}erfassen Variablen den Verlauf der HEAD-Bewegung, und Sie können sich für die Verwendung eines bestimmten Kopfes entscheiden, indem Sie mit dem Befehl die Reflogs von Git untersuchen git reflog.

Beispiel:

0aee51f HEAD@{0}: reset: moving to HEAD@{5}
290e035 HEAD@{1}: reset: moving to HEAD@{7}
0aee51f HEAD@{2}: reset: moving to HEAD@{3}
290e035 HEAD@{3}: reset: moving to HEAD@{3}
9e77426 HEAD@{4}: reset: moving to HEAD@{3}
290e035 HEAD@{5}: reset: moving to HEAD@{3}
0aee51f HEAD@{6}: reset: moving to HEAD@{3}
290e035 HEAD@{7}: reset: moving to HEAD@{3}
9e77426 HEAD@{8}: reset: moving to HEAD@{3}
290e035 HEAD@{9}: reset: moving to HEAD@{1}
0aee51f HEAD@{10}: reset: moving to HEAD@{4}
290e035 HEAD@{11}: reset: moving to HEAD^
9e77426 HEAD@{12}: reset: moving to HEAD^
eb48179 HEAD@{13}: reset: moving to HEAD~
f916d93 HEAD@{14}: reset: moving to HEAD~
0aee51f HEAD@{15}: reset: moving to HEAD@{5}
f19fd9b HEAD@{16}: reset: moving to HEAD~1
290e035 HEAD@{17}: reset: moving to HEAD~2
eb48179 HEAD@{18}: reset: moving to HEAD~2
0aee51f HEAD@{19}: reset: moving to HEAD@{5}
eb48179 HEAD@{20}: reset: moving to HEAD~2
0aee51f HEAD@{21}: reset: moving to HEAD@{1}
f916d93 HEAD@{22}: reset: moving to HEAD@{1}
0aee51f HEAD@{23}: reset: moving to HEAD@{1}
f916d93 HEAD@{24}: reset: moving to HEAD^
0aee51f HEAD@{25}: commit (amend): 3rd commmit
35a7332 HEAD@{26}: checkout: moving from temp2_new_br to temp2_new_br
35a7332 HEAD@{27}: commit (amend): 3rd commmit
72c0be8 HEAD@{28}: commit (amend): 3rd commmit

Ein Beispiel könnte sein, dass ich lokale Commits a-> b-> c-> d ausgeführt habe und dann 2 Commits verworfen habe, um meinen Code zu überprüfen - git reset HEAD~2- und danach möchte ich meinen HEAD wieder auf d - verschieben git reset HEAD@{1}.


17

Einfach ausgedrückt :

  • ~ gibt Vorfahren an
  • ^ gibt Eltern an

Sie können beim Zusammenführen einen oder mehrere Zweige angeben. Dann hat ein Commit zwei oder mehr Eltern und ist dann ^nützlich, um Eltern anzuzeigen.

Angenommen , Sie sind auf dem Zweig A und Sie haben zwei weitere Filialen: B und C .

In jedem Zweig sind die drei letzten Commits:

  • A : A1 , A2 , A3
  • B : B1 , B2 , B3
  • C : C1 , C3 , C3

Wenn Sie jetzt in Zweig A den folgenden Befehl ausführen:

git merge B C

dann kombinieren Sie drei Zweige miteinander (hier hat Ihr Zusammenführungs- Commit drei Eltern)

und

~ gibt den n-ten Vorfahren im ersten Zweig an, also

  • HEAD~zeigt A3 an
  • HEAD~2zeigt A2 an
  • HEAD~3zeigt A1 an

^ gibt den n-ten Elternteil an, also

  • HEAD^zeigt A3 an
  • HEAD^2zeigt B3 an
  • HEAD^3zeigt C3 an

Die nächste Verwendung von ~oder ^nebeneinander erfolgt im Kontext des durch vorherige Zeichen festgelegten Commits.

Hinweis 1 :

  • HEAD~3ist immer gleich: HEAD~~~und zu: HEAD^^^(jedes zeigt A1 an ),

        und allgemein :

  • HEAD~nist immer gleich: HEAD~...~( n- mal ~) und: HEAD^...^( n- mal ^).

Hinweis 2 :

  • HEAD^3ist nicht dasselbe wie HEAD^^^(das erste zeigt C3 an und das zweite zeigt A1 an ),

        und allgemein :

  • HEAD^1ist das gleiche wie HEAD^,
  • aber für n > 1: HEAD^nist immer nicht dasselbe wie HEAD^...^( n- mal ~).

15

TLDR

~ ist das, was Sie die meiste Zeit wollen, es verweist auf vergangene Commits auf den aktuellen Zweig

^ verweist auf Eltern (git-merge erstellt einen zweiten Elternteil oder mehr)

A ~ ist immer dasselbe wie A ^
A ~~ ist immer dasselbe wie A ^^, und so weiter ist
A ~ 2 jedoch nicht dasselbe wie A ^ 2,
da ~ 2 eine Abkürzung für ~~ ist,
während ^ 2 dies nicht ist Abkürzung für alles, es bedeutet das 2. Elternteil


11

HEAD ^^^ ist dasselbe wie HEAD ~ 3, wobei das dritte Commit vor HEAD ausgewählt wird

HEAD ^ 2 gibt den zweiten Kopf in einem Merge-Commit an


9
  • HEAD ~ gibt das erste übergeordnete Element in einem "Zweig" an.

  • Mit HEAD ^ können Sie ein bestimmtes übergeordnetes Element des Commits auswählen

Ein Beispiel:

Wenn Sie einem Seitenzweig folgen möchten, müssen Sie etwas wie angeben

master~209^2~15


0

Einfach ausgedrückt, für die erste Ebene der Abstammung (Abstammung, Vererbung, Abstammung usw.) zeigen HEAD ^ und HEAD ~ beide auf dasselbe Commit, das sich über dem HEAD (Commit) befindet.

Weiterhin ist HEAD ^ = HEAD ^ 1 = HEAD ~ = HEAD ~ 1. Aber HEAD ^^! = HEAD ^ 2! = HEAD ~ 2. Doch HEAD ^^ = HEAD ~ 2. Weiter lesen.

Über die erste Ebene der Abstammung hinaus werden die Dinge schwieriger, insbesondere wenn der Arbeitszweig / Hauptzweig Zusammenschlüsse hatte (von anderen Zweigen). Es gibt auch die Frage der Syntax mit dem Caret, HEAD ^^ = HEAD ~ 2 (sie sind gleichwertig) ABER HEAD ^^! = HEAD ^ 2 (sie sind zwei völlig verschiedene Dinge).

Jedes Caret bezieht sich auf das erste Elternteil des HEAD, weshalb Carets, die aneinandergereiht sind, Tilde-Ausdrücken entsprechen, da sie sich auf die ersten Eltern des ersten Elternteils (des ersten Elternteils) usw. usw. beziehen, die ausschließlich auf der Anzahl der verbundenen Carets basieren oder auf der Zahl nach der Tilde (so oder so bedeuten beide dasselbe), dh beim ersten Elternteil bleiben und x Generationen aufsteigen.

HEAD ~ 2 (oder HEAD ^^) bezieht sich auf das Commit, das zwei Abstammungsstufen über dem aktuellen Commit (HEAD) in der Hierarchie liegt, dh das Commit der Großeltern des HEAD.

HEAD ^ 2 bezieht sich dagegen NICHT auf das Commit des zweiten Elternteils des ersten Elternteils, sondern einfach auf das Commit des zweiten Elternteils. Dies liegt daran, dass das Caret das übergeordnete Element des Commits bedeutet und die folgende Nummer angibt, auf welches / welches übergeordnete Commit Bezug genommen wird (das erste übergeordnete Element, wenn dem Caret keine Nummer folgt [weil es eine Abkürzung für die Nummer ist 1 sein, was den ersten Elternteil bedeutet]). Im Gegensatz zum Caret impliziert die Zahl, die danach folgt, keine weitere Hierarchieebene nach oben, sondern impliziert, wie viele Ebenen seitwärts in der Hierarchie das richtige übergeordnete Element (Commit) gefunden werden muss. Im Gegensatz zur Zahl in einem Tilde-Ausdruck ist es nur ein Elternteil in der Hierarchie, unabhängig von der Zahl, mit der das Caret (sofort) ausgeführt wird. Anstatt nach oben, das Caret '

HEAD ^ 3 ist also gleich dem dritten Elternteil des HEAD-Commits (NICHT der Urgroßelternteil, wie HEAD ^^^ AND HEAD ~ 3 wäre ...).


-1

~ das bedeutet Eltern.

^ Wenn es Eltern von zwei oder mehr hat, wie z. B. Merge Commit, können wir den zweiten Elternteil oder einen anderen auswählen.

Wenn also nur eines wie (HEAD ~ oder HEAD ^) ist, hat es die gleichen Ergebnisse.

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.