281 votes

Comment faire un commit Git dans le passé ?

Je suis en train de tout convertir à Git pour mon usage personnel et j'ai trouvé d'anciennes versions d'un fichier déjà dans le dépôt. Comment puis-je le commiter dans l'historique dans l'ordre correct selon la "date de modification" du fichier afin d'avoir 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

1 votes

Réponses courtes et simples : stackoverflow.com/a/34639957/2708266

58 votes

Je me demande si les personnes qui cherchent la réponse à cette question veulent simplement maintenir leur "série de contributions" sur GitHub de cette façon.

10 votes

@ZitRo oui. Et il suffit de mettre git commit --date="xxx day ago" -m "yyy" est suffisant à cet effet si quelqu'un se pose la question.

228voto

Chris Johnsen Points 50064

Le conseil qui vous a été donné est erroné. Le fait de définir inconditionnellement GIT_AUTHOR_DATE dans un fichier --env-filter réécrirait la date de chaque livraison. De plus, il serait inhabituel d'utiliser git commit à l'intérieur de --index-filter .

Vous avez affaire à des problèmes multiples et indépendants.

Spécification de dates autres que "maintenant".

Chaque commit a deux dates : la date de l'auteur et la date du committer. Vous pouvez les remplacer en fournissant des valeurs à travers les variables d'environnement GIT_AUTHOR_DATE et GIT_COMMITTER_DATE pour toute commande qui écrit un nouveau commit. Voir "Formats de date" dans git-commit(1) ou le 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 pendant une utilisation normale est git commit . Il dispose également d'un --date 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 (celles-ci font partie de la variable "env" dont l'option porte le nom ; cf. "Options" dans git-filter-branch(1) et la commande "plomberie" sous-jacente git-commit-tree(1) .

Insertion d'un fichier dans un seul réf. Histoire

Si votre dépôt est très simple (c'est-à-dire que vous n'avez qu'une seule branche, pas de tags), alors vous pouvez probablement utiliser git rebasement pour faire le travail.

Dans les commandes suivantes, utilisez le nom de l'objet (hachage SHA-1) du commit au lieu de "A". N'oubliez pas d'utiliser l'une des méthodes "date override" lorsque vous exécutez la commande 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é), alors 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 vraiment que votre nouveau fichier soit ajouté via un nouveau commit Root (sans parents), alors 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 récent (Git 1.7.2), mais il y a des d'autres façons de faire la même chose qui fonctionnent sur les anciennes versions de Git.

Insertion d'un fichier dans un système multi réf. Histoire

Si votre dépôt est plus complexe (c'est-à-dire qu'il a plus d'une référence (branches, tags, etc.)), alors vous aurez probablement besoin d'utiliser git filter-branch . Avant d'utiliser git filter-branch vous devez faire une copie de sauvegarde de l'ensemble de votre référentiel. Un simple tar de votre arbre de travail complet (y compris le répertoire .git) est suffisant. git filter-branch fait des références de sauvegarde, mais il est souvent plus facile de récupérer d'un filtrage incorrect en supprimant simplement le fichier .git 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 attendu ( --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, alors vous pouvez faire ceci :

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 pas vraiment de raison de changer les dates des commits existants avec --env-filter 'GIT_AUTHOR_DATE=…' . Si vous l'avez utilisé, vous auriez dû le rendre conditionnel afin qu'il réécrive la date pour chaque livraison.

Si vous voulez que votre nouveau fichier apparaisse seulement dans les commits après un commit existant ("A"), alors vous pouvez faire ceci :

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 voulez que le fichier soit ajouté via un nouveau commit qui doit être inséré au milieu de votre historique, alors vous devrez générer le nouveau commit avant d'utiliser la commande git filter-branch et ajouter --parent-filter à 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 vous arranger pour que le fichier soit d'abord ajouté dans un nouveau commit racine : créez votre nouveau commit racine via la méthode "orphan" de l'application git rebasement (le capturer dans new_commit ), utilisez la méthode inconditionnelle --index-filter et un --parent-filter comme "sed -e \"s/^$/-p $new_commit/\"" .

0 votes

Dans le premier exemple du cas d'utilisation, "Insertion d'un fichier dans un historique multiréférence" : existe-t-il un moyen d'avoir --index-filter appliqué aux commits retournés par git rev-list ? Pour l'instant, je vois un filtre d'indexation appliqué à un sous-ensemble de rev-list. J'apprécierais tout commentaire.

0 votes

@Hedgehog : Tous les exemples de "multi-ref" utilisent -- --all pour traiter tous les commits accessibles depuis n'importe quel ref. Le dernier exemple montre comment modifier seulement certains commits (testez simplement GIT_COMMIT pour ce que vous voulez). Pour ne modifier qu'une liste spécifique de commits, vous pouvez sauvegarder la liste avant le filtrage (par ex. git rev-list … >/tmp/commits_to_rewrite ), puis tester l'appartenance à l'intérieur du filtre (ex. if grep -qF "$GIT_COMMIT" /tmp/commits_to_rewrite; then git update-index … ). Qu'essayez-vous exactement d'accomplir ? Vous pourriez vouloir commencer une nouvelle question si c'est trop difficile à expliquer dans les commentaires.

0 votes

Dans mon cas, j'ai un petit projet que j'avais l'intention de placer sous contrôle de source. (Je ne l'avais pas fait, car c'est un projet solo et je n'avais pas décidé quel système utiliser). Mon "contrôle de source" était juste une question de duplication de toute l'arborescence de mon projet dans un nouveau répertoire, et de renommage du répertoire de la version précédente. Je suis nouveau dans Git (après avoir utilisé SCCS, RCS et un affreux dérivé propriétaire de RCS qui ressemble à CVS), alors ne vous inquiétez pas de me prendre de haut. J'ai l'impression que la réponse implique une variation de la section "Insérer un fichier dans un historique de référence unique". Correct ?

