Wow, das war eine schwierige Frage! :-)
Mit Ihrem Skript reproduzierte ich das Problem. Dort werden alle etwas sehr merkwürdig war es aber so, zuerst, ich getrimmt, um die Fütterungsmaterial Schritt, so dass diese (leicht modifiziert) Skript:
#!/bin/sh
set -e
if [ -d testing_git ]; then
echo test dir testing_git already exists - halting
exit 1
fi
mkdir testing_git
cd testing_git
git init
touch main.txt
git add .
git commit -m "initial commit"
# setup B branch
git checkout -b B
echo hello > test.txt
git add .
git commit -m "added test.txt"
# setup master
git checkout master
echo hello > test.txt
git add .
git commit -m "added test.txt"
rm test.txt
git add .
git commit -m "remove test.txt"
Einmal ausgeführt, die Commits Inspektion, erhalte ich diese:
$ git log --graph --decorate | sed 's/@/ /'
* commit 249e4893ea7458f45fe5cdc496ddc0292a3f03ef (HEAD -> master)
| Author: Chris Torek <chris.torek gmail.com>
| Date: Thu May 5 20:28:02 2016 -0700
|
| remove test.txt
|
* commit a132dc9e3939b5338f7c784c58da9c83f4902c8d (B)
| Author: Chris Torek <chris.torek gmail.com>
| Date: Thu May 5 20:28:02 2016 -0700
|
| added test.txt
|
* commit 81c4d9be82094fdb4c88ed0a53bdbd5c3dfd7a5a
Author: Chris Torek <chris.torek gmail.com>
Date: Thu May 5 20:28:02 2016 -0700
initial commit
Beachten Sie, dass master
's Eltern-Commit ist Zweig B
Commit, und es gibt nur drei Commits, nicht vier. Wie kann das sein, wenn das Skript vier git commit
Befehle ausführt?
Jetzt ist sleep 2
zum Skript hinzufügen lassen, direkt nach git checkout master
, und starten Sie es und sehen, was passiert ...
[edit]
$ sh testrebase.sh
[snip output]
$ cd testing_git && git log --oneline --decorate --graph --all
* cddbff1 (HEAD -> master) remove test.txt
* c4ac1b2 added test.txt
| * fefc150 (B) added test.txt
|/
* 8c07bb6 initial commit
Whoa, jetzt haben wir vier Commits haben, und eine richtige Filiale!
Warum hat das erste Skript drei Commits gemacht, und das Hinzufügen von sleep 2
es ändern, um vier Commits zu machen?
Die Antwort liegt in der Identität eines Commits. Jedes Commit hat eine (angeblich!) Eindeutige ID, die eine Prüfsumme des Commit-Inhalts darstellt.Hier ist, was in den B
war -Verzweigung verpflichten, beim ersten Mal:
$ git cat-file -p B | sed 's/@/ /'
tree c3cd0188a6a1490204e25547986e49b0b445dec8
parent 81c4d9be82094fdb4c88ed0a53bdbd5c3dfd7a5a
author Chris Torek <chris.torek gmail.com> 1462505282 -0700
committer Chris Torek <chris.torek gmail.com> 1462505282 -0700
added test.txt
Wir haben die tree
, die parent
, zwei (Name, E-Mail, Zeitstempel) verdreifacht für Autor und Committer, eine leere Zeile, und dem Protokollnachricht Das übergeordnete Element ist das erste Commit auf dem Master-Zweig und der Baum ist der Baum, den wir erstellt haben, als wir test.txt
(mit seinem Inhalt) hinzugefügt haben.
Dann, als wir gingen, um die zweite commit auf der master
Zweig machen, machte Git einen neuen Baum aus den neuen Dateien. Dieser Baum war Bit für Bit identisch mit dem, den wir gerade für den Zweig B
festgelegt haben, also bekam er die gleiche eindeutige ID (denken Sie daran, dass es nur eine Kopie dieses Baumes im Repo gibt, also ist das korrekt). Dann machte es einen neuen Commit Objekt mit meinem Namen und E-Mail und Zeitstempel wie üblich, und die Protokollmeldung. Aber dieses Commit war Bit-für-Bit identisch mit dem Commit, das wir gerade für den Zweig B
gemacht haben, also haben wir die gleiche ID wie zuvor und den Zweig master
auf diesen Commit gesetzt.
Mit anderen Worten, wir wiederverwendet das Commit. Wir haben es einfach auf einem anderen Zweig erstellt (so dass master
auf denselben Commit wie B
zeigt).
Hinzufügen sleep 2
änderte den Zeitstempel auf dem neuen Festschreiben. Nun werden die beiden Commits (in B
und master
) nicht mehr Bit für Bit identisch:
$ git cat-file -p B | sed 's/@/ /' > bx
$ git cat-file -p master^ | sed 's/@/ /' > mx
$ diff bx mx
3,4c3,4
< author Chris Torek <chris.torek gmail.com> 1462505765 -0700
< committer Chris Torek <chris.torek gmail.com> 1462505765 -0700
---
> author Chris Torek <chris.torek gmail.com> 1462505767 -0700
> committer Chris Torek <chris.torek gmail.com> 1462505767 -0700
Verschiedene Zeitstempel = unterschiedliche Commits = viel sinnvoller Einrichtung.
Eigentlich die Rebase ausführen, ließ die Datei trotzdem fallen!
Es stellt sich heraus, dass dies beabsichtigt ist. Wenn Sie git rebase
ausführen, listet der Setup-Code nicht einfach jedes Commit für Rosinenpicken auf, sondern verwendet git rev-list --right-only
, um Commits zu finden, die es fallen lassen sollte.
Da die commit fügt hinzu, dass test.txt
im Upstream ist, Git fällt es nur ganz: die hier davon ausgegangen, dass Sie es an jemand Upstream gesendet, nahmen sie es schon, und es gibt keine Notwendigkeit, es wieder zu nehmen .
Lassen Sie uns das reproducer Skript wieder ändern -und wir werden das in der Lage sleep 2
diesmal zu nehmen, die Dinge-so, dass die Änderung master
ist anders, und werden nicht entfernt aus der Liste über --cherry-pick --right-only
beschleunigt.Wir werden noch test.txt
mit der gleichen einzigen Zeile hinzufügen, aber wir werden auch ändern main.txt
in dem Autocommit:
# setup master
git checkout master
echo hello > test.txt
echo and also slight difference >> main.txt
git add .
git commit -m "added test.txt"
Wir gehen weiter und biegen auf den endgültigen git checkout B
und git rebase master
Linien als auch, und dieses Mal, Rebasing Werke wie wir ursprünglich erwartet hatten:
$ git log --oneline --decorate --graph --all
* c31b13a (HEAD -> B) added test.txt
* da2ca52 (master) remove test.txt
* 6972019 added test.txt
* 0f0d2e8 initial commit
$ ls
main.txt test.txt
Ich hatte nicht erkannt, dass Rebase dies tat; es ist nicht etwas, was ich erwartet habe (obwohl wie die andere Antwort weist darauf hin, es dokumentiert ist), und es bedeutet, dass zu sagen „rebase nur cherry-pick wiederholt wird“ ist nicht ganz richtig: es cherry-pick wiederholt hat, mit speziellem Fälle von Ablegen von Commits.
Eigentlich für nicht-interaktive Fütterungsmaterial, verwendet es diese bemerkenswerte Bit:
git format-patch -k --stdout --full-index --cherry-pick --right-only \
--src-prefix=a/ --dst-prefix=b/ --no-renames --no-cover-letter \
"$revisions" ${restrict_revision+^$restrict_revision} \
>"$GIT_DIR/rebased-patches"
wo $revisions
dehnt sich aus, in diesem Fall zu master...B
.
Die --cherry-pick --right-only
Optionen git format-patch
sind nicht dokumentiert; Man muss wissen, in der git rev-list
Dokumentation für sie zu suchen.
Interactive Fütterungsmaterial verwendet eine andere Technik wählt aber noch keine Festschreibungen entfernt, die bereits in den vorgelagerten sind. Dies wird angezeigt, wenn Sie rebase
zu rebase -i
ändern, in dem die Rebase-Anweisungen aus einer noop
Zeile anstelle der erwarteten einzelnen Zeile bestehen.
Es sollte nicht sein, wenn ich deine Frage richtig lese. Hast du ein Skript um das zu reproduzieren? (Schreiben Sie etwas, das ein leeres Verzeichnis macht, läuft 'git init', erstellt Dateien und begeht, macht einen Zweig, mehrere Dateien erstellt, usw., und die letzte, läuft das' git rebase' das Problem zu zeigen.) – torek
Sure I‘ Ich schreibe einen wirklich schnell. – maxbart
Ok Sie dieses Skript kopieren und einfügen können http://pastebin.com/mPcmGCT5 edit: aktualisiert das Skript ein Verzeichnis zu machen (erstellt testing_git) – maxbart