453 Stimmen

Wie kann ich eine Git-Übertragung in der Vergangenheit vornehmen?

Ich bin dabei, alles für meinen persönlichen Gebrauch auf Git umzustellen, und ich habe einige alte Versionen einer Datei gefunden, die sich bereits im Repository befinden. Wie übertrage ich sie in der richtigen Reihenfolge gemäß dem Änderungsdatum der Datei in den Verlauf, damit ich einen genauen Verlauf der Datei habe?

Mir wurde gesagt, dass so etwas funktionieren würde:

git filter-branch --env-filter="GIT_AUTHOR_DATE=... --index-filter "git commit path/to/file --date " --tag-name-filter cat -- --all

412voto

Hyder B. Punkte 9093

Ich weiß, dass diese Frage schon ziemlich alt ist, aber bei mir hat es tatsächlich funktioniert:

git commit --date="10 day ago" -m "Your commit message"

267voto

Chris Johnsen Punkte 199970

Die Ratschläge, die Sie erhalten haben, sind fehlerhaft. Das unbedingte Setzen von GIT_AUTHOR_DATE in einer --env-filter würde das Datum jeder Übertragung neu schreiben. Außerdem wäre es ungewöhnlich, wenn man Git Commit innerhalb --index-filter .

Sie haben es hier mit mehreren, voneinander unabhängigen Problemen zu tun.

Angabe von anderen Daten als "jetzt"

Jede Übertragung hat zwei Daten: das Datum des Autors und das Datum des Übertragenden. Sie können jedes Datum überschreiben, indem Sie die Umgebungsvariablen GIT_AUTHOR_DATE und GIT_COMMITTER_DATE für jeden Befehl, der eine neue Übertragung schreibt, mit Werten versehen. Siehe "Datumsformate" in git-commit(1) oder die unten stehenden:

Git internal format = <unix timestamp> <time zone offset>, e.g.  1112926393 +0200
RFC 2822            = e.g. Thu, 07 Apr 2005 22:13:13 +0200
ISO 8601            = e.g. 2005-04-07T22:13:13

