web-dev-qa-db-fra.com

Comment faire un commit Git dans le passé?

Je convertis tout dans Git pour mon usage personnel et j'ai trouvé d'anciennes versions d'un fichier déjà dans le référentiel. Comment puis-je le valider dans l'historique dans le bon ordre en fonction de la "date de modification" du fichier afin que je dispose d'un historique précis du fichier?

On m'a dit que quelque chose comme ça marcherait:

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

Les conseils qui vous ont été donnés sont erronés. Définir inconditionnellement GIT_AUTHOR_DATE dans un --env-filter réécrirait la date de chaque commit. De plus, il serait inhabituel d'utiliser git commit inside --index-filter.

Vous avez affaire à plusieurs problèmes indépendants ici.

Spécification de dates autres que “maintenant”

Chaque commit a deux dates: la date de l'auteur et la date du committer. Vous pouvez remplacer chacune d'elles en fournissant des valeurs via les variables d'environnement GIT_AUTHOR_DATE et GIT_COMMITTER_DATE pour toute commande qui écrit une nouvelle validation. Voir “Formats de date” dans git-commit (1) ou ci-dessous:

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

La seule commande qui écrit un nouveau commit en utilisation normale est git commit. Il a aussi un --date option qui vous permet de spécifier directement la date de l'auteur. Votre utilisation prévue comprend git filter-branch --env-filter utilise également les variables d’environnement mentionnées ci-dessus (elles font partie de "env" après lequel l’option est nommée; voir "Options" dans git-filter-branch (1) et le sous-jacent "plomberie". ”Commande git-commit-tree (1) .

Insérer un fichier dans un single ref Historique

Si votre référentiel est très simple (vous n’avez qu’une seule branche, pas de balises), vous pouvez probablement utiliser git rebase pour faire le travail.

Dans les commandes suivantes, utilisez le nom d'objet (hachage SHA-1) de la validation au lieu de "A". N'oubliez pas d'utiliser l'une des méthodes de "substitution de la date" lorsque vous exécutez 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

Si vous voulez mettre à jour A pour inclure le nouveau fichier (au lieu de créer un nouveau commit où il a été ajouté), utilisez git commit --amend au lieu de git commit. Le résultat ressemblerait à ceci:

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

Ce qui précède fonctionne tant que vous pouvez nommer le commit qui devrait être le parent de votre nouveau commit. Si vous voulez réellement que votre nouveau fichier soit ajouté via un nouveau commit root (pas de parents), vous avez besoin de quelque chose d'un peu différent:

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 est relativement nouveau (Git 1.7.2), mais il y a autres façons de faire la même chose qui fonctionnent sur les anciennes versions de Git.

Insérer un fichier dans un multi - ref Historique

Si votre référentiel est plus complexe (c’est-à-dire qu’il a plusieurs références (branches, balises, etc.)), vous devrez probablement utiliser git filter-branch. Avant d’utiliser git filter-branch, vous devez créer une copie de sauvegarde de l’ensemble du référentiel. Un simple tar Une archive de votre arbre de travail complet (y compris le répertoire .git) est suffisante. git filter-branch effectue des références de sauvegarde, mais il est souvent plus facile de récupérer un filtre pas tout à fait à droite en supprimant simplement votre .git répertoire et le restaurer à partir de votre sauvegarde.

Remarque: Les exemples ci-dessous utilisent la commande de niveau inférieur git update-index --add au lieu de git add. Vous pouvez utiliser git add, mais vous devez d’abord copier le fichier depuis un emplacement externe vers le chemin prévu (--index-filter exécute sa commande dans un GIT_WORK_TREE temporaire qui est vide).

Si vous voulez que votre nouveau fichier soit ajouté à chaque commit existant, procédez comme suit:

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

Je ne vois vraiment aucune raison de modifier les dates des commits existants avec --env-filter 'GIT_AUTHOR_DATE=…'. Si vous l'utilisiez, vous auriez dû le rendre conditionnel pour qu'il réécrive la date de chaque commit.

Si vous souhaitez que votre nouveau fichier n'apparaisse que dans les commits après des validations existantes ("A"), procédez comme suit:

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

