Insérer une propagation avant la propagation racine dans Git?

j'ai déjà demandé comment écraser les deux premières commits dans un dépôt git.

bien que les solutions soient assez intéressantes et pas vraiment aussi tranchantes que d'autres choses en git, elles sont encore un peu du sac proverbial de la blessure si vous avez besoin de répéter la procédure de nombreuses fois tout au long du développement de votre projet.

donc, je préférerais ne souffrir qu'une fois, et ensuite être capable d'utiliser pour toujours le rebase interactive standard.

ce que je veux faire, alors, c'est avoir une première commit vide qui existe uniquement dans le but d'être la première. Pas de code, pas de rien. Je prends juste de l'espace pour que ce soit la base de rebase.

ma question est donc, ayant un dépôt existant, comment faire pour insérer une nouvelle propagation vide avant la première, et faire avancer tout le monde?

182
demandé sur Community 2009-03-14 08:31:11

14 réponses

à la Mi-2017 réponse

créer un nouveau commit complètement vide sans effets secondaires est probablement mieux fait en utilisant la plomberie de Git directement. Cela évite tout effet secondaire: ne pas toucher la copie de travail ou l'index, pas de branche temporaire à nettoyer, etc. So:

  1. pour créer une propagation, nous avons besoin d'un arbre de répertoire pour cela, donc nous en créons un vide en premier:

    tree=`git hash-object -wt tree --stdin < /dev/null`
    
  2. Maintenant, nous pouvons conclure qu'un commit autour d'elle:

    commit=`git commit-tree -m 'root commit' $tree`
    
  3. et maintenant nous pouvons rebaser sur cela:

    git rebase --onto $commit --root master
    

et c'est tout. Vous pouvez tout réorganiser en une seule doublure si vous connaissez assez bien votre coquille.

(N.B.: dans la pratique, j'utiliserais maintenant filter-branch . Modifier plus tard.)


Historique réponse (référencée par d'autres réponses)

Voici une implémentation plus propre de la même solution, en ce qu'elle fonctionne sans besoin de créer un dépôt supplémentaire, futz autour avec des télécommandes, et corriger une tête détachée:

# first you need a new empty branch; let's call it `newroot`
git checkout --orphan newroot
git rm -rf .

# then you apply the same steps
git commit --allow-empty -m 'root commit'
git rebase --onto newroot --root master
git branch -d newroot

voilà, vous avez fini sur master avec son histoire réécrite pour inclure une propagation de racine vide.


NB.: sur les anciennes versions de Git qui manque le --orphan passez à checkout , vous avez besoin de la plomberie pour créer une branche vide:

git symbolic-ref HEAD refs/heads/newroot
git rm --cached -r .
git clean -f -d
262
répondu Aristotle Pagaltzis 2017-05-02 11:17:05

Fusion d'Aristote Pagaltzis et Uwe Kleine-König réponses et Richard Bronosky commentaire.

git symbolic-ref HEAD refs/heads/newroot
git rm --cached -r .
git clean -f -d
# touch .gitignore && git add .gitignore # if necessary
git commit --allow-empty -m 'initial'
git rebase --onto newroot --root master
git branch -d newroot

(juste à mettre le tout en un seul endroit)

26
répondu Antony Hatchkins 2012-03-23 03:49:50

j'aime la réponse D'Aristote. Mais trouvé que pour un grand dépôt (>5000 commits) branche de filtre fonctionne mieux que rebase pour plusieurs raisons 1) c'est plus rapide 2) il ne nécessite pas d'intervention humaine en cas de conflit de fusion. 3) Il peut réécrire les étiquettes -- les préserver. Notez que filter-branch fonctionne parce qu'il n'y a pas de question sur le contenu de chaque propagation -- c'est exactement la même chose qu'avant ce 'rebase'.

mes pas sont:

# first you need a new empty branch; let's call it `newroot`
git symbolic-ref HEAD refs/heads/newroot
git rm --cached -r .
git clean -f -d

# then you apply the same steps
git commit --allow-empty -m 'root commit'