185voto

Hayder Abbass Points 452

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

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

148voto

mipadi Points 135410

Vous pouvez créer le commit comme d'habitude, mais lorsque vous le faites, définissez les variables d'environnement GIT_AUTHOR_DATE y GIT_COMMITTER_DATE aux dates appropriées.

Bien sûr, cela fera le commit à l'extrémité de votre branche (c'est-à-dire, devant le commit HEAD actuel). Si vous voulez le pousser plus loin dans le repo, vous devez être un peu plus fantaisiste. Disons que vous avez cet historique :

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

Et vous voulez que votre nouveau commit (marqué d'un "X") apparaisse deuxième :

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

La manière la plus simple serait de se brancher à partir du premier commit, d'ajouter votre nouveau commit, puis de rebaser tous les autres commits sur le nouveau. Comme ça :

$ 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

0 votes

L'un des fichiers devrait être le tout premier, dans mon cas.

28 votes

Je trouve toujours cette bonne réponse, mais je dois ensuite courir pour trouver le format de date, alors la voici pour la prochaine fois 'Fri Jul 26 19:32:10 2013 -0400'.

115 votes

GIT_AUTHOR_DATE='Fri Jul 26 19:32:10 2013 -0400' GIT_COMMITTER_DATE='Fri Jul 26 19:32:10 2013 -0400' git commit

41voto

skierpage Points 314

Dans mon cas, au fil du temps, j'avais sauvegardé un tas de versions de monfichier comme monfichier_bak, monfichier_ancien, monfichier_2010, backups/monfichier etc. Je voulais mettre l'historique de monfichier dans git en utilisant leurs dates de modification. Donc renommer le plus ancien en myfile, git add myfile alors git commit --date=(modification date from ls -l) myfile renommer le plus ancien en myfile, un autre commit git avec --date, répéter...

Pour automatiser quelque peu cette opération, vous pouvez utiliser shell-foo pour obtenir le temps de modification du fichier. J'ai commencé avec ls -l y cut mais stat(1) est plus direct

git commit --date="\`stat -c %y _myfile_\`" _myfile_

2 votes

Sympa. J'ai certainement eu des fichiers d'avant mon jours git pour laquelle je voulais utiliser l'heure de modification du fichier. Cependant, cela ne définit-il pas uniquement la date de validation (et non la date de l'auteur ?) ?

1 votes

De git-scm.com/docs/git-commit : --date "Remplace la date de l'auteur utilisée dans le commit." git log La date de l'article semble être la date de l'auteur, git log --pretty=fuller montre à la fois AuthorDate et CommitDate.

18 votes

El git commit option --date ne modifiera que le GIT_AUTHOR_DATE pas GIT_COMMITTER_DATE . Comme le Livre Pro Git explique : "L'auteur est la personne qui a rédigé l'œuvre à l'origine, tandis que le commettant est la personne qui a appliqué l'œuvre en dernier lieu." Dans le contexte des dates, le GIT_AUTHOR_DATE est la date à laquelle le fichier a été modifié, tandis que le paramètre GIT_COMMITTER_DATE est la date à laquelle il a été commis. Il est important ici de noter que par défaut, git log affiche les dates d'auteur en tant que "Date", mais utilise ensuite les dates de livraison pour le filtrage lorsqu'on lui donne un nom de domaine. --since option.

32voto

Vilson Vieira Points 489

Ce qui suit est ce que j'utilise pour valider les changements sur foo à 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 sur une date encore plus ancienne, disons 3 jours en arrière, il suffit de modifier l'option date argument : date -v-3d .

C'est très utile lorsque vous oubliez de valider quelque chose hier, par exemple.

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

git add foo ; git commit --date "yesterday" -m "Update"

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X