Si vous souhaitez que le fichier soit ajouté via un nouveau commit à insérer au milieu de votre historique, vous devez générer le nouveau commit avant d'utiliser git filter-branch et ajouter --parent-filter to 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

Vous pouvez également prendre des dispositions pour que le fichier soit d'abord ajouté dans une nouvelle validation root: créez votre nouvelle validation root via la méthode “Orphan” à partir de la section git rebase (capturez-le dans new_commit), utilisez l'inconditionnel --index-filter, et un --parent-filter comme "sed -e \"s/^$/-p $new_commit/\"".

180
Chris Johnsen

Vous pouvez créer la validation comme d'habitude, mais définissez les variables d'environnement GIT_AUTHOR_DATE et GIT_COMMITTER_DATE vers les dates/heures appropriées.

Bien sûr, cela fera que le commit sera situé au sommet de votre branche (c'est-à-dire devant le HEAD actuel) en cours. Si vous souhaitez le repousser plus loin dans le dépôt, vous devez disons que vous avez cette histoire:

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

Et vous voulez que votre nouveau commit (marqué comme "X") apparaisse second:

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

Le moyen le plus simple serait de créer une branche à partir du premier commit, d'ajouter votre nouveau commit, puis de rebaser tous les autres commits par-dessus le nouveau. Ainsi:

$ 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
108
mipadi

Je sais que cette question est assez ancienne, mais c'est ce qui a réellement fonctionné pour moi:

git commit --date="10 day ago" -m "Your commit message" 
86
Hyder B.

Dans mon cas, au fil du temps, j'avais sauvegardé plusieurs versions de myfile comme myfile_bak, myfile_old, myfile_2010, backups/myfile, etc. Je voulais mettre l'historique de mon fichier dans git en utilisant leurs dates de modification. Alors renommez le plus ancien en myfile, git add myfile, Puis git commit --date=(modification date from ls -l) myfile, renommez le plus ancien en myfile, un autre git commit avec --date, repeat ...

Pour automatiser cela quelque peu, vous pouvez utiliser Shell-foo pour obtenir l’heure de modification du fichier. J'ai commencé avec ls -l Et cut, mais stat (1) est plus direct

 git commit --date = "` stat -c% y mon fichier`" mon fichier
32
skierpage

Dans mon cas, en utilisant l'option --date, mon processus git s'est bloqué. Peut-être que j'ai fait quelque chose de terrible. Et en conséquence, un fichier index.lock est apparu. J'ai donc manuellement supprimé les fichiers .lock du dossier .git et exécuté, pour que tous les fichiers modifiés soient validés aux dates passées et cela a fonctionné cette fois. Merci pour toutes les réponses ici.

git commit --date="`date --date='2 day ago'`" -am "update"
16
JstRoRR

Voici ce que j’utilise pour valider des modifications sur foo en N=1 _ jours dans le passé:

git add foo
git commit -m "Update foo"
git commit --amend --date="$(date -v-1d)"

Si vous souhaitez vous engager dans une date encore plus ancienne, disons 3 jours en arrière, changez simplement l'argument date: date -v-3d.

C'est vraiment utile lorsque vous oubliez de commettre quelque chose hier, par exemple.

MISE À JOUR : --date accepte également des expressions comme --date "3 days ago" ou même --date "yesterday". Nous pouvons donc le réduire à une ligne:

git add foo ; git commit --date "yesterday" -m "Update"
15
Vilson Vieira

Vous pouvez toujours changer une date sur votre ordinateur, faire un commit, puis changer la date en arrière et Push.

3
Nik

Pour faire un commit qui semble avoir été fait dans le passé, vous devez définir à la fois GIT_AUTHOR_DATE et GIT_COMMITTER_DATE:

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

date -d'...' peut être une date exacte comme 2019-01-01 12:00:00 ou parent comme 5 months ago 24 days ago.

Pour voir les deux dates dans le journal git, utilisez:

git log --pretty=fuller
3
mx0

Ou utilisez simplement une ligne de commande toolhttps: //github.com/artiebits/fake-git-history pour la générer pour une plage de données spécifique

1
rel1x