# then use filter-branch to rebase everything on newroot
git filter-branch --parent-filter 'sed "s/^$/-p <sha of newroot>/"' --tag-name-filter cat master

notez que les options' --tag-name-filter cat ' signifient que les tags seront réécrits pour pointer vers les propagations nouvellement créées.

11
répondu Kent 2013-03-29 15:55:35

j'ai utilisé avec succès des morceaux de réponse D'Aristote et de Kent:

# first you need a new empty branch; let's call it `newroot`
git checkout --orphan newroot
git rm -rf .
git commit --allow-empty -m 'root commit'
git filter-branch --parent-filter \
'sed "s/^$/-p <sha of newroot>/"' --tag-name-filter cat -- --all
# clean up
git checkout master
git branch -D newroot
# make sure your branches are OK first before this...
git for-each-ref --format="%(refname)" refs/original/ | \
xargs -n 1 git update-ref -d

cela permettra également de réécrire toutes les branches (pas seulement master ) en plus des tags.

5
répondu ldav1s 2014-07-23 12:35:10

git rebase --root --onto $emptyrootcommit

devrait faire l'affaire facilement

3
répondu Uwe Kleine-König 2011-01-17 19:27:47

j'ai été excité et j'ai écrit une version 'idempotent' de ce joli script ... il insérera toujours la même commit vide, et si vous l'exécutez deux fois, il ne change pas vos hash de commit à chaque fois. Donc, voici mon point de vue sur git-insert-vide-racine :

#!/bin/sh -ev
# idempotence achieved!
tmp_branch=__tmp_empty_root
git symbolic-ref HEAD refs/heads/$tmp_branch
git rm --cached -r . || true
git clean -f -d
touch -d '1970-01-01 UTC' .
GIT_COMMITTER_DATE='1970-01-01T00:00:00 +0000' git commit \
  --date='1970-01-01T00:00:00 +0000' --allow-empty -m 'initial'
git rebase --committer-date-is-author-date --onto $tmp_branch --root master
git branch -d $tmp_branch

vaut-il la complexité supplémentaire? peut-être pas, mais je vais utiliser ce un.

cela devrait également permettre d'effectuer cette opération sur plusieurs copies clonées de le repo, et se retrouvent avec les mêmes résultats, donc ils sont toujours compatibles ... test. .. Oui, cela fonctionne, mais il faut aussi supprimer et ajouter vos télécommandes à nouveau, par exemple:

git remote rm origin
git remote add --track master user@host:path/to/repo
3
répondu Sam Watkins 2013-02-06 13:36:15

voilà ce que j'ai trouvé:

# Just setting variables on top for clarity.
# Set this to the path to your original repository.
ORIGINAL_REPO=/path/to/original/repository

# Create a new repository…
mkdir fun
cd fun
git init
# …and add an initial empty commit to it
git commit --allow-empty -m "The first evil."

# Add the original repository as a remote
git remote add previous $ORIGINAL_REPO
git fetch previous

# Get the hash for the first commit in the original repository
FIRST=`git log previous/master --pretty=format:%H  --reverse | head -1`
# Cherry-pick it
git cherry-pick $FIRST
# Then rebase the remainder of the original branch on top of the newly 
# cherry-picked, previously first commit, which is happily the second 
# on this branch, right after the empty one.
git rebase --onto master master previous/master

# rebase --onto leaves your head detached, I don't really know why)
# So now you overwrite your master branch with the newly rebased tree.
# You're now kinda done.
git branch -f master
git checkout master
# But do clean up: remove the remote, you don't need it anymore
git remote rm previous
3
répondu kch 2014-07-23 12:34:24

je pense qu'utiliser git replace et git filter-branch est une meilleure solution que d'utiliser un git rebase :

  • meilleure performance
  • plus facile et moins risqué (vous pourriez vérifier votre résultat à chaque étape et annuler ce que vous avez fait...)
  • bien travailler avec plusieurs branches avec des résultats garantis

