Git: Como poner en HEAD un estado (commit) anterior

No es muy habitual, pero en determinadas ocasiones uno quiere separar en varias ramas un gran cambio que venía trabajando en una sola rama. Esto sirve para poder tener un mejor control, al revisar pequeñas modificaciones.

Supongamos que tenemos un branch con los commits del 1 al 5, que incluyen una nueva funcionalidad A-Lógica y A-View (mezcladas en el branch), y quiero separar A-View a otro branch para poder hacer distintos pull requests.

En A elimino la parte relacionada a la View. A tendrá un commit 6  con la eliminación del código. Luego creo una nueva rama AA desde A.  Esa rama debe volver al estado del commit 5, sin perder el commit 6. Esto para poder luego hacer un diff A -> AA y ver en el mismo los cambios agregados para View.

A) 1-2-3-4-5-6

B) 1-2-3-4-5-6-5

La respuesta para lograr estos cambios, la encontré en un post nada menos que de Linus Torvalds (creador de Linux y Git). Donde el genio explica que no es una acción muy habitual pero que es muy simple y se soluciona con dos comandos.

git read-tree -u -m 5

git commit

 

Cito la respuesta de Linus:

This is actually an uncommonly easy operation for core git, but it’s a very unusual thing to want to do in general, so I don’t think there is any high-level command to do it directly. But it’s really easy to do with a single so-called “plumbing” command, namely “git read-tree”.

So the “core git” way to do it is to literally just do

git read-tree -u -m 3 git commit

(or use “–reset” instead of “-m” if you want to do it even in the presense unmerged entries).

What the above does is to literally just read the tree state at “3”, and make it the new index: the “-u” means that we also want to update the working tree to that state, and the “-m” means that we will merge in the old index stat information.

The commit then will then create the actual new commit: it will have the exact same tree as your commit ‘3’, but it will be a new commit (so call it 3′).

Of course, people have already pointed out that another easy way to do it is to just revert 5 and 4. That may be the more high-level way to do it, but the git-read-tree approach actually has the advantage that it will work even across merges etc, and it will be very unambiguous: we want *exactly* the state at commit 3 back, nothing else.

Linus