Der einzige Befehl, der bei normalem Gebrauch eine neue Übertragung schreibt, ist Git Commit . Es hat auch eine --date mit der Sie das Autorendatum direkt angeben können. Ihre voraussichtliche Verwendung umfasst git filter-branch --env-filter verwendet auch die oben erwähnten Umgebungsvariablen (diese sind Teil der "env", nach der die Option benannt ist; siehe "Optionen" in git-filter-branch(1) und der zugrundeliegende "Klempner"-Befehl git-commit-tree(1) .

Einfügen einer Datei in eine einzelne ref Geschichte

Wenn Ihr Repository sehr einfach ist (d.h. Sie haben nur einen einzigen Zweig, keine Tags), dann können Sie wahrscheinlich Git-Rebase um die Arbeit zu erledigen.

Verwenden Sie in den folgenden Befehlen den Objektnamen (SHA-1-Hash) der Übertragung anstelle von "A". Vergessen Sie nicht, eine der "date override"-Methoden zu verwenden, wenn Sie Git Commit .

---A---B---C---o---o---o   master

git checkout master
git checkout A~0
git add path/to/file
git commit --date='whenever'
git tag ,new-commit -m'delete me later'
git checkout -
git rebase --onto ,new-commit A
git tag -d ,new-commit

---A---N                      (was ",new-commit", but we delete the tag)
        \
         B'---C'---o---o---o   master

Wenn Sie A aktualisieren wollten, um die neue Datei einzuschließen (anstatt eine neue Übergabe zu erstellen, wo sie hinzugefügt wurde), dann verwenden Sie git commit --amend anstelle von git commit . Das Ergebnis würde wie folgt aussehen:

---A'---B'---C'---o---o---o   master

Dies funktioniert, solange Sie den Namen des Commits angeben können, der der Elternteil Ihres neuen Commits sein soll. Wenn Sie möchten, dass Ihre neue Datei über einen neuen Root-Commit (ohne Eltern) hinzugefügt wird, dann brauchen Sie etwas anderes:

B---C---o---o---o   master

git checkout master
git checkout --orphan new-root
git rm -rf .
git add path/to/file
GIT_AUTHOR_DATE='whenever' git commit
git checkout -
git rebase --root --onto new-root
git branch -d new-root

N                       (was new-root, but we deleted it)
 \
  B'---C'---o---o---o   master

git checkout --orphan ist relativ neu (Git 1.7.2), aber es gibt andere Möglichkeiten, das Gleiche zu tun die mit älteren Versionen von Git funktionieren.

Einfügen einer Datei in ein Multi- ref Geschichte

Wenn Ihr Repository komplexer ist (d.h. mehr als eine Referenz hat (Zweige, Tags usw.)), dann müssen Sie wahrscheinlich git filter-branch . Vor der Verwendung git filter-branch sollten Sie eine Sicherungskopie Ihres gesamten Repositorys erstellen. Eine einfache tar Archiv Ihres gesamten Arbeitsbaums (einschließlich des .git-Verzeichnisses) ist ausreichend. git filter-branch erstellt zwar Sicherungskopien, aber es ist oft einfacher, eine nicht ganz korrekte Filterung wiederherzustellen, indem Sie einfach Ihre .git und die Wiederherstellung aus Ihrer Sicherung.

Hinweis: Die folgenden Beispiele verwenden den untergeordneten Befehl git update-index --add anstelle von git add . Sie können verwenden Git hinzufügen aber Sie müssen die Datei zunächst von einem externen Speicherort in den erwarteten Pfad kopieren ( --index-filter führt seinen Befehl in einem temporären GIT_WORK_TREE aus, der leer ist).

Wenn Sie möchten, dass Ihre neue Datei zu jeder bestehenden Übertragung hinzugefügt wird, können Sie dies tun:

new_file=$(git hash-object -w path/to/file)
git filter-branch \
  --index-filter \
    'git update-index --add --cacheinfo 100644 '"$new_file"' path/to/file' \
  --tag-name-filter cat \
  -- --all
git reset --hard

Ich sehe nicht wirklich einen Grund, die Daten der vorhandenen Übertragungen mit --env-filter 'GIT_AUTHOR_DATE=…' . Wenn Sie es verwenden würden, müssten Sie es an Bedingungen knüpfen, damit das Datum bei jeder Übertragung neu geschrieben wird.

Wenn Sie möchten, dass Ihre neue Datei nur in den Commits nach einem bestehenden Commit ("A") auftaucht, dann können Sie dies tun:

file_path=path/to/file
before_commit=$(git rev-parse --verify A)
file_blob=$(git hash-object -w "$file_path")
git filter-branch \
  --index-filter '

    if x=$(git rev-list -1 "$GIT_COMMIT" --not '"$before_commit"') &&
       test -n "$x"; then
         git update-index --add --cacheinfo 100644 '"$file_blob $file_path"'
    fi

  ' \
  --tag-name-filter cat \
  -- --all
git reset --hard

Wenn Sie möchten, dass die Datei über eine neue Übergabe hinzugefügt wird, die in der Mitte der Historie eingefügt wird, müssen Sie die neue Übergabe vor der Verwendung von git filter-branch und hinzufügen --parent-filter a git filter-branch :

file_path=path/to/file
before_commit=$(git rev-parse --verify A)

git checkout master
git checkout "$before_commit"
git add "$file_path"
git commit --date='whenever'
new_commit=$(git rev-parse --verify HEAD)
file_blob=$(git rev-parse --verify HEAD:"$file_path")
git checkout -

git filter-branch \
  --parent-filter "sed -e s/$before_commit/$new_commit/g" \
  --index-filter '

    if x=$(git rev-list -1 "$GIT_COMMIT" --not '"$new_commit"') &&
       test -n "$x"; then
         git update-index --add --cacheinfo 100644 '"$file_blob $file_path"'
    fi

  ' \
  --tag-name-filter cat \
  -- --all
git reset --hard

Sie können auch dafür sorgen, dass die Datei zuerst in einem neuen Root-Commit hinzugefügt wird: Erstellen Sie Ihren neuen Root-Commit mit der Methode "orphan" aus der Git-Rebase Abschnitt (erfassen Sie ihn in new_commit ), verwenden Sie die unbedingte --index-filter und eine --parent-filter wie "sed -e \"s/^$/-p $new_commit/\"" .

210voto

mipadi Punkte 377834

Sie können die Übergabe wie gewohnt erstellen, aber wenn Sie die Übergabe durchführen, setzen Sie die Umgebungsvariablen GIT_AUTHOR_DATE y GIT_COMMITTER_DATE zu den entsprechenden Zeitpunkten.

Dies führt natürlich dazu, dass der Commit an der Spitze Ihres Zweigs steht (d.h. vor dem aktuellen HEAD-Commit). Wenn Sie den Commit weiter nach hinten im Repository verschieben wollen, müssen Sie sich etwas einfallen lassen. Sagen wir, Sie haben diese Geschichte:

o--o--o--o--o

Und Sie möchten, dass Ihre neue Übergabe (markiert als "X") erscheint zweite :

o--X--o--o--o--o

Der einfachste Weg wäre, vom ersten Commit zu verzweigen, den neuen Commit hinzuzufügen und dann alle anderen Commits auf den neuen Commit zu rebasen. Zum Beispiel so:

$ git checkout -b new_commit $desired_parent_of_new_commit
$ git add new_file
$ GIT_AUTHOR_DATE='your date' GIT_COMMITTER_DATE='your date' git commit -m 'new (old) files'
$ git checkout master
$ git rebase new_commit
$ git branch -d new_commit

87voto

Manav Mehra Punkte 399

Dies ist eine alte Frage, aber ich bin kürzlich darüber gestolpert.

git commit --date='year-month-day hour:minutes:seconds' -m "message"

Es würde also in etwa so aussehen: git commit --date='2021-01-01 12:12:00' -m "message" ordnungsgemäß funktionierte, und überprüfte es am GitHub y GitLab .

76voto

mx0 Punkte 5600

Um eine Übergabe zu machen, die so aussieht, als wäre sie in der Vergangenheit gemacht worden, müssen Sie beide GIT_AUTHOR_DATE y GIT_COMMITTER_DATE :

GIT_AUTHOR_DATE=$(date -d'...') GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" git commit -m '...'

donde date -d'...' kann genaues Datum sein wie 2019-01-01 12:00:00 oder relativ wie 5 months ago 24 days ago .

Um beide Daten im Git-Log zu sehen, verwenden Sie:

git log --pretty=fuller

Dies gilt auch für Merge Commits:

GIT_AUTHOR_DATE=$(date -d'...') GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" git merge <branchname> --no-ff

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X