L'idée derrière cela est:

  • Créer un nouveau vide commettre loin dans le passé
  • remplacer l'ancienne commit root par une commit exactement similaire sauf que la nouvelle commit root est ajoutée en tant que parent
  • Vérifier que tout est comme prévu et exécuter git filter-branch
  • une fois de plus, vérifiez que tout va bien et nettoyez les fichiers git dont vous n'avez plus besoin

voici un script pour les 2 premiers pas:

#!/bin/bash
root_commit_sha=$(git rev-list --max-parents=0 HEAD)
git checkout --force --orphan new-root
find . -path ./.git -prune -o -exec rm -rf {} \; 2> /dev/null
git add -A
GIT_COMMITTER_DATE="2000-01-01T12:00:00" git commit --date==2000-01-01T12:00:00 --allow-empty -m "empty root commit"
new_root_commit_sha=$(git rev-parse HEAD)

echo "The commit '$new_root_commit_sha' will be added before existing root commit '$root_commit_sha'..."

parent="parent $new_root_commit_sha"
replacement_commit=$(
 git cat-file commit $root_commit_sha | sed "s/author/$parent\nauthor/" |
 git hash-object -t commit -w --stdin
) || return 3
git replace "$root_commit_sha" "$replacement_commit"

vous pourriez exécutez ce script sans risque (même si faire une sauvegarde avant de faire une action que vous n'avez jamais faite auparavant est une bonne idée ;)), et si le résultat n'est pas celui attendu, supprimez simplement les fichiers créés dans le dossier .git/refs/replace et essayez à nouveau ;)

une fois que vous avez vérifié que l'état du dépôt est ce que vous attendez, exécutez la commande suivante pour mettre à jour l'histoire de toutes les branches :

git filter-branch -- --all

Maintenant, vous devez voir 2 histoires, l'ancien et le nouveau (voir l'aide sur filter-branch pour plus d'informations). Vous pouvez comparer les 2 et vérifier à nouveau si tout va bien. Si vous êtes satisfait, supprimez les fichiers inutiles:

rm -rf ./.git/refs/original
rm -rf ./.git/refs/replace

vous pouvez revenir à votre branche master et supprimer la branche temporaire:

git checkout master
git branch -D new-root

Maintenant, tout doit être fait ;)

2
répondu Philippe 2017-07-06 20:50:51

Voici mon script bash basé sur Kent réponse avec des améliorations:

  • , il vérifie la version originale de la branche, pas juste master , quand il est fait;
  • j'ai essayé d'éviter la branche temporaire, mais git checkout --orphan ne fonctionne qu'avec une branche, pas un État de tête détaché, donc il est vérifié assez longtemps pour faire la nouvelle propagation racine et puis supprimé;
  • il utilise le hachage de la nouvelle root commit during the filter-branch (Kent a laissé une place pour le remplacement manuel);
  • l'opération filter-branch ne réécrit que les branches locales, pas trop de télécommandes
  • les métadonnées de l'auteur et du committeur sont normalisées de sorte que la propagation racine soit identique d'un dépôt à l'autre.

#!/bin/bash

# Save the current branch so we can check it out again later
INITIAL_BRANCH=`git symbolic-ref --short HEAD`
TEMP_BRANCH='newroot'

# Create a new temporary branch at a new root, and remove everything from the tree
git checkout --orphan "$TEMP_BRANCH"
git rm -rf .

# Commit this empty state with generic metadata that will not change - this should result in the same commit hash every time
export GIT_AUTHOR_NAME='nobody'
export GIT_AUTHOR_EMAIL='nobody@example.org'
export GIT_AUTHOR_DATE='2000-01-01T00:00:00+0000'
export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"
export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
git commit --allow-empty -m 'empty root'
NEWROOT=`git rev-parse HEAD`

# Check out the commit we just made and delete the temporary branch
git checkout --detach "$NEWROOT"
git branch -D "$TEMP_BRANCH"

# Rewrite all the local branches to insert the new root commit, delete the 
# original/* branches left behind, and check out the rewritten initial branch
git filter-branch --parent-filter "sed \"s/^$/-p $NEWROOT/\"" --tag-name-filter cat -- --branches
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
git checkout "$INITIAL_BRANCH"
1
répondu luxagen 2017-05-23 12:34:39

pour commuter la propagation racine:

tout d'abord, créez le commit que vous voulez comme le premier.

deuxièmement, commuter l'ordre des commits en utilisant:

git rebase-i --root

un éditeur apparaîtra avec les commits jusqu'à la propagation racine, comme:

pick 1234 vieille racine message

pick 0294 Un commit au milieu

pick 5678 commit que vous voulez mettre à la racine

vous pouvez alors placer le commit que vous voulez en premier, en le plaçant dans la première ligne. Dans l'exemple:

pick 5678 commit que vous voulez mettre à la racine

pick 1234 vieille racine message

pick 0294 Un commit dans le milieu

Sortir de l'éditeur de l'ordre de validation aura changé.

PS: pour changer les utilisations de l'éditeur git, exécutez:

git config -- global core.Editeur nom du programme du rédacteur "vous voulez utiliser

1
répondu tigre200 2017-02-18 16:17:06

Voici une simple doublure qui peut être utilisée pour ajouter une commit vide au début d'un dépôt, si vous avez oublié de créer une commit vide immédiatement après "git init":

git rebase --root --onto $(git commit-tree -m 'Initial commit (empty)' 4b825dc642cb6eb9a060e54bf8d69288fbee4904)
1
répondu Rory 2017-12-09 21:00:41

suite à la réponse Aristote Pagaltzis et d'autres, mais en utilisant des commandes plus simples

zsh% git checkout --orphan empty     
Switched to a new branch 'empty'
zsh% git rm --cached -r .
zsh% git clean -fdx
zsh% git commit --allow-empty -m 'initial empty commit'
[empty (root-commit) 64ea894] initial empty commit
zsh% git checkout master
Switched to branch 'master'
zsh% git rebase empty
First, rewinding head to replay your work on top of it...
zsh% git branch -d empty 
Deleted branch empty (was 64ea894).

remarque votre déclaration ne devrait pas contenir de modifications locales en attente d'être commutées.

Note git checkout --orphan fonctionnera avec les nouvelles versions de git, je suppose.

Remarque la plupart du temps git status donne des conseils utiles.

0
répondu ony 2013-02-18 08:28:04

lancer un nouveau dépôt.

ramenez votre date à la date de début que vous voulez.

faites tout comme vous auriez aimé le faire, ajustant le temps du système pour refléter quand vous auriez voulu le faire de cette façon. Extraire des fichiers du dépôt existant au besoin pour éviter beaucoup de tapage inutile.

quand vous arrivez à aujourd'hui, changez les dépôts et c'est fini.

si vous êtes juste fou (établi) mais raisonnablement intelligent (probablement, parce que vous devez avoir une certaine quantité de smarts pour penser des idées folles comme ceci) vous allez script le processus.

cela rendra aussi plus agréable quand vous décidez que vous voulez que le passé se soit produit d'une autre façon dans une semaine.

-5
répondu MarkusQ 2009-03-14 05:49:02

je sais que ce post est ancien mais cette page est la première quand googler"inserting commit git".

pourquoi compliquer les choses simples?

vous avez A-B-C et vous voulez A-B-Z-C.

  1. git rebase -i trunk (ou quelque chose avant B)
  2. modifier le choix pour modifier sur la ligne B
  3. faites vos modifications: git add ..
  4. git commit ( git commit --amend qui modifier B et ne pas créer de Z)

[Vous pouvez faire autant de git commit que vous voulez ici pour insérer plus valide. Bien sûr, vous pouvez avoir des problèmes avec l'étape 5, mais résoudre un conflit de fusion avec git est une compétence que vous devriez avoir. Si non, la pratique!]

  1. git rebase --continue

Simple, n'est-ce pas?

si vous comprenez git rebase , ajouter une propagation 'root' ne devrait pas être un problème.

Avoir du plaisir avec git!

-7
répondu Alex 2015-05-07 17:28:28