Sie sind auf Seite 1von 519

Machine Translated by Google

Machine Translated by Google

Git profesional

Scott Chacón, Ben Straub

Versión 2.1.337-2-gab3dabe, 2022-03-01


Machine Translated by Google

Tabla de contenido
licencia _ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ............................. ............ 1

Prefacio de Scott Chacón . . . . . . . . . . . . . . . . . . . . . . . . . . . . ............................. ............ 2

Prefacio de Ben Straub . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ............................. ............3

Dedicatorias. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ............................. ............ 4

Colaboradores. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ............................. ............5

Introducción. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ............................. ............6

Empezando. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ............................. ............8

Sobre el control de versiones. . . . . . . . . . . . . . . . . . . . . . . . . . . . ............................. ............8

Una breve historia de Git . . . . . . . . . . . . . . . ............................. ........................ 12

¿Qué es Git? . . . . . . . . . . . . . . . . . . . . . . . . ............................. ........................ 12

La Línea de Comando . . . . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . dieciséis

Instalando Git . . . . . . . . . . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . dieciséis

Configuración de Git por primera vez. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

Obteniendo ayuda. . . . . . . . . . . . . . . . . . . . . . . . ............................. ........................ 22

resumen _ . . . . . . . . . . . . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . 23

Conceptos básicos de Git. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ........................ 24

Conseguir un repositorio Git . . . . . . . . . . . . . ............................. ........................ 24

Registro de cambios en el repositorio . ............................. . . . . . . . . . . . . . . . . . . . . . . . . 26

Visualización del historial de confirmaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

Deshacer cosas. . . . . . . . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . 45

Trabajar con controles remotos . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . 49

Etiquetado _ . . . . . . . . . . . . . . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . 55

Alias de Git . . . . . . . . . . . . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . 61

resumen _ . . . . . . . . . . . . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . 62

Ramificación Git. . . . . . . . . . . . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . 63

Ramas en pocas palabras . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . 63

Ramificación básica y fusión. . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . 70

Gestión de Sucursales. . . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . 79

Flujos de trabajo ramificados . . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . 82

Sucursales remotas . . . . . . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . 86

Rebasando . . . . . . . . . . . . . . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . 95

resumen _ . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104

Git en el servidor. . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105

Los Protocolos . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105

Obtener Git en un servidor . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

Generación de su clave pública SSH . . . . . . . . . . . . . . . . . . . . . .................................... 112

Configuración del servidor . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

Git Daemon . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116


Machine Translated by Google

HTTP inteligente . . . . . . . . . . . ............................. .................................... 117

GitWeb . . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119

GitLab . . . . . . . . . . . . . . . . ............................. .................................... 121

Opciones alojadas de terceros. . . . . . . . . . . . . . . . . . . . . . . . . . .................................... 124

resumen _ . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125

Git distribuido. . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126

Flujos de trabajo distribuidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126

Contribuyendo a un Proyecto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129

Mantenimiento de un proyecto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151

resumen _ . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166

GitHub. . . . . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167

Instalación y configuración de cuentas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167

Contribuyendo a un Proyecto . . . . . . . . . . . . . . . . . . . . . . . . . . . . .................................... 172

Mantenimiento de un proyecto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192

Administrar una organización. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207

Creación de secuencias de comandos de GitHub . . . . . .


............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210

resumen _ . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219

Herramientas Git . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220

Selección de revisión . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220

Puesta en escena interactiva. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229

Almacenamiento y limpieza . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233

Firmando su trabajo. . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239

Buscando _ . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243

Reescribiendo la Historia. . . . . . ............................. .................................... 247

Restablecer Desmitificado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255

Fusión avanzada. . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275

Rerrere . . . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294

Depurando con Git . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300

Submódulos. . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303

Agrupación. . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325

Reemplazar _ . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329

Almacenamiento de Credenciales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337

resumen _ . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342

Personalizando Git. . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343

Configuración Git . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343

Atributos Git. . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353

Ganchos Git . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361

Un ejemplo de política aplicada por Git . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364

resumen _ . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374

Git y otros sistemas. . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375

Git como cliente . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375


Machine Translated by Google

Migrando a Git . . . . . . . ............................. .................................... 412

resumen _ . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430

Internos de Git. . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431

Fontanería y Porcelanato. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431

Objetos Git . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 432

Referencias Git. . . . . . . . . ............................. .................................... 442

Paquetes de archivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446

La especificación de referencia . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449

Protocolos de transferencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452

Mantenimiento y Recuperación de Datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457

Variables de entorno . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464

resumen _ . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470

Apéndice A: Git en otros entornos. . . . . . . . . . . . . . . . . .................................... 471

Interfaces gráficas . . . ............................. .................................... 471

Git en Visual Studio . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 476

Git en Visual Studio Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . .................................... 477

Git en IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 477

Git en Sublime Text . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 478

Git en Bash . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 478

Git en Zsh . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479

Git en PowerShell . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481

resumen _ . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483

Apéndice B: Incrustación de Git en sus aplicaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 484

Git de línea de comandos . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 484

Libgit2. . . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 484

JGit. . . . . . . . . . . . . . . . . . . go- ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489

git . . . . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493

Dulwich . . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494

Apéndice C: Comandos Git . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496

Instalación y configuración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496

Conseguir y Crear Proyectos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498

Instantáneas básicas. . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 499

Ramificación y fusión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501

Compartir y actualizar proyectos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503

Inspección y Comparación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505

Depuración . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506

parcheo _ . . . . . . . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507

Correo electrónico. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507

Sistemas Externos . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509

administración _ . . . . . . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509

Comandos de fontanería . . ............................. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 510


Machine Translated by Google

índice _ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 511
Machine Translated by Google

Licencia
Este trabajo está bajo la licencia Creative Commons Attribution-NonCommercial-ShareAlike 3.0

Licencia sin portar. Para ver una copia de esta licencia, visite https://creativecommons.org/licenses/by-nc sa/3.0 o envíe una carta
a Creative Commons, PO Box 1866, Mountain View, CA 94042, EE. UU.

1
Machine Translated by Google

Prefacio de Scott Chacón


Bienvenido a la segunda edición de Pro Git. La primera edición se publicó hace ya más de cuatro años.
Desde entonces, muchas cosas han cambiado y, sin embargo, muchas cosas importantes no lo han hecho. Si bien la
mayoría de los comandos y conceptos básicos siguen siendo válidos hoy en día, ya que el equipo central de Git es fantástico
para mantener las cosas compatibles con versiones anteriores, ha habido algunas adiciones y cambios significativos en la
comunidad que rodea a Git. La segunda edición de este libro está destinada a abordar esos cambios y actualizar el libro para
que pueda ser más útil para el nuevo usuario.

Cuando escribí la primera edición, Git todavía era una herramienta relativamente difícil de usar y apenas adoptada por los
piratas informáticos más duros. Estaba comenzando a ganar fuerza en ciertas comunidades, pero no había alcanzado ni de
cerca la ubicuidad que tiene hoy. Desde entonces, casi todas las comunidades de código abierto lo han adoptado. Git ha
hecho un progreso increíble en Windows, en la explosión de interfaces gráficas de usuario para todas las plataformas, en
soporte IDE y en uso comercial. El Pro Git de hace cuatro años no sabe nada de eso. Uno de los principales objetivos de
esta nueva edición es abordar todas aquellas novedades

fronteras en la comunidad Git.

La comunidad de código abierto que usa Git también se ha disparado. Cuando originalmente me senté a escribir el libro hace
casi cinco años (me tomó un tiempo sacar la primera versión), acababa de comenzar a trabajar en una compañía muy poco
conocida que desarrollaba un sitio web de alojamiento de Git llamado GitHub. En el momento de la publicación, tal vez había
unos miles de personas usando el sitio y solo cuatro de nosotros trabajando en él.
Mientras escribo esta introducción, GitHub anuncia nuestro proyecto alojado número 10 millones, con casi 5 millones de
cuentas de desarrollador registradas y más de 230 empleados. Lo ames o lo odies, GitHub ha cambiado en gran medida
grandes franjas de la comunidad de código abierto de una manera que era apenas concebible cuando me senté a escribir la
primera edición.

Escribí una pequeña sección en la versión original de Pro Git sobre GitHub como un ejemplo de Git alojado con el que nunca
me sentí muy cómodo. No me gustó mucho que estaba escribiendo lo que sentía que era esencialmente un recurso de la
comunidad y también hablando de mi empresa en él. Si bien todavía no me gusta ese conflicto de intereses, la importancia
de GitHub en la comunidad de Git es inevitable. En lugar de un ejemplo de alojamiento de Git, he decidido convertir esa parte
del libro en una descripción más profunda de qué es GitHub y cómo usarlo de manera efectiva. Si va a aprender a usar Git,
saber cómo usar GitHub lo ayudará a participar en una gran comunidad, lo cual es valioso sin importar qué host de Git decida
usar para su propio código.

El otro gran cambio en el tiempo transcurrido desde la última publicación ha sido el desarrollo y el aumento del protocolo
HTTP para las transacciones de la red Git. La mayoría de los ejemplos del libro se han cambiado a HTTP desde SSH porque
es mucho más simple.

Ha sido increíble ver crecer a Git en los últimos años desde un sistema de control de versiones relativamente oscuro hasta
dominar básicamente el control de versiones comerciales y de código abierto. Estoy feliz de que a Pro Git le haya ido tan
bien y además haya podido ser uno de los pocos libros técnicos del mercado.

que es bastante exitoso y completamente de código abierto.

Espero que disfrutes de esta edición actualizada de Pro Git.

2
Machine Translated by Google

Prefacio de Ben Straub


La primera edición de este libro es lo que me enganchó a Git. Esta fue mi introducción a un estilo de creación de
software que se sentía más natural que cualquier cosa que hubiera visto antes. Para entonces, había sido
desarrollador durante varios años, pero este fue el giro correcto que me envió por un camino mucho más
interesante que el que estaba tomando.

Ahora, años después, soy colaborador de una importante implementación de Git, he trabajado para la mayor
empresa de alojamiento de Git y he viajado por el mundo enseñando a la gente sobre Git. Cuando Scott me
preguntó si estaría interesado en trabajar en la segunda edición, ni siquiera tuve que pensar.

Ha sido un gran placer y un privilegio trabajar en este libro. Espero que te ayude tanto como lo hizo
me.

3
Machine Translated by Google

dedicatorias
A mi esposa, Becky, sin la cual esta aventura nunca hubiera comenzado. — ben

Esta edición está dedicada a mis niñas. A mi esposa Jessica que me ha apoyado durante todos estos
años ya mi hija Josephine, que me apoyará cuando sea demasiado mayor para saber lo que está pasando.
—Scott

4
Machine Translated by Google

Colaboradores
Dado que este es un libro de código abierto, hemos recibido varias erratas y cambios de contenido donados a
lo largo de los años. Aquí están todas las personas que han contribuido a la versión en inglés de Pro Git como
proyecto de código abierto. Gracias a todos por ayudar a hacer de este un libro mejor para todos.

Colaboradores a partir de ab3dabe:

5
Machine Translated by Google

Introducción
Estás a punto de pasar varias horas de tu vida leyendo sobre Git. Tomemos un minuto para explicar lo que tenemos reservado para
usted. He aquí un breve resumen de los diez capítulos y los tres apéndices de este libro.

En el Capítulo 1, cubriremos los sistemas de control de versiones (VCS) y los conceptos básicos de Git: sin detalles técnicos, solo qué
es Git, por qué surgió en una tierra llena de VCS, qué lo distingue y por qué tanta gente. lo están usando. Luego, explicaremos cómo
descargar Git y configurarlo por primera vez si aún no lo tiene en su sistema.

En el Capítulo 2, repasaremos el uso básico de Git: cómo usar Git en el 80 % de los casos que encontrará con mayor frecuencia.
Después de leer este capítulo, debería poder clonar un repositorio, ver lo que ha sucedido en el historial del proyecto, modificar archivos
y aportar cambios. Si el libro se quema espontáneamente en este punto, ya debería ser bastante útil manejando Git en el tiempo que le
toma ir a buscar otra copia.

El Capítulo 3 trata sobre el modelo de bifurcación en Git, a menudo descrito como la característica principal de Git. Aquí aprenderá lo
que realmente diferencia a Git del resto. Cuando haya terminado, puede sentir la necesidad de pasar un momento tranquilo reflexionando
sobre cómo vivía antes de que la ramificación de Git fuera parte de su vida.

El Capítulo 4 cubrirá Git en el servidor. Este capítulo es para aquellos de ustedes que desean configurar Git dentro de su organización
o en su propio servidor personal para la colaboración. También exploraremos varias opciones alojadas si prefiere dejar que alguien más
se encargue de eso por usted.

El Capítulo 5 repasará con todo detalle varios flujos de trabajo distribuidos y cómo lograrlos con Git. Cuando haya terminado con este
capítulo, debería poder trabajar de manera experta con múltiples repositorios remotos, usar Git por correo electrónico y hacer
malabarismos hábilmente con numerosas ramas remotas y parches contribuidos.

El Capítulo 6 cubre en profundidad el servicio de hospedaje y las herramientas de GitHub. Cubrimos registrarse y administrar una
cuenta, crear y usar repositorios Git, flujos de trabajo comunes para contribuir a proyectos y aceptar contribuciones a los suyos, la
interfaz programática de GitHub y muchos pequeños consejos para facilitarle la vida en general.

El Capítulo 7 trata sobre los comandos avanzados de Git. Aquí aprenderá sobre temas como dominar el aterrador comando 'restablecer',
usar la búsqueda binaria para identificar errores, editar el historial, seleccionar la revisión en detalle y mucho más. Este capítulo
completará tu conocimiento de Git para que seas verdaderamente un
Maestro.

El Capítulo 8 trata sobre la configuración de su entorno Git personalizado. Esto incluye la configuración de secuencias de comandos
de enlace para hacer cumplir o fomentar políticas personalizadas y el uso de ajustes de configuración del entorno para que pueda
trabajar de la manera que desee. También cubriremos la creación de su propio conjunto de secuencias de comandos para hacer cumplir
una política de confirmación personalizada.

El Capítulo 9 trata sobre Git y otros VCS. Esto incluye usar Git en un mundo de Subversion (SVN) y convertir proyectos de otros VCS
a Git. Muchas organizaciones todavía usan SVN y no están dispuestas a cambiar, pero en este punto habrás aprendido el increíble
poder de Git, y este capítulo te muestra cómo lidiar si todavía tienes que usar un servidor SVN. También cubrimos cómo importar
proyectos de

6
Machine Translated by Google

varios sistemas diferentes en caso de que convenzas a todos para dar el paso.

El capítulo 10 profundiza en las turbias pero hermosas profundidades de las funciones internas de Git. Ahora que
sabe todo acerca de Git y puede manejarlo con poder y gracia, puede pasar a discutir cómo Git almacena sus
objetos, cuál es el modelo de objeto, detalles de los archivos de paquete, protocolos de servidor y más. A lo largo
del libro, nos referiremos a secciones de este capítulo en caso de que tenga ganas de profundizar en ese punto;
pero si es como nosotros y desea profundizar en los detalles técnicos, es posible que desee leer primero el Capítulo 10.
Te lo dejamos a ti.

En el Apéndice A, observamos una serie de ejemplos del uso de Git en varios entornos específicos. Cubrimos
una serie de diferentes GUI y entornos de programación IDE en los que puede querer usar Git y lo que está
disponible para usted. Si está interesado en una descripción general del uso de Git en su shell, su IDE o su editor
de texto, eche un vistazo aquí.

En el Apéndice B, exploramos la creación de scripts y la extensión de Git a través de herramientas como libgit2 y JGit.
Si está interesado en escribir herramientas personalizadas complejas y rápidas y necesita acceso a Git de bajo nivel,
aquí es donde puede ver cómo se ve ese panorama.

Finalmente, en el Apéndice C, revisamos todos los comandos principales de Git uno por uno y revisamos en qué
parte del libro los cubrimos y qué hicimos con ellos. Si desea saber en qué parte del libro usamos un comando
específico de Git, puede buscarlo aquí.

Empecemos.

7
Machine Translated by Google

Empezando
Este capítulo tratará sobre cómo comenzar con Git. Comenzaremos explicando algunos antecedentes sobre las herramientas de control
de versiones, luego pasaremos a cómo hacer que Git se ejecute en su sistema y finalmente cómo configurarlo para comenzar a trabajar.
Al final de este capítulo, debe comprender por qué existe Git, por qué debe usarlo y debe estar preparado para hacerlo.

Acerca del control de versiones


¿Qué es el "control de versiones" y por qué debería importarle? El control de versiones es un sistema que registra los cambios en un
archivo o conjunto de archivos a lo largo del tiempo para que pueda recuperar versiones específicas más adelante. Para los ejemplos de
este libro, utilizará el código fuente del software como archivos cuya versión se controla, aunque en realidad puede hacer esto con casi
cualquier tipo de archivo en una computadora.

Si usted es un diseñador gráfico o web y desea conservar todas las versiones de una imagen o diseño (lo que sin duda desearía), un
Sistema de control de versiones (VCS) es una opción muy inteligente. Le permite revertir archivos seleccionados a un estado anterior,
revertir todo el proyecto a un estado anterior, comparar cambios a lo largo del tiempo, ver quién modificó por última vez algo que podría
estar causando un problema, quién introdujo un problema y cuándo, y más. El uso de un VCS generalmente también significa que si
arruina las cosas o pierde archivos, puede recuperarlos fácilmente. Además, obtienes todo esto por muy pocos gastos generales.

Sistemas de control de versiones locales

El método de control de versiones elegido por muchas personas es copiar archivos en otro directorio (quizás un directorio con marca de
tiempo, si son inteligentes). Este enfoque es muy común porque es muy simple, pero también es increíblemente propenso a errores. Es
fácil olvidar en qué directorio se encuentra y accidentalmente escribir en el archivo incorrecto o copiar archivos que no deseaba.

Para lidiar con este problema, los programadores desarrollaron hace mucho tiempo VCS locales que tenían una base de datos simple
que mantenía todos los cambios en los archivos bajo control de revisión.

8
Machine Translated by Google

Figura 1. Control de versión local

Una de las herramientas VCS más populares fue un sistema llamado RCS, que todavía se distribuye con muchas
computadoras en la actualidad. RCS funciona manteniendo conjuntos de parches (es decir, las diferencias entre archivos)
en un formato especial en el disco; luego puede volver a crear el aspecto de cualquier archivo en cualquier momento
agregando todos los parches.

Sistemas de control de versiones centralizado

El siguiente gran problema que encuentran las personas es que necesitan colaborar con desarrolladores en otros sistemas.
Para hacer frente a este problema, se desarrollaron los Sistemas de Control de Versiones Centralizados (CVCS). Estos
sistemas (como CVS, Subversion y Perforce) tienen un solo servidor que contiene todos los archivos versionados y varios
clientes que extraen archivos de ese lugar central. Durante muchos años, este ha sido el estándar para el control de
versiones.

9
Machine Translated by Google

Figura 2. Control de versiones centralizado

Esta configuración ofrece muchas ventajas, especialmente sobre los VCS locales. Por ejemplo, todos saben hasta
cierto punto lo que están haciendo los demás en el proyecto. Los administradores tienen un control detallado sobre
quién puede hacer qué, y es mucho más fácil administrar un CVCS que tratar con bases de datos locales en cada
cliente.

Sin embargo, esta configuración también tiene algunas desventajas serias. El más obvio es el único punto de falla que
representa el servidor centralizado. Si ese servidor se cae durante una hora, entonces durante esa hora nadie puede
colaborar en absoluto o guardar cambios versionados en cualquier cosa en la que estén trabajando. Si el disco duro en
el que se encuentra la base de datos central se corrompe y no se han realizado las copias de seguridad adecuadas,
perderá absolutamente todo: el historial completo del proyecto, excepto las instantáneas individuales que la gente tenga
en sus máquinas locales. Los VCS locales sufren el mismo problema: cada vez que tiene el historial completo del
proyecto en un solo lugar, corre el riesgo de perderlo todo.

Sistemas de control de versiones distribuidos

Aquí es donde intervienen los sistemas de control de versiones distribuidos (DVCS). En un DVCS (como Git, Mercurial,
Bazaar o Darcs), los clientes no solo revisan la última instantánea de los archivos; más bien, reflejan completamente el
repositorio, incluido su historial completo. Por lo tanto, si algún servidor muere y estos sistemas estaban colaborando a
través de ese servidor, cualquiera de los repositorios del cliente se puede copiar en el servidor para restaurarlo. Cada
clon es realmente una copia de seguridad completa de todos los datos.

10
Machine Translated by Google

Figura 3. Control de versiones distribuido

Además, muchos de estos sistemas manejan bastante bien tener varios repositorios remotos con los que
pueden trabajar, por lo que puede colaborar con diferentes grupos de personas de diferentes maneras
simultáneamente dentro del mismo proyecto. Esto le permite configurar varios tipos de flujos de trabajo
que no son posibles en sistemas centralizados, como modelos jerárquicos.

11
Machine Translated by Google

Una breve historia de Git


Como ocurre con muchas grandes cosas de la vida, Git comenzó con un poco de destrucción creativa y una feroz controversia.

El kernel de Linux es un proyecto de software de código abierto de alcance bastante amplio. Durante los primeros años del mantenimiento
del kernel de Linux (1991–2002), los cambios en el software se transmitían como parches y archivos comprimidos. En 2002, el proyecto
del kernel de Linux comenzó a usar un DVCS patentado llamado BitKeeper.

En 2005, la relación entre la comunidad que desarrolló el kernel de Linux y la empresa comercial que desarrolló BitKeeper se rompió y
se revocó el estado gratuito de la herramienta. Esto llevó a la comunidad de desarrollo de Linux (y en particular a Linus Torvalds, el
creador de Linux) a desarrollar su propia herramienta basada en algunas de las lecciones que aprendieron mientras usaban BitKeeper.
Algunos de los objetivos del nuevo sistema eran los siguientes:

• Velocidad

• Diseño simple

• Fuerte soporte para el desarrollo no lineal (miles de ramas paralelas)

• Totalmente distribuido

• Capaz de manejar proyectos grandes como el kernel de Linux de manera eficiente (velocidad y tamaño de datos)

Desde su nacimiento en 2005, Git ha evolucionado y madurado para ser fácil de usar y, sin embargo, conservar estas cualidades
iniciales. Es increíblemente rápido, es muy eficiente con proyectos grandes y tiene un increíble sistema de bifurcación para el desarrollo
no lineal (ver Git Branching).

¿Qué es Git?

Entonces, ¿qué es Git en pocas palabras? Esta es una sección importante para absorber, porque si comprende qué es Git y los
fundamentos de cómo funciona, entonces usar Git de manera efectiva probablemente será mucho más fácil para usted. A medida que
aprende Git, intente despejar su mente de las cosas que puede saber sobre otros VCS, como CVS, Subversion o Perforce; hacerlo lo
ayudará a evitar confusiones sutiles al usar la herramienta.
Aunque la interfaz de usuario de Git es bastante similar a estos otros VCS, Git almacena y piensa en la información de una manera muy
diferente, y comprender estas diferencias lo ayudará a evitar confundirse mientras lo usa.

Instantáneas, no diferencias
La principal diferencia entre Git y cualquier otro VCS (Subversion y sus amigos incluidos) es la forma en que Git piensa acerca de sus
datos. Conceptualmente, la mayoría de los otros sistemas almacenan información como una lista de cambios basados en archivos.
Estos otros sistemas (CVS, Subversion, Perforce, Bazaar, etc.) piensan en la información que almacenan como un conjunto de archivos
y los cambios realizados en cada archivo a lo largo del tiempo (esto se describe comúnmente como control de versión basado en delta ).

12
Machine Translated by Google

Figura 4. Almacenamiento de datos como cambios en una versión base de cada archivo

Git no piensa ni almacena sus datos de esta manera. En cambio, Git piensa en sus datos más como una serie de
instantáneas de un sistema de archivos en miniatura. Con Git, cada vez que confirma o guarda el estado de su
proyecto, Git básicamente toma una imagen de cómo se ven todos sus archivos en ese momento y almacena una
referencia a esa instantánea. Para ser eficiente, si los archivos no han cambiado, Git no vuelve a almacenar el
archivo, solo un enlace al archivo anterior idéntico que ya ha almacenado. Git piensa en sus datos más como un
flujo de instantáneas.

Figura 5. Almacenamiento de datos como instantáneas del proyecto a lo largo del tiempo

Esta es una distinción importante entre Git y casi todos los demás VCS. Hace que Git reconsidere casi todos los
aspectos del control de versiones que la mayoría de los otros sistemas copiaron de la generación anterior. Esto
hace que Git se parezca más a un mini sistema de archivos con algunas herramientas increíblemente poderosas
integradas, en lugar de simplemente un VCS. Exploraremos algunos de los beneficios que obtiene al pensar en sus
datos de esta manera cuando cubramos la bifurcación de Git en Git Branching.

Casi todas las operaciones son locales

La mayoría de las operaciones en Git solo necesitan archivos y recursos locales para funcionar; por lo general, no
se necesita información de otra computadora en su red. Si está acostumbrado a un CVCS donde la mayoría de las
operaciones tienen una sobrecarga de latencia de red, este aspecto de Git le hará pensar que los dioses de la
velocidad han bendecido a Git con poderes sobrenaturales. Como tiene todo el historial del proyecto en su disco
local, la mayoría de las operaciones parecen casi instantáneas.

13
Machine Translated by Google

Por ejemplo, para explorar el historial del proyecto, Git no necesita ir al servidor para obtener el historial y mostrárselo,
simplemente lo lee directamente desde su base de datos local. Esto significa que ve el historial del proyecto casi al
instante. Si desea ver los cambios introducidos entre la versión actual de un archivo y el archivo de hace un mes, Git
puede buscar el archivo de hace un mes y hacer un cálculo de diferencia local, en lugar de tener que pedirle a un
servidor remoto que lo haga. o extraiga una versión anterior del archivo del servidor remoto para hacerlo localmente.

Esto también significa que es muy poco lo que no puede hacer si está desconectado o desconectado de la VPN. Si te
subes a un avión o a un tren y quieres hacer un poco de trabajo, puedes comprometerte felizmente (con tu copia local ,
¿recuerdas?) hasta que llegues a una conexión de red para cargar. Si va a casa y no puede hacer que su cliente VPN
funcione correctamente, aún puede trabajar. En muchos otros sistemas, hacerlo es imposible o doloroso. En Perforce,
por ejemplo, no puedes hacer mucho cuando no estás conectado al servidor; en Subversion y CVS, puede editar
archivos, pero no puede enviar cambios a su base de datos (porque su base de datos está fuera de línea). Esto puede
no parecer un gran problema, pero es posible que se sorprenda de la gran diferencia que puede hacer.

Git tiene integridad

Todo en Git se suma antes de que se almacene y luego se hace referencia a esa suma de verificación. Esto significa
que es imposible cambiar el contenido de cualquier archivo o directorio sin que Git lo sepa.
Esta funcionalidad está integrada en Git en los niveles más bajos y es parte integral de su filosofía. No puede perder
información en tránsito ni dañar el archivo sin que Git pueda detectarlo.

El mecanismo que usa Git para esta suma de verificación se llama hash SHA-1. Esta es una cadena de 40 caracteres
compuesta por caracteres hexadecimales (0–9 y a–f) y calculada en función del contenido de una estructura de archivo
o directorio en Git. Un hash SHA-1 se parece a esto:

24b9da6552252987aa493b52f8696cd6d3b00373

Verá estos valores hash por todas partes en Git porque los usa mucho. De hecho, Git almacena todo en su base de
datos no por nombre de archivo sino por el valor hash de su contenido.

Git generalmente solo agrega datos

Cuando realiza acciones en Git, casi todas solo agregan datos a la base de datos de Git. Es difícil hacer que el sistema
haga algo que no se pueda deshacer o que borre datos de alguna manera. Al igual que con cualquier VCS, puede
perder o estropear los cambios que aún no ha confirmado, pero después de confirmar una instantánea en Git, es muy
difícil perderla, especialmente si envía regularmente su base de datos a otro repositorio.

Esto hace que usar Git sea un placer porque sabemos que podemos experimentar sin el peligro de estropear gravemente
las cosas. Para obtener una visión más detallada de cómo Git almacena sus datos y cómo puede recuperar los datos
que parecen perdidos, consulte Deshacer cosas.

los tres estados

Preste atención ahora: esto es lo principal que debe recordar sobre Git si desea que el resto de su proceso de
aprendizaje transcurra sin problemas. Git tiene tres estados principales en los que pueden residir sus archivos:
modificado, preparado y confirmado:

14
Machine Translated by Google

• Modificado significa que ha cambiado el archivo pero aún no lo ha enviado a su base de datos.

• Preparado significa que ha marcado un archivo modificado en su versión actual para pasar a su próxima
instantánea de confirmación.

• Comprometido significa que los datos se almacenan de forma segura en su base de datos local.

Esto nos lleva a las tres secciones principales de un proyecto de Git: el árbol de trabajo, el área de preparación y el directorio de Git.

Figura 6. Árbol de trabajo, área de preparación y directorio de Git

El árbol de trabajo es una comprobación única de una versión del proyecto. Estos archivos se extraen de la base de datos comprimida
en el directorio Git y se colocan en el disco para que los use o modifique.

El área de preparación es un archivo, generalmente contenido en su directorio Git, que almacena información sobre lo que se incluirá
en su próxima confirmación. Su nombre técnico en la jerga de Git es "índice", pero la frase "área de ensayo" funciona igual de bien.

El directorio de Git es donde Git almacena los metadatos y la base de datos de objetos para su proyecto. Esta es la parte más
importante de Git y es lo que se copia cuando clonas un repositorio desde otra computadora.

El flujo de trabajo básico de Git es algo así:

1. Modifica archivos en su árbol de trabajo.

2. Organiza de forma selectiva solo aquellos cambios que desea que formen parte de su próxima confirmación, lo que agrega
sólo aquellos cambios en el área de preparación.

3. Realiza una confirmación, que toma los archivos tal como están en el área de preparación y almacena esa instantánea
permanentemente a su directorio Git.

Si una versión particular de un archivo está en el directorio de Git, se considera confirmada. si ha sido

15
Machine Translated by Google

modificado y se agregó al área de preparación, se organiza. Y si se modificó desde que se desprotegió pero no se ha puesto en
escena, se modifica. En Conceptos básicos de Git, aprenderá más sobre estos estados y cómo puede aprovecharlos u omitir la
parte preparada por completo.

La línea de comando
Hay muchas maneras diferentes de usar Git. Existen las herramientas de línea de comandos originales y hay muchas interfaces
gráficas de usuario de diferentes capacidades. Para este libro, usaremos Git en la línea de comando. Por un lado, la línea de
comandos es el único lugar donde puede ejecutar todos los comandos de Git: la mayoría de las GUI implementan solo un
subconjunto parcial de la funcionalidad de Git para simplificar. Si sabe cómo ejecutar la versión de línea de comandos,
probablemente también pueda descubrir cómo ejecutar la versión GUI, mientras que lo contrario no es necesariamente cierto.
Además, si bien la elección del cliente gráfico es una cuestión de gusto personal, todos los usuarios tendrán las herramientas de
línea de comandos instaladas y disponibles.

Por lo tanto, esperamos que sepa cómo abrir Terminal en macOS o Símbolo del sistema o PowerShell en Windows. Si no sabe
de lo que estamos hablando aquí, es posible que deba detenerse e investigar rápidamente para poder seguir el resto de los
ejemplos y descripciones de este libro.

Instalando Git
Antes de comenzar a usar Git, debe hacerlo disponible en su computadora. Incluso si ya está instalado, probablemente sea una
buena idea actualizar a la última versión. Puede instalarlo como un paquete o mediante otro instalador, o descargar el código
fuente y compilarlo usted mismo.

Este libro fue escrito usando Git versión 2.8.0. Aunque la mayoría de los comandos que usamos deberían

ÿ funcionar incluso en versiones antiguas de Git, es posible que algunos de ellos no funcionen o que actúen
de manera ligeramente diferente si está usando una versión anterior. Dado que Git es excelente para
preservar la compatibilidad con versiones anteriores, cualquier versión posterior a la 2.8 debería funcionar bien.

Instalación en Linux
Si desea instalar las herramientas básicas de Git en Linux a través de un instalador binario, generalmente puede hacerlo a
través de la herramienta de administración de paquetes que viene con su distribución. Si está en Fedora (o cualquier distribución
basada en RPM estrechamente relacionada, como RHEL o CentOS), puede usar dnf:

$ sudo dnf instalar git-all

Si tiene una distribución basada en Debian, como Ubuntu, pruebe con apt:

$ sudo apt install git-all

Para obtener más opciones, hay instrucciones para instalar en varias distribuciones diferentes de Unix en el sitio web de Git, en
https://git-scm.com/download/linux.

dieciséis
Machine Translated by Google

Instalación en macOS
Hay varias formas de instalar Git en una Mac. Lo más fácil es probablemente instalar las herramientas de línea de
comandos de Xcode. En Mavericks (10.9) o superior, puede hacer esto simplemente intentando ejecutar git desde la
Terminal la primera vez.

$ git --versión

Si aún no lo tiene instalado, le pedirá que lo instale.

Si desea una versión más actualizada, también puede instalarla a través de un instalador binario. Se mantiene un
instalador de macOS Git y está disponible para su descarga en el sitio web de Git, en https://git-scm.com/download/
mac .

Figura 7. Instalador de Git macOS

Instalación en Windows
También hay algunas formas de instalar Git en Windows. La compilación más oficial está disponible para descargar
en el sitio web de Git. Simplemente vaya a https://git-scm.com/download/win y la descarga comenzará
automáticamente. Tenga en cuenta que este es un proyecto llamado Git para Windows, que es independiente de Git
mismo; para obtener más información al respecto, vaya a https://gitforwindows.org.

Para obtener una instalación automática, puede usar el paquete Git Chocolatey. Tenga en cuenta que el paquete
Chocolatey es mantenido por la comunidad.

17
Machine Translated by Google

Instalación desde la fuente

En cambio, algunas personas pueden encontrar útil instalar Git desde la fuente, porque obtendrá la versión más
reciente. Los instaladores binarios tienden a estar un poco atrasados, aunque como Git ha madurado en los últimos
años, esto ha hecho una diferencia menor.

Si desea instalar Git desde la fuente, debe tener las siguientes bibliotecas de las que depende Git: autotools, curl,
zlib, openssl, expat y libiconv. Por ejemplo, si está en un sistema que tiene dnf (como Fedora) o apt-get (como un
sistema basado en Debian), puede usar uno de estos comandos para instalar las dependencias mínimas para
compilar e instalar Git. binarios:

$ sudo dnf install dh-autoreconf curl-devel expat-devel gettext-devel \ openssl-


devel perl-devel zlib-devel $ sudo apt-get install dh-autoreconf libcurl4-gnutls-
dev libexpat1-dev \ gettext libz-dev libssl- desarrollador

Para poder agregar la documentación en varios formatos (doc, html, info), se requieren estas dependencias
adicionales:

$ sudo dnf install asciidoc xmlto docbook2X $


sudo apt-get install asciidoc xmlto docbook2x

ÿ Los usuarios de RHEL


habilitar y derivados
el repositorio de RHEL
EPEL como CentOS
para descargar y Scientific
el paquete Linux deberán
docbook2X .

Si está utilizando una distribución basada en Debian (Debian/Ubuntu/Ubuntu-derivatives), también necesita el


paquete install-info :

$ sudo apt-get install install-info

Si está utilizando una distribución basada en RPM (Fedora/RHEL/RHEL-derivatives), también necesita el paquete
getopt (que ya está instalado en una distribución basada en Debian):

$ sudo dnf instalar getopt

Además, si está utilizando derivados de Fedora/RHEL/RHEL, debe hacer esto:

$ sudo ln -s /usr/bin/db2x_docbook2texi /usr/bin/docbook2x-texi

debido a las diferencias de nombres binarios.

Cuando tenga todas las dependencias necesarias, puede seguir adelante y obtener el tarball de lanzamiento
etiquetado más reciente de varios lugares. Puede obtenerlo a través del sitio kernel.org, en https://www.kernel.org/
pub/software/scm/git , o el espejo en el sitio web de GitHub, en https://github.com/git/git/releases. Es

18
Machine Translated by Google

generalmente un poco más claro cuál es la última versión en la página de GitHub, pero la página kernel.org también
tiene firmas de lanzamiento si desea verificar su descarga.

Luego compila e instala:

$ tar -zxf git-2.8.0.tar.gz $ cd


git-2.8.0 $ make configure
$ ./configure --prefix=/usr $
make all doc info $ sudo
make install install-doc install-
html install- información

Una vez hecho esto, también puede obtener Git a través de Git mismo para obtener actualizaciones:

$ git clone git://git.kernel.org/pub/scm/git/git.git

Configuración de Git por primera vez

Ahora que tiene Git en su sistema, querrá hacer algunas cosas para personalizar su entorno Git. Debería tener que
hacer estas cosas solo una vez en cualquier computadora; se quedarán entre actualizaciones. También puede cambiarlos
en cualquier momento ejecutando los comandos nuevamente.

Git viene con una herramienta llamada git config que le permite obtener y establecer variables de configuración que
controlan todos los aspectos de cómo se ve y opera Git. Estas variables se pueden almacenar en tres lugares diferentes:

1. Archivo [ruta]/etc/gitconfig : Contiene valores aplicados a cada usuario en el sistema y todos sus repositorios. Si pasa
la opción --system a git config, lee y escribe desde este archivo específicamente. Debido a que este es un archivo
de configuración del sistema, necesitaría privilegios administrativos o de superusuario para realizar cambios en él.

2. Archivo ~/.gitconfig o ~/.config/git/config : valores específicos personalmente para usted, el usuario. Puede hacer que
Git lea y escriba en este archivo específicamente pasando la opción --global , y esto afecta a todos los repositorios
con los que trabaja en su sistema.

3. archivo de configuración en el directorio Git (es decir, .git /config) de cualquier repositorio que esté usando
actualmente: Específico para ese único repositorio. Puede obligar a Git a leer y escribir en este archivo con la opción
--local , pero de hecho, esa es la opción predeterminada. Como era de esperar, debe estar ubicado en algún lugar
de un repositorio de Git para que esta opción funcione correctamente.

Cada nivel anula los valores del nivel anterior, por lo que los valores en .git/config superan a los de [ruta]/etc/gitconfig.

En los sistemas Windows, Git busca el archivo .gitconfig en el directorio $HOME (C:\Users\$USER para la mayoría de
las personas). También sigue buscando [ruta]/etc/gitconfig, aunque es relativo a la raíz de MSys, que es donde decidas
instalar Git en tu sistema Windows cuando ejecutas el instalador. Si usa la versión 2.x o posterior de Git para Windows,
también hay un archivo de configuración de nivel de sistema en C:\Documentos y configuraciones\Todos los
usuarios\Datos de programa\Git\config en Windows XP, y en

19
Machine Translated by Google

C:\ProgramData\Git\config en Windows Vista y posteriores. Este archivo de configuración solo se puede cambiar con git config -f <archivo>
como administrador.

Puede ver todas sus configuraciones y de dónde provienen usando:

$ git config --list --show-origen

Tu identidad
Lo primero que debe hacer cuando instala Git es configurar su nombre de usuario y dirección de correo electrónico. Esto es importante
porque cada confirmación de Git usa esta información, y está integrada de forma inmutable en las confirmaciones que comienzas a crear:

$ git config --usuario global.nombre "John Doe"


$ git config --usuario global.email johndoe@example.com

Nuevamente, debe hacer esto solo una vez si pasa la opción --global , porque Git siempre usará esa información para cualquier cosa que
haga en ese sistema. Si desea anular esto con un nombre o dirección de correo electrónico diferente para proyectos específicos, puede
ejecutar el comando sin la opción --global cuando esté en ese proyecto.

Muchas de las herramientas GUI le ayudarán a hacer esto cuando las ejecute por primera vez.

Tu editor
Ahora que su identidad está configurada, puede configurar el editor de texto predeterminado que se usará cuando Git necesite que escriba
un mensaje. Si no está configurado, Git usa el editor predeterminado de su sistema.

Si desea utilizar un editor de texto diferente, como Emacs, puede hacer lo siguiente:

$ git config --global core.editor emacs

En un sistema Windows, si desea utilizar un editor de texto diferente, debe especificar la ruta completa a su archivo ejecutable. Esto puede
ser diferente dependiendo de cómo esté empaquetado su editor.

En el caso de Notepad++, un popular editor de programación, es probable que desee utilizar la versión de 32 bits, ya que en el momento
de escribir este artículo, la versión de 64 bits no es compatible con todos los complementos. Si está en un sistema Windows de 32 bits o
tiene un editor de 64 bits en un sistema de 64 bits, escribirá algo como esto:

$ git config --global core.editor "'C:/Archivos de programa/Bloc de notas++/bloc de notas+


+.exe' -multiInst -notabbar -nosession -noPlugin"

20
Machine Translated by Google

Vim, Emacs y Notepad++ son editores de texto populares que suelen utilizar los desarrolladores en sistemas

ÿ basados en Unix como Linux y macOS o un sistema Windows. Si está utilizando otro editor o una versión
de 32 bits, busque instrucciones específicas sobre cómo configurar su editor favorito con Git en los
comandos git config core.editor.

Es posible que, si no configura su editor de esta manera, entre en un estado realmente confuso cuando Git

ÿ intente iniciarlo. Un ejemplo en un sistema Windows puede incluir una operación de Git finalizada
prematuramente durante una edición iniciada por Git.

Su nombre de sucursal predeterminado

Por defecto, Git creará una rama llamada master cuando crees un nuevo repositorio con git init.
Desde la versión 2.28 de Git en adelante, puede establecer un nombre diferente para la rama inicial.

Para configurar main como el nombre de rama predeterminado, haz lo siguiente:

$ git config --global init.defaultBranch principal

Comprobación de su configuración

Si desea verificar sus ajustes de configuración, puede usar el comando git config --list para enumerar todos los ajustes que Git
puede encontrar en ese punto:

$ git config --list


user.name=John
Doe
user.email=johndoe@example.com color.status=auto
color.branch=auto
color.interactive=auto
color.diff=auto
...

Es posible que vea claves más de una vez, porque Git lee la misma clave de diferentes archivos ([ruta]/etc/gitconfig y ~/.gitconfig,
por ejemplo). En este caso, Git usa el último valor para cada clave única que ve.

También puedes comprobar lo que Git cree que es el valor de una clave específica escribiendo git config <key>:

$ git config nombre de usuario


John Doe

21
Machine Translated by Google

Dado que Git puede leer el mismo valor de la variable de configuración de más de un archivo, es
posible que tenga un valor inesperado para uno de estos valores y no sepa por qué. En casos como
ese, puede consultar a Git sobre el origen de ese valor, y le dirá qué archivo de configuración tuvo la
última palabra para establecer ese valor:
ÿ
$ git config --show-origin rerere.autoActualizar
archivo:/home/johndoe/.gitconfig false

Obteniendo ayuda

Si alguna vez necesita ayuda mientras usa Git, hay tres formas equivalentes de obtener la ayuda completa de la página
del manual (página de manual) para cualquiera de los comandos de Git:

$ git help <verbo>


$ git <verbo> --help
$ man git-<verbo>

Por ejemplo, puede obtener la ayuda de la página de manual para el comando git config ejecutando esto:

$ git ayuda configuración

Estos comandos son buenos porque puedes acceder a ellos desde cualquier lugar, incluso sin conexión. Si las páginas
de manual y este libro no son suficientes y necesita ayuda en persona, puede probar los canales #git , #github o #gitlab
en el servidor IRC de Libera Chat, que se puede encontrar en https://libera. chat/. Estos canales se llenan regularmente
con cientos de personas que conocen muy bien Git y, a menudo, están dispuestas a ayudar.

Además, si no necesita la ayuda completa de la página de manual, pero solo necesita una actualización rápida de las
opciones disponibles para un comando de Git, puede solicitar la salida de "ayuda" más concisa con la opción -h , como
en :

22
Machine Translated by Google

$ git añadir -h
uso: git add [<opciones>] [--] <rutaespecificación>...

-n, --ejecución en seco carrera en seco

-v, --verbose ser detallado

-i, --interactive -p, --patch -e, recolección interactiva


--edit -f, --force -u, --update -- seleccionar tíos de forma interactiva
renormalize edite la diferencia actual y aplique
permitir agregar archivos ignorados de otra manera
actualizar archivos rastreados
renormalizar EOL de archivos rastreados (implica -u)
-N, --intento-de-agregar registre solo el hecho de que la ruta se agregará más tarde
-A, --todos -- agregue cambios de todos los archivos rastreados y no rastreados
ignorar-eliminación -todos) ignorar las rutas eliminadas en el árbol de trabajo (igual que --no

--actualizar no agregar, solo actualizar el índice


--ignorar-errores simplemente omita los archivos que no se pueden agregar debido a
errores
--ignore-missing --chmod compruebe si, incluso si faltan, los archivos se ignoran en la ejecución en seco
(+|-)x --pathspec-from-file anular el bit ejecutable de los archivos enumerados
<archivo> leer rutaspec del archivo
--pathspec-file-nul con --pathspec-from-file, los elementos pathspec son
separados con carácter NUL

Resumen
Debes tener una comprensión básica de lo que es Git y en qué se diferencia de cualquier sistema centralizado.
sistemas de control de versiones que puede haber estado usando anteriormente. Ahora también debería tener un trabajo
versión de Git en su sistema que está configurada con su identidad personal. Ahora es el momento de aprender algunos
Conceptos básicos de Git.

23
Machine Translated by Google

Conceptos básicos de Git

Si solo puede leer un capítulo para comenzar con Git, es este. Este capítulo cubre todos los comandos básicos que necesita para
hacer la gran mayoría de las cosas que eventualmente dedicará su tiempo a hacer con Git. Al final del capítulo, debería poder
configurar e inicializar un repositorio, comenzar y detener el seguimiento de archivos y preparar y confirmar cambios. También le
mostraremos cómo configurar Git para ignorar ciertos archivos y patrones de archivo, cómo deshacer errores rápida y fácilmente,
cómo navegar por el historial de su proyecto y ver cambios entre confirmaciones, y cómo empujar y extraer desde repositorios
remotos. .

Obtener un repositorio de Git


Por lo general, obtiene un repositorio de Git de una de estas dos maneras:

1. Puede tomar un directorio local que actualmente no está bajo control de versiones y convertirlo en un Git
repositorio, o

2. Puede clonar un repositorio Git existente desde otro lugar.

En cualquier caso, termina con un repositorio de Git en su máquina local, listo para trabajar.

Inicializar un repositorio en un directorio existente


Si tiene un directorio de proyecto que actualmente no está bajo control de versiones y desea comenzar a controlarlo con Git, primero
debe ir al directorio de ese proyecto. Si nunca ha hecho esto, se ve un poco diferente según el sistema que esté ejecutando:

para Linux:

$ cd /home/usuario/mi_proyecto

para macOS:

$ cd /Usuarios/usuario/mi_proyecto

para ventanas:

$ cd C:/Usuarios/usuario/mi_proyecto

y escriba:

$ git inicializar

Esto crea un nuevo subdirectorio llamado .git que contiene todos los archivos de repositorio necesarios: un esqueleto de repositorio
de Git. En este punto, todavía no se realiza un seguimiento de nada en su proyecto. Ver Git Internals para

24
Machine Translated by Google

más información sobre exactamente qué archivos están contenidos en el directorio .git que acaba de crear.

Si desea comenzar a controlar la versión de los archivos existentes (en lugar de un directorio vacío), probablemente
debería comenzar a rastrear esos archivos y realizar una confirmación inicial. Puede lograrlo con algunos comandos
de git add que especifican los archivos que desea rastrear, seguidos de una confirmación de git:

$ git add *.c


$ git add LICENSE
$ git commit -m 'Versión inicial del proyecto'

Repasaremos lo que hacen estos comandos en solo un minuto. En este punto, tiene un repositorio de Git con
archivos rastreados y una confirmación inicial.

Clonación de un repositorio existente

Si desea obtener una copia de un repositorio de Git existente, por ejemplo, un proyecto al que le gustaría contribuir,
el comando que necesita es git clone. Si está familiarizado con otros VCS como Subversion, notará que el comando
es "clonar" y no "pagar". Esta es una distinción importante: en lugar de obtener solo una copia de trabajo, Git recibe
una copia completa de casi todos los datos que tiene el servidor. Cada versión de cada archivo para el historial del
proyecto se extrae de forma predeterminada cuando ejecuta git clone. De hecho, si el disco de su servidor se
corrompe, a menudo puede usar casi cualquiera de los clones en cualquier cliente para restablecer el servidor al
estado en el que se encontraba cuando fue clonado (puede perder algunos ganchos del lado del servidor y demás,
pero todos los datos versionados estarían allí; consulte Obtener Git en un servidor para obtener más detalles).

Clonas un repositorio con git clone <url>. Por ejemplo, si desea clonar la biblioteca enlazable de Git llamada libgit2,
puede hacerlo así:

$ git clonar https://github.com/libgit2/libgit2

Eso crea un directorio llamado libgit2, inicializa un directorio .git dentro de él, extrae todos los datos de ese repositorio
y extrae una copia de trabajo de la última versión. Si ingresa al nuevo directorio libgit2 que se acaba de crear, verá
los archivos del proyecto allí, listos para trabajar o usar.

Si desea clonar el repositorio en un directorio con un nombre diferente a libgit2, puede especificar el nuevo nombre
del directorio como argumento adicional:

$ clon de git https://github.com/libgit2/libgit2 mylibgit

Ese comando hace lo mismo que el anterior, pero el directorio de destino se llama mylibgit.

Git tiene varios protocolos de transferencia diferentes que puede usar. El ejemplo anterior usa el protocolo https:// ,
pero también puede ver git:// o user@server:path/to/repo.git, que usa el protocolo de transferencia SSH. Obtener
Git en un servidor presentará todas las opciones disponibles que el servidor puede configurar para acceder a su
repositorio de Git y las ventajas y desventajas de cada una.

25
Machine Translated by Google

Registro de cambios en el repositorio


En este punto, debe tener un repositorio Git de buena fe en su máquina local y una copia de pago o de trabajo de todos
sus archivos frente a usted. Por lo general, querrá comenzar a realizar cambios y confirmar instantáneas de esos cambios
en su repositorio cada vez que el proyecto alcance un estado que desee registrar.

Recuerde que cada archivo en su directorio de trabajo puede estar en uno de dos estados: rastreado o no rastreado .
Los archivos rastreados son archivos que estaban en la última instantánea, así como cualquier archivo recién preparado;
pueden ser sin modificar, modificados o escalonados. En resumen, los archivos rastreados son archivos que Git conoce.

Los archivos sin seguimiento son todo lo demás: cualquier archivo en su directorio de trabajo que no estaba en su última
instantánea y que no está en su área de preparación. Cuando clone un repositorio por primera vez, todos sus archivos
serán rastreados y no modificados porque Git los revisó y no ha editado nada.

A medida que edita archivos, Git los ve como modificados, porque los ha cambiado desde su última confirmación.
A medida que trabaja, prepara selectivamente estos archivos modificados y luego confirma todos esos cambios
preparados, y el ciclo se repite.

Figura 8. El ciclo de vida del estado de tus archivos

Comprobación del estado de sus archivos

La herramienta principal que utiliza para determinar qué archivos están en qué estado es el comando git status . Si
ejecuta este comando directamente después de un clon, debería ver algo como esto:

$ git status
On branch
master Su rama está actualizada con 'origin/
master'. nada que cometer, árbol de trabajo limpio

Esto significa que tiene un directorio de trabajo limpio; en otras palabras, ninguno de sus archivos rastreados se modifica.
Git tampoco ve ningún archivo sin seguimiento, o se enumerarían aquí. Finalmente, el comando te dice en qué rama
estás y te informa que no se ha desviado de la misma

26
Machine Translated by Google

rama en el servidor. Por ahora, esa rama siempre es maestra, que es la predeterminada; no te preocupes por eso aquí.
Git Branching revisará las ramas y las referencias en detalle.

Supongamos que agrega un nuevo archivo a su proyecto, un simple archivo LÉAME . Si el archivo no existía antes y
ejecuta git status, verá su archivo sin seguimiento así:

$ echo 'Mi proyecto' > LÉAME $ git


estado
En maestro de rama
Su rama está actualizada con 'origen/maestro'.
Archivos sin
seguimiento: (use "git add <archivo>..." para incluir en lo que se confirmará)

LÉAME

no se agregó nada para confirmar, pero hay archivos sin rastrear presentes (use "git add" para rastrear)

Puede ver que su nuevo archivo README no tiene seguimiento, porque está bajo el encabezado "Archivos sin
seguimiento" en su salida de estado. Básicamente, sin seguimiento significa que Git ve un archivo que no tenía en la
instantánea anterior (confirmación) y que aún no se ha preparado; Git no comenzará a incluirlo en tus instantáneas de
confirmación hasta que le indiques explícitamente que lo haga. Hace esto para que no comience a incluir accidentalmente
archivos binarios generados u otros archivos que no tenía la intención de incluir. Desea comenzar a incluir README, así
que comencemos a rastrear el archivo.

Seguimiento de nuevos archivos

Para comenzar a rastrear un nuevo archivo, usa el comando git add. Para comenzar a rastrear el archivo LÉAME , puede
ejecutar esto:

$ git agregar LÉAME

Si ejecuta su comando de estado nuevamente, puede ver que su archivo README ahora está rastreado y preparado
para ser confirmado:

$ git status On
branch master Su
rama está actualizada con 'origin/master'.
Cambios a confirmar: (use "git
restore --staged <archivo>..." para quitar la preparación)

nuevo archivo: LÉAME

Se puede decir que está organizado porque está bajo el encabezado "Cambios por confirmar". Si se compromete en este
punto, la versión del archivo en el momento en que ejecutó git add es la que estará en la siguiente instantánea histórica.
Puede recordar que cuando ejecutó git init anteriormente, luego ejecutó git add <files> , eso fue para comenzar a rastrear
archivos en su directorio. El comando git add toma un camino

27
Machine Translated by Google

nombre para un archivo o un directorio; si es un directorio, el comando agrega todos los archivos en ese directorio
recursivamente.

Archivos modificados provisionales

Cambiemos un archivo que ya fue rastreado. Si cambia un archivo previamente rastreado llamado CONTRIBUTING.md
y luego ejecuta su comando de estado de git nuevamente, obtendrá algo similar a esto:

$ git status On
branch master Su
rama está actualizada con 'origin/master'.
Cambios a confirmar: (use "git
reset HEAD <archivo>..." para quitar la preparación)

nuevo archivo: LÉAME

Cambios no preparados para


confirmación: (use "git add <archivo>..." para actualizar lo que se confirmará) (use
"git checkout -- <archivo>..." para descartar cambios en el directorio de trabajo)

modificado: CONTRIBUYENDO.md

El archivo CONTRIBUTING.md aparece en una sección denominada "Cambios no preparados para confirmación", lo
que significa que un archivo del que se realiza un seguimiento se ha modificado en el directorio de trabajo pero aún no
está preparado. Para prepararlo, ejecuta el comando git add . git add es un comando multipropósito: lo usa para
comenzar a rastrear archivos nuevos, organizar archivos y hacer otras cosas, como marcar archivos en conflicto de
fusión como resueltos. Puede ser útil pensar en ello más como "agregar precisamente este contenido a la próxima
confirmación" en lugar de "agregar este archivo al proyecto". Ejecutemos git add ahora para preparar el archivo
CONTRIBUTING.md y luego ejecutemos git status nuevamente:

$ git add CONTRIBUTING.md $


git status On branch master Tu
branch está actualizada con 'origin/
master'.
Cambios a confirmar: (use "git
reset HEAD <archivo>..." para quitar la preparación)

nuevo archivo: LÉAME


modificado: CONTRIBUYENDO.md

Ambos archivos están preparados y se incluirán en su próxima confirmación. En este punto, suponga que recuerda un
pequeño cambio que desea realizar en CONTRIBUTING.md antes de confirmarlo. Lo abres de nuevo y haces ese
cambio, y estás listo para comprometerte. Sin embargo, ejecutemos git status una vez más:

28
Machine Translated by Google

$ vim CONTRIBUTING.md
$ git status On branch master
Su rama está actualizada
con 'origin/master'.
Cambios a confirmar: (use "git
reset HEAD <archivo>..." para quitar la preparación)

nuevo archivo: README


modificado: CONTRIBUTING.md

Cambios no preparados para confirmación:


(use "git add <archivo>..." para actualizar lo que se confirmará) (use "git checkout
-- <archivo>..." para descartar cambios en el directorio de trabajo)

modificado: CONTRIBUYENDO.md

¿Que demonios? Ahora CONTRIBUTING.md aparece como preparado y sin preparar. ¿Cómo es eso posible?
Resulta que Git presenta un archivo exactamente como está cuando ejecuta el comando git add . Si confirma
ahora, la versión de CONTRIBUTING.md que tenía la última vez que ejecutó el comando git add es la forma en
que se ingresará en la confirmación, no la versión del archivo como se ve en su directorio de trabajo cuando
ejecuta git commit. Si modifica un archivo después de ejecutar git add, debe ejecutar git add nuevamente para
preparar la última versión del archivo:

$ git add CONTRIBUTING.md $


git status On branch master Tu
branch está actualizada con 'origin/
master'.
Cambios a confirmar: (use "git
reset HEAD <archivo>..." para quitar la preparación)

nuevo archivo: LÉAME


modificado: CONTRIBUYENDO.md

Estado corto
Si bien la salida de estado de git es bastante completa, también es bastante prolija. Git también tiene un indicador
de estado corto para que pueda ver sus cambios de una manera más compacta. Si ejecuta git status -s o git
status --short , obtiene un resultado mucho más simplificado del comando:

$ git estado -s
M LÉAME
MM Rakefile A
lib/git.rb M lib/
simplegit.rb ?? LICENCIA.txt

29
Machine Translated by Google

Los archivos nuevos que no se rastrean tienen un ?? junto a ellos, los archivos nuevos que se agregaron al área de preparación tienen una
A, los archivos modificados tienen una M , etc. Hay dos columnas en la salida: la columna de la izquierda indica el estado del área de
preparación y la columna de la derecha indica el estado del árbol de trabajo. Entonces, por ejemplo, en esa salida, el archivo README se
modifica en el directorio de trabajo pero aún no se prepara, mientras que el archivo lib/simplegit.rb se modifica y se prepara. El Rakefile se
modificó, preparó y luego volvió a modificar, por lo que hay cambios que están preparados y no preparados.

Ignorar archivos

A menudo, tendrás una clase de archivos que no deseas que Git agregue automáticamente o que incluso te muestre como sin seguimiento.
Por lo general, estos son archivos generados automáticamente, como archivos de registro o archivos producidos por su sistema de
compilación. En tales casos, puede crear un archivo que enumere patrones para que coincidan con el nombre .gitignore. Aquí hay un
ejemplo de archivo .gitignore :

$ gato .gitignore
*.[oa]
*~

La primera línea le dice a Git que ignore cualquier archivo que termine en ".o" o ".a": archivos de objetos y archivos que pueden ser el
producto de la construcción de su código. La segunda línea le dice a Git que ignore todos los archivos cuyos nombres terminen con una
tilde (~), que es utilizada por muchos editores de texto como Emacs para marcar archivos temporales. También puede incluir un directorio
log, tmp o pid; documentación generada automáticamente; y así. Por lo general, es una buena idea configurar un archivo .gitignore para su
nuevo repositorio antes de comenzar, de modo que no envíe accidentalmente archivos que realmente no desea en su repositorio de Git.

Las reglas para los patrones que puede poner en el archivo .gitignore son las siguientes:

• Las líneas en blanco o las líneas que comienzan con # se ignoran.

• Los patrones globales estándar funcionan y se aplicarán recursivamente a lo largo de todo el proceso.
árbol.

• Puede comenzar los patrones con una barra diagonal (/) para evitar la recursividad.

• Puede finalizar los patrones con una barra diagonal (/) para especificar un directorio.

• Puede negar un patrón comenzando con un signo de exclamación (!).

Los patrones globales son como expresiones regulares simplificadas que usan los shells. Un asterisco (*) coincide con cero o más
caracteres; [abc] coincide con cualquier carácter dentro de los corchetes (en este caso, a, b o c); un signo de interrogación (?) coincide con
un solo carácter; y los corchetes que encierran caracteres separados por un guión ([0-9]) coinciden con cualquier carácter entre ellos (en
este caso, del 0 al 9). También puede usar dos asteriscos para hacer coincidir directorios anidados; a/**/z coincidiría con a/z, a/b/z, a/b/c/z,
etc.

Aquí hay otro ejemplo de archivo .gitignore :

30
Machine Translated by Google

# ignorar todos los archivos .a


*.a

# pero haga un seguimiento de lib.a, aunque esté ignorando los archivos .a arriba de !lib.a

# solo ignore el archivo TODO en el directorio actual, no subdir/TODO


/QUE HACER

# ignorar todos los archivos en cualquier directorio llamado build


build/

# ignorar doc/notes.txt, pero no doc/server/arch.txt doc/*.txt

# ignorar todos los archivos .pdf en el directorio doc/ y cualquiera de sus subdirectorios doc/**/*.pdf

GitHub mantiene una lista bastante completa de buenos ejemplos de archivos .gitignore para
ÿ docenas de proyectos e idiomas en https://github.com/github/gitignore si quieres un punto de
partida para tu proyecto.

En el caso simple, un repositorio podría tener un solo archivo .gitignore en su directorio raíz,
que se aplica recursivamente a todo el repositorio. Sin embargo, también es posible tener
archivos .gitignore adicionales en subdirectorios. Las reglas de estos archivos .gitignore

ÿ anidados se aplican solo a los archivos del directorio en el que se encuentran. El repositorio
fuente del kernel de Linux tiene 206 archivos .gitignore .

Está más allá del alcance de este libro entrar en los detalles de múltiples archivos .gitignore ;
ver man gitignore para los detalles.

Visualización de sus cambios preparados y no preparados

Si el comando de estado de git es demasiado vago para usted (quiere saber exactamente qué cambió, no solo
qué archivos se cambiaron), puede usar el comando git diff . Cubriremos git diff con más detalle más adelante,
pero probablemente lo usará más a menudo para responder estas dos preguntas: ¿Qué ha cambiado pero aún
no ha puesto en escena? ¿Y qué has puesto en escena que estás a punto de cometer? Aunque git status
responde a esas preguntas de manera muy general al enumerar los nombres de los archivos, git diff le muestra
las líneas exactas agregadas y eliminadas: el parche, por así decirlo.

Supongamos que edita y prepara el archivo README nuevamente y luego edita el archivo CONTRIBUTING.md
sin prepararlo. Si ejecuta su comando de estado de git , una vez más verá algo como esto:

31
Machine Translated by Google

$ git status On
branch master Su
rama está actualizada con 'origin/master'.
Cambios a confirmar: (use "git
reset HEAD <archivo>..." para quitar la preparación)

modificado: LÉAME

Cambios no preparados para confirmación:


(use "git add <archivo>..." para actualizar lo que se confirmará) (use "git checkout
-- <archivo>..." para descartar cambios en el directorio de trabajo)

modificado: CONTRIBUYENDO.md

Para ver lo que ha cambiado pero que aún no ha preparado, escriba git diff sin otros argumentos:

$ git diff diff --


git a/CONTRIBUCIÓN.md b/CONTRIBUCIÓN.md índice
8ebb991..643e24f 100644
--- a/CONTRIBUYENDO.md
+++ b/CONTRIBUTING.md
@@ -65,7 +65,8 @@ rama directamente, las cosas pueden complicarse.
Incluya una buena descripción de sus cambios cuando envíe su PR; si tenemos que leer la diferencia
completa para averiguar por qué está contribuyendo en primer lugar, es menos probable que reciba
comentarios y que su cambio se fusione. Además, divida sus cambios en partes integrales si tu parche es
+más largo que una docena de líneas.

Si está comenzando a trabajar en un área en particular, siéntase libre de enviar un PR que destaque su
trabajo en progreso (y tenga en cuenta en el título del PR que es

Ese comando compara lo que está en su directorio de trabajo con lo que está en su área de preparación. El
resultado le indica los cambios que ha realizado y que aún no ha realizado.

Si desea ver lo que ha organizado que se incluirá en su próxima confirmación, puede usar git diff --staged. Este
comando compara sus cambios por etapas con su última confirmación:

$ git diff --staged diff --git


a/README b/README modo de
archivo nuevo 100644 índice
0000000..03902a1
--- /dev/null
+++ b/LÉAME
@@ -0,0 +1 @@
+Mi Proyecto

32
Machine Translated by Google

Es importante tener en cuenta que git diff por sí mismo no muestra todos los cambios realizados desde su última
confirmación, solo los cambios que aún no están preparados. Si ha preparado todos sus cambios, git diff no le dará
ningún resultado.

Para otro ejemplo, si prepara el archivo CONTRIBUTING.md y luego lo edita, puede usar git diff para ver los cambios
en el archivo que están preparados y los cambios que no están preparados. Si nuestro entorno se ve así:

$ git add CONTRIBUTING.md


$ echo '# test line' >> CONTRIBUTING.md $ git
status On branch master Su rama está
actualizada con 'origin/master'.

Cambios a confirmar: (use


"git reset HEAD <archivo>..." para quitar la preparación)

modificado: CONTRIBUYENDO.md

Cambios no preparados para


confirmación: (use "git add <archivo>..." para actualizar lo que se
confirmará) (use "git checkout -- <archivo>..." para descartar cambios en el directorio de trabajo)

modificado: CONTRIBUYENDO.md

Ahora puedes usar git diff para ver lo que aún no está preparado:

$ git diff diff


--git a/CONTRIBUYENDO.md b/CONTRIBUYENDO.md
index 643e24f..87f08c8 100644 --- a/CONTRIBUYENDO.md
+++ b/CONTRIBUYENDO.md @@ -119,3 +119,4 @ @
en los ## Proyectos Iniciales

Consulte nuestra [lista


de proyectos](https://github.com/libgit2/libgit2/blob/development/PROJECTS.md). +#
línea de prueba

y git diff --cached para ver lo que has preparado hasta ahora (--staged y --cached son sinónimos):

33
Machine Translated by Google

$ git diff --cached diff --


git a/CONTRIBUCIÓN.md b/CONTRIBUCIÓN.md índice
8ebb991..643e24f 100644 --- a/CONTRIBUCIÓN.md ++
+ b/CONTRIBUCIÓN.md @@ -65,7 +65 ,8 @@ rama
directamente, las cosas pueden complicarse.

Incluya una buena descripción de sus cambios cuando envíe su PR; si tenemos que leer la
diferencia completa para averiguar por qué está contribuyendo en primer lugar, es menos
probable que reciba comentarios y que su cambio se fusione. Además, divida sus cambios en
partes integrales si tu parche es +más largo que una docena de líneas.

Si está comenzando a trabajar en un área en particular, siéntase libre de enviar un PR que


destaque su trabajo en progreso (y tenga en cuenta en el título del PR que es

Git Diff en una herramienta externa

Continuaremos usando el comando git diff de varias maneras a lo largo del resto del libro. Hay
otra forma de ver estas diferencias si prefiere un programa de visualización de diferencias
ÿ gráficas o externas. Si ejecuta git difftool en lugar de git diff, puede ver cualquiera de estas
diferencias en software como emerge, vimdiff y muchos más (incluidos los productos
comerciales). Ejecute git difftool --tool-help para ver qué hay disponible en su sistema.

Confirmar sus cambios


Ahora que su área de preparación está configurada de la manera que desea, puede confirmar sus cambios.
Recuerde que cualquier cosa que aún no esté preparada (cualquier archivo que haya creado o modificado que
no haya ejecutado git add on desde que los editó) no entrará en esta confirmación. Permanecerán como archivos
modificados en su disco. En este caso, supongamos que la última vez que ejecutó git status, vio que todo estaba
preparado, por lo que está listo para confirmar sus cambios. La forma más sencilla de confirmar es escribir git commit:

$ git confirmar

Al hacerlo, se inicia el editor de su elección.

Esto lo establece la variable de entorno EDITOR de su shell, generalmente vim o emacs,


ÿ aunque puede configurarlo con lo que quiera usando el comando git config --global core.editor
como vio en Primeros pasos.

El editor muestra el siguiente texto (este ejemplo es una pantalla de Vim):

34
Machine Translated by Google

# Ingrese el mensaje de confirmación para sus cambios. Las líneas que comiencen # con '#'
se ignorarán y un mensaje vacío anulará la confirmación.
# On branch master # Tu
sucursal está actualizada con 'origen/master'. #

# Cambios a confirmar: # nuevo


archivo: README # modificado:
CONTRIBUTING.md #

~
~
~

".git/COMMIT_EDITMSG" 9L, 283C

Puede ver que el mensaje de confirmación predeterminado contiene la última salida del comando de estado de git comentada y
una línea vacía en la parte superior. Puede eliminar estos comentarios y escribir su mensaje de confirmación, o puede dejarlos allí
para que le ayuden a recordar lo que está confirmando.

Para un recordatorio aún más explícito de lo que ha modificado, puede pasar la opción -v a git commit. Si lo

ÿ hace, también pone la diferencia de su cambio en el editor para que pueda ver exactamente qué cambios
está confirmando.

Cuando sales del editor, Git crea tu confirmación con ese mensaje de confirmación (sin los comentarios ni las diferencias).

Alternativamente, puede escribir su mensaje de confirmación en línea con el comando de confirmación especificándolo después
de un indicador -m , como este:

$ git commit -m "Story 182: corrige los puntos de referencia para la


velocidad" [master 463dc4f] Story 182: corrige los puntos de referencia
para la velocidad 2 archivos cambiados, 2 inserciones (+) modo de creación
100644 README

¡Ahora has creado tu primer compromiso! Puede ver que la confirmación le ha dado algunos resultados sobre sí misma: en qué
rama se comprometió (maestro), qué suma de verificación SHA-1 tiene la confirmación (463dc4f), cuántos archivos se cambiaron
y estadísticas sobre las líneas agregadas y eliminadas en el cometer.

Recuerde que la confirmación registra la instantánea que configuró en su área de preparación. Todo lo que no escenificaste
todavía está allí modificado; puedes hacer otro compromiso para agregarlo a tu historial. Cada vez que realiza una confirmación,
está grabando una instantánea de su proyecto a la que puede volver o comparar más tarde.

Saltarse el área de preparación

Aunque puede ser increíblemente útil para crear compromisos exactamente como los desea, el área de preparación a veces es
un poco más compleja de lo que necesita en su flujo de trabajo. Si quieres saltarte la

35
Machine Translated by Google

área de preparación, Git proporciona un atajo simple. Agregar la opción -a al comando de confirmación de git hace que Git
organice automáticamente todos los archivos que ya están rastreados antes de realizar la confirmación, lo que le permite omitir
la parte de agregar de git :

$ git status On
branch master Su
rama está actualizada con 'origin/master'.
Cambios no preparados para
confirmación: (use "git add <archivo>..." para actualizar lo que se confirmará)
(use "git checkout -- <archivo>..." para descartar cambios en el directorio de trabajo)

modificado: CONTRIBUYENDO.md

no se agregaron cambios para confirmar (use "git add" y/o "git commit -a") $ git commit
-a -m 'Agregar nuevos puntos de referencia' [master 83e38c7] Agregar nuevos puntos
de referencia 1 archivo cambiado, 5 inserciones (+), 0 eliminaciones (-)

Observe cómo no tiene que ejecutar git add en el archivo CONTRIBUTING.md en este caso antes de comprometerse.
Eso es porque el indicador -a incluye todos los archivos modificados. Esto es conveniente, pero tenga cuidado; a veces, esta
bandera hará que incluya cambios no deseados.

Eliminación de archivos

Para eliminar un archivo de Git, debe eliminarlo de sus archivos rastreados (más precisamente, eliminarlo de su área de
preparación) y luego confirmar. El comando git rm hace eso y también elimina el archivo de su directorio de trabajo para que no
lo vea como un archivo sin seguimiento la próxima vez.

Si simplemente elimina el archivo de su directorio de trabajo, aparece en el área "Cambios no preparados para confirmación" (es
decir, no preparados) de su salida de estado de git :

$ rm PROJECTS.md
$ git status On
branch master Su
rama está actualizada con 'origin/master'.
Cambios no preparados para
confirmación: (use "git add/rm <archivo>..." para actualizar lo que se confirmará)
(use "git checkout -- <archivo>..." para descartar cambios en el directorio de trabajo)

eliminado: PROYECTOS.md

no se agregaron cambios para confirmar (use "git add" y/o "git commit -a")

Luego, si ejecuta git rm, realiza la eliminación del archivo:

36
Machine Translated by Google

$ git rm PROJECTS.md rm
'PROJECTS.md' $ git
status On branch master
Su rama está actualizada
con 'origin/master'.
Cambios a confirmar: (use "git
reset HEAD <archivo>..." para quitar la preparación)

eliminado: PROYECTOS.md

La próxima vez que confirme, el archivo desaparecerá y ya no se rastreará. Si modificó el archivo o ya lo había agregado al área de
preparación, debe forzar la eliminación con la opción -f . Esta es una función de seguridad para evitar la eliminación accidental de datos
que aún no se han registrado en una instantánea y que no se pueden recuperar de Git.

Otra cosa útil que puede hacer es mantener el archivo en su árbol de trabajo pero eliminarlo de su área de preparación. En otras palabras,
es posible que desee mantener el archivo en su disco duro pero que Git no lo rastree nunca más. Esto es particularmente útil si olvidó
agregar algo a su archivo .gitignore y accidentalmente lo preparó, como un archivo de registro grande o un montón de archivos
compilados .a . Para hacer esto, use la opción --cached :

$ git rm --archivo LÉAME en caché

Puede pasar archivos, directorios y patrones de archivos globales al comando git rm . Eso significa que puedes hacer cosas como:

$ git rm registro/\*.registro

Tenga en cuenta la barra invertida (\) delante del *. Esto es necesario porque Git hace su propia expansión de nombre de archivo además
de la expansión de nombre de archivo de su shell. Este comando elimina todos los archivos que tienen la extensión .log en el directorio
log/ . O bien, puede hacer algo como esto:

$ git rm \*~

Este comando elimina todos los archivos cuyos nombres terminan en ~.

Mover archivos

A diferencia de muchos otros VCS, Git no realiza un seguimiento explícito del movimiento de archivos. Si cambia el nombre de un archivo
en Git, no se almacenan metadatos en Git que indiquen que cambió el nombre del archivo. Sin embargo, Git es bastante inteligente al
darse cuenta de eso después del hecho: nos ocuparemos de detectar el movimiento de archivos un poco más tarde.

Por lo tanto, es un poco confuso que Git tenga un comando mv . Si desea cambiar el nombre de un archivo en Git, puede ejecutar algo
como:

37
Machine Translated by Google

$ git mv archivo_de archivo_a

y funciona bien De hecho, si ejecuta algo como esto y mira el estado, verá que Git lo considera un archivo renombrado:

$ git mv README.md README $


git status On branch master Su
rama está actualizada con 'origin/
master'.
Cambios a confirmar: (use "git
reset HEAD <archivo>..." para quitar la preparación)

renombrado: LÉAME.md -> LÉAME

Sin embargo, esto es equivalente a ejecutar algo como esto:

$ mv LÉAME.md LÉAME $
git rm LÉAME.md $ git add
LÉAME

Git se da cuenta de que es un cambio de nombre implícitamente, por lo que no importa si cambia el nombre de un archivo
de esa manera o con el comando mv . La única diferencia real es que git mv es un comando en lugar de tres: es una función
de conveniencia. Más importante aún, puede usar cualquier herramienta que desee para cambiar el nombre de un archivo y
abordar el add/rm más tarde, antes de confirmar.

Visualización del historial de confirmaciones

Después de haber creado varias confirmaciones, o si ha clonado un repositorio con un historial de confirmaciones existente,
probablemente querrá mirar hacia atrás para ver qué sucedió. La herramienta más básica y poderosa para hacer esto es el
comando git log .

Estos ejemplos usan un proyecto muy simple llamado "simplegit". Para obtener el proyecto, ejecute:

$ clon de git https://github.com/schacon/simplegit-progit

Cuando ejecutas git log en este proyecto, deberías obtener un resultado similar a este:

38
Machine Translated by Google

$ git log
compromiso ca82a6dff817ec66f44342007202690a93763949
Autor: Scott Chacon <schacon@gee-mail.com>
Fecha: lun 17 de marzo 21:52:11 2008 -0700

Cambiar número de versión

cometer 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Autor: Scott Chacon <schacon@gee-mail.com> Fecha:
sábado 15 de marzo 16:40:33 2008 -0700

Eliminar prueba innecesaria

cometer a11bef06a3f659402fe7563abf99ad00de2209e6
Autor: Scott Chacon <schacon@gee-mail.com> Fecha:
sábado 15 de marzo 10:31:28 2008 -0700

Compromiso inicial

De forma predeterminada, sin argumentos, git log enumera las confirmaciones realizadas en ese repositorio en orden
cronológico inverso; es decir, las confirmaciones más recientes aparecen primero. Como puede ver, este comando enumera
cada confirmación con su suma de verificación SHA-1, el nombre y el correo electrónico del autor, la fecha de escritura y el
mensaje de confirmación.

Hay disponible una gran cantidad y variedad de opciones para el comando git log para mostrarle exactamente lo que está
buscando. Aquí te mostraremos algunos de los más populares.

Una de las opciones más útiles es -p o --patch, que muestra la diferencia (la salida del parche ) introducida en cada
confirmación. También puede limitar la cantidad de entradas de registro que se muestran, como usar -2 para mostrar solo
las dos últimas entradas.

39
Machine Translated by Google

$ git registro -p -2
cometer ca82a6dff817ec66f44342007202690a93763949

Autor: Scott Chacón <schacon@gee-mail.com>


Fecha: lun 17 de marzo 21:52:11 2008 -0700

Cambiar número de versión

diff --git a/Rakefile b/Rakefile


índice a874b73..8f94139 100644
--- a/Rakefile
+++ b/archivo Rake

@@ -5,7 +5,7 @@ requiere 'rake/gempackagetask'


especificación = Gema::Especificación.nuevo hacer |s|
ÿ

s.plataforma = Gema::Plataforma::RUBY
ÿ

nombre de = "simplegit"
- s.versión = "0.1.0"
+ s.versión = "0.1.1"
ÿ

s.autor = "Scott Chacón"


ÿ

s.email = "schacon@gee-mail.com"
ÿ

s.resumen = "Una joya simple para usar Git en código Ruby".

confirmar 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7

Autor: Scott Chacón <schacon@gee-mail.com>


Fecha: sábado 15 de marzo 16:40:33 2008 -0700

Eliminar prueba innecesaria

diferencia --git a/lib/simplegit.rb b/lib/simplegit.rb


índice a0a60ae..47c6340 100644

--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -18,8 +18,3 @@ clase SimpleGit
ÿ

final

final
-

-si $0 == __ARCHIVO__
- git = SimpleGit.nuevo
- pone git.show
-final

Esta opción muestra la misma información pero con una diferencia directamente después de cada entrada. Esto es muy
útil para la revisión de código o para explorar rápidamente lo que sucedió durante una serie de confirmaciones que un
ha añadido el colaborador. También puede usar una serie de opciones de resumen con git log. Para
Por ejemplo, si desea ver algunas estadísticas abreviadas para cada confirmación, puede usar la opción --stat :

40
Machine Translated by Google

$ git log --stat


compromiso ca82a6dff817ec66f44342007202690a93763949
Autor: Scott Chacon <schacon@gee-mail.com>
Fecha: lun 17 de marzo 21:52:11 2008 -0700

Cambiar número de versión

archivo de rastrillo
| 2 +- 1 archivo cambiado, 1 inserción (+), 1 borrado (-)

cometer 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Autor: Scott Chacon <schacon@gee-mail.com> Fecha:
sábado 15 de marzo 16:40:33 2008 -0700

Eliminar prueba innecesaria

lib/simplegit.rb | 5 ----- 1 archivo


cambiado, 5 eliminaciones (-)

cometer a11bef06a3f659402fe7563abf99ad00de2209e6
Autor: Scott Chacon <schacon@gee-mail.com> Fecha:
sábado 15 de marzo 10:31:28 2008 -0700

Compromiso inicial

LÉAME Rakefile | 23 ++++++++++++


| 6 ++++++
+++++++++++++ lib/simplegit.rb | 25
+++++++++++++++++++++++++++
3 archivos cambiados, 54 inserciones (+)

Como puede ver, la opción --stat imprime debajo de cada entrada de confirmación una lista de archivos
modificados, cuántos archivos se cambiaron y cuántas líneas en esos archivos se agregaron y eliminaron.
También pone un resumen de la información al final.

Otra opción realmente útil es --pretty. Esta opción cambia la salida del registro a formatos distintos al
predeterminado. Hay algunos valores de opciones preconstruidos disponibles para su uso. El valor de una
sola línea para esta opción imprime cada confirmación en una sola línea, lo cual es útil si está viendo muchas
confirmaciones. Además, los valores short, full y fuller muestran la salida aproximadamente en el mismo
formato pero con menos o más información, respectivamente:

$ git log --pretty=oneline


ca82a6dff817ec66f44342007202690a93763949 Cambiar el número de
versión 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 Eliminar prueba
innecesaria a11bef06a3f659402fe7563abf99ad00de2209e6 Confirmación inicial

El valor de opción más interesante es el formato, que le permite especificar su propio formato de salida de
registro. Esto es especialmente útil cuando genera resultados para el análisis de la máquina, ya que especifica
el formato explícitamente, sabe que no cambiará con las actualizaciones de Git:

41
Machine Translated by Google

$ git log --pretty=formato:"%h - %an, %ar : %s"


ca82a6d - Scott Chacon, hace 6 años: Cambiar número de versión
085bb3b - Scott Chacon, hace 6 años: Eliminar prueba innecesaria
a11bef0 - Scott Chacon, hace 6 años: Confirmación inicial

Especificadores útiles para git log --pretty=format enumera algunos de los especificadores más útiles que formatean
toma.

Tabla 1. Especificadores útiles para git log --pretty=format

especificador Descripción de la salida

%H cometer hash

%h Hash de confirmación abreviado

%T hachís de árbol

%t Hachís de árbol abreviado

%PAGS
Hashes principales

%pags
Hashes principales abreviados

%un Nombre del autor

%ae Correo electrónico del autor

%anuncio
Fecha del autor (el formato respeta la opción --date=)

%Arkansas
Autor fecha, relativa

%cn nombre del autor

% ce Correo electrónico del remitente

%discos compactos
fecha de confirmación

%cr
Fecha de compromiso, relativo

%s
Tema

Quizás se pregunte cuál es la diferencia entre autor y autor . El autor es el

persona que originalmente escribió el trabajo, mientras que el autor es la última persona que aplicó el

trabajo. Entonces, si envía un parche a un proyecto y uno de los miembros principales aplica el parche, ambos

obtienes crédito: tú como autor y el miembro principal como autor. Cubriremos esto
distinción un poco más en Distributed Git.

Los valores de las opciones oneline y format son particularmente útiles con otra opción de registro llamada --graph.

Esta opción agrega un pequeño y agradable gráfico ASCII que muestra su rama e historial de combinación:

42
Machine Translated by Google

$ git log --pretty=formato:"%h %s" --graph


* 2d3acf9 Ignorar errores de SIGCHLD en trap
* 5e3ee11 Combinar rama 'maestro' de git://github.com/dustin/grit
|\
| * 420eac9 Agregar método para obtener la rama actual
* | 30e367c Código de tiempo de espera y pruebas
* | 5a09431 Agregar protección de tiempo de espera a la arena
* | e1193f8 Soporte para cabezas con barras en ellas
|/
* d6016bc Requerir tiempo para xmlschema
* 11d191e Fusionar sucursal 'defunkt' en local

Este tipo de salida se volverá más interesante a medida que avanzamos en la ramificación y la fusión en el
Siguiente capítulo.

Esas son solo algunas opciones simples de formato de salida para git log ; hay muchas más. Común
options to git log enumera las opciones que hemos cubierto hasta ahora, así como algunos otros formatos comunes
opciones que pueden ser útiles, junto con cómo cambian la salida del comando de registro.

Tabla 2. Opciones comunes para git log

Opción Descripción

-pags
Muestra el parche introducido con cada confirmación.

--estadística Muestra estadísticas de archivos modificados en cada confirmación.

--shortstat
Muestra solo la línea de cambios/inserciones/eliminaciones del comando --stat.

--solo nombre Muestra la lista de archivos modificados después de la información de confirmación.

--nombre-estado Muestra la lista de archivos afectados con información agregada/modificada/eliminada también.

--abbrev-commit Muestra solo los primeros caracteres de la suma de verificación SHA-1 en lugar de los 40.

--fecha-relativa
Mostrar la fecha en un formato relativo (por ejemplo, "hace 2 semanas") en lugar de
utilizando el formato de fecha completa.

--grafico Muestre un gráfico ASCII de la rama y combine el historial junto a la salida del registro.

--bonito Mostrar confirmaciones en un formato alternativo. Los valores de opción incluyen oneline, short,
completo, más completo y formato (donde usted especifica su propio formato).

--una línea
Abreviatura de --pretty=oneline --abbrev-commit usados juntos.

Limitación de salida de registro

Además de las opciones de formato de salida, git log tiene varias opciones de limitación útiles; es decir,
opciones que le permiten mostrar solo un subconjunto de confirmaciones. Ya has visto una de esas opciones: el -2
opción, que muestra solo las dos últimas confirmaciones. De hecho, puede hacer -<n>, donde n es cualquier número entero para
muestra las últimas n confirmaciones. En realidad, es poco probable que lo use a menudo, porque Git canaliza por defecto todos
salida a través de un buscapersonas para que solo vea una página de salida de registro a la vez.

Sin embargo, las opciones de límite de tiempo como --since y --until son muy útiles. Por ejemplo, este
El comando obtiene la lista de confirmaciones realizadas en las últimas dos semanas:

43
Machine Translated by Google

$ git log --since=2.semanas

Este comando funciona con muchos formatos: puede especificar una fecha específica como "2008-01-15" o una fecha relativa como
"hace 2 años, 1 día, 3 minutos".

También puede filtrar la lista para confirmaciones que coincidan con algunos criterios de búsqueda. La opción --author le permite filtrar
por un autor específico, y la opción --grep le permite buscar palabras clave en los mensajes de confirmación.

Puede especificar más de una instancia de los criterios de búsqueda --author y --grep , lo que limitará la salida
de la confirmación a confirmaciones que coincidan con cualquiera de los patrones --author y cualquiera de los
ÿ patrones --grep ; sin embargo, agregar la opción --all-match limita aún más la salida a solo aquellas
confirmaciones que coinciden con todos los patrones --grep .

Otro filtro realmente útil es la opción -S (coloquialmente conocida como la opción "pickaxe" de Git), que toma una cadena y muestra
solo las confirmaciones que cambiaron el número de ocurrencias de esa cadena. Por ejemplo, si quisiera encontrar la última
confirmación que agregó o eliminó una referencia a una función específica, podría llamar a:

$ git log -S nombre_función

La última opción realmente útil para pasar a git log como filtro es una ruta. Si especifica un directorio o un nombre de archivo, puede
limitar la salida del registro a las confirmaciones que introdujeron un cambio en esos archivos. Esta es siempre la última opción y
generalmente está precedida por guiones dobles (--) para separar las rutas de las opciones:

$ git log -- ruta/al/archivo

En Opciones para limitar la salida de git log , enumeraremos estas y algunas otras opciones comunes para su referencia.

Tabla 3. Opciones para limitar la salida de git log

Opción Descripción

-<n>
Mostrar solo las últimas n confirmaciones

--desde, --después Limite las confirmaciones a las realizadas después de la fecha especificada.

--hasta, --antes Limite las confirmaciones a las realizadas antes de la fecha especificada.

--autor
Mostrar solo confirmaciones en las que la entrada del autor coincida con la
cadena especificada.

--committer
Mostrar solo confirmaciones en las que la entrada del confirmador coincida con la
cadena especificada.

--grep Mostrar solo confirmaciones con un mensaje de confirmación que contenga la cadena

44
Machine Translated by Google

Opción Descripción

-S
Mostrar solo confirmaciones agregando o eliminando código que coincida con la cadena

Por ejemplo, si desea ver qué confirmaciones que modifican archivos de prueba en el historial del código fuente de Git fueron
confirmadas por Junio Hamano en el mes de octubre de 2008 y no son confirmaciones de combinación, puede ejecutar algo como
esto:

$ git log --pretty="%h - %s" --author='Junio C Hamano' --since="2008-10-01" \ --before="2008-11-01" --


no- fusiones -- t/ 5610e3b - Corrección del error del caso de prueba cuando los atributos extendidos
están en uso acd3b9e - Mejora de hold_lock_file_for_{update,append}() API f563754 - demostración
de rotura de pago separado con enlace simbólico HEAD d1a43f2 - reinicio --hard/read-tree - -reset -u:
elimine las nuevas rutas no fusionadas 51a94af - Corrija "checkout --track -b newbranch" en HEAD
separado b0ad11e - pull: permita "git pull origin $something:$current_branch" en una rama no nacida

De las casi 40ÿ000 confirmaciones en el historial del código fuente de Git, este comando muestra las 6 que coinciden con esos criterios.

Prevención de la visualización de confirmaciones de fusión

Dependiendo del flujo de trabajo utilizado en su repositorio, es posible que un porcentaje considerable de las

ÿ confirmaciones en su historial de registro sean solo confirmaciones de combinación, que normalmente no son
muy informativas. Para evitar que la visualización de confirmaciones de combinación sature su historial de
registro, simplemente agregue la opción de registro --no-merges.

Deshacer cosas
En cualquier etapa, es posible que desee deshacer algo. Aquí, revisaremos algunas herramientas básicas para deshacer los cambios
que ha realizado. Tenga cuidado, porque no siempre puede deshacer algunos de estos deshacer. Esta es una de las pocas áreas en
Git donde puede perder algo de trabajo si lo hace mal.

Uno de los deshacer comunes tiene lugar cuando se confirma demasiado pronto y posiblemente se olvida de agregar algunos archivos,
o se estropea el mensaje de confirmación. Si desea rehacer esa confirmación, realice los cambios adicionales que olvidó, organícelos
y confirme nuevamente usando la opción --amend :

$ git confirmar --enmendar

Este comando toma su área de preparación y la usa para la confirmación. Si no ha realizado cambios desde su última confirmación
(por ejemplo, ejecuta este comando inmediatamente después de su confirmación anterior), su instantánea se verá exactamente igual
y todo lo que cambiará es su mensaje de confirmación.

Se activa el mismo editor de mensajes de confirmación, pero ya contiene el mensaje de su confirmación anterior. Puede editar el
mensaje como siempre, pero sobrescribe su confirmación anterior.

Como ejemplo, si se compromete y luego se da cuenta de que olvidó realizar los cambios en un archivo que deseaba

45
Machine Translated by Google

para agregar a este compromiso, puede hacer algo como esto:

$ git commit -m 'Confirmación inicial' $ git


add olvidado_archivo $ git commit --amend

Terminas con una sola confirmación: la segunda confirmación reemplaza los resultados de la primera.

Es importante comprender que cuando está modificando su último compromiso, no lo está arreglando
sino reemplazándolo por completo con un compromiso nuevo y mejorado que elimina el compromiso
anterior y coloca el compromiso nuevo en su lugar.
Efectivamente, es como si la confirmación anterior nunca hubiera ocurrido y no aparecerá en el
ÿ historial de tu repositorio.

El valor obvio de modificar las confirmaciones es realizar pequeñas mejoras en su última confirmación,
sin saturar el historial de su repositorio con mensajes de confirmación del tipo "Oops, olvidé agregar
un archivo" o "Maldita sea, arreglando un error tipográfico en la última confirmación".

Solo modifique las confirmaciones que todavía son locales y no se han enviado a ninguna parte.

ÿ Modificar confirmaciones enviadas anteriormente y forzar la inserción de la rama causará problemas


a sus colaboradores. Para obtener más información sobre lo que sucede cuando haces esto y cómo
recuperarte si estás en el lado receptor, lee Los peligros de la reorganización.

Desmontar un archivo provisional

Las siguientes dos secciones demuestran cómo trabajar con su área de preparación y cambios en el directorio de trabajo.
Lo bueno es que el comando que usa para determinar el estado de esas dos áreas también le recuerda cómo deshacer
los cambios en ellas. Por ejemplo, supongamos que ha cambiado dos archivos y desea confirmarlos como dos cambios
separados, pero accidentalmente escribe git add * y prepara ambos. ¿Cómo puedes quitar el escenario a uno de los dos?
El comando de estado de git te recuerda:

$ git add * $ git


status En el
maestro de la rama
Cambios que se confirmarán:
(use "git reset HEAD <archivo>..." para quitar la preparación)

renombrado: LÉAME.md -> LÉAME


modificado: CONTRIBUCIÓN.md

Justo debajo del texto "Cambios por confirmar", dice use git reset HEAD <archivo>... para quitar la preparación. Entonces,
usemos ese consejo para eliminar el archivo CONTRIBUTING.md :

46
Machine Translated by Google

$ git reset HEAD CONTRIBUTING.md


Cambios no preparados después del
reinicio: M CONTRIBUTING.md $ git
status On branch master

Cambios a confirmar: (use "git


reset HEAD <archivo>..." para quitar la preparación)

renombrado: LÉAME.md -> LÉAME

Cambios no preparados para


confirmación: (use "git add <archivo>..." para actualizar lo que se confirmará)
(use "git checkout -- <archivo>..." para descartar cambios en el directorio de trabajo)

modificado: CONTRIBUYENDO.md

El comando es un poco extraño, pero funciona. El archivo CONTRIBUTING.md se modifica, pero una vez más se
deshace.

Es cierto que git reset puede ser un comando peligroso, especialmente si proporciona el
ÿ indicador --hard . Sin embargo, en el escenario descrito anteriormente, el archivo en su directorio
de trabajo no se toca, por lo que es relativamente seguro.

Por ahora, esta invocación mágica es todo lo que necesitas saber sobre el comando git reset . Entraremos en
muchos más detalles sobre lo que hace el reinicio y cómo dominarlo para hacer cosas realmente interesantes en
Reset Demystified.

Desmodificar un archivo modificado

¿Qué sucede si se da cuenta de que no desea conservar los cambios en el archivo CONTRIBUTING.md ? ¿Cómo
puede desmodificarlo fácilmente, revertirlo a la apariencia que tenía la última vez que se comprometió (o clonó
inicialmente, o como lo ingresó en su directorio de trabajo)? Afortunadamente, git status también te dice cómo
hacerlo. En el resultado del último ejemplo, el área no preparada se ve así:

Cambios no preparados para


confirmación: (use "git add <archivo>..." para actualizar lo que se confirmará)
(use "git checkout -- <archivo>..." para descartar cambios en el directorio de trabajo)

modificado: CONTRIBUYENDO.md

Le dice bastante explícitamente cómo descartar los cambios que ha realizado. Hagamos lo que dice:

47
Machine Translated by Google

$ git checkout -- CONTRIBUTING.md $ git


status En branch master Cambios a confirmar:
(use "git reset HEAD <archivo>..." para quitar
la preparación)

renombrado: LÉAME.md -> LÉAME

Puede ver que los cambios se han revertido.

Es importante entender que git checkout -- <file> es un comando peligroso.

ÿ Cualquier cambio local que haya realizado en ese archivo se ha ido: Git simplemente reemplazó ese
archivo con la última versión preparada o confirmada. Nunca use este comando a menos que sepa
absolutamente que no quiere esos cambios locales no guardados.

Si desea mantener los cambios que ha realizado en ese archivo, pero aún necesita eliminarlo por ahora, repasaremos el
almacenamiento y la ramificación en Git Branching; estas son generalmente mejores formas de hacerlo.

Recuerde, cualquier cosa que se confirme en Git casi siempre se puede recuperar. Incluso las confirmaciones que estaban
en ramas que se eliminaron o las confirmaciones que se sobrescribieron con una confirmación --amend pueden
recuperarse (ver Recuperación de datos para la recuperación de datos). Sin embargo, cualquier cosa que pierda que
nunca se cometió es probable que nunca se vuelva a ver.

Deshacer cosas con git restore


La versión 2.23.0 de Git introdujo un nuevo comando: git restore. Es básicamente una alternativa a git reset que acabamos
de cubrir. Desde la versión 2.23.0 de Git en adelante, Git usará git restore en lugar de git reset para muchas operaciones
de deshacer.

Volvamos sobre nuestros pasos y deshagamos las cosas con git restore en lugar de git reset.

Desmontar un archivo en etapas con git restore

Las siguientes dos secciones demuestran cómo trabajar con su área de preparación y cambios en el directorio de trabajo
con git restore. Lo bueno es que el comando que usa para determinar el estado de esas dos áreas también le recuerda
cómo deshacer los cambios en ellas. Por ejemplo, supongamos que ha cambiado dos archivos y desea confirmarlos como
dos cambios separados, pero accidentalmente escribe git add * y prepara ambos. ¿Cómo puedes quitar el escenario a uno
de los dos? El comando de estado de git te recuerda:

$ git add * $ git


status En branch
master Cambios a
confirmar: (use "git restore --
staged <archivo>..." para quitar la preparación) modificado:
CONTRIBUTING.md renombrado:
LÉAME.md -> LÉAME

48
Machine Translated by Google

Justo debajo del texto "Cambios por confirmar", dice use git restore --staged <archivo>... para quitar la preparación.
Entonces, usemos ese consejo para eliminar el archivo CONTRIBUTING.md :

$ git restore --staged CONTRIBUTING.md $


git status En branch master Cambios que se
confirmarán: (use "git restore --staged
<file>..." para quitar la preparación)
renombrado: README.md -> README

Cambios no preparados para


confirmación: (use "git add <archivo>..." para actualizar lo que se
confirmará) (use "git restore <archivo>..." para descartar cambios en el directorio de
trabajo) modificado: CONTRIBUTING.md

El archivo CONTRIBUTING.md se modifica, pero una vez más se deshace.

Desmodificar un archivo modificado con git restore

¿Qué sucede si se da cuenta de que no desea conservar los cambios en el archivo CONTRIBUTING.md ? ¿Cómo puede
desmodificarlo fácilmente, revertirlo a la apariencia que tenía la última vez que se comprometió (o clonó inicialmente, o
como lo ingresó en su directorio de trabajo)? Afortunadamente, git status también te dice cómo hacerlo. En el resultado del
último ejemplo, el área no preparada se ve así:

Cambios no preparados para


confirmación: (use "git add <archivo>..." para actualizar lo que se
confirmará) (use "git restore <archivo>..." para descartar cambios en el directorio de
trabajo) modificado: CONTRIBUTING.md

Le dice bastante explícitamente cómo descartar los cambios que ha realizado. Hagamos lo que dice:

$ git restore CONTRIBUTING.md


$ git status En rama maestra

Cambios a confirmar: (use


"git restore --staged <archivo>..." para quitar el escenario)
renombrado: README.md -> README

Es importante comprender que git restore <file> es un comando peligroso. Cualquier cambio local que

ÿ haya realizado en ese archivo se ha ido: Git simplemente reemplazó ese archivo con la última versión
preparada o confirmada. Nunca use este comando a menos que sepa absolutamente que no quiere
esos cambios locales no guardados.

Trabajar con controles remotos


Para poder colaborar en cualquier proyecto de Git, debe saber cómo administrar su control remoto

49
Machine Translated by Google

repositorios Los repositorios remotos son versiones de su proyecto que están alojadas en Internet o en alguna
red. Puede tener varios de ellos, cada uno de los cuales generalmente es de solo lectura o de lectura/escritura
para usted. Colaborar con otros implica administrar estos repositorios remotos y enviar y recibir datos cuando
necesite compartir el trabajo. Administrar repositorios remotos incluye saber cómo agregar repositorios remotos,
eliminar remotos que ya no son válidos, administrar varias sucursales remotas y definirlas como rastreadas o
no, y más. En esta sección, cubriremos algunas de estas habilidades de administración remota.

Los repositorios remotos pueden estar en su máquina local.

Es muy posible que pueda estar trabajando con un repositorio "remoto" que, de hecho, está

ÿ en el mismo host que usted. La palabra "remoto" no implica necesariamente que el repositorio
esté en otro lugar de la red o de Internet, solo que está en otro lugar. Trabajar con un
repositorio remoto de este tipo todavía implicaría todas las operaciones estándar de empujar,
tirar y buscar como con cualquier otro control remoto.

Mostrando sus controles remotos

Para ver qué servidores remotos ha configurado, puede ejecutar el comando remoto git . Enumera los nombres
abreviados de cada identificador remoto que ha especificado. Si ha clonado su repositorio, al menos debería
ver el origen : ese es el nombre predeterminado que Git le da al servidor desde el que clonó:

$ git clone https://github.com/schacon/ticgit


Clonación en 'ticgit'... remoto: reutilización del
paquete existente: 1857, listo. remoto: Total 1857
(delta 0), reutilizado 0 (delta 0)
Recepción de objetos: 100 % (1857/1857), 374,35 KiB | 268,00 KiB/s, listo.
Resolviendo deltas: 100% (772/772), hecho.
Comprobando conectividad...
hecho. $ cd ticgit $ git origen
remoto

También puede especificar -v, que le muestra las URL que Git ha almacenado para que se use el nombre
abreviado al leer y escribir en ese control remoto:

$ git remoto -v
origen https://github.com/schacon/ticgit (buscar) origen
https://github.com/schacon/ticgit (empujar)

Si tiene más de un control remoto, el comando los enumera a todos. Por ejemplo, un repositorio con múltiples
controles remotos para trabajar con varios colaboradores podría verse así.

50
Machine Translated by Google

$ cd grit $
git remote -v
bakkdoor https://github.com/bakkdoor/grit (buscar) bakkdoor
https://github.com/bakkdoor/grit (empujar) cho45 https://
github.com/cho45/grit
cho45/grit(buscar)
(empujar)cho45 https://github.com/
defunkt https://github.com/
defunkt/grit (buscar) defunkt https://github.com/defunkt/grit
(empujar) koke git ://github.com/koke/grit.git
(buscar) git://github.com/koke/grit.git (empujar)
git@github.com:mojombo/grit.git (buscar)
git@github.com :mojombo/grit.git (presionar)

origen
koke origen

Esto significa que podemos obtener contribuciones de cualquiera de estos usuarios con bastante facilidad. Además, es posible
que tengamos permiso para impulsar uno o más de estos, aunque no podemos decirlo aquí.

Tenga en cuenta que estos controles remotos usan una variedad de protocolos; Cubriremos más sobre esto en Obtener Git en un
servidor.

Agregar repositorios remotos


Hemos mencionado y brindado algunas demostraciones de cómo el comando git clone agrega implícitamente el control remoto de
origen para usted. Aquí se explica cómo agregar un nuevo control remoto explícitamente. Para agregar un nuevo repositorio Git
remoto como un nombre abreviado al que puede hacer referencia fácilmente, ejecute git remote add <shortname> <url>:

$ git origen
remoto $ git
remoto agregar pb https://github.com/paulboone/ticgit $ git remoto
-v origen https://github.com/schacon/ticgit (buscar) origen https://
github.com/schacon /ticgit (empujar) pb https://github.com/paulboone/
ticgit (buscar) pb https://github.com/paulboone/ticgit (empujar)

Ahora puede usar la cadena pb en la línea de comando en lugar de la URL completa. Por ejemplo, si desea obtener toda la
información que tiene Paul pero que aún no tiene en su repositorio, puede ejecutar git fetch pb:

$ git fetch pb
remote: Contando objetos: 43, listo. remoto:
Comprimir objetos: 100% (36/36), hecho. remoto: Total 43
(delta 10), reutilizado 31 (delta 5)
Desembalaje de objetos: 100% (43/43), hecho.
Desde https://github.com/paulboone/ticgit *
[nueva rama] -> pb/mastermaestro
* [nueva rama] -> pb/
ticgit
ticgit

51
Machine Translated by Google

Ahora se puede acceder localmente a la rama principal de Paul como pb / master ; puede fusionarla con una de sus ramas o
puede consultar una rama local en ese punto si desea inspeccionarla. Repasaremos qué son las ramas y cómo usarlas con
mucho más detalle en Git Branching.

Obtener y extraer de sus controles remotos

Como acabas de ver, para obtener datos de tus proyectos remotos, puedes ejecutar:

$ git buscar <remoto>

El comando va a ese proyecto remoto y extrae todos los datos de ese proyecto remoto que aún no tiene. Después de hacer
esto, debe tener referencias a todas las sucursales desde ese control remoto, que puede fusionar o inspeccionar en cualquier
momento.

Si clona un repositorio, el comando agrega automáticamente ese repositorio remoto con el nombre "origen". Por lo tanto, git
fetch origin obtiene cualquier trabajo nuevo que se haya enviado a ese servidor desde que lo clonó (o lo obtuvo por última
vez). Es importante tener en cuenta que el comando git fetch solo descarga los datos a su repositorio local; no los fusiona
automáticamente con ninguno de sus trabajos ni modifica lo que está trabajando actualmente. Tienes que fusionarlo
manualmente en tu trabajo cuando estés listo.

Si su sucursal actual está configurada para rastrear una sucursal remota (consulte la siguiente sección y Git Branching para
obtener más información), puede usar el comando git pull para buscar automáticamente y luego fusionar esa sucursal remota
en su sucursal actual. Este puede ser un flujo de trabajo más fácil o más cómodo para usted; y, de forma predeterminada, el
comando git clone configura automáticamente su rama maestra local para rastrear la rama maestra remota (o como se llame
la rama predeterminada) en el servidor desde el que clonó.
Ejecutar git pull generalmente obtiene datos del servidor desde el que clonó originalmente e intenta fusionarlos
automáticamente con el código en el que está trabajando actualmente.

Desde la versión 2.27 de git en adelante, git pull dará una advertencia si la variable pull.rebase no está
configurada. Git seguirá advirtiéndote hasta que configures la variable.

ÿ Si desea el comportamiento predeterminado de git (avance rápido si es posible, de lo contrario, cree


una confirmación de fusión): git config --global pull.rebase "false"

Si desea volver a establecer la base al extraer: git config --global pull.rebase "true"

Empujando a sus controles remotos

Cuando tiene su proyecto en un punto que desea compartir, debe impulsarlo aguas arriba. El comando para esto es simple:
git push <remote> <branch>. Si desea enviar su rama maestra a su servidor de origen (nuevamente, la clonación
generalmente configura ambos nombres para usted automáticamente), puede ejecutar esto para enviar cualquier confirmación
que haya hecho al servidor:

$ git empujar maestro de origen

Este comando solo funciona si clonó desde un servidor al que tiene acceso de escritura y si

52
Machine Translated by Google

nadie ha empujado mientras tanto. Si usted y otra persona clonan al mismo tiempo y empujan contra la corriente
y luego empujan contra la corriente, su empuje será rechazado con razón. Tendrás que buscar su trabajo
primero e incorporarlo al tuyo antes de que se te permita presionar. Consulte Git Branching para obtener
información más detallada sobre cómo enviar contenido a servidores remotos.

Inspeccionar un control remoto

Si desea ver más información sobre un control remoto en particular, puede usar el comando git remote show
<remote> . Si ejecuta este comando con un nombre abreviado en particular, como origen, obtiene algo como
esto:

$ git remote show origin


*
origen remoto Obtener
URL: https://github.com/schacon/ticgit Push URL:
https://github.com/schacon/ticgit HEAD branch:
master
Sucursales remotas:
maestro rastreado dev-rama rastreada

Rama local configurada para 'git pull': el


maestro se fusiona con el maestro remoto
Referencia local configurada para 'git push':
maestro empuja a maestro (actualizado)

Muestra la URL del repositorio remoto, así como la información de la rama de seguimiento. El comando
útilmente le dice que si está en la rama maestra y ejecuta git pull, fusionará automáticamente la rama maestra
del control remoto con la local después de que se haya obtenido. También enumera todas las referencias
remotas que ha extraído.

Ese es un ejemplo simple que es probable que encuentre. Sin embargo, cuando usas Git más intensamente,
es posible que veas mucha más información de git remote show:

53
Machine Translated by Google

$ git mostrar origen remoto


*
origen remoto
URL: https://github.com/my-org/complex-project
Obtener URL: https://github.com/my-org/complex-project
Empuje URL: https://github.com/my-org/complex-project
rama HEAD: maestro
Sucursales remotas:
rama maestra rastreado
de desarrollo rastreado
markdown-strip rastreado
problema-43 problema-45 nuevo (la próxima búsqueda se almacenará en remotos/origen)
referencias/controles nuevo (la próxima búsqueda se almacenará en remotos/origen)
remotos/origen/problema-11 obsoleto (use 'git remote prune' para eliminar)
Ramas locales configuradas para 'git pull':
dev-branch se fusiona con dev-branch remoto
Maestro se fusiona con el maestro remoto
Referencias locales configuradas para 'git push':
fecha de rama de empuja a la rama de desarrollo (hasta
desarrollo)
fecha de la franja de empuja a la tira de rebajas (hasta
rebajas)
fecha maestra) empuja a dominar (hasta

Este comando muestra a qué rama se envía automáticamente cuando ejecuta git push mientras está en
ciertas ramas. También le muestra qué sucursales remotas en el servidor aún no tiene, cuáles
sucursales remotas que tiene que se han eliminado del servidor y varias sucursales locales
que pueden fusionarse automáticamente con su rama de seguimiento remoto cuando ejecutas git pull.

Cambio de nombre y eliminación de controles remotos

Puede ejecutar git remote rename para cambiar el nombre abreviado de un control remoto. Por ejemplo, si desea cambiar el nombre
pb a paul, puedes hacerlo con git remote rename:

$ git remoto renombrar pb paul


$ git remoto
origen
Pablo

Vale la pena mencionar que esto también cambia todos los nombres de las sucursales de seguimiento remoto. lo que solía
ser referenciado en pb/master ahora está en paul/master.

Si desea eliminar un control remoto por algún motivo: movió el servidor o ya no lo usa
un espejo en particular, o tal vez un colaborador ya no está contribuyendo; puede usar git
eliminación remota o git remoto rm:

54
Machine Translated by Google

$ git remoto eliminar paul


$ git origen remoto

Una vez que elimine la referencia a un control remoto de esta manera, también se eliminarán todas las ramas de seguimiento remoto
y los ajustes de configuración asociados con ese control remoto.

Etiquetado
Como la mayoría de los VCS, Git tiene la capacidad de etiquetar puntos específicos en el historial de un repositorio como importantes.
Por lo general, las personas usan esta funcionalidad para marcar puntos de lanzamiento (v1.0, v2.0 , etc.). En esta sección, aprenderá
cómo enumerar las etiquetas existentes, cómo crear y eliminar etiquetas y cuáles son los diferentes tipos de etiquetas.

Listado de sus etiquetas

Enumerar las etiquetas existentes en Git es sencillo. Simplemente escriba la etiqueta git (con opcional -l o --list):

$ git etiqueta
v1.0
v2.0

Este comando enumera las etiquetas en orden alfabético; el orden en que se muestran no tiene importancia real.

También puede buscar etiquetas que coincidan con un patrón en particular. El repositorio fuente de Git, por ejemplo, contiene más de
500 etiquetas. Si solo está interesado en ver la serie 1.8.5, puede ejecutar esto:

$ git etiqueta -l "v1.8.5*"


v1.8.5 v1.8.5-rc0

v1.8.5-rc1
v1.8.5-rc2
v1.8.5-rc3
v1.8.5.1
v1.8.5.2
v1.8.5.3
v1.8.5.4
v1.8.5.5

55
Machine Translated by Google

La lista de comodines de etiquetas requiere la opción -l o --list

Si solo desea la lista completa de etiquetas, ejecutar el comando git tag asume implícitamente que
desea una lista y proporciona una; el uso de -l o --list en este caso es opcional.
ÿ
Sin embargo, si proporciona un patrón comodín para hacer coincidir los nombres de las etiquetas, el
uso de -l o --list es obligatorio.

Creación de etiquetas

Git admite dos tipos de etiquetas: ligeras y anotadas.

Una etiqueta ligera es muy parecida a una rama que no cambia: es solo un puntero a una confirmación específica.

Sin embargo, las etiquetas anotadas se almacenan como objetos completos en la base de datos de Git. Son sumas de
verificación; contener el nombre del etiquetador, correo electrónico y fecha; tener un mensaje de etiquetado; y se puede
firmar y verificar con GNU Privacy Guard (GPG). En general, se recomienda que cree etiquetas anotadas para que pueda
tener toda esta información; pero si desea una etiqueta temporal o por alguna razón no desea conservar la otra información,
también hay disponibles etiquetas ligeras.

Etiquetas anotadas

Crear una etiqueta anotada en Git es simple. La forma más fácil es especificar -a cuando ejecuta el comando de etiqueta :

$ git etiqueta -a v1.4 -m "mi versión 1.4" $


git etiqueta v0.1

v1.3
v1.4

El -m especifica un mensaje de etiquetado, que se almacena con la etiqueta. Si no especifica un mensaje para una etiqueta
anotada, Git inicia su editor para que pueda escribirlo.

Puede ver los datos de la etiqueta junto con la confirmación que se etiquetó usando el comando git show :

56
Machine Translated by Google

$ git show v1.4


etiqueta v1.4
Etiquetador: Ben Straub <ben@straub.cc>
Fecha: sábado 3 de mayo 20:19:12 2014 -0700

mi versión 1.4

commit ca82a6dff817ec66f44342007202690a93763949 Autor:


Scott Chacon <schacon@gee-mail.com> Fecha: lun 17 de
marzo 21:52:11 2008 -0700

Cambiar número de versión

Eso muestra la información del etiquetador, la fecha en que se etiquetó la confirmación y el mensaje de anotación antes de mostrar la
información de la confirmación.

Etiquetas ligeras
Otra forma de etiquetar confirmaciones es con una etiqueta ligera. Esta es básicamente la suma de verificación de confirmación
almacenada en un archivo; no se guarda ninguna otra información. Para crear una etiqueta ligera, no proporcione ninguna de las
opciones -a, -s o -m , solo proporcione un nombre de etiqueta:

$ etiqueta git v1.4-lw


$ etiqueta git v0.1

v1.3
v1.4
v1.4-lw
v1.5

Esta vez, si ejecuta git show en la etiqueta, no verá la información adicional de la etiqueta. El comando solo muestra el compromiso:

$ git show v1.4-lw


commit ca82a6dff817ec66f44342007202690a93763949 Autor:
Scott Chacon <schacon@gee-mail.com> Fecha: lun 17 de
marzo 21:52:11 2008 -0700

Cambiar número de versión

Etiquetado posterior

También puede etiquetar confirmaciones después de haberlas superado. Supongamos que su historial de confirmación se ve así:

57
Machine Translated by Google

$ Git log = --pretty oneline


15027957951b64cf874c3557a0f3547bd83b3ff6 Combinar rama 'experimento'
a6b4c97498bd301d84096da251c98a07c7723e65 Crear soporte de escritura
0d52aaab4479697da7686c15f77a3d64d9165190 Una cosa más rama
6d52a271eda8725415634dd79daabbc4d9b6008e Combinar 'experimento'
0b7434d86859cc7b8c3d5e1dddfed66ff742fcbc Añadir función de cometer
4682c3261057305bdd616e23b64b0857d832627b Añadir TODO archivo
166ae0c4d3f420721acbb115cc33848dfcc2121a Crear soporte de escritura
9fceb02d0ae598e95dc970b74767f19372d61af8 actualización Rakefile
964f16d36dfccde844893cac5b347e7b3d44abbc Commit el TODO
8a5cbc430f1a9c3d00faaeffd07798508422908a Léame de actualización

Ahora, suponga que olvidó etiquetar el proyecto en v1.2, que estaba en la confirmación "Actualizar archivo de rake". Puede
agregarlo después del hecho. Para etiquetar esa confirmación, especifica la suma de comprobación de la confirmación (o parte
de ella) al final del comando:

$ git etiqueta -a v1.2 9fceb02

Puedes ver que has etiquetado la confirmación:

$ git etiqueta
v0.1
v1.2
v1.3
v1.4
v1.4-lw
v1.5

$ git show v1.2


etiqueta v1.2
Etiquetador: Scott Chacon <schacon@gee-mail.com>
Fecha: lun 9 de febrero 15:32:16 2009 -0800

versión 1.2
commit 9fceb02d0ae598e95dc970b74767f19372d61af8 Autor:
Magnus Chacon <mchacon@gee-mail.com> Fecha: dom 27 de
abril 20:43:35 2008 -0700

Actualizar archivo de rake


...

Compartir etiquetas

De forma predeterminada, el comando git push no transfiere etiquetas a servidores remotos. Tendrá que enviar etiquetas
explícitamente a un servidor compartido después de haberlas creado. Este proceso es como compartir sucursales remotas:
puede ejecutar git push origin <tagname>.

58
Machine Translated by Google

$ git push origin v1.5


Contando objetos: 14, listo.
Compresión delta usando hasta 8 hilos.
Comprimir objetos: 100% (12/12), hecho.
Objetos de escritura: 100 % (14/14), 2,05 KiB | 0 bytes/s, listo.
Total 14 (delta 3), reutilizado 0 (delta 0)
Para git@github.com:schacon/simplegit.git *
[nueva etiqueta] v1.5 -> v1.5

Si tiene muchas etiquetas que desea enviar a la vez, también puede usar la opción --tags para el comando git push .
Esto transferirá todas sus etiquetas al servidor remoto que aún no están allí.

$ git push origin --tags Contar


objetos: 1, listo.
Objetos de escritura: 100% (1/1), 160 bytes | 0 bytes/s, listo.
Total 1 (delta 0), reutilizado 0 (delta 0)
Para git@github.com:schacon/simplegit.git *
v1.4 -> v1.4
[nueva etiqueta] * [nueva etiqueta]
v1.4-lw -> v1.4-lw

Ahora, cuando alguien más clone o extraiga de su repositorio, también obtendrá todas sus etiquetas.

git push empuja ambos tipos de etiquetas

ÿ git push <remote> --tags enviará etiquetas ligeras y anotadas. Actualmente no hay opción para
enviar solo etiquetas ligeras, pero si usa git push <remote> --follow-tags, solo las etiquetas
anotadas se enviarán al control remoto.

Eliminación de etiquetas

Para eliminar una etiqueta en tu repositorio local, puedes usar git tag -d <tagname>. Por ejemplo, podríamos eliminar
nuestra etiqueta ligera anterior de la siguiente manera:

$ git tag -d v1.4-lw


Etiqueta eliminada 'v1.4-lw' (era e7d5add)

Tenga en cuenta que esto no elimina la etiqueta de ningún servidor remoto. Hay dos variaciones comunes para
eliminar una etiqueta de un servidor remoto.

La primera variación es git push <remote> :refs/tags/<tagname>:

$ git push origen: refs/tags/v1.4-lw Para /


git@github.com:schacon/simplegit.git - [eliminado]
v1.4-lw

59
Machine Translated by Google

La forma de interpretar lo anterior es leerlo como el valor nulo antes de que los dos puntos se inserten en el nombre de la etiqueta
remota, eliminándolo efectivamente.

La segunda (y más intuitiva) forma de eliminar una etiqueta remota es con:

$ git push origin --delete <nombre de etiqueta>

Echando un vistazo a las etiquetas

Si desea ver las versiones de los archivos a los que apunta una etiqueta, puede hacer una comprobación de git de esa etiqueta,
aunque esto pone su repositorio en estado de "CABEZA separada", lo que tiene algunos efectos secundarios negativos:

$ git checkout v2.0.0 Nota:


cambiando a 'v2.0.0'.

Estás en estado de 'CABEZA separada'. Puede mirar a su alrededor, realizar cambios


experimentales y confirmarlos, y puede descartar cualquier confirmación que realice en este
estado sin afectar ninguna rama al realizar otra comprobación.

Si desea crear una nueva rama para retener las confirmaciones que creó, puede hacerlo
(ahora o más tarde) usando -c con el comando cambiar. Ejemplo:

git switch -c <nuevo-nombre-de-sucursal>

O deshaga esta operación con:

interruptor git -

Desactive este consejo configurando la variable de configuración advisor.tachedHead en false

HEAD está ahora en 99ada87... Merge pull request #89 from schacon/appendix-final

$ git checkout v2.0-beta-0.1 La


posición anterior de HEAD era 99ada87... Fusionar la solicitud de extracción n.º 89 de schacon/appendix
final HEAD ahora está en df3f601... Agregar atlas.json e imagen de portada

En el estado "HEAD separado", si realiza cambios y luego crea una confirmación, la etiqueta permanecerá igual, pero su nueva
confirmación no pertenecerá a ninguna rama y no se podrá acceder a ella, excepto por el hash de confirmación exacto. Por lo
tanto, si necesita realizar cambios, digamos que está arreglando un error en una versión anterior, por ejemplo, generalmente
querrá crear una rama:

$ git checkout -b version2 v2.0.0 Cambiado


a una nueva rama 'version2'

Si hace esto y realiza una confirmación, su rama versión 2 será ligeramente diferente a su etiqueta v2.0.0 , ya que avanzará con
sus nuevos cambios, así que tenga cuidado.

60
Machine Translated by Google

Alias de Git
Antes de pasar al siguiente capítulo, queremos presentar una función que puede hacer que su experiencia con Git sea
más simple, fácil y familiar: los alias. En aras de la claridad, no los usaremos en ninguna otra parte de este libro, pero si
continúa usando Git con regularidad, los alias son algo que debe conocer.

Git no infiere automáticamente su comando si lo escribe parcialmente. Si no desea escribir el texto completo de cada uno
de los comandos de Git, puede configurar fácilmente un alias para cada comando usando git config. Aquí hay un par de
ejemplos que puede configurar:

$ git config --global alias.co checkout $ git


config --global alias.br branch $ git config
--global alias.ci commit $ git config --global
alias.st estado

Esto significa que, por ejemplo, en lugar de escribir git commit, solo necesita escribir git ci. A medida que vaya usando Git,
probablemente también usará otros comandos con frecuencia; no dude en crear nuevos alias.

Esta técnica también puede ser muy útil para crear comandos que cree que deberían existir. Por ejemplo, para corregir el
problema de usabilidad que encontró al desmontar un archivo, puede agregar su propio alias de desmontaje a Git:

$ git config --alias global.unstage 'restablecer HEAD --'

Esto hace que los siguientes dos comandos sean equivalentes:

$ git unstage fileA $


git reset HEAD -- fileA

Esto parece un poco más claro. También es común agregar un último comando, como este:

$ git config --global alias.last 'log -1 HEAD'

De esta manera, puedes ver la última confirmación fácilmente:

61
Machine Translated by Google

$ git última
confirmación 66938dae3329c7aebe598c2246a8e6af90d04646
Autor: Josh Goebel <dreamer3@example.com> Fecha:
martes 26 de agosto 19:48:51 2008 +0800

Prueba para cabeza actual

Firmado por: Scott Chacon <schacon@example.com>

Como puede ver, Git simplemente reemplaza el nuevo comando con cualquier alias para el que lo haya creado.
Sin embargo, tal vez desee ejecutar un comando externo, en lugar de un subcomando de Git. En ese caso, inicia
el comando con un ! personaje. Esto es útil si escribe sus propias herramientas que funcionan con un repositorio
de Git. Podemos demostrarlo creando un alias de git visual para ejecutar gitk:

$ git config --alias global.visual '!gitk'

Resumen
En este punto, puede realizar todas las operaciones básicas de Git local: crear o clonar un repositorio, realizar
cambios, organizar y confirmar esos cambios y ver el historial de todos los cambios por los que ha pasado el
repositorio. A continuación, cubriremos la característica principal de Git: su modelo de ramificación.

62
Machine Translated by Google

Ramificación Git
Casi todos los VCS tienen algún tipo de soporte de bifurcación. La ramificación significa que te desvías de la línea principal
de desarrollo y continúas trabajando sin alterar esa línea principal. En muchas herramientas de VCS, este es un proceso
algo costoso, que a menudo requiere que cree una nueva copia de su directorio de código fuente, lo que puede llevar
mucho tiempo para proyectos grandes.

Algunas personas se refieren al modelo de bifurcación de Git como su "característica principal", y ciertamente distingue a
Git en la comunidad de VCS. ¿Por qué es tan especial? La forma en que Git ramifica es increíblemente liviana, lo que
hace que las operaciones de ramificación sean casi instantáneas y, en general, cambiar de una rama a otra con la misma
rapidez. A diferencia de muchos otros VCS, Git fomenta los flujos de trabajo que se ramifican y fusionan con frecuencia,
incluso varias veces al día. Comprender y dominar esta función le brinda una herramienta poderosa y única y puede
cambiar por completo la forma en que se desarrolla.

Ramas en pocas palabras

Para comprender realmente la forma en que Git realiza la bifurcación, debemos dar un paso atrás y examinar cómo Git
almacena sus datos.

Como recordará de ¿Qué es Git?, Git no almacena datos como una serie de conjuntos de cambios o diferencias, sino
como una serie de instantáneas.

Cuando realiza una confirmación, Git almacena un objeto de confirmación que contiene un puntero a la instantánea del
contenido que preparó. Este objeto también contiene el nombre y la dirección de correo electrónico del autor, el mensaje
que escribió y punteros a la confirmación o confirmaciones que vinieron directamente antes de esta confirmación (su padre
o padres): cero padres para la confirmación inicial, un padre para una confirmación normal y múltiples padres para una
confirmación que resulta de una fusión de dos o más ramas.

Para visualizar esto, supongamos que tiene un directorio que contiene tres archivos, los prepara todos y los confirma. La
preparación de los archivos calcula una suma de verificación para cada uno (el hash SHA-1 que mencionamos en ¿Qué
es Git?), almacena esa versión del archivo en el repositorio de Git (Git se refiere a ellos como blobs) y agrega esa suma
de verificación a la preparación . área:

$ git add README test.rb


LICENCIA $ git commit -m 'Commit inicial'

Cuando crea la confirmación ejecutando git commit, Git suma cada subdirectorio (en este caso, solo el directorio raíz del
proyecto) y los almacena como un objeto de árbol en el repositorio de Git. Luego, Git crea un objeto de confirmación que
tiene los metadatos y un puntero al árbol del proyecto raíz para que pueda volver a crear esa instantánea cuando sea
necesario.

Su repositorio de Git ahora contiene cinco objetos: tres blobs (cada uno representa el contenido de uno de los tres
archivos), un árbol que enumera el contenido del directorio y especifica qué nombres de archivo se almacenan como qué
blobs, y una confirmación con el puntero a ese árbol raíz y todos los metadatos de confirmación.

63
Machine Translated by Google

Figura 9. Un commit y su árbol

Si realiza algunos cambios y confirma de nuevo, la próxima confirmación almacena un puntero a la confirmación que
vino inmediatamente antes.

Figura 10. Confirmaciones y sus padres

Una rama en Git es simplemente un puntero móvil ligero a una de estas confirmaciones. El nombre de rama
predeterminado en Git es maestro. A medida que comienza a realizar confirmaciones, se le proporciona una rama
maestra que apunta a la última confirmación que realizó. Cada vez que confirmas, el puntero de la rama maestra
avanza automáticamente.

La rama "maestra" en Git no es una rama especial. Es exactamente como cualquier otra rama.
ÿ La única razón por la que casi todos los repositorios tienen uno es que el comando git init lo crea
de manera predeterminada y la mayoría de las personas no se molestan en cambiarlo.

64
Machine Translated by Google

Figura 11. Una rama y su historial de confirmaciones

Creación de una nueva sucursal

¿Qué sucede cuando creas una nueva rama? Bueno, al hacerlo se crea un nuevo puntero para que te muevas. Digamos que desea
crear una nueva rama llamada testing. Haces esto con el comando git branch :

prueba de rama $ git

Esto crea un nuevo puntero a la misma confirmación en la que se encuentra actualmente.

Figura 12. Dos ramas que apuntan a la misma serie de confirmaciones

¿Cómo sabe Git en qué rama estás actualmente? Mantiene un puntero especial llamado HEAD. Tenga en cuenta que esto es muy
diferente al concepto de HEAD en otros VCS a los que puede estar acostumbrado, como Subversion o CVS. En Git, este es un
puntero a la rama local en la que se encuentra actualmente. En este caso, todavía estás en el maestro. El comando git branch solo
creó una nueva rama, no cambió a eso

sesenta y cinco
Machine Translated by Google

rama.

Figura 13. CABEZA apuntando a una rama

Puede ver esto fácilmente ejecutando un simple comando de registro de git que le muestra hacia dónde
apuntan los punteros de rama. Esta opción se llama --decorar.

$ git log --oneline --decorate f30ab


(HEAD -> master, testing) Agregar función n.° 32: posibilidad de agregar nuevos formatos a la interfaz
central 34ac2 Corrección del error n.° 1328: desbordamiento de pila en determinadas condiciones
98ca9 Confirmación inicial

Puede ver las ramas maestra y de prueba que están justo al lado de la confirmación de f30ab .

Cambio de sucursales

Para cambiar a una sucursal existente, ejecuta el comando git checkout . Pasemos a la nueva rama de
pruebas :

$ prueba de pago de git

Esto mueve HEAD para señalar la rama de prueba .

66
Machine Translated by Google

Figura 14. HEAD apunta a la rama actual

¿Cuál es el significado de eso? Bueno, hagamos otro commit:

$ vim test.rb
$ git commit -a -m 'hizo un cambio'

Figura 15. La rama HEAD avanza cuando se realiza una confirmación

Esto es interesante, porque ahora su rama de prueba ha avanzado, pero su rama maestra aún apunta
a la confirmación en la que estaba cuando ejecutó git checkout para cambiar de rama. Volvamos a la
rama principal :

$ git pago maestro

67
Machine Translated by Google

git log no muestra todas las ramas todo el tiempo

Si tuviera que ejecutar git log en este momento, es posible que se pregunte a dónde fue la rama
de "prueba" que acaba de crear, ya que no aparecería en la salida.

La rama no ha desaparecido; Git simplemente no sabe que estás interesado en esa rama y está
ÿ tratando de mostrarte lo que cree que te interesa. En otras palabras, de forma predeterminada, git
log solo mostrará el historial de confirmaciones debajo de la rama que has controlado.

Para mostrar el historial de confirmación de la rama deseada, debe especificarlo explícitamente:


git log testing. Para mostrar todas las ramas, agregue --all a su comando de registro de git .

Figura 16. HEAD se mueve cuando pagas

Ese comando hizo dos cosas. Volvió a mover el puntero HEAD para que apuntara a la rama maestra y revirtió los
archivos en su directorio de trabajo a la instantánea a la que apunta el maestro . Esto también significa que los cambios
que realice a partir de este momento se diferenciarán de una versión anterior del proyecto. Básicamente, rebobina el
trabajo que ha realizado en su rama de prueba para que pueda ir en una dirección diferente.

Cambiar de rama cambia los archivos en su directorio de trabajo

Es importante tener en cuenta que cuando cambias de rama en Git, los archivos en tu directorio
ÿ de trabajo cambiarán. Si cambia a una rama más antigua, su directorio de trabajo se revertirá para
que se vea como la última vez que se comprometió en esa rama. Si Git no puede hacerlo
limpiamente, no le permitirá cambiar en absoluto.

Hagamos algunos cambios y confirmemos de nuevo:

$ vim test.rb
$ git commit -a -m 'hizo otros cambios'

Ahora el historial de su proyecto ha divergido (consulte Historial divergente). Usted creó y cambió a una rama, hizo
algo de trabajo en ella y luego volvió a su rama principal e hizo otro trabajo. Ambos cambios están aislados en ramas
separadas: puede alternar entre el

68
Machine Translated by Google

ramas y combínalas cuando estés listo. E hizo todo eso con comandos simples de bifurcación, pago y confirmación .

Figura 17. Historia divergente

También puede ver esto fácilmente con el comando git log . Si ejecuta git log --oneline --decorate --graph --all , imprimirá el
historial de sus confirmaciones, mostrando dónde están sus punteros de rama y cómo se ha desviado su historial.

$ git log --oneline --decorate --graph --all * c2b9e


(HEAD, maestro) Hizo otros cambios | * 87ab2
(prueba) Hizo un cambio |/ * f30ab Agregar
característica #32 - capacidad de agregar nuevos
formatos a la interfaz central * 34ac2 Corrección de error #1328 - desbordamiento de pila
bajo ciertas condiciones * 98ca9 confirmación inicial de mi proyecto

Debido a que una rama en Git es en realidad un archivo simple que contiene la suma de verificación SHA-1 de 40 caracteres
de la confirmación a la que apunta, las ramas son baratas de crear y destruir. Crear una nueva rama es tan rápido y simple
como escribir 41 bytes en un archivo (40 caracteres y una nueva línea).

Esto contrasta marcadamente con la forma en que se bifurcan la mayoría de las herramientas VCS más antiguas, que
implica copiar todos los archivos del proyecto en un segundo directorio. Esto puede llevar varios segundos o incluso
minutos, dependiendo del tamaño del proyecto, mientras que en Git el proceso siempre es instantáneo. Además, debido a
que estamos grabando a los padres cuando nos comprometemos, encontrar una base de fusión adecuada para la fusión
se realiza automáticamente y, en general, es muy fácil de hacer. Estas características ayudan a animar a los desarrolladores
a crear y usar sucursales con frecuencia.

Veamos por qué deberías hacerlo.

69
Machine Translated by Google

Crear una nueva rama y cambiar a ella al mismo tiempo

ÿ Es típico crear una nueva rama y querer cambiar a esa nueva rama al mismo tiempo; esto se puede hacer
en una sola operación con git checkout -b <newbranchname>.

Desde la versión 2.23 de Git en adelante, puede usar git switch en lugar de git checkout para:

• Cambiar a una rama existente: git switch testing-branch.

ÿ • Cree una nueva rama y cambie a ella: git switch -c new-branch. La bandera -c
significa crear, también puede usar la bandera completa: --create.

• Regrese a su sucursal previamente desprotegida: git switch -.

Ramificación y fusión básicas


Veamos un ejemplo simple de bifurcación y fusión con un flujo de trabajo que podría usar en el mundo real. Seguirás estos pasos:

1. Trabaja un poco en un sitio web.

2. Cree una rama para una nueva historia de usuario en la que esté trabajando.

3. Haz algún trabajo en esa rama.

En esta etapa, recibirá una llamada de que otro problema es crítico y necesita una revisión. Harás lo siguiente:

1. Cambie a su sucursal de producción.

2. Cree una rama para agregar la revisión.

3. Después de probarlo, fusione la rama de revisión y pase a producción.

4. Vuelva a su historia de usuario original y continúe trabajando.

Ramificación básica

Primero, supongamos que está trabajando en su proyecto y ya tiene un par de confirmaciones en la rama maestra .

Figura 18. Un historial de confirmación simple

70
Machine Translated by Google

Ha decidido que va a trabajar en el problema 53 en cualquier sistema de seguimiento de problemas que utilice su
empresa. Para crear una nueva rama y cambiar a ella al mismo tiempo, puede ejecutar el comando git checkout con
el interruptor -b :

$ git pago -b iss53


Cambiado a una nueva rama "iss53"

Esta es la abreviatura de:

$ git sucursal iss53 $


git pago iss53

Figura 19. Creando un nuevo puntero de rama

Trabajas en tu sitio web y haces algunas confirmaciones. Al hacerlo, la rama iss53 se mueve hacia adelante, porque
la tiene desprotegida (es decir, su CABEZA la está apuntando):

$ vim index.html $
git commit -a -m 'Crear nuevo pie de página [problema 53]'

71
Machine Translated by Google

Figura 20. La rama iss53 ha avanzado con su trabajo

Ahora recibe la llamada de que hay un problema con el sitio web y debe solucionarlo de inmediato. Con Git, no tiene que
implementar su solución junto con los cambios de iss53 que ha realizado, y no tiene que esforzarse mucho para revertir esos
cambios antes de que pueda aplicar su solución a lo que está en producción. Todo lo que tiene que hacer es volver a su
rama maestra .

Sin embargo, antes de hacerlo, tenga en cuenta que si su directorio de trabajo o área de preparación tiene cambios no
confirmados que entran en conflicto con la rama que está revisando, Git no le permitirá cambiar de rama. Es mejor tener un
estado de trabajo limpio cuando cambia de sucursal. Hay formas de evitar esto (es decir, ocultar y modificar la confirmación)
que veremos más adelante, en Almacenamiento y limpieza. Por ahora, supongamos que ha confirmado todos sus cambios,
por lo que puede volver a su rama principal :

$ git pago maestro


Cambiado a la rama 'maestro'

En este punto, el directorio de trabajo de su proyecto es exactamente como era antes de que comenzara a trabajar en el
problema 53, y puede concentrarse en su revisión. Este es un punto importante para recordar: cuando cambias de rama, Git
restablece tu directorio de trabajo para que luzca como la última vez que te comprometiste en esa rama. Agrega, elimina y
modifica archivos automáticamente para asegurarse de que su copia de trabajo sea como se veía la rama en su última
confirmación.

A continuación, tiene que hacer una revisión. Vamos a crear una rama de revisión en la que trabajar hasta que se complete:

$ git checkout -b hotfix


Cambiado a una nueva rama 'revisión' $
vim index.html $ git commit -a -m 'Reparar
dirección de correo electrónico rota' [revisión 1fb7853]
Reparar dirección de correo electrónico rota 1 archivo
cambiado, 2 inserciones (+)

72
Machine Translated by Google

Figura 21. Rama Hotfix basada en maestro

Puede ejecutar sus pruebas, asegurarse de que la revisión sea lo que desea y, finalmente, fusionar la rama de
revisión nuevamente en su rama maestra para implementarla en producción. Haces esto con el comando git merge :

$ git checkout master $


git merge hotfix
Actualización de
f42c576..3a0874c Avance
rápido index.html | 2 ++ 1
archivo cambiado, 2 inserciones (+)

Notarás la frase "avance rápido" en esa fusión. Debido a que la confirmación C4 a la que apunta la revisión de la
rama en la que se fusionó estaba directamente delante de la confirmación C2 en la que se encuentra, Git simplemente
mueve el puntero hacia adelante. Para expresarlo de otra manera, cuando intenta fusionar una confirmación con una
confirmación a la que se puede llegar siguiendo el historial de la primera confirmación, Git simplifica las cosas
moviendo el puntero hacia adelante porque no hay trabajo divergente para fusionar, esto se llama " avance rápido."

Su cambio ahora está en la instantánea de la confirmación a la que apunta la rama principal y puede implementar la
corrección.

73
Machine Translated by Google

Figura 22. El maestro se reenvía rápidamente a revisión

Una vez implementada la solución súper importante, estará listo para volver al trabajo que estaba
haciendo antes de que lo interrumpieran. Sin embargo, primero eliminará la rama de revisión , porque
ya no la necesita: la rama maestra apunta al mismo lugar. Puede eliminarlo con la opción -d para git
branch:

$ git branch -d hotfix


Revisión de rama eliminada (3a0874c).

Ahora puede volver a su rama de trabajo en curso en el número 53 y continuar trabajando en él.

$ git checkout iss53


Cambiado a la rama "iss53" $
vim index.html $ git commit -a
-m 'Finalizar el nuevo pie de página [número 53]' [iss53
ad82d7a] Finalizar el nuevo pie de página [número 53] 1
archivo cambiado, 1 inserción (+)

74
Machine Translated by Google

Figura 23. Continúa el trabajo en iss53

Vale la pena señalar aquí que el trabajo que hizo en su rama de revisiones no está contenido en los archivos de su
rama iss53 . Si necesita incorporarlo, puede fusionar su rama principal en su rama iss53 ejecutando git merge master,
o puede esperar para integrar esos cambios hasta que decida volver a incorporar la rama iss53 en la principal más
tarde.

Fusión básica
Supongamos que ha decidido que el trabajo de su número 53 está completo y listo para fusionarse con su rama
principal . Para hacer eso, fusionará su rama iss53 en master, al igual que fusionó su rama de revisión anteriormente.
Todo lo que tiene que hacer es verificar la rama en la que desea fusionarse y luego ejecutar el comando git merge :

$ git checkout master


Cambiado a branch 'master'
$ git merge iss53 Fusión
realizada por la estrategia 'recursiva'.
índice.html | 1 + 1 inserción
archivo cambiado,
(+) 1

Esto se ve un poco diferente a la combinación de revisión que hizo anteriormente. En este caso, su historial de
desarrollo se ha desviado de algún punto más antiguo. Debido a que la confirmación en la rama en la que estás no
es un ancestro directo de la rama en la que te estás fusionando, Git tiene que trabajar un poco. En este caso, Git
realiza una combinación simple de tres vías, utilizando las dos instantáneas a las que apuntan las puntas de las
ramas y el ancestro común de las dos.

75
Machine Translated by Google

Figura 24. Tres instantáneas utilizadas en una combinación típica

En lugar de simplemente mover el puntero de la rama hacia adelante, Git crea una nueva instantánea que resulta de
esta combinación de tres vías y crea automáticamente una nueva confirmación que apunta a ella. Esto se conoce como
una confirmación de combinación y es especial porque tiene más de un padre.

Figura 25. Una confirmación de fusión

Ahora que su trabajo está fusionado, ya no necesita la rama iss53 . Puede cerrar el problema en su sistema de
seguimiento de problemas y eliminar la rama:

$ git rama -d iss53

Conflictos básicos de fusión

Ocasionalmente, este proceso no transcurre sin problemas. Si cambió la misma parte del mismo archivo de manera
diferente en las dos ramas que está fusionando, Git no podrá fusionarlos limpiamente. Si su solución para el problema
n.° 53 modificó la misma parte de un archivo que la rama de revisión , obtendrá un conflicto de combinación similar a
este:

76
Machine Translated by Google

$ git merge iss53


Fusión automática de
index.html CONFLICTO (contenido): Conflicto de fusión en
index.html Fusión automática fallida; solucione los conflictos y luego confirme el resultado.

Git no ha creado automáticamente una nueva confirmación de fusión. Ha pausado el proceso mientras resuelves
el conflicto. Si desea ver qué archivos no se fusionaron en algún momento después de un conflicto de fusión, puede
ejecutar git status:

$ git status On
branch master Tiene
rutas no fusionadas. (solucionar
conflictos y ejecutar "git commit")

Rutas no fusionadas:
(use "git add <file>..." para marcar la resolución)

ambos modificados: índice.html

no se agregaron cambios para confirmar (use "git add" y/o "git commit -a")

Todo lo que tenga conflictos de fusión y no se haya resuelto se muestra como no fusionado. Git agrega marcadores
estándar de resolución de conflictos a los archivos que tienen conflictos, para que pueda abrirlos manualmente y
resolver esos conflictos. Su archivo contiene una sección que se parece a esto:

<<<<<<< HEAD:index.html
<div id="footer">contacto: email.support@github.com</div>
=======
<div id="footer">
contáctenos en support@github.com </div> >>>>>>>
iss53:index.html

Esto significa que la versión en HEAD (su rama maestra , porque eso era lo que había verificado cuando ejecutó
su comando de combinación) es la parte superior de ese bloque (todo lo que está arriba de =======), mientras
que la versión en su rama iss53 se ve como todo en la parte inferior. Para resolver el conflicto, debe elegir un lado
o el otro o fusionar los contenidos usted mismo. Por ejemplo, puede resolver este conflicto reemplazando el bloque
completo con esto:

<div id="pie de página">


contáctenos en email.support@github.com </div>

Esta resolución tiene un poco de cada sección, y las líneas <<<<<<<, ======= y >>>>>>> se han eliminado por
completo. Una vez que haya resuelto cada una de estas secciones en cada archivo en conflicto, ejecute git add

77
Machine Translated by Google

en cada archivo para marcarlo como resuelto. La preparación del archivo lo marca como resuelto en Git.

Si desea utilizar una herramienta gráfica para resolver estos problemas, puede ejecutar git mergetool, que activa una herramienta de
combinación visual adecuada y lo guía a través de los conflictos:

$ git herramienta de combinación

Este mensaje se muestra porque 'merge.tool' no está configurado.


Consulte 'git mergetool --tool-help' o 'git help config' para obtener más detalles. 'git mergetool'
ahora intentará usar una de las siguientes herramientas: opendiff kdiff3 tkdiff xxdiff meld
tortoisemerge gvimdiff difuso diffmerge ecmerge p4merge araxis bc3 codecompare vimdiff emerge Merging:
index.html

Conflicto de combinación normal para 'index.html':


{local}: archivo modificado {remoto}: archivo
modificado Presione regresar para iniciar la
herramienta de resolución de combinación (opendiff):

Si desea utilizar una herramienta de combinación que no sea la predeterminada (Git eligió opendiff en este caso porque el comando se
ejecutó en una Mac), puede ver todas las herramientas compatibles enumeradas en la parte superior después de "una de las siguientes
herramientas". Simplemente escriba el nombre de la herramienta que prefiere usar.

ÿ Si necesita herramientas más avanzadas para resolver conflictos de combinación complicados, cubrimos más
sobre la combinación en Combinación avanzada.

Después de salir de la herramienta de fusión, Git le pregunta si la fusión fue exitosa. Si le dice a la secuencia de comandos que lo fue,
prepara el archivo para marcarlo como resuelto para usted. Puede ejecutar git status nuevamente para verificar que todos los conflictos
se hayan resuelto:

$ git estado
En maestro de rama
Se solucionaron todos los conflictos, pero aún se está
fusionando. (use "git commit" para concluir la fusión)

Cambios a realizar:

modificado: index.html

Si está satisfecho con eso y verifica que todo lo que tenía conflictos se ha preparado, puede escribir git commit para finalizar la
confirmación de fusión. El mensaje de confirmación por defecto se parece a esto:

78
Machine Translated by Google

Fusionar rama 'iss53'

Conflictos:
index.html #

# Parece que puede estar cometiendo una fusión.


# Si esto no es correcto, elimine el archivo # .git/
MERGE_HEAD # y vuelva a intentarlo.

# Ingrese el mensaje de confirmación para sus cambios. Las líneas que comiencen
# con '#' se ignorarán y un mensaje vacío anulará la confirmación.
# En el maestro de la
rama # Todos los conflictos se solucionaron pero aún se
está fusionando. # # Cambios a confirmar: # modificado:
index.html

Si cree que sería útil para otras personas que analicen esta combinación en el futuro, puede modificar
este mensaje de confirmación con detalles sobre cómo resolvió la combinación y explicar por qué realizó
los cambios que realizó si estos no son obvios.

Gestión de Sucursales
Ahora que creó, fusionó y eliminó algunas sucursales, veamos algunas herramientas de administración
de sucursales que serán útiles cuando comience a usar sucursales todo el tiempo.

El comando git branch hace más que solo crear y eliminar ramas. Si lo ejecuta sin argumentos, obtiene
una lista simple de sus ramas actuales:

$ git rama
iss53
* prueba

maestra

Fíjese en el carácter * que antecede a la rama principal : indica la rama que ha retirado actualmente (es
decir, la rama a la que apunta HEAD ). Esto significa que si se compromete en este punto, la rama
maestra avanzará con su nuevo trabajo. Para ver la última confirmación en cada rama, puedes ejecutar
git branch -v:

$ git branch -v
iss53 93b412c Arreglar el problema de
*
javascript master 7a98805 Merge branch
'iss53' testing 782fd34 Agregar scott a la lista de autores en el archivo Léame

79
Machine Translated by Google

Las útiles opciones --merged y --no-merged pueden filtrar esta lista a las ramas que tiene o que aún no ha
fusionado con la rama en la que se encuentra actualmente. Para ver qué ramas ya están fusionadas con la
rama en la que te encuentras, puedes ejecutar git branch --merged:

$ git branch --merged


iss53
* Maestro

Debido a que ya se fusionó en iss53 anteriormente, lo verá en su lista. Las ramas en esta lista sin el * delante
de ellas generalmente se pueden eliminar con git branch -d; ya has incorporado su trabajo a otra rama, así que
no vas a perder nada.

Para ver todas las ramas que contienen trabajo que aún no has fusionado, puedes ejecutar git branch --no
-merged:

$ rama git --pruebas no


fusionadas

Esto muestra tu otra rama. Debido a que contiene trabajo que aún no se fusionó, intentar eliminarlo con git
branch -d fallará:

$ git branch -d testing


error: La rama 'testing' no está completamente fusionada.
Si está seguro de que desea eliminarlo, ejecute 'git branch -D testing'.

Si realmente desea eliminar la rama y perder ese trabajo, puede forzarlo con -D, como señala el útil mensaje.

Las opciones descritas anteriormente, --merged y --no-merged , si no se les da un nombre de


confirmación o rama como argumento, le mostrarán lo que está, respectivamente, fusionado
o no fusionado en su rama actual .

Siempre puede proporcionar un argumento adicional para preguntar sobre el estado de fusión
con respecto a alguna otra rama sin verificar primero esa otra rama, como en, ¿qué no se
ÿ fusionó en la rama principal ?

$ git checkout testing $


git branch --no-merged master
topicA featureB

Cambiar el nombre de una sucursal

80
Machine Translated by Google

No cambie el nombre de las ramas que todavía están en uso por otros colaboradores. No cambie el nombre de

ÿ una rama como maestra/principal/principal sin haber leído la sección "Cambiar el nombre de la rama maestra".

Supongamos que tiene una rama que se llama bad-branch-name y desea cambiarla a un nombre de rama corregido, manteniendo
todo el historial. También desea cambiar el nombre de la sucursal en el control remoto (GitHub, GitLab, otro servidor). ¿Cómo haces
esto?

Cambie el nombre de la rama localmente con el comando git branch --move :

$ git branch --move bad-branch-name corregido-branch-name

Esto reemplaza su nombre de sucursal incorrecto con el nombre de sucursal corregido, pero este cambio es solo local por ahora. Para
permitir que otros vean la rama corregida en el control remoto, presione:

$ git push --set-upstream origin nombre-de-sucursal corregido

Ahora vamos a echar un breve vistazo a dónde estamos ahora:

$ git branch --all *


corregido-branch-name
main
mandos a distancia/origen/nombre-de-sucursal
incorrecto mandos a distancia/origen/nombre-de-sucursal
corregido mandos a distancia/origen/principal

Observe que está en la rama nombre-sucursal-corregido y está disponible en el control remoto. Sin embargo, la rama con el nombre
incorrecto también está presente allí, pero puede eliminarla ejecutando el siguiente comando:

$ git push origin --delete bad-branch-name

Ahora el nombre de la rama incorrecta se reemplaza por completo con el nombre de la rama corregido.

Cambiar el nombre de la rama maestra

Cambiar el nombre de una rama como master/main/mainline/default interrumpirá las integraciones, los servicios,
las utilidades auxiliares y los scripts de compilación/versión que utiliza su repositorio. Antes de hacer esto,

ÿ asegúrese de consultar con sus colaboradores. Además, asegúrese de realizar una búsqueda exhaustiva a
través de su repositorio y actualice cualquier referencia al nombre de la rama anterior en su código y scripts.

Cambie el nombre de su rama maestra local a principal con el siguiente comando:

81
Machine Translated by Google

$ rama git --mover maestro principal

Ya no hay una rama maestra local , porque se le cambió el nombre a la rama principal .

Para permitir que otros vean la nueva rama principal , debe empujarla hacia el control remoto. Esto hace que la rama renombrada esté disponible
en el control remoto.

$ git push --set-upstream origen principal

Ahora terminamos con el siguiente estado:

rama git --todos los *


mandos a distancia

principales/origen/HEAD -> origen/mandos a distancia


maestros/origen/mandos a distancia principales/origen/
maestro

Su rama maestra local se ha ido, ya que se reemplazó con la rama principal . La rama principal está presente en el control remoto. Sin embargo,

la rama maestra anterior todavía está presente en el control remoto. Otros colaboradores seguirán usando la rama principal como base de su

trabajo, hasta que realices algunos cambios adicionales.

Ahora tiene algunas tareas más por delante para completar la transición:

• Cualquier proyecto que dependa de este necesitará actualizar su código y/o configuración.

• Actualice los archivos de configuración del corredor de pruebas.

• Ajuste los scripts de compilación y lanzamiento.

• Configuración de redirección en su host de repositorio para cosas como la rama predeterminada del repositorio, reglas de combinación y

otras cosas que coincidan con los nombres de las sucursales.

• Actualizar las referencias a la sucursal antigua en la documentación.

• Cierre o fusione cualquier solicitud de incorporación de cambios dirigida a la rama anterior.

Una vez que haya realizado todas estas tareas y esté seguro de que la rama principal funciona igual que la rama principal , puede eliminar la rama

principal :

$ git push origen --eliminar maestro

Flujos de trabajo ramificados


Ahora que tiene los conceptos básicos de bifurcación y fusión, ¿qué puede o debe hacer con ellos? En esta sección, cubriremos algunos flujos de

trabajo comunes que esta bifurcación liviana hace posible, para que pueda decidir si desea incorporarlos en su propio ciclo de desarrollo.

82
Machine Translated by Google

Sucursales de larga duración

Debido a que Git usa una combinación simple de tres vías, la combinación de una rama con otra varias veces durante un
período prolongado generalmente es fácil de hacer. Esto significa que puede tener varias sucursales que siempre están
abiertas y que usa para diferentes etapas de su ciclo de desarrollo; puede fusionar regularmente de algunos de ellos a otros.

Muchos desarrolladores de Git tienen un flujo de trabajo que adopta este enfoque, como tener solo código que sea
completamente estable en su rama maestra , posiblemente solo código que se haya lanzado o se lanzará. Tienen otra rama
paralela llamada desarrollar o siguiente desde la que trabajan o usan para probar la estabilidad; no siempre es necesariamente
estable, pero cada vez que llega a un estado estable, se puede fusionar en maestro.
Se usa para extraer ramas temáticas (ramas de corta duración, como la rama iss53 anterior ) cuando están listas, para
asegurarse de que pasen todas las pruebas y no introduzcan errores.

En realidad, estamos hablando de punteros que se mueven hacia arriba en la línea de confirmaciones que está realizando.
Las ramas estables están más abajo en la línea de su historial de confirmaciones, y las ramas de última generación están
más arriba en el historial.

Figura 26. Una vista lineal de la ramificación de estabilidad progresiva

En general, es más fácil pensar en ellos como silos de trabajo, donde los conjuntos de confirmaciones se gradúan en un silo
más estable cuando se prueban por completo.

Figura 27. Una vista de "silo" de ramificación de estabilidad progresiva

Puede seguir haciendo esto para varios niveles de estabilidad. Algunos proyectos más grandes también tienen una rama
propuesta o pu (actualizaciones propuestas) que tiene ramas integradas que pueden no estar listas para pasar a la rama
siguiente o principal . La idea es que tus ramas estén en varios niveles de estabilidad; cuando alcanzan un nivel más estable,
se fusionan con la rama por encima de ellos. Una vez más, no es necesario tener varias sucursales de ejecución prolongada,
pero a menudo es útil, especialmente cuando se trata de proyectos muy grandes o complejos.

83
Machine Translated by Google

Ramas temáticas
Sin embargo, las ramas temáticas son útiles en proyectos de cualquier tamaño. Una rama de tema es una
rama de corta duración que crea y usa para una sola característica particular o trabajo relacionado. Esto es
algo que probablemente nunca antes haya hecho con un VCS porque generalmente es demasiado costoso
crear y fusionar sucursales. Pero en Git es común crear, trabajar, fusionar y eliminar ramas varias veces al día.

Viste esto en la última sección con las ramas iss53 y hotfix que creaste. Hizo algunas confirmaciones en
ellos y los eliminó directamente después de fusionarlos en su rama principal. Esta técnica le permite cambiar
de contexto rápida y completamente, ya que su trabajo está separado en silos donde todos los cambios en
esa rama tienen que ver con ese tema, es más fácil ver lo que sucedió durante la revisión del código y
demás. Puede mantener los cambios allí durante minutos, días o meses, y fusionarlos cuando estén listos,
independientemente del orden en que se crearon o trabajaron.

Considere un ejemplo de hacer algo de trabajo (en maestro), bifurcarse para un problema (iss91), trabajar
en él un poco, bifurcarse en la segunda rama para probar otra forma de manejar lo mismo ( iss91v2), volver
a su master branch y trabajar allí por un tiempo, y luego bifurcarse allí para hacer un trabajo que no está
seguro de que sea una buena idea (sucursal dumbidea ). Tu historial de confirmaciones se verá así:

Figura 28. Múltiples ramas temáticas

84
Machine Translated by Google

Ahora, supongamos que decide que le gusta más la segunda solución a su problema (iss91v2); y mostraste
la rama dumbidea a tus compañeros de trabajo, y resulta ser genial. Puede desechar la rama iss91 original
(perdiendo las confirmaciones C5 y C6) y fusionar las otras dos. Su historial entonces se ve así:

Figura 29. Historial después de fusionar dumbidea e iss91v2

Entraremos en más detalles sobre los diversos flujos de trabajo posibles para su proyecto Git en Git
distribuido, así que antes de decidir qué esquema de bifurcación usará su próximo proyecto, asegúrese de
leer ese capítulo.

Es importante recordar cuando esté haciendo todo esto que estas sucursales son completamente locales.
Cuando está ramificando y fusionando, todo se hace solo en su repositorio de Git; no hay comunicación con
el servidor.

85
Machine Translated by Google

Sucursales remotas
Las referencias remotas son referencias (punteros) en sus repositorios remotos, incluidas ramas, etiquetas, etc.
Puede obtener una lista completa de referencias remotas explícitamente con git ls-remote <remote> o git remote
show <remote> para sucursales remotas, así como más información. Sin embargo, una forma más común es
aprovechar las sucursales de seguimiento remoto.

Las sucursales de seguimiento remoto son referencias al estado de las sucursales remotas. Son referencias
locales que no puedes mover; Git los mueve por usted cada vez que realiza alguna comunicación de red, para
asegurarse de que representen con precisión el estado del repositorio remoto. Piense en ellos como marcadores,
para recordarle dónde estaban las sucursales en sus repositorios remotos la última vez que se conectó a ellos.

Los nombres de sucursales de seguimiento remoto toman la forma <remote>/<branch>. Por ejemplo, si quisiera
ver cómo se veía la rama principal en su control remoto de origen la última vez que se comunicó con él, verificaría
la rama principal/de origen . Si estaba trabajando en un problema con un socio y este envió una rama iss53 , es
posible que tenga su propia rama iss53 local , pero la rama en el servidor estaría representada por la rama de
seguimiento remoto origin/iss53.

Esto puede ser un poco confuso, así que veamos un ejemplo. Supongamos que tiene un servidor Git en su red en
git.ourcompany.com. Si clona a partir de esto, el comando de clonación de Git le asigna automáticamente el
nombre de origen , extrae todos sus datos, crea un puntero a la ubicación de su rama maestra y lo nombra como
origen/maestro localmente. Git también le brinda su propia rama maestra local que comienza en el mismo lugar
que la rama maestra de origen , por lo que tiene algo desde lo que trabajar.

"origen" no es especial
Al igual que el nombre de la rama "maestro" no tiene ningún significado especial en Git,

ÿ tampoco lo tiene "origen". Mientras que "maestro" es el nombre predeterminado para una rama
de inicio cuando ejecuta git init , que es la única razón por la que se usa ampliamente, "origen"
es el nombre predeterminado para un control remoto cuando ejecuta git clone. Si ejecuta git
clone -o booyah en su lugar, tendrá booyah/master como su rama remota predeterminada.

86
Machine Translated by Google

Figura 30. Servidor y repositorios locales después de la clonación

Si realiza algún trabajo en su rama maestra local y, mientras tanto, alguien más ingresa a git.ourcompany.com
y actualiza su rama principal , entonces sus historiales avanzan de manera diferente.
Además, mientras permanezca fuera de contacto con su servidor de origen , su puntero de origen/maestro no
moverse.

87
Machine Translated by Google

Figura 31. El trabajo local y remoto pueden divergir

Para sincronizar su trabajo con un control remoto determinado, ejecute un comando git fetch <remote>
(en nuestro caso, git fetch origin). Este comando busca el "origen" del servidor (en este caso, es
git.ourcompany.com), obtiene cualquier dato que aún no tenga y actualiza su base de datos local,
moviendo su puntero de origen/maestro a su posición nueva y más actualizada.

88
Machine Translated by Google

Figura 32. git fetch actualiza tus sucursales de seguimiento remoto

Para demostrar que tiene varios servidores remotos y cómo son las sucursales remotas para esos
proyectos remotos, supongamos que tiene otro servidor Git interno que uno de sus equipos de sprint usa
solo para el desarrollo. Este servidor está en git.team1.ourcompany.com. Puede agregarlo como una
nueva referencia remota al proyecto en el que está trabajando actualmente ejecutando el comando git
remote add como vimos en Conceptos básicos de Git. Nombre este equipo remoto , que será su nombre
abreviado para toda esa URL.

89
Machine Translated by Google

Figura 33. Adición de otro servidor como remoto

Ahora, puede ejecutar git fetch teamone para obtener todo lo que el servidor remoto de teamone tiene y que usted aún no
tiene. Debido a que ese servidor tiene un subconjunto de los datos que tiene su servidor de origen en este momento, Git no
obtiene datos pero establece una rama de seguimiento remoto llamada teamone/master para apuntar a la confirmación que
teamone tiene como su rama principal .

90
Machine Translated by Google

Figura 34. Rama de seguimiento remoto para teamone/master

Emprendedor

Cuando desee compartir una sucursal con el mundo, debe enviarla a un control remoto al que tenga acceso
de escritura. Sus sucursales locales no se sincronizan automáticamente con los controles remotos a los que
escribe; debe enviar explícitamente las sucursales que desea compartir. De esa forma, puede usar ramas
privadas para el trabajo que no desea compartir y subir solo las ramas temáticas en las que desea colaborar.

Si tiene una rama llamada serverfix en la que desea trabajar con otros, puede impulsarla hacia arriba de la
misma manera que impulsó su primera rama. Ejecute git push <remoto> <rama>:

$ git push origin serverfix


Contando objetos: 24, listo.
Compresión delta usando hasta 8 hilos.
Comprimir objetos: 100% (15/15), hecho.
Objetos de escritura: 100 % (24/24), 1,91 KiB | 0 bytes/s, listo.
Total 24 (delta 2), reutilizado 0 (delta 0)
A https://github.com/schacon/simplegit *
[nueva sucursal] arreglo del servidor -> arreglo del servidor

Esto es un poco un atajo. Git automáticamente expande el nombre de la rama serverfix a refs /heads/
serverfix:refs/heads/serverfix, lo que significa, "Toma mi rama local serverfix y empújala para actualizar la
rama serverfix remota". Repasaremos la parte refs/heads/ en detalle en Git

91
Machine Translated by Google

Internos, pero generalmente puede dejarlo fuera. También puede hacer git push origin serverfix:serverfix, que hace lo
mismo: dice: "Tome mi serverfix y conviértalo en el serverfix del control remoto". Puede usar este formato para insertar
una sucursal local en una sucursal remota que tiene un nombre diferente. Si no desea que se llame serverfix en el
control remoto, puede ejecutar git push origin serverfix:awesomebranch para enviar su rama serverfix local a la rama
awesomebranch en el proyecto remoto.

No escribas tu contraseña cada vez


Si está utilizando una URL HTTPS para transferir, el servidor Git le pedirá su nombre de usuario y
contraseña para la autenticación. De manera predeterminada, le solicitará esta información en la
terminal para que el servidor pueda saber si puede presionar.

ÿ Si no desea escribirlo cada vez que presiona, puede configurar un "caché de credenciales". Lo
más simple es mantenerlo en la memoria durante unos minutos, lo que puede configurar fácilmente
ejecutando git config --global credential.helper cache.

Para obtener más información sobre las diversas opciones de almacenamiento en caché de credenciales disponibles, consulte

Almacenamiento de credenciales.

La próxima vez que uno de sus colaboradores obtenga del servidor, obtendrá una referencia de dónde se encuentra
la versión de serverfix del servidor en el origen de la rama remota /serverfix:

$ git fetch origin


remote: Contando objetos: 7, listo. remoto:
Comprimir objetos: 100% (2/2), listo. remoto: Total 3
(delta 0), reutilizado 3 (delta 0)
Desembalaje de objetos: 100% (3/3), hecho.
De https://github.com/schacon/simplegit * [nueva
rama] serverfix -> origen/servidor fijo

Es importante tener en cuenta que cuando realiza una búsqueda que genera nuevas ramas de seguimiento remoto,
no tiene automáticamente copias editables locales de ellas. En otras palabras, en este caso, no tiene una nueva rama
serverfix , solo tiene un puntero origin/serverfix que no puede modificar.

Para fusionar este trabajo en su rama de trabajo actual, puede ejecutar git merge origin/serverfix. Si desea su propia
rama serverfix en la que pueda trabajar, puede basarla en su rama de seguimiento remoto:

$ git checkout -b serverfix origin/serverfix Branch


serverfix configurado para rastrear branch serverfix remoto desde el origen.
Cambiado a una nueva rama 'serverfix'

Esto le brinda una sucursal local en la que puede trabajar que comienza donde está origin/serverfix .

Rastreo de sucursales

Verificar una sucursal local desde una sucursal de seguimiento remoto crea automáticamente lo que se denomina una

92
Machine Translated by Google

"rama de seguimiento" (y la rama que rastrea se llama "rama ascendente"). Las sucursales de seguimiento son
sucursales locales que tienen una relación directa con una sucursal remota. Si está en una rama de seguimiento y
escribe git pull, Git sabe automáticamente de qué servidor buscar y en qué rama fusionarse.

Cuando clona un repositorio, generalmente crea automáticamente una rama maestra que rastrea origen/maestro. Sin
embargo, puede configurar otras sucursales de seguimiento si lo desea, que rastreen las sucursales en otros controles
remotos o que no rastreen la sucursal principal . El caso simple es el ejemplo que acaba de ver, ejecutando git
checkout -b <branch> <remote>/<branch>. Esta es una operación bastante común que Git proporciona la abreviatura
--track :

$ git checkout --track origin/serverfix Branch


serverfix configurado para rastrear branch serverfix remoto desde el origen.
Cambiado a una nueva rama 'serverfix'

De hecho, esto es tan común que incluso hay un atajo para ese atajo. Si el nombre de la sucursal que está tratando
de pagar (a) no existe y (b) coincide exactamente con un nombre en un solo control remoto, Git creará una sucursal
de seguimiento para usted:

$ git checkout serverfix


Branch serverfix configurado para rastrear branch serverfix remoto desde el origen.
Cambiado a una nueva rama 'serverfix'

Para configurar una sucursal local con un nombre diferente al de la sucursal remota, puede usar fácilmente la primera
versión con un nombre de sucursal local diferente:

$ git checkout -b sf origin/serverfix Branch


sf configurada para rastrear serverfix de sucursal remota desde el origen.
Cambiado a una nueva rama 'sf'

Ahora, su sucursal local sf se extraerá automáticamente de origin/serverfix.

Si ya tiene una sucursal local y desea establecerla en una sucursal remota que acaba de bajar, o si desea cambiar la
sucursal ascendente que está rastreando, puede usar la opción -u o --set-upstream-to para git branch para configurarlo
explícitamente en cualquier momento.

$ git branch -u origin/serverfix Branch


serverfix configurado para rastrear branch serverfix remoto desde el origen.

Taquigrafía aguas arriba

Cuando tiene configurada una rama de seguimiento, puede hacer referencia a su rama ascendente
ÿ con la abreviatura @{upstream} o @{u} . Entonces, si estás en la rama maestra y está rastreando
el origen/maestro, puedes decir algo como git merge @{u} en lugar de git merge origin/master si
lo deseas.

93
Machine Translated by Google

Si desea ver qué ramas de seguimiento ha configurado, puede usar la opción -vv para git branch.
Esto mostrará una lista de sus sucursales locales con más información, incluido el seguimiento de cada sucursal y si
su sucursal local está adelante, atrás o ambos.

$ git branch -vv


iss53 7e424c3[origen/iss53: adelante 2] Agregar corchetes olvidados
serverfix f8674d9
1ae2a45
[teamone/server-fix-good:
[origen/maestro] Implementar
adelante index
3, detrás
fix master
de 1] Esto
*
debería hacerlo probando 5ea463a Probar algo nuevo

Entonces, aquí podemos ver que nuestra rama iss53 está rastreando origin/iss53 y está "adelantada" por dos, lo que
significa que tenemos dos confirmaciones localmente que no se envían al servidor. También podemos ver que nuestra
rama maestra está rastreando el origen/maestro y está actualizada. A continuación, podemos ver que nuestra rama
serverfix está rastreando la rama server-fix-good en nuestro servidor teamone y está tres por delante y uno por detrás,
lo que significa que hay una confirmación en el servidor en el que aún no nos hemos fusionado y tres confirmaciones
localmente que no hemos empujado. Finalmente, podemos ver que nuestra rama de prueba no está rastreando
ninguna rama remota.

Es importante tener en cuenta que estos números son solo desde la última vez que los obtuvo de cada servidor.
Este comando no llega a los servidores, le informa sobre lo que ha almacenado en caché de estos servidores
localmente. Si desea obtener números totalmente actualizados por delante y por detrás, deberá obtenerlos de todos
sus controles remotos justo antes de ejecutar esto. Podrías hacerlo así:

$ git buscar --todos; rama git -vv

Tracción

Si bien el comando git fetch obtendrá todos los cambios en el servidor que aún no tiene, no modificará su directorio
de trabajo en absoluto. Simplemente obtendrá los datos por usted y le permitirá fusionarlos usted mismo. Sin embargo,
hay un comando llamado git pull que es esencialmente un git fetch seguido inmediatamente por un git merge en la
mayoría de los casos. Si tiene una rama de seguimiento configurada como se muestra en la última sección, ya sea al
configurarla explícitamente o al crearla para usted mediante los comandos de clonación o pago , git pull buscará qué
servidor y rama está rastreando su rama actual, buscar desde ese servidor y luego intente fusionarse en esa rama
remota.

En general, es mejor simplemente usar los comandos fetch y merge explícitamente, ya que la magia de git pull a
menudo puede ser confusa.

Eliminación de sucursales remotas

Supongamos que ha terminado con una rama remota; digamos que usted y sus colaboradores han terminado con
una función y la han fusionado con la rama maestra de su control remoto (o cualquier rama en la que se encuentre
su línea de código estable). Puede eliminar una rama remota usando la opción --delete para git push. Si desea
eliminar su rama serverfix del servidor, ejecute lo siguiente:

94
Machine Translated by Google

$ git push origin --delete serverfix Para


https://github.com/schacon/simplegit -
[eliminado] arreglo del servidor

Básicamente, todo lo que hace es eliminar el puntero del servidor. El servidor Git generalmente mantendrá los datos allí
por un tiempo hasta que se ejecute una recolección de basura, por lo que si se eliminó accidentalmente, a menudo es fácil
para recuperar.

rebase
En Git, hay dos formas principales de integrar cambios de una rama a otra: la fusión y la reorganización. En esta sección,
aprenderá qué es el rebase, cómo hacerlo, por qué es una herramienta bastante sorprendente y en qué casos no querrá
usarla.

La rebase básica
Si regresa a un ejemplo anterior de Combinación básica, puede ver que divergió su trabajo y realizó confirmaciones en
dos ramas diferentes.

Figura 35. Historia divergente simple

La forma más fácil de integrar las ramas, como ya hemos visto, es el comando fusionar . Realiza una combinación de
tres vías entre las dos instantáneas de rama más recientes (C3 y C4) y el ancestro común más reciente de las dos (C2),
creando una nueva instantánea (y compromiso).

95
Machine Translated by Google

Figura 36. Fusión para integrar historial de trabajo divergente

Sin embargo, hay otra forma: puede tomar el parche del cambio que se introdujo en C4 y volver a aplicarlo
encima de C3. En Git, esto se llama rebase. Con el comando rebase , puede tomar todos los cambios que
se confirmaron en una rama y reproducirlos en una rama diferente.

Para este ejemplo, verificaría la rama del experimento y luego la reorganizaría en la rama maestra de la
siguiente manera:

$ git checkout experiment $ git


rebase master Primero, rebobinando
el cabezal para reproducir tu trabajo encima...
Aplicando: comando por etapas agregado

Esta operación funciona yendo al ancestro común de las dos ramas (en la que está y en la que está
rebasando), obteniendo la diferencia introducida por cada confirmación de la rama en la que está, guardando
esas diferencias en temporal archivos, restableciendo la rama actual a la misma confirmación que la rama
en la que se está reorganizando y, finalmente, aplicando cada cambio a su vez.

Figura 37. Rebasando el cambio introducido en C4 a C3

En este punto, puede volver a la rama maestra y realizar una fusión de avance rápido.

$ git checkout master $ git


merge experimento

96
Machine Translated by Google

Figura 38. Avance rápido de la rama principal

Ahora, la instantánea a la que apunta C4' es exactamente la misma que apuntó C5 en el ejemplo de fusión. No
hay diferencia en el producto final de la integración, pero la reorganización hace que la historia sea más limpia.
Si examina el registro de una rama reorganizada, parece una historia lineal: parece que todo el trabajo sucedió
en serie, incluso cuando originalmente sucedió en paralelo.

A menudo, hará esto para asegurarse de que sus compromisos se apliquen correctamente en una rama remota,
tal vez en un proyecto en el que intenta contribuir pero que no mantiene. En este caso, haría su trabajo en una
rama y luego volvería a basar su trabajo en origin/master cuando estuviera listo para enviar sus parches al
proyecto principal. De esa manera, el mantenedor no tiene que hacer ningún trabajo de integración, solo un
avance rápido o una aplicación limpia.

Tenga en cuenta que la instantánea a la que apunta la confirmación final con la que termina, ya sea la última de
las confirmaciones reorganizadas para una reorganización o la confirmación de fusión final después de una
fusión, es la misma instantánea: solo el historial es diferente. La reorganización reproduce los cambios de una
línea de trabajo a otra en el orden en que se introdujeron, mientras que la fusión toma los puntos finales y los fusiona.

Rebases más interesantes


También puede hacer que su rebase se reproduzca en algo que no sea la rama de destino de rebase. Tome un
historial como Un historial con una rama temática de otra rama temática, por ejemplo. Bifurcó una rama de tema
(servidor) para agregar alguna funcionalidad del lado del servidor a su proyecto y realizó una confirmación.
Luego, se bifurcó para hacer los cambios en el lado del cliente (cliente) y se comprometió varias veces.
Finalmente, regresó a la rama de su servidor e hizo algunas confirmaciones más.

97
Machine Translated by Google

Figura 39. Una historia con una rama de tema de otra rama de tema

Supongamos que decide que desea fusionar los cambios del lado del cliente en su línea principal para un
lanzamiento, pero desea posponer los cambios del lado del servidor hasta que se pruebe más. Puede tomar los
cambios en el cliente que no está en el servidor (C8 y C9) y reproducirlos en su rama maestra usando la opción --
onto de git rebase:

$ git rebase --en el cliente del servidor maestro

Esto básicamente dice: "Tome la rama del cliente , descubra los parches, ya que se separó de la rama del servidor ,
y reproduzca estos parches en la rama del cliente como si estuviera basado directamente en la rama maestra ".
Es un poco complejo, pero el resultado es bastante bueno.

Figura 40. Cambio de base de una rama de tema a partir de otra rama de tema

Ahora puede avanzar rápidamente su rama principal (consulte Avance rápido de su rama principal para incluir los
cambios en la rama del cliente):

98
Machine Translated by Google

$ git checkout maestro


$ git merge cliente

Figura 41. Avance rápido de su rama principal para incluir los cambios en la rama del cliente

Supongamos que decide extraer también la rama de su servidor. Puede volver a establecer la base de la rama del
servidor en la rama maestra sin tener que verificarla primero ejecutando git rebase <basebranch> <topicbranch> , que
verifica la rama del tema (en este caso, el servidor) por usted y la reproduce en la rama base (Maestro):

$ git rebase servidor maestro

Esto reproduce el trabajo de su servidor sobre su trabajo maestro , como se muestra en Reorganización de la rama de
su servidor sobre su rama maestra .

Figura 42. Reorganización de la rama de su servidor sobre su rama maestra

Luego, puede avanzar rápidamente la rama base (maestra):

$ git pago maestro $


git merge servidor

Puede eliminar las ramas del cliente y del servidor porque todo el trabajo está integrado y ya no las necesita, dejando
su historial para todo este proceso con el aspecto del historial de confirmación final:

$ git sucursal -d cliente


$ git sucursal -d servidor

99
Machine Translated by Google

Figura 43. Historial de confirmación final

Los peligros del rebase


Ahh, pero la dicha de cambiar la base no está exenta de inconvenientes, que se pueden resumir en una sola línea:

No rebase las confirmaciones que existen fuera de su repositorio y en las que las personas pueden haber
basado el trabajo.

Si sigues esa pauta, estarás bien. Si no lo haces, la gente te odiará y tus amigos y familiares te despreciarán.

Cuando reorganizas cosas, estás abandonando compromisos existentes y creando otros nuevos que son similares
pero diferentes. Si empuja confirmaciones en algún lugar y otros las bajan y basan su trabajo en ellas, y luego
vuelve a escribir esas confirmaciones con git rebase y las vuelve a subir, sus colaboradores tendrán que volver a
fusionar su trabajo y las cosas se complicarán cuando intente tire de su trabajo de nuevo en el suyo.

Veamos un ejemplo de cómo el trabajo de rebase que ha hecho público puede causar problemas.
Suponga que clona desde un servidor central y luego trabaja con eso. Su historial de confirmación se ve así:

Figura 44. Clonar un repositorio y basar algo de trabajo en él

Ahora, otra persona hace más trabajo que incluye una combinación y envía ese trabajo al servidor central. Lo busca
y fusiona la nueva rama remota en su trabajo, haciendo que su historial se vea así:

100
Machine Translated by Google

Figura 45. Obtenga más confirmaciones y combínelas con su trabajo

A continuación, la persona que impulsó el trabajo fusionado decide volver atrás y cambiar la base de su
trabajo; hacen un git push --force para sobrescribir el historial en el servidor. Luego obtienes de ese servidor,
lo que reduce las nuevas confirmaciones.

Figura 46. Alguien empuja confirmaciones reorganizadas, abandonando confirmaciones en las que ha basado su trabajo

101
Machine Translated by Google

Ahora ambos están en un lío. Si haces un git pull, crearás una confirmación de combinación que incluye ambas líneas del
historial, y tu repositorio se verá así:

Figura 47. Te fusionas en el mismo trabajo nuevamente en una nueva confirmación de fusión

Si ejecuta un registro de git cuando su historial se ve así, verá dos confirmaciones que tienen el mismo autor, fecha y mensaje,
lo que será confuso. Además, si vuelve a enviar este historial al servidor, volverá a introducir todas esas confirmaciones
reorganizadas en el servidor central, lo que puede confundir aún más a las personas. Es bastante seguro asumir que el otro
desarrollador no quiere que C4 y C6 estén en la historia; es por eso que cambiaron de base en primer lugar.

Rebase cuando rebase


Si te encuentras en una situación como esta, Git tiene más magia que podría ayudarte. Si alguien en la fuerza de su equipo
impulsa cambios que sobrescriben el trabajo en el que usted se ha basado, su desafío es averiguar cuál es el suyo y qué han
reescrito.

Resulta que, además de la suma de verificación SHA-1 de confirmación, Git también calcula una suma de verificación que se
basa solo en el parche introducido con la confirmación. Esto se llama "parche-id".

Si extrae el trabajo que se reescribió y lo vuelve a basar sobre las nuevas confirmaciones de su socio, Git a menudo puede
descubrir con éxito qué es exclusivamente suyo y aplicarlo nuevamente sobre la nueva rama.

Por ejemplo, en el escenario anterior, si en lugar de hacer una fusión cuando estamos en Alguien empuja confirmaciones
reorganizadas, abandonando las confirmaciones en las que basó su trabajo , ejecutamos git rebase teamone/master, Git:

• Determinar qué trabajo es exclusivo de nuestra sucursal (C2, C3, C4, C6, C7)

• Determinar cuáles no son confirmaciones de fusión (C2, C3, C4)

• Determine cuáles no se han reescrito en la rama de destino (solo C2 y C3, ya que C4 es la


mismo parche que C4')

102
Machine Translated by Google

• Aplicar esos compromisos en la parte superior de teamone/master

Entonces, en lugar del resultado que vemos en Usted fusiona el mismo trabajo nuevamente en una nueva confirmación de fusión,
terminaríamos con algo más como Rebase sobre el trabajo de rebase forzado.

Figura 48. Rebase encima del trabajo de rebase forzado

Esto solo funciona si C4 y C4' que hizo su socio son casi exactamente el mismo parche. De lo contrario, la rebase no podrá decir que
es un duplicado y agregará otro parche similar a C4 (que probablemente no se aplicará limpiamente, ya que los cambios ya estarían
al menos un poco allí).

También puede simplificar esto ejecutando un git pull --rebase en lugar de un git pull normal . O puede hacerlo manualmente con un
git fetch seguido de un git rebase teamone/master en este caso.

Si está utilizando git pull y desea que --rebase sea el valor predeterminado, puede establecer el valor de configuración de pull.rebase
con algo como git config --global pull.rebase true.

Si solo vuelve a basar las confirmaciones que nunca han salido de su propia computadora, estará bien. Si vuelve a basar las
confirmaciones que se han enviado, pero de las que nadie más ha basado las confirmaciones, también estará bien. Si modifica la
base de confirmaciones que ya se han publicado públicamente, y es posible que la gente haya basado su trabajo en esas
confirmaciones, es posible que tenga algunos problemas frustrantes y el desprecio de su
compañeros de equipo

Si usted o un socio lo encuentran necesario en algún momento, asegúrese de que todos sepan ejecutar git pull --rebase para tratar de
hacer que el dolor después de que suceda sea un poco más simple.

Reorganización frente a fusión

Ahora que ha visto el cambio de base y la fusión en acción, es posible que se pregunte cuál es mejor.
Antes de que podamos responder esto, retrocedamos un poco y hablemos sobre lo que significa la historia.

Un punto de vista sobre esto es que el historial de confirmaciones de su repositorio es un registro de lo que realmente sucedió. Es
un documento histórico, valioso por derecho propio, y no debe ser alterado.

103
Machine Translated by Google

Desde este ángulo, cambiar el historial de confirmaciones es casi una blasfemia; estás mintiendo sobre lo que realmente
sucedió. Entonces, ¿qué pasa si hubo una serie desordenada de confirmaciones de fusión? Así fue como sucedió, y el
repositorio debería preservarlo para la posteridad.

El punto de vista opuesto es que el historial de confirmaciones es la historia de cómo se hizo tu proyecto.
No publicarías el primer borrador de un libro, así que ¿por qué mostrar tu desordenado trabajo? Cuando esté trabajando
en un proyecto, es posible que necesite un registro de todos sus pasos en falso y caminos sin salida, pero cuando
llegue el momento de mostrar su trabajo al mundo, es posible que desee contar una historia más coherente de cómo
obtener de A a B. Las personas en este campamento usan herramientas como rebase y filter-branch para reescribir
sus confirmaciones antes de que se fusionen en la rama principal. Usan herramientas como rebase y filter-branch para
contar la historia de la mejor manera para los futuros lectores.

Ahora, a la pregunta de si es mejor fusionar o reorganizar: espero que veas que no es tan simple. Git es una herramienta
poderosa y te permite hacer muchas cosas con tu historial, pero cada equipo y cada proyecto es diferente. Ahora que
sabe cómo funcionan ambas cosas, depende de usted decidir cuál es mejor para su situación particular.

Puede obtener lo mejor de ambos mundos: rebase los cambios locales antes de presionar para limpiar su trabajo, pero
nunca rebase nada que haya empujado en alguna parte.

Resumen
Hemos cubierto ramificaciones y fusiones básicas en Git. Debería sentirse cómodo creando y cambiando a nuevas
sucursales, cambiando entre sucursales y fusionando sucursales locales. También debería poder compartir sus
sucursales empujándolas a un servidor compartido, trabajando con otros en sucursales compartidas y reorganizando
sus sucursales antes de que se compartan. A continuación, cubriremos lo que necesitará para ejecutar su propio
servidor de alojamiento de repositorio Git.

104
Machine Translated by Google

Git en el servidor
En este punto, debería poder realizar la mayoría de las tareas diarias para las que usará Git.
Sin embargo, para poder colaborar en Git, deberá tener un repositorio de Git remoto.
Aunque técnicamente puede enviar y extraer cambios de los repositorios de las personas, no se recomienda hacerlo
porque puede confundir con bastante facilidad en qué están trabajando si no tiene cuidado. Además, desea que sus
colaboradores puedan acceder al repositorio incluso si su computadora está fuera de línea; a menudo es útil tener un
repositorio común más confiable. Por lo tanto, el método preferido para colaborar con alguien es configurar un repositorio
intermedio al que ambos tengan acceso, y empujar y extraer de él.

Ejecutar un servidor Git es bastante sencillo. Primero, elige qué protocolos desea que admita su servidor. La primera
sección de este capítulo cubrirá los protocolos disponibles y las ventajas y desventajas de cada uno. Las siguientes
secciones explicarán algunas configuraciones típicas que usan esos protocolos y cómo hacer que su servidor funcione
con ellos. Por último, repasaremos algunas opciones alojadas, si no le importa alojar su código en el servidor de otra
persona y no quiere pasar por la molestia de configurar y mantener su propio servidor.

Si no tiene interés en ejecutar su propio servidor, puede saltar a la última sección del capítulo para ver algunas opciones
para configurar una cuenta alojada y luego pasar al siguiente capítulo, donde analizamos los diversos entresijos del
trabajo. en un entorno de control de código fuente distribuido.

Un repositorio remoto es generalmente un repositorio simple , un repositorio de Git que no tiene un directorio de trabajo.
Debido a que el repositorio solo se usa como un punto de colaboración, no hay razón para tener una instantánea
desprotegida en el disco; son solo los datos de Git. En los términos más simples, un repositorio simple es el contenido
del directorio .git de su proyecto y nada más.

Los Protocolos
Git puede usar cuatro protocolos distintos para transferir datos: Local, HTTP, Secure Shell (SSH) y Git. Aquí discutiremos
qué son y en qué circunstancias básicas querrías (o no querrías) usarlos.

Protocolo Local
El más básico es el protocolo Local, en el que el repositorio remoto está en otro directorio del mismo host. Esto se usa
a menudo si todos en su equipo tienen acceso a un sistema de archivos compartido como un NFS mount, o en el caso
menos probable de que todos inicien sesión en la misma computadora. Esto último no sería lo ideal, porque todas las
instancias del repositorio de código residirían en la misma computadora, lo que haría mucho más probable una pérdida
catastrófica.

Si tiene un sistema de archivos montado compartido, puede clonar, enviar y extraer de un repositorio local basado en
archivos. Para clonar un repositorio como este, o para agregar uno como remoto a un proyecto existente, use la ruta al
repositorio como URL. Por ejemplo, para clonar un repositorio local, puede ejecutar algo como esto:

$ git clone /srv/git/proyecto.git

105
Machine Translated by Google

O puedes hacer esto:

$ git clonar archivo:///srv/git/project.git

Git funciona de manera ligeramente diferente si especifica explícitamente file:// al comienzo de la URL. Si solo especifica
la ruta, Git intenta usar enlaces duros o copiar directamente los archivos que necesita. Si especifica file://, Git inicia los
procesos que normalmente usa para transferir datos a través de una red, que generalmente es mucho menos eficiente.
La razón principal para especificar el prefijo file:// es si desea una copia limpia del repositorio sin referencias u objetos
extraños, generalmente después de una importación desde otro VCS o algo similar (consulte Git Internals para tareas
de mantenimiento). Usaremos la ruta normal aquí porque hacerlo casi siempre es más rápido.

Para agregar un repositorio local a un proyecto Git existente, puede ejecutar algo como esto:

$ git remoto agregar local_proj /srv/git/project.git

Luego, puede empujar y extraer de ese control remoto a través de su nuevo nombre remoto local_proj como si lo
estuviera haciendo a través de una red.

Los profesionales

Las ventajas de los repositorios basados en archivos son que son simples y utilizan los permisos de archivos existentes
y el acceso a la red. Si ya tiene un sistema de archivos compartido al que tiene acceso todo su equipo, configurar un
repositorio es muy fácil. Coloca la copia del repositorio en algún lugar al que todos tengan acceso compartido y
establece los permisos de lectura/escritura como lo haría con cualquier otro directorio compartido.
Discutiremos cómo exportar una copia de repositorio simple para este propósito en Obtener Git en un servidor.

Esta también es una buena opción para obtener rápidamente el trabajo del repositorio de trabajo de otra persona. Si
usted y un compañero de trabajo están trabajando en el mismo proyecto y quieren que verifique algo, ejecutar un
comando como git pull /home/john/project suele ser más fácil que empujar a un servidor remoto y luego buscarlo. .

Los contras

Las desventajas de este método son que el acceso compartido generalmente es más difícil de configurar y alcanzar
desde múltiples ubicaciones que el acceso a la red básica. Si desea presionar desde su computadora portátil cuando
está en casa, debe montar el disco remoto, lo que puede ser difícil y lento en comparación con el acceso basado en la
red.

Es importante mencionar que esta no es necesariamente la opción más rápida si está utilizando algún tipo de montaje
compartido. Un repositorio local es rápido solo si tiene acceso rápido a los datos. Un repositorio en NFS suele ser más
lento que el repositorio en SSH en el mismo servidor, lo que permite que Git se ejecute en discos locales en cada
sistema.

Finalmente, este protocolo no protege el depósito contra daños accidentales. Todos los usuarios tienen acceso de shell
completo al directorio "remoto", y no hay nada que les impida cambiar o eliminar archivos internos de Git y corromper
el repositorio.

106
Machine Translated by Google

Los protocolos HTTP

Git puede comunicarse a través de HTTP usando dos modos diferentes. Antes de Git 1.6.6, solo había una forma de
hacerlo, que era muy simple y generalmente de solo lectura. En la versión 1.6.6, se introdujo un nuevo protocolo más
inteligente que implicaba que Git podía negociar de manera inteligente la transferencia de datos de una manera similar a
como lo hace a través de SSH. En los últimos años, este nuevo protocolo HTTP se ha vuelto muy popular ya que es más
simple para el usuario y más inteligente en la forma de comunicarse. La versión más nueva a menudo se conoce como el
protocolo Smart HTTP y la forma más antigua como Dumb HTTP. Cubriremos primero el nuevo protocolo Smart HTTP.

HTTP inteligente

Smart HTTP funciona de manera muy similar a los protocolos SSH o Git, pero se ejecuta en puertos HTTPS estándar y
puede usar varios mecanismos de autenticación HTTP, lo que significa que a menudo es más fácil para el usuario que algo
como SSH, ya que puede usar cosas como autenticación de nombre de usuario/contraseña en lugar de tener para
configurar claves SSH.

Probablemente se haya convertido en la forma más popular de usar Git ahora, ya que se puede configurar para servir de
forma anónima como el protocolo git:// y también se puede impulsar con autenticación y encriptación como el protocolo
SSH. En lugar de tener que configurar diferentes URL para estas cosas, ahora puede usar una sola URL para ambos. Si
intenta enviar y el repositorio requiere autenticación (que normalmente debería), el servidor puede solicitar un nombre de
usuario y una contraseña. Lo mismo ocurre con la lectura
acceso.

De hecho, para servicios como GitHub, la URL que usa para ver el repositorio en línea (por ejemplo, https://github.com/
schacon/simplegit) es la misma URL que puede usar para clonar y, si tiene acceso, presionar.

HTTP tonto

Si el servidor no responde con un servicio inteligente Git HTTP, el cliente Git intentará recurrir al protocolo Dumb HTTP
más simple. El protocolo Dumb espera que el repositorio básico de Git se sirva como archivos normales desde el servidor
web. La belleza de Dumb HTTP es la simplicidad de configurarlo.
Básicamente, todo lo que tiene que hacer es colocar un repositorio de Git bajo la raíz de su documento HTTP y configurar
un enlace específico posterior a la actualización , y listo (consulte Git Hooks). En ese momento, cualquier persona que
pueda acceder al servidor web en el que colocó el repositorio también puede clonar su repositorio. Para permitir el acceso
de lectura a su repositorio a través de HTTP, haga algo como esto:

$ cd /var/www/htdocs/
$ git clone --bare /path/to/git_project gitproject.git $ cd
gitproject.git $ mv hooks/post-update.sample hooks/post-
update $ chmod a+x hooks/ post-actualización

Eso es todo. El enlace posterior a la actualización que viene con Git de forma predeterminada ejecuta el comando
adecuado (git update-server-info) para que la obtención y clonación de HTTP funcione correctamente. Este comando se
ejecuta cuando ingresa a este repositorio (quizás a través de SSH); luego, otras personas pueden clonar a través de algo como:

107
Machine Translated by Google

$ git clonar https://example.com/gitproject.git

En este caso particular, estamos usando la ruta /var/www/htdocs que es común para las configuraciones de Apache, pero puede usar
cualquier servidor web estático; simplemente coloque el repositorio básico en su ruta. Los datos de Git se sirven como archivos estáticos
básicos (consulte el capítulo sobre aspectos internos de Git para obtener detalles sobre cómo se sirven exactamente).

Por lo general, elegiría ejecutar un servidor HTTP inteligente de lectura/escritura o simplemente tener los archivos accesibles como de
solo lectura de la manera tonta. Es raro ejecutar una combinación de los dos servicios.

Los profesionales

Nos concentraremos en las ventajas de la versión inteligente del protocolo HTTP.

La sencillez de tener una sola URL para todos los tipos de acceso y hacer que el servidor solicite solo cuando se necesita autenticación
hace que las cosas sean muy fáciles para el usuario final. Poder autenticarse con un nombre de usuario y contraseña también es una
gran ventaja sobre SSH, ya que los usuarios no tienen que generar claves SSH localmente y cargar su clave pública en el servidor
antes de poder interactuar con ella. Para usuarios menos sofisticados, o usuarios en sistemas donde SSH es menos común, esta es
una gran ventaja en la usabilidad. También es un protocolo muy rápido y eficiente, similar al SSH.

También puede servir sus repositorios de solo lectura a través de HTTPS, lo que significa que puede cifrar la transferencia de contenido;
o puede llegar a hacer que los clientes usen certificados SSL firmados específicos.

Otra cosa buena es que HTTP y HTTPS son protocolos de uso tan común que los firewalls corporativos a menudo se configuran para
permitir el tráfico a través de sus puertos.

Los contras

Git sobre HTTPS puede ser un poco más complicado de configurar en comparación con SSH en algunos servidores. Aparte de eso,
hay muy pocas ventajas que otros protocolos tengan sobre Smart HTTP para servir a Git.
contenido.

Si está utilizando HTTP para la inserción autenticada, proporcionar sus credenciales a veces es más complicado que usar claves a
través de SSH. Sin embargo, hay varias herramientas de almacenamiento en caché de credenciales que puede usar, incluido el acceso
a Llaveros en macOS y Credential Manager en Windows, para que esto sea bastante sencillo. Lea Almacenamiento de credenciales
para ver cómo configurar el almacenamiento en caché seguro de contraseñas HTTP en su sistema.

El protocolo SSH

Un protocolo de transporte común para Git cuando el alojamiento propio se realiza a través de SSH. Esto se debe a que el acceso SSH
a los servidores ya está configurado en la mayoría de los lugares, y si no lo está, es fácil hacerlo. SSH también es un protocolo de red
autenticado y, debido a que es ubicuo, generalmente es fácil de configurar y usar.

Para clonar un repositorio Git sobre SSH, puede especificar una URL ssh:// como esta:

$ git clone ssh://[usuario@]servidor/proyecto.git

108
Machine Translated by Google

O puede usar la sintaxis similar a scp más corta para el protocolo SSH:

$ git clonar [usuario@]servidor:proyecto.git

En los dos casos anteriores, si no especifica el nombre de usuario opcional, Git asume el usuario con el que está
conectado actualmente.

Los profesionales

Las ventajas de usar SSH son muchas. Primero, SSH es relativamente fácil de configurar: los demonios SSH son
comunes, muchos administradores de red tienen experiencia con ellos y muchas distribuciones de sistemas operativos
se configuran con ellos o tienen herramientas para administrarlos. Luego, el acceso a través de SSH es seguro: todas
las transferencias de datos se cifran y autentican. Por último, al igual que los protocolos HTTPS, Git y Local, SSH es
eficiente y hace que los datos sean lo más compactos posible antes de transferirlos.

Los contras

El aspecto negativo de SSH es que no admite el acceso anónimo a su repositorio de Git. Si está utilizando SSH, las
personas deben tener acceso SSH a su máquina, incluso en una capacidad de solo lectura, lo que no hace que SSH
sea propicio para proyectos de código abierto para los cuales las personas simplemente deseen clonar su repositorio
para examinarlo. Si lo usa solo dentro de su red corporativa, SSH puede ser el único protocolo con el que debe lidiar. Si
desea permitir el acceso anónimo de solo lectura a sus proyectos y también desea usar SSH, tendrá que configurar
SSH para que pueda pasar, pero algo más para que otros puedan buscar.

El Protocolo Git
Finalmente, tenemos el protocolo Git. Este es un demonio especial que viene empaquetado con Git; escucha en un
puerto dedicado (9418) que brinda un servicio similar al protocolo SSH, pero sin autenticación en absoluto. Para que un
repositorio se sirva mediante el protocolo Git, debe crear un archivo git-daemon-export-ok (el demonio no servirá un
repositorio sin ese archivo), pero aparte de eso, no hay seguridad. . O el repositorio de Git está disponible para que
todos lo clonen, o no lo está.
Esto significa que, por lo general, no hay que forzar este protocolo. Puede habilitar el acceso push pero, dada la falta
de autenticación, cualquier persona en Internet que encuentre la URL de su proyecto podría enviarlo a ese proyecto.
Baste decir que esto es raro.

Los profesionales

El protocolo Git suele ser el protocolo de transferencia de red más rápido disponible. Si está atendiendo una gran
cantidad de tráfico para un proyecto público o atendiendo un proyecto muy grande que no requiere autenticación de
usuario para el acceso de lectura, es probable que desee configurar un demonio de Git para atender su proyecto. Utiliza
el mismo mecanismo de transferencia de datos que el protocolo SSH pero sin la sobrecarga de cifrado y autenticación.

Los contras

La desventaja del protocolo Git es la falta de autenticación. Por lo general, no es deseable que el protocolo Git sea el
único acceso a su proyecto. Por lo general, lo combinará con acceso SSH o HTTPS para

109
Machine Translated by Google

los pocos desarrolladores que tienen acceso de inserción (escritura) y hacen que todos los demás usen git:// para acceso
de solo lectura. También es probablemente el protocolo más difícil de configurar. Debe ejecutar su propio demonio, que
requiere la configuración de xinetd o systemd o similar, que no siempre es un paseo por el parque. También requiere
acceso de firewall al puerto 9418, que no es un puerto estándar que siempre permiten los firewalls corporativos. Detrás de
los grandes firewalls corporativos, este oscuro puerto suele estar bloqueado.

Obtener Git en un servidor


Ahora cubriremos la configuración de un servicio Git que ejecuta estos protocolos en su propio servidor.

Aquí demostraremos los comandos y los pasos necesarios para realizar instalaciones básicas y
simplificadas en un servidor basado en Linux, aunque también es posible ejecutar estos servicios en

ÿ servidores macOS o Windows. En realidad, configurar un servidor de producción dentro de su


infraestructura sin duda implicará diferencias en las medidas de seguridad o las herramientas del
sistema operativo, pero con suerte esto le dará una idea general de lo que implica.

Para configurar inicialmente cualquier servidor Git, debe exportar un repositorio existente a un nuevo repositorio básico,
un repositorio que no contiene un directorio de trabajo. Esto es generalmente fácil de hacer. Para clonar su repositorio
para crear un nuevo repositorio simple, ejecute el comando de clonación con la opción --bare . Por convención, los nombres
de directorios de repositorios desnudos terminan con el sufijo .git, así:

$ git clone --bare my_project my_project.git


Clonación en el repositorio básico 'my_project.git'...
hecho.

Ahora debería tener una copia de los datos del directorio Git en su directorio my_project.git .

Esto es más o menos equivalente a algo como:

$ cp -Rf mi_proyecto/.git mi_proyecto.git

Hay un par de diferencias menores en el archivo de configuración pero, para su propósito, es casi lo mismo. Toma el
repositorio de Git solo, sin un directorio de trabajo, y crea un directorio específicamente solo para él.

Poner el repositorio desnudo en un servidor


Ahora que tiene una copia simple de su repositorio, todo lo que necesita hacer es colocarlo en un servidor y configurar sus
protocolos. Supongamos que configuró un servidor llamado git.example.com al que tiene acceso SSH y desea almacenar
todos sus repositorios Git en el directorio /srv/git . Suponiendo que /srv/git existe en ese servidor, puede configurar su
nuevo repositorio copiando su repositorio básico
sobre:

110
Machine Translated by Google

$ scp -r mi_proyecto.git usuario@git.ejemplo.com:/srv/git

En este punto, otros usuarios que tengan acceso de lectura basado en SSH al directorio /srv/git en ese servidor pueden
clonar su repositorio ejecutando:

$ git clon usuario@git.ejemplo.com:/srv/git/mi_proyecto.git

Si un usuario usa SSH en un servidor y tiene acceso de escritura al directorio /srv/git/my_project.git , también tendrá
automáticamente acceso de inserción.

Git agregará automáticamente permisos de escritura grupales a un repositorio correctamente si ejecuta el comando git init
con la opción --shared . Tenga en cuenta que al ejecutar este comando, no destruirá ninguna confirmación, referencia, etc.
en el proceso.

$ ssh usuario@git.example.com
$ cd /srv/git/my_project.git $
git init --bare --shared

Verá lo fácil que es tomar un repositorio de Git, crear una versión básica y colocarlo en un servidor al que usted y sus
colaboradores tengan acceso SSH. Ahora estás listo para colaborar en el mismo proyecto.

Es importante tener en cuenta que esto es literalmente todo lo que necesita hacer para ejecutar un servidor Git útil al que
varias personas tengan acceso: simplemente agregue cuentas compatibles con SSH en un servidor y pegue un repositorio
básico en algún lugar donde todos esos usuarios hayan leído y escrito. el acceso a los. Estás listo para ir, no se necesita
nada más.

En las próximas secciones, verá cómo expandirse a configuraciones más sofisticadas. Esta discusión incluirá no tener que
crear cuentas de usuario para cada usuario, agregar acceso público de lectura a los repositorios, configurar interfaces de
usuario web y más. Sin embargo, tenga en cuenta que para colaborar con un par de personas en un proyecto privado,
todo lo que necesita es un servidor SSH y un repositorio básico.

Configuraciones pequeñas

Si tiene un equipo pequeño o simplemente está probando Git en su organización y tiene solo unos pocos desarrolladores,
las cosas pueden ser simples para usted. Uno de los aspectos más complicados de configurar un servidor Git es la gestión
de usuarios. Si desea que algunos repositorios sean de solo lectura para ciertos usuarios y de lectura/escritura para otros,
el acceso y los permisos pueden ser un poco más difíciles de organizar.

Acceso SSH

Si tiene un servidor al que todos sus desarrolladores ya tienen acceso SSH, generalmente es más fácil configurar su
primer repositorio allí, porque casi no tiene que hacer ningún trabajo (como cubrimos en la última sección). Si desea
permisos de tipo de control de acceso más complejos en sus repositorios, puede manejarlos con los permisos normales
del sistema de archivos del sistema operativo de su servidor.

111
Machine Translated by Google

Si desea colocar sus repositorios en un servidor que no tiene cuentas para todos los miembros de su equipo a
quienes desea otorgar acceso de escritura, debe configurar el acceso SSH para ellos. Suponemos que si tiene
un servidor con el que hacer esto, ya tiene instalado un servidor SSH, y así es como está accediendo al servidor.

Hay algunas maneras en las que puede otorgar acceso a todos los miembros de su equipo. El primero es
configurar cuentas para todos, lo cual es sencillo pero puede ser engorroso. Es posible que no desee ejecutar
adduser (o la posible alternativa useradd) y tenga que establecer contraseñas temporales para cada nuevo usuario.

Un segundo método es crear una sola cuenta de usuario 'git' en la máquina, pedirle a cada usuario que tenga
acceso de escritura que le envíe una clave pública SSH y agregar esa clave al archivo ~/.ssh/authorized_keys
de ese nuevo cuenta 'git'. En ese momento, todos podrán acceder a esa máquina a través de la cuenta 'git'. Esto
no afecta los datos de confirmación de ninguna manera: el usuario de SSH con el que te conectas no afecta las
confirmaciones que has registrado.

Otra forma de hacerlo es hacer que su servidor SSH se autentique desde un servidor LDAP o alguna otra fuente
de autenticación centralizada que ya haya configurado. Siempre que cada usuario pueda obtener acceso de shell
en la máquina, cualquier mecanismo de autenticación SSH que se le ocurra debería funcionar.

Generación de su clave pública SSH


Muchos servidores Git se autentican mediante claves públicas SSH. Para proporcionar una clave pública, cada
usuario de su sistema debe generar una si aún no tiene una. Este proceso es similar en todos los sistemas
operativos. Primero, debe verificar para asegurarse de que aún no tiene una clave. De forma predeterminada, las
claves SSH de un usuario se almacenan en el directorio ~/.ssh de ese usuario . Puede verificar fácilmente si ya
tiene una clave yendo a ese directorio y enumerando los contenidos:

$ cd ~/.ssh $
lsauthorized_keys2

id_dsa config id_dsa.pub hosts_conocidos

Está buscando un par de archivos con un nombre similar a id_dsa o id_rsa y un archivo coincidente con una
extensión .pub . El archivo .pub es su clave pública y el otro archivo es la clave privada correspondiente. Si no
tiene estos archivos (o ni siquiera tiene un directorio .ssh ), puede crearlos ejecutando un programa llamado ssh-
keygen, que se proporciona con el paquete SSH en sistemas Linux/macOS y viene con Git para Windows:

112
Machine Translated by Google

$ ssh-keygen -o
Generación de un par de claves rsa públicas/privadas.
Introduzca el archivo en el que guardar la clave (/home/schacon/.ssh/id_rsa): Directorio
creado '/home/schacon/.ssh'.
Ingrese la frase de contraseña (vacío para no tener frase de
contraseña): Ingrese la misma frase de contraseña
nuevamente: Su identificación ha sido guardada en /home/schacon/.ssh/id_rsa.
Su clave pública se ha guardado en /home/schacon/.ssh/id_rsa.pub.
La huella digital clave es:
d0:82:24:8e:d7:f1:bb:9b:33:53:96:93:49:da:9b:e3 schacon@mylaptop.local

Primero confirma dónde desea guardar la clave (.ssh/id_rsa), y luego solicita dos veces una frase de contraseña, que
puede dejar vacía si no desea escribir una contraseña cuando use la clave.
Sin embargo, si usa una contraseña, asegúrese de agregar la opción -o ; guarda la clave privada en un formato que es
más resistente al descifrado de contraseñas por fuerza bruta que el formato predeterminado. También puede usar la
herramienta ssh-agent para evitar tener que ingresar la contraseña cada vez.

Ahora, cada usuario que haga esto debe enviarle su clave pública a usted o a quien esté administrando el servidor Git
(suponiendo que esté utilizando una configuración de servidor SSH que requiere claves públicas). Todo lo que tienen
que hacer es copiar el contenido del archivo .pub y enviarlo por correo electrónico. Las claves públicas se ven así:

$ Cat ~ / .ssh / id_rsa.pub


AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom / GPL BWDSU +
nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3 Pbv7kOdJ /
MTyBlWXFCR + HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK / 7xA
t3FaoJoAsncM1Q9x5 + 3V0Ww68 / eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw / Pb0rwert / En mZ +
AW4OZPnTPI89ZPmVMLuayrD2cE86Z / il8b + + gw3r3 1nKatmIkjn2so1d01QraTlMqVSsbx NrRFi9wrf
+ M7Q == schacon-RSA ssh @mylaptop.local

Para obtener un tutorial más detallado sobre cómo crear una clave SSH en varios sistemas operativos, consulte la guía
de GitHub sobre claves SSH en https://docs.github.com/en/github/authenticating-to-github/generating-a-new ssh-key-and-
adding-it-to-the-ssh-agent.

Configuración del servidor


Veamos cómo configurar el acceso SSH en el lado del servidor. En este ejemplo, utilizará el método authorized_keys
para autenticar a sus usuarios. También asumimos que está ejecutando una distribución estándar de Linux como Ubuntu.

ÿ Gran parte comando,


de lo que se
endescribe
lugar de aquí seepuede
copiar automatizar
instalar mediante
manualmente el uso
las claves de ssh-copy-id
públicas.

Primero, crea una cuenta de usuario de git y un directorio .ssh para ese usuario.

113
Machine Translated by Google

$ sudo adduser git $ su


git $ cd $ mkdir .ssh
&& chmod 700 .ssh $
touch .ssh/authorized_keys && chmod
600 .ssh/authorized_keys

A continuación, debe agregar algunas claves públicas SSH del desarrollador al archivo authorized_keys para el usuario de git .
Supongamos que tiene algunas claves públicas de confianza y las ha guardado en archivos temporales. Nuevamente, las
claves públicas se ven así:

Ssh-rsa $ cat /tmp/id_rsa.john.pub


AAAAB3NzaC1yc2EAAAADAQABAAABAQCB007n / ww + ouN4gSLKssMxXnBOvf9LGt4L
ojG6rs6hPB09j9R / T17 / x4lhJA0F3FR1rP6kYBRsWj2aThGw6HXLm9 / 5zytK6Ztg3RPKK +
4k Yjh6541NYsnEAZuXz0jTTyAUfrtU3Z5E003C4oxOj6H0rfIF1kKI9MAQLMdpGW1GYEIgS9Ez
Sdfd8AcCIicTDWbqLAcU4UpkaX8KyGlLwsNuuGztobF8m72ALC / nLF6JLtPofwFBlgc + myiv
O7TCUSBdLQlgMVOFq1I2uPWQOkOWQAHukEOmfjy2jctxSDBQ220ymjaNsHT4kgtZg2AYYgPq
dAv8JggJICUvax2T9va5 GSG-par de claves

Simplemente los agrega al archivo authorized_keys del usuario de git en su directorio .ssh :

$ gato /tmp/id_rsa.john.pub >> ~/.ssh/claves_autorizadas $ gato /


tmp/id_rsa.josie.pub >> ~/.ssh/claves_autorizadas $ gato /tmp/
id_rsa.jessica.pub >> ~/ .ssh/claves_autorizadas

Ahora, puede configurar un repositorio vacío para ellos ejecutando git init con la opción --bare , que inicializa el repositorio sin
un directorio de trabajo:

$ cd /srv/git $
mkdir project.git $ cd
project.git $ git init --bare
Repositorio Git vacío
inicializado en /srv/git/project.git/

Luego, John, Josie o Jessica pueden insertar la primera versión de su proyecto en ese repositorio agregándolo como un control
remoto y empujando hacia arriba una rama. Tenga en cuenta que alguien debe instalarse en la máquina y crear un repositorio
simple cada vez que desee agregar un proyecto. Usemos gitserver como el nombre de host del servidor en el que configuró
su usuario y repositorio de git . Si lo está ejecutando internamente y configura DNS para que gitserver apunte a ese servidor,
entonces puede usar los comandos prácticamente como están (suponiendo que myproject es un proyecto existente con
archivos):

114
Machine Translated by Google

# en la computadora
de John $ cd myproject
$ git init $ git add . $
git commit -m 'Commit
inicial' $ git remote add origin
git@gitserver:/srv/git/project.git $ git push origin master

En este punto, los demás pueden clonarlo y volver a subir los cambios con la misma facilidad:

$ git clone git@gitserver:/srv/git/project.git $ cd


project $ vim README $ git commit -am 'Fix for
README file' $ git push origin master

Con este método, puede poner en funcionamiento rápidamente un servidor Git de lectura/escritura para un puñado de
desarrolladores.

Debe tener en cuenta que actualmente todos estos usuarios también pueden iniciar sesión en el servidor y obtener un shell
como usuario de git . Si desea restringir eso, deberá cambiar el shell a otra cosa en el archivo /etc/passwd .

Puede restringir fácilmente la cuenta de usuario de git a solo actividades relacionadas con Git con una herramienta de shell
limitada llamada git-shell que viene con Git. Si configura esto como el shell de inicio de sesión de la cuenta de usuario de git ,
entonces esa cuenta no puede tener acceso de shell normal a su servidor. Para usar esto, especifique git-shell en lugar de
bash o csh para el shell de inicio de sesión de esa cuenta. Para hacerlo, primero debe agregar la ruta completa del comando
git-shell a /etc/shells si aún no está allí:

$ cat /etc/shells # ver si git-shell ya está allí. Si no... $ which git-shell #


asegúrese de que git-shell esté instalado en su sistema. $ sudo -e /etc/shells #
y agregue la ruta a git-shell desde el último comando

Ahora puede editar el shell para un usuario usando chsh <nombre de usuario> -s <shell>:

$ sudo chsh git -s $(que git-shell)

Ahora, el usuario de git aún puede usar la conexión SSH para enviar y recibir repositorios de Git, pero no puede instalarse en
la máquina. Si lo intenta, verá un rechazo de inicio de sesión como este:

$ ssh git@gitserver
fatal: el shell interactivo de git no está habilitado.
sugerencia: ~/git-shell-commands debe existir y tener acceso de lectura y ejecución.
Conexión a gitserver cerrada.

115
Machine Translated by Google

En este punto, los usuarios aún pueden usar el reenvío de puertos SSH para acceder a cualquier host al que pueda
acceder el servidor git. Si desea evitar eso, puede editar el archivo authorized_keys y anteponer las siguientes
opciones a cada clave que desee restringir:

sin reenvío de puertos, sin reenvío X11, sin reenvío de agentes, sin pty

El resultado debería verse así:

$ Cat ~ / .ssh / authorized_keys sin


reenvío de puertos, no-X11-expedición, sin agente de expedición, sin Pty ssh-rsa
AAAAB3NzaC1yc2EAAAADAQABAAABAQCB007n / ww + ouN4gSLKssMxXnBOvf9LGt4LojG6rs6h
PB09j9R / T17 / x4lhJA0F3FR1rP6kYBRsWj2aThGw6HXLm9 / 5zytK6Ztg3RPKK +
4kYjh6541N YsnEAZuXz0jTTyAUfrtU3Z5E003C4oxOj6H0rfIF1kKI9MAQLMdpGW1GYEIgS9EzSdfd8AcC
IicTDWbqLAcU4UpkaX8KyGlLwsNuuGztobF8m72ALC / nLF6JLtPofwFBlgc +myivO7TCUSBd
LQlgMVOFq1I2uPWQOkOWQAHukEOmfjy2jctxSDBQ220ymjaNsHT4kgtZg2AYYgPqdAv8JggJ
ICUvax2T9va5 gsg-keypair

sin reenvío de puerto, sin reenvío X11, sin reenvío de agente, sin pty ssh-rsa
AAAAB3NzaC1yc2EAAAADAQABAAABAQDEwENNMomTboYI+LJieaAY16qiXiH3wuvENhBG...

Ahora los comandos de red de Git seguirán funcionando bien, pero los usuarios no podrán obtener un shell. Como
indica el resultado, también puede configurar un directorio en el directorio de inicio del usuario de git que personaliza
un poco el comando git-shell . Por ejemplo, puede restringir los comandos de Git que aceptará el servidor o puede
personalizar el mensaje que ven los usuarios si intentan acceder a SSH de esa manera. Ejecute git help shell para
obtener más información sobre cómo personalizar el shell.

Git demonio
A continuación, configuraremos un demonio que sirva repositorios mediante el protocolo "Git". Esta es una opción
común para un acceso rápido y no autenticado a sus datos de Git. Recuerde que dado que este no es un servicio
autenticado, todo lo que sirva a través de este protocolo es público dentro de su red.

Si está ejecutando esto en un servidor fuera de su firewall, debe usarse solo para proyectos que son públicamente
visibles para el mundo. Si el servidor en el que lo está ejecutando está dentro de su firewall, puede usarlo para
proyectos a los que una gran cantidad de personas o computadoras (integración continua o servidores de
compilación) tienen acceso de solo lectura, cuando no desea tener para agregar una clave SSH para cada uno.

En cualquier caso, el protocolo Git es relativamente fácil de configurar. Básicamente, debe ejecutar este comando
de manera demonizada:

$ demonio git --reuseaddr --base-path=/srv/git/ /srv/git/

La opción --reuseaddr permite que el servidor se reinicie sin esperar a que se agoten las conexiones antiguas,
mientras que la opción --base-path permite clonar proyectos sin especificar la ruta completa, y la ruta al final le dice
al demonio Git dónde busque repositorios para exportar. Si está ejecutando un firewall, también deberá perforar un
agujero en el puerto 9418 en el cuadro en el que está configurando esto.

116
Machine Translated by Google

Puede demonizar este proceso de varias maneras, según el sistema operativo que esté ejecutando.

Dado que systemd es el sistema de inicio más común entre las distribuciones modernas de Linux, puede usarlo para ese
propósito. Simplemente coloque un archivo en /etc/systemd/system/git-daemon.service con estos contenidos:

[Unidad]
Descripción=Iniciar Git Daemon

[Servicio]
ExecStart=/usr/bin/git daemon --reuseaddr --base-path=/srv/git/ /srv/git/

Reiniciar=siempre
Reiniciar segundo = 500 ms

Salida estándar=syslog
StandardError=syslog
SyslogIdentifier=git-daemon

Usuario=git
Grupo=git

[Instalar en pc]

WantedBy=multi-usuario.objetivo

Es posible que haya notado que el demonio Git se inicia aquí con git como grupo y como usuario. Modifíquelo para que
se ajuste a sus necesidades y asegúrese de que el usuario proporcionado exista en el sistema. Además, verifique que el
binario de Git esté realmente ubicado en /usr/bin/git y cambie la ruta si es necesario.

Finalmente, ejecutará systemctl enable git-daemon para iniciar automáticamente el servicio en el arranque, y puede
iniciar y detener el servicio con, respectivamente, systemctl start git-daemon y systemctl stop git daemon.

En otros sistemas, es posible que desee usar xinetd, un script en su sistema sysvinit , o algo más, siempre que obtenga
ese comando demonizado y observado de alguna manera.

A continuación, debe decirle a Git a qué repositorios debe permitir el acceso no autenticado basado en el servidor de Git.
Puede hacer esto en cada repositorio creando un archivo llamado git-daemon-export-ok.

$ cd /ruta/al/proyecto.git $ touch git-


daemon-export-ok

La presencia de ese archivo le dice a Git que está bien servir este proyecto sin autenticación.

HTTP inteligente

Ahora tenemos acceso autenticado a través de SSH y acceso no autenticado a través de git://, pero también hay un
protocolo que puede hacer ambas cosas al mismo tiempo. Configurar Smart HTTP es básicamente solo

117
Machine Translated by Google

habilitando un script CGI que se proporciona con Git llamado git-http-backend en el servidor. Este CGI leerá la ruta y
los encabezados enviados por git fetch o git push a una URL HTTP y determinará si el cliente puede comunicarse a
través de HTTP (lo cual es cierto para cualquier cliente desde la versión 1.6.6). Si el CGI ve que el cliente es inteligente,
se comunicará inteligentemente con él; de lo contrario, volverá al comportamiento tonto (por lo que es compatible con
versiones anteriores para lecturas con clientes más antiguos).

Veamos una configuración muy básica. Configuraremos esto con Apache como el servidor CGI. Si no tiene la
configuración de Apache, puede hacerlo en una caja de Linux con algo como esto:

$ sudo apt-get install apache2 apache2-utils $


a2enmod cgi alias env

Esto también habilita los módulos mod_cgi, mod_alias y mod_env , que son necesarios para que esto funcione
correctamente.

También deberá configurar el grupo de usuarios de Unix de los directorios /srv/git en www-data para que su servidor
web pueda leer y escribir acceso a los repositorios, porque la instancia de Apache que ejecuta el script CGI será (por
defecto) ejecutándose como ese usuario:

$ chgrp -R www-datos /srv/git

A continuación, debemos agregar algunas cosas a la configuración de Apache para ejecutar git-http-backend como
controlador de todo lo que ingrese a la ruta /git de su servidor web.

SetEnv GIT_PROJECT_ROOT /srv/git


SetEnv GIT_HTTP_EXPORT_ALL
ScriptAlias /git/ /usr/lib/git-core/git-http-backend/

Si omite la variable de entorno GIT_HTTP_EXPORT_ALL , Git solo servirá a los clientes no autenticados los repositorios
con el archivo git-daemon-export-ok en ellos, tal como lo hizo el demonio de Git.

Finalmente, querrá decirle a Apache que permita las solicitudes a git-http-backend y que haga que las escrituras se
autentiquen de alguna manera, posiblemente con un bloque Auth como este:

<Archivos "git-http-backend">
AuthType Basic AuthName "Git
Access"
AuthUserFile /srv/git/.htpasswd Requerir
expr !(%{QUERY_STRING} -strmatch '*service=git-receive-pack*' || %{REQUEST_URI} =~
m#/git-receive-pack$#)
Requerir usuario válido </
Files>

Eso requerirá que cree un archivo .htpasswd que contenga las contraseñas de todos los usuarios válidos. Aquí hay un
ejemplo de cómo agregar un usuario "schacon" al archivo:

118
Machine Translated by Google

$ htpasswd -c /srv/git/.htpasswd schacon

Hay muchas formas de que Apache autentique a los usuarios, tendrá que elegir e implementar una de ellas. Este
es solo el ejemplo más simple que se nos ocurrió. Es casi seguro que también querrá configurar esto a través de
SSL para que todos estos datos estén encriptados.

No queremos profundizar demasiado en los aspectos específicos de la configuración de Apache, ya que es


posible que esté utilizando un servidor diferente o tenga diferentes necesidades de autenticación. La idea es que
Git viene con un CGI llamado git-http-backend que, cuando se invoca, hará toda la negociación para enviar y
recibir datos a través de HTTP. No implementa ninguna autenticación en sí, pero eso se puede controlar
fácilmente en la capa del servidor web que lo invoca. Puede hacer esto con casi cualquier servidor web compatible
con CGI, así que vaya con el que mejor conoce.

ÿ Para obtener más información


Documentos sobre
de Apache cómo
aquí: configurar la autenticación en Apache, consulte el
https://httpd.apache.org/docs/current/howto/auth.html

GitWeb
Ahora que tiene acceso básico de lectura/escritura y de solo lectura a su proyecto, es posible que desee
configurar un visualizador simple basado en la web. Git viene con un script CGI llamado GitWeb que a veces se
usa para esto.

Figura 49. La interfaz de usuario basada en web de GitWeb

119
Machine Translated by Google

Si desea verificar cómo se vería GitWeb para su proyecto, Git viene con un comando para iniciar una
instancia temporal si tiene un servidor web liviano en su sistema como lighttpd o webrick. En las máquinas
Linux, a menudo se instala lighttpd , por lo que puede ejecutarlo escribiendo git instaweb en el directorio de
su proyecto. Si está ejecutando una Mac, Leopard viene preinstalado con Ruby, por lo que webrick puede
ser su mejor opción. Para iniciar instaweb con un controlador que no sea lighttpd, puede ejecutarlo con la
opción --httpd .

$ git instaweb --httpd=webrick


[2009-02-21 10:02:21] INFORMACIÓN WEBrick
1.3.1 [2009-02-21 10:02:21] INFORMACIÓN ruby 1.8.6 (2008-03-03) [ universal-darwin9.0]

Eso inicia un servidor HTTPD en el puerto 1234 y luego inicia automáticamente un navegador web que se
abre en esa página. Es bastante fácil de tu parte. Cuando haya terminado y desee apagar el servidor, puede
ejecutar el mismo comando con la opción --stop :

$ git instaweb --httpd=webrick--stop

Si desea ejecutar la interfaz web en un servidor todo el tiempo para su equipo o para un proyecto de código
abierto que está alojando, deberá configurar el script CGI para que lo sirva su servidor web normal.
Algunas distribuciones de Linux tienen un paquete gitweb que puede instalar a través de apt o dnf, por lo
que es posible que desee probarlo primero. Veremos cómo instalar GitWeb manualmente muy rápidamente.
Primero, debe obtener el código fuente de Git, que viene con GitWeb, y generar el script CGI personalizado:

$ git clone git://git.kernel.org/pub/scm/git/git.git $ cd git/ $ make


GITWEB_PROJECTROOT="/srv/git" prefix=/usr gitweb SUBDIR
gitweb SUBDIR ../ make[2 ]: `GIT-VERSION-FILE' está actualizado.

GEN gitweb.cgi GEN


estático/gitweb.js $ sudo cp
-Rf gitweb /var/www/

Tenga en cuenta que debe indicarle al comando dónde encontrar sus repositorios de Git con la variable
GITWEB_PROJECTROOT . Ahora, debe hacer que Apache use CGI para ese script, para lo cual puede
agregar un VirtualHost:

120
Machine Translated by Google

<Host virtual *:80>


Nombre del servidor gitserver
Raíz del documento /var/www/gitweb
<Directorio /var/www/gitweb>
ÿ

Opciones +ExecCGI +FollowSymLinks +SymLinksIfOwnerMatch


ÿ

Permitir anular todo


ÿ

ordenar permitir,
ÿ

denegar Permitir
ÿ

de todos AddHandler cgi-script


ÿ

cgi DirectoryIndex gitweb.cgi


</Directory> </VirtualHost>

Una vez más, GitWeb se puede servir con cualquier servidor web compatible con CGI o Perl; si prefiere usar otra cosa, no
debería ser difícil de configurar. En este punto, debería poder visitar http://gitserver/ para ver sus repositorios en línea.

GitLab
Sin embargo, GitWeb es bastante simplista. Si está buscando un servidor Git moderno y con todas las funciones, existen
varias soluciones de código abierto que puede instalar en su lugar. Como GitLab es uno de los más populares, cubriremos
su instalación y uso como ejemplo. Esto es más difícil que la opción GitWeb y requerirá más mantenimiento, pero es una
opción con todas las funciones.

Instalación

GitLab es una aplicación web respaldada por una base de datos, por lo que su instalación es más complicada que la de
otros servidores Git. Afortunadamente, este proceso está bien documentado y respaldado. GitLab recomienda
encarecidamente instalar GitLab en su servidor a través del paquete oficial Omnibus GitLab.

Las otras opciones de instalación son:

• Gráfico GitLab Helm, para usar con Kubernetes.

• Paquetes Dockerizados de GitLab para usar con Docker.

• De los archivos fuente.

• Proveedor de nube como AWS, Google Cloud Platform, Azure, OpenShift y Digital Ocean.

Para obtener más información, lea el archivo Léame de GitLab Community Edition (CE).

Administración

Se accede a la interfaz de administración de GitLab a través de la web. Simplemente dirija su navegador al nombre de
host o la dirección IP donde está instalado GitLab e inicie sesión como usuario administrador. El nombre de usuario
predeterminado es admin@local.host y la contraseña predeterminada es 5iveL!fe (que debe cambiar de inmediato). Una
vez que haya iniciado sesión, haga clic en el icono "Área de administración" en el menú en la parte superior derecha.

121
Machine Translated by Google

Figura 50. El elemento "Área de administración" en el menú de GitLab

Usuarios

Todos los que usen su servidor GitLab deben tener una cuenta de usuario. Las cuentas de usuario son bastante simples,
contienen principalmente información personal adjunta a los datos de inicio de sesión. Cada cuenta de usuario tiene un espacio
de nombres, que es una agrupación lógica de proyectos que pertenecen a ese usuario. Si el usuario jane tuviera un proyecto
llamado proyecto, la URL de ese proyecto sería http://servidor/jane/proyecto.

Figura 51. Pantalla de administración de usuarios de GitLab

Puede eliminar una cuenta de usuario de dos maneras: "Bloquear" a un usuario evita que inicie sesión en la instancia de GitLab,
pero todos los datos en el espacio de nombres de ese usuario se conservarán, y las confirmaciones firmadas con la dirección
de correo electrónico de ese usuario seguirán vinculándose a su perfil

"Destruir" a un usuario, por otro lado, lo elimina por completo de la base de datos y del sistema de archivos.
Se eliminarán todos los proyectos y datos en su espacio de nombres, y también se eliminarán todos los grupos que posean.
Obviamente, esta es una acción mucho más permanente y destructiva, y rara vez la necesitarás.

Grupos

Un grupo de GitLab es una colección de proyectos, junto con datos sobre cómo los usuarios pueden acceder a esos proyectos.
Cada grupo tiene un espacio de nombres de proyecto (de la misma manera que lo hacen los usuarios), por lo que si la
capacitación del grupo tiene materiales del proyecto, su URL sería http://servidor/capacitación/materiales.

122
Machine Translated by Google

Figura 52. La pantalla de administración del grupo GitLab

Cada grupo está asociado con una cantidad de usuarios, cada uno de los cuales tiene un nivel de permisos para los proyectos del grupo y
el grupo mismo. Estos van desde "Invitado" (solo problemas y chat) hasta "Propietario" (control total del grupo, sus miembros y sus
proyectos). Los tipos de permisos son demasiado numerosos para enumerarlos aquí, pero GitLab tiene un enlace útil en la pantalla de
administración.

Proyectos

Un proyecto de GitLab corresponde aproximadamente a un solo repositorio de Git. Cada proyecto pertenece a un solo espacio de nombres,
ya sea un usuario o un grupo. Si el proyecto pertenece a un usuario, el propietario del proyecto tiene control directo sobre quién tiene
acceso al proyecto; si el proyecto pertenece a un grupo, los permisos de nivel de usuario del grupo tendrán efecto.

Cada proyecto tiene un nivel de visibilidad, que controla quién tiene acceso de lectura a las páginas y al repositorio de ese proyecto. Si un
proyecto es privado, el propietario del proyecto debe otorgar acceso de forma explícita a usuarios específicos.
Un proyecto interno es visible para cualquier usuario que haya iniciado sesión y un proyecto público es visible para cualquier persona.
Tenga en cuenta que esto controla tanto el acceso a git fetch como el acceso a la interfaz de usuario web para ese proyecto.

Manos

GitLab incluye soporte para ganchos, tanto a nivel de proyecto como de sistema. Para cualquiera de estos, el servidor de GitLab realizará
un HTTP POST con algún JSON descriptivo cada vez que ocurran eventos relevantes.
Esta es una excelente manera de conectar sus repositorios de Git y su instancia de GitLab con el resto de su automatización de desarrollo,
como servidores de CI, salas de chat o herramientas de implementación.

Uso básico
Lo primero que querrá hacer con GitLab es crear un nuevo proyecto. Puede hacerlo haciendo clic en el icono "+" en la barra de herramientas.
Se le pedirá el nombre del proyecto, a qué espacio de nombres debe pertenecer y cuál debe ser su nivel de visibilidad. La mayor parte de
lo que especifica aquí no es permanente y se puede cambiar más tarde a través de la interfaz de configuración. Haz clic en "Crear proyecto"
y listo.

123
Machine Translated by Google

Una vez que exista el proyecto, probablemente querrá conectarlo con un repositorio Git local. Se puede acceder a cada proyecto a
través de HTTPS o SSH, cualquiera de los cuales se puede usar para configurar un control remoto de Git. Las URL están visibles en
la parte superior de la página de inicio del proyecto. Para un repositorio local existente, este comando creará un gitlab remoto con
nombre en la ubicación alojada:

$ git remoto agregar gitlab https://server/namespace/project.git

Si no tiene una copia local del repositorio, simplemente puede hacer esto:

$ git clonar https://server/namespace/project.git

La interfaz de usuario web proporciona acceso a varias vistas útiles del propio repositorio. La página de inicio de cada proyecto
muestra la actividad reciente, y los enlaces en la parte superior lo llevarán a las vistas de los archivos del proyecto y al registro de
confirmación.

Trabajando juntos
La forma más sencilla de trabajar juntos en un proyecto de GitLab es otorgar a cada usuario acceso push directo al repositorio de Git.
Puede agregar un usuario a un proyecto yendo a la sección "Miembros" de la configuración de ese proyecto y asociando el nuevo
usuario con un nivel de acceso (los diferentes niveles de acceso se discuten un poco en Grupos). Al darle a un usuario un nivel de
acceso de "Desarrollador" o superior, ese usuario puede enviar confirmaciones y bifurcaciones directamente al repositorio.

Otra forma de colaboración más desacoplada es mediante el uso de solicitudes de combinación. Esta característica permite que
cualquier usuario que pueda ver un proyecto contribuya a él de forma controlada. Los usuarios con acceso directo pueden
simplemente crear una rama, enviar confirmaciones a ella y abrir una solicitud de fusión desde su rama hacia la maestra o cualquier
otra rama. Los usuarios que no tienen permisos de inserción para un repositorio pueden "bifurcarlo" para crear su propia copia, enviar
confirmaciones a su copia y abrir una solicitud de fusión desde su bifurcación de regreso al proyecto principal. Este modelo le permite
al propietario tener el control total de lo que ingresa al repositorio y cuándo, al tiempo que permite contribuciones de usuarios que no
son de confianza.

Fusionar solicitudes y problemas son las principales unidades de discusión de larga duración en GitLab. Cada solicitud de combinación
permite una discusión línea por línea del cambio propuesto (que admite un tipo de revisión de código ligero), así como un hilo de
discusión general. Ambos pueden asignarse a usuarios u organizarse en hitos.

Esta sección se enfoca principalmente en las funciones de GitLab relacionadas con Git, pero como un proyecto maduro, proporciona
muchas otras funciones para ayudar a su equipo a trabajar en conjunto, como wikis de proyectos y herramientas de mantenimiento
del sistema. Un beneficio de GitLab es que, una vez que el servidor está configurado y funcionando, rara vez necesitará modificar un
archivo de configuración o acceder al servidor a través de SSH; la mayor parte de la administración y el uso general se pueden
realizar a través de la interfaz del navegador.

Opciones alojadas de terceros


Si no quiere pasar por todo el trabajo que implica configurar su propio servidor Git, tiene varias opciones para alojar sus proyectos
Git en un sitio de alojamiento dedicado externo. Hacerlo ofrece una

124
Machine Translated by Google

una serie de ventajas: un sitio de alojamiento es generalmente rápido de configurar y fácil de iniciar proyectos,
y no requiere mantenimiento ni supervisión del servidor. Incluso si configura y ejecuta su propio servidor
internamente, es posible que desee utilizar un sitio de alojamiento público para su código fuente abierto; por lo
general, es más fácil para la comunidad de código abierto encontrarlo y ayudarlo.

En estos días, tiene una gran cantidad de opciones de alojamiento para elegir, cada una con diferentes ventajas
y desventajas. Para ver una lista actualizada, consulte la página de GitHosting en el wiki principal de Git en
https://git.wiki.kernel.org/index.php/GitHosting.

Cubriremos el uso de GitHub en detalle en GitHub, ya que es el host de Git más grande que existe y es posible
que deba interactuar con proyectos alojados en él en cualquier caso, pero hay docenas más para elegir si no
desea configurar su propio servidor Git.

Resumen
Tiene varias opciones para poner en marcha un repositorio Git remoto para que pueda colaborar con otros o
compartir su trabajo.

Ejecutar su propio servidor le brinda mucho control y le permite ejecutar el servidor dentro de su propio firewall,
pero dicho servidor generalmente requiere una buena cantidad de su tiempo para configurarlo y mantenerlo. Si
coloca sus datos en un servidor alojado, es fácil de configurar y mantener; sin embargo, debe poder mantener
su código en los servidores de otra persona, y algunas organizaciones no lo permiten.

Debería ser bastante sencillo determinar qué solución o combinación de soluciones es adecuada para usted y
su organización.

125
Machine Translated by Google

Git distribuido
Ahora que tiene un repositorio Git remoto configurado como punto focal para que todos los desarrolladores compartan
su código, y está familiarizado con los comandos básicos de Git en un flujo de trabajo local, verá cómo utilizar algunos
de los flujos de trabajo distribuidos. que Git te ofrece.

En este capítulo, verá cómo trabajar con Git en un entorno distribuido como colaborador e integrador. Es decir,
aprenderá cómo contribuir con código con éxito a un proyecto y hacerlo lo más fácil posible para usted y el mantenedor
del proyecto, y también cómo mantener un proyecto con éxito con la contribución de varios desarrolladores.

Flujos de trabajo distribuidos

A diferencia de los sistemas de control de versiones centralizados (CVCS), la naturaleza distribuida de Git le permite
ser mucho más flexible en la forma en que los desarrolladores colaboran en los proyectos. En los sistemas
centralizados, cada desarrollador es un nodo que trabaja más o menos por igual con un concentrador central. En Git,
sin embargo, cada desarrollador es potencialmente tanto un nodo como un concentrador; es decir, cada desarrollador
puede aportar código a otros repositorios y mantener un repositorio público en el que otros puedan basar su trabajo y
al que puedan contribuir. Esto presenta una amplia gama de posibilidades de flujo de trabajo para su proyecto y/o su
equipo, por lo que cubriremos algunos paradigmas comunes que aprovechan esta flexibilidad. Repasaremos las
fortalezas y posibles debilidades de cada diseño; puede elegir uno solo para usar, o puede mezclar y combinar
características de cada uno.

Flujo de trabajo centralizado

En los sistemas centralizados, generalmente existe un único modelo de colaboración: el flujo de trabajo centralizado.
Un concentrador central, o repositorio, puede aceptar código y todos sincronizan su trabajo con él. Varios
desarrolladores son nodos (consumidores de ese centro) y se sincronizan con esa ubicación centralizada.

Figura 53. Flujo de trabajo centralizado

Esto significa que si dos desarrolladores clonan desde el concentrador y ambos realizan cambios, el primer
desarrollador que inserte sus cambios nuevamente puede hacerlo sin problemas. El segundo desarrollador debe fusionarse en el

126
Machine Translated by Google

el trabajo del primero antes de subir los cambios, para no sobrescribir los cambios del primer desarrollador.
Este concepto es tan cierto en Git como en Subversion (o cualquier CVCS), y este modelo funciona perfectamente bien
en Git.

Si ya se siente cómodo con un flujo de trabajo centralizado en su empresa o equipo, puede continuar usando fácilmente
ese flujo de trabajo con Git. Simplemente configure un único repositorio y proporcione a todos los miembros de su equipo
acceso push; Git no permitirá que los usuarios se sobrescriban entre sí.

Digamos que John y Jessica comienzan a trabajar al mismo tiempo. John termina su cambio y lo envía al servidor. Luego,
Jessica intenta impulsar sus cambios, pero el servidor los rechaza. Se le dice que está tratando de impulsar cambios que
no son de avance rápido y que no podrá hacerlo hasta que busque y fusione. Este flujo de trabajo es atractivo para
muchas personas porque es un paradigma con el que muchos están familiarizados y se sienten cómodos.

Esto tampoco se limita a equipos pequeños. Con el modelo de ramificación de Git, es posible que cientos de
desarrolladores trabajen con éxito en un solo proyecto a través de docenas de sucursales simultáneamente.

Flujo de trabajo del administrador de integración

Debido a que Git le permite tener varios repositorios remotos, es posible tener un flujo de trabajo en el que cada
desarrollador tenga acceso de escritura a su propio repositorio público y acceso de lectura a los demás.
Este escenario a menudo incluye un repositorio canónico que representa el proyecto "oficial". Para contribuir a ese
proyecto, crea su propio clon público del proyecto y le envía sus cambios. Luego, puede enviar una solicitud al mantenedor
del proyecto principal para obtener sus cambios. Luego, el mantenedor puede agregar su repositorio como un control
remoto, probar sus cambios localmente, fusionarlos en su rama y enviarlos de vuelta a su repositorio. El proceso funciona
de la siguiente manera (consulte Flujo de trabajo del administrador de integración):

1. El mantenedor del proyecto empuja a su repositorio público.

2. Un colaborador clona ese repositorio y realiza cambios.

3. El contribuyente empuja a su propia copia pública.

4. El colaborador envía un correo electrónico al mantenedor pidiéndole que realice los cambios.

5. El mantenedor agrega el repositorio del colaborador como remoto y lo fusiona localmente.

6. El mantenedor envía los cambios fusionados al repositorio principal.

Figura 54. Flujo de trabajo del administrador de integración

127
Machine Translated by Google

Este es un flujo de trabajo muy común con herramientas basadas en hub como GitHub o GitLab, donde es fácil
bifurcar un proyecto e insertar sus cambios en su bifurcación para que todos los vean. Una de las principales ventajas
de este enfoque es que puede continuar trabajando y el mantenedor del repositorio principal puede obtener sus
cambios en cualquier momento. Los colaboradores no tienen que esperar a que el proyecto incorpore sus cambios:
cada parte puede trabajar a su propio ritmo.

Flujo de trabajo de dictadores y tenientes

Esta es una variante de un flujo de trabajo de múltiples repositorios. Generalmente es utilizado por grandes proyectos
con cientos de colaboradores; un ejemplo famoso es el kernel de Linux. Varios administradores de integración están
a cargo de ciertas partes del repositorio; se llaman tenientes. Todos los lugartenientes tienen un gerente de
integración conocido como el dictador benévolo. El dictador benévolo empuja desde su directorio a un repositorio de
referencia del que todos los colaboradores deben extraer. El proceso funciona así (ver Flujo de trabajo del dictador
benévolo):

1. Los desarrolladores regulares trabajan en su rama de tema y vuelven a basar su trabajo sobre el maestro. los
rama maestra es la del repositorio de referencia a la que empuja el dictador.

2. Los tenientes fusionan las ramas temáticas de los desarrolladores en su rama principal .

3. El dictador fusiona las ramas maestras de los tenientes en la rama maestra del dictador .

4. Finalmente, el dictador empuja esa rama maestra al repositorio de referencia para que los otros desarrolladores
puedan volver a basarse en ella.

Figura 55. Flujo de trabajo del dictador benévolo

Este tipo de flujo de trabajo no es común, pero puede ser útil en proyectos muy grandes o en entornos muy
jerárquicos. Permite que el líder del proyecto (el dictador) delegue gran parte del trabajo y recopile grandes
subconjuntos de código en múltiples puntos antes de integrarlos.

Patrones para administrar ramas de código fuente

128
Machine Translated by Google

Martin Fowler ha elaborado una guía "Patrones para gestionar ramas de código fuente".
Esta guía cubre todos los flujos de trabajo comunes de Git y explica cómo y cuándo usarlos. También
ÿ hay una sección que compara frecuencias de integración altas y bajas.

https://martinfowler.com/articles/branching-patterns.html

Resumen de flujos de trabajo

Estos son algunos flujos de trabajo de uso común que son posibles con un sistema distribuido como Git, pero puede ver
que son posibles muchas variaciones para adaptarse a su flujo de trabajo particular del mundo real. Ahora que puede
(con suerte) determinar qué combinación de flujo de trabajo puede funcionar para usted, cubriremos algunos ejemplos
más específicos de cómo lograr los roles principales que conforman los diferentes flujos. En la siguiente sección,
aprenderá sobre algunos patrones comunes para contribuir a un proyecto.

Contribuir a un proyecto
La principal dificultad para describir cómo contribuir a un proyecto son las numerosas variaciones sobre cómo hacerlo.
Debido a que Git es muy flexible, las personas pueden trabajar juntas y lo hacen de muchas maneras, y es problemático
describir cómo debe contribuir: cada proyecto es un poco diferente. Algunas de las variables involucradas son el recuento
de colaboradores activos, el flujo de trabajo elegido, su acceso de confirmación y posiblemente el método de contribución
externa.

La primera variable es el recuento de colaboradores activos: ¿cuántos usuarios están contribuyendo activamente con
código para este proyecto y con qué frecuencia? En muchos casos, tendrá dos o tres desarrolladores con algunas
confirmaciones por día, o posiblemente menos para proyectos algo inactivos. Para empresas o proyectos más grandes,
la cantidad de desarrolladores podría ser de miles, con cientos o miles de confirmaciones cada día. Esto es importante
porque con más y más desarrolladores, te encuentras con más problemas para asegurarte de que tu código se aplique
limpiamente o se pueda fusionar fácilmente. Los cambios que envíe pueden volverse obsoletos o romperse gravemente
debido al trabajo que se fusionó mientras estaba trabajando o mientras sus cambios esperaban ser aprobados o
aplicados. ¿Cómo puede mantener su código constantemente actualizado y sus compromisos válidos?

La siguiente variable es el flujo de trabajo en uso para el proyecto. ¿Está centralizado y cada desarrollador tiene el mismo
acceso de escritura a la línea de código principal? ¿El proyecto tiene un mantenedor o administrador de integración que
verifica todos los parches? ¿Todos los parches son revisados y aprobados por pares? ¿Estás involucrado en ese
proceso? ¿Existe un sistema de lugartenientes y tiene que enviarles su trabajo primero?

La siguiente variable es su acceso de confirmación. El flujo de trabajo requerido para contribuir a un proyecto es muy
diferente si tiene acceso de escritura al proyecto que si no lo tiene. Si no tiene acceso de escritura, ¿cómo prefiere el
proyecto aceptar el trabajo contribuido? ¿Incluso tiene una política? ¿Cuánto trabajo estás contribuyendo a la vez? ¿Con
qué frecuencia contribuyes?

Todas estas preguntas pueden afectar la forma en que contribuye de manera efectiva a un proyecto y qué flujos de
trabajo son los preferidos o están disponibles para usted. Cubriremos aspectos de cada uno de estos en una serie de
casos de uso, pasando de lo simple a lo más complejo; debería poder construir los flujos de trabajo específicos que
necesita en la práctica a partir de estos ejemplos.

129
Machine Translated by Google

Directrices de compromiso

Antes de comenzar a ver los casos de uso específicos, aquí hay una nota rápida sobre los mensajes de confirmación.
Tener una buena guía para crear compromisos y cumplirla hace que trabajar con Git y colaborar con otros sea mucho más
fácil. El proyecto Git proporciona un documento que presenta una serie de buenos consejos para crear compromisos desde
los cuales enviar parches; puede leerlo en el código fuente de Git en el archivo Documentation/SubmittingPatches .

En primer lugar, sus envíos no deben contener errores de espacio en blanco. Git proporciona una manera fácil de verificar
esto: antes de comprometerse, ejecute git diff --check, que identifica posibles errores de espacios en blanco y los enumera
por usted.

Figura 56. Salida de git diff --check

Si ejecuta ese comando antes de comprometerse, puede saber si está a punto de cometer problemas de espacios en
blanco que pueden molestar a otros desarrolladores.

A continuación, intente hacer que cada confirmación sea un conjunto de cambios lógicamente separado. Si puede, intente
hacer que sus cambios sean digeribles: no programe un fin de semana completo en cinco problemas diferentes y luego
envíelos todos como una confirmación masiva el lunes. Incluso si no se compromete durante el fin de semana, use el área
de preparación el lunes para dividir su trabajo en al menos una confirmación por problema, con un mensaje útil por confirmación.
Si algunos de los cambios modifican el mismo archivo, intente usar git add --patch para organizar parcialmente los archivos
(cubierto en detalle en Puesta en escena interactiva). La instantánea del proyecto en la punta de la rama es idéntica ya sea
que realice una confirmación o cinco, siempre que todos los cambios se agreguen en algún momento, así que intente
facilitar las cosas a sus compañeros desarrolladores cuando tengan que revisar sus cambios.

Este enfoque también facilita extraer o revertir uno de los conjuntos de cambios si lo necesita más adelante.
Reescribir el historial describe una serie de trucos útiles de Git para reescribir el historial y organizar archivos de forma
interactiva: use estas herramientas para ayudar a crear un historial limpio y comprensible antes de enviar el trabajo a otra
persona.

Lo último a tener en cuenta es el mensaje de confirmación. Adquirir el hábito de crear mensajes de compromiso de calidad
hace que usar y colaborar con Git sea mucho más fácil. Como regla general, su

130
Machine Translated by Google

los mensajes deben comenzar con una sola línea que no tenga más de 50 caracteres y que describa el conjunto de
cambios de manera concisa, seguida de una línea en blanco, seguida de una explicación más detallada. El proyecto Git
requiere que la explicación más detallada incluya su motivación para el cambio y contraste su implementación con el
comportamiento anterior; esta es una buena guía a seguir. Escribe tu mensaje de confirmación en imperativo: "Corregir
error" y no "Corregir error" o "Corregir error". Aquí hay una plantilla que puede seguir, que hemos adaptado ligeramente
de una escrita originalmente por Tim Pope:

Resumen en mayúsculas, corto (50 caracteres o menos)

Texto explicativo más detallado, si es necesario. Envuélvalo en unos 72 caracteres


más o menos. En algunos contextos, la primera línea se trata como el asunto de un
correo electrónico y el resto del texto como el cuerpo. La línea en blanco que separa
el resumen del cuerpo es fundamental (a menos que omita el cuerpo por completo);
herramientas como rebase lo confundirán si ejecuta las dos juntas.

Escribe tu mensaje de confirmación en imperativo: "Corregir error" y no "Corregir error" o


"Corregir error". Esta convención coincide con los mensajes de confirmación generados por
comandos como git merge y git revert.

Los párrafos posteriores van después de las líneas en blanco.

- Las viñetas también están bien

- Por lo general, se usa un guión o un asterisco para la viñeta, seguido de un solo


espacio, con líneas en blanco en el medio, pero las convenciones varían aquí.

- Usar sangría francesa

Si todos sus mensajes de confirmación siguen este modelo, las cosas serán mucho más fáciles para usted y los
desarrolladores con los que colabora. El proyecto Git tiene mensajes de confirmación bien formateados: intente ejecutar
git log --no-merges allí para ver cómo se ve un historial de confirmación de proyecto bien formateado.

Haz lo que decimos, no lo que hacemos.

En aras de la brevedad, muchos de los ejemplos de este libro no tienen mensajes de confirmación

ÿ bien formateados como este; en su lugar, simplemente usamos la opción -m para git commit.

En resumen, haz lo que decimos, no lo que hacemos.

Equipo pequeño privado

La configuración más sencilla que probablemente encontrará es un proyecto privado con uno o dos desarrolladores más.
"Privado", en este contexto, significa código cerrado, no accesible para el mundo exterior. Usted y los demás
desarrolladores tienen acceso push al repositorio.

En este entorno, puede seguir un flujo de trabajo similar al que podría hacer al usar Subversion u otro sistema
centralizado. Todavía obtienes las ventajas de cosas como fuera de línea

131
Machine Translated by Google

confirmación y bifurcación y fusión mucho más simples, pero el flujo de trabajo puede ser muy similar; la principal
diferencia es que las fusiones ocurren en el lado del cliente en lugar de en el servidor en el momento de la
confirmación. Veamos cómo se vería cuando dos desarrolladores comiencen a trabajar juntos con un repositorio compartido.
El primer desarrollador, John, clona el repositorio, realiza un cambio y se compromete localmente. Los mensajes
de protocolo se han reemplazado con … en estos ejemplos para acortarlos un poco.

# John's Machine
$ git clone john@githost:simplegit.git
Clonación en 'simplegit'...
...
$ cd simplegit/ $
vim lib/simplegit.rb $ git
commit -am 'Eliminar valor predeterminado no
válido' [master 738ee87] Eliminar valor predeterminado
no válido 1 archivos cambiados, 1 inserciones (+), 1 eliminaciones (-)

La segunda desarrolladora, Jessica, hace lo mismo: clona el repositorio y realiza un cambio:

# Jessica's Machine
$ git clone jessica@githost:simplegit.git
Clonación en 'simplegit'...
...
$ cd simplegit/ $
vim TODO $ git
commit -am 'Agregar tarea de
reinicio' [master fbff5bc] Agregar tarea
de reinicio 1 archivos cambiados, 1 inserciones (+), 0 eliminaciones (-)

Ahora, Jessica envía su trabajo al servidor, que funciona bien:

# La máquina de
Jessica $ git push origin master
...
Para jessica@githost:simplegit.git
1edee6b..fbff5bc maestro -> maestro

La última línea del resultado anterior muestra un mensaje de retorno útil de la operación de inserción. El formato
básico es <oldref>..<newref> fromref ÿ toref, donde oldref significa la referencia anterior, newref significa la nueva
referencia, fromref es el nombre de la referencia local que se envía y toref es el nombre de la referencia remota
que se envía. actualizado. Verá un resultado similar a este a continuación en las discusiones, por lo que tener una
idea básica del significado ayudará a comprender los diversos estados de los repositorios.
Hay más detalles disponibles en la documentación de git-push.

Continuando con este ejemplo, poco después, John realiza algunos cambios, los envía a su repositorio local e
intenta enviarlos al mismo servidor:

132
Machine Translated by Google

# John's Machine
$ git push origin master
To john@githost:simplegit.git !
[rechazado] error maestromaestro
-> (no avance rápido): no se
pudo enviar algunas referencias a 'john@githost:simplegit.git'

En este caso, el impulso de John falla debido al impulso anterior de Jessica de sus cambios. Esto es especialmente
importante de entender si está acostumbrado a Subversion, porque notará que los dos desarrolladores no editaron
el mismo archivo. Aunque Subversion realiza automáticamente dicha fusión en el servidor si se editan diferentes
archivos, con Git, primero debe fusionar las confirmaciones localmente. En otras palabras, John primero debe
obtener los cambios anteriores de Jessica y fusionarlos en su repositorio local antes de que se le permita presionar.

Como primer paso, John obtiene el trabajo de Jessica (esto solo obtiene el trabajo anterior de Jessica, aún no lo
fusiona con el trabajo de John):

$ git buscar origen


...
De john@githost:simplegit +
049d078...fbff5bc maestro -> origen/maestro

En este punto, el repositorio local de John se parece a esto:

Figura 57. Historia divergente de John

Ahora John puede fusionar el trabajo de Jessica que obtuvo en su propio trabajo local:

133
Machine Translated by Google

$ git merge origin/master


Fusión realizada por la estrategia 'recursiva'.
POR HACER | 1 +
1 archivos cambiados, 1 inserciones (+), 0 eliminaciones (-)

Siempre que la fusión local se realice sin problemas, el historial actualizado de John ahora se verá así:

Figura 58. Repositorio de John después de fusionar origen/maestro

En este punto, es posible que John quiera probar este nuevo código para asegurarse de que ninguno de los trabajos de Jessica
afecte a ninguno de los suyos y, siempre que todo parezca estar bien, finalmente puede impulsar el nuevo trabajo combinado hasta el final.
servidor:

$ git empujar maestro de origen


...
Para john@githost:simplegit.git
fbff5bc..72bbc59 maestro -> maestro

Al final, el historial de confirmaciones de John se verá así:

Figura 59. Historial de John después de enviar al servidor de origen

134
Machine Translated by Google

Mientras tanto, Jessica creó una nueva rama de tema llamada problema 54 e hizo tres compromisos con esa
rama. Todavía no ha obtenido los cambios de John, por lo que su historial de confirmaciones se ve así:

Figura 60. Rama temática de Jessica

De repente, Jessica se entera de que John ha enviado un nuevo trabajo al servidor y quiere echarle un vistazo,
para poder obtener todo el contenido nuevo del servidor que aún no tiene:

# Máquina de Jessica
$ git fetch origen
...
De jessica@githost:simplegit
fbff5bc..72bbc59 maestro -> origen/maestro

Eso reduce el trabajo que John ha impulsado mientras tanto. La historia de Jessica ahora se ve así:

Figura 61. Historial de Jessica después de obtener los cambios de John

Jessica cree que su rama temática está lista, pero quiere saber qué parte del trabajo buscado de John tiene que
fusionar con su trabajo para poder impulsar. Ella ejecuta git log para averiguar:

$ git log --no-merges issue54..origin/master commit


738ee872852dfaa9d6634e0dea7a324040193016
Autor: John Smith <jsmith@example.com> Fecha:
viernes 29 de mayo 16:01:27 2009 -0700

Eliminar valor predeterminado no válido

La sintaxis issue54..origin/master es un filtro de registro que le pide a Git que muestre solo las confirmaciones
que están en la última rama (en este caso origen/maestro) que no están en la primera rama (en este caso

135
Machine Translated by Google

problema 54). Repasaremos esta sintaxis en detalle en Commit Ranges.

Del resultado anterior, podemos ver que hay una única confirmación que John ha realizado y que Jessica no se ha
fusionado con su trabajo local. Si fusiona origin/master, esa es la única confirmación que modificará su trabajo local.

Ahora, Jessica puede fusionar su trabajo de tema en su rama maestra , fusionar el trabajo de John (origen/maestro)
en su rama maestra y luego regresar al servidor nuevamente.

Primero (después de haber comprometido todo el trabajo en su rama temática de número 54), Jessica vuelve a su
rama maestra en preparación para integrar todo este trabajo:

$ git checkout master


Cambiado a branch 'master'
Su rama está detrás de 'origen/maestro' por 2 confirmaciones, y se puede avanzar rápidamente.

Jessica puede fusionar origin/master o issue54 primero; ambos están en sentido ascendente, por lo que el orden no
importa. La instantánea final debe ser idéntica sin importar el orden que elija; sólo la historia será diferente. Ella elige
fusionar la rama de problema 54 primero:

$ git merge issue54


Actualización de fbff5bc..4af4298
Avance rápido
| LÉAME 1 + lib/simplegit.rb | 6
+++++- 2 archivos cambiados, inserciones
6
(+), 1 borrado (-)

No se producen problemas; como puede ver, fue una simple combinación de avance rápido. Jessica ahora completa
el proceso de fusión local al fusionar el trabajo obtenido anteriormente de John que se encuentra en la rama de origen/
maestro :

$ git merge origin/master


Fusión automática lib/simplegit.rb
Fusión realizada por la estrategia 'recursiva'. lib/
2 +-
simplegit.rb | 1 archivos cambiados, 1 inserciones
(+), 1 eliminaciones (-)

Todo se fusiona limpiamente, y la historia de Jessica ahora se ve así:

136
Machine Translated by Google

Figura 62. La historia de Jessica después de fusionar los cambios de John

Ahora se puede acceder al origen/maestro desde la rama maestra de Jessica , por lo que debería poder empujar
con éxito (suponiendo que John no haya empujado aún más cambios mientras tanto):

$ git empujar maestro de origen


...
Para jessica@githost:simplegit.git
72bbc59..8059c15 maestro -> maestro

Cada desarrollador se comprometió varias veces y fusionó el trabajo de los demás con éxito.

Figura 63. Historial de Jessica después de devolver todos los cambios al servidor

Ese es uno de los flujos de trabajo más simples. Trabajas durante un tiempo (generalmente en una rama temática)
y fusionas ese trabajo en tu rama principal cuando está listo para integrarse. Cuando desee compartir ese trabajo,
obtenga y fusione su maestro desde origen/maestro si ha cambiado, y finalmente empuje a la rama maestra en el
servidor. La secuencia general es algo como esto:

137
Machine Translated by Google

Figura 64. Secuencia general de eventos para un flujo de trabajo Git simple de múltiples desarrolladores

Equipo administrado privado

En el siguiente escenario, observará los roles de colaborador en un grupo privado más grande. Aprenderá
a trabajar en un entorno en el que grupos pequeños colaboran en funciones, después de lo cual otra parte
integra esas contribuciones basadas en el equipo.

138
Machine Translated by Google

Digamos que John y Jessica están trabajando juntos en una función (llamémosla "función A"), mientras que Jessica
y un tercer desarrollador, Josie, están trabajando en una segunda (por ejemplo, "función B"). En este caso, la
empresa está utilizando un tipo de flujo de trabajo de administrador de integración donde el trabajo de los grupos
individuales está integrado solo por ciertos ingenieros, y la rama principal del repositorio principal puede ser
actualizada solo por esos ingenieros . En este escenario, todo el trabajo se realiza en sucursales basadas en
equipos y los integradores lo reúnen más tarde.

Sigamos el flujo de trabajo de Jessica mientras trabaja en sus dos características, colaborando en paralelo
con dos desarrolladores diferentes en este entorno. Suponiendo que ya clonó su repositorio, decide
trabajar primero en la función A. Ella crea una nueva rama para la característica y trabaja en
allí:

# Jessica's Machine $
git checkout -b featureA Cambiado
a una nueva rama 'featureA' $ vim lib/
simplegit.rb $ git commit -am 'Agregar límite
a la función de registro' [featureA 3300904] Agregar límite
a la función de registro 1 archivos cambiados, 1
inserciones (+), 1 eliminaciones (-)

En este punto, necesita compartir su trabajo con John, por lo que envía sus confirmaciones de rama de
característicaA al servidor. Jessica no tiene acceso de inserción a la rama maestra , solo los integradores
lo tienen, por lo que tiene que empujar a otra rama para colaborar con John:

$ git push -u característica de origenA


...
Para jessica@githost:simplegit.git * [nueva
rama] característicaA -> característicaA

Jessica le envía un correo electrónico a John para decirle que ha enviado un trabajo a una rama llamada
funciónA y que él puede verlo ahora. Mientras espera los comentarios de John, Jessica decide comenzar a
trabajar en la función B con Josie. Para comenzar, inicia una nueva rama de función, basándose en la rama
maestra del servidor :

# La máquina de Jessica
$ git fetch origin $ git
checkout -b featureB origin/master
Cambiado a una nueva rama 'featureB'

Ahora, Jessica hace un par de confirmaciones en la rama FeatureB :

139
Machine Translated by Google

$ vim lib/simplegit.rb $
git commit -am 'Hacer que la función ls-tree sea
recursiva' [featureB e5b0fdc] Hacer que la función ls-tree
sea recursiva 1 archivos cambiados, 1 inserciones (+), 1
eliminaciones (-) $ vim lib/simplegit .rb $ git commit -am
'Add ls-files' [featureB 8512791] Add ls-files 1 archivos
cambiados, 5 inserciones (+), 0 eliminaciones (-)

El repositorio de Jessica ahora se ve así:

Figura 65. Historial de confirmación inicial de Jessica

Está lista para impulsar su trabajo, pero recibe un correo electrónico de Josie que le informa que una rama con
un trabajo inicial de "característica B" ya se envió al servidor como la rama de FeatureBee . Jessica necesita
fusionar esos cambios con los suyos propios antes de poder enviar su trabajo al servidor. Jessica primero
obtiene los cambios de Josie con git fetch:

$ git buscar origen


...
De jessica@githost:simplegit *
[nueva rama] featureBeefeatureBee
-> origen/

Suponiendo que Jessica todavía está en su rama FeatureB desprotegida , ahora puede fusionar el trabajo de
Josie en esa rama con git merge:

$ git merge origin/featureBee


Auto-merging lib/simplegit.rb
Fusión realizada por la estrategia 'recursiva'.
lib/simplegit.rb | 4 ++++ 1 archivos
cambiados, 4
inserciones (+), 0 eliminaciones (-)

140
Machine Translated by Google

En este punto, Jessica quiere enviar todo este trabajo combinado de "función B" de vuelta al servidor, pero no quiere
simplemente enviar su propia rama de función B. Más bien, dado que Josie ya ha iniciado una rama de FeatureBee
upstream , Jessica quiere empujar a esa rama, lo que hace con:

$ git push -u origen característicaB:característicaBee


...
A jessica@githost:simplegit.git
fba9af8..cd685d1 funciónB -> funciónBee

Esto se llama refspec. Consulte The Refspec para obtener una discusión más detallada sobre las refspecs de Git y las
diferentes cosas que puede hacer con ellas. Observe también la bandera -u ; esto es la abreviatura de --set-upstream,
que configura las ramas para empujar y tirar más fácilmente más adelante.

De repente, Jessica recibe un correo electrónico de John, quien le dice que ha enviado algunos cambios a la rama de
funciónA en la que están colaborando, y le pide a Jessica que los revise. Nuevamente, Jessica ejecuta un simple git
fetch para obtener todo el contenido nuevo del servidor, incluido (por supuesto) el último trabajo de John:

$ git buscar origen


...
De jessica@githost:simplegit
3300904..aad881d característicaA -> origen/característicaA

Jessica puede mostrar el registro del nuevo trabajo de John comparando el contenido de la rama FeatureA recién
recuperada con su copia local de la misma rama:

$ git log featureA..origin/featureA commit


aad881d154acdaeb2b6b18ea0e827ed8a6d671e6
Autor: John Smith <jsmith@example.com> Fecha:
viernes 29 de mayo 19:57:33 2009 -0700

Aumente la salida del registro de 25 a 30

Si a Jessica le gusta lo que ve, puede fusionar el nuevo trabajo de John en su rama local FeatureA con:

$ git checkout featureA


Cambiado a branch 'featureA' $
git merge origin/featureA
Actualización 3300904..aad881d
Avance rápido lib/simplegit.rb | 1
10 ++++++++++-
archivos cambiados, 9 inserciones
(+), 1 eliminaciones (-)

Finalmente, es posible que Jessica desee realizar un par de cambios menores en todo ese contenido fusionado, por lo
que es libre de realizar esos cambios, enviarlos a su rama local de funciónA y enviar el resultado final al servidor:

141
Machine Translated by Google

$ git commit -am 'Agregar un pequeño ajuste al contenido


fusionado' [featureA 774b3ed] Agregar un pequeño ajuste al
contenido fusionado 1 archivos cambiados, 1 inserciones (+), 1
eliminaciones (-) $ git push
...
A jessica@githost:simplegit.git
3300904..774b3ed funciónA -> funciónA

El historial de confirmaciones de Jessica ahora se parece a esto:

Figura 66. Historial de Jessica después de comprometerse en una rama de características

En algún momento, Jessica, Josie y John informan a los integradores que las sucursales featureA y featureBee en el
servidor están listas para integrarse en la línea principal. Después de que los integradores fusionen estas ramas en la
línea principal, una búsqueda traerá la nueva confirmación de fusión, haciendo que el historial se vea así:

142
Machine Translated by Google

Figura 67. La historia de Jessica después de fusionar sus dos ramas temáticas

Muchos grupos cambian a Git debido a esta capacidad de tener varios equipos trabajando en paralelo, fusionando
las diferentes líneas de trabajo al final del proceso. La capacidad de los subgrupos más pequeños de un equipo
para colaborar a través de sucursales remotas sin tener que involucrar o obstaculizar necesariamente a todo el
equipo es un gran beneficio de Git. La secuencia para el flujo de trabajo que vio aquí es algo como esto:

143
Machine Translated by Google

Figura 68. Secuencia básica de este flujo de trabajo de equipo gestionado

Proyecto público bifurcado

Contribuir a proyectos públicos es un poco diferente. Debido a que no tiene los permisos para actualizar
directamente las ramas en el proyecto, debe hacer llegar el trabajo a los mantenedores de alguna otra manera.
Este primer ejemplo describe la contribución a través de bifurcaciones en hosts de Git que admiten bifurcaciones
fáciles. Muchos sitios de hospedaje admiten esto (incluidos GitHub, BitBucket, repo.or.cz y otros), y muchos
mantenedores de proyectos esperan este estilo de contribución. La siguiente sección se ocupa de los proyectos
que prefieren aceptar parches aportados por correo electrónico.

Primero, probablemente querrá clonar el repositorio principal, crear una rama de tema para el parche o parche

144
Machine Translated by Google

series con las que planea contribuir y haga su trabajo allí. La secuencia se ve básicamente así:

$ git clone <url> $


cd proyecto $ git
checkout -b característicaA ...
trabajo ... $ git commit ...
trabajo ... $ git commit

Es posible que desee usar rebase -i para reducir su trabajo a una sola confirmación, o reorganizar
ÿ el trabajo en las confirmaciones para que el parche sea más fácil de revisar para el mantenedor;
consulte Reescritura del historial para obtener más información sobre la reorganización interactiva.

Cuando el trabajo de su rama esté terminado y esté listo para devolverlo a los mantenedores, vaya a la página del
proyecto original y haga clic en el botón "Bifurcación", creando su propia bifurcación del proyecto con capacidad de escritura.
Luego debe agregar esta URL del repositorio como un nuevo control remoto de su repositorio local; en este ejemplo,
llamémoslo myfork:

$ git remoto agregar myfork <url>

A continuación, debe enviar su nuevo trabajo a este repositorio. Es más fácil enviar la rama de tema en la que está
trabajando a su repositorio bifurcado, en lugar de fusionar ese trabajo en su rama principal y enviarlo. La razón es que
si su trabajo no se acepta o se selecciona, no tiene que rebobinar su rama maestra (la operación de selección de Git se
trata con más detalle en Flujos de trabajo de cambio de base y selección). Si los mantenedores fusionan, modifican la
base o seleccionan su trabajo, eventualmente lo recuperará extrayéndolo de su repositorio de todos modos.

En cualquier caso, puedes impulsar tu trabajo con:

$ git push -u myfork funciónA

Una vez que su trabajo ha sido enviado a su bifurcación del repositorio, debe notificar a los mantenedores del proyecto
original que tiene trabajo que le gustaría que fusionaran. Esto a menudo se denomina solicitud de extracción y, por lo
general, genera dicha solicitud a través del sitio web (GitHub tiene su propio mecanismo de "Solicitud de extracción"
que revisaremos en GitHub ) o puede ejecutar el comando git request-pull y enviar un correo electrónico la salida
posterior al mantenedor del proyecto manualmente.

El comando git request-pull toma la rama base en la que desea que se extraiga su rama de tema y la URL del repositorio
de Git de la que desea que se extraigan, y produce un resumen de todos los cambios que solicita que se extraigan. Por
ejemplo, si Jessica quiere enviar a John una solicitud de extracción y ha realizado dos confirmaciones en la rama de
tema que acaba de enviar, puede ejecutar esto:

145
Machine Translated by Google

$ git request-pull origin/master myfork


Los siguientes cambios desde la confirmación 1edee6b1d61823a2de3b09c160d7080b8d1b3a40:
Jéssica Smith (1):
ÿ

Crear nueva función

están disponibles en el repositorio git en:

git://githost/simplegit.git característicaA

Jéssica Smith (2):


ÿ

Agregar límite a la función de registro


ÿ

Aumente la salida del registro de 25 a 30

lib/simplegit.rb | 1 10 ++++++++++-
archivos cambiados, 9 inserciones (+), 1 eliminaciones (-)

Esta salida se puede enviar al mantenedor: les dice de dónde se ramificó el trabajo, resume las
confirmaciones e identifica de dónde se extraerá el nuevo trabajo.

En un proyecto del que no eres el mantenedor, por lo general es más fácil tener una rama como maestro
que siempre realice un seguimiento del origen/maestro y hacer tu trabajo en ramas temáticas que puedes
descartar fácilmente si son rechazadas. Tener temas de trabajo aislados en ramas de temas también facilita
la reorganización de su trabajo si la punta del repositorio principal se ha movido mientras tanto y sus
compromisos ya no se aplican correctamente. Por ejemplo, si deseas enviar un segundo tema de trabajo al
proyecto, no continúes trabajando en la rama del tema que acabas de subir; empieza de nuevo desde la
rama maestra del repositorio principal:

$ git checkout -b featureB origen/maestro... trabajo...

$ git commit $
git push myfork featureB $ git
request-pull origin/master myfork ... solicitud
generada por correo electrónico para el mantenedor ... $ git fetch
origin

Ahora, cada uno de sus temas está contenido dentro de un silo, similar a una cola de parches, que puede
reescribir, reorganizar y modificar sin que los temas interfieran o dependan entre sí, así:

146
Machine Translated by Google

Figura 69. Historial de confirmación inicial con trabajo de funciónB

Digamos que el mantenedor del proyecto ha extraído un montón de otros parches y ha probado su primera
rama, pero ya no se fusiona limpiamente. En este caso, puede intentar volver a establecer la base de esa rama
sobre el origen/maestro, resolver los conflictos para el mantenedor y luego volver a enviar sus cambios:

$ git checkout funciónA


$ git rebase origin/master $
git push -f myfork funciónA

Esto reescribe su historial para que ahora se vea como el historial de confirmación después del trabajo de la característica A.

Figura 70. Historial de confirmaciones después del trabajo de FeatureA

Debido a que modificó la base de la rama, debe especificar -f en su comando push para poder reemplazar la
rama featureA en el servidor con una confirmación que no es un descendiente de ella. Una alternativa sería
enviar este nuevo trabajo a una rama diferente en el servidor (quizás llamada featureAv2).

Veamos otro escenario posible: el mantenedor ha analizado el trabajo en su segunda rama y le gusta el
concepto, pero le gustaría que cambiara un detalle de implementación. También aprovechará esta oportunidad
para mover el trabajo para que se base en la rama maestra actual del proyecto. Comienza una nueva rama
basada en la rama de origen/maestra actual , aplasta los cambios de la característica B allí, resuelve cualquier
conflicto, realiza el cambio de implementación y luego lo empuja como una nueva rama:

147
Machine Translated by Google

$ git checkout -b featureBv2 origin/master $ git


merge --squash featureB ... cambiar
implementación ... $ git commit $ git push myfork
featureBv2

La opción --squash toma todo el trabajo en la rama fusionada y lo comprime en un conjunto de cambios que produce el
estado del repositorio como si hubiera ocurrido una fusión real, sin realizar una confirmación de fusión. Esto significa que su
confirmación futura tendrá solo un padre y le permitirá introducir todos los cambios desde otra rama y luego realizar más
cambios antes de registrar la nueva confirmación.
Además, la opción --no-commit puede ser útil para retrasar la confirmación de fusión en el caso del proceso de fusión
predeterminado.

En este punto, puede notificar al mantenedor que realizó los cambios solicitados y que pueden encontrar esos cambios en
su rama featureBv2 .

Figura 71. Historial de confirmaciones después del trabajo de featureBv2

Proyecto público por correo electrónico

Muchos proyectos tienen procedimientos establecidos para aceptar parches; deberá verificar las reglas específicas para
cada proyecto, ya que serán diferentes. Dado que hay varios proyectos más antiguos y más grandes que aceptan parches
a través de una lista de correo de desarrolladores, veremos un ejemplo de eso ahora.

El flujo de trabajo es similar al caso de uso anterior: crea ramas de temas para cada serie de parches en la que trabaja. La
diferencia es cómo los envías al proyecto. En lugar de bifurcar el proyecto e impulsar su propia versión escribible, genera
versiones de correo electrónico de cada serie de confirmación y las envía por correo electrónico a la lista de correo del
desarrollador:

$ git checkout -b temaA ...


trabajo ...
$ git
confirmar ...
trabajar ... $ git confirmar

Ahora tiene dos confirmaciones que desea enviar a la lista de correo. Utiliza git format-patch para generar los archivos con
formato mbox que puede enviar por correo electrónico a la lista; convierte cada confirmación en un

148
Machine Translated by Google

mensaje de correo electrónico con la primera línea del mensaje de confirmación como asunto y el resto del mensaje más el parche
que introduce la confirmación como cuerpo. Lo bueno de esto es que aplicar un parche desde un correo electrónico generado con
format-patch conserva toda la información de confirmación correctamente.

$ git format-patch -M origin/master 0001-


add-limit-to-log-function.patch 0002-
aumentar-log-output-to-30-from-25.patch

El comando format-patch imprime los nombres de los archivos de parche que crea. El interruptor -M le dice a Git que busque cambios
de nombre. Los archivos terminan luciendo así:

$ cat 0001-add-limit-to-log-function.patch Desde


330090432754092d704da8e76ca5c05c198e71a8 Lun 17 de septiembre 00:00:00 2001
De: Jessica Smith <jessica@example.com> Fecha: Domingo, 6 de abril de 2008
10:17:23 - 0700 Asunto: [PATCH 1/2] Agregar límite a la función de registro

Limite la funcionalidad de registro a los primeros 20

---

lib/simplegit.rb | 1 2 +-
archivos cambiados, 1 inserciones (+), 1 eliminaciones (-)

diferencia --git a/lib/simplegit.rb b/lib/simplegit.rb índice


76f47bc..f9815f1 100644 --- a/lib/simplegit.rb +++ b/lib/
simplegit.rb @@ -14,7 +14,7 @@ clase SimpleGit fin

def log(árbol = 'maestro')


-
comando("git log #{treeish}")
+ command("git log -n 20 #{treeish}") fin

def ls_tree(árbol = 'maestro')


--

2.1.0

También puede editar estos archivos de parche para agregar más información para la lista de correo electrónico que no desea que
aparezca en el mensaje de confirmación. Si agrega texto entre la línea --- y el comienzo del parche (la línea diff --git ), los
desarrolladores pueden leerlo, pero el proceso de parcheo ignora ese contenido.

Para enviar esto por correo electrónico a una lista de correo, puede pegar el archivo en su programa de correo electrónico o enviarlo
a través de un programa de línea de comandos. Pegar el texto a menudo causa problemas de formato, especialmente con clientes
"más inteligentes" que no conservan las líneas nuevas y otros espacios en blanco de manera adecuada. Afortunadamente, Git
proporciona una herramienta para ayudarlo a enviar parches formateados correctamente a través de IMAP, lo que puede ser más
fácil para usted. Demostraremos cómo enviar un parche a través de Gmail, que resulta ser el agente de correo electrónico que mejor conocemos;

149
Machine Translated by Google

puede leer instrucciones detalladas para varios programas de correo al final del archivo Documentation/SubmittingPatches
mencionado anteriormente en el código fuente de Git.

Primero, debe configurar la sección imap en su archivo ~/.gitconfig . Puede establecer cada valor por separado con una serie
de comandos de configuración de git , o puede agregarlos manualmente, pero al final su archivo de configuración debería
verse así:

[imap]
carpeta = "[Gmail]/Borradores"
host = imaps://imap.gmail.com
usuario = usuario@gmail.com
contraseña = YX]8g76G_2^sFbd
puerto = 993 sslverify = falso

Si su servidor IMAP no usa SSL, las dos últimas líneas probablemente no sean necesarias y el valor de host será imap:// en
lugar de imaps://. Cuando esté configurado, puede usar git imap-send para colocar la serie de parches en la carpeta Borradores
del servidor IMAP especificado:

$ cat *.patch |git imap-send


Resolviendo imap.gmail.com... ok
Conectando a [74.125.142.109]:993... ok
Iniciando sesión... enviando 2 mensajes 100%
(2/2) hecho

En este punto, debería poder ir a su carpeta Borradores, cambiar el campo Para a la lista de correo a la que está enviando el
parche, posiblemente CC al mantenedor o persona responsable de esa sección, y enviarlo.

También puede enviar los parches a través de un servidor SMTP. Como antes, puede configurar cada valor por separado con
una serie de comandos de configuración de git , o puede agregarlos manualmente en la sección de envío de correo electrónico
en su archivo ~/.gitconfig :

[enviar
correo electrónico]
smtpencryption = tls smtpserver =
smtp.gmail.com smtpuser =
usuario@gmail.com smtpserverport = 587

Una vez hecho esto, puede usar git send-email para enviar sus parches:

150
Machine Translated by Google

$ git send-email *.patch 0001-add-


limit-to-log-function.patch 0002-increase-log-output-
to-30-from-25.patch ¿De quién deberían ser los correos
electrónicos? [Jessica Smith <jessica@ejemplo.com>]
Los correos electrónicos se enviarán desde: Jessica Smith <jessica@example.com>
¿A quién se deben enviar los correos electrónicos? jessica@example.com ¿ID de
mensaje que se usará como respuesta para el primer correo electrónico? y

Luego, Git escupe un montón de información de registro que se parece a esto para cada parche que está enviando:

(mbox) Agregar cc: Jessica Smith <jessica@example.com> de \line 'De: Jessica


Smith <jessica@example.com>'
está bien. El
registro dice: Sendmail: /usr/sbin/sendmail -i jessica@example.com De:
Jessica Smith <jessica@example.com> Para: jessica@example.com
Asunto: [PARCHE 1/2] Agregar límite a la función de registro Fecha :
sábado, 30 de mayo de 2009 13:29:15 -0700 Id. de mensaje:
<1243715356-61726-1-git-send-email-jessica@example.com> X-Mailer:
git-send-email 1.6.2.rc1 .20.g8c5b.dirty En respuesta a: <y> Referencias: <y>

resultado: correcto

ÿ Para obtener ayuda sobre cómo configurar su sistema y correo electrónico, más consejos y trucos, y una
sandbox para enviar un parche de prueba por correo electrónico, vaya a git-send-email.io.

Resumen
En esta sección, cubrimos múltiples flujos de trabajo y hablamos sobre las diferencias entre trabajar como parte de un pequeño equipo en

proyectos de código cerrado y contribuir a un gran proyecto público. Sabe verificar si hay errores de espacio en blanco antes de confirmar, y

puede escribir un excelente mensaje de confirmación. Aprendió cómo formatear parches y enviarlos por correo electrónico a una lista de correo

de desarrolladores. El manejo de las fusiones también se cubrió en el contexto de los diferentes flujos de trabajo. Ahora está bien preparado

para colaborar en cualquier proyecto.

A continuación, verás cómo trabajar la otra cara de la moneda: mantener un proyecto Git. Aprenderá a ser un dictador benévolo o un

administrador de integración.

Mantenimiento de un proyecto
Además de saber cómo contribuir de manera efectiva a un proyecto, es probable que necesite saber cómo mantener uno. Esto puede consistir

en aceptar y aplicar parches generados a través de format-patch y enviados por correo electrónico, o integrar cambios en sucursales remotas

para repositorios que haya agregado como remotos.

151
Machine Translated by Google

a tu proyecto. Ya sea que mantenga un repositorio canónico o quiera ayudar verificando o aprobando
parches, necesita saber cómo aceptar el trabajo de la manera más clara para otros colaboradores y
sostenible para usted a largo plazo.

Trabajando en Ramas Temáticas

Cuando está pensando en integrar un nuevo trabajo, generalmente es una buena idea probarlo en una
rama temática , una rama temporal creada específicamente para probar ese nuevo trabajo. De esta manera,
es fácil modificar un parche individualmente y dejarlo si no funciona hasta que tenga tiempo de volver a él.
Si crea un nombre de rama simple basado en el tema del trabajo que va a probar, como ruby_client o algo
descriptivo similar, puede recordarlo fácilmente si tiene que abandonarlo por un tiempo y regresar más
tarde. El mantenedor del proyecto Git tiende a nombrar estas ramas también, como sc/ruby_client, donde
sc es la abreviatura de la persona que contribuyó con el trabajo. Como recordará, puede crear la rama
basada en su rama maestra de esta manera:

$ git rama sc/ruby_client maestro

O, si también desea cambiar a él de inmediato, puede usar la opción checkout -b :

$ git checkout -b sc/ruby_client maestro

Ahora está listo para agregar el trabajo contribuido que recibió en esta rama temática y determinar si desea
fusionarlo con sus ramas a largo plazo.

Aplicación de parches desde el correo electrónico

Si recibe un parche por correo electrónico que necesita integrar en su proyecto, debe aplicar el parche en
su rama temática para evaluarlo. Hay dos formas de aplicar un parche enviado por correo electrónico: con
git apply o con git am.

Aplicar un parche con apply

Si recibió el parche de alguien que lo generó con git diff o alguna variación del comando Unix diff (que no
se recomienda; consulte la siguiente sección), puede aplicarlo con el comando git apply . Suponiendo que
guardó el parche en /tmp/patch-ruby-client.patch, puede aplicar el parche de esta manera:

$ git apply /tmp/patch-ruby-client.patch

Esto modifica los archivos en su directorio de trabajo. Es casi idéntico a ejecutar un comando patch -p1
para aplicar el parche, aunque es más paranoico y acepta menos coincidencias aproximadas que patch.
También maneja la adición, eliminación y cambio de nombre de archivos si se describen en el formato git
diff , que parche no funcionará. Finalmente, git apply es un modelo de "aplicar todo o cancelar todo" en el
que se aplica todo o nada, mientras que patch puede aplicar parches parcialmente, dejando su directorio
de trabajo en un estado extraño. git apply es en general mucho más conservador que patch. No creará una
confirmación para usted: después de ejecutarlo, debe organizar y confirmar los cambios introducidos manualmente.

152
Machine Translated by Google

También puede usar git apply para ver si un parche se aplica limpiamente antes de intentar aplicarlo realmente; puede ejecutar git
apply -- verifique con el parche:

$ git apply --check 0001-see-if-this-help-the-gem.patch error: el parche


falló: ticgit.gemspec:1 error: ticgit.gemspec: el parche no se aplica

Si no hay salida, entonces el parche debería aplicarse limpiamente. Este comando también sale con un estado distinto de cero si
falla la verificación, por lo que puede usarlo en secuencias de comandos si lo desea.

Aplicar un parche con am

Si el colaborador es un usuario de Git y fue lo suficientemente bueno como para usar el comando format-patch para generar su
parche, entonces su trabajo es más fácil porque el parche contiene información del autor y un mensaje de confirmación para usted.
Si puede, anime a sus colaboradores a usar format-patch en lugar de diff para generar parches para usted. Solo debería tener que
usar git apply para parches heredados y cosas por el estilo.

Para aplicar un parche generado por format-patch, usa git am (el comando se llama am porque se usa para "aplicar una serie de
parches desde un buzón"). Técnicamente, git am está diseñado para leer un archivo mbox, que es un formato simple de texto sin
formato para almacenar uno o más mensajes de correo electrónico en un archivo de texto. Se ve algo como esto:

De 330090432754092d704da8e76ca5c05c198e71a8 Lun 17 de septiembre 00:00:00 2001


De: Jessica Smith <jessica@example.com> Fecha: Dom, 6 de abril de 2008 10:17:23 -0700
Asunto: [PARCHE 1/2] Agregar límite a la función de registro

Limite la funcionalidad de registro a los primeros 20

Este es el comienzo de la salida del comando git format-patch que viste en la sección anterior; también representa un formato de
correo electrónico mbox válido. Si alguien le envió el parche por correo electrónico correctamente usando git send-email y lo
descarga en un formato mbox, entonces puede apuntar git am a ese archivo mbox y comenzará a aplicar todos los parches que
vea. Si ejecuta un cliente de correo que puede guardar varios correos electrónicos en formato mbox, puede guardar toda la serie
de parches en un archivo y luego usar git am para aplicarlos uno a la vez.

Sin embargo, si alguien subió un archivo de parche generado a través de git format-patch a un sistema de emisión de boletos o algo
similar, puede guardar el archivo localmente y luego pasar ese archivo guardado en su disco a git am para aplicarlo:

$ git am 0001-limit-log-function.patch Aplicando:


Agregar límite a la función de registro

Puede ver que se aplicó limpiamente y creó automáticamente la nueva confirmación para usted. La información del autor se toma
de los encabezados Desde y Fecha del correo electrónico , y el mensaje de confirmación es

153
Machine Translated by Google

tomado del Asunto y el cuerpo (antes del parche) del correo electrónico. Por ejemplo, si este parche se aplicó desde el ejemplo
de mbox anterior, la confirmación generada se vería así:

$ git log --pretty=fuller -1 commit


6c5e70b984a60b3cecd395edd5b48a7575bf58e0 Autor:
Jessica Smith <jessica@example.com> AutorConfirmación:
de abril 10:17:23 2008 -0700 Fecha: dom 6
Scott Chacon <schacon@gmail.com> Fecha de confirmación:
jue 9 de abril 09:19:06 2009 -0700

Agregar límite a la función de registro

Limite la funcionalidad de registro a los primeros 20

La información de Confirmación indica la persona que aplicó el parche y la hora en que se aplicó. La información del autor es
la persona que creó originalmente el parche y cuándo se creó originalmente.

Pero es posible que el parche no se aplique limpiamente. Tal vez su rama principal se haya alejado demasiado de la rama a
partir de la cual se creó el parche, o el parche depende de otro parche que aún no ha aplicado. En ese caso, el proceso de git
am fallará y te preguntará qué quieres hacer:

$ git am 0001-ver-si-esto-ayuda-a-la-gem.patch Aplicando:


Vea si esto ayuda a la gema error: el parche falló:
ticgit.gemspec:1 error: ticgit.gemspec: el parche no se aplica
El parche falló en 0001 .

Cuando haya resuelto este problema, ejecute "git am --resolved".


Si prefiere omitir este parche, ejecute "git am --skip".
Para restaurar la rama original y dejar de parchear, ejecute "git am --abort".

Este comando coloca marcadores de conflicto en cualquier archivo con el que tenga problemas, de forma similar a una
operación de fusión o reorganización en conflicto. Resuelve este problema de la misma manera: edite el archivo para resolver
el conflicto, prepare el nuevo archivo y luego ejecute git am --resolved para continuar con el siguiente parche:

$ (arreglar el archivo)
$ git add ticgit.gemspec $ git
am --resolved Aplicación: vea
si esto ayuda a la gema

Si desea que Git intente resolver el conflicto de manera un poco más inteligente, puede pasarle una opción -3 , lo que hace
que Git intente una combinación de tres vías. Esta opción no está activada de forma predeterminada porque no funciona si la
confirmación del parche en la que se basa no está en su repositorio. Si tiene ese compromiso, si el parche se basó en un
compromiso público, entonces la opción -3 generalmente es mucho más inteligente para aplicar un parche en conflicto:

154
Machine Translated by Google

$ git am -3 0001-ver-si-esto-ayuda-a-la-gem.patch Aplicar:


Ver si esto ayuda a la gema error: el parche falló:
ticgit.gemspec:1 error: ticgit.gemspec: el parche no se
aplica Usando el índice información para reconstruir un
árbol base...
Volviendo a la base de parches y fusión de 3 vías...
Sin cambios: el parche ya se aplicó.

En este caso, sin la opción -3 , el parche se habría considerado como un conflicto. Dado que se utilizó la opción
-3 , el parche se aplicó limpiamente.

Si está aplicando varios parches desde un mbox, también puede ejecutar el comando am en modo interactivo,
que se detiene en cada parche que encuentra y le pregunta si desea aplicarlo:

$ git am -3 -i mbox
El cuerpo de confirmación es:
--------------------------

A ver si esto ayuda a la gema


--------------------------

¿Aplicar? [sí]/[n]o/[e]dit/[v]ievar parche/[a]ccept all

Esto es bueno si tiene varios parches guardados, porque puede ver el parche primero si no recuerda cuál es, o
no aplicar el parche si ya lo ha hecho.

Cuando todos los parches para su tema se aplican y confirman en su rama, puede elegir si desea integrarlos en
una rama de mayor duración y cómo hacerlo.

Comprobación de sucursales remotas

Si su contribución provino de un usuario de Git que configuró su propio repositorio, introdujo una serie de cambios
en él y luego le envió la URL al repositorio y el nombre de la rama remota en la que se encuentran los cambios,
puede agregarlos como un remoto y hacer fusiones localmente.

Por ejemplo, si Jessica le envía un correo electrónico diciendo que tiene una gran función nueva en la rama de
cliente de ruby de su repositorio, puede probarla agregando el control remoto y revisando esa rama localmente:

$ git remote add jessica git://github.com/jessica/myproject.git $ git fetch


jessica $ git checkout -b rubyclient jessica/ruby-client

Si ella te vuelve a enviar un correo electrónico más tarde con otra sucursal que contenga otra gran característica,
puedes buscar y pagar directamente porque ya tienes la configuración remota.

Esto es más útil si está trabajando con una persona constantemente. Si alguien solo tiene un parche para
contribuir de vez en cuando, entonces aceptarlo por correo electrónico puede llevar menos tiempo que requerir
que todos ejecuten su propio servidor y tener que agregar y eliminar controles remotos continuamente para
obtener algunos parches. También es poco probable que desee tener cientos de controles remotos, cada uno para alguien q

155
Machine Translated by Google

contribuye sólo un parche o dos. Sin embargo, los scripts y los servicios alojados pueden hacer que esto sea más fácil;
depende en gran medida de cómo se desarrolle y cómo se desarrollen sus colaboradores.

La otra ventaja de este enfoque es que también obtienes el historial de las confirmaciones. Aunque puede tener
problemas legítimos de fusión, sabe en qué parte de su historial se basa su trabajo; una combinación de tres vías
adecuada es la predeterminada en lugar de tener que proporcionar un -3 y esperar que el parche se haya generado a
partir de una confirmación pública a la que tiene acceso.

Si no está trabajando con una persona de manera constante, pero aún desea obtener información de ella de esta manera,
puede proporcionar la URL del repositorio remoto al comando git pull . Esto realiza una extracción única y no guarda la
URL como una referencia remota:

$ git pull https://github.com/onetimeguy/project De https://


github.com/onetimeguy/project * branch HEAD ->
FETCH_HEAD Fusión realizada por la estrategia 'recursiva'.

Determinar lo que se introduce


Ahora tiene una rama de tema que contiene trabajo contribuido. En este punto, puede determinar qué le gustaría hacer
con él. Esta sección revisa un par de comandos para que pueda ver cómo puede usarlos para revisar exactamente lo
que introducirá si fusiona esto en su rama principal.

Suele ser útil obtener una revisión de todas las confirmaciones que están en esta rama pero que no están en tu rama
maestra . Puede excluir confirmaciones en la rama principal agregando la opción --not antes del nombre de la rama. Esto
hace lo mismo que el formato master..contrib que usamos anteriormente. Por ejemplo, si su colaborador le envía dos
parches y crea una rama llamada contrib y aplica esos parches allí, puede ejecutar esto:

$ git log contrib --not master commit


5b6235bd297351589efc4d73316f0a68d484f118 Autor:
Scott Chacon <schacon@gmail.com> Fecha: viernes 24
de octubre 09:53:59 2008 -0700

A ver si esto ayuda a la gema

cometer 7482e0d16d04bea79d0dba8988cc78df655f16a0
Autor: Scott Chacon <schacon@gmail.com>
Fecha: lun 22 oct 19:38:36 2008 -0700

Actualice gemspec para que funcione mejor

Para ver qué cambios introduce cada confirmación, recuerda que puedes pasar la opción -p a git log y agregará la
diferencia introducida en cada confirmación.

Para ver una diferencia completa de lo que sucedería si fusionara esta rama temática con otra rama, es posible que deba
usar un truco extraño para obtener los resultados correctos. Usted puede pensar en ejecutar esto:

156
Machine Translated by Google

$ git diff maestro

Este comando le da una diferencia, pero puede ser engañoso. Si su rama maestra ha avanzado desde que creó la
rama de tema a partir de ella, entonces obtendrá resultados aparentemente extraños. Esto sucede porque Git
compara directamente las instantáneas de la última confirmación de la rama de tema en la que se encuentra y la
instantánea de la última confirmación de la rama principal . Por ejemplo, si agregó una línea en un archivo en la
rama maestra , una comparación directa de las instantáneas parecerá que la rama del tema eliminará esa línea.

Si el maestro es un antepasado directo de su rama temática, esto no es un problema; pero si las dos historias han
divergido, la diferencia parecerá que está agregando todas las cosas nuevas en su rama de tema y eliminando todo
lo que es único en la rama principal .

Lo que realmente desea ver son los cambios agregados a la rama del tema: el trabajo que presentará si fusiona
esta rama con la principal. Lo hace haciendo que Git compare la última confirmación en su rama de tema con el
primer ancestro común que tiene con la rama maestra .

Técnicamente, puede hacerlo determinando explícitamente el ancestro común y luego ejecutando su diferencia en
él:

$ git merge-base contrib master


36c7dba2c95e6bbb78dfa822519ecfec6e1ca649
$ git diff 36c7db

o, más concisamente:

$ git diff $(git merge-base contrib master)

Sin embargo, ninguno de ellos es particularmente conveniente, por lo que Git proporciona otra abreviatura para
hacer lo mismo: la sintaxis de triple punto. En el contexto del comando git diff , puedes poner tres puntos después
de otra rama para hacer una diferencia entre la última confirmación de la rama en la que estás y su ancestro común
con otra rama:

$ git diff master...contrib

Este comando le muestra solo el trabajo que ha introducido su rama de tema actual desde su ancestro común con
el maestro. Esa es una sintaxis muy útil para recordar.

Integración del trabajo contribuido

Cuando todo el trabajo en su rama temática esté listo para integrarse en una rama más principal, la pregunta es
cómo hacerlo. Además, ¿qué flujo de trabajo general desea utilizar para mantener su proyecto? Tiene varias
opciones, por lo que cubriremos algunas de ellas.

157
Machine Translated by Google

Fusión de flujos de trabajo

Un flujo de trabajo básico es simplemente fusionar todo ese trabajo directamente en su rama principal . En este escenario,
tiene una rama maestra que contiene código básicamente estable. Cuando tiene trabajo en una rama de tema que cree que
ha completado, o trabajo que alguien más ha contribuido y ha verificado, lo fusiona con su rama maestra, elimina esa rama
de tema recién fusionada y repite.

Por ejemplo, si tenemos un repositorio con trabajo en dos ramas llamadas ruby_client y php_client que se parece a History
con varias ramas de temas, y fusionamos ruby_client seguido de php_client, su historial terminará luciendo como After a topic
branch merge.

Figura 72. Historia con varias ramas temáticas

Figura 73. Después de fusionar una rama de tema

Ese es probablemente el flujo de trabajo más simple, pero posiblemente puede ser problemático si se trata de proyectos más
grandes o más estables en los que desea tener mucho cuidado con lo que presenta.

Si tiene un proyecto más importante, es posible que desee utilizar un ciclo de combinación de dos fases. En este escenario,
tiene dos ramas de ejecución prolongada, maestra y de desarrollo, en las que determina que la maestra se actualiza solo
cuando se corta una versión muy estable y todo el código nuevo se integra en la rama de desarrollo . Regularmente empuja
estas dos ramas al repositorio público. Cada vez que tiene una nueva rama de tema para fusionar (antes de fusionar una
rama de tema), la fusiona en desarrollar (después de fusionar una rama de tema); luego, cuando etiqueta un lanzamiento,
avanza rápidamente el maestro a donde sea que esté ahora

158
Machine Translated by Google

La rama de desarrollo estable es (después de la publicación de un proyecto).

Figura 74. Antes de fusionar una rama de tema

Figura 75. Después de fusionar una rama de tema

Figura 76. Después del lanzamiento de un proyecto

De esta manera, cuando las personas clonen el repositorio de su proyecto, pueden consultar master para compilar la última versión estable y mantenerse al

día fácilmente, o pueden consultar el desarrollo, que es el contenido más avanzado. También puede extender este concepto al tener una rama de integración

donde

159
Machine Translated by Google

todo el trabajo se fusiona. Luego, cuando el código base en esa rama es estable y pasa las pruebas, lo fusiona en una
rama de desarrollo ; y cuando eso ha demostrado ser estable por un tiempo, avanza rápidamente su rama principal .

Flujos de trabajo de fusión grande

El proyecto Git tiene cuatro ramas de ejecución prolongada: master, next y seen (anteriormente 'pu', actualizaciones
propuestas) para trabajos nuevos y maint para backports de mantenimiento. Cuando los contribuyentes introducen un
nuevo trabajo, se recopila en ramas temáticas en el repositorio del mantenedor de una manera similar a la que hemos
descrito (consulte Administrar una serie compleja de ramas temáticas aportadas en paralelo). En este punto, los temas se
evalúan para determinar si son seguros y están listos para el consumo o si necesitan más trabajo. Si son seguros, se
fusionan con el siguiente y esa rama se empuja hacia arriba para que todos puedan probar los temas integrados juntos.

Figura 77. Gestión de una serie compleja de ramas temáticas aportadas en paralelo

Si los temas aún necesitan trabajo, se fusionan en visto en su lugar. Cuando se determina que son totalmente estables,
los temas se vuelven a fusionar en maestro. Las ramas siguientes y vistas se reconstruyen a partir del maestro. Esto
significa que el maestro casi siempre avanza, el siguiente se vuelve a basar ocasionalmente y lo visto se vuelve a basar
aún más a menudo:

160
Machine Translated by Google

Figura 78. Fusión de ramas de temas aportados en ramas de integración a largo plazo

Cuando una rama de tema finalmente se fusiona con la principal, se elimina del repositorio. El proyecto Git también
tiene una rama de mantenimiento que se bifurca de la última versión para proporcionar parches respaldados en caso
de que se requiera una versión de mantenimiento. Por lo tanto, cuando clonas el repositorio de Git, tienes cuatro
ramas que puedes consultar para evaluar el proyecto en diferentes etapas de desarrollo, dependiendo de qué tan
vanguardista quieras ser o cómo quieras contribuir; y el mantenedor tiene un flujo de trabajo estructurado para
ayudarlos a examinar nuevas contribuciones. El flujo de trabajo del proyecto Git está especializado. Para comprender
esto claramente, puede consultar la guía del administrador de Git.

Flujos de trabajo de rebase y selección de cerezas

Otros mantenedores prefieren cambiar la base o seleccionar el trabajo contribuido sobre su rama maestra , en lugar
de fusionarlo, para mantener un historial mayormente lineal. Cuando tiene trabajo en una rama de tema y ha
determinado que desea integrarla, se mueve a esa rama y ejecuta el comando rebase para reconstruir los cambios
sobre su rama maestra actual (o de desarrollo, etc.). Si eso funciona bien, puede avanzar rápidamente su rama
maestra y terminará con un historial de proyecto lineal.

La otra forma de mover el trabajo introducido de una rama a otra es seleccionarlo. Una selección de cereza en Git es
como una nueva base para una sola confirmación. Toma el parche que se introdujo en una confirmación e intenta
volver a aplicarlo en la rama en la que se encuentra actualmente. Esto es útil si tiene varios compromisos en una rama
de tema y desea integrar solo uno de ellos, o si solo tiene un compromiso en una rama de tema y prefiere seleccionarlo
en lugar de ejecutar rebase. Por ejemplo, suponga que tiene un proyecto que se ve así:

161
Machine Translated by Google

Figura 79. Historial de ejemplo antes de un pick-cherry

Si desea extraer la confirmación e43a6 en su rama maestra , puede ejecutar:

$ git cherry-pick e43a6


Terminado un cherry-pick.
[maestro]: creado a0a41a9: "Mensaje más amigable cuando falla el bloqueo del índice".
3 archivos cambiados, 17 inserciones (+), 3 eliminaciones (-)

Esto extrae el mismo cambio introducido en e43a6, pero obtiene un nuevo valor SHA-1 de confirmación,
porque la fecha aplicada es diferente. Ahora su historial se ve así:

Figura 80. Historial después de elegir una confirmación en una rama de tema

Ahora puede eliminar su rama de tema y eliminar las confirmaciones que no deseaba incluir.

162
Machine Translated by Google

regañar

Si está realizando muchas fusiones y cambios de base, o si está manteniendo una rama de tema de larga duración, Git tiene
una característica llamada "rerere" que puede ayudar.

Rerere significa "reutilizar resolución grabada", es una forma de atajar la resolución manual de conflictos. Cuando rerere está
habilitado, Git mantendrá un conjunto de imágenes previas y posteriores a las fusiones exitosas, y si nota que hay un conflicto
que se parece exactamente a uno que ya solucionó, solo usará la solución de la última vez. , sin molestarte con eso.

Esta característica viene en dos partes: un ajuste de configuración y un comando. La opción de configuración es rerere.enabled,
y es lo suficientemente útil como para ponerla en su configuración global:

$ git config --global rerere.habilitado verdadero

Ahora, cada vez que realice una combinación que resuelva conflictos, la resolución se registrará en el caché en caso de que
la necesite en el futuro.

Si lo necesita, puede interactuar con el caché rerere usando el comando git rerere . Cuando se invoca solo, Git verifica su
base de datos de resoluciones e intenta encontrar una coincidencia con cualquier conflicto de combinación actual y resolverlo
(aunque esto se hace automáticamente si rerere.enabled se establece en verdadero). También hay subcomandos para ver
qué se grabará, para borrar una resolución específica del caché y para borrar todo el caché. Cubriremos rerere con más
detalle en Rerere.

Etiquetado de tus lanzamientos

Cuando haya decidido cortar un lanzamiento, probablemente querrá asignar una etiqueta para poder volver a crear ese
lanzamiento en cualquier momento en el futuro. Puede crear una nueva etiqueta como se describe en Conceptos básicos de
Git. Si decide firmar la etiqueta como mantenedor, el etiquetado puede verse así:

$ git tag -s v1.5 -m 'mi etiqueta 1.5 firmada'


Necesita una frase de contraseña para desbloquear la clave
secreta para el usuario: "Scott Chacon <schacon@gmail.com>"
Clave DSA de 1024 bits, ID F721C45A, creada el 2009-02-09

Si firma sus etiquetas, es posible que tenga el problema de distribuir la clave PGP pública utilizada para firmar sus etiquetas.
El mantenedor del proyecto Git ha resuelto este problema al incluir su clave pública como un blob en el repositorio y luego
agregar una etiqueta que apunta directamente a ese contenido. Para hacer esto, puede averiguar qué clave desea ejecutando
gpg --list-keys:

$ gpg --list-keys /
Users/schacon/.gnupg/pubring.gpg
---------------------------------

pub 1024D/F721C45A 2009-02-09 [caduca: 2010-02-09] uid


Scott Chacon <schacon@gmail.com> sub 2048g/45D02282
2009-02-09 [caduca: 2010-02-09]

163
Machine Translated by Google

Luego, puede importar directamente la clave a la base de datos de Git exportándola y canalizándola a través de git hash-
object, que escribe un nuevo blob con esos contenidos en Git y le devuelve el SHA-1 del blob:

$ gpg -a --exportar F721C45A | git hash-objeto -w --stdin


659ef797d181633c87ec71ac3f9ba29fe5775b92

Ahora que tiene el contenido de su clave en Git, puede crear una etiqueta que apunte directamente a ella especificando
el nuevo valor SHA-1 que le dio el comando hash-object :

$ git etiqueta -a mantenedor-pgp-pub 659ef797d181633c87ec71ac3f9ba29fe5775b92

Si ejecuta git push --tags, la etiquetamaintainer -pgp-pub se compartirá con todos. Si alguien quiere verificar una etiqueta,
puede importar directamente su clave PGP sacando el blob directamente de la base de datos e importándolo a GPG:

$ git show mantenedor-pgp-pub | gpg --importar

Pueden usar esa clave para verificar todas sus etiquetas firmadas. Además, si incluye instrucciones en el mensaje de la
etiqueta, ejecutar git show <tag> le permitirá brindarle al usuario final instrucciones más específicas sobre la verificación
de la etiqueta.

Generación de un número de compilación

Debido a que Git no tiene números que aumentan monótonamente como 'v123' o el equivalente para cada confirmación,
si desea tener un nombre legible por humanos para acompañar una confirmación, puede ejecutar git describe en esa
confirmación. En respuesta, Git genera una cadena que consiste en el nombre de la etiqueta más reciente anterior a
esa confirmación, seguida por la cantidad de confirmaciones desde esa etiqueta, seguida finalmente por un valor SHA-1
parcial de la confirmación que se describe (precedido por la letra "g" que significa Git):

$ git describe maestro


v1.6.2-rc1-20-g8c5b85c

De esta manera, puede exportar una instantánea o compilarla y nombrarla algo comprensible para las personas. De
hecho, si construyes Git a partir del código fuente clonado del repositorio de Git, git --version te da algo parecido a esto.
Si está describiendo una confirmación que ha etiquetado directamente, simplemente le proporciona el nombre de la
etiqueta.

De forma predeterminada, el comando git describe requiere etiquetas anotadas (etiquetas creadas con el indicador -a o
-s ); si también desea aprovechar las etiquetas ligeras (sin anotaciones), agregue la opción --tags al comando. También
puede usar esta cadena como destino de un comando git checkout o git show , aunque se basa en el valor SHA-1
abreviado al final, por lo que es posible que no sea válido para siempre. Por ejemplo, el kernel de Linux saltó
recientemente de 8 a 10 caracteres para garantizar la exclusividad del objeto SHA-1, por lo que se invalidaron los
nombres de salida de git describe más antiguos.

164
Machine Translated by Google

Preparando un lanzamiento

Ahora desea lanzar una compilación. Una de las cosas que querrá hacer es crear un archivo de los
última instantánea de su código para aquellas pobres almas que no usan Git. El comando para hacer esto es git
archivo:

$ git archive master --prefix='proyecto/' | gzip > `git describe maestro`.tar.gz


$ ls *.tar.gz
v1.6.2-rc1-20-g8c5b85c.tar.gz

Si alguien abre ese tarball, obtiene la última instantánea de su proyecto en un directorio de proyectos .
También puede crear un archivo zip de la misma manera, pero pasando la opción --format=zip a
archivo git:

$ git archive master --prefix='proyecto/' --format=zip > `git describe master`.zip

Ahora tiene un buen tarball y un archivo zip de la versión de su proyecto que puede cargar en su
sitio web o correo electrónico a las personas.

El registro breve

Es hora de enviar un correo electrónico a su lista de correo de personas que quieren saber qué está pasando en su proyecto. A
buena manera de obtener rápidamente una especie de registro de cambios de lo que se ha agregado a su proyecto desde su
último lanzamiento o correo electrónico es usar el comando git shortlog . Resume todos los compromisos en el
rango que le das; por ejemplo, lo siguiente le brinda un resumen de todas las confirmaciones desde su último
versión, si su última versión se llamó v1.0.1:

$ git shortlog --no-merge master --not v1.0.1


Chris Wanstrath (6):
ÿ

Agregue soporte para etiquetas anotadas a Grit::Tag


ÿ

Agregue compatibilidad con etiquetas anotadas de referencias empaquetadas.

Añadir Grit::Commit#to_patch
ÿ

Actualizar versión e Historial.txt


ÿ

Quitar `puts` perdidos


ÿ

Hacer que ls_tree ignore nils

Tom Preston-Werner (4):


ÿ

fijar fechas en la historia


ÿ

método de versión dinámica


ÿ

Versión subida a 1.0.2


ÿ

gemspec regenerado para la versión 1.0.2

Obtiene un resumen limpio de todas las confirmaciones desde v1.0.1, agrupadas por autor, que puede enviar por correo electrónico a
tu lista.

165
Machine Translated by Google

Resumen
Debería sentirse bastante cómodo contribuyendo a un proyecto en Git, así como manteniendo su
propio proyecto o integrando las contribuciones de otros usuarios. ¡Felicitaciones por ser un
desarrollador de Git efectivo! En el próximo capítulo, aprenderá a usar el servicio de hospedaje de
Git más grande y popular, GitHub.

166
Machine Translated by Google

GitHub
GitHub es el host individual más grande para los repositorios de Git y es el punto central de colaboración para millones de
desarrolladores y proyectos. Un gran porcentaje de todos los repositorios de Git están alojados en GitHub, y muchos
proyectos de código abierto lo utilizan para el alojamiento de Git, el seguimiento de problemas, la revisión de código y otras cosas.
Entonces, si bien no es una parte directa del proyecto de código abierto de Git, existe una buena posibilidad de que desee
o necesite interactuar con GitHub en algún momento mientras usa Git profesionalmente.

Este capítulo trata sobre el uso efectivo de GitHub. Cubriremos registrarse y administrar una cuenta, crear y usar repositorios
Git, flujos de trabajo comunes para contribuir a proyectos y aceptar contribuciones a los suyos, la interfaz programática de
GitHub y muchos pequeños consejos para hacer su vida más fácil en general.

Si no está interesado en usar GitHub para hospedar sus propios proyectos o para colaborar con otros proyectos que están
hospedados en GitHub, puede saltar con seguridad a Git Tools.

Cambio de interfaces

Es importante tener en cuenta que, al igual que muchos sitios web activos, los elementos de la interfaz

ÿ de usuario en estas capturas de pantalla cambiarán con el tiempo. Con suerte, la idea general de lo
que estamos tratando de lograr aquí seguirá ahí, pero si desea versiones más actualizadas de estas
pantallas, las versiones en línea de este libro pueden tener capturas de pantalla más nuevas.

Instalación y configuración de la cuenta


Lo primero que debe hacer es configurar una cuenta de usuario gratuita. Simplemente visite https://github.com, elija un
nombre de usuario que aún no esté en uso, proporcione una dirección de correo electrónico y una contraseña, y haga clic
en el botón verde grande "Registrarse en GitHub".

167
Machine Translated by Google

Figura 81. El formulario de registro de GitHub

Lo siguiente que verá es la página de precios de los planes actualizados, pero es seguro ignorar esto por ahora.
GitHub te enviará un correo electrónico para verificar la dirección que proporcionaste. Adelante, haz esto; es bastante
importante (como veremos más adelante).

GitHub proporciona casi toda su funcionalidad con cuentas gratuitas, excepto algunas funciones
avanzadas.

ÿ Los planes pagos de GitHub incluyen herramientas y funciones avanzadas, así como límites más altos
para los servicios gratuitos, pero no los cubriremos en este libro. Para obtener más información sobre los
planes disponibles y su comparación, visite https://github.com/pricing .

Al hacer clic en el logotipo de Octocat en la parte superior izquierda de la pantalla, accederá a la página del panel de control.
Ahora está listo para usar GitHub.

Acceso SSH

A partir de ahora, puede conectarse con los repositorios de Git utilizando el protocolo https:// , autenticándose con el nombre
de usuario y la contraseña que acaba de configurar. Sin embargo, para simplemente clonar proyectos públicos, ni siquiera
necesita registrarse: la cuenta que acabamos de crear entra en juego cuando bifurcamos proyectos y empujamos a nuestras
bifurcaciones un poco más tarde.

Si desea utilizar controles remotos SSH, deberá configurar una clave pública. Si aún no tienes uno,

168
Machine Translated by Google

consulte Generación de su clave pública SSH. Abra la configuración de su cuenta usando el enlace en la parte superior derecha
de la ventana:

Figura 82. El enlace “Configuración de la cuenta”

Luego seleccione la sección "Claves SSH" en el lado izquierdo.

Figura 83. El enlace “Claves SSH”.

Desde allí, haga clic en el botón "Agregar una clave SSH", asigne un nombre a su clave, pegue el contenido de su archivo de
clave pública ~/.ssh/id_rsa.pub (o como lo haya llamado) en el área de texto y haga clic en “Añadir clave”.

Asegúrese de nombrar su clave SSH algo que pueda recordar. Puede nombrar cada una de sus claves

ÿ (por ejemplo, "Mi computadora portátil" o "Cuenta de trabajo") para que, si necesita revocar una clave
más tarde, pueda saber fácilmente cuál está buscando.

Tu avatar

A continuación, si lo desea, puede reemplazar el avatar que se genera para usted con una imagen de su elección. Primero
vaya a la pestaña "Perfil" (encima de la pestaña Claves SSH) y haga clic en "Cargar nueva imagen".

169
Machine Translated by Google

Figura 84. El enlace “Perfil”

Elegiremos una copia del logotipo de Git que está en nuestro disco duro y luego tendremos la oportunidad de recortarlo.

Figura 85. Recorta tu avatar

Ahora, en cualquier lugar donde interactúes en el sitio, las personas verán tu avatar junto a tu nombre de usuario.

Si ha cargado un avatar en el popular servicio Gravatar (a menudo utilizado para cuentas de Wordpress), ese avatar se
utilizará de forma predeterminada y no es necesario que realice este paso.

170
Machine Translated by Google

Sus direcciones de correo electrónico

La forma en que GitHub asigna sus compromisos de Git a su usuario es por dirección de correo electrónico. Si usa varias
direcciones de correo electrónico en sus confirmaciones y desea que GitHub las vincule correctamente, debe agregar todas las
direcciones de correo electrónico que ha usado en la sección Correos electrónicos de la sección de administración.

Figura 86. Agregar direcciones de correo electrónico

En Agregar direcciones de correo electrónico podemos ver algunos de los diferentes estados que son posibles. La dirección
superior se verifica y se configura como la dirección principal, lo que significa que es donde recibirá notificaciones y recibos. La
segunda dirección se verifica y, por lo tanto, se puede configurar como la principal si desea cambiarla.
La dirección final no está verificada, lo que significa que no puede convertirla en su dirección principal. Si GitHub ve alguno de
estos en mensajes de confirmación en cualquier repositorio del sitio, se vinculará a su usuario
ahora.

Autenticación de dos factores


Finalmente, para mayor seguridad, definitivamente debe configurar la autenticación de dos factores o "2FA". La autenticación de
dos factores es un mecanismo de autenticación que se está volviendo cada vez más popular recientemente para mitigar el riesgo
de que su cuenta se vea comprometida si su contraseña es robada de alguna manera. Al activarlo, GitHub le pedirá dos métodos
diferentes de autenticación, de modo que si uno de ellos se ve comprometido, un atacante no podrá acceder a su cuenta.

Puede encontrar la configuración de autenticación de dos factores en la pestaña Seguridad de la configuración de su cuenta.

171
Machine Translated by Google

Figura 87. 2FA en la pestaña Seguridad

Si hace clic en el botón "Configurar autenticación de dos factores", lo llevará a una página de configuración donde
puede optar por usar una aplicación de teléfono para generar su código secundario (una "contraseña de un solo uso
basada en el tiempo"), o puede hacer que GitHub le envíe un código por SMS cada vez que necesite iniciar sesión.

Después de elegir el método que prefiere y seguir las instrucciones para configurar 2FA, su cuenta estará un poco más
segura y tendrá que proporcionar un código además de su contraseña cada vez que inicie sesión en GitHub.

Contribuir a un proyecto
Ahora que nuestra cuenta está configurada, analicemos algunos detalles que podrían ser útiles para ayudarlo a
contribuir a un proyecto existente.

Proyectos de bifurcación

Si desea contribuir a un proyecto existente al que no tiene acceso push, puede "bifurcar" el proyecto. Cuando "bifurcas"
un proyecto, GitHub hará una copia del proyecto que es completamente tuya; vive en su espacio de nombres y puede
enviarlo.

Históricamente, el término "bifurcación" ha tenido un contexto algo negativo, lo que significa que
alguien tomó un proyecto de código abierto en una dirección diferente, a veces creando un proyecto
ÿ competitivo y dividiendo a los contribuyentes. En GitHub, una "bifurcación" es simplemente el
mismo proyecto en su propio espacio de nombres, lo que le permite realizar cambios en un proyecto
públicamente como una forma de contribuir de una manera más abierta.

De esta manera, los proyectos no tienen que preocuparse por agregar usuarios como colaboradores para darles acceso
push. Las personas pueden bifurcar un proyecto, empujarlo y contribuir con sus cambios al repositorio original creando
lo que se llama una solicitud de extracción, que trataremos a continuación. Esto abre un hilo de discusión con revisión
de código, y el propietario y el colaborador pueden comunicarse

172
Machine Translated by Google

sobre el cambio hasta que el propietario esté satisfecho con él, momento en el cual el propietario puede fusionarlo.

Para bifurcar un proyecto, visite la página del proyecto y haga clic en el botón "Bifurcar" en la parte superior derecha de la página.

Figura 88. El botón “Tenedor”

Después de unos segundos, accederá a la página de su nuevo proyecto, con su propia copia del código que se puede escribir.

El flujo de GitHub

GitHub está diseñado en torno a un flujo de trabajo de colaboración particular, centrado en solicitudes de incorporación de cambios. Este flujo

funciona ya sea que esté colaborando con un equipo muy unido en un único repositorio compartido, o una empresa distribuida globalmente o

una red de extraños que contribuyen a un proyecto a través de docenas de bifurcaciones. Se centra en el flujo de trabajo de Ramas de temas

cubierto en Git Branching.

Así es como funciona generalmente:

1. Bifurcar el proyecto.

2. Cree una rama de tema desde el maestro.

3. Haz algunos compromisos para mejorar el proyecto.

4. Empuje esta rama a su proyecto de GitHub.

5. Abra una solicitud de extracción en GitHub.

6. Discutir y, opcionalmente, continuar con la confirmación.

7. El propietario del proyecto fusiona o cierra la solicitud de extracción.

8. Sincroniza el maestro actualizado con tu bifurcación.

Este es básicamente el flujo de trabajo del Administrador de integración cubierto en el Flujo de trabajo del Administrador de integración, pero en

lugar de usar el correo electrónico para comunicarse y revisar los cambios, los equipos usan las herramientas basadas en la web de GitHub.

Veamos un ejemplo de cómo proponer un cambio en un proyecto de código abierto alojado en GitHub usando este flujo.

Puede usar la herramienta CLI oficial de GitHub en lugar de la interfaz web de GitHub para la mayoría de las cosas. La

ÿ herramienta se puede utilizar en sistemas Windows, MacOS y Linux. Ir a la página de inicio de la CLI de GitHub para las

instrucciones de instalación y el manual.

Crear una solicitud de extracción

Tony está buscando un código para ejecutar en su microcontrolador programable Arduino y encontró un gran archivo de programa en GitHub en

https://github.com/schacon/blink.

173
Machine Translated by Google

Figura 89. El proyecto al que queremos contribuir

El único problema es que la velocidad de parpadeo es demasiado rápida. Creemos que es mucho mejor esperar 3 segundos
en lugar de 1 entre cada cambio de estado. Así que mejoremos el programa y enviémoslo de vuelta al proyecto como un
cambio propuesto.

Primero, hacemos clic en el botón 'Fork' como se mencionó anteriormente para obtener nuestra propia copia del proyecto.
Nuestro nombre de usuario aquí es "tonychacon", por lo que nuestra copia de este proyecto está en https://github.com/
tonychacon/blink y ahí es donde podemos editarlo. Lo clonaremos localmente, crearemos una rama de tema, cambiaremos el
código y finalmente enviaremos ese cambio nuevamente a GitHub.

174
Machine Translated by Google

$ git clone https://github.com/tonychacon/blink ÿ Clonando en


'blink'...

$ cd
parpadear $ git checkout -b parpadeo lento ÿ
Cambiado a una nueva rama 'parpadeo lento'

$ sed -i '' 's/1000/3000/' blink.ino (macOS) ÿ # Si está en un


sistema Linux, haga esto en su lugar: # $ sed -i 's/1000/3000/'
blink. ino ÿ

$ git diff --word-diff ÿ diff --git a/


blink.ino b/blink.ino índice 15b9911..a6cc5a5
100644 --- a/blink.ino +++ b/blink.ino @@
-18, 7 +18,7 @@ void setup() { // la rutina de
bucle se ejecuta una y otra vez para siempre:
void loop() { digitalWrite(led, HIGH); //
enciende el LED (ALTO es el nivel de voltaje) [-delay(1000);-]
{+delay(3000);+} digitalWrite(led, LOW); [-retraso(1000);-]{+retraso(3000);
+} }
// Espera un segundo
// apaga el LED haciendo que el voltaje sea BAJO
// Espera un segundo

$ git commit -a -m 'Cambiar retraso a 3 segundos' ÿ [parpadeo


lento 5ca509d] Cambiar retraso a 3 segundos 1 archivo cambiado,
2 inserciones (+), 2 eliminaciones (-)

$ git push origin parpadeo lento ÿ Nombre


de usuario para 'https://github.com': tonychacon Contraseña
para 'https://tonychacon@github.com': Conteo de objetos:
5, listo.
Compresión delta usando hasta 8 hilos.
Comprimir objetos: 100% (3/3), listo.
Objetos de escritura: 100% (3/3), 340 bytes | 0 bytes/s, listo.
Total 3 (delta 1), reutilizado 0 (delta 0)
A https://github.com/tonychacon/blink* [nueva
sucursal] parpadeo lento -> parpadeo lento

ÿ Clona nuestra bifurcación del proyecto localmente.

ÿ Crear una rama temática descriptiva.

ÿ Haga nuestro cambio en el código.

ÿ Verifique que el cambio sea bueno.

ÿ Confirmar nuestro cambio a la rama de tema.

ÿ Vuelva a subir nuestra nueva rama de tema a nuestra bifurcación de GitHub.

Ahora, si volvemos a nuestra bifurcación en GitHub, podemos ver que GitHub notó que pusimos una nueva

175
Machine Translated by Google

el tema se ramifica y nos presenta un gran botón verde para ver nuestros cambios y abrir una solicitud de
extracción para el proyecto original.

Alternativamente, puede ir a la página "Sucursales" en https://github.com/<user>/<project>/branches para


ubicar su sucursal y abrir una nueva solicitud de extracción desde allí.

Figura 90. Botón Solicitud de extracción

Si hacemos clic en ese botón verde, veremos una pantalla que nos pide que le demos un título y una
descripción a nuestra solicitud de extracción. Casi siempre vale la pena esforzarse un poco en esto, ya que
una buena descripción ayuda al propietario del proyecto original a determinar qué estaba tratando de hacer,
si los cambios propuestos son correctos y si aceptar los cambios mejoraría el proyecto original.

También vemos una lista de las confirmaciones en nuestra rama de tema que están "por delante" de la rama
maestra (en este caso, solo una) y una diferencia unificada de todos los cambios que se realizarán si el
proyecto fusiona esta rama. dueño.

176
Machine Translated by Google

Figura 91. Página de creación de solicitud de extracción

Cuando presione el botón 'Crear solicitud de extracción' en esta pantalla, el propietario del proyecto que bifurcó recibirá una notificación
de que alguien está sugiriendo un cambio y lo vinculará a una página que tiene toda esta información.

Aunque las solicitudes de extracción se usan comúnmente para proyectos públicos como este cuando el
colaborador tiene un cambio completo listo para realizar, también se usa a menudo en proyectos internos al

ÿ comienzo del ciclo de desarrollo. Dado que puede seguir empujando a la rama del tema incluso después de
abrir la solicitud de extracción, a menudo se abre temprano y se usa como una forma de iterar en el trabajo como
equipo dentro de un contexto, en lugar de abrirse al final del proceso.

Iterando en una solicitud de extracción

En este punto, el propietario del proyecto puede ver el cambio sugerido y fusionarlo, rechazarlo o comentarlo. Digamos que le gusta la
idea, pero preferiría un tiempo un poco más largo para que la luz esté apagada que encendida.

177
Machine Translated by Google

Donde esta conversación puede tener lugar por correo electrónico en los flujos de trabajo presentados en Git distribuido, en
GitHub esto sucede en línea. El propietario del proyecto puede revisar la diferencia unificada y dejar un comentario haciendo
clic en cualquiera de las líneas.

Figura 92. Comentar una línea de código específica en una solicitud de extracción

Una vez que el mantenedor hace este comentario, la persona que abrió la solicitud de extracción (y, de hecho, cualquier otra
persona que vea el repositorio) recibirá una notificación. Veremos cómo personalizar esto más adelante, pero si tuviera
activadas las notificaciones por correo electrónico, Tony recibiría un correo electrónico como este:

Figura 93. Comentarios enviados como notificaciones por correo electrónico

Cualquiera también puede dejar comentarios generales sobre la solicitud de extracción. En la página de discusión de Solicitud
de extracción podemos ver un ejemplo del propietario del proyecto comentando una línea de código y luego dejando un
comentario general en la sección de discusión. Puede ver que los comentarios del código también se incorporan a la
conversación.

178
Machine Translated by Google

Figura 94. Página de discusión de solicitud de extracción

Ahora el contribuyente puede ver lo que debe hacer para que se acepte su cambio. Por suerte, esto es muy
sencillo. Donde por correo electrónico puede que tenga que volver a lanzar su serie y volver a enviarla a la lista de
correo, con GitHub simplemente se compromete con la rama del tema nuevamente y presiona, lo que actualizará
automáticamente la solicitud de extracción. En la solicitud de extracción final , también puede ver que el comentario
del código anterior se ha colapsado en la solicitud de extracción actualizada, ya que se hizo en una línea que se
modificó desde entonces.

Agregar confirmaciones a una solicitud de extracción existente no activa una notificación, por lo que una vez que
Tony ha enviado sus correcciones, decide dejar un comentario para informar al propietario del proyecto que realizó
el cambio solicitado.

179
Machine Translated by Google

Figura 95. Solicitud de extracción final

Una cosa interesante a tener en cuenta es que si hace clic en la pestaña "Archivos modificados" en esta solicitud de
extracción, obtendrá la diferencia "unificada", es decir, la diferencia agregada total que se introduciría en su rama
principal si este tema branch se fusionó. En términos de git diff , básicamente muestra automáticamente tu git diff
master…<branch> para la rama en la que se basa esta solicitud de extracción. Consulte Determinar qué se introduce
para obtener más información sobre este tipo de diferencia.

La otra cosa que notará es que GitHub verifica si la solicitud de extracción se fusiona limpiamente y proporciona un
botón para realizar la fusión por usted en el servidor. Este botón solo aparece si tiene acceso de escritura al
repositorio y es posible una combinación trivial. Si hace clic en él, GitHub realizará una combinación de "no avance
rápido", lo que significa que incluso si la combinación podría ser un avance rápido, aún creará una confirmación de
combinación.

Si lo prefiere, simplemente puede bajar la rama y fusionarla localmente. Si fusionas esto

180
Machine Translated by Google

ramificarse en la rama maestra y enviarla a GitHub, la solicitud de extracción se cerrará automáticamente.

Este es el flujo de trabajo básico que utilizan la mayoría de los proyectos de GitHub. Se crean ramas de temas, se abren solicitudes
de extracción en ellas, se produce una discusión, posiblemente se realiza más trabajo en la rama y, finalmente, la solicitud se cierra o
se fusiona.

No solo horquillas

Es importante tener en cuenta que también puede abrir una solicitud de extracción entre dos sucursales en el

ÿ mismo repositorio. Si está trabajando en una función con alguien y ambos tienen acceso de escritura al proyecto,
puede enviar una rama de tema al repositorio y abrir una solicitud de extracción en la rama principal de ese
mismo proyecto para iniciar la revisión del código y proceso de discusión. No es necesario bifurcar.

Solicitudes de extracción avanzadas

Ahora que hemos cubierto los conceptos básicos de contribuir a un proyecto en GitHub, cubramos algunos consejos y trucos
interesantes sobre las solicitudes de incorporación de cambios para que pueda ser más efectivo al usarlas.

Solicitudes de extracción como parches

Es importante comprender que muchos proyectos realmente no piensan en las solicitudes de incorporación de cambios como colas de
parches perfectos que deben aplicarse limpiamente en orden, ya que la mayoría de los proyectos basados en listas de correo piensan
en contribuciones de series de parches. La mayoría de los proyectos de GitHub piensan en las ramas de solicitud de extracción como
conversaciones iterativas en torno a un cambio propuesto, que culmina en una diferencia unificada que se aplica mediante la fusión.

Esta es una distinción importante, porque generalmente se sugiere el cambio antes de que se considere que el código es perfecto, lo
que es mucho más raro con las contribuciones de series de parches basadas en listas de correo. Esto permite una conversación más
temprana con los mantenedores para que llegar a la solución adecuada sea más un esfuerzo de la comunidad. Cuando se propone
código con una solicitud de extracción y los mantenedores o la comunidad sugieren un cambio, la serie de parches generalmente no
se vuelve a implementar, sino que la diferencia se envía como una nueva confirmación a la rama, lo que hace que la conversación
avance con el contexto de la obra anterior intacta.

Por ejemplo, si regresa y mira de nuevo la solicitud de extracción final, notará que el colaborador no volvió a basar su compromiso y
envió otra solicitud de extracción. En su lugar, agregaron nuevas confirmaciones y las empujaron a la rama existente. De esta manera,
si regresa y observa esta solicitud de extracción en el futuro, puede encontrar fácilmente todo el contexto de por qué se tomaron las
decisiones. Al presionar el botón "Combinar" en el sitio, se crea a propósito una confirmación de combinación que hace referencia a la
solicitud de extracción para que sea fácil regresar e investigar la conversación original si es necesario.

Mantenerse al día con Upstream

Si su solicitud de extracción se vuelve obsoleta o no se fusiona limpiamente, querrá arreglarla para que el mantenedor pueda fusionarla
fácilmente. GitHub probará esto por ti y te informará en la parte inferior de cada solicitud de extracción si la fusión es trivial o no.

181
Machine Translated by Google

Figura 96. La solicitud de extracción no se fusiona limpiamente

Si ve algo como que la solicitud de extracción no se fusiona limpiamente, querrá arreglar su rama para que se vuelva verde y
el mantenedor no tenga que hacer trabajo extra.

Tienes dos opciones principales para hacer esto. Puede cambiar la base de su rama encima de la rama de destino
(normalmente la rama maestra del repositorio que bifurcó), o puede fusionar la rama de destino en su rama.

La mayoría de los desarrolladores en GitHub elegirán hacer lo último, por las mismas razones que acabamos de mencionar
en la sección anterior. Lo que importa es el historial y la fusión final, por lo que el rebase no le brinda mucho más que un
historial ligeramente más limpio y, a cambio, es mucho más difícil y propenso a errores.

Si desea fusionarse en la rama de destino para hacer que su Solicitud de extracción se pueda fusionar, agregaría el repositorio
original como un nuevo control remoto, buscaría en él, fusionaría la rama principal de ese repositorio en su rama de tema,
solucionaría cualquier problema y finalmente lo impulsaría. copia de seguridad en la misma sucursal en la que abrió la solicitud
de extracción.

Por ejemplo, digamos que en el ejemplo de "tonychacon" que estábamos usando antes, el autor original hizo un cambio que
crearía un conflicto en la solicitud de extracción. Vayamos a través de esos pasos.

182
Machine Translated by Google

$ git remote add upstream https://github.com/schacon/blink ÿ

$ git fetch upstream ÿ


remoto: Contar objetos: 3, listo. remoto:
Comprimir objetos: 100% (3/3), hecho.
Desembalaje de objetos: 100% (3/3), hecho.
remoto: Total 3 (delta 0), reutilizado 0 (delta 0)
Desde https://github.com/schacon/blink *
Maestro
[nueva rama] -> upstream/master

$ git merge upstream/master ÿ


CONFLICTO de fusión automática
de blink.ino (contenido): Conflicto de fusión en blink.ino
Fusión automática fallida; solucione los conflictos y luego confirme el resultado.

$ vim blink.ino ÿ $ git


add blink.ino $ git
commit [parpadeo lento
3c8d735] Combinar rama de seguimiento remoto 'upstream/master' \ en parpadeo
más lento

$ git push origin parpadeo lento ÿ


Contar objetos: 6, listo.
Compresión delta usando hasta 8 hilos.
Comprimir objetos: 100% (6/6), hecho.
Objetos de escritura: 100% (6/6), 682 bytes | 0 bytes/s, listo.
Total 6 (delta 2), reutilizado 0 (delta 0)
A https://github.com/tonychacon/blink
ef4725c..3c8d735 parpadeo lento -> parpadeo lento

ÿ Agregue el repositorio original como un nombre ascendente remoto.

ÿ Obtenga el trabajo más nuevo desde ese control remoto.

ÿ Combine la rama principal de ese repositorio en su rama temática.

ÿ Solucionar el conflicto que ocurrió.

ÿ Vuelva a subir a la misma rama del tema.

Una vez que haga eso, la solicitud de extracción se actualizará automáticamente y se volverá a verificar para ver si se fusiona
limpiamente.

183
Machine Translated by Google

Figura 97. La solicitud de extracción ahora se fusiona limpiamente

Una de las mejores cosas de Git es que puedes hacer eso continuamente. Si tiene un proyecto de larga duración, puede
fusionarse fácilmente desde la rama de destino una y otra vez y solo tiene que lidiar con los conflictos que surgieron desde la
última vez que se fusionó, lo que hace que el proceso sea muy manejable.

Si absolutamente desea cambiar la base de la rama para limpiarla, sin duda puede hacerlo, pero se recomienda enfáticamente
no forzar el empuje sobre la rama en la que ya está abierta la solicitud de extracción. Si otras personas lo han eliminado y han
trabajado más en él, se encontrará con todos los problemas descritos en The Perils of Rebasing. En su lugar, inserte la rama
reorganizada en una nueva rama en GitHub y abra una nueva solicitud de extracción que haga referencia a la anterior, luego
cierre la original.

Referencias

Su próxima pregunta puede ser "¿Cómo hago referencia a la solicitud de extracción anterior?". Resulta que hay muchas, muchas
formas de hacer referencia a otras cosas en casi cualquier lugar donde puedas escribir en GitHub.

Comencemos con cómo hacer una referencia cruzada de otra solicitud de extracción o un problema. A todas las solicitudes de
extracción y problemas se les asignan números y son únicos dentro del proyecto. Por ejemplo, no puede tener Solicitud de
extracción n.° 3 y Problema n.° 3. Si desea hacer referencia a cualquier Solicitud de extracción o Problema de cualquier otro,
simplemente puede poner #<num> en cualquier comentario o descripción. También puede ser más específico si la solicitud de
emisión o extracción se encuentra en otro lugar; escribe nombredeusuario#<num> si te refieres a un problema o solicitud de
extracción en una bifurcación del repositorio en el que te encuentras, o nombre de usuario/repo#<num> para hacer referencia a
algo en otro repositorio.

Veamos un ejemplo. Digamos que modificamos la base de la rama en el ejemplo anterior, creamos una nueva solicitud de
extracción para ella y ahora queremos hacer referencia a la solicitud de extracción antigua a partir de la nueva. También
queremos hacer referencia a un problema en la bifurcación del repositorio y un problema en un proyecto completamente diferente.
Podemos completar la descripción al igual que las referencias cruzadas en una solicitud de extracción.

184
Machine Translated by Google

Figura 98. Referencias cruzadas en una solicitud de extracción

Cuando enviemos esta solicitud de extracción, veremos todo eso representado como referencias cruzadas representadas en una
solicitud de extracción.

Figura 99. Referencias cruzadas representadas en una solicitud de extracción

Tenga en cuenta que la URL completa de GitHub que pusimos allí se acortó a solo la información necesaria.

Ahora, si Tony regresa y cierra la solicitud de extracción original, podemos ver que al mencionarla en la nueva, GitHub ha creado
automáticamente un evento de seguimiento en la línea de tiempo de la solicitud de extracción. Esto significa que cualquier persona
que visite esta Solicitud de extracción y vea que está cerrada puede vincularse fácilmente a la que la reemplazó. El enlace se parecerá
a Enlace de regreso a la nueva solicitud de extracción en la línea de tiempo cerrada de la solicitud de extracción.

185
Machine Translated by Google

Figura 100. Vínculo de regreso a la nueva solicitud de extracción en la línea de tiempo cerrada de la solicitud de extracción

Además de los números de emisión, también puede hacer referencia a una confirmación específica de SHA-1. Tienes que
especificar un SHA-1 completo de 40 caracteres, pero si GitHub lo ve en un comentario, se vinculará directamente a la
confirmación. Nuevamente, puede hacer referencia a confirmaciones en bifurcaciones u otros repositorios de la misma manera
que lo hizo con los problemas.

Rebajas con sabor a GitHub

Vincular a otros problemas es solo el comienzo de cosas interesantes que puede hacer con casi cualquier cuadro de texto en
GitHub. En las descripciones, comentarios, comentarios de código y más de problemas y solicitudes de extracción, puede usar
lo que se denomina "rebaja con sabor de GitHub". Markdown es como escribir en texto sin formato pero que se representa de
forma rica.

Consulte Un ejemplo de Markdown con sabor de GitHub tal como está escrito y representado para ver un ejemplo de cómo se
pueden escribir comentarios o texto y luego representar mediante Markdown.

Figura 101. Un ejemplo de GitHub Flavored Markdown tal como está escrito y representado

El sabor GitHub de Markdown agrega más cosas que puede hacer más allá de la sintaxis básica de Markdown.
Todos estos pueden ser realmente útiles al crear comentarios o descripciones útiles de solicitudes de extracción o problemas.

186
Machine Translated by Google

Listas de tareas

La primera función Markdown específica de GitHub realmente útil, especialmente para usar en solicitudes de extracción, es la Lista de tareas. Una

lista de tareas es una lista de casillas de verificación de cosas que desea hacer. Ponerlos en un problema o solicitud de extracción normalmente indica

cosas que desea hacer antes de considerar que el elemento está completo.

Puede crear una lista de tareas como esta:

- [X] Escribe el código


- [ ] Escribe todas las pruebas
- [ ] Documentar el código

Si incluimos esto en la descripción de nuestra Solicitud de extracción o Problema, lo veremos representado como Listas de tareas representadas en
un comentario de Markdown.

Figura 102. Listas de tareas representadas en un comentario de Markdown

Esto se usa a menudo en las solicitudes de extracción para indicar todo lo que le gustaría hacer en la rama antes de que la solicitud de extracción esté

lista para fusionarse. La parte realmente interesante es que simplemente puede hacer clic en las casillas de verificación para actualizar el comentario;

no tiene que editar Markdown directamente para marcar las tareas.

Además, GitHub buscará listas de tareas en sus problemas y solicitudes de extracción y las mostrará como metadatos en las páginas que las

enumeran. Por ejemplo, si tiene una solicitud de extracción con tareas y mira la página de descripción general de todas las solicitudes de extracción,

puede ver qué tan avanzado está. Esto ayuda a las personas a dividir las solicitudes de extracción en subtareas y ayuda a otras personas a realizar

un seguimiento del progreso de la rama.

Puede ver un ejemplo de esto en el resumen de la lista de tareas en la lista de solicitud de extracción.

Figura 103. Resumen de la lista de tareas en la lista de solicitud de extracción

Estos son increíblemente útiles cuando abre una solicitud de extracción antes y la usa para realizar un seguimiento de su progreso a través de la

implementación de la función.

187
Machine Translated by Google

Fragmentos de código

También puede agregar fragmentos de código a los comentarios. Esto es especialmente útil si desea presentar algo que
podría intentar hacer antes de implementarlo como una confirmación en su rama.
Esto también se usa a menudo para agregar un código de ejemplo de lo que no funciona o lo que podría implementar esta solicitud
de extracción.

Para agregar un fragmento de código, debe "cerrarlo" con acentos graves.

```java
for(int i=0 ; yo < 5 ; i++)

"
{ System.out.println("i is : } + yo);

```

Si agrega un nombre de idioma como lo hicimos allí con 'java', GitHub también intentará resaltar la sintaxis del fragmento. En
el caso del ejemplo anterior, terminaría renderizándose como el ejemplo de código delimitado renderizado.

Figura 104. Ejemplo de código delimitado renderizado

citando

Si está respondiendo a una pequeña parte de un comentario largo, puede citar selectivamente el otro comentario precediendo
las líneas con el carácter > . De hecho, esto es tan común y tan útil que existe un atajo de teclado para ello. Si resalta el texto
en un comentario al que desea responder directamente y presiona la tecla r , citará ese texto en el cuadro de comentarios
por usted.

Las citas se ven así:

> Si es más noble en la mente sufrir


> Las hondas y las flechas de la fortuna ultrajante,

¿Qué tamaño tienen estas hondas y, en particular, estas flechas?

Una vez renderizado, el comentario se verá como un ejemplo de cita renderizado.

188
Machine Translated by Google

Figura 105. Ejemplo de cotización renderizada

emoticonos

Finalmente, también puedes usar emoji en tus comentarios. En realidad, esto se usa bastante en los
comentarios que ve en muchos problemas de GitHub y solicitudes de extracción. Incluso hay un asistente
de emoji en GitHub. Si está escribiendo un comentario y comienza con un carácter : , un autocompletador
lo ayudará a encontrar lo que está buscando.

Figura 106. Autocompletador de emojis en acción

Los emojis toman la forma de :<nombre>: en cualquier parte del comentario. Por ejemplo, podrías escribir
algo como esto:

189
Machine Translated by Google

Yo :ojos: ese :bicho: y yo :cold_sweat:.

:trofeo: para :microscopio: es.

:+1: y :sparkles: en este :barco:, ¡es :fuego::caca:!

:aplausos::tada::panda_face:

Cuando se represente, se vería como un emoji pesado comentando.

Figura 107. Emoji pesado comentando

No es que esto sea increíblemente útil, pero agrega un elemento de diversión y emoción a un medio en el que de otro modo
sería difícil transmitir emoción.

En realidad, hay una gran cantidad de servicios web que utilizan caracteres emoji en estos días. Puede
encontrar una gran hoja de referencia para encontrar emoji que exprese lo que quiere decir en:
ÿ
https://www.webfx.com/tools/emoji-cheat-sheet/

Imágenes

Esto no es técnicamente un Markdown con sabor a GitHub, pero es increíblemente útil. Además de agregar enlaces de imágenes
de Markdown a los comentarios, que pueden ser difíciles de encontrar e incrustar URL, GitHub le permite arrastrar y soltar
imágenes en áreas de texto para incrustarlas.

190
Machine Translated by Google

Figura 108. Arrastre y suelte imágenes para cargarlas y autoincrustarlas

Si observa Arrastrar y soltar imágenes para cargarlas e incrustarlas automáticamente, puede ver una pequeña
sugerencia de "Análisis como Markdown" sobre el área de texto. Al hacer clic en eso, obtendrá una hoja de trucos
completa de todo lo que puede hacer con Markdown en GitHub.

Mantenga actualizado su repositorio público de GitHub

Una vez que haya bifurcado un repositorio de GitHub, su repositorio (su "bifurcación") existe independientemente
del original. En particular, cuando el repositorio original tiene nuevas confirmaciones, GitHub te informa mediante
un mensaje como:

Esta rama está 5 confirmaciones detrás de progit:master.

Pero su repositorio de GitHub nunca será actualizado automáticamente por GitHub; esto es algo que debe hacer
usted mismo. Afortunadamente, esto es muy fácil de hacer.

Una posibilidad de hacer esto no requiere configuración. Por ejemplo, si bifurcó desde https://github.com/progit/
progit2.git, puede mantener su rama maestra actualizada de esta manera:

$ git checkout master ÿ $ git


pull https://github.com/progit/progit2.git ÿ $ git push origin
master ÿ

ÿ Si estaba en otra rama, regrese al maestro.

191
Machine Translated by Google

ÿ Obtener cambios de https://github.com/progit/progit2.git y fusionarlos en maestro.

ÿ Empuje su rama maestra al origen.

Esto funciona, pero es un poco tedioso tener que deletrear la URL de búsqueda cada vez. Puede automatizar este trabajo con
un poco de configuración:

$ git remote add progit https://github.com/progit/progit2.git ÿ $ git


fetch progit ÿ $ git branch --set-upstream-to=progit/master master ÿ
$ git config --local remote.pushDefault origin ÿ

ÿ Agregue el repositorio de origen y asígnele un nombre. Aquí, he elegido llamarlo progit.

ÿ Obtenga una referencia sobre las ramas de progit, en particular, master.

ÿ Configure su rama maestra para buscar desde el control remoto progit .

ÿ Defina el repositorio de envío predeterminado al origen.

Una vez hecho esto, el flujo de trabajo se vuelve mucho más simple:

$ git checkout master ÿ $


git pull ÿ $ git push ÿ

ÿ Si estaba en otra rama, regrese al maestro.

ÿ Obtener cambios de progit y fusionar cambios en maestro.

ÿ Empuje su rama maestra al origen.

Este enfoque puede ser útil, pero no está exento de inconvenientes. Git felizmente hará este trabajo por ti en silencio, pero no
te avisará si haces una confirmación para dominar, extraer de progit y luego enviar al origen ; todas esas operaciones son
válidas con esta configuración. Por lo tanto, deberá tener cuidado de nunca comprometerse directamente con el maestro, ya
que esa rama pertenece efectivamente al repositorio ascendente.

Mantenimiento de un proyecto
Ahora que nos sentimos cómodos contribuyendo a un proyecto, veamos el otro lado: crear, mantener y administrar su propio
proyecto.

Creación de un nuevo repositorio

Vamos a crear un nuevo repositorio para compartir el código de nuestro proyecto. Comience haciendo clic en el botón "Nuevo
repositorio" en el lado derecho del tablero, o desde el botón + en la barra de herramientas superior al lado de su nombre de
usuario como se ve en el menú desplegable "Nuevo repositorio".

192
Machine Translated by Google

Figura 109. El área “Tus repositorios”

Figura 110. El menú desplegable “Nuevo repositorio”

Esto lo lleva al formulario de "nuevo repositorio":

193
Machine Translated by Google

Figura 111. El formulario “nuevo repositorio”

Todo lo que realmente tiene que hacer aquí es proporcionar un nombre de proyecto; el resto de los campos son completamente
opcionales. Por ahora, simplemente haga clic en el botón "Crear repositorio" y listo: tiene un nuevo repositorio en GitHub, llamado
<usuario>/<nombre_del_proyecto>.

Dado que aún no tiene ningún código allí, GitHub le mostrará instrucciones sobre cómo crear un nuevo repositorio de Git o conectar
un proyecto de Git existente. No profundizaremos en esto aquí; si necesita un repaso, consulte Conceptos básicos de Git.

Ahora que su proyecto está alojado en GitHub, puede proporcionar la URL a cualquier persona con la que desee compartir su
proyecto. Se puede acceder a cada proyecto en GitHub a través de HTTPS como https://github.com/<user>/<project_name>,
través de SSH como git@github.com:<user>/
ya
<project_name>.
Git puede buscar y enviar a estas dos URL, pero tienen un control de acceso basado en las credenciales del usuario que se conecta
a ellas.

A menudo, es preferible compartir la URL basada en HTTPS para un proyecto público, ya que el usuario no
necesita tener una cuenta de GitHub para acceder a ella para la clonación. Los usuarios deberán tener una

ÿ cuenta y una clave SSH cargada para acceder a su proyecto si les proporciona la URL de SSH. El HTTPS
también es exactamente la misma URL que pegarían en un navegador para ver el proyecto allí.

Agregar colaboradores
Si está trabajando con otras personas a las que desea otorgar acceso de confirmación, debe agregarlas como "colaboradores". Si
Ben, Jeff y Louise se registran para obtener cuentas en GitHub y desea otorgarles acceso automático a su repositorio, puede
agregarlos a su proyecto. Si lo hace, les dará acceso "push", lo que significa que tienen acceso de lectura y escritura al proyecto y al
repositorio de Git.

Haga clic en el enlace "Configuración" en la parte inferior de la barra lateral derecha.

194
Machine Translated by Google

Figura 112. El enlace de configuración del repositorio

Luego seleccione "Colaboradores" en el menú del lado izquierdo. Luego, simplemente escriba un nombre de usuario
en el cuadro y haga clic en "Agregar colaborador". Puedes repetir esto tantas veces como quieras para otorgar acceso
a todos los que quieras. Si necesita revocar el acceso, simplemente haga clic en la "X" en el lado derecho de su
fila.

Figura 113. Colaboradores del repositorio

Gestión de solicitudes de extracción

Ahora que tiene un proyecto con algo de código y tal vez incluso algunos colaboradores que también tienen acceso
push, repasemos qué hacer cuando usted mismo recibe una solicitud de extracción.

Las solicitudes de extracción pueden provenir de una rama en una bifurcación de su repositorio o pueden provenir de
otra rama en el mismo repositorio. La única diferencia es que los que están en una bifurcación a menudo son de
personas a las que no puedes enviar a su rama y ellos no pueden enviar a la tuya, mientras que con las solicitudes de
extracción internas generalmente ambas partes pueden acceder a la rama.

195
Machine Translated by Google

Para estos ejemplos, supongamos que usted es "tonychacon" y ha creado un nuevo proyecto de código Arduino llamado
"fade".

Notificaciónes de Correo Electrónico

Alguien viene, hace un cambio en su código y le envía una solicitud de extracción. Debería recibir un correo electrónico
notificándole sobre la nueva solicitud de extracción y debería parecerse a una notificación por correo electrónico de una
nueva solicitud de extracción.

Figura 114. Notificación por correo electrónico de una nueva solicitud de extracción

Hay algunas cosas a tener en cuenta sobre este correo electrónico. Le dará un pequeño diffstat: una lista de archivos que
han cambiado en la solicitud de extracción y cuánto. Le da un enlace a la solicitud de extracción en GitHub. También le
brinda algunas URL que puede usar desde la línea de comandos.

Si observa la línea que dice git pull <url> parche-1, esta es una forma sencilla de fusionarse en una rama remota sin tener
que agregar un control remoto. Repasamos esto rápidamente en Verificación de sucursales remotas. Si lo desea, puede
crear y cambiar a una rama de tema y luego ejecutar este comando para fusionar los cambios de Solicitud de extracción.

Las otras URL interesantes son las URL .diff y .patch , que, como puede suponer, proporcionan versiones unificadas de
diferencias y parches de la solicitud de incorporación de cambios. Técnicamente, podría fusionarse en el trabajo de solicitud
de extracción con algo como esto:

$ curl https://github.com/tonychacon/fade/pull/1.patch | git soy

196
Machine Translated by Google

Colaborando en la solicitud de extracción

Como cubrimos en The GitHub Flow, ahora puede tener una conversación con la persona que abrió la solicitud de extracción.
Puede comentar líneas específicas de código, comentar confirmaciones completas o comentar toda la solicitud de extracción
en sí misma, utilizando GitHub Flavored Markdown en todas partes.

Cada vez que alguien más comenta sobre la solicitud de extracción, seguirás recibiendo notificaciones por correo electrónico
para que sepas que hay actividad. Cada uno tendrá un enlace a la solicitud de extracción donde se está realizando la
actividad y también puede responder directamente al correo electrónico para comentar en el hilo de la solicitud de extracción.

Figura 115. Las respuestas a los correos electrónicos se incluyen en el hilo

Una vez que el código está en el lugar que le gusta y desea fusionarlo, puede extraer el código y fusionarlo localmente, ya
sea con la sintaxis de git pull <url> <branch> que vimos anteriormente, o agregando la bifurcación como un control remoto
y buscar y fusionar.

Si la combinación es trivial, también puede presionar el botón "Combinar" en el sitio de GitHub. Esto hará una fusión de "no
avance rápido", creando una confirmación de fusión incluso si fuera posible una fusión de avance rápido. Esto significa que
pase lo que pase, cada vez que presiona el botón de combinación, se crea una confirmación de combinación. Como puede
ver en el botón Fusionar y las instrucciones para fusionar una solicitud de extracción manualmente, GitHub le brinda toda
esta información si hace clic en el enlace de sugerencia.

197
Machine Translated by Google

Figura 116. Botón Fusionar e instrucciones para fusionar una solicitud de extracción manualmente

Si decide que no desea fusionarlo, también puede simplemente cerrar la solicitud de extracción y se notificará a la
persona que la abrió.

Referencias de solicitud de extracción

Si está lidiando con muchas solicitudes de extracción y no quiere agregar un montón de controles remotos o hacer
extracciones únicas cada vez, hay un buen truco que GitHub le permite hacer. Este es un truco un poco avanzado y
repasaremos los detalles un poco más en The Refspec, pero puede ser bastante útil.

GitHub en realidad anuncia las ramas de solicitud de extracción para un repositorio como una especie de pseudo-
ramas en el servidor. De forma predeterminada, no los obtiene cuando clona, pero están ocultos y puede acceder a
ellos con bastante facilidad.

Para demostrar esto, vamos a utilizar un comando de bajo nivel (a menudo denominado comando de "plomería", sobre
el cual leeremos más en Plomería y porcelana) llamado ls-remote. Este comando generalmente no se usa en las
operaciones diarias de Git, pero es útil para mostrarnos qué referencias están presentes en el servidor.

Si ejecutamos este comando contra el repositorio "parpadeo" que estábamos usando anteriormente, obtendremos una
lista de todas las ramas y etiquetas y otras referencias en el repositorio.

198
Machine Translated by Google

$ git ls-remote https://github.com/schacon/blink


10d539600d86723087810ec636870a504f4fee4d CABEZA
10d539600d86723087810ec636870a504f4fee4d refs/heads/master
6a83107c62950be9453aac297bb0193fd743cd6e refs/pull/1/head refs/
afe83c2d1a70674c9505cc1d8b7d380d5e076ed3 pull/1/merge refs/
3c8d735ee16296c242be7a9742ebfbc2665adec1 pull/2/head refs/pull/
15c9f4f80973a2758462ab2066b6ad9fe8dcf03d 2/merge refs/pull/4/
a5a7751a33b7e86c5e9bb07b26001bb17d775d1a head refs/pull/4/
31a45fc257e8433c8d8804e3e848cf61c9d3166c merge

Por supuesto, si está en su repositorio y ejecuta git ls-remote origin o cualquier control remoto que desee verificar, le
mostrará algo similar a esto.

Si el repositorio está en GitHub y tiene solicitudes de extracción abiertas, obtendrá estas referencias que tienen el prefijo
refs/pull/. Estas son básicamente ramas, pero dado que no están bajo referencias/cabezas/ no las obtienes normalmente
cuando clonas o obtienes del servidor; el proceso de obtención las ignora normalmente.

Hay dos referencias por solicitud de extracción: la que termina en /head apunta exactamente a la misma confirmación
que la última confirmación en la rama de solicitud de extracción. Entonces, si alguien abre una solicitud de extracción en
nuestro repositorio y su rama se llama corrección de errores y apunta a confirmar a5a775, entonces en nuestro
repositorio no tendremos una rama de corrección de errores (ya que está en su bifurcación), pero tendremos pull/<pr#>/
head que apunta a a5a775. Esto significa que podemos extraer fácilmente todas las ramas de solicitud de extracción de
una sola vez sin tener que agregar un montón de controles remotos.

Ahora, podría hacer algo como obtener la referencia directamente.

$ git buscar origen refs/pull/958/head De


https://github.com/libgit2/libgit2 * branch refs/
pull/958/head -> FETCH_HEAD

Esto le dice a Git: "Conéctese al control remoto de origen y descargue la referencia llamada refs/pull/958/head". Git
obedece felizmente y descarga todo lo que necesita para construir esa referencia, y pone un puntero a la confirmación
que desea en .git/FETCH_HEAD. Puedes seguir eso con git merge FETCH_HEAD en una rama en la que quieras
probarlo, pero ese mensaje de confirmación de combinación parece un poco raro. Además, si está revisando muchas
solicitudes de extracción, esto se vuelve tedioso.

También hay una manera de obtener todas las solicitudes de extracción y mantenerlas actualizadas cada vez que se
conecta al control remoto. Abra .git/config en su editor favorito y busque el control remoto de origen . Debería verse un
poco como esto:

["origen" remoto] url


= https://github.com/libgit2/libgit2 fetch = +refs/heads/
*:refs/remotes/origin/*

Esa línea que comienza con fetch = es una "refspec". Es una forma de asignar nombres en el control remoto con nombres
en su directorio local .git . Este en particular le dice a Git, "las cosas en el control remoto que son

199
Machine Translated by Google

bajo refs/heads debe ir a mi repositorio local bajo refs/remotes/origin". Puede modificar esta sección para agregar otra refspec:

["origen" remoto] url =


https://github.com/libgit2/libgit2.git fetch = +refs/heads/*:refs/
remotes/origin/* fetch = +refs/pull/*/head:refs/ remotos/origen/pr/
*

Esa última línea le dice a Git: "Todas las referencias que parecen refs/pull/123/head deben almacenarse localmente como refs/
remotes/origin/pr/123". Ahora, si guardas ese archivo y haces un git fetch:

$ git fetch # …
* [nueva
referencia] * refs/pull/1/cabeza -> origen/pr/1 refs/pull/
[nueva referencia] 2/cabeza -> origen/pr/2 refs/pull/4/cabeza
* [nueva -> origen/pr/4
referencia] # …

Ahora todas las solicitudes de extracción remotas se representan localmente con referencias que actúan de manera muy similar a
las ramas de seguimiento; son de solo lectura y se actualizan cuando haces una recuperación. Esto hace que sea muy fácil probar
el código de una solicitud de extracción localmente:

$ git checkout pr/2


Comprobación de archivos: 100 % (3769/3769), listo.
Sucursal pr/2 configurada para rastrear la sucursal remota pr/2 desde el origen.
Cambiado a una nueva sucursal 'pr/2'

Los ojos de águila entre ustedes notarían la cabeza en el extremo de la parte remota de la refspec.
También hay una refs/pull/#/merge ref en el lado de GitHub, que representa la confirmación que resultaría si presiona el botón
"merge" en el sitio. Esto puede permitirle probar la combinación incluso antes de presionar el botón.

Solicitudes de extracción en solicitudes de extracción

No solo puede abrir solicitudes de extracción dirigidas a la rama principal o principal , sino que también puede abrir una solicitud de
extracción dirigida a cualquier sucursal de la red. De hecho, incluso puede apuntar a otra solicitud de extracción.

Si ve una solicitud de extracción que se está moviendo en la dirección correcta y tiene una idea para un cambio que depende de ella
o no está seguro de que sea una buena idea, o simplemente no tiene acceso de inserción a la rama de destino, puede abrir una
solicitud de extracción directamente en él.

Cuando va a abrir una Solicitud de extracción, hay un cuadro en la parte superior de la página que especifica a qué rama está
solicitando extraer y de cuál está solicitando extraer. Si presiona el botón "Editar" a la derecha de ese cuadro, puede cambiar no solo
las ramas sino también qué bifurcación.

200
Machine Translated by Google

Figura 117. Cambiar manualmente la bifurcación y la rama de destino de la solicitud de extracción

Aquí puede especificar con bastante facilidad fusionar su nueva rama en otra solicitud de extracción u otra bifurcación del proyecto.

Menciones y Notificaciones

GitHub también tiene un sistema de notificaciones bastante bueno incorporado que puede ser útil cuando tiene preguntas o necesita
comentarios de personas o equipos específicos.

En cualquier comentario puede comenzar a escribir un carácter @ y comenzará a autocompletarse con los nombres y nombres de
usuario de las personas que son colaboradores o contribuyentes en el proyecto.

Figura 118. Comience a escribir @ para mencionar a alguien

También puede mencionar a un usuario que no está en ese menú desplegable, pero a menudo el autocompletador puede hacerlo
más rápido.

Una vez que publique un comentario con una mención de usuario, ese usuario será notificado. Esto significa que esta puede ser una
forma realmente efectiva de atraer a las personas a las conversaciones en lugar de hacer que voten. Muy a menudo, en las solicitudes
de extracción en GitHub, las personas atraerán a otras personas de sus equipos o de su empresa para revisar un problema o una
solicitud de extracción.

201
Machine Translated by Google

Si se menciona a alguien en una Solicitud de extracción o un Problema, se "suscribirá" y continuará recibiendo


notificaciones cada vez que ocurra alguna actividad en él. También estarás suscrito a algo si lo abriste, si estás
viendo el repositorio o si comentas algo. Si ya no desea recibir notificaciones, hay un botón "Cancelar suscripción"
en la página en el que puede hacer clic para dejar de recibir actualizaciones.

Figura 119. Darse de baja de un problema o solicitud de extracción

La página de notificaciones

Cuando mencionamos "notificaciones" aquí con respecto a GitHub, nos referimos a una forma específica en la
que GitHub intenta ponerse en contacto con usted cuando ocurren eventos y hay algunas formas diferentes de
configurarlos. Si va a la pestaña "Centro de notificaciones" desde la página de configuración, puede ver algunas
de las opciones que tiene.

202
Machine Translated by Google

Figura 120. Opciones del centro de notificaciones

Las dos opciones son recibir notificaciones por "Correo electrónico" y por "Web" y puede elegir una, ninguna o
ambas para cuando participa activamente en las cosas y para la actividad en los repositorios que está viendo.

Notificaciones web

Las notificaciones web solo existen en GitHub y solo puede consultarlas en GitHub. Si tiene esta opción
seleccionada en sus preferencias y se activa una notificación para usted, verá un pequeño punto azul sobre el
ícono de notificaciones en la parte superior de su pantalla como se ve en el Centro de notificaciones.

Figura 121. Centro de notificaciones

Si hace clic en eso, verá una lista de todos los elementos sobre los que ha sido notificado, agrupados por
proyecto. Puede filtrar las notificaciones de un proyecto específico haciendo clic en su nombre en la barra lateral
izquierda. También puede reconocer la notificación haciendo clic en el icono de marca de verificación junto a cualquier

203
Machine Translated by Google

notificación, o reconozca todas las notificaciones en un proyecto haciendo clic en la marca de verificación en la parte superior del grupo.
También hay un botón de silencio junto a cada marca de verificación en el que puede hacer clic para no recibir más notificaciones sobre
ese elemento.

Todas estas herramientas son muy útiles para manejar un gran número de notificaciones. Muchos usuarios avanzados de GitHub
simplemente desactivarán las notificaciones por correo electrónico por completo y administrarán todas sus notificaciones a través de esta
pantalla.

Notificaciónes de Correo Electrónico

Las notificaciones por correo electrónico son la otra forma en que puede manejar las notificaciones a través de GitHub. Si tiene esto
activado, recibirá correos electrónicos para cada notificación. Vimos ejemplos de esto en Comentarios enviados como notificaciones por
correo electrónico y Notificación por correo electrónico de una nueva Solicitud de extracción. Los correos electrónicos también se enhebrarán
correctamente, lo cual es bueno si está utilizando un cliente de correo electrónico de enhebrado.

También hay una buena cantidad de metadatos incrustados en los encabezados de los correos electrónicos que le envía GitHub, lo que

puede ser muy útil para configurar filtros y reglas personalizados.

Por ejemplo, si observamos los encabezados de correo electrónico reales enviados a Tony en el correo electrónico que se muestra en la
notificación por correo electrónico de una nueva solicitud de extracción, veremos lo siguiente entre la información enviada:

Para: tonychacon/fade <fade@noreply.github.com>


ID de mensaje: <tonychacon/fade/pull/1@github.com>
Asunto: [fade] Espere más para ver mejor el efecto de atenuación (#1)
X-GitHub-Recipient: tonychacon
List-ID: tonychacon/fade <fade.tonychacon.github.com> List-
Archive: https://github.com/tonychacon/fade List-Post:
<mailto:reply+i-4XXX @reply.github.com> Lista-Cancelar
suscripción: <mailto:unsub+i-XXX@reply.github.com>,...
X-GitHub-Recipient-Address: tchacon@example.com

Hay un par de cosas interesantes aquí. Si desea resaltar o redirigir correos electrónicos a este proyecto en particular o incluso a una
solicitud de extracción, la información en Message-ID le brinda todos los datos en formato <usuario>/<proyecto>/<tipo>/<id> . Si esto fuera
un problema, por ejemplo, el campo <tipo> habría sido "problemas" en lugar de "extracción".

Los campos List-Post y List-Unsubscribe significan que si tiene un cliente de correo que los entiende, puede publicar fácilmente en la lista o
"Cancelar suscripción" del hilo. Eso sería esencialmente lo mismo que hacer clic en el botón "silenciar" en la versión web de la notificación
o "Cancelar suscripción" en la página de Problema o Solicitud de extracción.

También vale la pena señalar que si tiene habilitadas las notificaciones web y de correo electrónico y lee la versión de correo electrónico de
la notificación, la versión web también se marcará como leída si tiene imágenes permitidas en su cliente de correo.

Archivos especiales

Hay un par de archivos especiales que GitHub notará si están presentes en tu repositorio.

204
Machine Translated by Google

LÉAME

El primero es el archivo README , que puede tener casi cualquier formato que GitHub reconozca como prosa. Por
ejemplo, podría ser README, README.md, README.asciidoc, etc. Si GitHub ve un archivo README en su fuente, lo
mostrará en la página de inicio del proyecto.

Muchos equipos usan este archivo para almacenar toda la información relevante del proyecto para alguien que podría ser
nuevo en el repositorio o proyecto. Esto generalmente incluye cosas como:

• Para qué es el proyecto

• Cómo configurarlo e instalarlo

• Un ejemplo de cómo usarlo o ponerlo en marcha

• La licencia bajo la cual se ofrece el proyecto

• Cómo contribuir a ello

Dado que GitHub procesará este archivo, puede incrustar imágenes o enlaces en él para una mayor facilidad de
comprensión.

CONTRIBUYENDO

El otro archivo especial que reconoce GitHub es el archivo CONTRIBUTING . Si tiene un archivo llamado CONTRIBUCIÓN
con cualquier extensión de archivo, GitHub mostrará Abriendo una solicitud de extracción cuando exista un archivo
CONTRIBUCIÓN cuando alguien comience a abrir una solicitud de extracción.

Figura 122. Apertura de una solicitud de extracción cuando existe un archivo CONTRIBUYENTE

La idea aquí es que puede especificar cosas específicas que desea o no desea en una solicitud de extracción enviada a
su proyecto. De esta manera, las personas pueden leer las pautas antes de abrir la solicitud de extracción.

Administración de proyecto

En general, no hay muchas cosas administrativas que pueda hacer con un solo proyecto, pero hay un par de elementos
que pueden ser de interés.

205
Machine Translated by Google

Cambiar la rama predeterminada

Si está utilizando una rama que no sea "maestra" como su rama predeterminada en la que desea que las personas abran
solicitudes de extracción o vean de forma predeterminada, puede cambiar eso en la página de configuración de su repositorio
en la pestaña "Opciones".

Figura 123. Cambiar la rama por defecto de un proyecto

Simplemente cambie la rama predeterminada en el menú desplegable y esa será la predeterminada para todas las operaciones
principales a partir de ese momento, incluida la rama que se verifica de forma predeterminada cuando alguien clona el repositorio.

Transferencia de un proyecto

Si desea transferir un proyecto a otro usuario u organización en GitHub, hay una opción "Transferir propiedad" en la parte inferior
de la misma pestaña "Opciones" de la página de configuración de su repositorio que le permite hacer esto.

Figura 124. Transferir un proyecto a otro usuario u organización de GitHub

Esto es útil si está abandonando un proyecto y alguien quiere hacerse cargo de él, o si su proyecto se está haciendo más grande
y quiere trasladarlo a una organización.

Esto no solo mueve el repositorio junto con todos sus observadores y estrellas a otro lugar, sino que también configura una
redirección desde su URL al nuevo lugar. También redirigirá los clones y las recuperaciones de Git, no solo las solicitudes web.

206
Machine Translated by Google

Administrar una organización


Además de las cuentas de usuario único, GitHub tiene lo que se denomina Organizaciones. Al igual que las cuentas
personales, las cuentas organizacionales tienen un espacio de nombres donde existen todos sus proyectos, pero muchas
otras cosas son diferentes. Estas cuentas representan un grupo de personas con propiedad compartida de proyectos y hay
muchas herramientas para administrar subgrupos de esas personas. Normalmente estas cuentas se utilizan para grupos
Open Source (como “perl” o “rails”) o empresas (como “google” o “twitter”).

Conceptos básicos de organización

Una organización es bastante fácil de crear; simplemente haga clic en el icono "+" en la parte superior derecha de cualquier
página de GitHub y seleccione "Nueva organización" en el menú.

Figura 125. Elemento de menú “Nueva organización”

Primero deberá nombrar su organización y proporcionar una dirección de correo electrónico para un punto de contacto
principal para el grupo. Luego puede invitar a otros usuarios a ser copropietarios de la cuenta si lo desea.

Siga estos pasos y pronto será el propietario de una nueva organización. Al igual que las cuentas personales, las
organizaciones son gratuitas si todo lo que planea almacenar allí será de código abierto.

Como propietario de una organización, cuando bifurca un repositorio, tendrá la opción de bifurcarlo al espacio de nombres
de su organización. Cuando crea nuevos repositorios, puede crearlos en su cuenta personal o en cualquiera de las
organizaciones de las que es propietario. También "observa" automáticamente cualquier nuevo repositorio creado en estas
organizaciones.

Al igual que en Tu avatar, puedes cargar un avatar para tu organización para personalizarlo un poco. También, al igual que
las cuentas personales, tiene una página de inicio para la organización que enumera todos sus repositorios y puede ser
vista por otras personas.

Ahora cubramos algunas de las cosas que son un poco diferentes con una cuenta organizacional.

equipos

Las organizaciones están asociadas con personas individuales por medio de equipos, que son simplemente una agrupación
de cuentas de usuarios individuales y repositorios dentro de la organización y qué tipo de acceso tienen esas personas en
esos repositorios.

207
Machine Translated by Google

Por ejemplo, supongamos que su empresa tiene tres repositorios: frontend, backend y deploymentscripts. Le
gustaría que sus desarrolladores de HTML/CSS/JavaScript tuvieran acceso al frontend y tal vez al backend, y que
su gente de operaciones tuviera acceso al backend y los scripts de implementación. Los equipos lo hacen fácil,
sin tener que administrar a los colaboradores de cada repositorio individual.

La página Organización le muestra un panel simple de todos los repositorios, usuarios y equipos que están bajo
esta organización.

Figura 126. La página Organización

Para administrar sus equipos, puede hacer clic en la barra lateral de equipos en el lado derecho de la página en la
página de la organización. Esto lo llevará a una página que puede usar para agregar miembros al equipo, agregar
repositorios al equipo o administrar la configuración y los niveles de control de acceso para el equipo. Cada equipo
puede tener acceso de solo lectura, lectura/escritura o administrativo a los repositorios. Puede cambiar ese nivel
haciendo clic en el botón "Configuración" en la página El equipo.

208
Machine Translated by Google

Figura 127. La página del Equipo

Cuando invite a alguien a un equipo, recibirá un correo electrónico informándole que ha sido invitado.

Además, las @menciones del equipo (como @acmecorp/frontend) funcionan de la misma manera que con los usuarios
individuales, excepto que todos los miembros del equipo se suscriben al hilo. Esto es útil si desea la atención de alguien
en un equipo, pero no sabe exactamente a quién preguntar.

Un usuario puede pertenecer a cualquier cantidad de equipos, así que no se limite solo a los equipos de control de acceso.
Los equipos de interés especial como ux, css o refactoring son útiles para ciertos tipos de preguntas, y otros como legales
y daltónicos para un tipo completamente diferente.

Registro de auditoría

Las organizaciones también brindan a los propietarios acceso a toda la información sobre lo que sucedió en la organización.
Puede ir a la pestaña 'Registro de auditoría' y ver qué eventos han ocurrido a nivel de organización, quién los realizó y en
qué parte del mundo se realizaron.

209
Machine Translated by Google

Figura 128. El registro de auditoría

También puede filtrar hacia tipos específicos de eventos, lugares específicos o personas específicas.

Scripting GitHub
Así que ahora hemos cubierto todas las principales funciones y flujos de trabajo de GitHub, pero cualquier grupo o
proyecto grande tendrá personalizaciones que quizás deseen realizar o servicios externos que quizás deseen integrar.

Afortunadamente para nosotros, GitHub es bastante pirateable de muchas maneras. En esta sección, cubriremos cómo
usar el sistema de enlaces de GitHub y su API para hacer que GitHub funcione como queremos.

Servicios y Hooks

La sección Hooks and Services de la administración del repositorio de GitHub es la forma más fácil de tener

210
Machine Translated by Google

GitHub interactúa con sistemas externos.

Servicios

Primero vamos a echar un vistazo a los Servicios. Tanto las integraciones de Hooks como de Servicios se pueden
encontrar en la sección Configuración de su repositorio, donde anteriormente vimos agregar Colaboradores y cambiar
la rama predeterminada de su proyecto. En la pestaña "Webhooks and Services" verá algo como la sección de
configuración de Servicios y Hooks.

Figura 129. Sección de configuración de Servicios y Hooks

Hay docenas de servicios entre los que puede elegir, la mayoría de ellos integraciones en otros sistemas comerciales
y de código abierto. La mayoría de ellos son para servicios de integración continua, rastreadores de errores y problemas,
sistemas de salas de chat y sistemas de documentación. Veremos cómo configurar uno muy simple, el gancho de
correo electrónico. Si elige "correo electrónico" en el menú desplegable "Agregar servicio", obtendrá una pantalla de
configuración como la configuración del servicio de correo electrónico.

Figura 130. Configuración del servicio de correo electrónico

211
Machine Translated by Google

En este caso, si presionamos el botón "Agregar servicio", la dirección de correo electrónico que especificamos recibirá un
correo electrónico cada vez que alguien ingrese al repositorio. Los servicios pueden escuchar muchos tipos diferentes de
eventos, pero la mayoría solo escucha eventos push y luego hace algo con esos datos.

Si hay un sistema que está utilizando que le gustaría integrar con GitHub, debe consultar aquí para ver si hay una
integración de servicio existente disponible. Por ejemplo, si está utilizando Jenkins para ejecutar pruebas en su base de
código, puede habilitar la integración del servicio incorporado de Jenkins para iniciar una ejecución de prueba cada vez
que alguien ingresa a su repositorio.

Manos

Si necesita algo más específico o desea integrarse con un servicio o sitio que no está incluido en esta lista, puede usar
el sistema de ganchos más genérico. Los ganchos de repositorio de GitHub son bastante simples. Especificas una URL
y GitHub publicará una carga HTTP en esa URL en cualquier evento que desees.

En general, la forma en que esto funciona es que puede configurar un pequeño servicio web para escuchar una carga útil
de enlace de GitHub y luego hacer algo con los datos cuando se reciben.

Para habilitar un gancho, haga clic en el botón "Agregar webhook" en la sección de configuración de Servicios y ganchos.
Esto lo llevará a una página que se parece a la configuración de Webhook.

Figura 131. Configuración de gancho web

La configuración de un enlace web es bastante simple. En la mayoría de los casos, simplemente ingrese una URL y una
clave secreta y presione "Agregar webhook". Hay algunas opciones para los eventos para los que desea que GitHub le
envíe una carga útil: el valor predeterminado es obtener solo una carga útil para el evento de inserción , cuando alguien
inserta código nuevo en cualquier rama de su repositorio.

212
Machine Translated by Google

Veamos un pequeño ejemplo de un servicio web que puede configurar para manejar un enlace web. Usaremos el marco web Ruby
Sinatra ya que es bastante conciso y debería poder ver fácilmente lo que estamos haciendo.

Digamos que queremos recibir un correo electrónico si una persona específica ingresa a una rama específica de nuestro proyecto
modificando un archivo específico. Podríamos hacerlo bastante fácilmente con un código como este:

requieren 'sinatra'
requieren 'json'
requieren 'correo'

post '/payload' do push =


JSON.parse(request.body.read) # analizar el JSON

# reunir los datos que buscamos pusher =


push["pusher"]["name"] branch = push["ref"]

# obtener una lista de todos los archivos tocados files =


push["commits"].map do |commit| commit['agregado'] +
commit['modificado'] + commit['eliminado'] end files = files.flatten.uniq

# verifique nuestros criterios if pusher


== 'schacon' && branch == 'ref/heads/
ÿ

special-branch' && files.include?('special-file.txt')


ÿ

Mail.deliver hacer desde


ÿ

'tchacon@example.com'
ÿ

para 'tchacon@example.com'
ÿ

asunto 'Scott cambió el archivo' cuerpo


ÿ

"ALARMA" final

final
final

Aquí tomamos la carga JSON que nos entrega GitHub y buscamos quién la empujó, a qué rama empujaron y qué archivos se tocaron
en todas las confirmaciones que se empujaron. Luego lo verificamos con nuestros criterios y enviamos un correo electrónico si
coincide.

Para desarrollar y probar algo como esto, tiene una buena consola de desarrollador en la misma pantalla donde configuró la conexión.
Puede ver las últimas entregas que GitHub ha intentado realizar para ese webhook. Para cada enlace, puede profundizar en cuándo
se entregó, si fue exitoso y el cuerpo y los encabezados tanto de la solicitud como de la respuesta. Esto hace que sea increíblemente
fácil probar y depurar sus ganchos.

213
Machine Translated by Google

Figura 132. Información de depuración de web hooks

La otra gran característica de esto es que puede volver a entregar cualquiera de las cargas útiles para probar su servicio
fácilmente.

Para obtener más información sobre cómo escribir webhooks y todos los diferentes tipos de eventos que puede escuchar,
vaya a la documentación del desarrollador de GitHub en https://developer.github.com/webhooks/.

La API de GitHub
Los servicios y ganchos le brindan una forma de recibir notificaciones automáticas sobre eventos que ocurren en sus
repositorios, pero ¿qué sucede si necesita más información sobre estos eventos? ¿Qué sucede si necesita automatizar algo
como agregar colaboradores o etiquetar problemas?

Aquí es donde la API de GitHub resulta útil. GitHub tiene toneladas de puntos finales de API para hacer casi

214
Machine Translated by Google

cualquier cosa que pueda hacer en el sitio web de forma automatizada. En esta sección, aprenderemos cómo
autenticarse y conectarse a la API, cómo comentar un problema y cómo cambiar el estado de una solicitud de
extracción a través de la API.

Uso básico
Lo más básico que puede hacer es una simple solicitud GET en un punto final que no requiere autenticación. Esto
podría ser un usuario o información de solo lectura en un proyecto de código abierto. Por ejemplo, si queremos saber
más sobre un usuario llamado “schacon”, podemos ejecutar algo como esto:

$ curl https://api.github.com/users/schacon { "iniciar


sesión": "schacon", "id": 70, "avatar_url": "https://
avatars.githubusercontent.com/u/70", #…

"nombre": "Scott Chacon",


"compañía": "GitHub", "siguiente":
19, "creado_en":
"2008-01-27T17:19:28Z", "actualizado_en":
"2014-06-10T02: 37:23Z" }

Hay toneladas de puntos finales como este para obtener información sobre organizaciones, proyectos, problemas,
compromisos, casi cualquier cosa que pueda ver públicamente en GitHub. Incluso puede usar la API para representar
Markdown arbitrariamente o encontrar una plantilla .gitignore .

$ curl https://api.github.com/gitignore/templates/Java { "nombre": "Java",


"fuente": "*.clase

# Herramientas móviles para Java


(J2ME) .mtj.tmp/

# Archivos del paquete


# *.jar *.war

*.oreja

# registros de fallas de la máquina virtual,


consulte https://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
"

215
Machine Translated by Google

Comentar sobre un problema

Sin embargo, si desea realizar una acción en el sitio web, como comentar un problema o una solicitud de
extracción, o si desea ver o interactuar con contenido privado, deberá autenticarse.

Hay varias formas de autenticarse. Puede usar la autenticación básica con solo su nombre de usuario y contraseña,
pero generalmente es una mejor idea usar un token de acceso personal. Puede generar esto desde la pestaña
"Aplicaciones" de su página de configuración.

Figura 133. Genere su token de acceso desde la pestaña "Aplicaciones" de su página de configuración

Le preguntará qué ámbitos desea para este token y una descripción. Asegúrese de usar una buena descripción
para que se sienta cómodo eliminando el token cuando ya no se use su secuencia de comandos o aplicación.

GitHub solo le mostrará el token una vez, así que asegúrese de copiarlo. Ahora puede usar esto para autenticarse
en su secuencia de comandos en lugar de usar un nombre de usuario y una contraseña. Esto es bueno porque
puede limitar el alcance de lo que quiere hacer y el token es revocable.

Esto también tiene la ventaja adicional de aumentar su límite de tasa. Sin autenticación, estará limitado a 60
solicitudes por hora. Si te autenticas puedes realizar hasta 5.000 solicitudes por hora.

Entonces, usémoslo para hacer un comentario sobre uno de nuestros problemas. Digamos que queremos dejar
un comentario sobre un tema específico, el número 6. Para hacerlo, tenemos que hacer una solicitud HTTP POST
a repos/<user>/<repo>/issues/<num>/comments con el token que acabamos de generar como encabezado de
Autorización.

216
Machine Translated by Google

$ curl -H "Tipo de contenido: aplicación/json" \


ÿ

-H "Autorización: token TOKEN" \ --


ÿ

data '{"body":"Un nuevo comentario, :+1:"}' \


ÿ

https://api.github.com/repos/schacon/blink/issues/6 /comentarios

{ "id": 58322100,
"html_url": "https://github.com/schacon/blink/issues/6#issuecomment-58322100",
...
"usuario":
{ "iniciar sesión":
"tonychacon", "id": 7874698,
"avatar_url": "https://avatars.githubusercontent.com/u/7874698?v=2", "tipo":
"Usuario" , }, "created_at": "2014-10-08T07:48:19Z", "updated_at":
"2014-10-08T07:48:19Z", "body": "Un nuevo comentario, :+1:" }

Ahora, si va a ese problema, puede ver el comentario que acabamos de publicar con éxito como en Un comentario
publicado desde la API de GitHub.

Figura 134. Un comentario publicado desde la API de GitHub

Puede usar la API para hacer casi cualquier cosa que pueda hacer en el sitio web: crear y establecer hitos, asignar
personas a problemas y solicitudes de extracción, crear y cambiar etiquetas, acceder a datos de confirmación,
crear nuevas confirmaciones y ramas, abrir, cerrar o fusionar Pull Requests, crear y editar equipos, comentar
líneas de código en un Pull Request, buscar en el sitio y así sucesivamente.

Cambiar el estado de una solicitud de extracción

Hay un último ejemplo que veremos, ya que es realmente útil si está trabajando con solicitudes de extracción.
Cada confirmación puede tener uno o más estados asociados y hay una API para agregar y consultar ese estado.

La mayoría de los servicios de prueba e integración continua utilizan esta API para reaccionar a las inserciones
probando el código que se envió y luego informan si esa confirmación ha superado todas las pruebas. También
puede usar esto para verificar si el mensaje de compromiso tiene el formato correcto, si el remitente siguió todas
las pautas de su contribución, si el compromiso se firmó de manera válida, cualquier cantidad de cosas.

Supongamos que configura un webhook en su repositorio que accede a un pequeño servicio web que busca una
cadena Firmado por en el mensaje de confirmación.

217
Machine Translated by Google

requiere 'httpparty'
requieren 'sinatra'
requiere 'json'

publicar '/ carga útil' hacer


push = JSON.parse(request.body.read) # analizar el JSON
nombre_repo = empujar['repositorio']['nombre_completo']

# mirar a través de cada mensaje de confirmación


push["commits"].each do |commit|

# busca una cadena Firmada por


if /Firmado-por/.match commit['mensaje']
ÿ

estado = 'éxito'
ÿ

description = '¡Se cerró con éxito!'


demás
ÿ

estado = 'fracaso'
ÿ

description = 'No se encontró ninguna firma.'


final

# publicar estado en GitHub


sha = confirmar["id"]
status_url = "https://api.github.com/repos/#{repo_name}/statuses/#{sha}"

estado = {
ÿ

"estado" => estado,


ÿ

"descripción" => descripción,


ÿ

"target_url" => "http://example.com/how-to-signoff",


ÿ

"contexto" => "validar/aprobar"


}
HTTParty.post(estado_url,
ÿ

:cuerpo => estado.to_json,


ÿ

:encabezados => {
ÿ

'Tipo de contenido' => 'aplicación/json',


ÿ

'User-Agent' => 'tonychacon/firmar',


ÿ

'Autorización' => "token #{ENV['TOKEN']}" }


)
final
final

Esperemos que esto sea bastante simple de seguir. En este controlador de gancho web, revisamos cada confirmación que
se acaba de enviar, buscamos la cadena 'Firmado por' en el mensaje de confirmación y finalmente PUBLICAMOS
a través de HTTP al punto final de la API /repos/<user>/<repo>/statuses/<commit_sha> con el estado.

En este caso, puede enviar un estado ('éxito', 'fracaso', 'error'), una descripción de lo que sucedió, un objetivo
URL a la que el usuario puede ir para obtener más información y un "contexto" en caso de que haya varios estados para
un solo compromiso. Por ejemplo, un servicio de prueba puede proporcionar un estado y un servicio de validación como
esto también puede proporcionar un estado: el campo "contexto" es cómo se diferencian.

218
Machine Translated by Google

Si alguien abre una nueva solicitud de extracción en GitHub y este enlace está configurado, es posible que vea algo como el estado
de confirmación a través de la API.

Figura 135. Estado de compromiso a través de la API

Ahora puede ver una pequeña marca de verificación verde junto a la confirmación que tiene una cadena de "Firmado por" en el
mensaje y una cruz roja en la que el autor olvidó firmar. También puede ver que la solicitud de extracción toma el estado de la última
confirmación en la rama y le advierte si falla.
Esto es realmente útil si está utilizando esta API para obtener resultados de prueba, de modo que no fusione accidentalmente algo
donde la última confirmación está fallando en las pruebas.

octokit
Aunque hemos estado haciendo casi todo a través de curl y solicitudes HTTP simples en estos ejemplos, existen varias bibliotecas
de código abierto que hacen que esta API esté disponible de una manera más idiomática.
En el momento de escribir este artículo, los lenguajes admitidos incluyen Go, Objective-C, Ruby y .NET. Echa un vistazo a https://
github.com/octokit para obtener más información sobre estos, ya que manejan gran parte del HTTP por usted.

Con suerte, estas herramientas pueden ayudarlo a personalizar y modificar GitHub para que funcione mejor para sus flujos de
trabajo específicos. Para obtener documentación completa sobre toda la API, así como guías para tareas comunes, consulte https://
developer.github.com.

Resumen
Ahora eres un usuario de GitHub. Sabes cómo crear una cuenta, administrar una organización, crear y enviar repositorios, contribuir
a los proyectos de otras personas y aceptar contribuciones de otros. En el próximo capítulo, aprenderá herramientas y consejos más
potentes para lidiar con situaciones complejas, lo que realmente lo convertirá en un maestro de Git.

219
Machine Translated by Google

Herramientas Git

A estas alturas, ha aprendido la mayoría de los comandos y flujos de trabajo diarios que necesita para administrar o mantener un
repositorio de Git para su control de código fuente. Ha realizado las tareas básicas de seguimiento y confirmación de archivos, y ha
aprovechado el poder del área de ensayo y la bifurcación y fusión de temas ligeros.

Ahora explorará una serie de cosas muy poderosas que Git puede hacer que no necesariamente usará en el día a día, pero que puede
necesitar en algún momento.

Selección de revisión
Git te permite hacer referencia a una sola confirmación, un conjunto de confirmaciones o un rango de confirmaciones de varias maneras.
No son necesariamente obvios, pero es útil conocerlos.

Revisiones individuales

Obviamente, puede referirse a cualquier confirmación individual por su hash SHA-1 completo de 40 caracteres, pero también hay formas
más amigables para los humanos de referirse a las confirmaciones. Esta sección describe las diversas formas en que puede hacer
referencia a cualquier confirmación.

Corto SHA-1

Git es lo suficientemente inteligente como para averiguar a qué compromiso te refieres si proporcionas los primeros caracteres del hash
SHA-1, siempre que ese hash parcial tenga al menos cuatro caracteres y no sea ambiguo; es decir, ningún otro objeto en la base de
datos de objetos puede tener un hash que comience con el mismo prefijo.

Por ejemplo, para examinar una confirmación específica donde sabe que agregó cierta funcionalidad, primero puede ejecutar el comando
git log para ubicar la confirmación:

220
Machine Translated by Google

$ git registro
compromiso 734713bc047d87bf7eac9674765ae793478c50d3

Autor: Scott Chacon <schacon@gmail.com> Fecha:


viernes 2 de enero 18:32:33 2009 -0800

Arreglar el manejo de referencias, agregar gc auto, actualizar pruebas

confirmar d921970aadf03b3cf0e71becdaab3147ba71cdef Fusionar:

1c002dd... 35cfb2b...
Autor: Scott Chacon <schacon@gmail.com> Fecha: Jue
11 de diciembre 15:08:43 2008 -0800

Fusionar confirmación 'phedders/rdocs'

cometer 1c002dd4b536e7479fe34593e72e6c6c1819e53b Autor:

Scott Chacon <schacon@gmail.com> Fecha: Jue 11 de diciembre


14:58:32 2008 -0800

Agrega algo de culpa y fusiona cosas

En este caso, digamos que está interesado en la confirmación cuyo hash comienza con 1c002dd…. Puede inspeccionar
esa confirmación con cualquiera de las siguientes variaciones de git show (suponiendo que las versiones más cortas no
sean ambiguas):

$ programa git 1c002dd4b536e7479fe34593e72e6c6c1819e53b $


programa git 1c002dd4b536e7479f $ programa git 1c002d

Git puede encontrar una abreviatura corta y única para sus valores SHA-1. Si pasa --abbrev-commit al comando git log ,
la salida usará valores más cortos pero los mantendrá únicos; por defecto usa siete caracteres, pero los hace más
largos si es necesario para mantener el SHA-1 sin ambigüedades:

$ git log --abbrev-commit --pretty=oneline ca82a6d Cambia


el número de versión 085bb3b Elimina el código de prueba
innecesario a11bef0 Confirmación inicial

Generalmente, de ocho a diez caracteres son más que suficientes para ser únicos dentro de un proyecto. Por ejemplo,
a partir de febrero de 2019, el kernel de Linux (que es un proyecto bastante grande) tiene más de 875ÿ000
confirmaciones y casi siete millones de objetos en su base de datos de objetos, sin dos objetos cuyos SHA-1 sean
idénticos en los primeros 12 caracteres.

221
Machine Translated by Google

UNA BREVE NOTA SOBRE SHA-1

Muchas personas se preocupan en algún momento de que, por casualidad aleatoria, tengan
dos objetos distintos en su repositorio que tengan el mismo valor SHA-1. ¿Entonces que?

Si confirma un objeto que tiene el mismo valor SHA-1 que un objeto diferente anterior en su
repositorio, Git verá el objeto anterior que ya está en su base de datos Git, supondrá que ya
estaba escrito y simplemente lo reutilizará. Si intenta verificar ese objeto nuevamente en algún
momento, siempre obtendrá los datos del primer objeto.

Sin embargo, debe tener en cuenta lo ridículamente improbable que es este escenario. El
resumen SHA-1 es de 20 bytes o 160 bits. La cantidad de objetos con hash aleatorio
necesarios para garantizar una probabilidad del 50% de una sola colisión es de
aproximadamente 280 (la fórmula para determinar la probabilidad de colisión es p = (n(n-1)/2)
* (1/2^160)). 280 es 1,2 x 1024 o 1 millón de billones de billones. Eso es 1200 veces el número
ÿ de granos de arena en la tierra.

Aquí hay un ejemplo para darle una idea de lo que se necesitaría para obtener una colisión
SHA-1. Si los 6.500 millones de humanos en la Tierra estuvieran programando, y cada
segundo, cada uno produjera un código que fuera el equivalente de toda la historia del kernel
de Linux (6.5 millones de objetos Git) y lo insertara en un enorme repositorio Git, tomaría
aproximadamente 2 años. hasta que ese repositorio contenía suficientes objetos para tener un
50% de probabilidad de una sola colisión de objetos SHA-1. Por lo tanto, una colisión orgánica
SHA-1 es menos probable que todos los miembros de su equipo de programación sean
atacados y asesinados por lobos en incidentes no relacionados en la misma noche.

Si le dedica varios miles de dólares de potencia informática, es posible sintetizar dos archivos
con el mismo hash, como se demuestra en https://shattered.io/ en febrero de 2017. Git está
avanzando hacia el uso de SHA256 como el algoritmo hash predeterminado, que es mucho
más resistente a los ataques de colisión y tiene un código para ayudar a mitigar este ataque
(aunque no puede eliminarlo por completo).

Referencias de sucursales

Una forma sencilla de referirse a una confirmación en particular es si es la confirmación en la punta de una rama;
en ese caso, simplemente puede usar el nombre de la rama en cualquier comando de Git que espere una
referencia a una confirmación. Por ejemplo, si desea examinar el último objeto de confirmación en una rama, los
siguientes comandos son equivalentes, asumiendo que la rama topic1 apunta a la confirmación ca82a6d…:

$ git muestra ca82a6dff817ec66f44342007202690a93763949


$ git muestra tema1

Si desea ver a qué SHA-1 específico apunta una rama, o si desea ver a qué se reduce cualquiera de estos
ejemplos en términos de SHA-1, puede usar una herramienta de plomería de Git llamada rev-parse. Puede ver
Git Internals para obtener más información sobre las herramientas de plomería; básicamente, rev-parse existe para

222
Machine Translated by Google

operaciones de nivel inferior y no está diseñado para ser utilizado en las operaciones diarias. Sin embargo, a veces puede
ser útil cuando necesita ver lo que realmente está sucediendo. Aquí puede ejecutar rev-parse en su sucursal.

$ git rev-parse topic1


ca82a6dff817ec66f44342007202690a93763949

Nombres cortos de RefLog

Una de las cosas que hace Git en segundo plano mientras trabaja es mantener un "registro de referencia", un registro de
dónde han estado sus referencias HEAD y branch durante los últimos meses.

Puedes ver tu reflog usando git reflog:

$ git reflog
734713b HEAD@{0}: confirmación: corrige el manejo de referencias, agrega gc auto,
actualiza las pruebas d921970 HEAD@{1}: fusiona phedders/rdocs: fusión realizada por la estrategia
'recursiva'. 1c002dd HEAD@{2}: confirmación: agrega algo de culpa y fusiona cosas 1c36188
HEAD@{3}: rebase -i (squash): actualización de HEAD 95df984 HEAD@{4}: confirmación: # Esta es
una combinación de dos confirmaciones. 1c36188 HEAD@{5}: rebase -i (squash): actualizando
HEAD 7e05da5 HEAD@{6}: rebase -i (pick): actualizando HEAD

Cada vez que se actualiza la sugerencia de su sucursal por cualquier motivo, Git almacena esa información para usted en
este historial temporal. También puede usar sus datos de reflog para hacer referencia a confirmaciones anteriores. Por
ejemplo, si desea ver el quinto valor anterior de HEAD de su repositorio, puede usar la referencia @{5} que ve en la salida
de reflog:

$ mostrar HEAD@{5}

También puede usar esta sintaxis para ver dónde estaba una rama hace un tiempo específico. Por ejemplo, para ver dónde
estaba ayer su rama maestra , puede escribir:

$ git show master@{ayer}

Eso le mostraría dónde estaba ayer la punta de su rama maestra . Esta técnica solo funciona para los datos que aún están
en su registro de referencia, por lo que no puede usarla para buscar confirmaciones de más de unos pocos meses.

Para ver la información de reflog formateada como la salida de registro de git , puede ejecutar git log -g:

223
Machine Translated by Google

$ git log -g master commit


734713bc047d87bf7eac9674765ae793478c50d3 Reflog: master@{0}

(Scott Chacon <schacon@gmail.com>)


Reflog mensaje: confirmar: Arreglar manejo de referencias, agregar gc auto, actualizar pruebas
Autor: Scott Chacon <schacon@gmail.com> Fecha: Vie 2 de enero 18:32:33 2009 -0800

Arreglar el manejo de referencias, agregar gc auto, actualizar pruebas

confirmar d921970aadf03b3cf0e71becdaab3147ba71cdef Reflog:

master@{1} (Scott Chacon <schacon@gmail.com>)


Mensaje de reflog: fusionar phedders/rdocs: Fusión realizada por recurso recursivo.
Autor: Scott Chacon <schacon@gmail.com> Fecha: Jue
11 de diciembre 15:08:43 2008 -0800

Fusionar confirmación 'phedders/rdocs'

Es importante tener en cuenta que la información de reflog es estrictamente local: es un registro solo de lo que ha hecho en su
repositorio. Las referencias no serán las mismas en la copia del repositorio de otra persona; Además, inmediatamente después
de clonar inicialmente un repositorio, tendrá un registro de referencia vacío, ya que aún no se ha producido ninguna actividad en
su repositorio. Ejecutar git show HEAD@{2.months.ago} le mostrará la confirmación coincidente solo si clonó el proyecto hace al
menos dos meses; si lo clonó más recientemente, verá solo su primera confirmación local.

Piense en el reflog como la versión de Git del historial de shell

Si tiene experiencia en UNIX o Linux, puede pensar en el reflog como la versión de Git del historial de shell,
ÿ que enfatiza que lo que hay allí es claramente relevante solo para usted y su "sesión", y no tiene nada que
ver con nadie más que pueda estar trabajando en la misma máquina.

Escapar llaves en PowerShell

Al usar PowerShell, las llaves como { y } son caracteres especiales y se deben escapar. Puede escapar de
`
ellos con un acento grave o poner la referencia de confirmación entre comillas:

ÿ
$ mostrar HEAD@{0} $ # no trabajará

mostrar HEAD@`{0`} # OK $ mostrar


"HEAD@{0}" # OK

Referencias de ascendencia

La otra forma principal de especificar una confirmación es a través de su ascendencia. Si coloca un ^ (signo de intercalación) al
final de una referencia, Git lo resuelve para que signifique el padre de esa confirmación. Supongamos que miras el historial de tu
proyecto:

224
Machine Translated by Google

$ git log --pretty=format:'%h %s' --graph * 734713b


Arreglar manejo de referencias, agregar gc auto, actualizar pruebas *
d921970 Merge commit 'phedders/rdocs' |\ | * 35cfb2b Algunos cambios de
rdoc * | 1c002dd Agregue culpa y fusione cosas |/ * 1c36188 Ignorar *.gem
* 9b29157 Agregue open3_detach a la lista de archivos gemspec

Luego, puede ver la confirmación anterior especificando HEAD^, que significa "el padre de HEAD":

$ git show HEAD^


commit d921970aadf03b3cf0e71becdaab3147ba71cdef Merge:
1c002dd... 35cfb2b...
Autor: Scott Chacon <schacon@gmail.com> Fecha: Jue
11 de diciembre 15:08:43 2008 -0800

Fusionar confirmación 'phedders/rdocs'

Escapar del símbolo de intercalación en Windows

En Windows en cmd.exe, ^ es un carácter especial y debe tratarse de manera diferente.


Puede duplicarlo o poner la referencia de confirmación entre comillas:

ÿ # NO funcionará en Windows
$ git muestra CABEZA^
$ git muestra CABEZA^^ # OK
$ git muestra "CABEZA^" # OK

También puede especificar un número después de ^ para identificar qué padre desea; por ejemplo, d921970^2
significa "el segundo padre de d921970". Esta sintaxis es útil solo para confirmaciones de fusión, que tienen
más de un padre: el primer padre de una confirmación de fusión es de la rama en la que estaba cuando se
fusionó (con frecuencia maestro), mientras que el segundo padre de una confirmación de fusión es de la rama
que se fusionó (digamos, tema):

225
Machine Translated by Google

$ mostrar git d921970^


confirmar 1c002dd4b536e7479fe34593e72e6c6c1819e53b
Autor: Scott Chacon <schacon@gmail.com> Fecha:
Jue 11 de diciembre 14:58:32 2008 -0800

Agrega algo de culpa y fusiona cosas

$ mostrar git d921970^2


compromiso 35cfb2b795a55793d7cc56a6cc2060b4bb732548
Autor: Paul Hedderly <paul+git@mjr.org> Fecha:
miércoles 10 de diciembre 22:22:03 2008 +0000

Algunos cambios de rdoc

La otra especificación principal de ascendencia es la ~ (tilde). Esto también se refiere al primer padre, por lo que HEAD~ y HEAD^
son equivalentes. La diferencia se hace evidente cuando especifica un número. HEAD~2 significa "el primer padre del primer padre"
o "el abuelo": atraviesa los primeros padres la cantidad de veces que especifique. Por ejemplo, en el historial mencionado
anteriormente, HEAD~3 sería:

$ git show HEAD~3


commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
Autor: Tom Preston-Werner <tom@mojombo.com> Fecha:
Vie 7 de noviembre 13:47:59 2008 -0500

Ignorar *.gem

Esto también se puede escribir HEAD~~~, que nuevamente es el primer padre del primer padre del primer padre:

$ mostrar HEAD~~~
cometer 1c3618887afb5fbcbea25b7c013f4e2114448b8d
Autor: Tom Preston-Werner <tom@mojombo.com>
Fecha: Vie 7 de noviembre 13:47:59 2008 -0500

Ignorar *.gem

También puede combinar estas sintaxis: puede obtener el segundo elemento principal de la referencia anterior (suponiendo que se
trate de una confirmación de combinación) utilizando HEAD~3^2, y así sucesivamente.

Rangos de confirmación

Ahora que puede especificar confirmaciones individuales, veamos cómo especificar rangos de confirmaciones. Esto es particularmente
útil para administrar sus sucursales: si tiene muchas sucursales, puede usar especificaciones de rango para responder preguntas
como "¿Qué trabajo hay en esta sucursal que aún no he fusionado con mi sucursal principal?"

226
Machine Translated by Google

punto doble

La especificación de rango más común es la sintaxis de doble punto. Básicamente, esto le pide a Git que resuelva un rango de
confirmaciones a las que se puede acceder desde una confirmación pero no desde otra. Por ejemplo, supongamos que tiene un
historial de confirmaciones que se parece a un historial de ejemplo para la selección de rango.

Figura 136. Ejemplo de historial de selección de rango

Supongamos que desea ver qué hay en su rama experimental que aún no se ha fusionado con su rama maestra . Puede pedirle
a Git que le muestre un registro de solo esas confirmaciones con maestro ... experimento, lo que significa "todas las
confirmaciones accesibles desde el experimento que no son accesibles desde el maestro". En aras de la brevedad y la claridad
en estos ejemplos, las letras de los objetos de confirmación del diagrama se utilizan en lugar de la salida del registro real en el
orden en que se mostrarían:

$ git log master..experiment


DC

Si, por otro lado, desea ver lo contrario (todas las confirmaciones en el maestro que no están en el experimento ), puede invertir
los nombres de las ramas. experiment..master le muestra todo en el maestro no accesible desde el experimento:

$ git log experiment..master F

mi

Esto es útil si desea mantener actualizada la rama del experimento y obtener una vista previa de lo que está a punto de fusionar.
Otro uso frecuente de esta sintaxis es ver lo que está a punto de enviar a un control remoto:

$ git log origin/master..HEAD

Este comando le muestra cualquier confirmación en su rama actual que no esté en la rama maestra en su control remoto de
origen . Si ejecuta un git push y su rama actual está rastreando origin/master, las confirmaciones enumeradas por git log origin/
master..HEAD son las confirmaciones que se transferirán al servidor. También puede omitir un lado de la sintaxis para que Git
asuma HEAD. Por ejemplo, puede obtener los mismos resultados que en el ejemplo anterior escribiendo git log origin/master..
— Git sustituye HEAD si falta un lado.

227
Machine Translated by Google

Puntos Múltiples

La sintaxis de doble punto es útil como abreviatura, pero tal vez desee especificar más de dos ramas para indicar su revisión,
como ver qué confirmaciones hay en cualquiera de varias ramas que no están en la rama en la que se encuentra actualmente.
Git te permite hacer esto usando el carácter ^ o --not antes de cualquier referencia desde la que no quieras ver confirmaciones
accesibles. Por lo tanto, los siguientes tres comandos son equivalentes:

$ git log refA..refB $ git log


^refA refB $ git log refB --
no refA

Esto es bueno porque con esta sintaxis puede especificar más de dos referencias en su consulta, lo que no puede hacer con la
sintaxis de doble punto. Por ejemplo, si desea ver todas las confirmaciones a las que se puede acceder desde refA o refB pero
no desde refC, puede usar cualquiera de las siguientes opciones:

$ git log refA refB ^refC $ git log


refA refB --no refC

Esto lo convierte en un sistema de consulta de revisión muy poderoso que debería ayudarlo a descubrir qué hay en sus sucursales.

punto triple

La última sintaxis principal de selección de rango es la sintaxis de triple punto, que especifica todas las confirmaciones a las que
puede acceder cualquiera de las dos referencias, pero no ambas. Vuelva a consultar el historial de confirmación de ejemplo en
Historial de ejemplo para la selección de rango. Si desea ver lo que hay en el maestro o el experimento, pero no en las referencias
comunes, puede ejecutar:

$ git log master... experimento FE

D
C

Nuevamente, esto le brinda una salida de registro normal , pero le muestra solo la información de confirmación para esas cuatro
confirmaciones, que aparecen en el orden de fecha de confirmación tradicional.

Un interruptor común para usar con el comando de registro en este caso es --left-right, que le muestra en qué lado del rango se
encuentra cada confirmación. Esto ayuda a que la salida sea más útil:

$ git log --izquierda-derecha maestro...experimento < F


<E

> re
>C

228
Machine Translated by Google

Con estas herramientas, puede hacer que Git sepa mucho más fácilmente qué compromiso o compromisos desea

inspeccionar.

Puesta en Escena Interactiva

En esta sección, verás algunos comandos interactivos de Git que pueden ayudarte a crear tus confirmaciones.

para incluir solo ciertas combinaciones y partes de archivos. Estas herramientas son útiles si modifica un

una gran cantidad de archivos, luego decida que desea que esos cambios se dividan en varios

confirmaciones enfocadas en lugar de una gran confirmación desordenada. De esta manera, puede asegurarse de que sus compromisos sean

conjuntos de cambios separados lógicamente y pueden ser revisados fácilmente por los desarrolladores que trabajan con usted.

Si ejecuta git add con la opción -i o --interactive , Git ingresa a un modo de shell interactivo,

mostrando algo como esto:

$ git añadir -i
ÿ

puesta en camino sin etapas


1: escena sin +0/-1 POR HACER

2: 3: cambios sin +1/-1 índice.html


cambios sin cambios +5/-1 lib/simplegit.rb

*** Comandos ***


1: [s]estado 5: 2: [actualizar] 3: [r]evert 4: [a]ñadir sin seguimiento
[p]armado 6: [d]si 7: [q]uir 8: [ayuda]
¿Y ahora qué?

Puede ver que este comando le muestra una vista muy diferente de su área de preparación de lo que está

probablemente solía: básicamente, la misma información que obtienes con el estado de git pero un poco más breve

e informativo. Enumera los cambios que ha preparado a la izquierda y los cambios sin preparar a la derecha.

Después de esto, viene una sección de "Comandos", que le permite hacer una serie de cosas como puesta en escena y

anular archivos, organizar partes de archivos, agregar archivos sin seguimiento y mostrar diferencias de lo que ha sido

escenificado.

Archivos provisionales y no provisionales

Si escribe u o 2 (para actualizar) en el indicador Qué ahora> , se le preguntará qué archivos desea actualizar.

escenario:

que ahora > tu


ÿ

puesta camino sin etapas


1: en escena +0/-1 POR HACER

sin cambios 2:
sin3:cambios
sin +1/-1 índice.html
cambios +5/-1 lib/simplegit.rb
Actualizar>>

Para organizar los archivos TODO e index.html , puede escribir los números:

229
Machine Translated by Google

Actualizar>> 1,2
ÿ

puesta en camino sin etapas


* 1: escena sin +0/-1 POR HACER

* 2: cambios sin +1/-1 índice.html


3: cambios sin cambios +5/-1 lib/simplegit.rb
Actualizar>>

El * al lado de cada archivo significa que el archivo está seleccionado para ser preparado. Si presiona Enter después de escribir nada

en el indicador Actualizar>> , Git toma todo lo seleccionado y lo organiza por ti:

Actualizar>>
2 caminos actualizados

*** Comandos ***


1: [s]estado 5: 2: [actualizar] 3: [r]evert 7: 4: [a]ñadir sin seguimiento
[p]armado 6: [d]si [q]uir 8: [ayuda]
que ahora >s
ÿ

por camino sin etapas


1: etapas Nada que hacer
2: +0/-1 nada index.html
3: +1/-1 sin cambios +5/-1 lib/simplegit.rb

Ahora puede ver que los archivos TODO e index.html están preparados y el archivo simplegit.rb todavía está
sin escenificar Si desea desorganizar el archivo TODO en este punto, use la opción r o 3 (para revertir):

*** Comandos ***


1: [s]estado 5: 2: [actualizar] 3: [r]evert 7: 4: [a]ñadir sin seguimiento
[p]armado 6: [d]si [q]uir 8: [ayuda]
que ahora >r
ÿ

camino sin etapas


1: 2: organizado +0/-1 Nada que hacer
3: +1/-1 nada index.html
sin cambios +5/-1 lib/simplegit.rb
Revertir>> 1
ÿ

por camino sin etapas


* 1: etapas Nada que hacer
2: +0/-1 nada index.html
+1/-1 3: sin cambios +5/-1 lib/simplegit.rb
Revertir>> [ingresar]
revirtió un camino

Mirando de nuevo tu estado de Git, puedes ver que has desmontado el archivo TODO :

230
Machine Translated by Google

*** Comandos ***


1: [s]estado 5: 2: [actualizar] 3: [r]evert 7: 4: [a]ñadir sin seguimiento
[p]armado 6: [d]si [q]uir 8: [ayuda]
que ahora >s
ÿ

puesta en camino sin etapas


1: escena sin +0/-1 POR HACER

2: 3: cambios nada index.html


+1/-1 sin cambios +5/-1 lib/simplegit.rb

Para ver la diferencia de lo que ha organizado, puede usar el comando d o 6 (para diferencia). Te muestra una lista
de sus archivos preparados, y puede seleccionar aquellos para los que le gustaría ver la diferencia preparada. Esta
es muy parecido a especificar git diff --cached en la línea de comando:

*** Comandos ***


1: [s]estado 5: 2: [actualizar] 3: [r]evert 7: 4: [a]ñadir sin seguimiento
[p]armado 6: [d]si [q]uir 8: [ayuda]
que ahora >d
ÿ

camino sin etapas


1: organizado +1/-1 nada index.html
Revisar diferencia>> 1

diferencia --git a/index.html b/index.html


índice 4d07108..4335f49 100644
--- a/index.html
+++ b/index.html
@@ -16,7 +16,7 @@ Buscador de fechas

<p id="fuera">...</p>

-<div id="footer">contacto: support@github.com</div>


+<div id="footer">contacto: email.support@github.com</div>

<script tipo="texto/javascript">

Con estos comandos básicos, puede usar el modo de agregar interactivo para tratar con su área de preparación un
poco más fácilmente.

Parches de puesta en escena

También es posible que Git organice ciertas partes de los archivos y no el resto. Por ejemplo, si haces dos
cambios en su archivo simplegit.rb y desea organizar uno de ellos y no el otro, hacerlo es muy
fácil en Git. Desde el mismo indicador interactivo explicado en la sección anterior, escriba p o 5 (para
parche). Git te preguntará qué archivos te gustaría organizar parcialmente; entonces, para cada sección del
archivos seleccionados, mostrará fragmentos de la diferencia del archivo y le preguntará si desea organizarlos, uno por
una:

231
Machine Translated by Google

diff --git a/lib/simplegit.rb b/lib/simplegit.rb índice dd5ecc4..57399e0


100644

--- a/lib/simplegit.rb +++ b/lib/


simplegit.rb @@ -22,7 +22,7
@@ clase SimpleGit fin

def log(árbol = 'maestro') comando("git log -n


-
25 #{árbol}") comando("git log -n 30 #{árbol}") end
+

def culpar (camino)


¿Escenificar este trozo [y,n,a,d,/,j,J,g,e,?]?

Tienes muchas opciones en este punto. ¿ Escribiendo ? muestra una lista de lo que puede hacer:

¿Escenificar este trozo [y,n,a,d,/,j,J,g,e,?]? ? y - organizar


este trozo n - no poner este trozo en escena a - poner en
escena este y todos los trozos restantes en el archivo d - no
poner en escena este trozo ni ninguno de los trozos restantes en el archivo g -
seleccionar un trozo para ir a / - buscar un trozo que coincida con la expresión regular dada j - dejar este
trozo indeciso, ver el siguiente trozo indeciso J - dejar este trozo indeciso, ver el siguiente trozo k - dejar
este trozo indeciso, ver el trozo anterior indeciso K - dejar este trozo indeciso, ver el trozo anterior s -
dividir el trozo actual en trozos más pequeños e - ¿editar manualmente el trozo actual? - imprimir ayuda

Por lo general, escribirá y o n si desea preparar cada trozo, pero también puede ser útil colocarlos todos en ciertos archivos u omitir
una decisión de trozo hasta más tarde. Si prepara una parte del archivo y deja otra parte sin preparar, su salida de estado se verá así:

que ahora > 1


ÿ

puesta en ruta no preparada


1: 2: escena sin +0/-1 TODO
cambios nada index.html +4/-0 lib/
3: +1/-1 +1/-1 simplegit.rb

El estado del archivo simplegit.rb es interesante. Te muestra que un par de líneas están escenificadas y otras no. Has preparado
parcialmente este archivo. En este punto, puede salir del script de adición interactivo y ejecutar git commit para confirmar los archivos
parcialmente preparados.

Tampoco es necesario que esté en el modo de adición interactiva para realizar la puesta en escena del archivo parcial; puede iniciar el

232
Machine Translated by Google

misma secuencia de comandos usando git add -p o git add --patch en la línea de comando.

Además, puede usar el modo parche para restablecer parcialmente archivos con el comando git reset --patch , para verificar
partes de archivos con el comando git checkout --patch y para guardar partes de archivos con el comando git stash save --
patch . Entraremos en más detalles sobre cada uno de estos a medida que lleguemos a usos más avanzados de estos
comandos.

Almacenamiento y limpieza
A menudo, cuando ha estado trabajando en parte de su proyecto, las cosas están en un estado desordenado y desea
cambiar de rama por un tiempo para trabajar en otra cosa. El problema es que no desea hacer una confirmación de trabajo
a medio hacer solo para poder volver a este punto más tarde. La respuesta a este problema es el comando git stash .

Stashing toma el estado sucio de su directorio de trabajo, es decir, sus archivos rastreados modificados y cambios
preparados, y lo guarda en una pila de cambios sin terminar que puede volver a aplicar en cualquier momento (incluso en
una rama diferente).

Migrando a git stash push


A fines de octubre de 2017, ha habido una amplia discusión sobre la lista de correo de Git, en la que el
comando git stash save está en desuso en favor de la alternativa existente git stash push. La razón
principal de esto es que git stash push introduce la opción de ocultar las especificaciones de ruta

ÿ seleccionadas , algo que git stash save no admite.

git stash save no desaparecerá pronto, así que no te preocupes por si desaparece repentinamente.
Pero es posible que desee comenzar a migrar a la alternativa de inserción para la nueva funcionalidad .

Esconder tu trabajo
Para demostrar el ocultamiento, ingresará a su proyecto y comenzará a trabajar en un par de archivos y posiblemente
realizará uno de los cambios. Si ejecuta git status, puede ver su estado sucio:

$ git status
Cambios a confirmar: (use
"git reset HEAD <archivo>..." para quitar la etapa)

modificado: index.html

Cambios no preparados para


confirmación: (use "git add <archivo>..." para actualizar lo que se
confirmará) (use "git checkout -- <archivo>..." para descartar cambios en el directorio de trabajo)

modificado: lib/simplegit.rb

Ahora quiere cambiar de rama, pero no quiere comprometer lo que ha estado trabajando todavía,

233
Machine Translated by Google

así que guardarás los cambios. Para insertar un nuevo alijo en su pila, ejecute git stash o git stash push:

$ git alijo
Directorio de trabajo guardado y estado del índice \
"WIP en maestro: 049d078 Crear archivo de índice"
HEAD está ahora en 049d078 Crear archivo de índice
(Para restaurarlos, escriba "git stash apply")

Ahora puede ver que su directorio de trabajo está limpio:

$ git estado
# En el maestro de la

rama, no hay nada que confirmar, el directorio de trabajo está limpio

En este punto, puede cambiar de sucursal y trabajar en otro lugar; sus cambios se almacenan en su pila. Para ver
qué alijos has almacenado, puedes usar git stash list:

$ git stash list


stash@{0}: WIP en maestro: 049d078 Crear archivo de índice
stash@{1}: WIP en maestro: c264051 Revertir "Agregar archivo_tamaño"
stash@{2}: WIP en maestro: 21d80a5 Agregar número al registro

En este caso, se guardaron dos escondites anteriormente, por lo que tiene acceso a tres obras escondidas diferentes.
Puede volver a aplicar el que acaba de guardar utilizando el comando que se muestra en la salida de ayuda del
comando de almacenamiento original: git stash apply. Si desea aplicar uno de los stashes más antiguos, puede
especificarlo nombrándolo, así: git stash apply stash@{2}. Si no especifica un alijo, Git asume el alijo más reciente e
intenta aplicarlo:

$ git alijo aplicar


En maestro de rama
Cambios no preparados para
confirmación: (use "git add <archivo>..." para actualizar lo que se confirmará)
(use "git checkout -- <archivo>..." para descartar cambios en el directorio de trabajo)

modificado: index.html modificado:


lib/simplegit.rb

no se agregaron cambios para confirmar (use "git add" y/o "git commit -a")

Puede ver que Git vuelve a modificar los archivos que revirtió cuando guardó el alijo. En este caso, tenía un directorio
de trabajo limpio cuando intentó aplicar el alijo e intentó aplicarlo en la misma rama desde la que lo guardó. No es
necesario tener un directorio de trabajo limpio y aplicarlo en la misma rama para aplicar con éxito un alijo. Puede
guardar un alijo en una rama, cambiar a otra rama más tarde e intentar volver a aplicar los cambios. También puede
tener archivos modificados y no confirmados en su directorio de trabajo cuando aplica un alijo: Git le brinda conflictos
de combinación en todo caso.

234
Machine Translated by Google

ya no se aplica limpiamente.

Los cambios en sus archivos se volvieron a aplicar, pero el archivo que preparó antes no se volvió a preparar.
Para hacer eso, debe ejecutar el comando git stash apply con una opción --index para indicarle al comando que
intente volver a aplicar los cambios por etapas. Si hubiera ejecutado eso en su lugar, habría vuelto a su posición
original:

$ git stash apply --index


En maestro de rama
Cambios a confirmar: (use "git
reset HEAD <archivo>..." para quitar la preparación)

modificado: index.html

Cambios no preparados para


confirmación: (use "git add <archivo>..." para actualizar lo que se confirmará)
(use "git checkout -- <archivo>..." para descartar cambios en el directorio de trabajo)

modificado: lib/simplegit.rb

La opción de aplicar solo intenta aplicar el trabajo guardado: continúa teniéndolo en su pila. Para eliminarlo,
puede ejecutar git stash drop con el nombre del alijo que desea eliminar:

$ git stash list


stash@{0}: WIP en maestro: 049d078 Crear archivo de índice
stash@{1}: WIP en maestro: c264051 Revertir "Agregar archivo_tamaño"
stash@{2}: WIP en maestro: 21d80a5 Agregar número para registrar $
git alijo soltar alijo @{0}
Alijo soltado@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43)

También puede ejecutar git stash pop para aplicar el alijo y luego soltarlo inmediatamente de su pila.

Almacenamiento creativo

Hay algunas variantes ocultas que también pueden ser útiles. La primera opción que es bastante popular es la
opción --keep-index del comando git stash . Esto le dice a Git que no solo incluya todo el contenido en etapas en
el alijo que se está creando, sino que lo deje simultáneamente en el índice.

235
Machine Translated by Google

$ git status -s M
index.html
M lib/simplegit.rb

$ git alijo --mantener-índice


Directorio de trabajo guardado y WIP de estado de índice en maestro: 1b65b17 agregó el archivo de índice
HEAD ahora está en 1b65b17 agregó el archivo de índice

$ git status -s M
index.html

Otra cosa común que puede querer hacer con el almacenamiento oculto es esconder los archivos sin rastrear, así
como los rastreados. De forma predeterminada, git stash solo almacenará archivos rastreados modificados y
preparados. Si especifica --include-untracked o -u, Git incluirá archivos sin seguimiento en el alijo que se está creando.
Sin embargo, incluir archivos sin seguimiento en el alijo no incluirá archivos explícitamente ignorados ; para incluir
adicionalmente archivos ignorados, use --all (o simplemente -a).

$ git status -s M
index.html M lib/
simplegit.rb ?? nuevo-
archivo.txt

$ git alijo -u
Directorio de trabajo guardado y WIP de estado de índice en maestro: 1b65b17 agregó el archivo de índice
HEAD ahora está en 1b65b17 agregó el archivo de índice

$ git estado -s $

Finalmente, si especifica el indicador --patch , Git no ocultará todo lo que se modifique, sino que le preguntará de
forma interactiva cuáles de los cambios desea ocultar y cuáles desea mantener en su directorio de trabajo.

236
Machine Translated by Google

$ git stash --diferencia de


parche --git a/lib/simplegit.rb b/lib/simplegit.rb índice
66d332e..8bb5674 100644 --- a/lib/simplegit.rb +++ b/lib/simplegit.rb
@@ -16,6 +16,10 @@ clase SimpleGit devuelve `#{git_cmd}
2>&1`.chomp end end

+
+ def mostrar (árbol = 'maestro')
+ comando("mostrar git #{treeish}") fin
+

final
test
Stash this hunk [y,n,q,a,d,/,e,?]? y

Directorio de trabajo guardado y WIP de estado de índice en maestro: 1b65b17 agregó el archivo de índice

Creación de una sucursal a partir de un alijo

Si oculta algún trabajo, déjelo allí por un tiempo y continúe en la rama desde la que escondió el trabajo,
es posible que tenga problemas para volver a aplicar el trabajo. Si la aplicación intenta modificar un
archivo que ya modificó, obtendrá un conflicto de fusión y tendrá que intentar resolverlo. Si desea una
forma más fácil de probar los cambios ocultos nuevamente, puede ejecutar git stash branch <nuevo
nombre de la rama>, que crea una nueva rama para usted con el nombre de la rama seleccionada,
comprueba la confirmación en la que estaba cuando escondió su trabajo, vuelve a aplicar su trabajo allí
y luego suelta el alijo si se aplica con éxito:

$ git stash branch testchanges M


index.html M lib/simplegit.rb Cambiado a
una nueva rama 'testchanges'

En los cambios de prueba de


rama Cambios que se deben
confirmar: (use "git reset HEAD <archivo>..." para quitar la etapa)

modificado: index.html

Cambios no preparados para confirmación:


(use "git add <archivo>..." para actualizar lo que se confirmará) (use "git checkout --
<archivo>..." para descartar cambios en el directorio de trabajo)

modificado: lib/simplegit.rb

Refs descartados/almacenamiento@{0} (29d385a81d163dfd45a452a2ce816487a6b8b014)

237
Machine Translated by Google

Este es un buen atajo para recuperar fácilmente el trabajo guardado y trabajar en él en una nueva rama.

Limpieza de su directorio de trabajo


Finalmente, es posible que no desee guardar algunos trabajos o archivos en su directorio de trabajo, sino simplemente
deshacerse de ellos; para eso está el comando git clean .

Algunas razones comunes para limpiar su directorio de trabajo pueden ser eliminar el cruft generado por fusiones o
herramientas externas o eliminar artefactos de compilación para ejecutar una compilación limpia.

Deberá tener mucho cuidado con este comando, ya que está diseñado para eliminar archivos de su directorio de trabajo que
no se rastrean. Si cambia de opinión, a menudo no es posible recuperar el contenido de esos archivos. Una opción más
segura es ejecutar git stash --all para eliminar todo pero guardarlo en un alijo.

Suponiendo que desea eliminar archivos cruft o limpiar su directorio de trabajo, puede hacerlo con git clean. Para eliminar
todos los archivos no rastreados en su directorio de trabajo, puede ejecutar git clean -f -d, que elimina cualquier archivo y
también cualquier subdirectorio que quede vacío como resultado. El -f significa 'forzar' o "realmente hacer esto", y es
obligatorio si la variable de configuración de Git clean.requireForce no se establece explícitamente en false.

Si alguna vez quiere ver lo que haría, puede ejecutar el comando con la opción --dry-run (o -n) , que significa "haga una
prueba y dígame qué habría eliminado".

$ git clean -d -n
Quitaría test.o
Quitaría tmp/

De forma predeterminada, el comando git clean solo eliminará los archivos sin seguimiento que no se ignoren. Cualquier
archivo que coincida con un patrón en su .gitignore u otros archivos ignorados no se eliminará. Si también desea eliminar
esos archivos, como eliminar todos los archivos .o generados a partir de una compilación para que pueda hacer una
compilación completamente limpia, puede agregar -x al comando de limpieza.

$ git status -s M
lib/simplegit.rb ??
construir.TMP
?? tmp/

$ git clean -n -d
Eliminaría build.TMP
Quitaría tmp/

$ git clean -n -d -x
Eliminaría build.TMP
Quitaría test.o
Quitaría tmp/

Si no sabe qué va a hacer el comando git clean , siempre ejecútelo con -n primero para duplicar

238
Machine Translated by Google

verifique antes de cambiar el -n a -f y hacerlo de verdad. La otra forma en que puede tener cuidado con el proceso es
ejecutarlo con el indicador -i o "interactivo".

Esto ejecutará el comando de limpieza en un modo interactivo.

$ git clean -x -i
Eliminaría los siguientes elementos:
build.TMP test.o *** Comandos ***

1: limpiar cada 2: filtrar por patrón 3: seleccione por números 4: preguntar

6: ayuda 5: salir

¿Y ahora qué?

De esta manera, puede recorrer cada archivo individualmente o especificar patrones para eliminar de forma interactiva.

Existe una situación peculiar en la que es posible que deba ser más contundente al pedirle a Git que
limpie su directorio de trabajo. Si se encuentra en un directorio de trabajo en el que ha copiado o
ÿ clonado otros repositorios de Git (quizás como submódulos), incluso git clean -fd se negará a eliminar
esos directorios. En casos como ese, debe agregar una segunda opción -f para enfatizar.

Firmar su trabajo
Git es criptográficamente seguro, pero no es infalible. Si acepta el trabajo de otros en Internet y desea verificar que las
confirmaciones provienen realmente de una fuente confiable, Git tiene algunas formas de firmar y verificar el trabajo con
GPG.

GPG Introducción
En primer lugar, si desea firmar algo, debe configurar GPG e instalar su clave personal.

$ gpg --list-keys /Users/


schacon/.gnupg/pubring.gpg
---------------------------------

pub 2048R/0A46826A 2014-06-04 uid


sub 2048R/874529A9 2014-06-04
Scott Chacon (clave de firma Git) <schacon@gmail.com>

Si no tiene una clave instalada, puede generar una con gpg --gen-key.

$ gpg --gen-clave

Una vez que tenga una clave privada para iniciar sesión, puede configurar Git para que la use para firmar cosas configurando la
configuración de configuración de user.signingkey .

239
Machine Translated by Google

$ git config --usuario global.signingkey 0A46826A

Ahora Git usará su clave de forma predeterminada para firmar etiquetas y confirmaciones si lo desea.

Etiquetas de firma

Si tiene una clave privada GPG configurada, ahora puede usarla para firmar nuevas etiquetas. Todo lo que tienes que hacer es usar -s
en lugar de -a:

$ git tag -s v1.5 -m 'mi etiqueta 1.5 firmada'

Necesita una frase de contraseña para desbloquear la clave secreta


para el usuario: "Ben Straub <ben@straub.cc>" Clave RSA de 2048
bits, ID 800430EB, creada el 04-05-2014

Si ejecuta git show en esa etiqueta, puede ver su firma GPG adjunta:

$ git show v1.5


etiqueta v1.5
Etiquetador: Ben Straub <ben@straub.cc>
Fecha: sábado 3 de mayo 20:29:41 2014 -0700

mi etiqueta 1.5 firmada


-----BEGIN PGP SIGNATURE-----
Versión: GnuPG v1

iQEcBAABAgAGBQJTZbQlAAoJEF0+sviABDDrZbQH/09PfE51KPVPlanr6q1v4/Ut
LQxfojUWiLQdg2ESJItkcuweYg + kc3HCyFejeDIBw9dpXt00rY26p05qrpnG + 85b hM1 /
PswpPLuBSr + oCIDj5GMC2r2iEKsfv2fJbNW8iWAXVLoWZRF8B0MfqX / YTMbm
ecorc4iXzQu7tupRihslbNkfvfciMnSDeSvzCpWAHl7h8Wj6hhqePmLm9lAYqnKp
8S5B/1SSQuEAjRZgI4IexpZoeKGVDptPHxLLS38fozsyi0QyDyzEgJxcJQVMXxVi
RUysgqjcpT8+iQM1PblGfHR4XAhuOqN5Fx06PSaFZhqvWFezJ28/CLyX5q+oIVk=
=EFTF
-----FINALIZAR FIRMA DE PGP-----

commit ca82a6dff817ec66f44342007202690a93763949 Autor:


Scott Chacon <schacon@gee-mail.com> Fecha: lun 17 de marzo
21:52:11 2008 -0700

Cambiar número de versión

Verificación de etiquetas

Para verificar una etiqueta firmada, usa git tag -v <tag-name>. Este comando usa GPG para verificar la firma. Necesita la clave pública
del firmante en su conjunto de claves para que esto funcione correctamente:

240
Machine Translated by Google

$ git tag -v v1.4.2.1 object


883653babd8ee7ea23e6a5c392bb739348b1eb61 type commit
tag v1.4.2.1 tagger Junio C Hamano <junkio@cox.net>
1158138501 -0700

GIT 1.4.2.1

Correcciones menores desde 1.4.2, incluidos git-mv y git-http con alternativas. gpg: Firma
realizada el miércoles 13 de septiembre 02:08:25 PDT de 2006 usando la clave DSA ID F3119B9A
gpg: Buena firma de "Junio C Hamano <junkio@cox.net>" también conocido como "[imagen jpeg de
gpg: tamaño 1513]"
Huella digital de clave principal: 3565 2A26 2040 E066 C9A7 4A7D C0C6 D9A4 F311 9B9A

Si no tiene la clave pública del firmante, obtiene algo como esto:

gpg: Firma realizada el miércoles 13 de septiembre 02:08:25 PDT de 2006 con el ID de clave DSA
F3119B9A gpg: No se puede verificar la firma: clave pública no encontrada error: no se pudo
verificar la etiqueta 'v1.4.2.1'

Confirmaciones de firma

En las versiones más recientes de Git (v1.7.9 y superiores), ahora también puede firmar confirmaciones individuales. Si está
interesado en firmar confirmaciones directamente en lugar de solo las etiquetas, todo lo que necesita hacer es agregar una -S
a su comando de confirmación de git .

$ git commit -a -S -m 'Commit firmado'

Necesita una frase de contraseña para desbloquear la clave secreta


para el usuario: "Scott Chacon (clave de firma de Git) <schacon@gmail.com>"
Clave RSA de 2048 bits, ID 0A46826A, creada el 04-06-2014

[master 5c3386c] Confirmación firmada


4 archivos cambiados, 4 inserciones (+), 24 eliminaciones (-)
reescribir Rakefile (100%) modo de creación 100644 lib/git.rb

Para ver y verificar estas firmas, también hay una opción --show-signature para git log.

241
Machine Translated by Google

$ git log --show-signature -1


compromiso 5c3386cf54bba0a33a32da706aa52bc0155503c2
gpg: Firma realizada el miércoles 4 de junio a las 19:49:17 PDT de 2014 con ID de clave
RSA 0A46826A gpg: Buena firma de "Scott Chacon (clave de firma Git) <schacon@gmail.com>"
Autor: Scott Chacon <schacon@gmail.com>
Fecha: miércoles 4 de junio 19:49:17 2014 -0700

compromiso firmado

Además, puede configurar git log para verificar las firmas que encuentre y enumerarlas en su salida con el %G? formato.

$ git log --pretty="formato:%h %G? %aN %s"

5c3386c G Scott Chacon Confirmación


firmada ca82a6d N Scott Chacon Cambiar el número de
versión 085bb3b N Scott Chacon Eliminar el código de prueba
innecesario a11bef0 N Scott Chacon Confirmación inicial

Aquí podemos ver que solo la última confirmación está firmada y es válida y las confirmaciones anteriores no lo son.

En Git 1.8.3 y versiones posteriores, se puede indicar a git merge y git pull que inspeccionen y rechacen al fusionar
una confirmación que no lleva una firma GPG confiable con el comando --verify-signatures .

Si usa esta opción al fusionar una rama y contiene confirmaciones que no están firmadas ni son válidas, la fusión no
funcionará.

$ git merge --verify-signatures non-verify fatal:


Confirmar ab06180 no tiene una firma GPG.

Si la fusión contiene solo confirmaciones firmadas válidas, el comando de fusión le mostrará todas las firmas que ha
verificado y luego avanzará con la fusión.

$ git merge --verify-signatures firm-branch Commit


13ad65e tiene una buena firma GPG de Scott Chacon (clave de firma de Git)
<schacon@gmail.com> Actualizando 5c3386c..13ad65e Avance rápido LÉAME | 2 ++ 1
archivo cambiado, 2 inserciones (+)

También puede usar la opción -S con el comando git merge para firmar la confirmación de combinación resultante. El
siguiente ejemplo verifica que cada compromiso en la rama que se fusionará esté firmado y, además, firma el
compromiso de fusión resultante.

242
Machine Translated by Google

$ git merge --verify-signatures -S firmed-branch Commit 13ad65e


tiene una buena firma GPG de Scott Chacon (clave de firma Git) <schacon@gmail.com>

Necesita una frase de contraseña para desbloquear la clave


secreta para el usuario: "Scott Chacon (clave de firma de Git)
<schacon@gmail.com>" Clave RSA de 2048 bits, ID 0A46826A, creada el 04-06-2014

Fusión realizada por la estrategia 'recursiva'.


LÉAME | 2 ++ 1
archivo cambiado, 2 inserciones (+)

Todos deben firmar


La firma de etiquetas y compromisos es excelente, pero si decide usar esto en su flujo de trabajo normal, deberá
asegurarse de que todos en su equipo entiendan cómo hacerlo. Si no lo hace, terminará dedicando mucho tiempo a
ayudar a las personas a descubrir cómo reescribir sus confirmaciones con versiones firmadas.
Asegúrese de comprender GPG y los beneficios de firmar cosas antes de adoptar esto como parte de su flujo de
trabajo estándar.

buscando
Con una base de código de casi cualquier tamaño, a menudo necesitará encontrar dónde se llama o define una
función, o mostrar el historial de un método. Git proporciona un par de herramientas útiles para revisar el código y las
confirmaciones almacenadas en su base de datos de forma rápida y sencilla. Revisaremos algunos de ellos.

GitGrep
Git viene con un comando llamado grep que le permite buscar fácilmente a través de cualquier árbol confirmado, el
directorio de trabajo o incluso el índice de una cadena o expresión regular. Para los ejemplos que siguen, buscaremos
en el código fuente de Git.

De forma predeterminada, git grep buscará en los archivos de su directorio de trabajo. Como primera variación, puede
usar cualquiera de las opciones -n o --line-number para imprimir los números de línea donde Git ha encontrado
coincidencias:

243
Machine Translated by Google

$ git grep -n gmtime_r


compat/gmtime.c:3:#undef gmtime_r
gmtime.c:8: compat/gmtime.c:11:struct
return git_gmtime_r(timep,
tm *git_gmtime_r(const
&result);
time_t
compat/
*timep, struct tm *result) compat/gmtime.c:16: ret = gmtime_r(timep, result); compat/mingw.c:826:struct
tm *gmtime_r(const time_t *timep,
time_t
struct
*timep,
tm *resultado)
struct tm *resultado);
compat/mingw.h:206:struct
fecha.c:482: fecha.c:545:
tm *gmtime_r(const

if (gmtime_r(&now, &now_tm)) if
(gmtime_r(&time, tm)) { date.c:758: /
* gmtime_r() in match_digit() puede haberlo aplastado
compat-util.h:1138: */ git- tm *git_gmtime_r(const time_t *,
estructura
estructura tm *); git-compat-util.h:1140:#define gmtime_r git_gmtime_r

Además de la búsqueda básica que se muestra arriba, git grep admite una gran cantidad de otras opciones
interesantes.

Por ejemplo, en lugar de imprimir todas las coincidencias, puede pedirle a git grep que resuma la salida
mostrándole solo qué archivos contenían la cadena de búsqueda y cuántas coincidencias había en cada archivo
con la opción -c o --count :

$ git grep --count gmtime_r


compatibilidad/gmtime.c:4
compatibilidad/mingw.c:1
compatibilidad/mingw.h:1 fecha.c:3

git-compat-util.h:2

Si está interesado en el contexto de una cadena de búsqueda, puede mostrar el método o función adjunto para
cada cadena coincidente con cualquiera de las opciones -p o --show-function :

$ git grep -p gmtime_r *.c


date.c=static int match_multi_number(timestamp_t num, char c, const char *date, date.c:
date.c=static int match_digit(const char *date,
si (gmtime_r(&ahora, struct tm *tm, int *offset, int *tm_gmt) if
&ahora_tm))
(gmtime_r(&time, tm)) { date.c: date.c=int parse_date_basic(const char *date, timestamp_t
*timestamp, int *offset) date.c:

/* gmtime_r() en match_digit() puede haberlo golpeado */

Como puede ver, la rutina gmtime_r se llama desde las funciones match_multi_number y match_digit en el
archivo date.c (la tercera coincidencia mostrada representa solo la cadena que aparece en un comentario).

También puede buscar combinaciones complejas de cadenas con el indicador --and , lo que garantiza que se
produzcan varias coincidencias en la misma línea de texto. Por ejemplo, busquemos cualquier línea que defina
una constante cuyo nombre contenga cualquiera de las subcadenas "LINK" o "BUF_MAX", específicamente en
una versión anterior del código base de Git representado por la etiqueta v1.8.0 (agregaremos la --romper y

244
Machine Translated by Google

--opciones de encabezado que ayudan a dividir la salida en un formato más legible):

$ git grep --break --heading \ -n -e


'#define' --and \( -e LINK -e BUF_MAX \) v1.8.0 v1.8.0:incorporado/index-
pack.c 62:#define FLAG_LINK (1u<<20)

v1.8.0: caché.h
73:#definir S_IFGITLINK 0160000
74:#definir S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK)

v1.8.0:entorno.c
54:#definir OBJECT_CREATION_MODE OBJECT_CREATION_USES_HARDLINKS

v1.8.0:strbuf.c
326: #define STRBUF_MAXLINK (2*PATH_MAX)

v1.8.0:enlaces
simbólicos.c 53:#define FL_SYMLINK (1 << 2)

v1.8.0:zlib.c 30:/
* #define ZLIB_BUF_MAX ((uInt)-1) */ 31:#define
ZLIB_BUF_MAX ((uInt) 1024 * 1024 * 1024) /* 1GB */

El comando git grep tiene algunas ventajas sobre los comandos de búsqueda normales como grep y ack.
La primera es que es muy rápido, la segunda es que puedes buscar en cualquier árbol de Git, no solo en el
directorio de trabajo. Como vimos en el ejemplo anterior, buscamos términos en una versión anterior del código
fuente de Git, no en la versión que estaba actualmente desprotegida.

Búsqueda de registro de Git

Tal vez no esté buscando dónde existe un término, sino cuándo existió o se introdujo. El comando git log tiene
varias herramientas poderosas para encontrar confirmaciones específicas por el contenido de sus mensajes o
incluso el contenido de la diferencia que introducen.

Si, por ejemplo, queremos averiguar cuándo se introdujo originalmente la constante ZLIB_BUF_MAX , podemos
usar la opción -S (coloquialmente conocida como la opción "pickaxe" de Git) para decirle a Git que nos muestre
solo las confirmaciones que cambiaron el número. de ocurrencias de esa cadena.

$ git log -S ZLIB_BUF_MAX --oneline


e01503b zlib: permite alimentar más de 4 GB de una vez ef49a7a
zlib: zlib solo puede procesar 4 GB a la vez

Si observamos la diferencia de esos compromisos, podemos ver que en ef49a7a se introdujo la constante y en
e01503b se modificó.

Si necesita ser más específico, puede proporcionar una expresión regular para buscar con la opción -G .

245
Machine Translated by Google

Búsqueda de registro de línea

Otra búsqueda de registro bastante avanzada que es increíblemente útil es la búsqueda de historial de línea.
Simplemente ejecute git log con la opción -L y le mostrará el historial de una función o línea de código en su
base de código.

Por ejemplo, si quisiéramos ver todos los cambios realizados en la función git_deflate_bound en el archivo
zlib.c , podríamos ejecutar git log -L :git_deflate_bound:zlib.c. Esto intentará descubrir cuáles son los límites de
esa función y luego revisará el historial y nos mostrará cada cambio que se realizó en la función como una serie
de parches que se remontan a cuando se creó la función por primera vez.

$ git log -L :git_deflate_bound:zlib.c commit


ef49a7a0126d64359c974b4b3b71d7ad42ee3bca
Autor: Junio C Hamano <gitster@pobox.com> Fecha:
Vie Jun 10 11:52:15 2011 -0700

zlib: zlib solo puede procesar 4 GB a la vez

diff --git a/zlib.cb/zlib.c --- a/zlib.c


+++ b/zlib.c @@ -85,5 +130,5
@@ -sin firmar long
git_deflate_bound(z_streamp
strm, sin firmar tamaño largo) +largo sin firmar git_deflate_bound(git_zstream
*strm, tamaño largo sin firmar) {

-
return deflateBound(strm, tamaño);
+ return deflateBound(&strm->z, tamaño);
}

commit 225a6f1068f71723a910e8565db4e252b3ca21fa
Autor: Junio C Hamano <gitster@pobox.com> Fecha:
Vie Jun 10 11:18:17 2011 -0700

zlib: ajuste deflateBound() también

diferencia --git a/zlib.cb/zlib.c ---


a/zlib.c +++ b/zlib.c

@@ -81,0 +85,5
@@ +largo sin firmar git_deflate_bound(z_streamp strm, tamaño largo sin
firmar) +{
+ return deflateBound(strm, tamaño);
+}
+

Si Git no puede averiguar cómo hacer coincidir una función o método en su lenguaje de programación, también
puede proporcionarle una expresión regular (o regex). Por ejemplo, esto habría hecho lo mismo que el ejemplo
anterior: git log -L '/unsigned long git_deflate_bound/',/^}/:zlib.c. También puede darle un rango de líneas o un
solo número de línea y obtendrá el mismo tipo de salida.

246
Machine Translated by Google

Reescribiendo la historia
Muchas veces, al trabajar con Git, es posible que desee revisar su historial de confirmación local. Una de las mejores
cosas de Git es que te permite tomar decisiones en el último momento posible. Puede decidir qué archivos van en qué
confirmaciones justo antes de confirmar con el área de preparación, puede decidir que no tenía la intención de trabajar
en algo todavía con git stash, y puede reescribir las confirmaciones que ya sucedieron para que parezcan sucedió de
una manera diferente. Esto puede implicar cambiar el orden de las confirmaciones, cambiar mensajes o modificar
archivos en una confirmación, agrupar o dividir confirmaciones, o eliminar confirmaciones por completo, todo antes de
compartir su trabajo con otros.

En esta sección, verá cómo realizar estas tareas para que pueda hacer que su historial de confirmaciones tenga el
aspecto que desea antes de compartirlo con otros.

No presiones tu trabajo hasta que estés satisfecho con él

Una de las reglas cardinales de Git es que, dado que gran parte del trabajo es local dentro de su
clon, tiene mucha libertad para reescribir su historia localmente. Sin embargo, una vez que empujas
ÿ tu trabajo, es una historia completamente diferente, y debes considerar el trabajo empujado como
definitivo a menos que tengas una buena razón para cambiarlo. En resumen, debe evitar presionar
su trabajo hasta que esté satisfecho con él y listo para compartirlo con el resto del mundo.

Cambiar la última confirmación


Cambiar su compromiso más reciente es probablemente la reescritura más común de la historia que hará. A menudo
querrá hacer dos cosas básicas en su última confirmación: simplemente cambie el mensaje de confirmación o cambie
el contenido real de la confirmación agregando, eliminando y modificando archivos.

Si simplemente desea modificar su último mensaje de confirmación, eso es fácil:

$ git confirmar --enmendar

El comando anterior carga el mensaje de confirmación anterior en una sesión de editor, donde puede realizar cambios
en el mensaje, guardar esos cambios y salir. Cuando guarda y cierra el editor, el editor escribe una nueva confirmación
que contiene ese mensaje de confirmación actualizado y lo convierte en su nueva última confirmación.

Si, por otro lado, desea cambiar el contenido real de su última confirmación, el proceso funciona básicamente de la
misma manera: primero haga los cambios que cree que olvidó, organice esos cambios y la subsiguiente confirmación
de git --amend reemplaza eso última confirmación con su nueva confirmación mejorada.

Debe tener cuidado con esta técnica porque la modificación cambia el SHA-1 de la confirmación. Es como una
reorganización muy pequeña: no modifiques tu última confirmación si ya la has enviado.

247
Machine Translated by Google

Una confirmación modificada puede (o no) necesitar un mensaje de confirmación modificado

Cuando modifica una confirmación, tiene la oportunidad de cambiar tanto el mensaje de


confirmación como el contenido de la confirmación. Si modifica sustancialmente el contenido de
la confirmación, es casi seguro que debería actualizar el mensaje de confirmación para reflejar
ese contenido modificado.

ÿ Por otro lado, si sus enmiendas son suficientemente triviales (corregir un error tipográfico tonto
o agregar un archivo que olvidó preparar) de modo que el mensaje de confirmación anterior
esté bien, simplemente puede hacer los cambios, prepararlos y evitar el editor innecesario.
sesión íntegramente con:

$ git commit --enmendar --no-editar

Cambio de varios mensajes de confirmación

Para modificar una confirmación que está más atrás en su historial, debe pasar a herramientas más complejas. Git
no tiene una herramienta de historial de modificación, pero puede usar la herramienta de reorganización para
reorganizar una serie de confirmaciones en el HEAD en el que se basaron originalmente en lugar de moverlas a
otra. Con la herramienta de reorganización interactiva, puede detenerse después de cada confirmación que desee
modificar y cambiar el mensaje, agregar archivos o hacer lo que desee. Puede ejecutar rebase de forma interactiva
agregando la opción -i a git rebase. Debe indicar cuánto tiempo atrás desea reescribir las confirmaciones diciéndole
al comando en qué confirmación se debe reorganizar.

Por ejemplo, si desea cambiar los últimos tres mensajes de confirmación, o cualquiera de los mensajes de
confirmación en ese grupo, proporcione como argumento a git rebase -i el padre de la última confirmación que
desea editar, que es HEAD~2 ^ o CABEZA~3. Puede ser más fácil recordar el ~3 porque está tratando de editar
las últimas tres confirmaciones, pero tenga en cuenta que en realidad está designando hace cuatro confirmaciones,
el padre de la última confirmación que desea editar:

$ git rebase-i HEAD~3

Recuerde nuevamente que este es un comando de reorganización: cada confirmación en el rango HEAD~3..HEAD
con un mensaje modificado y todos sus descendientes serán reescritos. No incluya ninguna confirmación que ya
haya enviado a un servidor central; hacerlo confundirá a otros desarrolladores al proporcionar una versión alternativa
del mismo cambio.

Ejecutar este comando le brinda una lista de confirmaciones en su editor de texto que se parece a esto:

248
Machine Translated by Google

pick f7f3f6d Cambiar mi nombre un poco pick


310154e Actualizar formato README y agregar culpa pick a5f4a0d
Agregar archivo cat

# Rebase 710f0f8..a5f4a0d en 710f0f8


#
# Comandos: #

p, elige <commit> = usa commit # r, reformula


<commit> = usa commit, pero edita el mensaje de commit # e, edit <commit> = usa commit,
pero deja de modificar # s, squash <commit > = usar confirmación, pero fusionarla con la
confirmación anterior # f, arreglar <commit> = como "squash", pero descartar el mensaje de
registro de esta confirmación # x, exec <comando> = ejecutar comando (el resto de la línea) usando shell #
b , romper = parar aquí (continuar rebase más tarde con 'git rebase --continue') # d, drop <commit> = eliminar
commit # l, label <label> = etiqueta HEAD actual con un nombre # t, reset <label> = restablecer HEAD a una
etiqueta # m, fusionar [-C <commit> | -c <compromiso>] <etiqueta> [# <una línea>] # . # .

cree una confirmación de combinación utilizando el mensaje de la confirmación


de combinación original (o el oneline, si no se especificó ninguna confirmación de
#. combinación original). Use -c <commit> para reformular el mensaje de confirmación.

# # Estas líneas se pueden reordenar; se ejecutan de arriba hacia abajo. #

# Si elimina una línea aquí, ESE COMPROMISO SE PERDERÁ. # # Sin


embargo, si eliminas todo, el rebase será abortado. #

# Tenga en cuenta que las confirmaciones vacías están comentadas

Es importante tener en cuenta que estas confirmaciones se enumeran en el orden opuesto al que normalmente las ve con
el comando de registro . Si ejecuta un registro, verá algo como esto:

$ git log --pretty=format:"%h %s" HEAD~3..HEAD a5f4a0d Agregar


archivo cat

310154e Actualizar el formato README y agregar culpa f7f3f6d


Cambiar un poco mi nombre

Observe el orden inverso. La reorganización interactiva le brinda un script que se ejecutará. Comenzará en la confirmación
que especifique en la línea de comando (HEAD~3) y reproducirá los cambios introducidos en cada una de estas
confirmaciones de arriba a abajo. Enumera el más antiguo en la parte superior, en lugar del más nuevo, porque ese es el
primero que se reproducirá.

Debe editar el script para que se detenga en la confirmación que desea editar. Para hacerlo, cambie la palabra "elegir" a la
palabra "editar" para cada una de las confirmaciones después de las cuales desea que el script se detenga. Por ejemplo,
para modificar solo el tercer mensaje de confirmación, cambie el archivo para que se vea así:

249
Machine Translated by Google

editar f7f3f6d Cambiar un poco mi nombre


elegir 310154e Actualizar formato README y agregar culpa elegir
a5f4a0d Agregar archivo cat

Cuando guarda y sale del editor, Git lo rebobina hasta la última confirmación en esa lista y lo deja en la línea de
comando con el siguiente mensaje:

$ git rebase -i HEAD~3 Se


detuvo en f7f3f6d... Cambia un poco mi nombre Puedes
modificar el compromiso ahora, con

git commit --enmendar

Una vez que esté satisfecho con los cambios, ejecute

git rebase --continuar

Estas instrucciones le dicen exactamente qué hacer. Escribe:

$ git confirmar --enmendar

Cambie el mensaje de confirmación y salga del editor. Entonces corre:

$ git rebase --continuar

Este comando aplicará las otras dos confirmaciones automáticamente, y luego habrá terminado. Si cambia la selección
para editar en más líneas, puede repetir estos pasos para cada confirmación que cambie para editar. Cada vez, Git se
detendrá, le permitirá modificar la confirmación y continuar cuando haya terminado.

Reordenación de confirmaciones

También puede usar rebases interactivos para reordenar o eliminar confirmaciones por completo. Si desea eliminar el
compromiso "Agregar archivo cat" y cambiar el orden en que se introducen los otros dos compromisos, puede cambiar
el script de rebase de esto:

pick f7f3f6d Cambiar mi nombre un poco


pick 310154e Actualizar formato README y agregar culpa pick
a5f4a0d Agregar archivo cat

a esto:

elija 310154e Actualice el formato LÉAME y agregue la culpa elija


f7f3f6d Cambie un poco mi nombre

250
Machine Translated by Google

Cuando guarda y sale del editor, Git rebobina su rama al padre de estas confirmaciones, aplica 310154e y
luego f7f3f6d, y luego se detiene. Efectivamente, cambia el orden de esas confirmaciones y elimina la
confirmación "Agregar archivo cat" por completo.

Compromisos de aplastamiento

También es posible tomar una serie de confirmaciones y agruparlas en una única confirmación con la
herramienta de reorganización interactiva. El script pone instrucciones útiles en el mensaje de rebase:

#
# Comandos:

# p, elige <commit> = usa commit # r,


reformula <commit> = usa commit, pero edita el mensaje de commit # e, edit <commit> =
usa commit, pero deja de modificar # s, squash <commit> = use commit, pero combínelo
con el commit anterior # f, fixup <commit> = como "squash", pero descarte el mensaje de
registro de este commit # x, exec <command> = ejecute el comando (el resto de la línea) usando shell # b,
break = deténgase aquí (continúe con la rebase más tarde con 'git rebase --continue') # d, suelte <commit>
= elimine la confirmación # l, label <label> = etiquete el HEAD actual con un nombre # t, reset <label> =
reinicie HEAD a una etiqueta # m, fusionar [-C <confirmar> | -c <compromiso>] <etiqueta> [# <una línea>] # .

cree una confirmación de combinación utilizando el mensaje de la confirmación


#. de combinación original (o el oneline, si no se especificó ninguna confirmación de
#. combinación original). Use -c <commit> para reformular el mensaje de confirmación.
#

# Estas líneas se pueden reordenar; se ejecutan de arriba hacia abajo. # # Si elimina una línea aquí,
ESE COMPROMISO SE PERDERÁ. #

# Sin embargo, si elimina todo, la reorganización se anulará. # # Tenga en cuenta que las
confirmaciones vacías están comentadas

Si, en lugar de "seleccionar" o "editar", especifica "aplastar", Git aplica tanto ese cambio como el cambio
directamente anterior y hace que fusione los mensajes de confirmación. Entonces, si desea hacer una sola
confirmación de estas tres confirmaciones, haga que el script se vea así:

elegir f7f3f6d Cambiar mi nombre un poco


squash 310154e Actualizar formato README y agregar culpa squash a5f4a0d
Agregar archivo cat

Cuando guarda y sale del editor, Git aplica los tres cambios y luego lo vuelve a colocar en el editor para
fusionar los tres mensajes de confirmación:

251
Machine Translated by Google

# Esta es una combinación de 3 confirmaciones.

# El mensaje del primer compromiso es: Cambiar


un poco mi nombre

# Este es el segundo mensaje de confirmación:

Actualice el formato README y agregue culpa

# Este es el tercer mensaje de confirmación:

Agregar archivo cat

Cuando guardas eso, tienes una única confirmación que introduce los cambios de las tres confirmaciones anteriores.

Dividir una confirmación

Dividir una confirmación deshace una confirmación y luego realiza una etapa parcial y confirma tantas veces como
confirmaciones desee terminar. Por ejemplo, suponga que desea dividir la confirmación del medio de sus tres
confirmaciones. En lugar de "Actualizar el formato LÉAME y agregar culpa", desea dividirlo en dos confirmaciones:
"Actualizar el formato LÉAME" para el primero y "Agregar culpa" para el segundo. Puede hacerlo en el script rebase -i
cambiando la instrucción en la confirmación que desea dividir para "editar":

elija f7f3f6d Cambie un poco mi nombre edite 310154e


Actualice el formato LÉAME y agregue la culpa elija a5f4a0d Agregue el archivo cat

Luego, cuando el script lo lleva a la línea de comando, restablece ese compromiso, toma los cambios que se han
restablecido y crea múltiples compromisos a partir de ellos. Cuando guardas y sales del editor, Git rebobina hasta el padre
de la primera confirmación de tu lista, aplica la primera confirmación (f7f3f6d), aplica la segunda (310154e) y te lleva a la
consola. Allí, puede hacer un restablecimiento mixto de esa confirmación con git reset HEAD^, que efectivamente deshace
esa confirmación y deja los archivos modificados sin preparar. Ahora puede organizar y confirmar archivos hasta que
tenga varias confirmaciones y ejecutar git rebase --continúe cuando haya terminado:

$ git reset HEAD^ $ git add


README $ git commit -m
'Actualizar formato README' $ git add lib/simplegit.rb $ git commit
-m 'Agregar culpa' $ git rebase --continue

Git aplica la última confirmación (a5f4a0d) en el script y su historial se ve así:

252
Machine Translated by Google

$ git log -4 --pretty=format:"%h %s" 1c002dd


Agregar archivo cat
9b29157 Agregar
culpa 35cfb2b Actualizar formato README
f7f3f6d Cambiar un poco mi nombre

Esto cambia los SHA-1 de las tres confirmaciones más recientes en su lista, así que asegúrese de que no aparezca
ninguna confirmación modificada en esa lista que ya haya enviado a un repositorio compartido. Observe que la última
confirmación (f7f3f6d) de la lista no ha cambiado. A pesar de que esta confirmación se muestra en el script, debido a
que se marcó como "elegir" y se aplicó antes de cualquier cambio de reorganización, Git deja la confirmación sin
modificar.

Eliminación de un compromiso

Si desea deshacerse de una confirmación, puede eliminarla usando el script rebase -i . En la lista de confirmaciones,
coloque la palabra "soltar" antes de la confirmación que desea eliminar (o simplemente elimine esa línea del script de
rebase):

pick 461cb2a Esta confirmación está


bien drop 5aecc10 Esta confirmación está rota

Debido a la forma en que Git crea los objetos de confirmación, eliminar o modificar una confirmación provocará la
reescritura de todas las confirmaciones que le siguen. Cuanto más retroceda en el historial de su repositorio, más
confirmaciones deberán recrearse. Esto puede causar muchos conflictos de combinación si tiene muchas confirmaciones
más adelante en la secuencia que dependen de la que acaba de eliminar.

Si llega a la mitad de una rebase como esta y decide que no es una buena idea, siempre puede detenerse.
Escribe git rebase --abort, y tu repositorio volverá al estado en el que estaba antes de que comenzaras el rebase.

Si termina una reorganización y decide que no es lo que desea, puede usar git reflog para recuperar una versión anterior
de su rama. Consulte Recuperación de datos para obtener más información sobre el comando reflog .

ÿ Drew DeVault hizo unaPuede


git rebase. guía práctica con ejercicios
encontrarlo para aprender a usar
en: https://git-rebase.io/

La opción nuclear: filtro-rama


Hay otra opción de reescritura de historial que puede usar si necesita reescribir una mayor cantidad de confirmaciones
de alguna manera programable; por ejemplo, cambiar su dirección de correo electrónico globalmente o eliminar un
archivo de cada confirmación. El comando es filter-branch y puede reescribir grandes franjas de su historial, por lo que
probablemente no debería usarlo a menos que su proyecto aún no sea público y otras personas no hayan basado su
trabajo en las confirmaciones que está a punto de reescribir. . Sin embargo, puede ser muy útil. Aprenderá algunos de
los usos comunes para que pueda tener una idea de algunas de las cosas que es capaz de hacer.

253
Machine Translated by Google

git filter-branch tiene muchas trampas y ya no es la forma recomendada de reescribir el


historial. En su lugar, considere usar git-filter-repo, que es un script de Python que hace un
ÿ mejor trabajo para la mayoría de las aplicaciones en las que normalmente recurriría a filter-
branch. Su documentación y código fuente se pueden encontrar en
https://github.com/newren/git-filter-repo.

Eliminar un archivo de cada confirmación

Esto ocurre con bastante frecuencia. Alguien accidentalmente envía un archivo binario enorme con un agregado
., una contraseña
de git desconsiderado y desea eliminarlo
y desea que de todas partes.
su proyecto Talcódigo
sea de vez accidentalmente envió es
abierto. filter-branch un la
archivo que contenía
herramienta que
probablemente quieras usar para borrar todo tu historial. Para eliminar un archivo llamado contraseñas.txt de
todo su historial, puede usar la opción --tree-filter para filtrar la rama:

$ git filter-branch --tree-filter 'rm -f contraseñas.txt' HEAD Reescribir


6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21)
Ref 'refs/heads/master' fue reescrito

La opción --tree-filter ejecuta el comando especificado después de cada pago del proyecto y luego vuelve a
confirmar los resultados. En este caso, elimina un archivo llamado contraseñas.txt de cada instantánea, ya sea
que exista o no. Si desea eliminar todos los archivos de respaldo del editor comprometidos accidentalmente,
puede ejecutar algo como git filter-branch --tree-filter 'rm -f *~' HEAD.

Podrás ver a Git reescribiendo árboles y confirmaciones y luego mover el puntero de rama al final. Por lo general,
es una buena idea hacer esto en una rama de prueba y luego reiniciar la rama maestra después de haber
determinado que el resultado es lo que realmente desea. Para ejecutar filter-branch en todas sus sucursales,
puede pasar --all al comando.

Hacer de un subdirectorio la nueva raíz

Suponga que ha realizado una importación desde otro sistema de control de código fuente y tiene subdirectorios
que no tienen sentido (troncal, etiquetas, etc.). Si desea que el subdirectorio troncal sea la nueva raíz del proyecto
para cada confirmación, filter-branch también puede ayudarlo a hacerlo:

$ git filter-branch --subdirectory-filter trunk HEAD


Reescribir 856f0bf61e41a27326cdae8f09fe708d679f596f (12/12)
Ref 'refs/heads/master' fue reescrito

Ahora su nueva raíz del proyecto es lo que estaba en el subdirectorio troncal cada vez. Git también eliminará
automáticamente las confirmaciones que no afectaron al subdirectorio.

Cambio de direcciones de correo electrónico globalmente

Otro caso común es que olvidó ejecutar git config para configurar su nombre y dirección de correo electrónico
antes de comenzar a trabajar, o tal vez desee abrir un proyecto en el trabajo y cambiar todas las direcciones de
correo electrónico de su trabajo a su dirección personal. En cualquier caso, también puede cambiar las direcciones
de correo electrónico en varias confirmaciones en un lote con filter-branch . Debe tener cuidado de cambiar sólo el

254
Machine Translated by Google

direcciones de correo electrónico que son suyas, por lo que usa --commit-filter:

'
$ git filter-branch --commit-filter if
ÿ

[ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ];
ÿ

luego
ÿ

GIT_AUTHOR_NAME="Scott Chacón";
ÿ

GIT_AUTHOR_EMAIL="schacon@ejemplo.com";
ÿ

git commit-tree "$@";


ÿ

demás

git commit-tree "$@";


ÿ

Fi' CABEZA

Esto pasa y reescribe cada compromiso para tener su nueva dirección. Porque las confirmaciones contienen
los valores SHA-1 de sus padres, este comando cambia cada confirmación SHA-1 en su historial, no
solo aquellos que tienen la dirección de correo electrónico correspondiente.

Restablecer Desmitificado
Antes de pasar a herramientas más especializadas, hablemos de los comandos de restablecimiento y pago de Git.
Estos comandos son dos de las partes más confusas de Git cuando los encuentras por primera vez. Ellos
hacer tantas cosas que parece inútil entenderlas realmente y emplearlas adecuadamente.
Para ello, recomendamos una simple metáfora.

los tres arboles


Una forma más fácil de pensar en restablecer y pagar es a través del marco mental de Git como contenido
administrador de tres árboles diferentes. Por "árbol" aquí, en realidad queremos decir "colección de archivos", no específicamente
la estructura de datos Hay algunos casos en los que el índice no actúa exactamente como un árbol, pero para nuestro
propósitos, es más fácil pensar en ello de esta manera por ahora.

Git como sistema gestiona y manipula tres árboles en su funcionamiento normal:

Árbol Papel

CABEZA Última instantánea de confirmación, siguiente padre

Índice Instantánea de próxima confirmación propuesta

Directorio de trabajo Salvadera

La cabeza

HEAD es el puntero a la referencia de rama actual, que a su vez es un puntero a la última confirmación
hecho en esa rama. Eso significa que HEAD será el padre de la próxima confirmación que se cree. Es
por lo general, lo más sencillo es pensar en HEAD como la instantánea de tu última confirmación en esa rama.

De hecho, es bastante fácil ver cómo se ve esa instantánea. Aquí hay un ejemplo de obtener el real
lista de directorios y sumas de verificación SHA-1 para cada archivo en la instantánea HEAD:

255
Machine Translated by Google

$ git cat-file -p HEAD árbol


cfda3bf379e4f8dba8717dee55aab78aef7f4daf
autor Scott Chacon 1301511835 -0700 autor
Scott Chacon 1301511835 -0700

Compromiso inicial

$ git ls-tree -r HEAD


100644 blob a906cb2a4a904a152... LÉAME
100644 blob 8f94139338f9404f2... Rakefile 040000
árbol 99f1a6d12cb4b6f19... lib

Los comandos cat-file y ls-tree de Git son comandos de "plomería" que se usan para cosas de nivel inferior y no se usan
realmente en el trabajo diario, pero nos ayudan a ver qué está pasando aquí.

El índice

El índice es su próxima confirmación propuesta. También nos hemos estado refiriendo a este concepto como el "Área de
preparación" de Git, ya que esto es lo que Git mira cuando ejecutas git commit.

Git completa este índice con una lista de todos los contenidos de los archivos que se desprotegieron por última vez en su
directorio de trabajo y cómo se veían cuando se desprotegieron originalmente. Luego reemplaza algunos de esos archivos
con nuevas versiones de ellos, y git commit lo convierte en el árbol para una nueva confirmación.

$ git ls-files -s 100644


a906cb2a4a904a152e80877d4088654daad0c859 0 LÉAME 100644
8f94139338f9404f26296befa88755fc2598c289 0 Rakefile
100644 47c6340d6459e05787f644c2447d2595f5d3a54b 0 lib/simplegit.rb

Nuevamente, aquí estamos usando git ls-files, que es más un comando detrás de escena que le muestra cómo se ve su índice
actualmente.

El índice no es técnicamente una estructura de árbol, en realidad se implementa como un manifiesto aplanado, pero para
nuestros propósitos es lo suficientemente parecido.

El directorio de trabajo

Finalmente, tiene su directorio de trabajo (también conocido comúnmente como el "árbol de trabajo"). Los otros dos árboles
almacenan su contenido de manera eficiente pero inconveniente, dentro de la carpeta .git .
El directorio de trabajo los desempaqueta en archivos reales, lo que hace que sea mucho más fácil editarlos. Piense en el
directorio de trabajo como una caja de arena, donde puede probar los cambios antes de enviarlos a su área de ensayo (índice)
y luego al historial.

256
Machine Translated by Google

$ árbol
.
ÿÿÿ LÉAME
ÿÿÿ Rakefile ÿÿÿ
lib
ÿÿÿ
ÿ

simplegit.rb

1 directorio, 3 archivos

El flujo de trabajo

El flujo de trabajo típico de Git es registrar instantáneas de su proyecto en estados sucesivamente mejores, mediante
la manipulación de estos tres árboles.

Visualicemos este proceso: supongamos que ingresa a un nuevo directorio con un solo archivo. Llamaremos a esto
v1 del archivo, y lo indicaremos en azul. Ahora ejecutamos git init, que creará un repositorio Git con una referencia
HEAD que apunta a la rama maestra no nacida.

257
Machine Translated by Google

En este punto, solo el árbol de directorios de trabajo tiene contenido.

Ahora queremos confirmar este archivo, por lo que usamos git add para tomar contenido en el directorio de trabajo
y copiarlo en el índice.

258
Machine Translated by Google

Luego ejecutamos git commit, que toma el contenido del índice y lo guarda como una instantánea permanente,
crea un objeto de confirmación que apunta a esa instantánea y actualiza el maestro para que apunte a esa
confirmación.

259
Machine Translated by Google

Si ejecutamos git status, no veremos cambios, porque los tres árboles son iguales.

Ahora queremos hacer un cambio en ese archivo y confirmarlo. Pasaremos por el mismo proceso; primero,
cambiamos el archivo en nuestro directorio de trabajo. Llamemos a esto v2 del archivo, e indíquelo en rojo.

260
Machine Translated by Google

Si ejecutamos git status en este momento, veremos el archivo en rojo como "Cambios no preparados para confirmación",
porque esa entrada difiere entre el índice y el directorio de trabajo. A continuación, ejecutamos git add en él para colocarlo
en nuestro índice.

261
Machine Translated by Google

En este punto, si ejecutamos git status, veremos el archivo en verde debajo de "Cambios por confirmar" porque el
índice y HEAD difieren, es decir, nuestra próxima confirmación propuesta ahora es diferente de nuestra última
confirmación. Finalmente, ejecutamos git commit para finalizar la confirmación.

262
Machine Translated by Google

Ahora , el estado de git no nos dará ningún resultado, porque los tres árboles vuelven a ser iguales.

Cambiar de rama o clonar pasa por un proceso similar. Cuando revisa una rama, cambia HEAD para apuntar a la nueva
referencia de rama, completa su índice con la instantánea de esa confirmación y luego copia el contenido del índice en
su directorio de trabajo.

El papel del reinicio

El comando de reinicio tiene más sentido cuando se ve en este contexto.

Para los propósitos de estos ejemplos, digamos que modificamos file.txt nuevamente y lo confirmamos por tercera vez.
Así que ahora nuestra historia se ve así:

263
Machine Translated by Google

Ahora analicemos exactamente qué hace el restablecimiento cuando lo llama. Manipula directamente estos tres árboles
de una manera simple y predecible. Realiza hasta tres operaciones básicas.

Paso 1: Mover CABEZA

Lo primero que hará el reinicio es mover lo que apunta HEAD. Esto no es lo mismo que cambiar HEAD (que es lo que
hace el proceso de pago ); reset mueve la rama a la que apunta HEAD. Esto significa que si HEAD está configurado en
la rama maestra (es decir, actualmente está en la rama maestra ), la ejecución de git reset 9e5e6a4 comenzará haciendo
que el punto maestro sea 9e5e6a4.

264
Machine Translated by Google

No importa qué forma de reinicio con un compromiso invoques, esto es lo primero que siempre intentará hacer.
Con reset --soft, simplemente se detendrá allí.

Ahora tómese un segundo para mirar ese diagrama y darse cuenta de lo que sucedió: esencialmente deshizo
el último comando de confirmación de git . Cuando ejecutas git commit, Git crea un nuevo commit y mueve la
rama a la que apunta HEAD. Cuando restablece de nuevo a HEAD~ (el padre de HEAD), está moviendo la
rama de regreso a donde estaba, sin cambiar el índice o el directorio de trabajo. Ahora puede actualizar el
índice y ejecutar git commit nuevamente para lograr lo que hubiera hecho git commit --amend (consulte Cambio
de la última confirmación).

Paso 2: Actualización del índice (--mixed)

Tenga en cuenta que si ejecuta git status ahora, verá en verde la diferencia entre el índice y el nuevo HEAD.

Lo siguiente que hará el reinicio es actualizar el índice con el contenido de cualquier instantánea a la que
ahora apunta HEAD.

265
Machine Translated by Google

Si especifica la opción --mixed , el restablecimiento se detendrá en este punto. Este también es el valor predeterminado, por lo
que si no especifica ninguna opción (simplemente reinicie HEAD~ en este caso), aquí es donde se detendrá el comando.

Ahora tómese otro segundo para mirar ese diagrama y darse cuenta de lo que sucedió: todavía deshizo su última confirmación,
pero también quitó todo. Regresó a antes de ejecutar todos sus comandos git add y git commit .

Paso 3: Actualización del directorio de trabajo (--hard)

Lo tercero que hará el reinicio es hacer que el directorio de trabajo se vea como el índice. Si usa la opción --hard , continuará
hasta esta etapa.

266
Machine Translated by Google

Así que pensemos en lo que acaba de pasar. Deshiciste tu última confirmación, los comandos git add y git commit , y todo el trabajo que hiciste en

tu directorio de trabajo.

Es importante tener en cuenta que esta marca (--hard) es la única forma de hacer que el comando de reinicio sea peligroso y uno de los pocos

casos en los que Git realmente destruirá los datos. Cualquier otra invocación de reinicio se puede deshacer con bastante facilidad, pero la opción --

hard no, ya que sobrescribe a la fuerza los archivos en el directorio de trabajo. En este caso particular, todavía tenemos la versión v3 de nuestro

archivo en una confirmación en nuestra base de datos de Git, y podríamos recuperarla mirando nuestro reflog, pero si no la hubiéramos confirmado,

Git aún habría sobrescrito el archivo y sería irrecuperable.

Resumen

El comando de reinicio sobrescribe estos tres árboles en un orden específico y se detiene cuando se lo indica:

1. Mueva los puntos HEAD de la rama a (deténgase aquí si es --soft).

2. Haga que el índice se vea como HEAD (deténgase aquí a menos que --hard).

3. Haga que el directorio de trabajo se parezca al índice.

267
Machine Translated by Google

Restablecer con una ruta

Eso cubre el comportamiento del reinicio en su forma básica, pero también puede proporcionarle un camino para
actuar. Si especifica una ruta, el restablecimiento omitirá el paso 1 y limitará el resto de sus acciones a un archivo o
conjunto de archivos específico. En realidad, esto tiene sentido: HEAD es solo un puntero, y no puede señalar parte
de una confirmación y parte de otra. Pero el índice y el directorio de trabajo se pueden actualizar parcialmente, por
lo que el restablecimiento continúa con los pasos 2 y 3.

Entonces, supongamos que ejecutamos git reset file.txt. Este formulario (ya que no especificó un compromiso
SHA-1 o rama, y no especificó --soft o --hard) es una abreviatura de git reset --mixed HEAD file.txt, que hará lo
siguiente:

1. Mueva los puntos HEAD de la rama a (omitido).

2. Haz que el índice parezca HEAD (detente aquí).

Entonces, esencialmente solo copia file.txt de HEAD al índice.

Esto tiene el efecto práctico de desorganizar el archivo. Si miramos el diagrama de ese comando y pensamos en lo
que hace git add , son exactamente opuestos.

268
Machine Translated by Google

Esta es la razón por la que la salida del comando git status sugiere que ejecute esto para eliminar un archivo (consulte Desactivar un
archivo almacenado para obtener más información al respecto).

Podríamos fácilmente no dejar que Git asuma que queremos decir "extraer los datos de HEAD" especificando un compromiso específico
para extraer esa versión del archivo. Simplemente ejecutaríamos algo como git reset eb43bf file.txt.

269
Machine Translated by Google

Esto efectivamente hace lo mismo que si hubiéramos revertido el contenido del archivo a v1 en el directorio de
trabajo, ejecutado git add y luego revertido a v3 nuevamente (sin realmente seguir todos esos pasos). Si
ejecutamos git commit ahora, registrará un cambio que revertirá ese archivo a v1, aunque en realidad nunca lo
volvimos a tener en nuestro directorio de trabajo.

También es interesante notar que, al igual que git add, el comando de reinicio aceptará una opción --patch para
eliminar el contenido pieza por pieza. Por lo tanto, puede eliminar o revertir contenido de forma selectiva.

aplastamiento

Veamos cómo hacer algo interesante con este nuevo poder: aplastar confirmaciones.

Digamos que tiene una serie de confirmaciones con mensajes como "ups", "WIP" y "olvidé este archivo". Puede
usar el reinicio para agruparlos rápida y fácilmente en una sola confirmación que lo haga lucir realmente inteligente.
Squashing Commits muestra otra forma de hacer esto, pero en este ejemplo es más sencillo usar reset.

Digamos que tiene un proyecto donde la primera confirmación tiene un archivo, la segunda confirmación agregó
un nuevo archivo y cambió el primero, y la tercera confirmación cambió el primer archivo nuevamente. El segundo
compromiso fue un trabajo en progreso y desea aplastarlo.

270
Machine Translated by Google

Puede ejecutar git reset --soft HEAD~2 para mover la rama HEAD a una confirmación anterior (la confirmación
más reciente que desea conservar):

271
Machine Translated by Google

Y luego simplemente ejecute git commit nuevamente:

272
Machine Translated by Google

Ahora puede ver que su historial accesible, el historial que enviaría, ahora parece que tuvo una confirmación
con el archivo-a.txt v1, luego una segunda que modificó el archivo-a.txt a v3 y agregó el archivo b.txt. La
confirmación con la versión v2 del archivo ya no está en el historial.

Echale un vistazo

Finalmente, puede preguntarse cuál es la diferencia entre pagar y restablecer . Al igual que restablecer, el
proceso de pago manipula los tres árboles, y es un poco diferente dependiendo de si le da al comando una
ruta de archivo o no.

273
Machine Translated by Google

sin caminos

Ejecutar git checkout [branch] es bastante similar a ejecutar git reset --hard [branch] en el sentido de que actualiza los tres
árboles para que se vea como [branch], pero hay dos diferencias importantes.

Primero, a diferencia de reset --hard, checkout es seguro para el directorio de trabajo; verificará para asegurarse de que
no está eliminando los archivos que tienen cambios. En realidad, es un poco más inteligente que eso: intenta hacer una
combinación trivial en el directorio de trabajo, por lo que se actualizarán todos los archivos que no haya cambiado. reset --
hard, por otro lado, simplemente reemplazará todo en todos los ámbitos sin verificar.

La segunda diferencia importante es cómo el proceso de pago actualiza HEAD. Mientras que el restablecimiento moverá
la rama a la que apunta HEAD, el checkout moverá HEAD para que apunte a otra rama.

Por ejemplo, supongamos que tenemos ramas maestras y de desarrollo que apuntan a diferentes compromisos, y
actualmente estamos en desarrollo (entonces HEAD lo señala). Si ejecutamos git reset master, el desarrollo ahora
apuntará a la misma confirmación que hace master . Si, en cambio, ejecutamos git checkout master, el desarrollo no se
mueve, HEAD sí lo hace. HEAD ahora apuntará al maestro.

Entonces, en ambos casos estamos moviendo HEAD al punto de cometer A, pero la forma en que lo hacemos es muy
diferente. reset moverá los puntos HEAD de la rama, checkout mueve HEAD mismo.

con caminos

La otra forma de ejecutar el pago es con una ruta de archivo que, al igual que el restablecimiento, no mueve HEAD. Es
como el archivo git reset [branch] en el sentido de que actualiza el índice con ese archivo en ese compromiso, pero también

274
Machine Translated by Google

sobrescribe el archivo en el directorio de trabajo. Sería exactamente como git reset --hard [branch] file
(si el reinicio le permitiera ejecutar eso), no es seguro para el directorio de trabajo y no mueve HEAD.

Además, al igual que git reset y git add, el proceso de pago aceptará una opción --patch para permitirle seleccionar
revertir el contenido del archivo trozo a trozo.

Resumen
Esperemos que ahora entienda y se sienta más cómodo con el comando de reinicio , pero
probablemente todavía esté un poco confundido acerca de cómo difiere exactamente del proceso de pago y posiblemente no podría
recuerda todas las reglas de las diferentes invocaciones.

Aquí hay una hoja de trucos para saber qué comandos afectan a qué árboles. La columna "HEAD" dice "REF" si

ese comando mueve la referencia (rama) a la que apunta HEAD, y "HEAD" si mueve HEAD
sí mismo. Preste especial atención al 'WD Safe?' columna: si dice NO, tómese un segundo para pensar antes
ejecutando ese comando.

CABEZA Índice Workdir WD seguro?

Nivel de compromiso

restablecer --soft [comprometer] ÁRBITRO NO NO SÍ

restablecer [comprometer] ÁRBITRO SÍ NO SÍ

reset --hard [confirmar] ÁRBITRO SÍ SÍ NO

pagar <confirmar> CABEZA SÍ SÍ SÍ

Nivel de archivo

restablecer [confirmar] <rutas> NO SÍ NO SÍ

pago [confirmar] <rutas> NO SÍ SÍ NO

Fusión avanzada
La fusión en Git suele ser bastante fácil. Dado que Git facilita la fusión de otra rama múltiple
veces, significa que puede tener una rama de larga duración pero puede mantenerla actualizada a medida que avanza,
resolver pequeños conflictos a menudo, en lugar de ser sorprendido por un gran conflicto al final de la
serie.

Sin embargo, a veces ocurren conflictos complicados. A diferencia de otros sistemas de control de versiones, Git no
No intente ser demasiado inteligente sobre la resolución de conflictos de fusión. La filosofía de Git es ser inteligente sobre
determinar cuándo una resolución de fusión es inequívoca, pero si hay un conflicto, no intenta ser
ingenioso para resolverlo automáticamente. Por lo tanto, si espera demasiado para fusionar dos ramas que
divergen rápidamente, puede encontrarse con algunos problemas.

En esta sección, repasaremos cuáles podrían ser algunos de esos problemas y qué herramientas te brinda Git para ayudarte.
manejar estas situaciones más difíciles. También cubriremos algunos de los diferentes tipos no estándar de
fusiones que puede hacer, así como ver cómo revertir las fusiones que ha hecho.

275
Machine Translated by Google

Fusionar conflictos

Si bien cubrimos algunos conceptos básicos sobre la resolución de conflictos de fusión en Conflictos de fusión básicos,
para conflictos más complejos, Git proporciona algunas herramientas para ayudarlo a descubrir qué está sucediendo y
cómo manejar mejor el conflicto.

En primer lugar, si es posible, intente asegurarse de que su directorio de trabajo esté limpio antes de realizar una
combinación que pueda tener conflictos. Si tiene trabajo en curso, confírmelo en una rama temporal o guárdelo. Esto hace
que puedas deshacer cualquier cosa que intentes aquí. Si tiene cambios sin guardar en su directorio de trabajo cuando
intenta una combinación, algunos de estos consejos pueden ayudarlo a conservar ese trabajo.

Veamos un ejemplo muy simple. Tenemos un archivo Ruby súper simple que imprime 'hola mundo'.

#! /usr/bin/env rubí

def hola
pone 'hola mundo' al
final

Hola()

En nuestro repositorio, creamos una nueva rama llamada espacio en blanco y procedemos a cambiar todos los finales de
línea de Unix a finales de línea de DOS, esencialmente cambiando cada línea del archivo, pero solo con espacios en
blanco. Luego cambiamos la línea “hola mundo” por “hola mundo”.

276
Machine Translated by Google

$ git checkout -b espacio en blanco


Cambiado a una nueva rama 'espacio en blanco'

$ unix2dos hello.rb
unix2dos: convirtiendo el archivo hello.rb a formato DOS ...
$ git commit -am 'Convertir hello.rb a DOS' [espacio en
blanco 3270f76] Convertir hello.rb a DOS 1 archivo
cambiado, 7 inserciones (+) , 7 eliminaciones (-)

$ vim hola.rb $
git diff -b diff --
git a/hola.rb b/hola.rb índice
ac51efd..e85207e 100755
--- a/hola.rb
+++ b/hola.rb
@@ -1,7 +1,7
@@ #! /usr/bin/env rubí

def hola -
pone 'hola mundo' +
pone 'hola mundo'^M fin

Hola()

$ git commit -am 'Usar español en lugar de inglés' [espacio


en blanco 6d338d2] Usar español en lugar de inglés 1
archivo cambiado, 1 inserción (+), 1 eliminación (-)

Ahora volvemos a nuestra rama maestra y agregamos documentación para la función.

277
Machine Translated by Google

$ git pago maestro


Cambiado a la rama 'maestro'

$ vim hola.rb $
git diff diff --git a/
hola.rb b/hola.rb index ac51efd..36c06c8
100755 --- a/hola.rb +++ b/hola.rb @@
-1,5 +1,6 @@ #! /usr/bin/env rubí

+# imprime un saludo def


hola pone 'hola mundo' fin

$ git commit -am 'Agregar comentario documentando la


función' [master bec6336] Agregar comentario documentando la
función 1 archivo cambiado, 1 inserción (+)

Ahora tratamos de fusionar nuestra rama de espacios en blanco y obtendremos conflictos debido a los cambios en los espacios
en blanco.

$ git merge whitespace


Fusión automática hello.rb
CONFLICTO (contenido): Conflicto de fusión en hello.rb
Fusión automática fallida; solucione los conflictos y luego confirme el resultado.

Cancelar una fusión

Ahora tenemos algunas opciones. Primero, veamos cómo salir de esta situación. Si tal vez no esperabas conflictos y no quieres
lidiar con la situación todavía, simplemente puedes salir de la fusión con git merge --abort.

$ git status -sb ##


maestro UU hola.rb

$ git fusionar --abortar

$ git estado -sb ##


maestro

La opción git merge --abort intenta volver a su estado antes de ejecutar la combinación. Los únicos casos en los que es posible
que no pueda hacer esto a la perfección serían si tuviera cambios no guardados y no confirmados en su directorio de trabajo
cuando lo ejecutó; de lo contrario, debería funcionar bien.

278
Machine Translated by Google

Si por alguna razón solo desea comenzar de nuevo, también puede ejecutar git reset --hard HEAD, y su repositorio volverá al último
estado comprometido. Recuerde que cualquier trabajo no comprometido se perderá, así que asegúrese de no querer ninguno de sus
cambios.

Ignorando espacios en blanco

En este caso específico, los conflictos están relacionados con espacios en blanco. Sabemos esto porque el caso es simple, pero
también es bastante fácil darse cuenta en casos reales cuando se observa el conflicto porque cada línea se elimina de un lado y se
vuelve a agregar en el otro. De forma predeterminada, Git ve todas estas líneas como modificadas, por lo que no puede fusionar los
archivos.

Sin embargo, la estrategia de combinación predeterminada puede admitir argumentos, y algunos de ellos tratan de ignorar correctamente
los cambios de espacios en blanco. Si ve que tiene muchos problemas de espacios en blanco en una combinación, simplemente puede
anularla y volver a hacerlo, esta vez con -Xignore-all-space o -Xignore-space-change. La primera opción ignora completamente los
espacios en blanco al comparar líneas, la segunda trata las secuencias de uno o más espacios en blanco como equivalentes.

$ git merge -Xignore-space-change whitespace


Fusión automática hello.rb Fusión realizada por
la estrategia 'recursiva'. hola.rb | 2 +- 1 archivo
cambiado, 1 inserción (+), 1 borrado (-)

Dado que en este caso, los cambios reales en el archivo no estaban en conflicto, una vez que ignoramos los cambios en los espacios
en blanco, todo se fusiona perfectamente.

Este es un salvavidas si tiene a alguien en su equipo a quien le gusta volver a formatear todo de vez en cuando, desde espacios a
pestañas o viceversa.

Refusión manual de archivos

Aunque Git maneja bastante bien el preprocesamiento de espacios en blanco, hay otros tipos de cambios que tal vez Git no pueda
manejar automáticamente, pero son correcciones programables. Como ejemplo, supongamos que Git no puede manejar el cambio de
espacio en blanco y necesitamos hacerlo a mano.

Lo que realmente tenemos que hacer es ejecutar el archivo que estamos tratando de fusionar a través de un programa dos2unix antes
de intentar la fusión de archivos real. Entonces, ¿cómo haríamos eso?

Primero, entramos en el estado de conflicto de fusión. Luego queremos obtener copias de mi versión del archivo, su versión (de la rama
en la que nos estamos fusionando) y la versión común (desde donde ambos lados se bifurcaron). Luego, queremos arreglar su lado o
el nuestro y volver a intentar la fusión nuevamente solo para este único archivo.

Obtener las tres versiones de archivo es bastante fácil. Git almacena todas estas versiones en el índice bajo "etapas", cada una de las
cuales tiene números asociados. La etapa 1 es el ancestro común, la etapa 2 es su versión y la etapa 3 es de MERGE_HEAD, la
versión que está fusionando ("la de ellos").

Puede extraer una copia de cada una de estas versiones del archivo en conflicto con el comando git show y una sintaxis especial.

279
Machine Translated by Google

$ git show :1:hola.rb > hola.común.rb $ git


show :2:hola.rb > hola.nuestro.rb $ git show
:3:hola.rb > hola.los suyos.rb

Si desea obtener un poco más de núcleo duro, también puede usar el comando ls-files -u plumbing para obtener los
SHA-1 reales de los blobs de Git para cada uno de estos archivos.

$ GIT LS-Files -U
100755 AC51EFDC3DF4F4FD328D1A02AD05331D8E2C9111 1 HELLO.RB
100755 36C06C8752C78D2AFF89571132F3BF7841A7B5C3 2 HELLO.RB
100755 E85207E04DFDD5EB0A1E9OBBBC67FD837C44A1CD 3 HELLO.RB

El :1:hello.rb es solo una forma abreviada de buscar ese blob SHA-1.

Ahora que tenemos el contenido de las tres etapas en nuestro directorio de trabajo, podemos corregir el suyo manualmente
para solucionar el problema de los espacios en blanco y volver a fusionar el archivo con el poco conocido comando git
merge-file que hace precisamente eso.

$ dos2unix hello.theirs.rb
dos2unix: convirtiendo el archivo hello.theirs.rb a formato Unix ...

$ git merge-file -p \
hola.nuestro.rb hola.común.rb hola.los suyos.rb > hola.rb

$ git diff -b diff


--cc hola.rb índice
36c06c8,e85207e..0000000 --- a/
hola.rb +++ b/hola.rb @@@ -1,8
-1,7 +1,8 @@@ #! /usr/bin/env
rubí

+# imprime un saludo def


hola
- pone 'hola mundo' +
pone 'hola mundo' fin

Hola()

En este punto, hemos fusionado muy bien el archivo. De hecho, esto funciona mejor que la opción de ignorar el cambio
de espacio porque en realidad corrige los cambios de espacio en blanco antes de fusionarlos en lugar de simplemente
ignorarlos. En la fusión ignorar-cambio de espacio , en realidad terminamos con unas pocas líneas con terminaciones de
línea de DOS, haciendo que las cosas se mezclen.

Si desea tener una idea antes de finalizar este compromiso sobre lo que realmente cambió entre un lado y el otro, puede
pedirle a git diff que compare lo que hay en su directorio de trabajo que

280
Machine Translated by Google

está a punto de comprometerse como resultado de la fusión en cualquiera de estas etapas. Vamos a repasarlos todos.

Para comparar su resultado con lo que tenía en su rama antes de la fusión, en otras palabras, para ver qué introdujo la
fusión, puede ejecutar git diff --ours:

$ git diff --ours *


Ruta no fusionada hello.rb
diff --git a/hello.rb b/hello.rb index
36c06c8..44d0a25 100755
--- a/hola.rb ++
+ b/hola.rb @@
-2,7 +2,7 @@

# imprime un saludo def


hola - pone 'hola mundo' +
pone 'hola mundo' end

Hola()

Entonces, aquí podemos ver fácilmente que lo que sucedió en nuestra rama, lo que en realidad estamos introduciendo
en este archivo con esta fusión, es cambiar esa línea única.

Si queremos ver cómo el resultado de la fusión difiere de lo que estaba de su lado, puede ejecutar git diff --theirs. En este
ejemplo y en el siguiente, tenemos que usar -b para quitar el espacio en blanco porque lo estamos comparando con lo
que está en Git, no con nuestro archivo limpio hello.theirs.rb .

$ git diff --theirs -b * Ruta


no fusionada hello.rb diff --
git a/hello.rb b/hello.rb index
e85207e..44d0a25 100755
--- a/hola.rb ++
+ b/hola.rb @@
-1,5 +1,6 @@ #! /
usr/bin/env rubí

+# imprime un saludo def


hello pone 'hello mundo' end

Finalmente, puedes ver cómo el archivo ha cambiado desde ambos lados con git diff --base.

281
Machine Translated by Google

$ git diff --base -b *


Ruta no fusionada hello.rb
diff --git a/hello.rb b/hello.rb index
ac51efd..44d0a25 100755 --- a/
hello.rb +++ b/hello.rb @@ -1,7 +1,8
@@ #! /usr/bin/env rubí

+# imprime un saludo def


hello - pone 'hola mundo' +
pone 'hola mundo' end

Hola()

En este punto, podemos usar el comando git clean para borrar los archivos adicionales que creamos para hacer la
combinación manual pero que ya no necesitamos.

$ git clean -f
Eliminando hello.common.rb
Eliminando hello.ours.rb
Eliminando hello.theirs.rb

Comprobación de conflictos

Tal vez no estemos contentos con la resolución en este punto por alguna razón, o tal vez la edición manual de uno o
ambos lados todavía no funcionó bien y necesitamos más contexto.

Cambiemos un poco el ejemplo. Para este ejemplo, tenemos dos ramas de mayor duración que tienen algunas
confirmaciones pero crean un conflicto de contenido legítimo cuando se fusionan.

$ git log --graph --oneline --decorate --all * f1270f7


(HEAD, master) Actualizar README * 9af9d3b
Crear README * 694971d Actualizar frase a 'hola
world' | * e3eb223 (mundo) Añadir más pruebas | *
7cff591 Crear script de prueba inicial | * c3ffff1
Cambiar texto a 'hola mundo' |/ * b7dcc89 Código
inicial de hola mundo

Ahora tenemos tres confirmaciones únicas que viven solo en la rama maestra y otras tres que viven en la rama mundo . Si
intentamos fusionar la rama mundo , tenemos un conflicto.

282
Machine Translated by Google

$ git merge mundo


Fusión automática hello.rb
CONFLICTO (contenido): Conflicto de fusión en hello.rb Fusión
automática fallida; solucione los conflictos y luego confirme el resultado.

Nos gustaría ver cuál es el conflicto de fusión. Si abrimos el archivo, veremos algo como esto:

#! /usr/bin/env rubí

definitivamente hola

<<<<<<< CABEZA

pone 'hola mundo'


=======

pone 'hola mundo' >>>>>>>


mundo
final

Hola()

Ambos lados de la combinación agregaron contenido a este archivo, pero algunas de las confirmaciones modificaron el
archivo en el mismo lugar que causó este conflicto.

Exploremos un par de herramientas que ahora tiene a su disposición para determinar cómo surgió este conflicto. Tal vez no
sea obvio cómo exactamente debe solucionar este conflicto. Necesitas mas
contexto.

Una herramienta útil es git checkout con la opción --conflict . Esto volverá a verificar el archivo nuevamente y reemplazará los
marcadores de conflicto de combinación. Esto puede ser útil si desea restablecer los marcadores e intentar resolverlos
nuevamente.

Puede pasar --conflict ya sea diff3 o merge (que es el predeterminado). Si lo pasa diff3, Git usará una versión ligeramente
diferente de los marcadores de conflicto, no solo brindándole las versiones "nuestra" y "suya", sino también la versión "base"
en línea para brindarle más contexto.

$ git checkout --conflict=diff3 hola.rb

Una vez que ejecutemos eso, el archivo se verá así:

283
Machine Translated by Google

#! /usr/bin/env rubí

definitivamente hola

<<<<<<< nuestro

pone 'hola mundo' |||||||


base pone 'hola mundo'

=======

pone 'hola mundo'


>>>>>>> suyos
final

Hola()

Si le gusta este formato, puede configurarlo como predeterminado para futuros conflictos de combinación configurando la
configuración merge.conflictstyle en diff3.

$ git config --global merge.conflictstyle diff3

El comando git checkout también puede tomar las opciones --ours y --theirs , lo que puede ser una forma realmente rápida de
elegir un lado o el otro sin fusionar cosas en absoluto.

Esto puede ser particularmente útil para conflictos de archivos binarios donde simplemente puede elegir un lado, o donde solo
desea fusionar ciertos archivos desde otra rama: puede fusionar y luego extraer ciertos archivos de un lado u otro antes de
confirmar .

Combinar registro

Otra herramienta útil para resolver conflictos de fusión es git log. Esto puede ayudarlo a obtener contexto sobre lo que pudo
haber contribuido a los conflictos. Revisar un poco de historia para recordar por qué dos líneas de desarrollo tocaban la misma
área de código puede ser realmente útil a veces.

Para obtener una lista completa de todas las confirmaciones únicas que se incluyeron en cualquiera de las ramas involucradas
en esta fusión, podemos usar la sintaxis de "punto triple" que aprendimos en Punto triple.

$ git log --oneline --left-right HEAD...MERGE_HEAD < f1270f7


Actualizar LÉAME < 9af9d3b Crear LÉAME < 694971d Actualizar
frase a 'hola world' > e3eb223 Agregar más pruebas > 7cff591
Crear script de prueba inicial > c3ffff1 Cambiar texto a ' hola mundo'

Esa es una buena lista de los seis compromisos totales involucrados, así como también en qué línea de desarrollo estaba
cada compromiso.

Sin embargo, podemos simplificar aún más esto para darnos un contexto mucho más específico. Si añadimos el --merge

284
Machine Translated by Google

opción para git log, solo mostrará las confirmaciones en cualquier lado de la combinación que toque un archivo que actualmente
está en conflicto.

$ git log --oneline --left-right --merge < 694971d


Actualizar frase a 'hola world' > c3ffff1 Cambiar
texto a 'hello mundo'

Si ejecuta eso con la opción -p en su lugar, obtiene solo las diferencias con el archivo que terminó en conflicto.
Esto puede ser realmente útil para brindarle rápidamente el contexto que necesita para ayudarlo a comprender por qué algo
entra en conflicto y cómo resolverlo de manera más inteligente.

Formato diferencial combinado

Dado que Git organiza cualquier resultado de fusión que tenga éxito, cuando ejecuta git diff mientras se encuentra en un estado
de fusión en conflicto, solo obtiene lo que todavía está en conflicto. Esto puede ser útil para ver lo que aún tiene que resolver.

Cuando ejecuta git diff directamente después de un conflicto de combinación, le dará información en un formato de salida de
diferencia bastante único.

$ git diff diff


--cc hola.rb
index 0399cd5,59727f0..0000000
--- a/hola.rb +++ b/hola.rb @@@
-1,7 -1,7 +1,11 @@@ #! /usr/bin/
env rubí

definitivamente hola

++<<<<<<<
CABEZA + pone 'hola mundo'
++=======
+ pone 'hola mundo' +
+>>>>>>> mundo
final

Hola()

El formato se llama "Diferencia combinada" y le brinda dos columnas de datos al lado de cada línea. La primera columna le
muestra si esa línea es diferente (agregada o eliminada) entre la rama "nuestra" y el archivo en su directorio de trabajo y la
segunda columna hace lo mismo entre la rama "suya" y la copia de su directorio de trabajo.

Entonces, en ese ejemplo, puede ver que las líneas <<<<<<< y >>>>>>> están en la copia de trabajo pero no estaban en ninguno
de los lados de la fusión. Esto tiene sentido porque la herramienta de combinación los colocó allí para nuestro contexto, pero se
espera que los eliminemos.

Si resolvemos el conflicto y volvemos a ejecutar git diff , veremos lo mismo, pero es un poco más

285
Machine Translated by Google

útil.

$ vim hola.rb $
git diff diff --cc
hola.rb index
0399cd5,59727f0..0000000 --- a/
hola.rb +++ b/hola.rb @@@ -1,7
-1,7 + 1,7 @@@ #! /usr/bin/env
rubí

def hola -
pone 'hola mundo' -
pone 'hola mundo' ++
pone 'hola mundo' end

Hola()

Esto nos muestra que “hola mundo” estaba en nuestro lado pero no en la copia de trabajo, que “hola mundo”
estaba en su lado pero no en la copia de trabajo y finalmente que “hola mundo” no estaba en ninguno de los
lados pero ahora está en la copia de trabajo. Esto puede ser útil para revisar antes de confirmar la resolución.

También puede obtener esto del registro de git para cualquier combinación para ver cómo se resolvió algo después del hecho. Git
generará este formato si ejecuta git show en una confirmación de combinación, o si agrega una opción --cc a un registro de git -p
(que de forma predeterminada solo muestra parches para confirmaciones que no son de combinación).

286
Machine Translated by Google

$ git registro --cc -p -1


confirmar 14f41939956d80b9e17bb8721354c33f8d5b5a79
Fusionar: f1270f7 e3eb223
Autor: Scott Chacon <schacon@gmail.com> Fecha:
viernes 19 de septiembre 18:14:49 2014 +0200

Fusionar rama 'mundo'

Conflictos:
ÿ

hola.rb

diferencia --cc hola.rb


índice 0399cd5,59727f0..e1d0799
--- a/hola.rb
+++ b/hola.rb
@@@ -1,7 -1,7 +1,7
@@@ #! /usr/bin/env rubí

def hola -
pone 'hola mundo' - pone
'hola mundo' ++ pone 'hola
mundo' end

Hola()

Deshacer fusiones
Ahora que sabe cómo crear una confirmación de combinación, probablemente haga algunos por error. Una de las mejores
cosas de trabajar con Git es que está bien cometer errores, porque es posible (y en muchos casos fácil) corregirlos.

Las confirmaciones de fusión no son diferentes. Digamos que comenzó a trabajar en una rama de tema, la fusionó
accidentalmente con la principal y ahora su historial de confirmaciones se ve así:

287
Machine Translated by Google

Figura 137. Confirmación de fusión accidental

Hay dos formas de abordar este problema, dependiendo de cuál sea el resultado deseado.

Corrige las referencias

Si la confirmación de combinación no deseada solo existe en su repositorio local, la mejor y más fácil solución es mover las ramas
para que apunten a donde usted quiere. En la mayoría de los casos, si sigue el errante git merge con git reset --hard HEAD~, esto
restablecerá los punteros de rama para que se vean así:

Figura 138. Historial después de git reset --hard HEAD~

Cubrimos el restablecimiento en Restablecer desmitificado, por lo que no debería ser demasiado difícil averiguar qué está pasando
aquí. Aquí hay un repaso rápido: restablecer --hard generalmente pasa por tres pasos:

1. Mueva los puntos HEAD de la rama. En este caso, queremos mover el maestro a donde estaba antes de la confirmación de
fusión (C6).

2. Haz que el índice se parezca a HEAD.

288
Machine Translated by Google

3. Haga que el directorio de trabajo se parezca al índice.

La desventaja de este enfoque es que está reescribiendo el historial, lo que puede ser problemático con un repositorio
compartido. Consulte The Perils of Rebasing para obtener más información sobre lo que puede suceder; la versión corta
es que si otras personas tienen las confirmaciones que estás reescribiendo, probablemente deberías evitar reiniciar. Este
enfoque tampoco funcionará si se han creado otras confirmaciones desde la fusión; mover los árbitros efectivamente
perdería esos cambios.

Invertir el compromiso

Si mover los punteros de rama no funciona para usted, Git le da la opción de hacer una nueva confirmación que deshace
todos los cambios de uno existente. Git llama a esta operación "revertir" y, en este escenario particular, la invocaría así:

$ git revert -m 1 HEAD


[master b1d8379] Revertir "Fusionar rama 'tema'"

El indicador -m 1 indica qué padre es la "línea principal" y debe mantenerse. Cuando invoca una combinación en HEAD
(tema de fusión de git), la nueva confirmación tiene dos padres: el primero es HEAD (C6) y el segundo es la punta de la
rama que se está fusionando (C4). En este caso, queremos deshacer todos los cambios introducidos al fusionar en el
padre #2 (C4), manteniendo todo el contenido del padre #1 (C6).

El historial con la confirmación de reversión se ve así:

Figura 139. Historial después de git revert -m 1

La nueva confirmación ^M tiene exactamente el mismo contenido que C6, por lo que a partir de aquí es como si la fusión
nunca hubiera ocurrido, excepto que las confirmaciones ahora no fusionadas todavía están en el historial de HEAD . Git se
confundirá si intenta fusionar el tema en el maestro nuevamente:

$ git merge topic


Ya está actualizado.

No hay nada en el tema que no esté accesible desde el maestro. Lo que es peor, si agrega trabajo al tema y lo fusiona
nuevamente, Git solo traerá los cambios desde la fusión revertida:

289
Machine Translated by Google

Figura 140. Historial con una mala fusión

La mejor manera de evitar esto es anular la fusión original, ya que ahora desea incorporar los cambios que se
revirtieron y luego crear una nueva confirmación de fusión:

$ git revert ^M
[master 09f0126] Revert "Revertir "Fusionar rama 'tema'""
$ git merge topic

Figura 141. Historial después de volver a fusionar una fusión revertida

En este ejemplo, M y ^M se cancelan. ^^M se fusiona efectivamente en los cambios de C3 y C4, y C8 se fusiona en
los cambios de C7, por lo que ahora el tema está totalmente fusionado.

Otros tipos de fusiones


Hasta ahora hemos cubierto la fusión normal de dos ramas, normalmente manejada con lo que se llama la estrategia
de fusión "recursiva". Sin embargo, hay otras formas de fusionar ramas. Vamos a cubrir algunos de ellos rápidamente.

Preferencia nuestra o de ellos

En primer lugar, hay otra cosa útil que podemos hacer con el modo de fusión "recursivo" normal.
Ya hemos visto las opciones ignore-all-space e ignore-space-change que se pasan con una -X , pero también
podemos decirle a Git que favorezca un lado o el otro cuando vea un conflicto.

De manera predeterminada, cuando Git ve un conflicto entre dos ramas que se fusionan, agregará marcadores de
conflicto de fusión en su código y marcará el archivo como en conflicto y le permitirá resolverlo. Si prefiere que Git
simplemente elija un lado específico e ignore el otro lado en lugar de dejarlo manualmente

290
Machine Translated by Google

resolver el conflicto, puede pasar el comando de combinación ya sea -Xours o -Xtheirs.

Si Git ve esto, no agregará marcadores de conflicto. Cualquier diferencia que sea fusionable, se fusionará.
Cualquier diferencia que entre en conflicto, simplemente elegirá el lado que especifique en su totalidad, incluidos los archivos
binarios.

Si volvemos al ejemplo de "hola mundo" que estábamos usando antes, podemos ver que la fusión en nuestra rama genera
conflictos.

$ git merge mundo


Auto-merging hello.rb
CONFLICTO (contenido): Conflicto de fusión en hello.rb
Resuelto 'hello.rb' usando la resolución anterior.
Fusión automática fallida; solucione los conflictos y luego confirme el resultado.

Sin embargo, si lo ejecutamos con -Xours o -Xtheirs , no lo hace.

$ git merge -Xours mundo


Auto-merging hello.rb Merge
realizado por la estrategia 'recursiva'. hola.rb |
2 +- prueba.sh | 2 ++ 2 archivos cambiados, 3
inserciones (+), 1 eliminación (-) modo de
creación 100644 test.sh

En ese caso, en lugar de obtener marcadores de conflicto en el archivo con "hola mundo" en un lado y "hola mundo" en el otro,
simplemente elegirá "hola mundo". Sin embargo, todos los demás cambios que no están en conflicto en esa rama se fusionan
correctamente.

Esta opción también se puede pasar al comando git merge-file que vimos anteriormente ejecutando algo como git merge-file --
ours para fusiones de archivos individuales.

Si desea hacer algo como esto, pero Git no intenta siquiera fusionar los cambios desde el otro lado, hay una opción más
draconiana, que es la estrategia de fusión "nuestra". Esto es diferente de la opción de combinación recursiva "nuestra" .

Esto básicamente hará una fusión falsa. Registrará una nueva confirmación de fusión con ambas ramas como padres, pero ni
siquiera mirará la rama en la que se está fusionando. Simplemente registrará como resultado de la fusión el código exacto en
su rama actual.

$ git merge -s ours mundo


Merge realizado por la estrategia 'ours'.
$ git diff CABEZA CABEZA~ $

Puede ver que no hay diferencia entre la rama en la que estábamos y el resultado de la fusión.

291
Machine Translated by Google

Esto a menudo puede ser útil para engañar básicamente a Git para que piense que una rama ya está fusionada cuando
haciendo una fusión más adelante. Por ejemplo, digamos que se bifurcó de una rama de lanzamiento y ha hecho algunos
trabaje en él que querrá volver a fusionar en su rama principal en algún momento. Mientras tanto
algunas correcciones de errores en el maestro deben ser respaldadas en su rama de lanzamiento . Puedes fusionar la corrección de errores.
bifurcarse en la rama de lanzamiento y también fusionarse -s nuestra la misma rama en su rama maestra
(a pesar de que la solución ya está allí), de modo que cuando vuelva a fusionar la rama de lanzamiento , hay
no hay conflictos de la corrección de errores.

Fusión de subárboles

La idea de la combinación de subárboles es que tiene dos proyectos, y uno de los proyectos se asigna a un
subdirectorio del otro. Cuando especifica una fusión de subárbol, Git suele ser lo suficientemente inteligente como para
descubra que uno es un subárbol del otro y fusione adecuadamente.

Veremos un ejemplo de agregar un proyecto separado a un proyecto existente y luego fusionarlo


el código del segundo en un subdirectorio del primero.

Primero, agregaremos la aplicación Rack a nuestro proyecto. Agregaremos el proyecto Rack como referencia remota
en nuestro propio proyecto y luego verifíquelo en su propia rama:

$ git remoto agregar rack_remote https://github.com/rack/rack


$ git fetch rack_remote --sin etiquetas
advertencia: no hay compromisos comunes
remoto: Contando objetos: 3184, hecho.
remoto: Comprimir objetos: 100% (1465/1465), listo.
remoto: Total 3184 (delta 1952), reutilizado 2770 (delta 1675)
Recepción de objetos: 100 % (3184/3184), 677,42 KiB | 4 KiB/s, listo.
Resolviendo deltas: 100% (1952/1952), hecho.
Desde https://github.com/rack/rack
* [nueva rama] build -> rack_remote/build
* [nueva rama] maestro -> rack_remote/master
* [nueva rama] rack-0.4 -> rack_remote/rack-0.4
* [nueva rama] rack-0.9 -> rack_remote/rack-0.9
$ git checkout -b rack_branch rack_remote/maestro
Branch rack_branch configurado para rastrear sucursales remotas refs/remotes/rack_remote/master.
Cambiado a una nueva rama "rack_branch"

Ahora tenemos la raíz del proyecto Rack en nuestra rama rack_branch y nuestro propio proyecto en el
rama maestra . Si revisa uno y luego el otro, puede ver que tienen un proyecto diferente
raíces:

$ ls
AUTORES PROBLEMAS CONOCIDOS Rakefile ejemplo de liberación

PROCESO DE COPIAR LÉAME bin $ git pago contribución prueba

maestro
Cambiado a rama "maestro"
$ ls
LÉAME

292
Machine Translated by Google

Este es un concepto extraño. No todas las ramas en su repositorio tienen que ser ramas del mismo proyecto. No es
común, porque rara vez es útil, pero es bastante fácil tener ramas que contengan historias completamente diferentes.

En este caso, queremos llevar el proyecto Rack a nuestro proyecto maestro como un subdirectorio. Podemos hacer eso
en Git con git read-tree. Aprenderá más sobre read-tree y sus amigos en Git Internals, pero por ahora sepa que lee el
árbol raíz de una rama en su área de ensayo actual y directorio de trabajo. Acabamos de volver a su rama principal y
llevamos la rama rack_branch al subdirectorio rack de nuestra rama principal de nuestro proyecto principal:

$ git read-tree --prefix=rack/ -u rack_branch

Cuando nos comprometemos, parece que tenemos todos los archivos de Rack en ese subdirectorio, como si los
hubiéramos copiado desde un tarball. Lo que se pone interesante es que podemos fusionar cambios de una de las ramas
a la otra con bastante facilidad. Por lo tanto, si el proyecto Rack se actualiza, podemos incorporar cambios ascendentes
cambiando a esa rama y extrayendo:

$ git pago rack_branch $ git


tirar

Luego, podemos fusionar esos cambios nuevamente en nuestra rama principal . Para extraer los cambios y completar
previamente el mensaje de confirmación, use la opción --squash , así como la opción -Xsubtree de la estrategia de
combinación recursiva . La estrategia recursiva es la predeterminada aquí, pero la incluimos para mayor claridad.

$ git checkout master $


git merge --squash -s recursive -Xsubtree=rack rack_branch
Confirmación de Squash: no se actualiza HEAD
La fusión automática salió bien; detenido antes de cometer según lo solicitado

Todos los cambios del proyecto Rack se fusionan y están listos para confirmarse localmente. También puede hacer lo
contrario: realizar cambios en el subdirectorio rack de su rama maestra y luego fusionarlos en su rama rack_branch más
tarde para enviarlos a los mantenedores o empujarlos hacia arriba.

Esto nos brinda una manera de tener un flujo de trabajo algo similar al flujo de trabajo del submódulo sin usar submódulos
(que trataremos en Submódulos). Podemos mantener ramas con otros proyectos relacionados en nuestro repositorio y
fusionarlas en subárboles en nuestro proyecto de vez en cuando. Es bueno de alguna manera, por ejemplo, todo el
código está comprometido en un solo lugar. Sin embargo, tiene otros inconvenientes en el sentido de que es un poco
más complejo y más fácil cometer errores al reintegrar cambios o empujar accidentalmente una rama a un repositorio no
relacionado.

Otra cosa un poco extraña es que para obtener una diferencia entre lo que tiene en su subdirectorio rack y el código en
su rama rack_branch , para ver si necesita fusionarlos, no puede usar el comando diff normal. En su lugar, debe ejecutar
git diff-tree con la rama que desea comparar
para:

293
Machine Translated by Google

$ git diff-tree -p rack_branch

O bien, para comparar lo que hay en el subdirectorio de su bastidor con lo que era la rama maestra en el servidor la
última vez que buscó, puede ejecutar:

$ git diff-tree -p rack_remote/maestro

regañar

La funcionalidad de git rerere es una característica un poco oculta. El nombre significa "reutilizar resolución grabada" y,
como su nombre lo indica, le permite pedirle a Git que recuerde cómo resolvió un conflicto de fragmentos para que la
próxima vez que vea el mismo conflicto, Git pueda resolverlo automáticamente. .

Hay una serie de escenarios en los que esta funcionalidad puede ser realmente útil. Uno de los ejemplos que se
mencionan en la documentación es cuando desea asegurarse de que una rama de tema de larga duración finalmente se
fusionará limpiamente, pero no desea tener un montón de confirmaciones de combinación intermedias que saturan su
historial de confirmaciones. Con rerere habilitado, puede intentar la combinación ocasional, resolver los conflictos y luego
salir de la combinación. Si hace esto continuamente, entonces la combinación final debería ser fácil porque rerere puede
hacer todo por usted automáticamente.

Esta misma táctica se puede usar si desea mantener una rama reorganizada para no tener que lidiar con los mismos
conflictos de reorganización cada vez que lo hace. O si desea tomar una rama que fusionó y solucionó un montón de
conflictos y luego decide reorganizarla, es probable que no tenga que volver a hacer todos los mismos conflictos.

Otra aplicación de rerere es cuando fusionas un montón de ramas de temas en evolución en una cabeza comprobable de
vez en cuando, como suele hacer el proyecto Git. Si las pruebas fallan, puede rebobinar las fusiones y volver a hacerlas
sin la rama de tema que hizo que las pruebas fallaran sin tener que volver a resolver los conflictos.

Para habilitar la funcionalidad rerere , simplemente tiene que ejecutar esta configuración:

$ git config --global rerere.habilitado verdadero

También puede activarlo creando el directorio .git/rr-cache en un repositorio específico, pero la configuración es más clara
y habilita esa característica globalmente para usted.

Ahora veamos un ejemplo sencillo, similar al anterior. Digamos que tenemos un archivo llamado hello.rb que se parece a
esto:

#! /usr/bin/env rubí

def hola
pone 'hola mundo' al
final

294
Machine Translated by Google

En una rama cambiamos la palabra “hola” por “hola”, luego en otra rama cambiamos el “mundo” por “mundo”, igual
que antes.

Cuando fusionamos las dos ramas juntas, obtendremos un conflicto de fusión:

$ git merge i18n-world Fusión


automática hello.rb
CONFLICTO (contenido): Conflicto de fusión en hello.rb
Preimagen grabada para 'hello.rb'
Fusión automática fallida; solucione los conflictos y luego confirme el resultado.

Debería notar la nueva línea Preimagen grabada para ARCHIVO allí. De lo contrario, debería verse exactamente
como un conflicto de fusión normal. En este punto, rerere nos puede decir algunas cosas. Normalmente, puede
ejecutar git status en este punto para ver qué es lo que está en conflicto:

$ git estado
# En el maestro de rama
# Rutas no fusionadas:
# (usar "git reset HEAD <archivo>..." para eliminar) # (usar "git
add <archivo>..." para marcar la resolución) #

# ambos modificados: hola.rb


#

Sin embargo, git rerere también le dirá para qué ha registrado el estado previo a la fusión con git rerere
estado:

$ git rerere estado hola.rb

295
Machine Translated by Google

Y git rerere diff mostrará el estado actual de la resolución: con qué comenzó a resolver y en qué lo resolvió.

$ git rerere diff --- a/


hola.rb +++ b/hola.rb
@@ -1,11 +1,11 @@
#! /usr/bin/env rubí

definitivamente hola

-<<<<<<<
- pone 'hola mundo'
-=======
+<<<<<<< CABEZA

pone 'hola mundo'


->>>>>>>
+=======
+ pone 'hola mundo'
+>>>>>>> i18n-world
final

Además (y esto no está realmente relacionado con rerere), puede usar git ls-files -u para ver los archivos en conflicto
y las versiones anterior, izquierda y derecha:

$ GIT LS-Files -U
100644 39804C942A9C1F2C03DC7C5EBCD7F3E3A6B97519 1 HELLO.RB
100644 A440DB6E8D1FD76AD438A49025A9AD9CE746F581 2 HELLO.RB
100644 54336BE847C3758Ab6048764196074443848474 3 hola.rb

Ahora puede resolverlo para que solo diga 'hola mundo' y puede ejecutar git rerere diff nuevamente para ver qué
recordará rerere:

$ git rerere diff --- a/


hola.rb +++ b/hola.rb
@@ -1,11 +1,7 @@
#! /usr/bin/env rubí

definitivamente hola

-<<<<<<<
- pone 'hola mundo'
-=======

- pone 'hola mundo'


->>>>>>>
+ pone 'hola mundo' al
final

296
Machine Translated by Google

Eso básicamente dice que cuando Git ve un conflicto de fragmentos en un archivo hello.rb que tiene "hola mundo" en un lado y
"hola mundo" en el otro, lo resolverá como "hola mundo".

Ahora podemos marcarlo como resuelto y confirmarlo:

$ git add hello.rb $


git commit
Resolución grabada para 'hello.rb'.
[maestro 68e16e5] Fusionar rama 'i18n'

Puede ver que "Resolución grabada para ARCHIVO".

Ahora, deshagamos esa fusión y luego volvamos a basarla en la parte superior de nuestra rama principal . Podemos mover nuestra
rama hacia atrás usando git reset como vimos en Reset Demystified.

$ git reset --cabeza dura^


HEAD está ahora en ad63f15 i18n el hola

Nuestra fusión se deshace. Ahora vamos a reorganizar la rama del tema.

297
Machine Translated by Google

$ git checkout i18n-world


Cambiado a la rama 'i18n-world'

$ git rebase master


Primero, rebobinando head para reproducir tu trabajo encima...
Aplicando: i18n una palabra
Usando información de índice para reconstruir un árbol base...
Volviendo a la base de parches y fusión de 3 vías...
CONFLICTO hello.rb de
fusión automática (contenido): Conflicto de fusión en hello.rb
Resuelto 'hello.rb' usando la resolución anterior.
No se pudo fusionar en los cambios.
El parche falló en 0001 i18n una palabra

Ahora, obtuvimos el mismo conflicto de combinación que esperábamos, pero eche un vistazo al ARCHIVO
resuelto usando la línea de resolución anterior. Si miramos el archivo, veremos que ya se resolvió, no hay
marcadores de conflicto de combinación en él.

#! /usr/bin/env rubí

def hola
pone 'hola mundo' final

Además, git diff te mostrará cómo se volvió a resolver automáticamente:

$ git diff diff


--cc hola.rb index
a440db6,54336ba..0000000 --- a/
hola.rb +++ b/hola.rb

@@@ -1,7 -1,7 +1,7 @@@


#! /usr/bin/env rubí

def hola - pone


'hola mundo' - pone 'hola
mundo' ++ pone 'hola mundo'
end

298
Machine Translated by Google

También puede recrear el estado del archivo en conflicto con git checkout:

$ git checkout --conflict=merge hola.rb $ gato hola.rb #! /


usr/bin/env rubí

definitivamente hola

<<<<<<< nuestro

pone 'hola mundo'


=======

pone 'hola mundo' >>>>>>>


fin de ellos

Vimos un ejemplo de esto en Combinación avanzada. Sin embargo, por ahora, volvamos a resolverlo simplemente ejecutando
git rerere nuevamente:

$ git rerere
Resuelto 'hello.rb' usando la resolución anterior. $ gato hola.rb #! /
usr/bin/env rubí

def hola

pone 'hola mundo' final

Hemos vuelto a resolver el archivo automáticamente usando la resolución en caché rerere . Ahora puede agregar y continuar la
reorganización para completarla.

299
Machine Translated by Google

$ git add hello.rb $


git rebase --continuar
aplicando: i18n una palabra

Por lo tanto, si realiza muchas fusiones, o desea mantener una rama de tema actualizada con su rama maestra sin un montón
de fusiones, o cambia de base con frecuencia, puede activar rerere para ayudarlo un poco.

Depuración con Git


Además de ser principalmente para el control de versiones, Git también proporciona un par de comandos para ayudarlo a
depurar sus proyectos de código fuente. Debido a que Git está diseñado para manejar casi cualquier tipo de contenido, estas
herramientas son bastante genéricas, pero a menudo pueden ayudarlo a buscar un error o un culpable cuando las cosas salen mal.

Anotación de archivo

Si rastrea un error en su código y desea saber cuándo se introdujo y por qué, la anotación de archivos suele ser su mejor
herramienta. Te muestra qué confirmación fue la última en modificar cada línea de cualquier archivo. Entonces, si ve que un
método en su código tiene errores, puede anotar el archivo con la culpa de git para determinar qué confirmación fue
responsable de la introducción de esa línea.

El siguiente ejemplo usa git culp para determinar qué compromiso y quién fue el responsable de las líneas en el Makefile del
kernel de Linux de nivel superior y, además, usa la opción -L para restringir la salida de la anotación a las líneas 69 a 82 de
ese archivo:

$ git reproche -L 69,82 Makefile


b8b0618cf6fab (Cheng Renquan 2009-05-26 16:03:07 +0800 69) ifeq ("$(origen V)",
"línea de comando") b8b0618cf6fab (Cheng Renquan 2009-05- 26 16:03:07 +0800
70) KBUILD_VERBOSE = $(V) ^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36
-0700 71) endif ^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20: 36 -0700 72) ifndef
KBUILD_VERBOSE ^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 73)
KBUILD_VERBOSE = 0 ^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36) endif ^4 -0
1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 75) 066b7ed955808 (Michal
Marek 2014-07-04 14:29:30 +0200 76) ifeq ($(KBUILD_VERBOSE),1) 066b7ed955808
(Michal -07-04 14:29:30 +0200 77) tranquilo = 066b7ed955808 (Michal Marek 2014-07-04
14:29:30 +0200 78) Q = 066b7ed955808 (Michal Marek 2014-07-04 14:29:30 + 0200 79)
else 066b7ed955808 (Michal Marek 2014-07-04 14:29:30 +0200 80) quiet=quiet_
066b7ed955808 (Michal Marek 2014-07-04 14:29:30 +0200 81) Q = @ 066b7ed95 Marek 2
014-07-04 14:29:30 +0200 82) fin

Observe que el primer campo es el SHA-1 parcial de la confirmación que modificó por última vez esa línea. Los siguientes dos
campos son valores extraídos de ese compromiso: el nombre del autor y la fecha de creación de ese

commit, para que pueda ver fácilmente quién modificó esa línea y cuándo. Después de eso viene el número de línea y el
contenido del archivo. También tenga en cuenta las líneas de confirmación ^1da177e4c3f4 , donde el prefijo ^ designa las
líneas que se introdujeron en la confirmación inicial del repositorio y se han mantenido sin cambios desde entonces. Esto es
un poco confuso, porque ahora has visto al menos tres formas diferentes en que Git usa

300
Machine Translated by Google

el ^ para modificar un compromiso SHA-1, pero eso es lo que significa aquí.

Otro aspecto interesante de Git es que no realiza un seguimiento de los cambios de nombre de archivos de forma explícita. Graba las instantáneas

y luego trata de averiguar qué fue renombrado implícitamente, después del hecho. uno de los interesantes
Las características de esto es que también puede pedirle que descubra todo tipo de movimiento de código. Si pasas -C
para culpar a Git, Git analiza el archivo que está anotando e intenta averiguar dónde están los fragmentos de código.
dentro de él originalmente provinieron si fueron copiados de otro lugar. Por ejemplo, digamos que eres
refactorizar un archivo llamado GITServerHandler.m en varios archivos, uno de los cuales es GITPackUpload.m. Por
culpando a GITPackUpload.m con la opción -C , puede ver de dónde vinieron originalmente las secciones del código
desde:

$ git culpa -C -L 141,153 GITPackUpload.m


f344f58d GITServerHandler.m (Scott 2009-01-04 141)
f344f58d GITServerHandler.m (Scott 2009-01-04 142) - (vacío) reunirObjetoShasFromC
f344f58d GITServerHandler.m (Scott 2009-01-04 143) {
70befddd GITServerHandler.m (Scott 2009-03-22 144) //NSLog(@"REUNIR COMUNICACIÓN

ad11ac80 GITPackUpload.m (Scott 2009-03-24 145)


ad11ac80 GITPackUpload.m (Scott 2009-03-24 146) NSString *parentSha;
(Scott 2009-03-24
ad11ac80 GITPackUpload.m ad11ac80 147)
GITPackUpload.m GITCommit *commit = [g
(Scott 2009-03-24 148)
ad11ac80 GITPackUpload.m (Scott 2009-03-24 149) //NSLog(@"REUNIR COMUNICACIÓN

ad11ac80 GITPackUpload.m (Scott 2009-03-24 150)


56ef2caf GITServerHandler.m (Scott 2009-01-05 151) si (comprometer) {
56ef2caf GITServerHandler.m (Scott 2009-01-05 152) [refDict setOb
56ef2caf GITServerHandler.m (Scott 2009-01-05 153)

Esto es realmente útil. Normalmente, obtienes como confirmación original la confirmación donde copiaste el
código terminado, porque esa es la primera vez que toca esas líneas en este archivo. Git te dice el original
confirme donde escribió esas líneas, incluso si fue en otro archivo.

Búsqueda binaria

Anotar un archivo ayuda si sabe dónde comienza el problema. si no sabes lo que es


rompiendo, y ha habido docenas o cientos de confirmaciones desde el último estado en el que sabe
el código funcionó, es probable que recurras a git bisect para obtener ayuda. El comando bisect hace una búsqueda binaria
a través de su historial de compromisos para ayudarlo a identificar lo más rápido posible qué compromiso introdujo
un problema.

Supongamos que acaba de enviar una versión de su código a un entorno de producción, está obteniendo un error
informes sobre algo que no estaba sucediendo en su entorno de desarrollo, y no puede
imagina por qué el código está haciendo eso. Vuelves a tu código y resulta que puedes reproducir
el problema, pero no puede averiguar qué es lo que va mal. Puede bisecar el código para averiguarlo. Primero tú
ejecute git bisect start para que las cosas funcionen, y luego use git bisect bad para decirle al sistema que el
El compromiso actual en el que estás está roto. Luego, debe decir a bisect cuando fue el último buen estado conocido,
usando git bisect good <good_commit>:

301
Machine Translated by Google

$ git bisect start $ git


bisect bad $ git bisect
good v1.0 Bisecting:
Quedan 6 revisiones para probar después de esto
[ecb6e1bc347ccecc5f9350d878ce677feb13d3b2] Manejo de errores en repositorio

Git descubrió que hubo alrededor de 12 confirmaciones entre la confirmación que marcó como la última buena
confirmación (v1.0) y la mala versión actual, y revisó la del medio por usted. En este punto, puede ejecutar su prueba
para ver si el problema existe a partir de este compromiso. Si es así, entonces se introdujo en algún momento antes de
esta confirmación intermedia; si no es así, entonces el problema se introdujo en algún momento después de la
confirmación intermedia. Resulta que no hay ningún problema aquí, y le dices a Git que al escribir git bisect good y
continúa tu viaje:

$ git bisecar bien


Bisectriz: Quedan 3 revisiones para probar después
de esto [b047b02ea83310a70fd603dc8cd7a6cd13d15c04] Asegurar esto

Ahora estás en otra confirmación, a mitad de camino entre la que acabas de probar y tu confirmación incorrecta. Ejecutas
tu prueba nuevamente y descubres que este compromiso está roto, así que le dices a Git que with git bisect bad:

$ bisecar mal
Bisección: Quedan 1 revisiones para probar después de
esto [f71ce38690acf49c1f3c9bea38e09d82a5ce6014] Eliminar tabla de excepciones

Esta confirmación está bien, y ahora Git tiene toda la información que necesita para determinar dónde se introdujo el
problema. Le informa el SHA-1 de la primera confirmación incorrecta y muestra parte de la información de confirmación
y qué archivos se modificaron en esa confirmación para que pueda averiguar qué sucedió que pudo haber introducido
este error:

$ git bisect good


b047b02ea83310a70fd603dc8cd7a6cd13d15c04 is first bad commit
commit b047b02ea83310a70fd603dc8cd7a6cd13d15c04 Autor: PJ
Hyett <pjhyett@example.com> Fecha: Tue Jan 27 14:48:32 2009 -0
8009

asegurar esta cosa

Configuración M

Cuando haya terminado, debe ejecutar git bisect reset para restablecer su HEAD a donde estaba antes de comenzar, o
terminará en un estado extraño:

$ git bisect restablecer

302
Machine Translated by Google

Esta es una herramienta poderosa que puede ayudarlo a verificar cientos de confirmaciones en busca de un error
introducido en minutos. De hecho, si tiene un script que saldrá 0 si el proyecto es bueno o no 0 si el proyecto es malo,
puede automatizar completamente git bisect. En primer lugar, vuelve a indicarle el alcance de la bisectriz al proporcionar
las confirmaciones buenas y malas conocidas. Puede hacer esto enumerándolos con el comando de inicio de bisección si
lo desea, enumerando primero la confirmación mala conocida y la confirmación buena conocida en segundo lugar:

$ git bisect iniciar HEAD v1.0


$ git bisect ejecutar test-error.sh

Al hacerlo, automáticamente se ejecuta test-error.sh en cada confirmación extraída hasta que Git encuentra la primera
confirmación rota. También puede ejecutar algo como hacer o hacer pruebas o lo que sea que tenga que ejecute pruebas
automatizadas para usted.

Submódulos
A menudo sucede que mientras trabaja en un proyecto, necesita usar otro proyecto dentro de él.
Tal vez sea una biblioteca desarrollada por un tercero o que esté desarrollando por separado y utilizando en varios
proyectos principales. Surge un problema común en estos escenarios: desea poder tratar los dos proyectos por separado
y aún así poder usar uno desde el otro.

Aquí hay un ejemplo. Suponga que está desarrollando un sitio web y creando fuentes Atom. En lugar de escribir su propio
código generador de Atom, decide usar una biblioteca. Es probable que deba incluir este código de una biblioteca
compartida, como una instalación de CPAN o una gema de Ruby, o copiar el código fuente en su propio árbol de proyectos.
El problema de incluir la biblioteca es que es difícil personalizar la biblioteca de alguna manera y, a menudo, es más difícil
implementarla, porque debe asegurarse de que cada cliente tenga esa biblioteca disponible. El problema de copiar el
código en su propio proyecto es que los cambios personalizados que realice son difíciles de fusionar cuando los cambios
anteriores estén disponibles.

Git aborda este problema utilizando submódulos. Los submódulos le permiten mantener un repositorio de Git como un
subdirectorio de otro repositorio de Git. Esto le permite clonar otro repositorio en su proyecto y mantener sus confirmaciones
separadas.

Comenzando con submódulos


Veremos el desarrollo de un proyecto simple que se ha dividido en un proyecto principal y algunos subproyectos.

Comencemos agregando un repositorio Git existente como un submódulo del repositorio en el que estamos trabajando.
Para agregar un nuevo submódulo, use el comando git submodule add con la URL absoluta o relativa del proyecto que le
gustaría comenzar a rastrear. En este ejemplo, agregaremos una biblioteca llamada "DbConnector".

303
Machine Translated by Google

$ git submodule add https://github.com/chaconinc/DbConnector Cloning


into 'DbConnector'... remote: Counting objects: 11, done. remoto:
Comprimir objetos: 100% (10/10), hecho. remoto: Total 11 (delta 0),
reutilizado 11 (delta 0)

Desembalaje de objetos: 100% (11/11), hecho.


Comprobando conectividad... hecho.

De forma predeterminada, los submódulos agregarán el subproyecto a un directorio con el mismo nombre que
el repositorio, en este caso, "DbConnector". Puede agregar una ruta diferente al final del comando si desea
que vaya a otro lugar.

Si ejecuta git status en este punto, notará algunas cosas.

$ git status On
branch master Su
rama está actualizada con 'origin/master'.

Cambios a confirmar: (use


"git reset HEAD <archivo>..." para quitar la preparación)

nuevo archivo: .gitmodules nuevo


archivo: DbConnector

Primero debería notar el nuevo archivo .gitmodules . Este es un archivo de configuración que almacena la
asignación entre la URL del proyecto y el subdirectorio local en el que lo extrajo:

[submódulo "DbConnector"]
ruta = DbConnector url =
https://github.com/chaconinc/DbConnector

Si tiene varios submódulos, tendrá varias entradas en este archivo. Es importante tener en cuenta que este
archivo está controlado por versión con sus otros archivos, como su archivo .gitignore . Es empujado y tirado
con el resto de su proyecto. Así es como otras personas que clonan este proyecto saben de dónde obtener los
proyectos de submódulos.

Dado que la URL en el archivo .gitmodules es lo que otras personas intentarán clonar/
recuperar primero, asegúrese de usar una URL a la que puedan acceder si es posible. Por

ÿ ejemplo, si usa una URL diferente para empujar a la que otros usarían para extraer, use la
que otros tienen acceso. Puede sobrescribir este valor localmente con git config
submodule.DbConnector.url PRIVATE_URL para su propio uso. Cuando corresponda, una
URL relativa puede ser útil.

La otra lista en la salida de estado de git es la entrada de la carpeta del proyecto. Si ejecuta git diff en eso, verá
algo interesante:

304
Machine Translated by Google

$ git diff --cached DbConnector diff --git a/


DbConnector b/DbConnector nuevo modo de
archivo 160000 índice 0000000..c3f01dc --- /dev/
null +++ b/DbConnector @@ -0,0 +1 @@ +
Confirmación de subproyecto
c3f01dc8862123d317dd46284b05b6892c7b29bc

Aunque DbConnector es un subdirectorio en su directorio de trabajo, Git lo ve como un submódulo y no rastrea su contenido
cuando no está en ese directorio. En cambio, Git lo ve como una confirmación particular de ese repositorio.

Si desea una salida diferencial un poco más agradable, puede pasar la opción --submodule a git diff.

$ git diff --cached --submodule diff --git


a/.gitmodules b/.gitmodules nuevo modo de archivo
100644 índice 0000000..71fc376 --- /dev/null +++
b/.gitmodules @@ -0,0 +1,3 @@ +[submódulo
"DbConnector"] ruta = DbConnector url = https://
github.com/chaconinc/DbConnector Submódulo
DbConnector 0000000...c3f01dc (nuevo submódulo)

+
+

Cuando te comprometes, ves algo como esto:

$ git commit -am 'Agregar módulo DbConnector' [master


fb9093c] Agregar módulo DbConnector 2 archivos
cambiados, 4 inserciones (+) modo de creación
100644 .gitmodules modo de creación 160000
DbConnector

Observe el modo 160000 para la entrada DbConnector . Ese es un modo especial en Git que básicamente significa que está
registrando una confirmación como una entrada de directorio en lugar de un subdirectorio o un archivo.

Por último, impulsa estos cambios:

$ git empujar maestro de origen

Clonación de un proyecto con submódulos

Aquí clonaremos un proyecto con un submódulo en él. Cuando clona un proyecto de este tipo, de forma predeterminada obtiene
los directorios que contienen submódulos, pero ninguno de los archivos dentro de ellos todavía:

305
Machine Translated by Google

$ git clone https://github.com/chaconinc/MainProject Clonación en


'MainProject'... remoto: Contando objetos: 14, hecho. remoto:
Comprimir objetos: 100% (13/13), hecho. remoto: Total 14 (delta
1), reutilizado 13 (delta 0)

Desembalaje de objetos: 100% (14/14), hecho.


Comprobando conectividad... hecho.
$ cd MainProject $ ls -la total 16

drwxr-xr-x 9 personal de schacon 306 17 de septiembre


15:21 . drwxr-xr-x 7 personal de schacon 238 17 de septiembre
15:21 .. drwxr-xr-x 13 personal de schacon 442 17 de septiembre
15:21 .git -rw-r--r-- 1 personal de schacon 92 17 de septiembre
15:21 .gitmodules drwxr-xr-x 2 personal de schacon 68 17 de septiembre
15:21 DbConnector -rw-r--r-- 1 personal de schacon 756 17 de septiembre
15:21 Makefile drwxr-xr-x 3 personal de schacon 102 17 de septiembre
15:21 incluye drwxr-xr-x 4 personal de schacon 136 17 de septiembre
15:21 scripts drwxr-xr-x 4 personal de schacon 136 17 de septiembre 15:21
src $ cd DbConnector/ $ ls $

El directorio DbConnector está ahí, pero vacío. Debe ejecutar dos comandos: git submodule init para inicializar su archivo de
configuración local y git submodule update para obtener todos los datos de ese proyecto y verifique la confirmación adecuada que
figura en su superproyecto:

$ git submodule init


Submódulo 'DbConnector' (https://github.com/chaconinc/DbConnector) registrado para la ruta 'DbConnector'
$ git submodule update Clonación en 'DbConnector'... remoto: Contando objetos: 11, hecho. remoto:
Comprimir objetos: 100% (10/10), listo. remoto: Total 11 (delta 0), reutilizado 11 (delta 0)

Desembalaje de objetos: 100% (11/11), hecho.


Comprobando conectividad... hecho.
Ruta del submódulo 'DbConnector': desprotegido 'c3f01dc8862123d317dd46284b05b6892c7b29bc'

Ahora su subdirectorio DbConnector está en el estado exacto en el que se encontraba cuando se comprometió anteriormente.

Sin embargo, hay otra manera de hacer esto que es un poco más simple. Si pasa --recurse-submodules al comando git clone ,
automáticamente inicializará y actualizará cada submódulo en el repositorio, incluidos los submódulos anidados si alguno de los
submódulos en el repositorio tiene submódulos.

306
Machine Translated by Google

$ git clone --recurse-submodules https://github.com/chaconinc/MainProject Clonación en


'MainProject'... remoto: Contando objetos: 14, hecho. remoto: Comprimir objetos: 100%
(13/13), hecho. remoto: Total 14 (delta 1), reutilizado 13 (delta 0)

Desembalaje de objetos: 100% (14/14), hecho.


Comprobando conectividad... hecho.
Submódulo 'DbConnector' (https://github.com/chaconinc/DbConnector) registrado para la ruta 'DbConnector'

Clonación en 'DbConnector'...
remoto: Contar objetos: 11, hecho. remoto:
Comprimir objetos: 100% (10/10), hecho. remoto: Total 11
(delta 0), reutilizado 11 (delta 0)
Desembalaje de objetos: 100% (11/11), hecho.
Comprobando conectividad... hecho.
Ruta del submódulo 'DbConnector': desprotegido 'c3f01dc8862123d317dd46284b05b6892c7b29bc'

Si ya clonó el proyecto y olvidó --recurse-submodules, puede combinar los pasos de actualización de git submodule init y
git submodule ejecutando git submodule update --init. También para inicializar, buscar y verificar cualquier submódulo
anidado, puede usar la infalible actualización del submódulo git --init --recursive.

Trabajar en un proyecto con submódulos


Ahora tenemos una copia de un proyecto con submódulos y colaboraremos con nuestros compañeros de equipo tanto en
el proyecto principal como en el proyecto del submódulo.

Obtención de cambios ascendentes desde el control remoto del submódulo

El modelo más simple de usar submódulos en un proyecto sería si simplemente estuviera consumiendo un subproyecto y
quisiera obtener actualizaciones de él de vez en cuando, pero en realidad no estuviera modificando nada en su pago.
Veamos un ejemplo simple allí.

Si desea verificar si hay trabajo nuevo en un submódulo, puede ingresar al directorio y ejecutar git fetch y git merge en la
rama ascendente para actualizar el código local.

$ git fetch
Desde https://github.com/chaconinc/DbConnector
c3f01dc..d0354fc maestro -> origin/master $ git merge
origin/master
Actualizando c3f01dc..d0354fc Avance rápido de scripts/
connect.sh | | src/db.c 1 + 2 archivos cambiados, 2
inserciones (+)
1+

Ahora, si regresa al proyecto principal y ejecuta git diff --submodule , puede ver que el submódulo se actualizó y obtener
una lista de confirmaciones que se le agregaron. Si no quieres escribir

307
Machine Translated by Google

--submodule cada vez que ejecuta git diff, puede configurarlo como el formato predeterminado configurando el valor
de configuración de diff.submodule en "log".

$ git config --global diff.submodule log $ git diff


Submodule DbConnector c3f01dc..d0354fc: >
rutina db más eficiente > mejor rutina de conexión
ÿ

Si se compromete en este punto, bloqueará el submódulo para que tenga el nuevo código cuando otras personas
actualicen.

También hay una manera más fácil de hacer esto, si prefiere no buscar y fusionar manualmente en el subdirectorio.
Si ejecuta git submodule update --remote, Git irá a sus submódulos y buscará y actualizará por usted.

$ actualización del submódulo git --remote


DbConnector remoto: conteo de objetos: 4, listo.
remoto: Comprimir objetos: 100% (2/2), listo. remoto:
Total 4 (delta 2), reutilizado 4 (delta 2)
Desembalaje de objetos: 100% (4/4), hecho.
Desde https://github.com/chaconinc/DbConnector
3f19983..d0354fc maestro -> origen/maestro Ruta del
submódulo
'DbConnector': desprotegido 'd0354fc054692d3906c85c3af05ddce39a1c0644'

Este comando supondrá de manera predeterminada que desea actualizar el pago a la rama principal del repositorio
de submódulos. Sin embargo, puede configurar esto en algo diferente si lo desea. Por ejemplo, si desea que el
submódulo DbConnector rastree la rama "estable" de ese repositorio, puede configurarlo en su archivo .gitmodules
(para que todos los demás también lo rastreen) o simplemente en su archivo local .git/config . Configurémoslo en el
archivo .gitmodules :

$ git config -f .gitmodules submodule.DbConnector.branch estable

$ git submodule update --remote


remote: Contando objetos: 4, listo. remoto:
Comprimir objetos: 100% (2/2), listo. remoto: Total 4
(delta 2), reutilizado 4 (delta 2)
Desembalaje de objetos: 100% (4/4), hecho.
Desde https://github.com/chaconinc/DbConnector
27cf5d3..c87d55d estable -> origen/estable Ruta del
submódulo 'DbConnector': desprotegido 'c87d55d4c6d4b05ee34fbc8cb6f7bf4585ae6687'

Si omite -f .gitmodules , solo hará el cambio por usted, pero probablemente tenga más sentido rastrear esa información
con el repositorio para que todos los demás también lo hagan.

Cuando ejecutamos git status en este punto, Git nos mostrará que tenemos "nuevas confirmaciones" en el submódulo.

308
Machine Translated by Google

$ git status On
branch master Su
rama está actualizada con 'origin/master'.

Cambios no preparados para


confirmación: (use "git add <archivo>..." para actualizar lo que se confirmará)
(use "git checkout -- <archivo>..." para descartar cambios en el directorio de trabajo)

modificado: .gitmodules
modificado: DbConnector (nuevas confirmaciones)

no se agregaron cambios para confirmar (use "git add" y/o "git commit -a")

Si establece el ajuste de configuración status.submodulesummary, Git también le mostrará un breve resumen


de los cambios en sus submódulos:

$ git config status.submodulesummary 1

$ git status On
branch master Su
rama está actualizada con 'origin/master'.

Cambios no preparados para


confirmación: (use "git add <archivo>..." para actualizar lo que se confirmará)
(use "git checkout -- <archivo>..." para descartar cambios en el directorio de trabajo)

modificado: .gitmodules
modificado: DbConnector (nuevas confirmaciones)

Submódulos cambiados pero no actualizados:

* DbConnector c3f01dc...c87d55d (4): > captura


ÿ

líneas terminadas no nulas

En este punto, si ejecuta git diff , podemos ver que hemos modificado nuestro archivo .gitmodules y también
que hay una serie de confirmaciones que hemos extraído y que están listas para confirmar en nuestro proyecto
de submódulo.

309
Machine Translated by Google

$ git diferencia
diff --git a/.gitmodules b/.gitmodules
índice 6fc0b3d..fd1cc29 100644

--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,4 @@
[submódulo "DbConnector"]
ÿ

ruta = conector Db
ÿ

URL = https://github.com/chaconinc/DbConnector
+ rama = estable
Submódulo DbConnector c3f01dc..c87d55d:
ÿ

> capturar líneas terminadas no nulas


ÿ

> manejo de errores más robusto


ÿ

> rutina db más eficiente


ÿ

> mejor rutina de conexión

Esto es genial, ya que podemos ver el registro de compromisos que estamos a punto de comprometer en nuestro

submódulo. Una vez confirmado, también puede ver esta información después del hecho cuando ejecuta git

registro -p.

$ git log -p --submódulo


confirmar 0a24cfc121a8a3c118e0105ae4ae4c00281cf7ae

Autor: Scott Chacón <schacon@gmail.com>


Fecha: miércoles 17 de septiembre 16:37:02 2014 +0200

actualizando DbConnector para corregir errores

diff --git a/.gitmodules b/.gitmodules


índice 6fc0b3d..fd1cc29 100644

--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,4 @@
[submódulo "DbConnector"]
ÿ

ruta = conector Db
ÿ

URL = https://github.com/chaconinc/DbConnector
+ rama = estable
Submódulo DbConnector c3f01dc..c87d55d:
ÿ

> capturar líneas terminadas no nulas


ÿ

> manejo de errores más robusto


ÿ

> rutina db más eficiente


ÿ

> mejor rutina de conexión

Por defecto, Git intentará actualizar todos sus submódulos cuando ejecute la actualización de submódulos de git

--remoto. Si tiene muchos de ellos, es posible que desee pasar el nombre del submódulo que desea

para intentar actualizar.

310
Machine Translated by Google

Obtener cambios ascendentes desde Project Remote

Pongámonos ahora en la piel de su colaborador, que tiene su propio clon local del repositorio de MainProject.
Simplemente ejecutar git pull para obtener los cambios recién confirmados no es suficiente:

$ git pull De
https://github.com/chaconinc/MainProject fb9093c..0a24cfc
maestro -> origen/maestro Obteniendo submódulo
DbConnector De
https://github.com/chaconinc/DbConnector -> origen/estable
c3f01dc..c87d55d estable Actualización de fb9093c..0a24cfc
Avance rápido

+- .gitmodules DbConnector | |22+- 2


archivos cambiados, 2 inserciones
(+), 2
eliminaciones (-)

$ git status On
branch master Su rama
está actualizada con 'origin/master'.
Cambios no preparados para
confirmación: (use "git add <archivo>..." para actualizar lo que se confirmará) (use
"git checkout -- <archivo>..." para descartar cambios en el directorio de trabajo)

modificado: DbConnector (nuevas confirmaciones)

Submódulos cambiados pero no actualizados:

* DbConnector c87d55d...c3f01dc (4): < capturar


ÿ

líneas terminadas no nulas < manejo de


ÿ

errores más robusto < rutina db más eficiente


ÿ

< mejor rutina de conexión

no se agregaron cambios para confirmar (use "git add" y/o "git commit -a")

De forma predeterminada, el comando git pull recupera de forma recursiva los cambios de los submódulos, como
podemos ver en el resultado del primer comando anterior. Sin embargo, no actualiza los submódulos. Esto se muestra
en la salida del comando git status , que muestra que el submódulo está "modificado" y tiene "nuevas confirmaciones".
Además, los corchetes que muestran las nuevas confirmaciones apuntan a la izquierda (<), lo que indica que estas
confirmaciones se registran en MainProject pero no están presentes en la comprobación local de DbConnector. Para
finalizar la actualización, debe ejecutar la actualización del submódulo git:

311
Machine Translated by Google

$ actualización del submódulo git --init --recursive


Ruta del submódulo 'vendor/plugins/demo': desprotegido
'48679c6302815f6c76f1fe30625d795d9e55fc56'

$ git status
On branch master
Su rama está actualizada con 'origin/master'. nada que
cometer, árbol de trabajo limpio

Tenga en cuenta que para estar seguro, debe ejecutar la actualización del submódulo git con el indicador --init en
caso de que MainProject confirme que acaba de agregar nuevos submódulos, y con el indicador --recursive si
algún submódulo tiene submódulos anidados.

Si desea automatizar este proceso, puede agregar el indicador --recurse-submodules al comando git pull (desde
Git 2.14). Esto hará que Git ejecute la actualización del submódulo de git justo después de la extracción, poniendo
los submódulos en el estado correcto. Además, si desea que Git siempre extraiga con --recurse -submodules,
puede establecer la opción de configuración submodule.recurse en verdadero (esto funciona para git pull desde
Git 2.15). Esta opción hará que Git use el indicador --recurse-submodules para todos los comandos que lo admitan
(excepto clonar).

Hay una situación especial que puede ocurrir al extraer actualizaciones de superproyectos: podría ser que el
repositorio ascendente haya cambiado la URL del submódulo en el archivo .gitmodules en una de las
confirmaciones que extrae. Esto puede suceder, por ejemplo, si el proyecto del submódulo cambia su plataforma
de alojamiento. En ese caso, es posible que git pull --recurse-submodules, o git submodule update, falle si el
superproyecto hace referencia a una confirmación de submódulo que no se encuentra en el submódulo remoto
configurado localmente en su repositorio. Para remediar esta situación, se requiere el comando de sincronización
del submódulo git :

# copia la nueva URL a tu configuración local


$ git submodule sync --recursive # actualiza el
submódulo desde la nueva URL $ git submodule
update --init --recursive

Trabajar en un submódulo

Es muy probable que si usa submódulos, lo haga porque realmente desea trabajar en el código del submódulo al
mismo tiempo que trabaja en el código del proyecto principal (o en varios submódulos) . De lo contrario,
probablemente estaría utilizando un sistema de gestión de dependencias más simple (como Maven o Rubygems).

Así que ahora veamos un ejemplo de hacer cambios en el submódulo al mismo tiempo que el proyecto principal y
confirmar y publicar esos cambios al mismo tiempo.

Hasta ahora, cuando ejecutamos el comando de actualización del submódulo git para obtener cambios de los
repositorios del submódulo, Git obtendría los cambios y actualizaría los archivos en el subdirectorio, pero dejaría
el sub-repositorio en lo que se llama un estado de "CABEZA separada". Esto significa que no hay una rama de
trabajo local (como el maestro, por ejemplo) que rastrea los cambios. Sin cambios de seguimiento de rama de trabajo, eso

312
Machine Translated by Google

significa que incluso si realiza cambios en el submódulo, es muy posible que esos cambios se pierdan la próxima vez que
ejecute la actualización del submódulo de git. Debe realizar algunos pasos adicionales si desea realizar un seguimiento de
los cambios en un submódulo.

Para configurar su submódulo para que sea más fácil de ingresar y piratear, debe hacer dos cosas. Debe ingresar a cada
submódulo y verificar una rama para trabajar. Luego, debe decirle a Git qué hacer si ha realizado cambios y luego
actualizar el submódulo de git: el control remoto extrae el nuevo trabajo desde arriba. Las opciones son que puede
fusionarlos en su trabajo local, o puede intentar reorganizar su trabajo local además de los nuevos cambios.

En primer lugar, vayamos a nuestro directorio de submódulos y busquemos una rama.

$ cd DbConnector/
$ git pago estable
Cambiado a la rama 'estable'

Intentemos actualizar nuestro submódulo con la opción "combinar". Para especificarlo manualmente, simplemente
podemos agregar la opción --merge a nuestra llamada de actualización . Aquí veremos que hubo un cambio en el servidor
para este submódulo y se fusionó.

$ cd ..
$ git submodule update --remote --merge remote:
Contando objetos: 4, listo. remoto: Comprimir
objetos: 100% (2/2), listo. remoto: Total 4 (delta 2),
reutilizado 4 (delta 2)
Desembalaje de objetos: 100% (4/4), hecho.
Desde https://github.com/chaconinc/DbConnector
c87d55d..92c7337 estable -> origen/estable Actualizando
c87d55d..92c7337
Avance rápido src/main.c | 1 + 1 archivo cambiado, 1
inserción (+)

Ruta del submódulo 'DbConnector': fusionado en '92c7337b30ef9e0893e758dac2459d07362ab5ea'

Si vamos al directorio DbConnector, tenemos los nuevos cambios ya fusionados en nuestra rama estable local. Ahora
veamos qué sucede cuando hacemos nuestro propio cambio local en la biblioteca y alguien más envía otro cambio al
mismo tiempo.

$ cd DbConnector/
$ vim src/db.c $ git
commit -am 'Unicode support' [stable
f906e16] Unicode support 1 archivo
cambiado, 1 inserción (+)

Ahora, si actualizamos nuestro submódulo, podemos ver qué sucede cuando hemos realizado un cambio local y aguas
arriba también tiene un cambio que debemos incorporar.

313
Machine Translated by Google

$ cd .. $
git submodule update --remote --rebase Primero,
rebobinando el cabezal para reproducir su trabajo encima...
Aplicando: compatibilidad con
Unicode Ruta del submódulo 'DbConnector': rebasado en '5d60ef9bbebf5a0c1c1050f242ceeb54ad58da94'

Si olvida --rebase o --merge, Git simplemente actualizará el submódulo a lo que sea que esté en el servidor y
restablecerá su proyecto a un estado HEAD separado.

$ actualización del submódulo git --remote


Ruta del submódulo 'DbConnector': desprotegido '5d60ef9bbebf5a0c1c1050f242ceeb54ad58da94'

Si esto sucede, no se preocupe, simplemente puede volver al directorio y verificar su rama nuevamente (que
aún contendrá su trabajo) y fusionar o reorganizar origin/stable (o cualquier rama remota que desee)
manualmente.

Si no ha confirmado sus cambios en su submódulo y ejecuta una actualización de submódulo que causaría
problemas, Git buscará los cambios pero no sobrescribirá el trabajo no guardado en su directorio de submódulo.

$ git submodule update --remote remote:


Contando objetos: 4, listo. remoto: Comprimir
objetos: 100% (3/3), hecho. remoto: Total 4 (delta 0), reutilizado
4 (delta 0)
Desembalaje de objetos: 100% (4/4), hecho.
Desde https://github.com/chaconinc/DbConnector
5d60ef9..c75e92a stable -> origin/stable error: Sus cambios
locales en los
siguientes archivos se sobrescribirán al finalizar la compra: scripts/setup.sh Por favor, confirme sus cambios o
guárdelos antes de poder cambiar de rama.

Cancelación
No se puede realizar el pago 'c75e92a2b3855c9e5b66f915308390d9db204aca' en la ruta del submódulo
'DbConnector'

Si realizó cambios que entran en conflicto con algo que cambió anteriormente, Git le informará cuando ejecute
la actualización.

$ git submodule update --remote --merge Auto-fusión


de scripts/setup.sh CONFLICTO (contenido): Conflicto
de combinación en scripts/setup.sh Preimagen grabada para 'scripts/
setup.sh'
Fusión automática fallida; solucione los conflictos y luego confirme el resultado.
No se puede fusionar 'c75e92a2b3855c9e5b66f915308390d9db204aca' en la ruta del submódulo
'DbConnector'

Puede ir al directorio del submódulo y solucionar el conflicto como lo haría normalmente.

314
Machine Translated by Google

Publicación de cambios de submódulos

Ahora tenemos algunos cambios en nuestro directorio de submódulos. Algunas de estas fueron traídas desde arriba por
nuestras actualizaciones y otras se hicieron localmente y aún no están disponibles para nadie más, ya que aún no las hemos
enviado.

$ git diff
Submódulo DbConnector c87d55d..82d2ad3:
ÿ

> Fusionar desde origen/estable


> Actualizar secuencia de comandos de configuración
ÿ

> Soporte Unicode


ÿ

> Eliminar método innecesario


ÿ

> Agregar nueva opción para la agrupación de conexiones

Si nos comprometemos en el proyecto principal y lo impulsamos sin impulsar también los cambios del submódulo, otras
personas que intenten verificar nuestros cambios tendrán problemas, ya que no tendrán forma de obtener los cambios del
submódulo de los que dependen. . Esos cambios solo existirán en nuestra copia local.

Para asegurarse de que esto no suceda, puede pedirle a Git que verifique que todos sus submódulos se hayan enviado
correctamente antes de enviar el proyecto principal. El comando git push toma el argumento --recurse -submodules que se
puede configurar como "verificar" o "bajo demanda". La opción "verificar" hará que la inserción simplemente falle si alguno de
los cambios de submódulos confirmados no se ha aplicado.

$ git push --recurse-submódulos=verificar


Las siguientes rutas de submódulos contienen cambios que no se pueden
encontrar en ningún control remoto:
ConectorBD

Por favor, inténtalo

git push --recurse-submodules=bajo demanda

o cd a la ruta y uso

empujar git

para empujarlos a un control remoto.

Como puede ver, también nos brinda algunos consejos útiles sobre lo que podríamos querer hacer a continuación. La opción
simple es ingresar a cada submódulo y presionar manualmente los controles remotos para asegurarse de que estén disponibles
externamente y luego intentar presionar nuevamente. Si desea que el comportamiento de verificación ocurra para todas las
inserciones, puede hacer que este comportamiento sea el predeterminado haciendo git config push.recurseSubmodules check.

La otra opción es usar el valor "bajo demanda", que intentará hacer esto por usted.

315
Machine Translated by Google

$ git push --recurse-submodules=submódulo de


inserción bajo demanda 'DbConnector'
Contar objetos: 9, hecho.
Compresión delta usando hasta 8 hilos.
Comprimir objetos: 100% (8/8), hecho.
Objetos de escritura: 100% (9/9), 917 bytes | 0 bytes/s, listo.
Total 9 (delta 3), reutilizado 0 (delta 0)
A https://github.com/chaconinc/DbConnector
c75e92a..82d2ad3 estable -> estable
Contar objetos: 2, hecho.
Compresión delta usando hasta 8 hilos.
Comprimir objetos: 100% (2/2), hecho.
Objetos de escritura: 100% (2/2), 266 bytes | 0 bytes/s, listo.
Total 2 (delta 1), reutilizado 0 (delta 0)
A https://github.com/chaconinc/MainProject
3d6d338..9a377d1 maestro -> maestro

Como puede ver allí, Git ingresó al módulo DbConnector y lo empujó antes de empujar el proyecto principal. Si la inserción de
ese submódulo falla por alguna razón, la inserción del proyecto principal también fallará. Puede hacer que este comportamiento
sea el predeterminado haciendo git config push.recurseSubmodules a pedido.

Fusión de cambios de submódulos

Si cambia la referencia de un submódulo al mismo tiempo que otra persona, es posible que tenga algunos problemas. Es
decir, si los historiales de los submódulos han divergido y están comprometidos con ramas divergentes en un superproyecto,
puede que le cueste un poco arreglarlo.

Si una de las confirmaciones es un ancestro directo de la otra (una combinación de avance rápido), entonces Git simplemente
elegirá la última para la combinación, por lo que funciona bien.

Sin embargo, Git no intentará ni siquiera una fusión trivial por ti. Si el submódulo se compromete a divergir y necesita
fusionarse, obtendrá algo similar a esto:

$ git pull
remote: Contando objetos: 2, listo. remoto:
Comprimir objetos: 100% (1/1), listo. remoto: Total 2 (delta
1), reutilizado 2 (delta 1)
Desembalaje de objetos: 100% (2/2), hecho.
Desde https://github.com/chaconinc/MainProject
9a377d1..eb974f8 maestro -> origen/maestro
Obtención de la advertencia de DbConnector
del submódulo: no se pudo fusionar el submódulo DbConnector (no se encontraron las siguientes confirmaciones de fusión)
CONFLICTO de DbConnector
de fusión automática (submódulo): conflicto de fusión en
DbConnector Fusión automática fallida; solucione los conflictos y luego confirme el resultado.

Entonces, básicamente, lo que sucedió aquí es que Git descubrió que las dos ramas registran puntos en la historia del
submódulo que son divergentes y deben fusionarse. Lo explica como "combinar

316
Machine Translated by Google

siguientes confirmaciones no encontradas”, lo cual es confuso, pero explicaremos por qué en un momento.

Para resolver el problema, debe averiguar en qué estado debe estar el submódulo. Extrañamente, Git realmente no le
brinda mucha información para ayudar aquí, ni siquiera los SHA-1 de las confirmaciones de ambos lados de la historia.
Afortunadamente, es fácil de averiguar. Si ejecuta git diff , puede obtener los SHA-1 de las confirmaciones registradas en
ambas ramas que intentaba fusionar.

$ git diff diff


--cc DbConnector
índice eb41d76,c771610..0000000
--- a/DbConnector +++ b/DbConnector

Entonces, en este caso, eb41d76 es la confirmación en nuestro submódulo que teníamos y c771610 es la confirmación
que tenía upstream. Si vamos a nuestro directorio de submódulos, ya debería estar en eb41d76 ya que la combinación no
lo habría tocado. Si por alguna razón no lo es, simplemente puede crear y pagar una rama que apunte a ella.

Lo importante es el SHA-1 de la confirmación del otro lado. Esto es lo que tendrás que fusionar y resolver. Puede
simplemente probar la combinación con el SHA-1 directamente, o puede crear una rama para él y luego intentar combinarla.
Le sugerimos lo último, aunque solo sea para hacer un mensaje de confirmación de combinación más agradable.

Entonces, iremos a nuestro directorio de submódulos, crearemos una rama llamada "try-merge" basada en ese segundo
SHA-1 de git diff y fusionaremos manualmente.

$ cd ConectorBD

$ git rev-parse HEAD


eb41d764bccf88be77aced643c13a7fa86714135

$ rama git probar fusionar c771610

$ git merge try-merge


Fusión automática de src/
main.c CONFLICTO (contenido): Conflicto de fusión en
src/main.c Preimagen grabada para 'src/main.c'
Fusión automática fallida; solucione los conflictos y luego confirme el resultado.

Tenemos un conflicto de combinación real aquí, por lo que si lo resolvemos y lo confirmamos, simplemente podemos
actualizar el proyecto principal con el resultado.

317
Machine Translated by Google

$ vim src/main.c ÿ $ git


add src/main.c $ git
commit -am 'fusionó nuestros cambios'
Resolución grabada para 'src/main.c'. [master
9fd905e] fusionó nuestros cambios

$ cd .. ÿ $ git
diff ÿ diff --cc
DbConnector
eb41d76 índice, c771610..0000000 ---
a / dbconnector +++ b / dbconnector
@@@ -1,1 -1,1 +1,1 @@@ -
Subproyecto cometer
eb41d764bccf88be77aced643c13a7fa86714135 -Subproject cometer
c77161012afbbe1f58b5053316ead08f4b7e6d1d ++ Subproyecto cometer
9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a $ git agregar DbConnector ÿ

$ git commit -m "Combinar los cambios de Tom" ÿ


[master 10d2c60] Combinar los cambios de Tom

ÿ Primero resolvemos el conflicto.

ÿ Luego volvemos al directorio principal del proyecto.

ÿ Podemos verificar los SHA-1 nuevamente.

ÿ Resuelva la entrada del submódulo en conflicto.

ÿ Confirmar nuestra fusión.

Puede ser un poco confuso, pero en realidad no es muy difícil.

Curiosamente, hay otro caso que maneja Git. Si existe una confirmación de fusión en el directorio del submódulo que contiene
ambas confirmaciones en su historial, Git te la sugerirá como una posible solución. Ve que en algún momento del proyecto del
submódulo, alguien fusionó las ramas que contenían estos dos compromisos, por lo que tal vez querrá ese.

Esta es la razón por la cual el mensaje de error de antes era "combinar después de las confirmaciones no encontradas", porque
no podía hacer esto. Es confuso porque ¿quién esperaría que intentara hacer esto?

Si encuentra una sola confirmación de fusión aceptable, verá algo como esto:

318
Machine Translated by Google

$ advertencia de git merge


origin/master: no se pudo fusionar el submódulo DbConnector (no avance rápido)
Encontré una posible resolución de fusión para el submódulo:
9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a: > fusionó nuestros cambios
Si esto es correcto, simplemente agréguelo al índice, por ejemplo, usando:

índice de actualización de git --cacheinfo 160000 9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a


"ConectorBD"

que aceptará esta sugerencia.


CONFLICTO de DbConnector
de fusión automática (submódulo): conflicto de fusión en
DbConnector Fusión automática fallida; solucione los conflictos y luego confirme el resultado.

El comando sugerido que proporciona Git actualizará el índice como si hubiera ejecutado git add (que borra el
conflicto), luego confirme. Aunque probablemente no deberías hacer esto. Puede ir fácilmente al directorio de
submódulos, ver cuál es la diferencia, avanzar rápidamente a este compromiso, probarlo correctamente y luego
confirmarlo.

$ cd DbConnector/ $
git merge 9fd905e
Actualización eb41d76..9fd905e
Avance rápido

$ cd .. $
git add DbConnector $ git
commit -am 'Avance rápido a un submódulo secundario común'

Esto logra lo mismo, pero al menos de esta manera puede verificar que funciona y que tiene el código en el
directorio de su submódulo cuando haya terminado.

Sugerencias para submódulos

Hay algunas cosas que puede hacer para que trabajar con submódulos sea un poco más fácil.

Submódulo Foreach

Hay un comando de submódulo foreach para ejecutar algún comando arbitrario en cada submódulo. Esto puede
ser realmente útil si tiene varios submódulos en el mismo proyecto.

Por ejemplo, supongamos que queremos iniciar una nueva función o corregir un error y tenemos trabajo en varios
submódulos. Podemos guardar fácilmente todo el trabajo en todos nuestros submódulos.

319
Machine Translated by Google

$ submódulo de git para cada 'alijo de git'


Ingresando 'CryptoLibrary'
No hay cambios locales para guardar
Ingresando 'DbConnector'
Directorio de trabajo guardado y WIP de estado de índice en estable: 82d2ad3 Fusionar
desde origen/estable
HEAD ahora está en 82d2ad3 Fusión desde origen/estable

Luego podemos crear una nueva rama y cambiar a ella en todos nuestros submódulos.

$ git submódulo para cada 'git checkout -b funciónA'


Ingresando 'CryptoLibrary'
Cambiado a una nueva rama 'featureA'
Ingresando 'DbConnector'
Cambiado a una nueva rama 'featureA'

Entiendes la idea. Una cosa realmente útil que puede hacer es producir una buena diferencia unificada de lo que se cambia en su
proyecto principal y también en todos sus subproyectos.

320
Machine Translated by Google

$ git diferencia; submódulo git para cada 'git diff'


El submódulo DbConnector contiene contenido modificado
diferencia --git a/src/main.cb/src/main.c índice
210f1ae..1f0acdc 100644 --- a/src/main.c +++ b/
src/main.c @@ -245,6 + 245,8 @@ static int
handle_alias(int *argcp, const char ***argv)

commit_pager_choice();

+ url = url_decode(url_orig);
+
ÿ

/* construir alias_argv */
ÿ

alias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1)); alias_argv[0] =


ÿ

alias_cadena + 1; Ingresando la diferencia 'DbConnector' --git a/src/db.cb/


src/db.c index 1aaefb6..5297645 100644

--- a/src/db.c +++


b/src/db.c @@
-93,6 +93,11 @@ char *url_decode_mem(const char *url, int len) return
ÿ

url_decode_internal(&url, len , NULO, &fuera, 0);


}

+char *url_decode(const char *url) +{

+ return url_decode_mem(url, strlen(url));


+}
+
char *url_decode_parameter_name(const char **consulta) {

estructura strbuf salida = STRBUF_INIT;

Aquí podemos ver que estamos definiendo una función en un submódulo y llamándola en el proyecto principal.
Obviamente, este es un ejemplo simplificado, pero espero que le dé una idea de cómo puede ser útil.

Alias útiles

Es posible que desee configurar algunos alias para algunos de estos comandos, ya que pueden ser bastante largos
y no puede establecer opciones de configuración para la mayoría de ellos para que sean predeterminados. Cubrimos
la configuración de alias de Git en Git Aliases, pero aquí hay un ejemplo de lo que puede configurar si planea trabajar
mucho con submódulos en Git.

$ git config alias.sdiff '!'"git diff && git submodule foreach 'git diff'" $ git config alias.spush 'push --
recurse-submodules=on-demand' $ git config alias.supdate 'actualización de submódulo -- remoto --
merge'

De esta manera, simplemente puede ejecutar git update cuando desee actualizar sus submódulos, o git spush

321
Machine Translated by Google

para empujar con la verificación de dependencia del submódulo.

Problemas con los submódulos

Sin embargo, el uso de submódulos no está exento de contratiempos.

Cambio de ramas

Por ejemplo, cambiar ramas con submódulos en ellas también puede ser complicado con versiones de
Git anteriores a Git 2.13. Si crea una nueva rama, agrega un submódulo allí y luego vuelve a cambiar a
una rama sin ese submódulo, todavía tiene el directorio del submódulo como un directorio sin seguimiento:

$ git --version git


versión 2.12.2

$ git checkout -b add-crypto


Cambiado a una nueva rama 'add-crypto'

$ submódulo git agregar https://github.com/chaconinc/CryptoLibrary Clonación


en 'CryptoLibrary'...
...

$ git commit -am 'Agregar biblioteca


criptográfica' [add-crypto 4445836] Agregar
biblioteca criptográfica 2 archivos cambiados, 4
inserciones (+) modo de creación 160000 CryptoLibrary

$ advertencia de git
checkout master: no se puede rmdir CryptoLibrary: el directorio no está vacío
Cambiado a la rama 'maestro'
Su rama está actualizada con 'origen/maestro'.

$ git estado
En maestro de rama
Su rama está actualizada con 'origen/maestro'.

Archivos sin
seguimiento: (use "git add <archivo>..." para incluir en lo que se confirmará)

Criptobiblioteca/

no se agregó nada para confirmar, pero hay archivos sin rastrear presentes (use "git add" para rastrear)

Eliminar el directorio no es difícil, pero puede ser un poco confuso tenerlo allí. Si lo elimina y luego vuelve
a cambiar a la rama que tiene ese submódulo, deberá ejecutar la actualización del submódulo --init para
volver a llenarlo.

322
Machine Translated by Google

$ git clean-ffdx
Eliminando CryptoLibrary/

$ git checkout add-crypto


Cambiado a la rama 'add-crypto'

$ ls biblioteca criptográfica/

$ actualización del submódulo git --init


Ruta del submódulo 'CryptoLibrary': desprotegido 'b8dda6aa182ea4464f3f3264b11e0268545172af'

$ ls biblioteca criptográfica/
Makefile incluye guiones origen

Una vez más, no es realmente muy difícil, pero puede ser un poco confuso.

Las versiones más recientes de Git (Git >= 2.13) simplifican todo esto al agregar el indicador --recurse-submodules al
comando git checkout , que se encarga de colocar los submódulos en el estado correcto para la rama a la que estamos
cambiando.

$ git --version git


versión 2.13.3

$ git checkout -b add-crypto


Cambiado a una nueva rama 'add-crypto'

$ submódulo git agregar https://github.com/chaconinc/CryptoLibrary Clonación en


'CryptoLibrary'...
...

$ git commit -am 'Agregar biblioteca


criptográfica' [add-crypto 4445836] Agregar
biblioteca criptográfica 2 archivos cambiados, 4
inserciones (+) modo de creación 160000 CryptoLibrary

$ git checkout --recurse-submodules master Cambiado


a branch 'master'
Su rama está actualizada con 'origen/maestro'.

$ git status On
branch master Su
rama está actualizada con 'origin/master'.

nada que cometer, árbol de trabajo limpio

Usar el indicador --recurse-submodules de git checkout también puede ser útil cuando trabaja en varias ramas en el
superproyecto, cada una con su submódulo apuntando a diferentes confirmaciones. De hecho, si cambia entre ramas que
registran el submódulo en diferentes confirmaciones, al ejecutar git

323
Machine Translated by Google

estado, el submódulo aparecerá como "modificado" e indicará "nuevas confirmaciones". Esto se debe a que, de forma
predeterminada, el estado del submódulo no se transfiere al cambiar de rama.

Esto puede ser muy confuso, por lo que es una buena idea hacer siempre git checkout --recurse-submodules cuando
tu proyecto tenga submódulos. Para las versiones anteriores de Git que no tienen el indicador --recurse-submodules ,
después de pagar puedes usar la actualización del submódulo de git --init --recursive para poner los submódulos en
el estado correcto.

Afortunadamente, puedes decirle a Git (>=2.14) que siempre use el indicador --recurse-submodules configurando la
opción de configuración submodule.recurse: git config submodule.recurse true. Como se señaló anteriormente, esto
también hará que Git recurra a submódulos para cada comando que tenga la opción --recurse-submodules (excepto
git clone).

Cambiar de subdirectorios a submódulos

La otra advertencia principal con la que se encuentra mucha gente implica cambiar de subdirectorios a submódulos.
Si ha estado rastreando archivos en su proyecto y desea moverlos a un submódulo, debe tener cuidado o Git se
enojará con usted. Suponga que tiene archivos en un subdirectorio de su proyecto y desea cambiarlo a un submódulo.
Si elimina el subdirectorio y luego ejecuta la adición de submódulo, Git le grita:

$ rm -Rf CryptoLibrary/
$ git submodule add https://github.com/chaconinc/CryptoLibrary
'CryptoLibrary' ya existe en el índice

Primero debe desorganizar el directorio CryptoLibrary . Luego puedes agregar el submódulo:

$ git rm -r CryptoLibrary $
git submodule add https://github.com/chaconinc/CryptoLibrary
Clonación en 'CryptoLibrary'... remoto: Contar objetos: 11, listo.
remoto: Comprimir objetos: 100% (10/10), listo. remoto: Total 11
(delta 0), reutilizado 11 (delta 0)

Desembalaje de objetos: 100% (11/11), hecho.


Comprobando conectividad... hecho.

Ahora supongamos que hiciste eso en una sucursal. Si intenta volver a una rama donde esos archivos todavía están
en el árbol real en lugar de un submódulo, obtendrá este error:

$ git checkout master


error: los siguientes archivos de árbol de trabajo sin seguimiento se sobrescribirán al finalizar
la compra: CryptoLibrary/Makefile CryptoLibrary/includes/crypto.h

...
Muévalos o elimínelos antes de poder cambiar de rama.
Abortando

324
Machine Translated by Google

Puede forzarlo a cambiar con checkout -f, pero tenga cuidado de no tener cambios sin guardar allí, ya que podrían
sobrescribirse con ese comando.

$ git checkout -f master


advertencia: no se puede rmdir CryptoLibrary: el directorio no está vacío
Cambiado a la rama 'maestro'

Luego, cuando vuelve a cambiar, obtiene un directorio CryptoLibrary vacío por alguna razón y es posible que la actualización
del submódulo de git tampoco lo solucione. Es posible que deba ingresar al directorio de su submódulo y ejecutar un pago de
git. para recuperar todos sus archivos. Puede ejecutar esto en un script foreach de submódulo para ejecutarlo para múltiples
submódulos.

Es importante tener en cuenta que los submódulos en estos días mantienen todos sus datos de Git en el directorio .git del
proyecto principal , por lo que, a diferencia de versiones mucho más antiguas de Git, destruir un directorio de submódulos no
perderá ninguna confirmación o rama que tenía.

Con estas herramientas, los submódulos pueden ser un método bastante simple y efectivo para desarrollar varios proyectos
relacionados pero aún separados simultáneamente.

Agrupación
Aunque hemos cubierto las formas comunes de transferir datos de Git a través de una red (HTTP, SSH, etc.), en realidad hay
una forma más de hacerlo que no se usa comúnmente pero que puede ser bastante útil.

Git es capaz de "agrupar" sus datos en un solo archivo. Esto puede ser útil en varios escenarios. Tal vez su red no funcione y
quiera enviar cambios a sus compañeros de trabajo. Tal vez esté trabajando en algún lugar fuera del sitio y no tenga acceso
a la red local por razones de seguridad. Tal vez su tarjeta inalámbrica/ethernet se acaba de romper. Tal vez no tenga acceso
a un servidor compartido por el momento, quiera enviar actualizaciones por correo electrónico a alguien y no quiera transferir
40 confirmaciones a través de un parche de formato.

Aquí es donde el comando git bundle puede ser útil. El comando bundle empaquetará todo lo que normalmente se enviaría
por cable con un comando git push en un archivo binario que puede enviar por correo electrónico a alguien o colocar en una
unidad flash, luego desagregarlo en otro repositorio.

Veamos un ejemplo sencillo. Digamos que tienes un repositorio con dos confirmaciones:

325
Machine Translated by Google

$ git registro
compromiso 9a466c572fe88b195efd356c3f2bbeccdb504102
Autor: Scott Chacon <schacon@gmail.com> Fecha:
miércoles 10 de marzo 07:34:10 2010 -0800

Segunda confirmación

cometer b1ec3248f39900d2a406049d762aa68e9641be25
Autor: Scott Chacon <schacon@gmail.com> Fecha: miércoles
10 de marzo 07:34:01 2010 -0800

Primera confirmación

Si desea enviar ese repositorio a alguien y no tiene acceso a un repositorio para enviarlo, o simplemente no desea configurar
uno, puede agruparlo con git bundle create.

$ git bundle create repo.bundle HEAD master Contando


objetos: 6, listo.
Compresión delta usando hasta 2 hilos.
Comprimir objetos: 100% (2/2), hecho.
Objetos de escritura: 100% (6/6), 441 bytes, hecho.
Total 6 (delta 0), reutilizado 0 (delta 0)

Ahora tiene un archivo llamado repo.bundle que tiene todos los datos necesarios para volver a crear la rama principal del
repositorio. Con el comando de paquete , debe enumerar cada referencia o rango específico de confirmaciones que desea incluir.
Si tiene la intención de que esto se clone en otro lugar, debe agregar HEAD como referencia, así como lo hemos hecho aquí.

Puede enviar por correo electrónico este archivo repo.bundle a otra persona, o ponerlo en una unidad USB y recorrerlo.

Por otro lado, digamos que le enviaron este archivo repo.bundle y desea trabajar en el proyecto. Puede clonar desde el archivo
binario a un directorio, como lo haría desde una URL.

$ git clone repo.bundle repo Clonación


en 'repo'...
...
$ cd repo $
git log --oneline 9a466c5
Segunda confirmación
b1ec324 Primera confirmación

Si no incluye HEAD en las referencias, también debe especificar -b master o cualquier rama que esté incluida porque, de lo
contrario, no sabrá qué rama verificar.

Ahora supongamos que realiza tres confirmaciones y desea enviar las nuevas confirmaciones a través de un paquete en una memoria
USB o correo electrónico.

326
Machine Translated by Google

$ git log --oneline 71b84da


Última confirmación: segundo repositorio
c99cf5b Cuarta confirmación: segundo repositorio
7011d3d Tercera confirmación: segundo repositorio
9a466c5 Segunda confirmación
b1ec324 Primera confirmación

Primero, debemos determinar el rango de confirmaciones que queremos incluir en el paquete. A diferencia de los
protocolos de red que determinan el conjunto mínimo de datos para transferir a través de la red, tendremos que
resolver esto manualmente. Ahora, podría hacer lo mismo y agrupar todo el repositorio, lo que funcionará, pero es
mejor simplemente agrupar la diferencia: solo las tres confirmaciones que acabamos de hacer localmente.

Para hacer eso, tendrás que calcular la diferencia. Como describimos en Rangos de confirmaciones, puede especificar
un rango de confirmaciones de varias formas. Para obtener las tres confirmaciones que tenemos en nuestra rama
maestra que no estaban en la rama que clonamos originalmente, podemos usar algo como origin/master..master o
master ^origin/master. Puede probar eso con el comando de registro .

$ git log --oneline master ^origin/master 71b84da Última


confirmación - segundo repositorio c99cf5b Cuarta
confirmación - segundo repositorio 7011d3d Tercera
confirmación - segundo repositorio

Así que ahora que tenemos la lista de compromisos que queremos incluir en el paquete, agrupémoslos. Hacemos eso
con el comando git bundle create , dándole un nombre de archivo que queremos que sea nuestro paquete y el rango
de confirmaciones que queremos incluir en él.

$ git bundle create commits.bundle master ^9a466c5 Contando


objetos: 11, listo.
Compresión delta usando hasta 2 hilos.
Comprimir objetos: 100% (3/3), listo.
Objetos de escritura: 100% (9/9), 775 bytes, listo.
Total 9 (delta 0), reutilizado 0 (delta 0)

Ahora tenemos un archivo commits.bundle en nuestro directorio. Si tomamos eso y se lo enviamos a nuestro socio,
ella puede importarlo al repositorio original, incluso si se ha hecho más trabajo allí mientras tanto.

Cuando obtiene el paquete, puede inspeccionarlo para ver qué contiene antes de importarlo a su repositorio. El primer
comando es el comando de verificación del paquete que se asegurará de que el archivo sea realmente un paquete Git
válido y que tenga todos los ancestros necesarios para reconstituirlo correctamente.

327
Machine Translated by Google

$ git bundle verificar ../commits.bundle El paquete


contiene 1 referencia
71b84daaf49abed142a373b6e5c59a22dc6560dc referencias/jefes/maestro
El paquete requiere estos 1 ref
9a466c572fe88b195efd356c3f2bbeccdb504102 segundo compromiso
../commits.bundle está bien

Si el empaquetador hubiera creado un paquete de solo las dos últimas confirmaciones que había hecho, en lugar de
las tres, el repositorio original no podría importarlo, ya que le falta el historial de requisitos. El comando de verificación
se habría visto así:

$ git bundle verificar ../commits-bad.bundle error: El


repositorio carece de estas confirmaciones de requisitos previos:
error: 7011d3d8fc200abe0ad561c011c3852a4b7bbe95 Tercera confirmación - segundo repositorio

Sin embargo, nuestro primer paquete es válido, por lo que podemos obtener confirmaciones de él. Si desea ver qué
ramas hay en el paquete que se pueden importar, también hay un comando para enumerar solo las cabezas:

$ git bundle list-heads ../commits.bundle


71b84daaf49abed142a373b6e5c59a22dc6560dc refs/heads/master

El subcomando de verificación también le indicará las cabezas. El punto es ver qué se puede extraer, de modo que
pueda usar los comandos fetch o pull para importar confirmaciones de este paquete. Aquí buscaremos la rama maestra
del paquete en una rama llamada other-master en nuestro repositorio:

$ git fetch ../commits.bundle master:other-master From ../


commits.bundle * [nueva rama]
Maestro -> otro-maestro

Ahora podemos ver que tenemos las confirmaciones importadas en la rama de otro maestro , así como cualquier
confirmación que hayamos hecho mientras tanto en nuestra propia rama maestra .

$ git log --oneline --decorate --graph --all * 8255d41 (HEAD,


maestro) Tercer compromiso - primer repositorio | * 71b84da (otro
maestro) Última confirmación - segundo repositorio | * c99cf5b Cuarto
compromiso - segundo repositorio | * 7011d3d Tercer compromiso -
segundo repositorio |/ * 9a466c5 Segundo compromiso * b1ec324
Primer compromiso

Por lo tanto, git bundle puede ser realmente útil para compartir o realizar operaciones de tipo red cuando no tiene la red
adecuada o el repositorio compartido para hacerlo.

328
Machine Translated by Google

Reemplazar

Como hemos enfatizado antes, los objetos en la base de datos de objetos de Git no se pueden cambiar, pero Git
proporciona una forma interesante de pretender reemplazar objetos en su base de datos con otros objetos.

El comando replace te permite especificar un objeto en Git y decir "cada vez que te refieras a este objeto, finge que es
un objeto diferente ". Esto suele ser útil para reemplazar una confirmación en su historial con otra sin tener que
reconstruir todo el historial con, por ejemplo, git filter-branch.

Por ejemplo, supongamos que tiene un historial de código enorme y desea dividir su repositorio en un historial breve
para los nuevos desarrolladores y un historial mucho más extenso para las personas interesadas en la minería de datos.
Puede injertar un historial en el otro "reemplazando" la primera confirmación en la nueva línea con la última confirmación
en la anterior. Esto es bueno porque significa que en realidad no tienes que volver a escribir cada compromiso en el
nuevo historial, como normalmente tendrías que hacer para unirlos (porque el parentesco afecta a los SHA-1).

Probemos esto. Tomemos un repositorio existente, divídalo en dos repositorios, uno reciente y otro histórico, y luego
veremos cómo podemos recombinarlos sin modificar los valores SHA-1 de los repositorios recientes a través de replace.

Usaremos un repositorio simple con cinco confirmaciones simples:

$ git log --oneline ef989d8


Quinta confirmación
c6e1e95 Cuarto compromiso
9c68fdc Tercer compromiso
945704c Segundo compromiso
c1822cf Primera confirmación

Queremos dividir esto en dos líneas de la historia. Una línea va desde el compromiso uno hasta el compromiso cuatro:
ese será el histórico. La segunda línea será solo los compromisos cuatro y cinco, esa será la historia reciente.

329
Machine Translated by Google

Bueno, crear la historia histórica es fácil, podemos simplemente poner una rama en la historia y luego
empujar esa rama a la rama maestra de un nuevo repositorio remoto.

$ git branch history c6e1e95 $ git log --


oneline --decorate ef989d8 (HEAD, master)
Quinta confirmación c6e1e95 (historial) Cuarta
confirmación 9c68fdc Tercera confirmación

945704c Segunda confirmación


c1822cf Primera confirmación

330
Machine Translated by Google

Ahora podemos enviar la nueva rama de historial a la rama maestra de nuestro nuevo repositorio:

$ git remote add project-history https://github.com/schacon/project-history $ git push project-


history history:master Contar objetos: 12, listo.

Compresión delta usando hasta 2 hilos.


Comprimir objetos: 100% (4/4), hecho.
Objetos de escritura: 100% (12/12), 907 bytes, hecho.
Total 12 (delta 0), reutilizado 0 (delta 0)
Desembalaje de objetos: 100% (12/12), hecho.
Para git@github.com:schacon/project-history.git *
[nueva rama] historial -> maestro

OK, entonces nuestra historia está publicada. Ahora, la parte más difícil es truncar nuestra historia reciente para que sea

331
Machine Translated by Google

menor. Necesitamos una superposición para poder reemplazar un compromiso en uno con un compromiso equivalente en
el otro, por lo que vamos a truncar esto solo para confirmar cuatro y cinco (así que comete cuatro superposiciones).

$ git log --oneline --decorate ef989d8 (HEAD,


maestro) Quinta confirmación c6e1e95 (historial)
Cuarta confirmación 9c68fdc Tercera confirmación
945704c Segunda confirmación

c1822cf Primera confirmación

En este caso, es útil crear una confirmación base que tenga instrucciones sobre cómo expandir el historial, para que otros
desarrolladores sepan qué hacer si alcanzan la primera confirmación en el historial truncado y necesitan más. Entonces, lo
que vamos a hacer es crear un objeto de confirmación inicial como nuestro punto base con instrucciones, luego reorganizar
las confirmaciones restantes (cuatro y cinco) encima de él.

Para hacer eso, debemos elegir un punto para dividir, que para nosotros es el tercer compromiso, que es 9c68fdc en
lenguaje SHA. Entonces, nuestro compromiso base se basará en ese árbol. Podemos crear nuestra confirmación base
usando el comando commit-tree , que simplemente toma un árbol y nos devolverá un objeto de confirmación SHA-1
completamente nuevo y sin padres.

$ echo 'Obtener historial de bla, bla, bla' | git commit-árbol 9c68fdc^{árbol}


622e88e9cbfbacfb75b5279245b9fb38dfea10cf

El comando commit-tree es uno de un conjunto de comandos que comúnmente se conocen como


comandos de 'plomería'. Estos son comandos que generalmente no están destinados a usarse

ÿ directamente, sino que son utilizados por otros comandos de Git para realizar trabajos más pequeños.
En ocasiones, cuando estamos haciendo cosas más extrañas como esta, nos permiten hacer cosas
de muy bajo nivel, pero no están diseñadas para el uso diario. Puede leer más sobre los comandos de
plomería en Plomería y Porcelana.

332
Machine Translated by Google

Bien, ahora que tenemos una confirmación base, podemos reorganizar el resto de nuestro historial además de eso
con git rebase --onto. El argumento --onto será el SHA-1 que acabamos de obtener del árbol de confirmación y el
punto de rebase será la tercera confirmación (el padre de la primera confirmación que queremos mantener, 9c68fdc):

$ git rebase --onto 622e88 9c68fdc Primero,


rebobinando head para reproducir su trabajo encima...
Aplicando: cuarta confirmación
Aplicando: quinta confirmación

333
Machine Translated by Google

Bien, ahora hemos reescrito nuestro historial reciente sobre un compromiso base descartable que ahora
tiene instrucciones sobre cómo reconstituir el historial completo si quisiéramos. Podemos enviar ese nuevo
historial a un nuevo proyecto y ahora, cuando las personas clonen ese repositorio, solo verán las dos
confirmaciones más recientes y luego una confirmación base con instrucciones.

Ahora cambiemos los roles a alguien que clone el proyecto por primera vez y que quiera el historial completo.
Para obtener los datos del historial después de clonar este repositorio truncado, habría que agregar un
segundo control remoto para el repositorio histórico y buscar:

334
Machine Translated by Google

$ git clon https://github.com/schacon/project $ cd proyecto

$ git log --oneline master e146b5f


Quinta confirmación
81a708d Cuarto compromiso

622e88e Obtener historial de bla, bla, bla

$ git remote add project-history https://github.com/schacon/project-history $ git fetch project-history From


https://github.com/schacon/project-history * [nueva rama] maestro

-> proyecto-historia/maestro

Ahora el colaborador tendría sus confirmaciones recientes en la rama maestra y las confirmaciones históricas en la
rama proyecto-historial/maestra .

$ git log --oneline master e146b5f


Quinta confirmación 81a708d Cuarta
confirmación

622e88e Obtener historial de bla, bla, bla

$ git log --oneline project-history/master c6e1e95 Cuarto


compromiso
9c68fdc Tercer compromiso
945704c Segundo compromiso
c1822cf Primer compromiso

Para combinarlos, simplemente puede llamar a git replace con el compromiso que desea reemplazar y luego el
compromiso con el que desea reemplazarlo. Así que queremos reemplazar la "cuarta" confirmación en la rama
maestra con la "cuarta" confirmación en la rama proyecto-historial/maestra :

$ git reemplazar 81a708d c6e1e95

Ahora, si observa el historial de la rama maestra , parece que se ve así:

$ git log --oneline master e146b5f


Quinta confirmación 81a708d Cuarta
confirmación 9c68fdc Tercera
confirmación
945704c Segunda confirmación
c1822cf Primera confirmación

¿Guay, verdad? Sin tener que cambiar todos los SHA-1 en sentido ascendente, pudimos reemplazar una confirmación
en nuestro historial con una confirmación completamente diferente y todas las herramientas normales (bisectar,
culpar, etc.) funcionarán como esperábamos.

335
Machine Translated by Google

Curiosamente, todavía muestra 81a708d como SHA-1, aunque en realidad está usando los datos de confirmación
c6e1e95 con los que lo reemplazamos. Incluso si ejecuta un comando como cat-file, le mostrará los datos reemplazados:

$ git cat-file -p 81a708d árbol


7bc544cf438903b65ca9104a1e30345eee6c083d

padre 9c68fdceee073230f19ebb8b5e7fc71b479c0252 autor Scott


Chacon <schacon@gmail.com> 1268712581 -0700 autor Scott Chacon
<schacon@gmail.com> 1268712581 -0700

cuarto compromiso

Recuerde que el padre real de 81a708d fue nuestra confirmación de marcador de posición (622e88e), no 9c68fdce
como se indica aquí.

Otra cosa interesante es que estos datos se mantienen en nuestras referencias:

336
Machine Translated by Google

$ Git para-cada-ref
e146b5f14e79d4935160c0e83fb9ebe526b8da0d comprometen refs / heads / master
c6e1e95051d41771a649f3145423f8809d1a74d4 cometer árbitros / mandos a distancia / historia /
maestro e146b5f14e79d4935160c0e83fb9ebe526b8da0d cometer árbitros / mandos a distancia /
origen / HEAD e146b5f14e79d4935160c0e83fb9ebe526b8da0d cometer árbitros / mandos a distancia /
origin / master c6e1e95051d41771a649f3145423f8809d1a74d4 comprometerse
referencias/reemplazar/81a708dd0e167a3f691541c7a6463343bc457040

Esto significa que es fácil compartir nuestro reemplazo con otros, porque podemos enviarlo a nuestro servidor y otras personas
pueden descargarlo fácilmente. Esto no es tan útil en el escenario de injerto de historia que hemos visto aquí (ya que todos
estarían descargando ambas historias de todos modos, así que ¿por qué separarlas?) pero puede ser útil en otras circunstancias.

Almacenamiento de Credenciales

Si usa el transporte SSH para conectarse a controles remotos, es posible que tenga una clave sin una frase de contraseña, lo que
le permite transferir datos de forma segura sin tener que escribir su nombre de usuario y contraseña. Sin embargo, esto no es
posible con los protocolos HTTP: cada conexión necesita un nombre de usuario y una contraseña. Esto se vuelve aún más difícil
para los sistemas con autenticación de dos factores, donde el token que usa para una contraseña se genera aleatoriamente y no
se puede pronunciar.

Afortunadamente, Git tiene un sistema de credenciales que puede ayudar con esto. Git tiene algunas opciones proporcionadas en
el cuadro:

• El valor predeterminado es no almacenar en caché en absoluto. Cada conexión le pedirá su nombre de usuario y
clave.

• El modo "caché" mantiene las credenciales en la memoria durante un cierto período de tiempo. Ninguna de las contraseñas
se almacena nunca en el disco y se eliminan de la memoria caché después de 15 minutos.

• El modo de "almacenamiento" guarda las credenciales en un archivo de texto sin formato en el disco y nunca caducan. Esto
significa que hasta que cambie su contraseña para el host de Git, no tendrá que volver a escribir sus credenciales. La
desventaja de este enfoque es que sus contraseñas se almacenan en texto claro en un archivo sin formato en su directorio
de inicio.

• Si usa una Mac, Git viene con un modo "osxkeychain", que almacena en caché las credenciales en el llavero seguro que está
adjunto a su cuenta del sistema. Este método almacena las credenciales en el disco y nunca caducan, pero están encriptadas
con el mismo sistema que almacena los certificados HTTPS y los autocompletados de Safari.

• Si usa Windows, macOS o Linux, puede instalar un asistente llamado "Git Credential
Gerente". Esto utiliza almacenes de datos nativos de la plataforma para controlar la información confidencial.

Puede elegir uno de estos métodos estableciendo un valor de configuración de Git:

$ git config --global credential.helper caché

Algunos de estos ayudantes tienen opciones. El asistente de "almacenamiento" puede tomar un argumento --file <ruta> , que
personaliza dónde se guarda el archivo de texto sin formato (el valor predeterminado es ~/.git-credentials). El ayudante de "caché"

337
Machine Translated by Google

acepta la opción --timeout <segundos> , que cambia la cantidad de tiempo que su daemon se mantiene en ejecución (el
valor predeterminado es "900" o 15 minutos). Aquí hay un ejemplo de cómo configuraría el asistente de "almacenamiento"
con un nombre de archivo personalizado:

$ git config --global credential.helper 'store --file ~/.my-credentials'

Git incluso te permite configurar varios ayudantes. Al buscar credenciales para un host en particular, Git las consultará en
orden y se detendrá después de proporcionar la primera respuesta. Al guardar las credenciales, Git enviará el nombre de
usuario y la contraseña a todos los ayudantes de la lista y podrán elegir qué hacer con ellos. Así es como se vería
un .gitconfig si tuviera un archivo de credenciales en una memoria USB, pero quisiera usar el caché en memoria para
ahorrar algo de escritura si la unidad no está conectada:

[credencial]
ayudante = almacenar --file /mnt/thumbdrive/.git-credentials ayudante =
caché --tiempo de espera 30000

Bajo el capó

¿Como funciona todo esto? El comando raíz de Git para el sistema auxiliar de credenciales es git credential, que toma un
comando como argumento y luego más entradas a través de stdin.

Esto podría ser más fácil de entender con un ejemplo. Digamos que se configuró un asistente de credenciales y el asistente
almacenó las credenciales para mygithost. Aquí hay una sesión que usa el comando "llenar", que se invoca cuando Git
intenta encontrar credenciales para un host:

$ git credential fill ÿ


protocol=https ÿ host=mygithost
ÿ protocol=https ÿ host=mygithost
username=bob password=s3cre7
$ git credential fill ÿ
protocol=https host=unknownhost

Nombre de usuario para 'https://unknownhost': bob


Contraseña para 'https://bob@unknownhost':
protocol=https host=unknownhost

nombre de

usuario=bob contraseña=s3cre7

ÿ Esta es la línea de comando que inicia la interacción.

ÿ Git-credential está esperando la entrada en stdin. Le proporcionamos las cosas que sabemos: la

338
Machine Translated by Google

protocolo y nombre de host.

ÿ Una línea en blanco indica que la entrada está completa y el sistema de credenciales debe responder con
lo que sabe

ÿ Git-credential luego se hace cargo y escribe en stdout con los bits de información que encontró.

ÿ Si no se encuentran las credenciales, Git le pide al usuario el nombre de usuario y la contraseña, y proporciona
devuélvalos a la salida estándar de invocación (aquí están conectados a la misma consola).

El sistema de credenciales en realidad está invocando un programa que está separado de Git; cuál y cómo depende del valor de
configuración de credential.helper . Hay varias formas que puede tomar:

Valor de configuración Conducta

Foo
Ejecuta git-credential-foo

foo -a --opt=bcd Ejecuta git-credential-foo -a --opt=bcd

/absoluta/ruta/foo -xyz Ejecuta /absolute/path/foo -xyz

!f() { echo "contraseña=s3cre7"; }; F ¡ Código después ! evaluado en caparazón

Entonces, los ayudantes descritos anteriormente en realidad se denominan git-credential-cache, git-credential-store, etc., y
podemos configurarlos para tomar argumentos de línea de comandos. La forma general para esto es "git-credential-foo [args]
<acción>". El protocolo stdin/stdout es el mismo que git-credential, pero usan un conjunto de acciones ligeramente diferente:

• get es una solicitud de un par de nombre de usuario/contraseña.

• store es una solicitud para guardar un conjunto de credenciales en la memoria de este ayudante.

• borrar purgar las credenciales de las propiedades dadas de la memoria de este ayudante.

Para las acciones de almacenar y borrar , no se requiere respuesta (Git lo ignora de todos modos). Sin embargo, para la acción
de obtener , Git está muy interesado en lo que el ayudante tiene que decir. Si el ayudante no sabe nada útil, simplemente puede
salir sin resultados, pero si lo sabe, debe aumentar la información proporcionada con la información que ha almacenado. La
salida se trata como una serie de sentencias de asignación; cualquier cosa proporcionada reemplazará lo que Git ya sabe.

Aquí está el mismo ejemplo anterior, pero omitiendo git-credential y yendo directamente a git credential-store:

$ git credential-store --file ~/git.store store ÿ


protocol=https host=mygithost username=bob
password=s3cre7 $ git credential-store --file ~/git.store
get ÿ protocol=https host=mygithost

nombre de usuario =

bob ÿ contraseña = s3cre7

339
Machine Translated by Google

ÿ Aquí le decimos a git-credential-store que guarde algunas credenciales: el nombre de usuario "bob" y el
la contraseña "s3cre7" se utilizará cuando https://mygithost se accede.

ÿ Ahora recuperaremos esas credenciales. Proporcionamos las partes de la conexión que ya conocemos.
(https://migithost), y una línea vacía.

ÿ git-credential-store responde con el nombre de usuario y la contraseña que almacenamos anteriormente.

Así es como se ve el archivo ~/git.store :

https://bob:s3cre7@mygithost

Es solo una serie de líneas, cada una de las cuales contiene una URL decorada con credenciales. Los ayudantes de osxkeychain
y wincred usan el formato nativo de sus almacenes de respaldo, mientras que el caché usa su propio formato en memoria (que
ningún otro proceso puede leer).

Una caché de credenciales personalizada

Dado que git-credential-store y friends son programas separados de Git, no es un gran salto darse cuenta de que cualquier
programa puede ser un asistente de credenciales de Git. Los ayudantes proporcionados por Git cubren muchos casos de uso
comunes, pero no todos. Por ejemplo, supongamos que su equipo tiene algunas credenciales que se comparten con todo el
equipo, quizás para la implementación. Estos se almacenan en un directorio compartido, pero no desea copiarlos en su propio
almacén de credenciales, ya que cambian con frecuencia. Ninguno de los ayudantes existentes cubre este caso; veamos qué se
necesitaría para escribir el nuestro. Hay varias características clave que este programa debe tener:

1. La única acción a la que debemos prestar atención es obtener; almacenar y borrar son operaciones de escritura, por lo que
simplemente salga limpiamente cuando se reciban.

2. El formato de archivo del archivo de credenciales compartidas es el mismo que usa git-credential-store.

3. La ubicación de ese archivo es bastante estándar, pero debemos permitir que el usuario pase una ruta personalizada
por si acaso.

Una vez más, escribiremos esta extensión en Ruby, pero cualquier idioma funcionará siempre que Git pueda ejecutar el producto
terminado. Aquí está el código fuente completo de nuestro nuevo asistente de credenciales:

340
Machine Translated by Google

#!/usr/bin/env rubí

requiere 'optparse'

ruta = File.expand_path '~/.git-credentials' ÿ


OptionParser.new do |opts| opts.banner = 'USO: git-
credential-read-only [opciones] <acción>' opts.on('-f', '--file PATH', 'Specify path for
backing store') do |argpath| ruta = Archivo.expand_path argpath
ÿ

final
fin.analizar!

exit (0) a menos que ARGV [0].downcase == 'get' ÿ


exit (0) a menos que File.exists? sendero

conocido = {} ÿ
while línea = STDIN.se
''
rompe si línea.strip == k,v =
línea.strip.split '=', 2 conocido[k] = v final

File.readlines(path).each do |fileline| ÿ
prot,usuario,contraseña,host = fileline.scan(/^(.*?):\/\/(.*?):(.*?)@(.*)$/).primero si prot ==
conocido['protocolo'] y host == conocido['host'] y usuario == conocido['nombre de usuario'] luego

pone "protocolo=#{prot}" pone


ÿ

"host=#{host}" pone "nombre


ÿ

de usuario=#{usuario}" pone
ÿ

"contraseña=#{contraseña}"
ÿ

exit(0)
final
final

ÿ Aquí analizamos las opciones de la línea de comandos, lo que permite al usuario especificar el archivo de entrada. El valor
predeterminado es ~/.git-credentials.

ÿ Este programa solo responde si la acción es obtener y existe el archivo de almacenamiento de respaldo.

ÿ Este ciclo lee desde stdin hasta que se alcanza la primera línea en blanco. Las entradas se almacenan en el conocido
hash para referencia posterior.

ÿ Este bucle lee el contenido del archivo de almacenamiento y busca coincidencias. Si el protocolo, el host y el nombre de usuario
de conocido coinciden con esta línea, el programa imprime los resultados en la salida estándar y sale.

Guardaremos nuestro asistente como git-credential-read-only, lo pondremos en algún lugar de nuestra RUTA y lo marcaremos como
ejecutable. Así es como se ve una sesión interactiva:

341
Machine Translated by Google

$ git credential-read-only --file=/mnt/shared/creds get


protocol=https host=mygithost username=bob

protocolo = https
host = mygithost
nombre de usuario

= bob contraseña = s3cre7

Dado que su nombre comienza con "git-", podemos usar la sintaxis simple para el valor de configuración:

$ git config --global credential.helper 'solo lectura --file /mnt/shared/creds'

Como puede ver, extender este sistema es bastante sencillo y puede resolver algunos problemas comunes para
usted y su equipo.

Resumen
Ha visto una serie de herramientas avanzadas que le permiten manipular sus confirmaciones y el área de preparación
con mayor precisión. Cuando observe problemas, debería poder averiguar fácilmente qué compromiso los introdujo,
cuándo y por quién. Si desea utilizar subproyectos en su proyecto, ha aprendido a adaptarse a esas necesidades.
En este punto, debería poder hacer la mayoría de las cosas en Git que necesitará en la línea de comando día a día
y sentirse cómodo haciéndolo.

342
Machine Translated by Google

Personalizando Git
Hasta ahora, hemos cubierto los conceptos básicos de cómo funciona Git y cómo usarlo, y hemos presentado una serie de
herramientas que proporciona Git para ayudarlo a usarlo de manera fácil y eficiente. En este capítulo, veremos cómo puede hacer
que Git funcione de una manera más personalizada, introduciendo varias opciones de configuración importantes y el sistema de
ganchos. Con estas herramientas, es fácil hacer que Git funcione exactamente como usted, su empresa o su grupo lo necesitan.

Configuración Git
Como leyó brevemente en Primeros pasos, puede especificar los ajustes de configuración de Git con el comando git config . Una
de las primeras cosas que hizo fue configurar su nombre y dirección de correo electrónico:

$ git config --usuario global.nombre "John Doe"


$ git config --usuario global.email johndoe@example.com

Ahora aprenderá algunas de las opciones más interesantes que puede configurar de esta manera para personalizar su uso de Git.

Primero, una revisión rápida: Git usa una serie de archivos de configuración para determinar el comportamiento no predeterminado
que puede desear. El primer lugar en el que Git busca estos valores es en el archivo [ruta]/etc/gitconfig de todo el sistema, que
contiene configuraciones que se aplican a cada usuario en el sistema y todos sus repositorios. Si pasa la opción --system a git
config, lee y escribe desde este archivo específicamente.

El siguiente lugar que busca Git es el archivo ~/.gitconfig (o ~/.config/git/config) , que es específico para cada usuario. Puede
hacer que Git lea y escriba en este archivo pasando la opción --global .

Finalmente, Git busca valores de configuración en el archivo de configuración en el directorio de Git (.git/config) de cualquier
repositorio que esté usando actualmente. Estos valores son específicos de ese único repositorio y representan pasar la opción --
local a git config. Si no especifica con qué nivel desea trabajar, este es el valor predeterminado.

Cada uno de estos "niveles" (sistema, global, local) sobrescribe los valores en el nivel anterior, por lo que los valores en .git/config
superan a los de [ruta]/etc/gitconfig, por ejemplo.

Los archivos de configuración de Git son texto sin formato, por lo que también puede establecer estos

ÿ valores editando manualmente el archivo e insertando la sintaxis correcta. Sin embargo, generalmente es
más fácil ejecutar el comando git config .

Configuración básica del cliente

Las opciones de configuración reconocidas por Git se dividen en dos categorías: del lado del cliente y del lado del servidor. La
mayoría de las opciones son del lado del cliente, configurando sus preferencias personales de trabajo. Se admiten muchas ,
muchas opciones de configuración, pero una gran parte de ellas son útiles solo en ciertos casos extremos; Cubriremos solo las
opciones más comunes y útiles aquí. Si desea ver una lista de todas las opciones que reconoce su versión de Git, puede ejecutar:

343
Machine Translated by Google

$ hombre git-config

Este comando enumera todas las opciones disponibles con bastante detalle. También puede encontrar este material de referencia
en https://git-scm.com/docs/git-config.

core.editor

De manera predeterminada, Git usa lo que haya configurado como su editor de texto predeterminado a través de una de las variables
de entorno de shell VISUAL o EDITOR, o recurre al editor vi para crear y editar sus mensajes de confirmación y etiquetado. Para
cambiar ese valor predeterminado a otra cosa, puede usar la configuración core.editor :

$ git config --global core.editor emacs

Ahora, sin importar cuál esté configurado como su editor de shell predeterminado, Git activará Emacs para editar mensajes.

commit.plantilla

Si establece esto en la ruta de un archivo en su sistema, Git usará ese archivo como el mensaje inicial predeterminado cuando
confirme. El valor de crear una plantilla de confirmación personalizada es que puede usarla para recordarse a sí mismo (o a otros) el
formato y el estilo adecuados al crear un mensaje de confirmación.

Por ejemplo, considere un archivo de plantilla en ~/.gitmessage.txt que se vea así:

Línea de asunto (trate de mantener menos de 50 caracteres)

Descripción de varias líneas del compromiso,


siéntase libre de ser detallado.

[Boleto: X]

Tenga en cuenta cómo esta plantilla de compromiso le recuerda al autor de confirmación que debe mantener la línea de asunto
breve (por el bien de git log --oneline output), para agregar más detalles debajo de eso y para referirse a un problema o número de
ticket de seguimiento de errores, si existe.

Para decirle a Git que lo use como el mensaje predeterminado que aparece en su editor cuando ejecuta git commit, establezca el
valor de configuración de commit.template :

$ git config --global commit.template ~/.gitmessage.txt $ git commit

Luego, su editor se abrirá a algo como esto para su mensaje de confirmación de marcador de posición cuando confirme:

344
Machine Translated by Google

Línea de asunto (trate de mantener menos de 50 caracteres)

Descripción de varias líneas del compromiso,


siéntase libre de ser detallado.

[Boleto: X]
# Ingrese el mensaje de confirmación para sus cambios. Las líneas que comiencen #
con '#' se ignorarán y un mensaje vacío anulará la confirmación.
# En el maestro de rama
# Cambios que se confirmarán: #
(use "git reset HEAD <archivo>..." para quitar la preparación)
# # modificado: lib/test.rb #

~
~

".git/COMMIT_EDITMSG" 14L, 297C

Si su equipo tiene una política de mensajes de confirmación, colocar una plantilla para esa política en su sistema y configurar Git
para que la use de forma predeterminada puede ayudar a aumentar las posibilidades de que esa política se siga con regularidad.

core.pager

Esta configuración determina qué buscapersonas se usa cuando las páginas de Git se generan, como log y diff. Puede configurarlo
en más o en su buscapersonas favorito (de manera predeterminada, es menos), o puede desactivarlo configurándolo en una cadena
en blanco:

''
$ git config --global core.pager

Si ejecuta eso, Git paginará la salida completa de todos los comandos, sin importar cuánto tiempo sean.

usuario.signingkey

Si está creando etiquetas anotadas firmadas (como se explica en Cómo firmar su trabajo), configurar su clave de firma GPG como un
ajuste de configuración facilita las cosas. Configure su ID de clave así:

$ git config --global user.signingkey <gpg-key-id>

Ahora, puede firmar etiquetas sin tener que especificar su clave cada vez con el comando de etiqueta git :

$ git tag -s <nombre-de-etiqueta>

core.excluyearchivo

Puede poner patrones en el archivo .gitignore de su proyecto para que Git no los vea como archivos sin seguimiento o

345
Machine Translated by Google

intente organizarlos cuando ejecute git add en ellos, como se explica en Ignorar archivos.

Pero a veces desea ignorar ciertos archivos para todos los repositorios con los que trabaja. Si su computadora ejecuta macOS,
probablemente esté familiarizado con los archivos .DS_Store . Si su editor preferido es Emacs o Vim, conoce los nombres de
archivo que terminan con ~ o .swp.

Esta configuración le permite escribir una especie de archivo .gitignore global . Si crea un archivo ~/.gitignore_global con estos
contenidos:

*~

.*.swp .DS_Store

…y ejecutas git config --global core.excludesfile ~/.gitignore_global, Git nunca más te molestará con esos archivos.

ayuda.autocorrección

Si escribe mal un comando, le muestra algo como esto:

$ git chekcout master git:


'chekcout' no es un comando de git. Consulte 'git --help'.

El comando más similar es


verificar

Git trata de averiguar a qué te referías, pero aún se niega a hacerlo. Si establece help.autocorrect en 1, Git ejecutará este
comando por usted:

$ git chekcout master


ADVERTENCIA: Llamó a un comando Git llamado 'chekcout', que no existe.
Continuando bajo el supuesto de que querías decir 'pago' en 0.1
segundos automáticamente...

Tenga en cuenta que el negocio de "0.1 segundos". help.autocorrect es en realidad un número entero que representa décimas
de segundo. Entonces, si lo configura en 50, Git le dará 5 segundos para cambiar de opinión antes de ejecutar el comando
autocorregido.

Colores en Git

Git es totalmente compatible con la salida de terminal en color, lo que ayuda en gran medida a analizar visualmente la salida del
comando de forma rápida y sencilla. Varias opciones pueden ayudarlo a configurar el color según sus preferencias.

color.ui

Git colorea automáticamente la mayor parte de su salida, pero hay un cambio maestro si no te gusta este comportamiento. Para
desactivar todas las salidas de terminal de color de Git, haga lo siguiente:

346
Machine Translated by Google

$ git config --global color.ui falso

La configuración predeterminada es automática, que colorea la salida cuando va directamente a una terminal, pero omite los
códigos de control de color cuando la salida se redirige a una tubería o un archivo.

También puede configurarlo para que siempre ignore la diferencia entre terminales y tuberías. Rara vez querrás esto; en la
mayoría de los escenarios, si desea códigos de color en su salida redirigida, puede pasar un indicador --color al comando Git para
forzarlo a usar códigos de color. La configuración predeterminada es casi siempre lo que desea.

color.*

Si desea ser más específico acerca de qué comandos están coloreados y cómo, Git proporciona configuraciones de color
específicas para los verbos. Cada uno de estos se puede configurar como verdadero, falso o siempre:

color.rama
color.diff
color.interactivo
color.estado

Además, cada uno de estos tiene subconfiguraciones que puede usar para establecer colores específicos para partes de la salida,
si desea anular cada color. Por ejemplo, para configurar la metainformación en su salida de diff en primer plano azul, fondo negro
y texto en negrita, puede ejecutar:

$ git config --global color.diff.meta "azul negro negrita"

Puede establecer el color en cualquiera de los siguientes valores: normal, negro, rojo, verde, amarillo, azul, magenta, cian o
blanco. Si desea un atributo como negrita en el ejemplo anterior, puede elegir entre negrita, tenue, ul (subrayado), parpadeo y
reverso (intercambiar primer plano y fondo).

Herramientas de comparación y combinación externa

Aunque Git tiene una implementación interna de diff, que es lo que hemos estado mostrando en este libro, puede configurar una
herramienta externa en su lugar. También puede configurar una herramienta gráfica de resolución de conflictos de combinación
en lugar de tener que resolver los conflictos manualmente. Demostraremos cómo configurar Perforce Visual Merge Tool (P4Merge)
para hacer sus diferencias y fusionar resoluciones, porque es una buena herramienta gráfica y es gratis.

Si desea probar esto, P4Merge funciona en todas las plataformas principales, por lo que debería poder hacerlo.
Usaremos nombres de rutas en los ejemplos que funcionan en sistemas macOS y Linux; para Windows, deberá cambiar /usr/local/
bin a una ruta ejecutable en su entorno.

Para comenzar, descargue P4Merge de Perforce. A continuación, configurará secuencias de comandos de envoltura externas
para ejecutar sus comandos. Usaremos la ruta de macOS para el ejecutable; en otros sistemas, será donde esté instalado su
binario p4merge . Configure un script contenedor de combinación llamado extMerge que llama a su binario con todos los
argumentos proporcionados:

347
Machine Translated by Google

$ cat /usr/local/bin/extMerge #!/


bin/sh /Applications/p4merge.app/
Contents/MacOS/p4merge $*

El contenedor diff verifica para asegurarse de que se proporcionen siete argumentos y pasa dos de ellos a su secuencia de
comandos de combinación. De forma predeterminada, Git pasa los siguientes argumentos al programa diff:

ruta archivo antiguo hexadecimal antiguo modo antiguo archivo nuevo hexadecimal nuevo modo nuevo

Debido a que solo desea los argumentos del archivo antiguo y del archivo nuevo , utiliza el script contenedor para pasar los que
necesita.

$ cat /usr/local/bin/extDiff #!/bin/


sh [ $# -eq 7 ] && /usr/local/bin/
extMerge "$2" "$5"

También debe asegurarse de que estas herramientas sean ejecutables:

$ sudo chmod +x /usr/local/bin/extMerge $


sudo chmod +x /usr/local/bin/extDiff

Ahora puede configurar su archivo de configuración para usar sus herramientas personalizadas de resolución de combinación y
diferencias. Esto requiere una serie de configuraciones personalizadas: merge.tool para decirle a Git qué estrategia usar,
mergetool.<tool>.cmd para especificar cómo ejecutar el comando, mergetool.<tool>.trustExitCode para decirle a Git si el código de
salida de ese programa indica una resolución de fusión exitosa o no, y diff.external para decirle a Git qué comando ejecutar para
diffs. Entonces, puede ejecutar cuatro comandos de configuración:

$ git config --global merge.tool extMerge $ git


config --global mergetool.extMerge.cmd \ 'extMerge
"$BASE" "$LOCAL" "$REMOTE" "$MERGED"' $ git config
--global mergetool.extMerge .trustExitCode false $ git config --global
diff.external extDiff

o puede editar su archivo ~/.gitconfig para agregar estas líneas:

[combinar]
herramienta = extMerge
[mergetool "extMerge"] cmd = extMerge "$BASE" "$LOCAL"
"$REMOTE" "$MERGED" trustExitCode = false
[dif]
externo = extDiff

348
Machine Translated by Google

Después de configurar todo esto, si ejecuta comandos diff como este:

$ git diferencia 32d1776b1^ 32d1776b1

En lugar de obtener la salida diff en la línea de comando, Git activa P4Merge, que se parece a esto:

Figura 142. Fusión P4

Si intenta fusionar dos ramas y posteriormente tiene conflictos de fusión, puede ejecutar el comando git mergetool; inicia P4Merge para permitirle

resolver los conflictos a través de esa herramienta GUI.

Lo bueno de esta configuración de contenedor es que puede cambiar sus herramientas de diferenciación y combinación fácilmente. Por ejemplo,

para cambiar sus herramientas extDiff y extMerge para ejecutar la herramienta KDiff3, todo lo que tiene que hacer es editar su archivo extMerge :

$ cat /usr/local/bin/extMerge #!/bin/


sh /Aplicaciones/kdiff3.app/Contents/
MacOS/kdiff3 $*

Ahora, Git utilizará la herramienta KDiff3 para la visualización de diferencias y la resolución de conflictos de fusión.

Git viene preestablecido para usar una serie de otras herramientas de resolución de combinación sin tener que configurar la configuración de

cmd. Para ver una lista de las herramientas que admite, intente esto:

349
Machine Translated by Google

$ git mergetool --tool-ayuda


'git mergetool --tool=<tool>' se puede establecer en uno de los siguientes:
ÿ

surgir
ÿ

gvimdiff
ÿ

gvimdiff2
ÿ

abierto
ÿ

p4merge
ÿ

vimdiff
ÿ

vimdiff2

Las siguientes herramientas son válidas, pero actualmente no están disponibles:


ÿ

araxis
ÿ

bc3
ÿ

comparar código
ÿ

caminante delta
ÿ

difuminar
ÿ

difuso
ÿ

emerger
ÿ

kdiff3
ÿ

fusionar

tkdif
ÿ

tortuga emerge
ÿ

xxdif

Algunas de las herramientas enumeradas anteriormente solo funcionan en una ventana


ambiente. Si se ejecutan en una sesión de solo terminal, fallarán.

Si no está interesado en usar KDiff3 para diferenciar, sino que quiere usarlo solo para la resolución de fusión,
y el comando kdiff3 está en su camino, entonces puede ejecutar:

$ git config --global merge.herramienta kdiff3

Si ejecuta esto en lugar de configurar los archivos extMerge y extDiff , Git usará KDiff3 para fusionar
resolución y la herramienta Git diff normal para diffs.

Formato y espacios en blanco

Los problemas de formato y espacios en blanco son algunos de los problemas más sutiles y frustrantes que muchos
encuentran los desarrolladores cuando colaboran, especialmente entre plataformas. Es muy fácil para parches o
otro trabajo colaborado para introducir cambios sutiles en los espacios en blanco porque los editores introducen silenciosamente
y si sus archivos alguna vez tocan un sistema Windows, sus finales de línea podrían ser reemplazados. Git tiene un
pocas opciones de configuración para ayudar con estos problemas.

core.autocrlf

Si está programando en Windows y trabaja con personas que no lo son (o viceversa), podrá
probablemente se encuentre con problemas de final de línea en algún momento. Esto se debe a que Windows usa un carácter de
retorno de carro y un carácter de avance de línea para las nuevas líneas en sus archivos, mientras que macOS y Linux

350
Machine Translated by Google

los sistemas utilizan sólo el carácter de avance de línea. Este es un hecho sutil pero increíblemente molesto del trabajo multiplataforma;

muchos editores en Windows reemplazan silenciosamente los finales de línea de estilo LF existentes con CRLF, o insertan ambos caracteres

de final de línea cuando el usuario presiona la tecla Intro.

Git puede manejar esto convirtiendo automáticamente los finales de línea CRLF en LF cuando agrega un archivo al índice, y viceversa cuando

extrae el código en su sistema de archivos. Puede activar esta funcionalidad con la configuración core.autocrlf . Si está en una máquina con

Windows, configúrelo en verdadero : esto convierte las terminaciones LF en CRLF cuando revisa el código:

$ git config --global core.autocrlf verdadero

Si está en un sistema Linux o macOS que usa finales de línea LF, entonces no quiere que Git los convierta automáticamente cuando extrae

archivos; sin embargo, si se introduce accidentalmente un archivo con terminaciones CRLF, es posible que desee que Git lo arregle. Puede

decirle a Git que convierta CRLF a LF en la confirmación, pero no al revés configurando core.autocrlf para ingresar:

$ git config --global core.entrada autocrlf

Esta configuración debería dejarte terminaciones CRLF en las cajas de Windows, pero terminaciones LF en los sistemas macOS y Linux y en

el repositorio.

Si es un programador de Windows que realiza un proyecto solo para Windows, puede desactivar esta función y registrar los retornos de carro

en el repositorio estableciendo el valor de configuración en falso:

$ git config --global core.autocrlf falso

core.espacio en blanco

Git viene predeterminado para detectar y solucionar algunos problemas de espacios en blanco. Puede buscar seis problemas principales de

espacios en blanco: tres están habilitados de forma predeterminada y se pueden desactivar, y tres están deshabilitados de forma predeterminada
pero se pueden activar.

Los tres que están activados por defecto son blank-at-eol, que busca espacios al final de una línea; blank-at-eof, que detecta líneas en blanco
al final de un archivo; y space-before-tab, que busca espacios antes de las tabulaciones al principio de una línea.

Los tres que están deshabilitados de forma predeterminada pero que se pueden activar son sangría sin tabulación, que busca líneas que

comienzan con espacios en lugar de tabulaciones (y se controla mediante la opción de ancho de tabulación ); sangría de tabulación, que busca

tabulaciones en la parte de sangría de una línea; y cr-at-eol, que le dice a Git que los retornos de carro al final de las líneas están bien.

Puede decirle a Git cuál de estos desea habilitar configurando core.whitespace en los valores que desea activar o desactivar, separados por

comas. Puede deshabilitar una opción anteponiendo un - delante de su nombre, o usar el valor predeterminado dejándolo completamente fuera

de la cadena de configuración. Por ejemplo, si desea que se configure todo menos el espacio antes de la pestaña , puede hacer esto (con el

espacio final como una abreviatura para cubrir tanto el espacio en blanco en eol como en blanco en eof):

351
Machine Translated by Google

$ git config --global core.whitespace \ final-


space,-space-before-tab,indent-with-non-tab,tab-in-dent,cr-at-eol

O puede especificar solo la parte personalizada:

$ git config --global core.whitespace \ -space-


before-tab,indent-with-non-tab,tab-in-dent,cr-at-eol

Git detectará estos problemas cuando ejecute un comando git diff e intente colorearlos para que pueda solucionarlos antes de
comprometerse. También usará estos valores para ayudarlo cuando aplique parches con git apply. Cuando esté aplicando
parches, puede pedirle a Git que le avise si está aplicando parches con los problemas de espacios en blanco especificados:

$ git apply --whitespace=warn <parche>

O puede hacer que Git intente solucionar el problema automáticamente antes de aplicar el parche:

$ git apply --whitespace=arreglar <parche>

Estas opciones también se aplican al comando git rebase . Si cometió problemas de espacios en blanco, pero aún no los ha
actualizado, puede ejecutar git rebase --whitespace=fix para que Git solucione automáticamente los problemas de espacios en
blanco mientras reescribe los parches.

Configuración del servidor

No hay tantas opciones de configuración disponibles para el lado del servidor de Git, pero hay algunas interesantes de las que
quizás desee tomar nota.

recibir.fsckObjects

Git es capaz de asegurarse de que cada objeto recibido durante una inserción aún coincida con su suma de verificación SHA-1
y apunte a objetos válidos. Sin embargo, no hace esto por defecto; es una operación bastante costosa y podría ralentizar la
operación, especialmente en grandes repositorios o envíos. Si desea que Git verifique la coherencia de los objetos en cada
inserción, puede forzarlo a que lo haga configurando receive.fsckObjects en
cierto:

$ git config --system recibir.fsckObjects verdadero

Ahora, Git verificará la integridad de su repositorio antes de que se acepte cada envío para asegurarse de que los clientes
defectuosos (o maliciosos) no introduzcan datos corruptos.

recibir.denyNonFastForwards

Si cambia la base de las confirmaciones que ya envió y luego intenta enviarlas de nuevo, o si intenta enviar una confirmación a
una rama remota que no contiene la confirmación que la rama remota

352
Machine Translated by Google

apunta actualmente, se le negará. En general, esta es una buena política; pero en el caso de la rebase, puede determinar que sabe lo
que está haciendo y puede forzar la actualización de la rama remota con un indicador -f en su comando push.

Para decirle a Git que rechace las inserciones forzadas, configure receive.denyNonFastForwards:

$ git config --system receive.denyNonFastForwards verdadero

La otra forma en que puede hacer esto es a través de ganchos de recepción del lado del servidor, que cubriremos en un momento.
Ese enfoque le permite hacer cosas más complejas, como negar avances no rápidos a un determinado subconjunto de usuarios.

recibir.denyDeletes

Una de las soluciones a la política denyNonFastForwards es que el usuario elimine la rama y luego la vuelva a insertar con la nueva
referencia. Para evitar esto, establezca receive.denyDeletes en verdadero:

$ git config --system receive.denyDeletes verdadero

Esto niega cualquier eliminación de ramas o etiquetas; ningún usuario puede hacerlo. Para eliminar sucursales remotas, debe eliminar
los archivos ref del servidor manualmente. También hay formas más interesantes de hacer esto por usuario a través de ACL, como
aprenderá en Ejemplo de política aplicada por Git.

Atributos Git
Algunas de estas configuraciones también se pueden especificar para una ruta, de modo que Git aplique esas configuraciones solo
para un subdirectorio o subconjunto de archivos. Estas configuraciones específicas de la ruta se denominan atributos de Git y se
establecen en un archivo .gitattributes en uno de sus directorios (normalmente la raíz de su proyecto) o en el archivo .git/info/attributes
si no desea el archivo de atributos. comprometidos con tu proyecto.

Con los atributos, puede hacer cosas como especificar estrategias de combinación separadas para archivos o directorios individuales
en su proyecto, decirle a Git cómo diferenciar archivos que no son de texto o hacer que Git filtre el contenido antes de ingresarlo o
sacarlo de Git. En esta sección, aprenderá sobre algunos de los atributos que puede establecer en sus rutas en su proyecto de Git y
verá algunos ejemplos del uso de esta función en la práctica.

Archivos binarios

Un buen truco para el que puede usar los atributos de Git es decirle a Git qué archivos son binarios (en caso de que no pueda
descifrarlo) y darle a Git instrucciones especiales sobre cómo manejar esos archivos. Por ejemplo, algunos archivos de texto pueden
ser generados por una máquina y no diferibles, mientras que algunos archivos binarios sí pueden ser diferidos. Verás cómo decirle a
Git cuál es cuál.

Identificación de archivos binarios

Algunos archivos parecen archivos de texto, pero para todos los efectos deben tratarse como datos binarios. Por ejemplo, los proyectos
de Xcode en macOS contienen un archivo que termina en .pbxproj, que es básicamente un conjunto de datos JSON (formato de datos
de JavaScript de texto sin formato) escrito en el disco por el IDE, que registra la configuración de compilación, etc. Aunque técnicamente
es un archivo de texto (porque todo es UTF-8), no desea tratar

353
Machine Translated by Google

como tal porque es realmente una base de datos liviana: no puede fusionar los contenidos si dos personas lo cambian, y las diferencias
generalmente no son útiles. El archivo está destinado a ser consumido por una máquina. En esencia, desea tratarlo como un archivo
binario.

Para decirle a Git que trate todos los archivos pbxproj como datos binarios, agregue la siguiente línea a su archivo .gitattributes :

*.pbxproj binario

Ahora, Git no intentará convertir ni solucionar problemas de CRLF; ni intentará calcular o imprimir una diferencia para los cambios en
este archivo cuando ejecute git show o git diff en su proyecto.

Diferenciar archivos binarios

También puede usar la funcionalidad de atributos de Git para diferenciar archivos binarios de manera efectiva. Para ello, dígale a Git
cómo convertir sus datos binarios a un formato de texto que se pueda comparar a través de la diferencia normal.

Primero, usará esta técnica para resolver uno de los problemas más molestos conocidos por la humanidad: el control de versiones de
documentos de Microsoft Word. Todo el mundo sabe que Word es el editor más horrible que existe, pero, curiosamente, todo el mundo
todavía lo usa. Si desea controlar las versiones de los documentos de Word, puede pegarlos en un repositorio de Git y confirmarlos de
vez en cuando; pero de que sirve eso Si ejecuta git diff normalmente, solo verá algo como esto:

$ git diff diff --


git a/chapter1.docx b/chapter1.docx index 88839c4..4afcb7c
100644 Los archivos binarios a/chapter1.docx y b/
chapter1.docx difieren

No puede comparar directamente dos versiones a menos que las revise y las escanee manualmente, ¿verdad?
Resulta que puedes hacer esto bastante bien usando los atributos de Git. Ponga la siguiente línea en su archivo .gitattributes :

*.docx diff=palabra

Esto le dice a Git que cualquier archivo que coincida con este patrón (.docx) debe usar el filtro de "palabra" cuando intenta ver una
diferencia que contiene cambios. ¿Qué es el filtro de “palabra”? Tienes que configurarlo. Aquí configurará Git para usar el programa
docx2txt para convertir documentos de Word en archivos de texto legibles, que luego diferenciará correctamente.

Primero, deberá instalar docx2txt; puede descargarlo desde https://sourceforge.net/projects/docx2txt . Siga las instrucciones en el archivo
INSTALL para colocarlo en algún lugar donde su shell pueda encontrarlo. A continuación, escribirá un script contenedor para convertir la
salida al formato que espera Git. Cree un archivo que esté en algún lugar de su ruta llamado docx2txt y agregue estos contenidos:

#!/bin/bash
docx2txt.pl "$1" -

354
Machine Translated by Google

No olvides hacer chmod a+x en ese archivo. Finalmente, puedes configurar Git para usar este script:

$ git config diff.word.textconv docx2txt

Ahora Git sabe que si intenta hacer una diferencia entre dos instantáneas y alguno de los archivos termina en .docx, debe
ejecutar esos archivos a través del filtro de "palabra", que se define como el programa docx2txt . Esto efectivamente crea
buenas versiones basadas en texto de sus archivos de Word antes de intentar diferenciarlos.

He aquí un ejemplo: el capítulo 1 de este libro se convirtió a formato Word y se comprometió en un repositorio de Git. Luego
se agregó un nuevo párrafo. Esto es lo que muestra git diff :

$ git diff diff


--git a/capítulo1.docx b/capítulo1.docx índice
0b013ca..ba25db5 100644
--- a/chapter1.docx +++
b/chapter1.docx @@
-2,6 +2,7 @@ Este
capítulo tratará sobre cómo empezar con Git. Comenzaremos por el principio explicando algunos
antecedentes sobre las herramientas de control de versiones, luego pasaremos a cómo hacer que Git se
ejecute en su sistema y finalmente cómo configurarlo para comenzar a trabajar. Al final de este capítulo,
debe comprender por qué Git existe, por qué debe usarlo y debe estar preparado para hacerlo.

1.1. Acerca del control de


versiones ¿Qué es el "control de versiones" y por qué debería importarle? El control de versiones es un
sistema que registra los cambios en un archivo o conjunto de archivos a lo largo del tiempo para que pueda
recuperar versiones específicas más adelante. Para los ejemplos de este libro, utilizará el código fuente del
software como archivos cuya versión está controlada, aunque en realidad puede hacer esto con casi cualquier
tipo de archivo en una computadora.
+Pruebas: 1, 2, 3.
Si usted es un diseñador gráfico o web y desea conservar todas las versiones de una imagen o diseño (lo
que sin duda desearía), un Sistema de control de versiones (VCS) es una opción muy inteligente. Le
permite revertir archivos a un estado anterior, revertir todo el proyecto a un estado anterior, comparar cambios
a lo largo del tiempo, ver quién modificó por última vez algo que podría estar causando un problema, quién
introdujo un problema y cuándo, y más. El uso de un VCS generalmente también significa que si arruina las
cosas o pierde archivos, puede recuperarlos fácilmente. Además, obtienes todo esto por muy pocos gastos
generales.
1.1.1. Sistemas de control de versiones locales
El método de control de versiones elegido por muchas personas es copiar archivos en otro
directorio (quizás un directorio con marca de tiempo, si son inteligentes). Este enfoque es muy común porque
es muy simple, pero también es increíblemente propenso a errores. Es fácil olvidar en qué directorio se
encuentra y accidentalmente escribir en el archivo incorrecto o copiar archivos que no deseaba.

Git nos dice de manera exitosa y sucinta que agregamos la cadena "Pruebas: 1, 2, 3", lo cual es correcto.
No es perfecto (los cambios de formato no se mostrarían aquí), pero sin duda funciona.

Otro problema interesante que puede resolver de esta manera consiste en diferenciar archivos de imagen. Una forma de
hacer esto es pasar archivos de imagen a través de un filtro que extrae su información EXIF: metadatos que se registran.

355
Machine Translated by Google

con la mayoría de los formatos de imagen. Si descarga e instala el programa exiftool , puede usarlo para

convierta sus imágenes en texto sobre los metadatos, por lo que al menos la diferencia le mostrará un texto

representación de cualquier cambio que haya ocurrido. Ponga la siguiente línea en su archivo .gitattributes :

*.png diferencia=exif

Configure Git para usar esta herramienta:

$ git config diff.exif.textconv exiftool

Si reemplaza una imagen en su proyecto y ejecuta git diff, verá algo como esto:

diff --git a/imagen.png b/imagen.png


índice 88839c4..4afcb7c 100644
--- a/imagen.png
+++ b/imagen.png
@@ -1,12 +1,12 @@
Número de versión de ExifTool : 7,74
-Tamaño de archivo -Fecha/hora de : 70 KB
modificación de archivo +Tamaño de : 2009:04:21 07:02:45-07:00
archivo +Fecha/hora de modificación de : 94 KB
archivo Tipo de archivo Tipo MIME -Ancho : 2009:04:21 07:02:43-07:00
de imagen -Alto de imagen +Ancho de : PNG
imagen +Alto de imagen Profundidad de : imagen/png
bits Tipo de color : 1058
: 889
: 1056
: 827
:8
: RGB con Alfa

Puede ver fácilmente que el tamaño del archivo y las dimensiones de la imagen han cambiado.

Expansión de palabras clave

Los desarrolladores acostumbrados a esos sistemas a menudo solicitan la expansión de palabras clave de estilo SVN o CVS. los

El problema principal con esto en Git es que no puedes modificar un archivo con información sobre la confirmación.

después de que te hayas comprometido, porque Git primero hace una suma de verificación del archivo. Sin embargo, puede inyectar texto en un archivo

cuando esté desprotegido y elimínelo nuevamente antes de agregarlo a una confirmación. Los atributos de Git te ofrecen

dos maneras de hacer esto.

Primero, puede inyectar la suma de verificación SHA-1 de un blob en un campo $Id$ en el archivo automáticamente. Si usted

establece este atributo en un archivo o conjunto de archivos, luego, la próxima vez que revise esa rama, Git lo hará

reemplace ese campo con el SHA-1 del blob. Es importante notar que no es el SHA-1 del

commit, sino del propio blob. Ponga la siguiente línea en su archivo .gitattributes :

356
Machine Translated by Google

*.txt identificación

Agregue una referencia $Id$ a un archivo de prueba:

$ echo '$Id$' > prueba.txt

La próxima vez que revise este archivo, Git inyecta el SHA-1 del blob:

$ rm test.txt $
git checkout -- test.txt $ cat
test.txt $Id:
42812b7653c7b88933f8a9d6cad0ca16714b9bb3 $

Sin embargo, ese resultado es de uso limitado. Si usó la sustitución de palabras clave en CVS o Subversion, puede incluir una
marca de fecha: el SHA-1 no es tan útil porque es bastante aleatorio y no puede saber si un SHA-1 es más antiguo o más nuevo
que otro. con solo mirarlos.

Resulta que puede escribir sus propios filtros para hacer sustituciones en los archivos al confirmar/pagar.
Estos se denominan filtros "limpios" y "difuminados". En el archivo .gitattributes , puede configurar un filtro para rutas particulares
y luego configurar secuencias de comandos que procesarán los archivos justo antes de que se desprotejan ("difuminar", consulte
El filtro "difuminado" se ejecuta al finalizar la compra) y justo antes de que están preparados ("limpiar", consulte El filtro "limpio"
se ejecuta cuando los archivos están preparados). Estos filtros se pueden configurar para hacer todo tipo de cosas divertidas.

Figura 143. El filtro "difuminado" se ejecuta al finalizar la compra

357
Machine Translated by Google

Figura 144. El filtro "limpio" se ejecuta cuando los archivos se preparan

El mensaje de confirmación original para esta característica brinda un ejemplo simple de cómo ejecutar todo su
código fuente C a través del programa de sangría antes de confirmar. Puede configurarlo configurando el atributo de
filtro en su archivo .gitattributes para filtrar archivos \*.c con el filtro "sangría":

*.c filtro=sangría

Luego, dígale a Git qué hace el filtro de "sangría" al difuminar y limpiar:

$ git config --global filter.indent.clean sangría $ git config --global


filter.indent.smudge cat

En este caso, cuando envíe archivos que coincidan con *.c, Git los ejecutará a través del programa de sangría antes
de prepararlos y luego los ejecutará a través del programa cat antes de volver a registrarlos en el disco. El programa
cat esencialmente no hace nada: escupe los mismos datos que vienen. Esta combinación filtra efectivamente todos
los archivos de código fuente C a través de la sangría antes de confirmar.

Otro ejemplo interesante es la expansión de la palabra clave $Date$ , estilo RCS. Para hacer esto correctamente,
necesita un pequeño script que tome un nombre de archivo, calcule la última fecha de confirmación para este
proyecto e inserte la fecha en el archivo. Aquí hay un pequeño script de Ruby que hace eso:

#! /usr/bin/env ruby data =


STDIN.read
last_date = `git log --pretty=format:"%ad" -1` puts
data.gsub('$Date$', '$Date: ' + last_date.to_s + '$')

Todo lo que hace el script es obtener la fecha de confirmación más reciente del comando git log , pegarla en cualquier
cadena $Date$ que vea en stdin e imprimir los resultados; debería ser fácil de hacer en cualquier idioma en el que
se sienta más cómodo. Puede nombrar este archivo expand_date y ponerlo en su ruta. Ahora, necesitas

358
Machine Translated by Google

configure un filtro en Git ( llámelo dater ) y dígale que use su filtro expand_date para borrar los archivos al finalizar la
compra. Usarás una expresión de Perl para limpiar eso en la confirmación:

$ git config filter.dater.smudge expand_date $ git


config filter.dater.clean 'perl -pe "s/\\\$Date[^\\\$]*\\\$/\\\$Date\\\ PS

Este fragmento de código de Perl elimina todo lo que ve en una cadena $Date$ , para volver al punto de partida. Ahora
que su filtro está listo, puede probarlo configurando un atributo Git para ese archivo que involucra el nuevo filtro y
creando un archivo con su palabra clave $Date$ :

fecha*.txt filtro=fecha

$ echo '# $Fecha$' > fecha_prueba.txt

Si confirma esos cambios y revisa el archivo nuevamente, verá la palabra clave correctamente sustituida:

$ git add date_test.txt .gitattributes $ git commit


-m "Expansión de fecha de prueba en Git" $ rm
date_test.txt $ git checkout date_test.txt $ cat
date_test.txt # $Date: Tue Apr 21 07:26:52 2009 -
0700$

Puede ver cuán poderosa puede ser esta técnica para aplicaciones personalizadas. Sin embargo, debe tener cuidado,
porque el archivo .gitattributes se confirma y pasa con el proyecto, pero el controlador (en este caso, dater) no lo está,
por lo que no funcionará en todas partes. Cuando diseñe estos filtros, deberían poder fallar correctamente y hacer que
el proyecto siga funcionando correctamente.

Exportando tu repositorio
Los datos de atributos de Git también le permiten hacer algunas cosas interesantes al exportar un archivo de su
proyecto.

exportar-ignorar

Puede decirle a Git que no exporte ciertos archivos o directorios al generar un archivo. Si hay un subdirectorio o
archivo que no desea incluir en su archivo de almacenamiento pero que sí desea registrar en su proyecto, puede
determinar esos archivos a través del atributo exportar-ignorar .

Por ejemplo, digamos que tiene algunos archivos de prueba en un subdirectorio test/ y no tiene sentido incluirlos en la
exportación tarball de su proyecto. Puede agregar la siguiente línea a su archivo de atributos de Git:

probar/exportar-ignorar

359
Machine Translated by Google

Ahora, cuando ejecuta git archive para crear un tarball de su proyecto, ese directorio no se incluirá en el archivo.

exportar-sust

Al exportar archivos para su implementación, puede aplicar el formato de git log y el procesamiento de expansión de palabras
clave a partes seleccionadas de archivos marcados con el atributo `export-subst.

Por ejemplo, si desea incluir un archivo llamado LAST_COMMIT en su proyecto y tener metadatos sobre la última confirmación
inyectada automáticamente cuando se ejecuta el archivo git , puede, por ejemplo, configurar sus archivos .gitattributes y
LAST_COMMIT de esta manera:

LAST_COMMIT subst de exportación

$ echo 'Última fecha de confirmación: $Formato:%cd por %aN$' >


LAST_COMMIT $ git add LAST_COMMIT .gitattributes $ git commit -am
'agregando archivo LAST_COMMIT para archivos'

Cuando ejecuta git archive, el contenido del archivo archivado se verá así:

$ git archivo CABEZA | tar xCf ../deployment-testing - $ cat ../


deployment-testing/LAST_COMMIT Última fecha de confirmación:
martes 21 de abril 08:38:48 2009 -0700 por Scott Chacon

Las sustituciones pueden incluir, por ejemplo, el mensaje de confirmación y cualquier nota de git, y el registro de git puede
hacer un ajuste de texto simple:

$ echo '$Formato: Última confirmación: %h por %aN en %cd%n%+w(76,6,9)%B$' > LAST_COMMIT


$ git commit -am 'export-subst uses git log'\' formateador personalizado

git archive usa el procesador `pretty=format:` de git log'\''s directamente,


y elimina el marcado `$Format:` y `$` circundante de la salida.

'

$ git archivo @ | alquitrán xfO - LAST_COMMIT


Último compromiso: 312ccc8 por Jim Hill el viernes 8 de mayo 09:14:04 2015 -0700
ÿ

export-subst usa el formateador personalizado de git log

git archive usa el procesador `pretty=format:` de git log directamente, y elimina el


ÿ

marcado `$Format:` y `$` circundante de la salida.

El archivo resultante es adecuado para el trabajo de implementación, pero como cualquier archivo exportado, no es adecuado
para trabajos de desarrollo posteriores.

360
Machine Translated by Google

fusionar estrategias

También puede usar los atributos de Git para decirle a Git que use diferentes estrategias de combinación para archivos específicos
en su proyecto. Una opción muy útil es decirle a Git que no intente fusionar archivos específicos cuando tengan conflictos, sino
que use su lado de la fusión sobre el de otra persona.

Esto es útil si una rama en su proyecto se ha separado o está especializada, pero desea poder fusionar los cambios nuevamente
y desea ignorar ciertos archivos. Supongamos que tiene un archivo de configuración de la base de datos llamado base de
datos.xml que es diferente en dos ramas y desea fusionarse en su otra rama sin estropear el archivo de la base de datos. Puede
configurar un atributo como este:

base de datos.xml merge=nuestro

Y luego defina una estrategia de combinación ficticia nuestra con:

$ git config --global merge.ours.driver verdadero

Si se fusiona en la otra rama, en lugar de tener conflictos de fusión con el archivo base de datos.xml, verá algo como esto:

$ git merge topic Fusión


automática de base de datos.xml
Fusión hecha por recursiva.

En este caso, database.xml permanece en la versión que tenía originalmente.

Ganchos Git
Como muchos otros sistemas de control de versiones, Git tiene una forma de activar secuencias de comandos personalizadas
cuando ocurren ciertas acciones importantes. Hay dos grupos de estos ganchos: del lado del cliente y del lado del servidor. Los
enlaces del lado del cliente se activan mediante operaciones como la confirmación y la fusión, mientras que los enlaces del lado
del servidor se ejecutan en operaciones de red, como la recepción de confirmaciones enviadas. Puede usar estos ganchos por
todo tipo de razones.

Instalar un gancho

Todos los ganchos se almacenan en el subdirectorio de ganchos del directorio Git. En la mayoría de los proyectos, eso es .git/
hooks. Cuando inicializa un nuevo repositorio con git init, Git llena el directorio de ganchos con un montón de scripts de ejemplo,
muchos de los cuales son útiles por sí mismos; pero también documentan los valores de entrada de cada script. Todos los
ejemplos están escritos como scripts de shell, con algo de Perl incorporado, pero cualquier script ejecutable con el nombre
adecuado funcionará bien; puede escribirlos en Ruby o Python o en cualquier idioma con el que esté familiarizado. Si desea
utilizar los scripts de enlace incluidos, tendrá que cambiarles el nombre; todos sus nombres de archivo terminan con .sample.

Para habilitar un script de enlace, coloque un archivo en el subdirectorio de enlaces de su directorio .git que tenga el nombre
adecuado (sin ninguna extensión) y sea ejecutable. A partir de ese momento, debe ser

361
Machine Translated by Google

llamado. Cubriremos la mayoría de los principales nombres de archivo de gancho aquí.

Ganchos del lado del cliente

Hay muchos ganchos del lado del cliente. Esta sección los divide en ganchos de flujo de trabajo de confirmación, scripts de flujo de trabajo de correo

electrónico y todo lo demás.

Es importante tener en cuenta que los ganchos del lado del cliente no se copian cuando clona un repositorio. Si su intención

ÿ con estos scripts es hacer cumplir una política, probablemente querrá hacerlo en el lado del servidor; consulte el ejemplo en

Ejemplo de política aplicada por Git.

Enlaces de flujo de trabajo de confirmación

Los primeros cuatro ganchos tienen que ver con el proceso de confirmación.

El enlace previo a la confirmación se ejecuta primero, incluso antes de escribir un mensaje de confirmación. Se usa para inspeccionar la instantánea

que está a punto de ser confirmada, para ver si olvidó algo, para asegurarse de que se ejecuten las pruebas o para examinar cualquier cosa que

necesite inspeccionar en el código. Salir de este gancho con un valor distinto de cero aborta la confirmación, aunque puede omitirla con git commit --

no-verify. Puede hacer cosas como verificar el estilo del código (ejecutar lint o algo equivalente), buscar espacios en blanco al final (el enlace

predeterminado hace exactamente esto) o buscar la documentación adecuada sobre nuevos métodos.

El gancho prepare-commit-msg se ejecuta antes de que se active el editor de mensajes de confirmación, pero después de que se cree el mensaje

predeterminado. Te permite editar el mensaje predeterminado antes de que el autor de la confirmación lo vea.

Este enlace toma algunos parámetros: la ruta al archivo que contiene el mensaje de confirmación hasta el momento, el tipo de confirmación y la

confirmación SHA-1 si se trata de una confirmación modificada. Este enlace generalmente no es útil para confirmaciones normales; más bien, es

bueno para confirmaciones en las que el mensaje predeterminado se genera automáticamente, como mensajes de confirmación con plantilla,

confirmaciones de combinación, confirmaciones aplastadas y confirmaciones modificadas. Puede usarlo junto con una plantilla de confirmación para

insertar información mediante programación.

El enlace commit-msg toma un parámetro, que nuevamente es la ruta a un archivo temporal que contiene el mensaje de confirmación escrito por el

desarrollador. Si este script sale distinto de cero, Git cancela el proceso de confirmación, por lo que puede usarlo para validar el estado de su proyecto

o el mensaje de confirmación antes de permitir que se realice una confirmación. En la última sección de este capítulo, demostraremos el uso de este

enlace para verificar que su mensaje de confirmación cumpla con un patrón requerido.

Una vez que se completa todo el proceso de confirmación, se ejecuta el gancho posterior a la confirmación . No requiere ningún parámetro, pero

puede obtener fácilmente la última confirmación ejecutando git log -1 HEAD. Generalmente, este script se usa para notificaciones o algo similar.

Ganchos de flujo de trabajo de correo electrónico

Puede configurar tres enlaces del lado del cliente para un flujo de trabajo basado en correo electrónico. Todos son invocados por el comando git am ,

por lo que si no está usando ese comando en su flujo de trabajo, puede pasar con seguridad a la siguiente sección. Si está tomando parches por

correo electrónico preparados por git format-patch, entonces algunos de estos pueden serle útiles.

El primer enlace que se ejecuta es applypatch-msg. Toma un solo argumento: el nombre del temporal

362
Machine Translated by Google

archivo que contiene el mensaje de confirmación propuesto. Git aborta el parche si este script sale distinto de cero.
Puede usar esto para asegurarse de que un mensaje de confirmación tenga el formato correcto o para normalizar el mensaje haciendo
que el script lo edite en su lugar.

El siguiente enlace que se ejecuta al aplicar parches a través de git am es pre-applypatch. De forma un tanto confusa, se ejecuta después
de que se aplica el parche pero antes de que se realice una confirmación, por lo que puede usarlo para inspeccionar la instantánea antes
de realizar la confirmación. Puede ejecutar pruebas o inspeccionar el árbol de trabajo con este script. Si falta algo o las pruebas no pasan,
salir de cero cancela el script git am sin confirmar el parche.

El último enlace que se ejecuta durante una operación de git am es post-applypatch, que se ejecuta después de realizar la confirmación.
Puede usarlo para notificar a un grupo o al autor del parche que sacó que lo ha hecho.
No puede detener el proceso de aplicación de parches con este script.

Otros ganchos de clientes

El enlace de pre-rebase se ejecuta antes de que rebase cualquier cosa y puede detener el proceso al salir de un valor distinto de cero.
Puede usar este gancho para no permitir el rebase de cualquier confirmación que ya se haya enviado. El ejemplo de gancho previo a la
reorganización que instala Git hace esto, aunque hace algunas suposiciones que pueden no coincidir con su flujo de trabajo.

El enlace posterior a la reescritura se ejecuta mediante comandos que reemplazan las confirmaciones, como git commit --amend y git
rebase (aunque no mediante git filter-branch). Su único argumento es qué comando activó la reescritura y recibe una lista de reescrituras
en stdin. Este gancho tiene muchos de los mismos usos que los ganchos posteriores a la compra y posteriores a la fusión .

Después de ejecutar un pago de Git con éxito, se ejecuta el gancho posterior al pago ; puede usarlo para configurar su directorio de
trabajo correctamente para el entorno de su proyecto. Esto puede significar mover archivos binarios grandes que no desea controlar la
fuente, documentación de generación automática, o algo por el estilo.

El enlace posterior a la combinación se ejecuta después de un comando de combinación exitoso. Puede usarlo para restaurar datos en
el árbol de trabajo que Git no puede rastrear, como datos de permisos. Este enlace también puede validar la presencia de archivos
externos al control de Git que puede querer copiar cuando cambie el árbol de trabajo.

El gancho previo al envío se ejecuta durante el envío de git, después de que se hayan actualizado las referencias remotas pero antes de
que se hayan transferido los objetos. Recibe el nombre y la ubicación del control remoto como parámetros, y una lista de referencias a
actualizar a través de stdin. Puede usarlo para validar un conjunto de actualizaciones de referencia antes de que se produzca una
inserción (un código de salida distinto de cero anulará la inserción).

Ocasionalmente, Git realiza la recolección de elementos no utilizados como parte de su funcionamiento normal, al invocar git gc --auto.
El gancho pre-auto-gc se invoca justo antes de que se lleve a cabo la recolección de elementos no utilizados y se puede usar para
notificarle que esto está sucediendo o para cancelar la recolección si ahora no es un buen momento.

Ganchos del lado del servidor

Además de los enlaces del lado del cliente, puede usar un par de enlaces del lado del servidor importantes como administrador del
sistema para aplicar casi cualquier tipo de política para su proyecto. Estos scripts se ejecutan antes y después de las inserciones en el
servidor. Los ganchos previos pueden salir distintos de cero en cualquier momento para rechazar el empuje, así como

363
Machine Translated by Google

imprimir un mensaje de error al cliente; puede configurar una política de inserción tan compleja como desee.

pre-recibir

La primera secuencia de comandos que se ejecuta cuando se maneja una inserción de un cliente es la recepción previa. Toma una
lista de referencias que se envían desde stdin; si sale distinto de cero, no se acepta ninguno de ellos. Puede usar este enlace para
hacer cosas como asegurarse de que ninguna de las referencias actualizadas no sea de avance rápido, o para controlar el acceso a
todas las referencias y archivos que están modificando con el empuje.

actualizar

La secuencia de comandos de actualización es muy similar a la secuencia de comandos previa a la recepción , excepto que se ejecuta
una vez para cada rama que el pusher intenta actualizar. Si el empujador está tratando de empujar a varias ramas, la recepción previa
se ejecuta solo una vez, mientras que la actualización se ejecuta una vez por rama a la que están empujando. En lugar de leer desde
la entrada estándar, este script toma tres argumentos: el nombre de la referencia (rama), el SHA-1 al que apuntaba la referencia antes
de la inserción y el SHA-1 que el usuario está tratando de insertar. Si el script de actualización sale distinto de cero, solo se rechaza
esa referencia; otras referencias aún se pueden actualizar.

post-recepción

El enlace posterior a la recepción se ejecuta después de que se completa todo el proceso y se puede usar para actualizar otros
servicios o notificar a los usuarios. Toma los mismos datos estándar que el gancho de pre-recepción . Los ejemplos incluyen enviar
una lista por correo electrónico, notificar a un servidor de integración continua o actualizar un sistema de seguimiento de tickets; incluso
puede analizar los mensajes de confirmación para ver si es necesario abrir, modificar o cerrar algún ticket.
Esta secuencia de comandos no puede detener el proceso de inserción, pero el cliente no se desconecta hasta que se completa, así
que tenga cuidado si intenta hacer algo que pueda llevar mucho tiempo.

ÿ Si está escribiendo unde


versiones guión/gancho
indicadores que otrosde
de línea necesitarán
comandos;leer, prefiera
Dentro el largo
de seis meses nos lo agradecerás.

Un ejemplo de política aplicada por Git


En esta sección, usará lo que aprendió para establecer un flujo de trabajo de Git que verifique un formato de mensaje de confirmación
personalizado y permita que solo ciertos usuarios modifiquen ciertos subdirectorios en un proyecto.
Creará secuencias de comandos de cliente que ayuden al desarrollador a saber si su envío será rechazado y secuencias de comandos
de servidor que realmente harán cumplir las políticas.

Los scripts que mostraremos están escritos en Ruby; en parte por nuestra inercia intelectual, pero también porque Ruby es fácil de
leer, incluso si no necesariamente puedes escribirlo. Sin embargo, cualquier idioma funcionará: todas las secuencias de comandos de
enlace de muestra distribuidas con Git están en Perl o Bash, por lo que también puede ver muchos ejemplos de enlaces en esos
idiomas al mirar las muestras.

Gancho del lado del servidor

Todo el trabajo del lado del servidor irá al archivo de actualización en su directorio de ganchos . El enlace de actualización se ejecuta
una vez por cada rama que se empuja y toma tres argumentos:

• El nombre de la referencia que se envía a

364
Machine Translated by Google

• La revisión anterior donde estaba esa rama

• La nueva revisión que se está impulsando

También tiene acceso al usuario que realiza la inserción si la inserción se ejecuta a través de SSH. Si ha permitido que todos se
conecten con un solo usuario (como "git") a través de la autenticación de clave pública, es posible que deba darle a ese usuario un
contenedor de shell que determine qué usuario se está conectando en función de la clave pública y establecer un entorno. variable
en consecuencia. Aquí supondremos que el usuario que se conecta está en la variable de entorno $USER , por lo que su secuencia
de comandos de actualización comienza reuniendo toda la información que necesita:

#!/usr/bin/env rubí

$refname = ARGV[0]
$oldrev = ARGV[1]
$newrev = ARGV[2]
$usuario = ENV['USUARIO']

pone "Políticas de aplicación..."


pone "(#{$refname}) (#{$oldrev[0,6]}) (#{$newrev[0,6]})"

Sí, esas son variables globales. No juzgues, es más fácil demostrarlo de esta manera.

Aplicación de un formato de mensaje de compromiso específico

Su primer desafío es hacer cumplir que cada mensaje de confirmación se adhiera a un formato particular. Solo para tener un objetivo,
suponga que cada mensaje debe incluir una cadena que se parezca a "ref: 1234" porque desea que cada compromiso se vincule a
un elemento de trabajo en su sistema de tickets. Debe observar cada confirmación que se envía hacia arriba, ver si esa cadena está
en el mensaje de confirmación y, si la cadena está ausente en alguna de las confirmaciones, salir con un valor distinto de cero para
que se rechace la inserción.

Puede obtener una lista de los valores SHA-1 de todas las confirmaciones que se envían tomando los valores $newrev y $oldrev y
pasándolos a un comando de plomería de Git llamado git rev-list. Este es básicamente el comando git log , pero de manera
predeterminada, solo imprime los valores SHA-1 y ninguna otra información. Entonces, para obtener una lista de todos los SHA-1 de
confirmación introducidos entre un SHA-1 de confirmación y otro, puede ejecutar algo como esto:

$ git rev-list 538c33..d14fc7


d14fc7c847ab946ec39590d87783c69b031bdfb7
9f585da4401b0a3999e84113824d15245c13f0be
234071a1be950e2a8d078e6141f5cd20c1e61ad3
dfa04c9ef3d5197182f13fb5b9b1fb7717d2222a
17716ec0f1ff5c77eff40b7fe912f9f6cfd0e475

Puede tomar esa salida, recorrer cada uno de esos SHA-1 confirmados, capturar el mensaje y probar ese mensaje con una expresión
regular que busca un patrón.

Debe descubrir cómo obtener el mensaje de confirmación de cada una de estas confirmaciones para probar. Para obtener el

365
Machine Translated by Google

datos de confirmación sin procesar, puede usar otro comando de plomería llamado git cat-file. Repasaremos todos estos comandos de
plomería en detalle en Git Internals; pero por ahora, esto es lo que da ese comando
usted:

$ git cat-file commit ca82a6 tree


cfda3bf379e4f8dba8717dee55aab78aef7f4daf padre
085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 autor
Scott Chacon <schacon@gmail.com> 1205815931 -0700 autor Scott
Chacon <schacon@gmail.com -07052910

Cambiar el número de versión

Una forma sencilla de obtener el mensaje de confirmación de una confirmación cuando tiene el valor SHA-1 es ir a la primera línea en
blanco y tomar todo lo que sigue. Puede hacerlo con el comando sed en los sistemas Unix:

$ git cat-file commit ca82a6 | sed '1,/^$/d'


Cambiar el número de versión

Puede usar ese conjuro para obtener el mensaje de confirmación de cada confirmación que se intenta enviar y salir si ve algo que no
coincide. Para salir de la secuencia de comandos y rechazar la inserción, salga de un valor distinto de cero. Todo el método se ve así:

$regex = /\[ref: (\d+)\]/

# formato de mensaje de compromiso


personalizado aplicado def check_message_format
miss_revs = `git rev-list #{$oldrev}..#{$newrev}`.split("\n") miss_revs.each do |
rev| mensaje = `git cat-file commit #{rev} | sed '1,/^$/d'` if !$regex.match(mensaje)
pone "[POLÍTICA] Su mensaje no tiene el formato correcto" exit 1

final
final final

comprobar_mensaje_formato

Poner eso en su secuencia de comandos de actualización rechazará las actualizaciones que contienen confirmaciones que tienen mensajes
que no se adhieren a su regla.

Aplicación de un sistema ACL basado en el usuario

Suponga que desea agregar un mecanismo que utiliza una lista de control de acceso (ACL) que especifica qué usuarios pueden enviar
cambios a qué partes de sus proyectos. Algunas personas tienen acceso completo y otras solo pueden enviar cambios a ciertos
subdirectorios o archivos específicos. Para hacer cumplir esto, escribirá esas reglas en un archivo llamado acl que se encuentra en su
repositorio Git básico en el servidor. Tendras

366
Machine Translated by Google

el gancho de actualización mira esas reglas, ve qué archivos se están introduciendo para todas las confirmaciones
que se envían y determina si el usuario que realiza el envío tiene acceso para actualizar todos esos archivos.

Lo primero que harás es escribir tu ACL. Aquí utilizará un formato muy parecido al mecanismo CVS ACL: utiliza una
serie de líneas, donde el primer campo es disponible o no disponible, el siguiente campo es una lista delimitada por
comas de los usuarios a los que se aplica la regla y el último campo es la ruta a la que se aplica la regla (en blanco
significa acceso abierto). Todos estos campos están delimitados por un carácter de barra vertical (|) .

En este caso, tiene un par de administradores, algunos escritores de documentación con acceso al directorio doc y
un desarrollador que solo tiene acceso a los directorios lib y tests , y su archivo ACL se ve así:

aproveche|nickh,pjhyett,defunkt,tpw
aproveche|usinclair,cdickens,ebronte|doc
aproveche|schacon|lib aproveche|schacon|
pruebas

Comienza leyendo estos datos en una estructura que puedes usar. En este caso, para simplificar el ejemplo, solo
aplicará las directivas de disponibilidad . Aquí hay un método que le brinda una matriz asociativa donde la clave es el
nombre de usuario y el valor es una matriz de rutas en las que el usuario ha escrito
acceso:

def get_acl_access_data(acl_file) #
leer datos ACL
''
acl_file = File.read(acl_file).split("\n").reject { |line| línea == acceso = {} }
acl_file.each hacer |línea| disponibilidad, usuarios, ruta = línea.split('|') siguiente
a menos que disponibilidad == 'disponibilidad' usuarios.split(',').each do |
usuario| acceso[usuario] ||= [] acceso[usuario] << ruta fin fin

acceso
final

En el archivo ACL que vio anteriormente, este método get_acl_access_data devuelve una estructura de datos que
se ve así:

367
Machine Translated by Google

{"defunkt"=>[nil],
"tpw"=>[nil],
"nickh"=>[nil],
"pjhyett"=>[nil],
"schacon"=>["lib", "tests" ],
"cdickens"=>["doc"],
"usinclair"=>["doc"],
"ebronte"=>["doc"]}

Ahora que tiene los permisos ordenados, debe determinar qué rutas han modificado las confirmaciones que
se están enviando, para que pueda asegurarse de que el usuario que está enviando tenga acceso a todas ellas.

Puede ver con bastante facilidad qué archivos se han modificado en una sola confirmación con la opción --
name-only del comando git log (mencionado brevemente en Git Basics):

$ git log -1 --solo nombre --pretty=formato:'' 9f585d

LÉAME
lib/prueba.rb

Si usa la estructura ACL devuelta por el método get_acl_access_data y la compara con los archivos
enumerados en cada una de las confirmaciones, puede determinar si el usuario tiene acceso para enviar todas
sus confirmaciones:

368
Machine Translated by Google

# solo permite que ciertos usuarios modifiquen ciertos subdirectorios en un proyecto


def comprobar_directorio_permisos
acceso = get_acl_access_data('acl')

# ver si alguien está tratando de empujar algo que no puede


new_commits = `git rev-list #{$oldrev}..#{$newrev}`.split("\n")
new_commits.each hacer |rev|
archivos_modificados = `git log -1 --name-only --pretty=formato:'' #{rev}`.split("\n")
files_modified.each do |ruta|
ÿ

siguiente si ruta.tamaño == 0
ÿ

tiene_archivo_acceso = falso
ÿ

acceso[$usuario].each do |access_path|
ÿ

si !access_path # el usuario tiene acceso a todo


ÿ

|| (path.start_with? access_path) # acceso a esta ruta


ÿ

tiene_archivo_acceso = verdadero
ÿ

final
ÿ

final
ÿ

si !tiene_archivo_acceso
ÿ

pone "[POLÍTICA] No tiene acceso para empujar a #{ruta}"


ÿ

salida 1
ÿ

final
final
final
final

verificar_directorio_permisos

Obtiene una lista de nuevas confirmaciones que se envían a su servidor con git rev-list. Entonces, para cada uno de esos
confirma, encuentra qué archivos se modifican y se asegura de que el usuario que está presionando tenga acceso a todos
las rutas que se modifican.

Ahora sus usuarios no pueden enviar confirmaciones con mensajes mal formados o con archivos modificados fuera
de sus caminos designados.

Probarlo

Si ejecuta chmod u+x .git/hooks/update, que es el archivo en el que debería haber puesto todo esto
código, y luego intenta enviar una confirmación con un mensaje no conforme, obtiene algo como esto:

369
Machine Translated by Google

$ git push -f origin master Contando


objetos: 5, listo.
Comprimir objetos: 100% (3/3), listo.
Objetos de escritura: 100% (3/3), 323 bytes, listo.
Total 3 (delta 1), reutilizado 0 (delta 0)
Desembalaje de objetos: 100% (3/3), hecho.
Aplicación de políticas...
(refs/heads/master) (8338c5) (c5b616)
[POLÍTICA] Su mensaje no tiene el formato correcto error:
hooks/update salieron con el código de error 1 error: hook
rechazó actualizar refs/heads/master To git@gitserver:project.git !
[remoto rechazado] maestro -> maestro (gancho rechazado)
error: no se pudo enviar algunas referencias a 'git@gitserver:project.git'

Hay un par de cosas interesantes aquí. Primero, ves esto donde el gancho comienza a correr.

Aplicación de políticas...
(refs/heads/master) (fb8c72) (c56860)

Recuerde que lo imprimió al principio de su secuencia de comandos de actualización. Cualquier cosa que su secuencia de
comandos haga eco en la salida estándar se transferirá al cliente.

Lo siguiente que notará es el mensaje de error.

[POLÍTICA] Su mensaje no tiene el formato correcto error:


ganchos/actualización salió con el código de error 1 error:
gancho rechazó actualizar refs/heads/master

Usted imprimió la primera línea, las otras dos fueron Git diciéndole que la secuencia de comandos de actualización salió de un
valor distinto de cero y eso es lo que está rechazando su impulso. Por último, tienes esto:

Para git@gitserver:project.git !
[remoto rechazado] maestro -> maestro (gancho rechazado) error:
no se pudo enviar algunas referencias a 'git@gitserver:project.git'

Verá un mensaje de rechazo remoto para cada referencia que su enlace rechazó, y le indica que se rechazó específicamente
debido a una falla en el enlace.

Además, si alguien intenta editar un archivo al que no tiene acceso y envía una confirmación que lo contiene, verá algo similar.
Por ejemplo, si un autor de documentación intenta enviar una confirmación modificando algo en el directorio lib , verá:

[POLÍTICA] No tiene acceso para empujar a lib/test.rb

370
Machine Translated by Google

De ahora en adelante, siempre que el script de actualización esté allí y sea ejecutable, su repositorio nunca tendrá
un mensaje de confirmación sin su patrón y sus usuarios estarán en un espacio aislado.

Ganchos del lado del cliente

La desventaja de este enfoque es el lloriqueo que inevitablemente resultará cuando se rechacen los envíos de
confirmación de los usuarios. Que su trabajo cuidadosamente elaborado sea rechazado en el último minuto puede
ser extremadamente frustrante y confuso; y además, tendrán que editar su historial para corregirlo, lo que no siempre
es para los débiles de corazón.

La respuesta a este dilema es proporcionar algunos ganchos del lado del cliente que los usuarios pueden ejecutar
para notificarles cuando están haciendo algo que es probable que el servidor rechace. De esa manera, pueden
corregir cualquier problema antes de comprometerse y antes de que esos problemas se vuelvan más difíciles de
solucionar. Debido a que los ganchos no se transfieren con un clon de un proyecto, debe distribuir estos scripts de
alguna otra manera y luego hacer que sus usuarios los copien en su directorio .git/hooks y hacerlos ejecutables.
Puede distribuir estos ganchos dentro del proyecto o en un proyecto separado, pero Git no los configurará
automáticamente.

Para comenzar, debe verificar su mensaje de confirmación justo antes de que se registre cada confirmación, para
que sepa que el servidor no rechazará sus cambios debido a mensajes de confirmación mal formateados. Para
hacer esto, puede agregar el enlace commit-msg . Si tiene que leer el mensaje del archivo pasado como primer
argumento y compararlo con el patrón, puede obligar a Git a cancelar la confirmación si no hay ninguna coincidencia:

#!/usr/bin/env ruby
archivo_mensaje = ARGV[0]
mensaje = Archivo.leer(archivo_mensaje)

$regex = /\[ref: (\d+)\]/

if !$regex.match(message) pone
"[POLICY] Su mensaje no tiene el formato correcto" exit 1

final

Si ese script está en su lugar (en .git/hooks/commit-msg) y es ejecutable, y confirma con un mensaje que no tiene el
formato correcto, verá esto:

$ git commit -am 'Prueba'


[POLÍTICA] Su mensaje no tiene el formato correcto

No se completó ninguna confirmación en esa instancia. Sin embargo, si su mensaje contiene el patrón adecuado,
Git le permite confirmar:

$ git commit -am 'Test [ref: 132]' [master


e05c914] Test [ref: 132] 1 archivo
modificado, 1 inserciones (+), 0 eliminaciones (-)

371
Machine Translated by Google

A continuación, desea asegurarse de que no está modificando archivos que están fuera del alcance de su ACL. Si el
directorio .git de su proyecto contiene una copia del archivo ACL que usó anteriormente, entonces el siguiente script
de confirmación previa aplicará esas restricciones por usted:

#!/usr/bin/env rubí

$usuario = ENV['USUARIO']

# [inserte el método acl_access_data desde arriba]

# solo permite que ciertos usuarios modifiquen ciertos subdirectorios en un proyecto def
check_directory_perms access = get_acl_access_data('.git/acl')

files_modified = `git diff-index --cached --name-only HEAD`.split("\n") files_modified.each do |ruta|


next if ruta.tamaño == 0 has_file_access = false acceso[$usuario].each do |access_path| si !
acceso_ruta || (ruta.index(ruta_acceso) == 0)

has_file_access = final
verdadero
if !has_file_access pone
ÿ

"[POLÍTICA] No tiene acceso para empujar a #{ruta}" exit 1


ÿ

final
final
final

verificar_directorio_permisos

Este es aproximadamente el mismo script que la parte del lado del servidor, pero con dos diferencias importantes.
Primero, el archivo ACL está en un lugar diferente, porque este script se ejecuta desde su directorio de trabajo, no
desde su directorio .git . Tienes que cambiar la ruta al archivo ACL de esto:

acceso = get_acl_access_data('acl')

a esto:

acceso = get_acl_access_data('.git/acl')

La otra diferencia importante es la forma en que obtiene una lista de los archivos que se han modificado.
Debido a que el método del lado del servidor mira el registro de confirmaciones y, en este punto, la confirmación aún
no se ha registrado, en su lugar, debe obtener su lista de archivos del área de preparación. En lugar de:

archivos_modificados = `git log -1 --name-only --pretty=formato:'' #{ref}`

372
Machine Translated by Google

tienes que usar:

files_modified = `git diff-index --cached --name-only HEAD`

Pero esas son las únicas dos diferencias; de lo contrario, el script funciona de la misma manera. Una advertencia es que
espera que se ejecute localmente como el mismo usuario que presiona en la máquina remota. Si eso es diferente, debe
configurar la variable $user manualmente.

Otra cosa que podemos hacer aquí es asegurarnos de que el usuario no inserte referencias que no sean de avance rápido.
Para obtener una referencia que no sea un avance rápido, debe volver a establecer una base más allá de una confirmación
que ya ha enviado o intentar enviar una rama local diferente a la misma rama remota.

Presumiblemente, el servidor ya está configurado con receive.denyDeletes y receive.denyNonFastForwards para hacer


cumplir esta política, por lo que lo único accidental que puede intentar detectar es reorganizar las confirmaciones que ya
se han enviado.

Aquí hay un ejemplo de secuencia de comandos previa a la reorganización que verifica eso. Obtiene una lista de todas las
confirmaciones que está a punto de reescribir y verifica si existen en alguna de sus referencias remotas. Si ve uno al que
se puede acceder desde una de sus referencias remotas, aborta la reorganización.

#!/usr/bin/env rubí

base_branch = ARGV[0]
if ARGV[1] topic_branch
= ARGV[1] else topic_branch
= "HEAD" end

target_shas = `git rev-list #{base_branch}..#{topic_branch}`.split("\n") remote_refs =


`git branch -r`.split("\n").map { |r| r.strip }

target_shas.each hacer |sha|


remote_refs.each hacer |remote_ref|
shas_push = `git rev-list ^#{sha}^@ refs/remotes/#{remote_ref}` if
shas_pushed.split("\n").include?(sha)
ÿ

pone "[POLICY] Commit #{sha} ya se ha enviado a #{remote_ref}" exit 1


ÿ

final final

final

Este script usa una sintaxis que no se trató en Selección de revisión. Obtiene una lista de confirmaciones que ya se han
subido al ejecutar esto:

`git rev-list ^#{sha}^@ refs/controles remotos/#{remote_ref}`

373
Machine Translated by Google

La sintaxis SHA^@ se resuelve en todos los padres de esa confirmación. Está buscando cualquier compromiso al que se
pueda acceder desde el último compromiso en el control remoto y que no sea accesible desde ningún padre de ninguno
de los SHA-1 que está tratando de subir, lo que significa que es un avance rápido.

El principal inconveniente de este enfoque es que puede ser muy lento y, a menudo, innecesario: si no intenta forzar la
inserción con -f, el servidor le advertirá y no aceptará la inserción. Sin embargo, es un ejercicio interesante y, en teoría,
puede ayudarlo a evitar una reorganización que luego tendrá que regresar y corregir.

Resumen
Hemos cubierto la mayoría de las principales formas en que puede personalizar su cliente y servidor Git para que se
ajuste mejor a su flujo de trabajo y proyectos. Aprendió sobre todo tipo de opciones de configuración, atributos basados
en archivos y enlaces de eventos, y creó un servidor de aplicación de políticas de ejemplo. Ahora debería poder hacer
que Git se ajuste a casi cualquier flujo de trabajo que pueda soñar.

374
Machine Translated by Google

Git y otros sistemas


El mundo no es perfecto. Por lo general, no puede cambiar inmediatamente todos los proyectos con los que entra en
contacto a Git. A veces, está atascado en un proyecto que usa otro VCS y desearía que fuera Git. Pasaremos la primera
parte de este capítulo aprendiendo sobre las formas de usar Git como cliente cuando el proyecto en el que está
trabajando está alojado en un sistema diferente.

En algún momento, es posible que desee convertir su proyecto existente a Git. La segunda parte de este capítulo cubre
cómo migrar su proyecto a Git desde varios sistemas específicos, así como un método que funcionará si no existe una
herramienta de importación preconstruida.

Git como cliente


Git brinda una experiencia tan agradable para los desarrolladores que muchas personas han descubierto cómo usarlo
en su estación de trabajo, incluso si el resto de su equipo usa un VCS completamente diferente. Hay varios de estos
adaptadores, llamados "puentes", disponibles. Aquí cubriremos los que es más probable que encuentres en la naturaleza.

Git y subversión

Una gran fracción de los proyectos de desarrollo de código abierto y una buena cantidad de proyectos corporativos
utilizan Subversion para administrar su código fuente. Ha existido durante más de una década, y durante la mayor parte
de ese tiempo fue la elección de facto de VCS para proyectos de código abierto. También es muy similar en muchos
aspectos a CVS, que antes de eso era el chico grande del mundo del control de código fuente.

Una de las grandes características de Git es un puente bidireccional a Subversion llamado git svn. Esta herramienta le
permite usar Git como un cliente válido para un servidor Subversion, por lo que puede usar todas las funciones locales
de Git y luego enviar a un servidor Subversion como si estuviera usando Subversion localmente. Esto significa que puede
hacer bifurcaciones y fusiones locales, usar el área de preparación, usar rebase y selección selectiva, etc., mientras sus
colaboradores continúan trabajando en sus formas oscuras y antiguas. Es una buena manera de colar a Git en el entorno
corporativo y ayudar a sus compañeros desarrolladores a ser más eficientes mientras presiona para que se cambie la
infraestructura para admitir completamente a Git. El puente Subversion es la droga de entrada al mundo DVCS.

git svn

El comando base en Git para todos los comandos puente de Subversion es git svn. Se requieren bastantes comandos,
por lo que mostraremos los más comunes mientras revisamos algunos flujos de trabajo simples.

Es importante tener en cuenta que cuando usa git svn, está interactuando con Subversion, que es un sistema que
funciona de manera muy diferente a Git. Si bien puede realizar bifurcaciones y fusiones locales, generalmente es mejor
mantener su historial lo más lineal posible reorganizando su trabajo y evitando hacer cosas como interactuar
simultáneamente con un repositorio remoto de Git.

No reescriba su historial e intente empujar de nuevo, y no empuje a un repositorio de Git paralelo para colaborar con
otros desarrolladores de Git al mismo tiempo. Subversion solo puede tener un único historial lineal, y confundirlo es muy
fácil. Si está trabajando con un equipo, y algunos usan SVN y otros usan Git, asegúrese de que todos estén usando el
servidor SVN para colaborar; hacerlo hará que

375
Machine Translated by Google

tu vida más fácil.

Configuración

Para demostrar esta funcionalidad, necesita un repositorio SVN típico al que tenga acceso de escritura.
Si desea copiar estos ejemplos, tendrá que hacer una copia grabable de un repositorio de prueba SVN.
Para hacerlo fácilmente, puede usar una herramienta llamada svnsync que viene con Subversion.

Para continuar, primero debe crear un nuevo repositorio local de Subversion:

$ mkdir /tmp/prueba-svn $
svnadmin create /tmp/prueba-svn

Luego, habilite a todos los usuarios para que cambien los revprops; la manera fácil es agregar un script previo al cambio de revprop
que siempre sale en 0:

$ cat /tmp/test-svn/hooks/pre-revprop-change #!/bin/sh exit


0; $ chmod +x /tmp/test-svn/hooks/pre-revprop-change

Ahora puede sincronizar este proyecto con su máquina local llamando a svnsync init con los repositorios hacia y desde.

$ archivo de inicio svnsync:///tmp/test-svn \ http://


su-servidor-svn.example.org/svn/

Esto configura las propiedades para ejecutar la sincronización. A continuación, puede clonar el código ejecutando:

$ svnsync archivo de sincronización:///tmp/test-


svn Revisión confirmada 1.
Propiedades copiadas para la revisión 1.
Transmitiendo datos de archivo .............................[...]
Revisión comprometida 2.
Propiedades copiadas para la revisión 2.
[…]

Aunque esta operación puede tardar solo unos minutos, si intenta copiar el repositorio original a otro repositorio remoto en
lugar de uno local, el proceso tardará casi una hora, aunque haya menos de 100 confirmaciones. Subversion tiene que clonar
una revisión a la vez y luego devolverla a otro repositorio; es ridículamente ineficiente, pero es la única manera fácil de hacerlo.

Empezando

Ahora que tiene un repositorio de Subversion al que tiene acceso de escritura, puede realizar un flujo de trabajo típico.
Comenzará con el comando git svn clone , que importa un Subversion completo

376
Machine Translated by Google

repositorio en un repositorio Git local. Recuerde que si está importando desde un repositorio de Subversion
alojado real, debe reemplazar el archivo:///tmp/test-svn aquí con la URL de su repositorio de Subversion:

$ git svn clone file:///tmp/test-svn -T trunk -b branch -t tags Repositorio Git vacío
inicializado en /private/tmp/progit/test-svn/.git/ r1 =
dcbfb5891860124cc2e8cc616cded42624897125 (refs/remotes/ origen/tronco)
A m4/acx_pthread.m4 A m4/
stl_hash.m4 A java/src/test/
java/com/google/protobuf/UnknownFieldSetTest.java A java/src/test/java/com/google/
protobuf/WireFormatTest.java

r75 = 556a3e1e7ad1fde0a32823fc7e4d046bcfd86dae (refs/controles remotos/origen/troncal)


Se encontró un posible punto de bifurcación: file:///tmp/test-svn/trunk => file:///tmp/test svn/
branches/my-calc-branch, 75
Padre de rama encontrado: (refs/remotes/origin/my-calc-branch)
556a3e1e7ad1fde0a32823fc7e4d046bcfd86dae
Siguiendo al padre con do_switch
Se siguió correctamente el padre
r76 = 0fb585761df569eaecd8146c71e58d70147460a2 (refs/remotes/origin/my-calc-branch)
Desprotegido HEAD:
file:///tmp/test-svn/trunk r75

Esto ejecuta el equivalente a dos comandos: git svn init seguido de git svn fetch , en la URL que proporcione.
Esto puede tomar un tiempo. Si, por ejemplo, el proyecto de prueba tiene solo alrededor de 75 confirmaciones
y el código base no es tan grande, Git debe verificar cada versión, una a la vez, y confirmarlas
individualmente. Para un proyecto con cientos o miles de confirmaciones, esto puede tardar literalmente
horas o incluso días en finalizar.

La parte -T trunk -b branch -t tags le dice a Git que este repositorio de Subversion sigue las convenciones
básicas de bifurcación y etiquetado. Si nombra su tronco, ramas o etiquetas de manera diferente, puede
cambiar estas opciones. Debido a que esto es tan común, puede reemplazar esta parte completa con -s, lo
que significa diseño estándar e implica todas esas opciones. El siguiente comando es equivalente:

$ git svn clonar archivo:///tmp/test-svn -s

En este punto, debe tener un repositorio Git válido que haya importado sus ramas y etiquetas:

$ git branch -a
* master remotes/

origin/my-calc-branch remotes/origin/
tags/2.0.2 remotes/origin/tags/
release-2.0.1 remotes/origin/tags/
release-2.0.2 remotes/origin/ tags/
release-2.0.2rc1 remotos/origen/troncal

377
Machine Translated by Google

Tenga en cuenta cómo esta herramienta gestiona las etiquetas de Subversion como referencias remotas. Echemos un vistazo más de cerca

con el comando show-ref de plomería de Git:

$ Git show-ref
árbitros bfd2d79303166789fc73af4046651a4b35c12f0b
556a3e1e7ad1fde0a32823fc7e4d046bcfd86dae refs / heads / master
0fb585761df569eaecd8146c71e58d70147460a2 árbitros / mandos a distancia / origen / my-calc-branch /
mandos a distancia / origen / etiquetas / 2.0.2 285c2b2e36e467dd4d91c8e3c0c0e1750b3fe8ca árbitros /
mandos a distancia / origen / etiquetas / versión 2.0.1-cbda99cb45d9abcb9793db1d4f70ae562a969f1e refs/
controles remotos/origin/tags/release-2.0.2 a9f074aa89e826d6f9d30808ce5ae3ffe711feda refs/remotos/origin/
tags/release-2.0.2rc1 556a3e1e7ad1fde0a32823fc7e4d046bcfd86dae refs/remotos/origen/troncal

Git no hace esto cuando clona desde un servidor Git; así es como se ve un repositorio con etiquetas después de un clon nuevo:

$ Espectáculo-ref
git c3dcbe8488c6240392e8a5d7553bbffcb0f94ef0 árbitros / mandos a distancia / origen /
árbitros 32ef1d1c7cc8c603ab78416262cc421b80a8c2df maestro / mandos a distancia / origen /
rama-1 75f703a3580a9b81ead89fe1138e6da858c5ba18 árbitros / mandos a distancia / origen /
rama-2 23f8588dde934e8f33c263c6d8359b2ae095f863 árbitros / etiquetas / v0.1.0
7064938bd5e7ef47bfd79a685a62c1e2649e2ce7 árbitros / etiquetas / v0.2.0
6dcb09b5b57875f334f61aebed695e2e4193db5e referencias/etiquetas/v1.0.0

Git obtiene las etiquetas directamente en refs/tags, en lugar de tratarlas como ramas remotas.

Volver a comprometerse con Subversion

Ahora que tiene un directorio de trabajo, puede trabajar un poco en el proyecto y enviar sus confirmaciones hacia arriba, utilizando Git de

manera efectiva como un cliente SVN. Si edita uno de los archivos y lo confirma, tiene una confirmación que existe en Git localmente que no

existe en el servidor de Subversion:

$ git commit -am 'Agregar instrucciones git-svn al LÉAME' [master 4af61fd] Agregar
instrucciones git-svn al archivo LÉAME 1 cambiado, 5 inserciones (+)

A continuación, debe impulsar su cambio en sentido ascendente. Observe cómo esto cambia la forma en que trabaja con Subversion: puede

realizar varias confirmaciones fuera de línea y luego enviarlas todas a la vez al servidor de Subversion. Para enviar a un servidor de Subversion,

ejecute el comando git svn dcommit :

378
Machine Translated by Google

$ git svn dcommit


Comprometerse con file:///tmp/test-svn/trunk ...
M README.txt
Confirmado r77 M
README.txt
r77 = 95e0222ba6399739834380eb10afcd73e0670bc5 (refs/controles remotos/origen/troncal)
Sin cambios entre 4af61fd05045e07598c553167e0f31c84fd6ffe1 y refs/remotes/origin/
trunk
Restablecer a las últimas referencias/controles remotos/origen/troncal

Esto toma todas las confirmaciones que ha realizado además del código del servidor de Subversion, realiza una
confirmación de Subversion para cada una y luego vuelve a escribir su confirmación de Git local para incluir un
identificador único. Esto es importante porque significa que todas las sumas de verificación SHA-1 para sus confirmaciones
cambian. En parte por esta razón, trabajar con versiones remotas basadas en Git de sus proyectos al mismo tiempo que
un servidor Subversion no es una buena idea. Si observa la última confirmación, puede ver el nuevo git-svn-id que se
agregó:

$ git registro -1
compromiso 95e0222ba6399739834380eb10afcd73e0670bc5
Autor: ben <ben@0b684db3-b064-4277-89d1-21af03df0a68>
Fecha: Jue 24 de julio 03:08:36 2014 +0000

Agregar instrucciones de git-svn al LÉAME

git-svn-id: archivo:///tmp/test-svn/trunk@77 0b684db3-b064-4277-89d1-21af03df0a68

Tenga en cuenta que la suma de verificación SHA-1 que originalmente comenzó con 4af61fd cuando se comprometió
ahora comienza con 95e0222. Si desea enviar tanto a un servidor Git como a un servidor Subversion, primero debe
enviar (dcommit) al servidor Subversion, porque esa acción cambia sus datos de confirmación.

Incorporando nuevos cambios

Si está trabajando con otros desarrolladores, en algún momento uno de ustedes presionará y luego el otro intentará
impulsar un cambio que entra en conflicto. Ese cambio será rechazado hasta que se fusione en su trabajo. En git svn, se
ve así:

379
Machine Translated by Google

$ git svn dcommit


Comprometerse con file:///tmp/test-svn/trunk ...

ERROR desde SVN:


Transacción no está actualizado: Archivo '/trunk/README.txt' no está actualizado W:
d5837c4b461b7c0e018b49d12398769d2bfc240a y árbitros / mandos a distancia / origen / tronco son diferentes,
usando rebase:: 100 644 100 644 f414c433af0fd6734428cf9d2a9fd8ba00ada145
c80b6127dd04f5fcda218730ddf3a2da4eb39138 M LEEME.txt

El maestro de sucursal actual está actualizado.


ERROR: no todos los cambios se han confirmado en SVN; sin embargo, los cambios confirmados (si los
hay) parecen estar integrados correctamente en el árbol de trabajo.
Consulte los mensajes anteriores para obtener más detalles.

Para resolver esta situación, puede ejecutar git svn rebase, que extrae cualquier cambio en el servidor que aún no tiene y
vuelve a basar cualquier trabajo que tenga sobre lo que está en el servidor:

$ git svn rebase


Comprometerse con file:///tmp/test-svn/trunk ...

ERROR desde SVN:


Transacción no está actualizado: Archivo '/trunk/README.txt' no está actualizado W:
eaa029d99f87c5c822c5c29039d19111ff32ef46 y árbitros / mandos a distancia / origen / tronco diferentes, usando
rebase:: 100 644 100 644 65536c6e30d263495c17d781962cfff12422693a b34372b25ccf4945fe5658fa381b075045e7702a
M LEEME.txt En primer lugar, cabeza rebobinadora para reproducir su trabajo encima de ella...

Aplicando: actualizar foo


Usando información de índice para reconstruir un árbol base...
M README.txt

Volviendo a la base de parches y fusión de 3 vías...


ERROR de fusión automática de
README.txt: no todos los cambios se han confirmado en SVN, sin embargo, los cambios confirmados (si
los hay) parecen estar integrados correctamente en el árbol de trabajo.
Consulte los mensajes anteriores para obtener más detalles.

Ahora, todo su trabajo está por encima de lo que está en el servidor de Subversion, por lo que puede dcommit con éxito:

$ git svn dcommit


Comprometerse con file:///tmp/test-svn/trunk ...
M LÉAME.txt
Comprometido r85
M README.txt r85 =

9c29704cc0bbbed7bd58160cfb66cb9191835cd8 (refs/controles remotos/origen/troncal)


Sin cambios entre 5762f56732a958d6cfda681b661d2a239cc53ef5 y refs/remotes/origin/
trunk Restablecer a los últimos refs/remotes/origin/trunk

380
Machine Translated by Google

Tenga en cuenta que, a diferencia de Git, que requiere que fusione el trabajo ascendente que aún no tiene
localmente antes de poder empujar, git svn lo obliga a hacerlo solo si los cambios entran en conflicto (muy
parecido a cómo funciona Subversion). Si alguien más envía un cambio a un archivo y luego usted envía un
cambio a otro archivo, su dcommit funcionará bien:

$ git svn dcommit


Comprometerse con file:///tmp/test-svn/trunk ...
M configure.ac Confirmado
r87 M autogen.sh r86 =

d8450bab8a77228a644b7dc0e95977ffc61adff7 (refs/remotes/origin/trunk)
M configure.ac r87 =
f3653ea40cb4e26b6281cec102e35dcba1fe17c4 (refs/controles remotos/origen/troncal)
W: a0253d06732169107aa020390d9fefd2b1d92806 y árbitros / mandos a distancia / origen / tronco son diferentes,
usando rebase:: 100755 100755 efa5a59965fbbb5b2b0a12890f1b351bb5493c18
e757b59a9439312d80d5d43bb65d4a7d0389ed6d M autogen.sh En primer lugar, la cabeza de rebobinado para

volver a reproducir su trabajo en la parte superior de la misma ...

Es importante recordar esto, porque el resultado es un estado de proyecto que no existía en ninguna de sus
computadoras cuando presionó. Si los cambios son incompatibles pero no entran en conflicto, es posible que
tenga problemas que sean difíciles de diagnosticar. Esto es diferente a usar un servidor Git: en Git, puede probar
completamente el estado en su sistema cliente antes de publicarlo, mientras que en SVN, nunca puede estar
seguro de que los estados inmediatamente antes y después de la confirmación sean idénticos.

También debe ejecutar este comando para obtener cambios del servidor de Subversion, incluso si no está listo
para comprometerse. Puede ejecutar git svn fetch para obtener los nuevos datos, pero git svn rebase realiza la
búsqueda y luego actualiza sus confirmaciones locales.

$ git svn rebase M


autogen.sh r88 =
c9c5f83c64bd755368784b444bc7a0216cc1e17b (refs/controles remotos/origen/troncal)
Primero, rebobinar la cabeza para reproducir su trabajo encima de ella...
Maestro de avance rápido a refs/remotes/origin/trunk.

Ejecutar git svn rebase de vez en cuando asegura que su código esté siempre actualizado. Sin embargo, debe
asegurarse de que su directorio de trabajo esté limpio cuando ejecute esto. Si tiene cambios locales, debe ocultar
su trabajo o confirmarlo temporalmente antes de ejecutar git svn rebase ; de lo contrario, el comando se detendrá
si ve que el rebase dará como resultado un conflicto de fusión.

Problemas de bifurcación de Git

Cuando se sienta cómodo con un flujo de trabajo de Git, es probable que cree ramas de temas, trabaje en ellas y
luego las combine . en una sola rama cada vez en lugar de fusionar ramas. La razón para preferir el cambio de
base es que Subversion tiene un historial lineal y no se ocupa de las fusiones como lo hace Git, por lo que git svn
sigue solo al primer padre cuando convierte las instantáneas en confirmaciones de Subversion.

381
Machine Translated by Google

Suponga que su historial se parece a lo siguiente: creó una rama de experimento , realizó dos confirmaciones y luego
las fusionó nuevamente en el maestro. Cuando dcommit, verá un resultado como este:

$ git svn dcommit


Comprometerse con file:///tmp/test-svn/trunk ...
M CAMBIOS.txt
Confirmado r89
M CHANGES.txt r89 =
89d492c884ea7c834353563d5d913c6adf933981 (refs/controles remotos/origen/troncal)
M COPIANDO.txt M
INSTALANDO.txt
Comprometido r90
M INSTALACIÓN.txt M
COPIANDO.txt
r90 = cb522197870e61467473391799148f6721bcf9a0 (refs/controles remotos/origen/troncal)
No hay cambios entre 71af502c214ba13123992338569f4669877f55fd y refs/
remotes/origin/trunk
Restablecer a las últimas referencias/controles remotos/origen/troncal

Ejecutar dcommit en una rama con historial fusionado funciona bien, excepto que cuando observa el historial de su
proyecto Git, no ha reescrito ninguna de las confirmaciones que realizó en la rama del experimento ; en cambio,
todos esos cambios aparecen en la versión SVN del confirmación de fusión única.

Cuando alguien más clona ese trabajo, todo lo que ven es la confirmación de combinación con todo el trabajo
comprimido en él, como si hubieras ejecutado git merge --squash; no ven los datos de confirmación sobre de dónde
provienen o cuándo se confirmaron.

Ramificación de subversión

La bifurcación en Subversion no es lo mismo que la bifurcación en Git; si puede evitar usarlo mucho, probablemente
sea lo mejor. Sin embargo, puede crear y confirmar sucursales en Subversion usando git svn.

Creación de una nueva rama SVN

Para crear una nueva rama en Subversion, ejecute git svn branch [nueva-rama]:

$ git svn branch opera


Copiando archivo:///tmp/test-svn/trunk en r90 a file:///tmp/test-svn/branches/opera...
Punto de bifurcación posible encontrado: file:///tmp/test-svn/trunk => file:///tmp/test svn/
branches/opera, 90 Padre de rama encontrado: (refs/remotes/origin/opera)
cb522197870e61467473391799148f6721bcf9a0 Padre siguiente con do_switch Se siguió con
éxito el padre r91 = f1b64a3855d3c8dd84ee0ef10fa89d27f1584302 (refs/remotes/origin/opera)

Esto hace el equivalente al comando svn copy trunk branch/opera en Subversion y opera en el servidor de Subversion.
Es importante tener en cuenta que no te registra en ese

382
Machine Translated by Google

rama; si confirma en este punto, ese compromiso irá al tronco en el servidor, no a Opera.

Cambio de ramas activas

Git determina a qué rama van sus dcommits buscando la punta de cualquiera de sus ramas de Subversion en su
historial; debe tener solo una, y debe ser la última con un git-svn-id en su historial de ramas actual.

Si desea trabajar en más de una rama simultáneamente, puede configurar ramas locales para que se descommit a
ramas específicas de Subversion iniciándolas en la confirmación de Subversion importada para esa rama. Si desea
una sucursal de ópera en la que pueda trabajar por separado, puede ejecutar:

$ git branch opera remotos/origen/opera

Ahora, si desea fusionar su rama Opera en Trunk (su rama maestra ), puede hacerlo con una fusión normal de git.
Pero debe proporcionar un mensaje de confirmación descriptivo (a través de -m), o la fusión dirá "Fusionar rama
opera" en lugar de algo útil.

Recuerde que aunque está utilizando git merge para realizar esta operación, y la combinación probablemente será
mucho más fácil de lo que sería en Subversion (porque Git detectará automáticamente la base de combinación
adecuada para usted), esta no es una combinación normal de Git cometer. Tiene que devolver estos datos a un
servidor de Subversion que no pueda manejar una confirmación que rastree más de un padre; entonces, después
de subirlo, se verá como una sola confirmación que aplastó todo el trabajo de otra rama bajo una sola confirmación.
Después de fusionar una rama con otra, no puede regresar fácilmente y continuar trabajando en esa rama, como
normalmente puede hacerlo en Git. El comando dcommit que ejecuta borra cualquier información que diga en qué
rama se fusionó, por lo que los cálculos posteriores de la base de fusión serán incorrectos: el dcommit hace que su
resultado de git merge parezca que ejecutó git merge --squash.
Desafortunadamente, no hay una buena manera de evitar esta situación: Subversion no puede almacenar esta
información, por lo que siempre estará paralizado por sus limitaciones mientras lo usa como su servidor. Para evitar
problemas, debe eliminar la sucursal local (en este caso, Opera) después de fusionarla con la troncal.

Comandos de subversión

El conjunto de herramientas git svn proporciona una serie de comandos para ayudar a facilitar la transición a Git al
proporcionar una funcionalidad similar a la que tenía en Subversion. Aquí hay algunos comandos que le brindan lo
que solía hacer Subversion.

Historial de estilo SVN

Si está acostumbrado a Subversion y desea ver su historial en estilo de salida SVN, puede ejecutar git svn log para
ver su historial de confirmaciones en formato SVN:

383
Machine Translated by Google

$ git registro svn


-------------------------------------------------- ----------------------

r87 | chacón | 2014-05-02 16:07:37 -0700 (sábado, 02 de mayo de 2014) | 2 lineas

cambio autógeno

-------------------------------------------------- ----------------------

r86 | chacón | 2014-05-02 16:00:21 -0700 (sábado, 02 de mayo de 2014) | 2 lineas

Fusionar rama 'experimento'

-------------------------------------------------- ----------------------

r85 | chacón | 2014-05-02 16:00:09 -0700 (sábado, 02 de mayo de 2014) | 2 lineas

actualizado el registro de cambios

Debe saber dos cosas importantes sobre git svn log. En primer lugar, funciona sin conexión, a diferencia del
comando real svn log , que solicita los datos al servidor de Subversion. En segundo lugar, solo muestra las
confirmaciones que se han confirmado en el servidor de Subversion. Las confirmaciones locales de Git que no
ha cancelado no aparecen; tampoco las confirmaciones que las personas hayan realizado en el servidor de
Subversion mientras tanto. Es más como el último estado conocido de las confirmaciones en el servidor de Subversion.

Anotación SVN

Al igual que el comando git svn log simula el comando svn log fuera de línea, puede obtener el equivalente de
svn annotate ejecutando git svn reproche [ARCHIVO]. La salida se ve así:

$ git svn reproche README.txt


2 temporal Búferes de protocolo: formato de intercambio de datos de Google 2
temporal Copyright 2008 Google Inc. 2 temporal http://code.google.com/apis/
protocolbuffers/ 2 temporal 22 temporal Instalación de C++ - Unix 22 temporal =
====================== 2 schacon temporales Comprometiéndose en git-svn.
79 78 schacon 2 temporal Para compilar e instalar el tiempo de ejecución del búfer
del protocolo C++ y el compilador del búfer temporal del protocolo 2 (protoc),
ejecute lo siguiente: 2 temporal

Nuevamente, no muestra las confirmaciones que realizó localmente en Git o que se han enviado a Subversion
mientras tanto.

Información del servidor SVN

También puede obtener el mismo tipo de información que le brinda svn info ejecutando git svn info:

384
Machine Translated by Google

$ git svn info Ruta: .

URL: https://schacon-test.googlecode.com/svn/trunk Raíz del


repositorio: https://schacon-test.googlecode.com/svn UUID del repositorio:
4c93b258-373f-11de-be05-5f7a86268029 Revisión: 87

Tipo de nodo: directorio


Horario: normal
Autor modificado por última vez: schacon
Última modificación Rev: 87
Fecha de última modificación: 2009-05-02 16:07:37 -0700 (sábado, 02 de mayo de 2009)

Esto es como culpar e iniciar sesión , ya que se ejecuta sin conexión y está actualizado solo desde la última vez que se comunicó
con el servidor de Subversion.

Ignorando lo que Subversion ignora

Si clona un repositorio de Subversion que tiene las propiedades svn:ignore configuradas en cualquier lugar, probablemente querrá
configurar los archivos .gitignore correspondientes para no enviar accidentalmente archivos que no debería. git svn tiene dos
comandos para ayudar con este problema. El primero es git svn create-ignore, que crea automáticamente los archivos .gitignore
correspondientes para que su próxima confirmación pueda incluirlos.

El segundo comando es git svn show-ignore, que imprime en la salida estándar las líneas que necesita colocar en un
archivo .gitignore para que pueda redirigir la salida al archivo de exclusión de su proyecto:

$ git svn mostrar-ignorar > .git/info/exclude

De esa manera, no ensucia el proyecto con archivos .gitignore . Esta es una buena opción si es el único usuario de Git en un
equipo de Subversion y sus compañeros de equipo no quieren archivos .gitignore en el proyecto.

Resumen de Git-Svn

Las herramientas de git svn son útiles si está atascado con un servidor Subversion o si se encuentra en un entorno de desarrollo
que requiere ejecutar un servidor Subversion. Sin embargo, debe considerarlo paralizado Git, o encontrará problemas en la
traducción que pueden confundirlo a usted y a sus colaboradores. Para no meterse en problemas, trate de seguir estas pautas:

• Mantenga un historial de Git lineal que no contenga confirmaciones de combinación realizadas por git merge. Rebase cualquier
el trabajo que realiza fuera de su sucursal principal de vuelta a ella; no lo fusiones.

• No configure ni colabore en un servidor Git separado. Posiblemente tenga uno para acelerar los clones para los nuevos
desarrolladores, pero no presione nada que no tenga una entrada git-svn-id . Incluso es posible que desee agregar un gancho
de pre-recepción que verifique cada mensaje de confirmación en busca de un git-svn-id y rechace las inserciones que
contienen confirmaciones sin él.

Si sigue esas pautas, trabajar con un servidor Subversion puede ser más llevadero. Sin embargo, si es posible migrar a un servidor
Git real, hacerlo puede beneficiar mucho más a su equipo.

385
Machine Translated by Google

Git y Mercurial

El universo DVCS es más grande que solo Git. De hecho, hay muchos otros sistemas en este espacio, cada uno con su propio
punto de vista sobre cómo realizar correctamente el control de versiones distribuidas. Aparte de Git, el más popular es Mercurial, y
los dos son muy similares en muchos aspectos.

La buena noticia, si prefiere el comportamiento del lado del cliente de Git pero está trabajando con un proyecto cuyo código fuente
se controla con Mercurial, es que hay una manera de usar Git como cliente para un repositorio alojado en Mercurial. Dado que la
forma en que Git se comunica con los repositorios del servidor es a través de controles remotos, no debería sorprender que este
puente se implemente como un asistente remoto. El nombre del proyecto es git-remote-hg y se puede encontrar en https://
github.com/felipec/git-remote-hg.

git-remote-hg

Primero, debe instalar git-remote-hg. Básicamente, esto implica dejar caer su archivo en algún lugar de su ruta, así:

$ curl -o ~/bin/git-remote-hg \
https://raw.githubusercontent.com/felipec/git-remote-hg/master/git-remote-hg $
chmod +x ~/bin/git-remote -hg

…asumiendo que ~/bin está en su $PATH. Git-remote-hg tiene otra dependencia: la biblioteca mercurial para Python. Si tiene
Python instalado, esto es tan simple como:

$ pip instalar mercurial

Si no tiene Python instalado, visite https://www.python.org/ y consíguelo primero.

Lo último que necesitará es el cliente Mercurial. Vaya a https://www.mercurial-scm.org/ e instálalo si aún no lo has hecho.

Ahora estás listo para rockear. Todo lo que necesita es un repositorio de Mercurial al que pueda ingresar. Afortunadamente, todos
los repositorios de Mercurial pueden actuar de esta manera, así que solo usaremos el repositorio "hola mundo" que todos usan
para aprender Mercurial:

$ hg clon http://selenic.com/repo/hello /tmp/hello

Empezando

Ahora que tenemos un repositorio adecuado del "lado del servidor", podemos pasar por un flujo de trabajo típico. Como verá, estos
dos sistemas son lo suficientemente similares como para que no haya mucha fricción.

Como siempre con Git, primero clonamos:

386
Machine Translated by Google

$ git clone hg::/tmp/hello /tmp/hello-git $ cd /


tmp/hello-git $ git log --oneline --graph --
decorate * ac7955c (HEAD, origin/master,
origin/branches/default , origin/HEAD, refs/hg/origin/branches/default, refs/
hg/origin/bookmarks/master, master) Crear un archivo MAKE * 65bb417 Crear un
programa estándar 'hola, mundo'

Notará que trabajar con un repositorio de Mercurial usa el comando estándar de clonación de git . Esto se debe a que git-
remote-hg está funcionando a un nivel bastante bajo, usando un mecanismo similar a cómo se implementa el protocolo HTTP/
S de Git (ayudantes remotos). Dado que tanto Git como Mercurial están diseñados para que cada cliente tenga una copia
completa del historial del repositorio, este comando crea una copia completa, que incluye todo el historial del proyecto, y lo
hace con bastante rapidez.

El comando log muestra dos confirmaciones, la última de las cuales es señalada por una gran cantidad de referencias. Resulta
que algunos de estos no están realmente allí. Echemos un vistazo a lo que realmente hay en el directorio .git :

$ árbol. ÿÿ notas
ÿ ÿÿÿ hg ÿÿÿ
controles
remotos ÿ ÿÿÿ
origen ÿ ÿÿÿ
etiquetas HEAD

ÿÿÿ

9 directorios, 5 archivos

Git-remote-hg está tratando de hacer las cosas más idiomáticamente al estilo de Git, pero bajo el capó está administrando el
mapeo conceptual entre dos sistemas ligeramente diferentes. El directorio refs/hg es donde se almacenan las referencias
remotas reales. Por ejemplo, refs/hg/origin/branches/default es un archivo de referencia de Git que contiene el SHA-1 que
comienza con "ac7955c", que es la confirmación a la que apunta el maestro . Entonces, el directorio refs/hg es como un falso
refs/remotes/origin, pero tiene la distinción adicional entre marcadores y ramas.

El archivo notes/hg es el punto de partida de cómo git-remote-hg asigna hashes de confirmación de Git a ID de conjuntos de
cambios de Mercurial. Exploremos un poco:

387
Machine Translated by Google

$ gato billetes/
hg d4c10386...

$ git archivo-gato -p d4c10386...


árbol 1781c96...
autor remoto-hg <> 1408066400 -0800
autor remoto-hg <> 1408066400 -0800

notas para el maestro

$ git ls-tree 1781c96...


100644 blob ac9117f... 65bb417...
100644 blob 485e178... ac7955c...

$ git archivo-gato -p ac9117f


0a04b987be5ae354b710cefeba0e2d9de7ad41a9

Entonces , refs/notes/hg apunta a un árbol, que en la base de datos de objetos de Git es una lista de otros objetos con nombres. git ls-tree
genera el modo, el tipo, el hash del objeto y el nombre de archivo de los elementos dentro de un árbol. Una vez que profundizamos en uno
de los elementos del árbol, encontramos que dentro hay un blob llamado "ac9117f" (el hash SHA-1 de la confirmación apuntada por el
maestro), con contenido "0a04b98" (que es la ID del Conjunto de cambios de Mercurial en la punta de la rama predeterminada ).

La buena noticia es que, en general, no tenemos que preocuparnos por todo esto. El flujo de trabajo típico no será muy diferente de trabajar
con un control remoto de Git.

Hay una cosa más a la que debemos prestar atención antes de continuar: ignora. Mercurial y Git usan un mecanismo muy similar para
esto, pero es probable que no desee enviar un archivo .gitignore a un repositorio de Mercurial. Afortunadamente, Git tiene una forma de
ignorar archivos que son locales en un repositorio en disco, y el formato Mercurial es compatible con Git, por lo que solo tiene que copiarlo:

$ cp .hgignore .git/info/exclude

El archivo .git/info/exclude actúa como un .gitignore, pero no está incluido en las confirmaciones.

flujo de trabajo

Supongamos que hemos trabajado y realizado algunas confirmaciones en la rama maestra , y está listo para enviarlo al repositorio remoto.
Así es como se ve nuestro repositorio en este momento:

$ git log --oneline --graph --decorate * ba04a2a


(HEAD, master) Actualizar makefile * d25d16f
Adiós * ac7955c (origen/master, origin/branches/
default, origin/HEAD, refs/hg/origin/branches/default , refs/hg/origin/
bookmarks/master) Crear un makefile * 65bb417 Crear un programa estándar 'hola, mundo'

388
Machine Translated by Google

Nuestra rama maestra está dos confirmaciones por delante de origen/maestro, pero esas dos confirmaciones solo existen
en nuestra máquina local. Veamos si alguien más ha estado haciendo un trabajo importante al mismo tiempo:

$ git fetch
From hg::/tmp/hello
ac7955c..df85e87 maestro -> origen/maestro
ac7955c..df85e87 sucursales/predeterminado -> origen/sucursales/
predeterminado $ git log --oneline --graph --decorate --all * 7b07969 (refs/
notas/hg) Notas por defecto * d4c1038 Notas por maestro * df85e87
(origen/maestro, origen/sucursales/predeterminado, origen/HEAD, refs/hg/
origen/sucursales/predeterminado, refs/hg/origen/ marcadores/maestro)
Añadir algo de documentación | * ba04a2a (HEAD, maestro) Actualizar makefile | *
d25d16f Adiós |/ * ac7955c Crear un archivo MAKE * 65bb417 Crear un programa
estándar 'hola, mundo'

Como usamos el indicador --all , vemos las referencias de "notas" que git-remote-hg usa internamente, pero podemos
ignorarlas. El resto es lo que esperábamos; origin/master ha avanzado una confirmación y nuestro historial ahora se ha
separado. A diferencia de los otros sistemas con los que trabajamos en este capítulo, Mercurial es capaz de manejar
fusiones, por lo que no vamos a hacer nada complicado.

$ git merge origin/master


Fusión automática hello.c
Fusión realizada por la estrategia 'recursiva'.
hola.c | 2 +- 1 archivo cambiado, 1 inserción
(+), 1 eliminación (-) $ git log --oneline --graph --
decorate * 0c64627 (HEAD, maestro) Fusionar rama
de seguimiento remoto 'origen/maestro' |\ | * df85e87 (origin/master, origin/
branches/default, origin/HEAD, refs/hg/origin/branches/default, refs/hg/origin/
bookmarks/master) Agregar algo de documentación * | ba04a2a Actualizar
archivo MAKE * | d25d16f Adiós |/ * ac7955c Crear un archivo MAKE * 65bb417 Crear
un programa estándar 'hola, mundo'

Perfecto. Realizamos las pruebas y todo pasa, así que estamos listos para compartir nuestro trabajo con el resto del equipo:

$ git push
To hg::/tmp/hello
df85e87..0c64627 maestro -> maestro

389
Machine Translated by Google

¡Eso es todo! Si echa un vistazo al repositorio de Mercurial, verá que esto hizo lo que esperábamos:

$ hg log -G --estilo compacto


o 5[consejo]:4,2 dc8fa4f932b8 2014-08-14 19:33 -0700 ben
|\ | Combinar rama de seguimiento remoto 'origen/maestro'
|

| o 4 64f27bcefc35 2014-08-14 19:27 -0700 ben


Actualizar archivo MAKE
||

| | | o 3:1 4256fc29598f 2014-08-14 19:27 -0700 ben


| Adiós
||

| @ | 2 7db0b4848b3c 2014-08-14 19:30 -0700 ben


|/ Agregar algo de documentación

|
o 1 82e55d328c8c 2005-08-26 01:21 -0700mpm
Crear un archivo MAKE
||

o 0 0a04b987be5a 2005-08-26 01:20 -0700mpm


ÿ

Crear un programa estándar de 'hola, mundo'

El conjunto de cambios con el número 2 fue realizado por Mercurial, y los conjuntos de cambios con el número 3 y 4 fueron
hecho por git-remote-hg, empujando confirmaciones hechas con Git.

Ramas y marcadores

Git tiene solo un tipo de rama: una referencia que se mueve cuando se realizan confirmaciones. En Mercurial, este
tipo de referencia se llama "marcador" y se comporta de la misma manera que una rama de Git.

El concepto de Mercurial de una "sucursal" es más pesado. La rama en la que se realiza un conjunto de cambios es
registrado con el conjunto de cambios, lo que significa que siempre estará en el historial del repositorio. Aquí hay un
ejemplo de una confirmación que se realizó en la rama de desarrollo :

$ hg registro -l 1
conjunto de cambios: 6:8f65e5e02793
rama: desarrollar
etiqueta: propina

usuario: Ben Straub <ben@straub.cc>


fecha: jue 14 de agosto 20:06:38 2014 -0700
resumen: Más documentación

Tenga en cuenta la línea que comienza con "rama". Git realmente no puede replicar esto (y no necesita hacerlo; ambos
tipos de rama se pueden representar como una referencia de Git), pero git-remote-hg necesita comprender el
diferencia, porque a Mercurial le importa.

Crear marcadores de Mercurial es tan fácil como crear ramas de Git. En el lado de Git:

390
Machine Translated by Google

$ git checkout -b funciónA


Cambiado a una nueva rama 'featureA'
$ git push origen funciónA
Para hg::/tmp/hola
* [nueva sucursal] característicaA -> característicaA

Eso es todo al respecto. En el lado de Mercurial, se ve así:

$ hg marcadores
característicaA 5:bd5ac26f11f9
$ hg registro --estilo compacto -G
@ 6[consejo] 8f65e5e02793 2014-08-14 20:06 -0700 ben
| Más documentación
|
o 5[característicaA]:4,2 bd5ac26f11f9 2014-08-14 20:02 -0700 ben
|\ | Combinar rama de seguimiento remoto 'origen/maestro'
|

| o 4 0434aaa6b91f 2014-08-14 20:01 -0700 ben


| actualizar el archivo MAKE

||

| | o 3:1 318914536c86 2014-08-14 20:00 -0700 ben


| adiós
||

| o | 2 f098c7f45c4f 2014-08-14 20:01 -0700 ben


|/ Agregar algo de documentación

|
o 1 82e55d328c8c 2005-08-26 01:21 -0700mpm
Crear un archivo MAKE
||

o 0 0a04b987be5a 2005-08-26 01:20 -0700mpm


ÿ

Crear un programa estándar de 'hola, mundo'

Tenga en cuenta la nueva etiqueta [featureA] en la revisión 5. Estos actúan exactamente como las ramas de Git en el lado de Git, con
una excepción: no puede eliminar un marcador del lado de Git (esta es una limitación de los ayudantes remotos).

También puede trabajar en una rama Mercurial de "peso pesado": simplemente coloque una rama en las ramas
espacio de nombres:

$ git checkout -b sucursales/permanente


Cambiado a una nueva sucursal 'sucursales/permanente'
$ vi Makefile
$ git commit -am 'Un cambio permanente'
$ git push origen ramas/permanente
Para hg::/tmp/hola
* [nueva sucursal] sucursales/permanente -> sucursales/permanente

Esto es lo que parece en el lado de Mercurial:

391
Machine Translated by Google

$ hg sucursales
desarrollo 7:a4529d07aad4
permanente 6:8f65e5e02793
predeterminado 5:bd5ac26f11f9 (inactivo)
$ hg log -G
o conjunto de cambios: 7:a4529d07aad4
| rama: permanente
| etiqueta: consejo

| padre: 5:bd5ac26f11f9
| usuario: Ben Straub <ben@straub.cc>
| fecha: | jue 14 de agosto 20:21:09 2014 -0700
resumen: | Un cambio permanente

| @ conjunto de cambios: 6: 8f65e5e02793


|/ rama: desarrollar
usuario:<ben@straub.cc>
| Ben Straub
| jue 14fecha:
de agosto 20:06:38 2014 -0700
| Más documentación
resumen:
|
o conjunto de cambios: 5:bd5ac26f11f9
|\ marcador: funciónA
| | padre: | | 4:0434aaa6b91f
padre:
usuario:
|| || 2:f098c7f45c4f
fecha:
resumen:
|| [...] Ben Straub <ben@straub.cc>
jue 14 ago 20:02:21 2014 -0700
Combinar rama de seguimiento remoto 'origen/maestro'

El nombre de la rama "permanente" se registró con el conjunto de cambios marcado como 7.

Desde el lado de Git, trabajar con cualquiera de estos estilos de rama es lo mismo: solo paga, confirma,
buscar, fusionar, tirar y empujar como lo haría normalmente. Una cosa que debes saber es que Mercurial
no admite reescribir el historial, solo agregarlo. Así es como se ve nuestro repositorio de Mercurial
después de un rebase interactivo y un empuje forzado:

392
Machine Translated by Google

$ hg registro --estilo compacto -G


o 10[consejo] 99611176cbc9 2014-08-14 20:21 -0700 ben
| Un cambio permanente
|
o 9 f23e12f939c3 2014-08-14 20:01 -0700 ben
Agregar algo de documentación

||

o 8:1 c16971d33922 2014-08-14 20:00 -0700 ben


adiós
||

| o 7:5 a4529d07aad4 2014-08-14 20:21 -0700 ben


|||| Un cambio permanente

| | @ 6 8f65e5e02793 2014-08-14 20:06 -0700 ben


| |/ | | Más documentación

|o 5[característicaA]:4,2 bd5ac26f11f9 2014-08-14 20:02 -0700 ben


| |\ | | Combinar rama de seguimiento remoto 'origen/maestro'
|
| | o 4 0434aaa6b91f 2014-08-14 20:01 -0700 ben
| | actualizar el archivo MAKE

|| ||

+---o 3:1 318914536c86 2014-08-14 20:00 -0700 ben


| adiós
||

| | o 2 f098c7f45c4f 2014-08-14 20:01 -0700 ben


|/ Agregar algo de documentación

|
o 1 82e55d328c8c 2005-08-26 01:21 -0700mpm
Crear un archivo MAKE
||

o 0 0a04b987be5a 2005-08-26 01:20 -0700mpm


ÿ

Crear un programa estándar "hola, mundo"

Se han creado los conjuntos de cambios 8, 9 y 10 y pertenecen a la rama permanente , pero el antiguo
los conjuntos de cambios todavía están allí. Esto puede ser muy confuso para sus compañeros de equipo que usan Mercurial,
así que trata de evitarlo.

Resumen Mercurial

Git y Mercurial son lo suficientemente similares como para que trabajar a través del límite sea bastante sencillo. Si usted
evite cambiar el historial que ha quedado en su máquina (como generalmente se recomienda), es posible que ni siquiera
tenga en cuenta que el otro extremo es Mercurial.

Git y Bazar

Entre los DVCS, otro famoso es Bazaar. Bazaar es gratuito y de código abierto, y forma parte de la
Proyecto GNU. Se comporta de manera muy diferente a Git. A veces, para hacer lo mismo que con Git,
tiene que usar una palabra clave diferente, y algunas palabras clave que son comunes no tienen el mismo

393
Machine Translated by Google

sentido. En particular, la gestión de sucursales es muy diferente y puede causar confusión, especialmente cuando alguien
viene del universo de Git. Sin embargo, es posible trabajar en un repositorio de Bazaar desde uno de Git.

Hay muchos proyectos que te permiten usar Git como cliente de Bazaar. Aquí usaremos el proyecto de Felipe Contreras
que puedes encontrar en https://github.com/felipec/git-remote-bzr. Para instalarlo solo debes descargar el archivo git-
remote-bzr en una carpeta contenida en tu $PATH:

$ wget https://raw.github.com/felipec/git-remote-bzr/master/git-remote-bzr -O ~/bin/


git-remote-bzr $ chmod +x ~/bin/git-remote- bzr

También necesita tener Bazaar instalado. ¡Eso es todo!

Crear un repositorio de Git a partir de un repositorio de Bazaar

Es simple de usar. Basta con clonar un repositorio de Bazaar con el prefijo bzr::. Dado que tanto Git como Bazaar realizan
clones completos en su máquina, es posible adjuntar un clon de Git a su clon local de Bazaar, pero no se recomienda.
Es mucho más fácil adjuntar su clon de Git directamente al mismo lugar al que se adjunta su clon de Bazaar: el repositorio
central.

Supongamos que trabajó con un repositorio remoto que se encuentra en la dirección bzr+ssh://
developer@mybazaarserver:myproject. Luego debes clonarlo de la siguiente manera:

$ git clone bzr::bzr+ssh://developer@mybazaarserver:myproject myProject-Git $


cd myProject-Git

En este punto, se crea su repositorio Git pero no se compacta para un uso óptimo del disco. Por eso también deberías
limpiar y compactar tu repositorio Git, especialmente si es grande:

$ git gc --agresivo

Sucursales de bazar

Bazaar solo te permite clonar ramas, pero un repositorio puede contener varias ramas y git remote-bzr puede clonar
ambas. Por ejemplo, para clonar una rama:

$ git clon bzr::bzr://bzr.savannah.gnu.org/emacs/trunk emacs-trunk

Y para clonar todo el repositorio:

$ git clon bzr::bzr://bzr.savannah.gnu.org/emacs emacs

El segundo comando clona todas las ramas contenidas en el repositorio de emacs; sin embargo, es

394
Machine Translated by Google

posible señalar algunas ramas:

$ git config remote-bzr.branches 'tronco, xwindow'

Algunos repositorios remotos no te permiten listar sus ramas, en cuyo caso tienes que especificarlas manualmente,
y aunque podrías especificar la configuración en el comando de clonación, puede que te resulte más fácil:

$ git init emacs


$ git remote add origin bzr::bzr://bzr.savannah.gnu.org/emacs $
git config remote-bzr.branches 'trunk, xwindow' $ git fetch

Ignorar lo que se ignora con .bzrigore

Dado que está trabajando en un proyecto administrado con Bazaar, no debe crear un archivo .gitignore porque puede
configurarlo accidentalmente bajo el control de versiones y molestar a las otras personas que trabajan con Bazaar.
La solución es crear el archivo .git/info/exclude ya sea como un enlace simbólico o como un archivo normal. Más
adelante veremos cómo resolver esta cuestión.

Bazaar usa el mismo modelo que Git para ignorar archivos, pero también tiene dos características que no tienen un
equivalente en Git. La descripción completa se puede encontrar en la documentación. las dos caracteristicas
están:

1. "!!" le permite ignorar ciertos patrones de archivo incluso si se especifican con un "!" regla.

2. "RE:" al comienzo de una línea le permite especificar una expresión regular de Python (Solo Git
permite globos de concha).

En consecuencia, hay dos situaciones diferentes a considerar:

1. Si el archivo .bzrigore no contiene ninguno de estos dos prefijos específicos, simplemente puede
haga un enlace simbólico a él en el repositorio: ln -s .bzrignore .git/info/exclude.

2. De lo contrario, debe crear el archivo .git/info/exclude y adaptarlo para ignorar exactamente el mismo
archivos en .bzrigore.

Cualquiera que sea el caso, deberá permanecer atento a cualquier cambio de .bzrignore para asegurarse de que el
archivo .git/info/exclude siempre refleje .bzrignore. De hecho, si el archivo .bzrignore cambiara y contuviera una o
más líneas que comenzaran con "!!" o "RE:", Git no puede interpretar estas líneas, tendrá que adaptar su archivo .git/
info/exclude para ignorar los mismos archivos que los ignorados con .bzrignore. Además, si el archivo .git/info/exclude
era un enlace simbólico, primero deberá eliminar el enlace simbólico, copiar .bzrignore a .git/info/exclude y luego
adaptar este último.
Sin embargo, tenga cuidado con su creación porque con Git es imposible volver a incluir un archivo si se excluye un
directorio principal de ese archivo.

Obtener los cambios del repositorio remoto

Para obtener los cambios del control remoto, extraiga los cambios como de costumbre, utilizando los comandos de Git. Suponiendo que

395
Machine Translated by Google

que sus cambios están en la rama maestra , fusiona o vuelve a basar su trabajo en la rama de origen/maestra :

$ git pull --rebase origen

Empuje su trabajo en el repositorio remoto

Debido a que Bazaar también tiene el concepto de confirmaciones de combinación, no habrá ningún problema si presiona una
confirmación de combinación. Para que pueda trabajar en una rama, fusionar los cambios en el maestro e impulsar su trabajo.
Luego, crea sus ramas, prueba y confirma su trabajo como de costumbre. Finalmente envías tu trabajo al repositorio de Bazaar:

$ git empujar maestro de origen

Advertencias

El marco de ayuda remota de Git tiene algunas limitaciones que se aplican. En particular, estos comandos no funcionan:

• git push origin :branch-to-delete (Bazaar no puede aceptar eliminaciones de ref de esta manera)

• git push origin old:new (empujará old)

• git push --rama de origen de ejecución en seco (empujará)

Resumen

Dado que los modelos de Git y Bazaar son similares, no hay mucha resistencia cuando se trabaja cruzando el límite. Siempre
y cuando esté atento a las limitaciones y esté siempre consciente de que el repositorio remoto no es nativo de Git, estará bien.

Git y por fuerza

Perforce es un sistema de control de versiones muy popular en entornos corporativos. Ha existido desde 1995, lo que lo
convierte en el sistema más antiguo cubierto en este capítulo. Como tal, está diseñado con las limitaciones de su época; asume
que siempre está conectado a un único servidor central y que solo se guarda una versión en el disco local. Sin duda, sus
características y restricciones se adaptan bien a varios problemas específicos, pero hay muchos proyectos que usan Perforce
donde Git realmente funcionaría mejor.

Hay dos opciones si desea mezclar su uso de Perforce y Git. El primero que cubriremos es el puente "Git Fusion" de los
creadores de Perforce, que le permite exponer subárboles de su depósito de Perforce como repositorios Git de lectura y
escritura. El segundo es git-p4, un puente del lado del cliente que le permite usar Git como cliente de Perforce, sin necesidad
de reconfigurar el servidor de Perforce.

Fusión Git

Perforce proporciona un producto llamado Git Fusion (disponible en http://www.perforce.com/git-fusion), que sincroniza un
servidor Perforce con repositorios Git en el lado del servidor.

396
Machine Translated by Google

Configuración

Para nuestros ejemplos, utilizaremos el método de instalación más sencillo para Git Fusion, que consiste en descargar
una máquina virtual que ejecuta el demonio Perforce y Git Fusion. Puede obtener la imagen de la máquina virtual
desde http://www.perforce.com/downloads/Perforce/20-User, y una vez que termine de descargarse, impórtelo a su
software de virtualización favorito (usaremos VirtualBox).

Al iniciar la máquina por primera vez, le pide que personalice la contraseña para tres usuarios de Linux (root, perforce
y git) y proporcione un nombre de instancia, que se puede usar para distinguir esta instalación de otras en la misma
red. Cuando todo se haya completado, verás esto:

Figura 145. La pantalla de arranque de la máquina virtual Git Fusion

Debe tomar nota de la dirección IP que se muestra aquí, la usaremos más adelante. A continuación, crearemos un
usuario de Perforce. Seleccione la opción "Iniciar sesión" en la parte inferior y presione enter (o SSH a la máquina), e
inicie sesión como root. Luego use estos comandos para crear un usuario:

$ p4 -p localhost:1666 -u superusuario -f john $


p4 -p localhost:1666 -u john contraseña $ salir

397
Machine Translated by Google

El primero abrirá un editor VI para personalizar el usuario, pero puede aceptar los valores predeterminados
escribiendo :wq y presionando Intro. El segundo le pedirá que ingrese una contraseña dos veces. Eso es todo lo que
necesitamos hacer con un indicador de shell, así que salga de la sesión.

Lo siguiente que deberá hacer para seguir adelante es decirle a Git que no verifique los certificados SSL. La imagen
de Git Fusion viene con un certificado, pero es para un dominio que no coincidirá con la dirección IP de su máquina
virtual, por lo que Git rechazará la conexión HTTPS. Si va a ser una instalación permanente, consulte el manual de
Perforce Git Fusion para instalar un certificado diferente; para nuestros propósitos de ejemplo, esto será suficiente:

$ exportar GIT_SSL_NO_VERIFY=verdadero

Ahora podemos probar que todo funciona.

$ git clone https://10.0.1.254/Talkhouse Clonación


en 'Talkhouse'...
Nombre de usuario para 'https://10.0.1.254':
john Contraseña para 'https://john@10.0.1.254':
remoto: Conteo de objetos: 630, listo. remoto:
Comprimir objetos: 100% (581/581), hecho. remoto: Total
630 (delta 172), reutilizado 0 (delta 0)
Recepción de objetos: 100 % (630/630), 1,22 MiB | 0 bytes/s, listo.
Resolviendo deltas: 100% (172/172), hecho.
Comprobando conectividad... hecho.

La imagen de la máquina virtual viene equipada con un proyecto de muestra que puede clonar. Aquí estamos clonando
a través de HTTPS, con el usuario john que creamos anteriormente; Git solicita credenciales para esta conexión, pero
el caché de credenciales nos permitirá omitir este paso para cualquier solicitud posterior.

Configuración de fusión

Una vez que haya instalado Git Fusion, querrá modificar la configuración. En realidad, esto es bastante fácil de hacer
con su cliente Perforce favorito; simplemente asigne el directorio //.git-fusion en el servidor Perforce a su espacio de
trabajo. La estructura del archivo se ve así:

398
Machine Translated by Google

$ árbol
.
ÿÿÿ objetos ÿ
ÿÿÿ repositorios ÿ ÿ
ÿÿÿ [...] ÿ ÿÿÿ árboles ÿ
ÿÿÿ [...] ÿ ÿÿÿ p4gf_config
ÿÿÿ repositorios ÿ ÿÿÿ ÿ
Talkhouse ÿ ÿ p4gf_config

ÿÿÿ
usuarios
ÿÿÿ
ÿ

p4gf_mapa de usuario

498 directorios, 287 archivos

El directorio de objetos es utilizado internamente por Git Fusion para asignar objetos de Perforce a Git y
viceversa, no tendrá que meterse con nada allí. Hay un archivo p4gf_config global en este directorio, así
como uno para cada repositorio: estos son los archivos de configuración que determinan cómo se comporta
Git Fusion. Echemos un vistazo al archivo en la raíz:

[repo-creación] juego
de caracteres = utf8

[git-to-perforce]
change-owner = autor
enable-git-branch-creation = sí enable-
swarm-reviews = sí enable-git-merge-
commits = sí enable-git-submodules =
sí verificación previa-commit = ninguno
ignorar -autor-permisos = sin verificación
de permiso de lectura = ninguno git-
merge-evitación-después-de-cambiar-
num = 12107

[forzosamente a git]
http-url = ninguno
ssh-url = ninguno

[@features]
imports = Falso
chunked-push = Falso
matrix2 = Falso paralelo-
push = Falso

[autenticación]
email-case-sensitivity = no

399
Machine Translated by Google

No entraremos en los significados de estas banderas aquí, pero tenga en cuenta que este es solo un archivo de texto con formato
INI, muy parecido a lo que Git usa para la configuración. Este archivo especifica las opciones globales, que luego pueden anularse
mediante archivos de configuración específicos del repositorio, como repos/Talkhouse/p4gf_config. Si abre este archivo, verá una
sección [@repo] con algunas configuraciones que son diferentes de los valores predeterminados globales.
También verá secciones que se ven así:

[Talkhouse-master]
git-branch-name = vista
maestra = //depot/Talkhouse/main-dev/... ...

Este es un mapeo entre una rama de Perforce y una rama de Git. La sección puede tener el nombre que desee, siempre que el
nombre sea único. git-branch-name le permite convertir una ruta de depósito que sería engorrosa en Git a un nombre más amigable.
La configuración de vista controla cómo se mapean los archivos de Perforce en el repositorio de Git, usando la sintaxis de mapeo
de vista estándar. Se puede especificar más de una asignación, como en este ejemplo:

[mapeo de múltiples
proyectos] git-branch-name
= master view = //depot/project1/main/... project1/...
ÿ

//depósito/proyecto2/línea principal/... proyecto2/...

De esta manera, si su mapeo de espacio de trabajo normal incluye cambios en la estructura de los directorios, puede replicar eso
con un repositorio de Git.

El último archivo del que hablaremos es users/p4gf_usermap, que asigna usuarios de Perforce a usuarios de Git, y que es posible
que ni siquiera necesite. Al convertir un conjunto de cambios de Perforce a una confirmación de Git, el comportamiento
predeterminado de Git Fusion es buscar el usuario de Perforce y usar la dirección de correo electrónico y el nombre completo
almacenados allí para el campo autor/autor en Git. Al convertir de otra manera, el valor predeterminado es buscar el usuario de
Perforce con la dirección de correo electrónico almacenada en el campo de autor de la confirmación de Git y enviar el conjunto de
cambios como ese usuario (con la aplicación de permisos). En la mayoría de los casos, este comportamiento funcionará bien, pero
considere el siguiente archivo de mapeo:

john john@example.com "John Doe"


john johnny@appleseed.net "John Doe"
bob employeeX@example.com "Anon X. Mouse"
joe employeeY@example.com "Anon Y. Mouse"

Cada línea tiene el formato <usuario> <correo electrónico> "<nombre completo>" y crea una sola asignación de usuario. Las dos
primeras líneas asignan dos direcciones de correo electrónico distintas a la misma cuenta de usuario de Perforce. Esto es útil si
creó confirmaciones de Git en varias direcciones de correo electrónico diferentes (o cambió las direcciones de correo electrónico),
pero desea que se asignen al mismo usuario de Perforce. Al crear una confirmación de Git a partir de un conjunto de cambios de
Perforce, la primera línea que coincide con el usuario de Perforce se usa para la información de autoría de Git.

Las últimas dos líneas enmascaran los nombres reales y las direcciones de correo electrónico de Bob y Joe de las confirmaciones
de Git que se crean. Esto es bueno si desea abrir un proyecto interno, pero no desea publicar su directorio de empleados en todo
el mundo. Tenga en cuenta que las direcciones de correo electrónico y los nombres completos deben

400
Machine Translated by Google

ser único, a menos que desee que todos los compromisos de Git se atribuyan a un solo autor ficticio.

flujo de trabajo

Perforce Git Fusion es un puente bidireccional entre Perforce y el control de versiones de Git. Echemos un vistazo a cómo se
siente trabajar desde el lado de Git. Asumiremos que hemos mapeado en el proyecto "Jam" usando un archivo de
configuración como se muestra arriba, que podemos clonar así:

$ git clone https://10.0.1.254/Jam


Clonando en 'Jam'...
Nombre de usuario para 'https://10.0.1.254':
john Contraseña para 'https://john@10.0.1.254':
remoto: Conteo de objetos: 2070, listo. remoto:
Comprimir objetos: 100% (1704/1704), listo.
Recepción de objetos: 100 % (2070/2070), 1,21 MiB | 0 bytes/s, listo.
remoto: Total 2070 (delta 1242), reutilizado 0 (delta 0)
Resolviendo deltas: 100% (1242/1242), hecho.
Comprobando conectividad...
hecho. $ rama git -un maestro
*

mandos a distancia/origen/HEAD -> origen/


maestro mandos a distancia/origen/maestro
mandos a distancia/origen/rel2.1 $ git log --
oneline --decorate --graph --all * 0a38c33 (origin/
rel2.1) Crear versión Jam 2.1 rama. | * d254865 (HEAD, origin/
master, origin/HEAD, master) Actualice a los últimos metrowerks en Beos, el de Intel. | * bd2f54a Se
corrigió la fuga del identificador NT de Jam. | * c0f29e7 Arreglar URL en un documento jam | * puerto
lynx de cc644ac Radstone. [...]

La primera vez que haga esto, puede llevar algo de tiempo. Lo que sucede es que Git Fusion está convirtiendo todos los
conjuntos de cambios aplicables en el historial de Perforce en confirmaciones de Git. Esto sucede localmente en el servidor,
por lo que es relativamente rápido, pero si tiene mucho historial, aún puede llevar algo de tiempo. Las recuperaciones
posteriores realizan una conversión incremental, por lo que se sentirá más como la velocidad nativa de Git.

Como puede ver, nuestro repositorio se ve exactamente como cualquier otro repositorio de Git con el que pueda trabajar.
Hay tres ramas, y Git ha creado una rama maestra local que realiza un seguimiento del origen/maestro. Trabajemos un poco
y creemos un par de confirmaciones nuevas:

401
Machine Translated by Google

#...
$ git log --oneline --decorate --graph --all * cfd46ab
(HEAD, maestro) Agregar documentación para la nueva función *
a730d77 Espacio en blanco * d254865 (origen/maestro, origen/HEAD)
Actualizar a la última versión de metrowerks en Beos: el Intel uno. * bd2f54a Se corrigió la fuga
del identificador NT de Jam. [...]

Tenemos dos nuevos compromisos. Ahora vamos a comprobar si alguien más ha estado trabajando:

$ git fetch
remote: Contando objetos: 5, listo.
remoto: Comprimir objetos: 100% (3/3), hecho. remoto:
Total 3 (delta 2), reutilizado 0 (delta 0)
Desembalaje de objetos: 100% (3/3), hecho.
Desde https://10.0.1.254/Jam
d254865..6afeb15 maestro -> origen/maestro $ git log --
oneline --decorate --graph --all * 6afeb15 (origen/maestro,
origen/HEAD) Actualizar copyright | * cfd46ab (HEAD, maestro)
Agregar documentación para la nueva función | * a730d77 Espacio en
blanco |/ * d254865 Actualice a los últimos metrowerks en Beos, el de
Intel. * bd2f54a Se corrigió la fuga del identificador NT de Jam. [...]

¡Parece que alguien lo estaba! No lo sabría desde esta vista, pero la confirmación 6afeb15 en realidad se creó usando un cliente
Perforce. Simplemente parece otra confirmación desde el punto de vista de Git, que es exactamente el punto. Veamos cómo el
servidor Perforce maneja una confirmación de fusión:

402
Machine Translated by Google

$ git merge origin/master


Fusión automática README
Fusión realizada por la estrategia 'recursiva'.
LÉAME | 2 +- 1
archivo cambiado, 1 inserción (+), 1 eliminación (-) $ git
push Contar objetos: 9, listo.

Compresión delta usando hasta 8 hilos.


Comprimir objetos: 100% (9/9), listo.
Objetos de escritura: 100% (9/9), 917 bytes | 0 bytes/s, listo.
Total 9 (delta 6), reutilizado 0 (delta 0) remoto:
Por fuerza: 100 % (3/3) Cargando el árbol de confirmación en la memoria...
remoto: Por fuerza: 100 % (5/5) Encontrando confirmaciones secundarias...
remoto : Forzado: Ejecutando git fast-export... remoto: Forzado: 100% (3/3)
Comprobando confirmaciones... remoto: El procesamiento continuará incluso
si la conexión está cerrada. remoto: Perforce: 100% (3/3) Copiando listas de
cambios... remoto: Perforce: Envío de nuevos objetos de confirmación de Git
a Perforce: 4 Para https://10.0.1.254/Jam 6afeb15..89cba2b maestro -> maestro

Git cree que funcionó. Echemos un vistazo al historial del archivo README desde el punto de vista de Perforce,
utilizando la función de gráfico de revisión de p4v:

Figura 146. Gráfico de revisión Forforce resultante de Git push

Si nunca ha visto esta vista antes, puede parecer confuso, pero muestra los mismos conceptos que un visor gráfico
para el historial de Git. Estamos mirando el historial del archivo README , por lo que el árbol de directorios en la
parte superior izquierda solo muestra ese archivo tal como aparece en varias ramas. En la parte superior derecha,
tenemos un gráfico visual de cómo se relacionan las diferentes revisiones del archivo, y la vista general de este gráfico está en

403
Machine Translated by Google

abajo a la derecha. El resto de la vista corresponde a la vista de detalles de la revisión seleccionada (2 en este caso).

Una cosa a tener en cuenta es que el gráfico se ve exactamente como el del historial de Git. Perforce no tenía una rama
con nombre para almacenar las confirmaciones 1 y 2 , por lo que creó una rama "anónima" en el directorio .git-fusion
para contenerla. Esto también sucederá con las ramas de Git con nombre que no se correspondan con una rama de
Perforce con nombre (y luego puede asignarlas a una rama de Perforce usando el archivo de configuración).

La mayor parte de esto sucede detrás de escena, pero el resultado final es que una persona en un equipo puede usar
Git, otra puede usar Perforce, y ninguno de ellos sabrá sobre la elección del otro.

Resumen de Git-Fusion

Si tiene (o puede obtener) acceso a su servidor Perforce, Git Fusion es una excelente manera de hacer que Git y Perforce
se comuniquen entre sí. Hay un poco de configuración involucrada, pero la curva de aprendizaje no es muy pronunciada.
Esta es una de las pocas secciones de este capítulo donde no aparecerán advertencias sobre el uso de toda la potencia
de Git. Eso no quiere decir que Perforce estará contento con todo lo que le des, si intentas reescribir el historial que ya
se ha empujado, Git Fusion lo rechazará, pero Git Fusion se esfuerza mucho por sentirse nativo. Incluso puede usar
submódulos de Git (aunque se verán extraños para los usuarios de Perforce) y fusionar ramas (esto se registrará como
una integración en el lado de Perforce).

Si no puede convencer al administrador de su servidor para que configure Git Fusion, todavía hay una manera de usar
estas herramientas juntas.

Git-p4

Git-p4 es un puente bidireccional entre Git y Perforce. Se ejecuta completamente dentro de su repositorio de Git, por lo
que no necesitará ningún tipo de acceso al servidor de Perforce (aparte de las credenciales de usuario, por supuesto).
Git-p4 no es una solución tan flexible ni completa como Git Fusion, pero le permite hacer la mayor parte de lo que le
gustaría hacer sin invadir el entorno del servidor.

Necesitará la herramienta p4 en algún lugar de su RUTA para trabajar con git-p4. A partir de este
ÿ escrito, está disponible gratuitamente en http://www.perforce.com/downloads/Perforce/20-User .

Configuración

Por ejemplo, ejecutaremos el servidor Perforce desde Git Fusion OVA como se muestra arriba, pero omitiremos el
servidor Git Fusion e iremos directamente al control de versiones de Perforce.

Para usar el cliente de línea de comandos p4 (del que depende git-p4), deberá configurar un par de variables de entorno:

$ exportar P4PORT=10.0.1.254:1666
$ exportar P4USER=juan

Empezando

Como con cualquier cosa en Git, el primer comando es clonar:

404
Machine Translated by Google

$ git p4 clone //depot/www/live www-shallow


Importando desde //depot/www/live a www-shallow
Repositorio Git vacío inicializado en /private/tmp/www-shallow/.git/
Realizando la importación inicial de // depot/www/live/ de la revisión
#head into refs/remotes/p4/master

Esto crea lo que en términos de Git es un clon "superficial"; solo la última revisión de Perforce se importa a Git; recuerda, Perforce no está
diseñado para dar todas las revisiones a todos los usuarios. Esto es suficiente para usar Git como cliente de Perforce, pero para otros
propósitos no es suficiente.

Una vez que está terminado, tenemos un repositorio Git completamente funcional:

$ cd myproject
$ git log --oneline --all --graph --decorate *
70eaf78 (HEAD, p4/master, p4/HEAD, master) Importación inicial de //depot/www/live/ desde
el estado en la revisión # cabeza

Tenga en cuenta que hay un control remoto "p4" para el servidor Perforce, pero todo lo demás parece un clon estándar. En realidad, eso
es un poco engañoso; en realidad no hay un control remoto allí.

$ git remoto -v

No existen controles remotos en este repositorio. Git-p4 ha creado algunas referencias para representar el estado del servidor, y se ven
como referencias remotas para el registro de git, pero Git no las administra y no puede enviarlas.

flujo de trabajo

Bien, hagamos un poco de trabajo. Supongamos que ha progresado en una característica muy importante y está listo para mostrársela al
resto de su equipo.

$ git log --oneline --all --graph --decorate *


018467c (HEAD, master) Cambiar título de
página * c0fb617 Actualizar enlace * 70eaf78
(p4/master, p4/HEAD) Importación inicial de //depot/www/live / del estado en revisión #head

Hicimos dos confirmaciones nuevas que estamos listos para enviar al servidor de Perforce. Veamos si alguien más estuvo trabajando hoy:

405
Machine Translated by Google

$ git p4 sincronizar
git p4 sincronizar
Realizando una importación incremental en refs/remotes/p4/master git branch
Rutas de depósito: //depot/www/live/
Destino de importación: refs/remotes/p4/master
Importando revisión 12142 (100%) $
git log --oneline --all --graph --decorate * 75cd059 (p4/
master, p4/HEAD) Actualizar copyright | * 018467c
(HEAD, master) Cambiar título de página | * c0fb617
Enlace de actualización |/ * 70eaf78 Importación inicial
de //depot/www/live/ desde el estado en la revisión #head

Parece que lo fueron, y master y p4/master han divergido. El sistema de bifurcación de Perforce no se parece
en nada al de Git, por lo que enviar confirmaciones de combinación no tiene ningún sentido. Git-p4 recomienda
que rebase sus confirmaciones, e incluso viene con un atajo para hacerlo:

$ git p4 rebase
Realizando una importación incremental en refs/remotes/p4/master git branch
Rutas de depósito: //depot/www/live/ ¡No hay cambios para importar!

Rebasando la rama actual en remotes/p4/master Primero,


rebobinando la cabeza para reproducir su trabajo encima de ella...
Solicitando: Actualizar
enlace Solicitando: Cambiar
título de página index.html | 2 +-
1 archivo cambiado, 1 inserción (+), 1 borrado (-)

Probablemente lo sepa por el resultado, pero git p4 rebase es un atajo para git p4 sync seguido de git rebase
p4/master. Es un poco más inteligente que eso, especialmente cuando se trabaja con varias ramas, pero esta
es una buena aproximación.

Ahora nuestra historia vuelve a ser lineal y estamos listos para contribuir con nuestros cambios a Perforce. El
comando de envío de git p4 intentará crear una nueva revisión de Perforce para cada confirmación de Git entre
p4/master y master. Ejecutarlo nos lleva a nuestro editor favorito, y el contenido del archivo se parece a esto:

406
Machine Translated by Google

# Una especificación de cambio forzoso.


#
# Cambiar: El número de cambio. 'nuevo' en una nueva lista de cambios.
# Fecha: La fecha de la última modificación de esta especificación.
# Cliente: El cliente en el que se creó la lista de cambios. Solo lectura.
# Usuario: El usuario que creó la lista de cambios.
# Estado: Ya sea 'pendiente' o 'enviado'. Solo lectura.
# Tipo: # Ya sea 'público' o 'restringido'. El valor predeterminado es 'público'.
Descripción: Comentarios sobre la lista de cambios. Requerido.
# Trabajos: Los trabajos abiertos se cerrarán con esta lista de cambios.
# Puede eliminar trabajos de esta lista. (Nuevas listas de cambios solamente).
# Archivos: Qué archivos abiertos de la lista de cambios predeterminada se agregarán
# a esta lista de cambios. Puede eliminar archivos de esta lista.
# (Nuevas listas de cambios solamente).

Cambio: nuevo

Cliente: john_bens-mbp_8487

Usuario: juan

Estado: nuevo

Descripción:
Actualizar enlace

Archivos:

//depósito/www/live/index.html # editar

######## git author ben@straub.cc no coincide con su cuenta p4.


######## Utilice la opción --preserve-user para modificar la autoría.
######## La variable git-p4.skipUserNameCheck oculta este mensaje.
######## todo debajo de esta línea es solo la diferencia #######
--- //depósito/www/live/index.html 2014-08-31 18:26:05.000000000 0000
+++ /Users/ben/john_bens-mbp_8487/john_bens-mbp_8487/depot/www/live/index.html 2014-
08-31 18:26:05.000000000 0000
@@ -60,7 +60,7 @@
</td>
<td valign=superior>
Fuente y documentación para
-<a href="http://www.perforce.com/jam/jam.html">
+<a href="atasco.html">
Atasco/MR</a>,
una herramienta de creación de software.

</td>

Este es principalmente el mismo contenido que vería al ejecutar p4 enviar, excepto las cosas al final que
git-p4 ha incluido útilmente. Git-p4 intenta respetar la configuración de Git y Perforce individualmente cuando

407
Machine Translated by Google

tiene que proporcionar un nombre para una confirmación o conjunto de cambios, pero en algunos casos desea anularlo.
Por ejemplo, si la confirmación de Git que está importando fue escrita por un colaborador que no tiene una cuenta de
usuario de Perforce, es posible que desee que el conjunto de cambios resultante parezca que lo escribió él (y no usted).

Git-p4 ha importado útilmente el mensaje de la confirmación de Git como el contenido de este conjunto de cambios de
Perforce, por lo que todo lo que tenemos que hacer es guardar y salir, dos veces (una para cada confirmación). La salida
de shell resultante se verá así:

$ git p4 enviar
Perforce checkout para ruta de depósito //depot/www/live/ ubicado en /Users/ben/john_bens mbp_8487/
john_bens-mbp_8487/depot/www/live/ Sincronizando p4 checkout... - archivo(s) up- hasta la fecha.

...
Aplicando el enlace de actualización
dbac45b //depot/www/live/index.html#4 - abierto para editar
Cambio 12143 creado con 1 archivo(s) abierto(s).
Enviando cambio 12143.
Bloqueando 1 archivos...
editar //depot/www/live/index.html#5
Cambio 12143 enviado.
Aplicando 905ec6a Cambiar título de
página //depot/www/live/index.html#5 - abierto para editar
Cambio 12144 creado con 1 archivo(s) abierto(s).
Enviando cambio 12144.
Bloqueando 1 archivos...
editar //depot/www/live/index.html#6
Cambio 12144 enviado.
¡Todas las confirmaciones aplicadas!

Realizando una importación incremental en refs/remotes/p4/master git branch Rutas


de depósito: //depot/www/live/ Destino de importación: refs/remotes/p4/master
Importando revisión 12144 (100 %)

Cambiando la base de la rama actual en remotes/p4/master


Primero, rebobinando head para reproducir su trabajo encima... $ git
log --oneline --all --graph --decorate * 775a46f (HEAD, p4/master, p4 /
HEAD, maestro) Cambiar el título de la página * 05f1ade Actualizar enlace *
75cd059 Actualizar copyright * 70eaf78 Importación inicial de //depot/www/live/
desde el estado en la revisión #head

El resultado es como si acabáramos de hacer un git push, que es la analogía más cercana a lo que realmente sucedió.

Tenga en cuenta que durante este proceso, cada confirmación de Git se convierte en un conjunto de cambios de Perforce;
si desea agruparlos en un solo conjunto de cambios, puede hacerlo con una reorganización interactiva antes de ejecutar
git p4 submit. También tenga en cuenta que los hashes SHA-1 de todas las confirmaciones que se enviaron como
conjuntos de cambios han cambiado; esto se debe a que git-p4 agrega una línea al final de cada confirmación que convierte:

408
Machine Translated by Google

$ git registro -1
compromiso 775a46f630d8b46535fc9983cf3ebe6b9aa53145
Autor: John Doe <john@example.com> Fecha:
dom 31 de agosto 10:31:44 2014 -0800

Cambiar el título de la página

[git-p4: rutas de depósito = "//depósito/www/live/": cambio = 12144]

¿Qué sucede si intentas enviar una confirmación de fusión? Hagamos un intento. Esta es la situación en la que
nos hemos metido:

$ git log --oneline --all --graph --decorate * 3be6fd8 (HEAD,


master) Dirección de correo electrónico correcta * 1dcbf21
Combinar rama de seguimiento remoto 'p4/master' |\ | * c4689fc (p4/
master, p4/HEAD) Corrección de gramática * | cbacd0a Bordes de la
tabla: sí, por favor * | b4959b6 Marca registrada |/ * 775a46f Cambiar
título de página * 05f1ade Actualizar enlace * 75cd059 Actualizar
copyright * 70eaf78 Importación inicial de //depot/www/live/ desde el
estado en revisión #head

La historia de Git y Perforce diverge después de 775a46f. El lado de Git tiene dos confirmaciones, luego una
confirmación de fusión con el encabezado de Perforce, luego otra confirmación. Vamos a intentar enviarlos
además de un solo conjunto de cambios en el lado de Perforce. Veamos qué pasaría si intentáramos enviar ahora:

$ git p4 enviar -n
Forzar el pago de la ruta del depósito //depot/www/live/ ubicado en /Users/ben/john_bens mbp_8487/john_bens-
mbp_8487/depot/www/live/
Sincronizaría el pago de p4 en /Users/ben/john_bens-mbp_8487/john_bens mbp_8487/depot/www/
live/
Aplicaría
b4959b6 Marca comercial
cbacd0a Bordes de la tabla: sí, por favor 3be6fd8
Dirección de correo electrónico correcta

El indicador -n es la abreviatura de --dry-run, que intenta informar lo que sucedería si el comando de envío se
ejecutara de verdad. En este caso, parece que estaríamos creando tres conjuntos de cambios de Perforce, que
corresponden a las tres confirmaciones no fusionadas que aún no existen en el servidor de Perforce. Eso suena
exactamente como lo que queremos, veamos cómo resulta:

409
Machine Translated by Google

$ git p4 enviar […] $


git log --oneline --all
--graph --decorate * dadbd89 (HEAD, p4/master, p4/HEAD,
master) Dirección de correo electrónico correcta * 1b79a80 Bordes de la tabla: sí, por favor *
0097235 Marca registrada

* Corrección de gramática
c4689fc * 775a46f Cambiar título de
página * 05f1ade Enlace de
actualización * 75cd059 Actualizar
copyright * 70eaf78 Importación inicial de //depot/www/live/ desde el estado en revisión #head

Nuestra historia se volvió lineal, como si nos hubiéramos reorganizado antes de enviar (que de hecho es exactamente lo que
sucedió). Esto significa que puede ser libre de crear, trabajar, descartar y fusionar ramas en el lado de Git sin temor a que su
historial de alguna manera se vuelva incompatible con Perforce. Si puede reorganizarlo, puede contribuirlo a un servidor Perforce.

Derivación

Si su proyecto Perforce tiene múltiples ramas, no está de suerte; git-p4 puede manejar eso de una manera que lo hace sentir como
Git. Digamos que su depósito de Perforce se presenta así:

//deposito
ÿÿÿ
ÿ

proyecto
ÿÿÿ principal ÿÿÿ dev
ÿ

Y supongamos que tiene una rama de desarrollo , que tiene una especificación de vista que se ve así:

//depósito/proyecto/principal/... //depósito/proyecto/dev/...

Git-p4 puede detectar automáticamente esa situación y hacer lo correcto:

410
Machine Translated by Google

$ git p4 clone --detect-branches //depot/project@all


Importando desde //depot/project@all al proyecto
Repositorio Git vacío inicializado en /private/tmp/project/.git/
Importando revisión 20 (50%)
Importación de un nuevo proyecto/desarrollo de rama

Reanudando con cambio 20


Importando revisión 22 (100%)
Ramas actualizadas: main
dev $ cd project; git log --oneline --all --graph --decorate *
eae77ae (HEAD, p4/master, p4/HEAD, master) principal | *
10d55fb (p4/proyecto/dev) dev | * a43cfae Poblar //almacén/
proyecto/principal/... //almacén/proyecto/desarrollo/.... |/ * 2b83451 Proyecto
init

Tenga en cuenta el especificador "@all" en la ruta del almacén; eso le dice a git-p4 que clone no solo el último conjunto de
cambios para ese subárbol, sino todos los conjuntos de cambios que alguna vez hayan tocado esos caminos. Esto se acerca más
al concepto de clon de Git, pero si está trabajando en un proyecto con una larga historia, podría llevar un tiempo.

El indicador --detect-branches le dice a git-p4 que use las especificaciones de rama de Perforce para mapear las ramas a las referencias de Git.

Si estas asignaciones no están presentes en el servidor de Perforce (que es una forma perfectamente válida de usar Perforce),
puede decirle a git-p4 cuáles son las asignaciones de rama y obtendrá el mismo resultado:

$ git init project


Inicializó el repositorio Git vacío en /tmp/project/.git/ $ cd
project $ git config git-p4.branchList main:dev $ git clone --
detect-branches //depot/project@all .

Establecer la variable de configuración git-p4.branchList en main:dev le dice a git-p4 que "main" y "dev" son ambas ramas, y la
segunda es hija de la primera.

Si ahora hacemos git checkout -b dev p4/project/dev y hacemos algunas confirmaciones, git-p4 es lo suficientemente inteligente
como para apuntar a la rama correcta cuando hacemos git p4 submit. Desafortunadamente, git-p4 no puede mezclar clones
superficiales y múltiples ramas; si tiene un proyecto enorme y quiere trabajar en más de una rama, tendrá que clonar git p4 una
vez para cada rama a la que desee enviar.

Para crear o integrar sucursales, deberá utilizar un cliente Perforce. Git-p4 solo puede sincronizar y enviar a ramas existentes, y
solo puede hacerlo con un conjunto de cambios lineal a la vez. Si fusiona dos ramas en Git e intenta enviar el nuevo conjunto de
cambios, todo lo que se registrará será un montón de cambios en los archivos; se perderán los metadatos sobre qué sucursales
están involucradas en la integración.

Resumen de Git y Perforce

Git-p4 hace posible usar un flujo de trabajo Git con un servidor Perforce, y es bastante bueno en eso.
Sin embargo, es importante recordar que Perforce está a cargo de la fuente y solo está usando Git para trabajar localmente. Solo
tenga mucho cuidado al compartir confirmaciones de Git; si tienes un control remoto que otro

411
Machine Translated by Google

la gente usa, no envíe ninguna confirmación que aún no se haya enviado al servidor de Perforce.

Si desea combinar libremente el uso de Perforce y Git como clientes para el control de código fuente, y puede convencer al
administrador del servidor para que lo instale, Git Fusion convierte el uso de Git en un cliente de control de versiones de primera
clase para un servidor Perforce.

Migrar a Git
Si tiene un código base existente en otro VCS pero ha decidido comenzar a usar Git, debe migrar su proyecto de una forma u
otra. Esta sección repasa algunos importadores para sistemas comunes y luego demuestra cómo desarrollar su propio importador
personalizado. Aprenderá cómo importar datos de varios de los sistemas SCM más grandes utilizados profesionalmente, porque
constituyen la mayoría de los usuarios que están cambiando y porque las herramientas de alta calidad para ellos son fáciles de
conseguir.

Subversión
Si leyó la sección anterior sobre el uso de git svn, puede usar fácilmente esas instrucciones para git svn clonar un repositorio;
luego, deje de usar el servidor Subversion, empuje a un nuevo servidor Git y comience a usarlo. Si desea el historial, puede
lograrlo tan rápido como extraiga los datos del servidor de Subversion (lo que puede llevar un tiempo).

Sin embargo, la importación no es perfecta; y debido a que tomará tanto tiempo, es mejor que lo hagas bien. El primer problema
es la información del autor. En Subversion, cada persona que se compromete tiene un usuario en el sistema que se registra en
la información de confirmación. Los ejemplos de la sección anterior muestran schacon en algunos lugares, como la salida de
culpa y el registro de git svn. Si desea asignar esto a mejores datos de autor de Git, necesita una asignación de los usuarios de
Subversion a los autores de Git. Cree un archivo llamado users.txt que tenga esta asignación en un formato como este:

schacon = Scott Chacon <schacon@geemail.com>


selse = Someo Nelse <selse@geemail.com>

Para obtener una lista de los nombres de autor que usa SVN, puede ejecutar esto:

$ registro svn --xml --quiet | autor de grep | ordenar -u | \


perl -pe 's/.*>(.*?)<.*/$1 = /'

Eso genera la salida del registro en formato XML, luego conserva solo las líneas con la información del autor, descarta los
duplicados y elimina las etiquetas XML. Obviamente, esto solo funciona en una máquina con grep, sort y perl instalados. Luego,
redirija esa salida a su archivo users.txt para que pueda agregar los datos de usuario de Git equivalentes junto a cada entrada.

Si está intentando esto en una máquina con Windows, este es el punto en el que tendrá problemas.

ÿ Microsoft ha brindado buenos consejos y ejemplos en https://docs.microsoft.com/en-us/azure/devops/repos/


git/perform-migration-from svn-to-git.

412
Machine Translated by Google

Puede proporcionar este archivo a git svn para ayudarlo a mapear los datos del autor con mayor precisión.
También puede decirle a git svn que no incluya los metadatos que Subversion normalmente importa, pasando --
no-metadata al comando clonar o init . Los metadatos incluyen un git-svn-id dentro de cada mensaje de
confirmación que generará Git durante la importación. Esto puede inflar su registro de Git y puede hacerlo un poco confuso.

Debe conservar los metadatos cuando desee duplicar las confirmaciones realizadas en el
ÿ repositorio de Git en el repositorio SVN original. Si no desea la sincronización en su registro de
confirmación, no dude en omitir el parámetro --no-metadata .

Esto hace que su comando de importación se vea así:

$ git svn clone http://my-project.googlecode.com/svn/ \ --authors-


""
ÿ

file=users.txt --no-metadata --prefix $ cd my_project -s mi_proyecto

Ahora debería tener una mejor importación de Subversion en su directorio my_project . En lugar de confirmaciones
que se ven así:

cometer 37efa680e8473b615de980fa935944215428a35a
Autor: schacon <schacon@4c93b258-373f-11de-be05-5f7a86268029>
Fecha: dom 3 mayo 00:12:22 2009 +0000

instalación fija - ir al maletero

git-svn-id: https://my-project.googlecode.com/svn/trunk@94 4c93b258-373f-11de-be05-5f7a86268029

se ven así:

cometer 03a8785f44c8ea5cdb0e8834b7c8e6c469be2ff2
Autor: Scott Chacon <schacon@geemail.com> Fecha:
domingo 3 de mayo 00:12:22 2009 +0000

instalación fija - ir al maletero

No solo el campo Autor se ve mucho mejor, sino que el git-svn-id ya no está allí.

También debe hacer un poco de limpieza posterior a la importación. Por un lado, deberías limpiar las extrañas
referencias que git svn configuró. Primero moverá las etiquetas para que sean etiquetas reales en lugar de
extrañas ramas remotas, y luego moverá el resto de las ramas para que sean locales.

Para mover las etiquetas para que sean etiquetas Git adecuadas, ejecute:

$ para t en $(git for-each-ref --format='%(refname:short)' refs/remotes/tags); haz git tag ${t/tags\//} $t &&
git branch -D -r $t; hecho

413
Machine Translated by Google

Esto toma las referencias que eran ramas remotas que comenzaron con refs/remotes/tags/ y las convierte en etiquetas reales
(ligeras).

A continuación, mueva el resto de las referencias bajo refs/remotes para que sean sucursales locales:

$ for b in $(git for-each-ref --format='%(refname:short)' refs/remotes); hacer git branch $b refs/


remotes/$b && git branch -D -r $b; hecho

Puede suceder que vea algunas ramas adicionales con el sufijo @xxx (donde xxx es un número), mientras que en Subversion solo
ve una rama. Esta es en realidad una característica de Subversion llamada "peg-revisions", que es algo para lo que Git simplemente
no tiene una contraparte sintáctica. Por lo tanto, git svn simplemente agrega el número de versión de svn al nombre de la rama de
la misma manera que lo hubieras escrito en svn para abordar la revisión de clavija de esa rama. Si ya no le importan las revisiones
de clavijas, simplemente elimínelas:

$ para p en $(git for-each-ref --format='%(refname:short)' | grep @); haz git branch -D $p; hecho

Ahora todas las ramas antiguas son ramas Git reales y todas las etiquetas antiguas son etiquetas Git reales.

Hay una última cosa que limpiar. Desafortunadamente, git svn crea una rama adicional llamada troncal, que se asigna a la rama
predeterminada de Subversion, pero la referencia troncal apunta al mismo lugar que la maestra.
Dado que master es más idiomáticamente Git, aquí se explica cómo eliminar la rama adicional:

$ git rama -d tronco

Lo último que debe hacer es agregar su nuevo servidor Git como un control remoto y presionarlo. Aquí hay un ejemplo de cómo
agregar su servidor como un control remoto:

$ git remoto agregar origen git@my-git-server:myrepository.git

Como desea que todas sus ramas y etiquetas suban, ahora puede ejecutar esto:

$ git push origen --todos


$ git push origen --etiquetas

Todas sus ramas y etiquetas deben estar en su nuevo servidor Git en una importación agradable y limpia.

Mercurial
Dado que Mercurial y Git tienen modelos bastante similares para representar versiones, y dado que Git es un poco más flexible,
convertir un repositorio de Mercurial a Git es bastante sencillo, usando una herramienta llamada "hg-fast-export", que necesitará un
copia de:

414
Machine Translated by Google

$ clon de git https://github.com/frej/fast-export.git

El primer paso en la conversión es obtener un clon completo del repositorio de Mercurial que desea convertir:

$ hg clone <URL de repositorio remoto> /tmp/hg-repo

El siguiente paso es crear un archivo de asignación de autor. Mercurial es un poco más indulgente que Git por lo que pondrá
en el campo de autor para los conjuntos de cambios, por lo que este es un buen momento para limpiar la casa. Generar esto
es un comando de una línea en un shell bash :

$ cd /tmp/hg-repo $
registro de hg | usuario grep: | ordenar | único | sed 's/usuario: *//' > ../autores

Esto tomará unos segundos, dependiendo de la duración del historial de su proyecto, y luego el archivo /tmp/authors se verá
así:

Beto

bob@localhost
bob <bob@company.com>
bob jones <bob <AT> empresa <DOT> com>
Bob Jones <bob@company.com> Joe Smith
<joe@company.com>

En este ejemplo, la misma persona (Bob) ha creado conjuntos de cambios con cuatro nombres diferentes, uno de los cuales
parece correcto y el otro no sería válido para una confirmación de Git. Hg fast-export nos permite arreglar esto convirtiendo
cada línea en una regla: "<input>"="<output>", asignando una <input> a una <output>. Dentro de las cadenas <input> y
<output> , se admiten todas las secuencias de escape entendidas por la codificación python string_escape . Si el archivo de
asignación de autor no contiene una <entrada> coincidente, ese autor se enviará a Git sin modificar. Si todos los nombres de
usuario se ven bien, no necesitaremos este archivo en absoluto. En este ejemplo, queremos que nuestro archivo se vea así:

"bob"="Bob Jones <bob@company.com>"


"bob@localhost"="Bob Jones <bob@company.com>" "bob
<bob@company.com>"="Bob Jones <bob@company .com>" "bob
jones <bob <AT> empresa <DOT> com>"="Bob Jones <bob@company.com>"

El mismo tipo de archivo de mapeo se puede usar para cambiar el nombre de ramas y etiquetas cuando Git no permite el
nombre de Mercurial.

El siguiente paso es crear nuestro nuevo repositorio Git y ejecutar el script de exportación:

415
Machine Translated by Google

$ git init /tmp/converted $


cd /tmp/converted $ /tmp/
fast-export/hg-fast-export.sh -r /tmp/hg-repo -A /tmp/authors

El indicador -r le dice a hg-fast-export dónde encontrar el repositorio de Mercurial que queremos convertir, y el
indicador -A le indica dónde encontrar el archivo de mapeo de autor (los archivos de mapeo de ramas y etiquetas se
especifican mediante -B y - banderas T respectivamente). El script analiza los conjuntos de cambios de Mercurial y
los convierte en un script para la función de "importación rápida" de Git (que discutiremos en detalle más adelante).
Esto toma un poco (aunque es mucho más rápido de lo que sería a través de la red) y el resultado es bastante detallado:

416
Machine Translated by Google

$ /tmp/exportación-rápida/hg-exportación-rápida.sh -r /tmp/hg-repo -A /tmp/autores


Cargado 4 autores

maestro: exportar la revisión completa 1/22208 con 13/0/0 archivos agregados/cambiados/eliminados


maestro: exportar revisión delta simple 2/22208 con 1/1/0 archivos agregados/cambiados/eliminados
maestro: exportar revisión delta simple 3/22208 con 0/1/0 archivos agregados/cambiados/eliminados
[…]
maestro: exportar la revisión delta simple 22206/22208 con 0/4/0 agregado/cambiado/eliminado
archivos

maestro: exportar la revisión delta simple 22207/22208 con 0/2/0 agregado/cambiado/eliminado


archivos

maestro: Exportación completa revisión delta 22208/22208 con 3/213/0


archivos agregados/modificados/eliminados
Exportando etiqueta [0.4c] en [hg r9] [git: 10]
Exportando etiqueta [0.4d] en [hg r16] [git :17]
[…]
Etiqueta de exportación [3.1-rc] en [hg r21926] [git: 21927]
Exportando etiqueta [3.1] en [hg r21973] [git :21974]
22315 comandos emitidos

estadísticas de git-fast-import:
-------------------------------------------------- -------------------

Objetos asignados: 120000

Objetos totales: 115032 208171 duplicados )


ÿ

blobs: ( 40504 ( 205320 duplicados 26117 deltas de 39602

intentos)
ÿ

árboles : 52320 ( 2851 duplicados 47467 deltas de 47599

intentos)
ÿ

comete: 22208 ( 0 duplicados 0 deltas de 0

intentos)
ÿ

etiquetas: 0( 0 duplicados 0 deltas de 0

intentos)
Total sucursales: 109 2 cargas )
ÿ

marcas: (1048576 22208 único )


ÿ

átomos: (1952)

Memoria total: 7860 KiB


ÿ

2235 KiB
ÿ

agrupaciones: objetos: 5625 KiB


-------------------------------------------------- -------------------

=
pack_report: getpagesize() 4096
paquete_informe: core.packedGitWindowSize = 1073741824
informe_paquete: core.packedGitLimit = 8589934592
= 90430
pack_report: pack_used_ctr
= 46771
pack_report: pack_mmap_calls
= 1/1
pack_report: pack_open_windows
pack_report: pack_mapped = 340852700 / 340852700
-------------------------------------------------- -------------------

$ git shortlog -sn


369 Bob Jones
365 Juan Pérez

417
Machine Translated by Google

Eso es prácticamente todo lo que hay que hacer. Todas las etiquetas de Mercurial se han convertido en etiquetas de Git,
y las ramas y marcadores de Mercurial se han convertido en ramas de Git. Ahora está listo para llevar el repositorio a su
nuevo hogar del lado del servidor:

$ git remote add origin git@my-git-server:myrepository.git $ git


push origin --all

Bazar

Bazaar es una herramienta de DVCS muy parecida a Git y, como resultado, es bastante sencillo convertir un repositorio
de Bazaar en uno de Git. Para lograr esto, deberá importar el complemento bzr-fastimport .

Obtener el complemento bzr-fastimport

El procedimiento para instalar el complemento fastimport es diferente en los sistemas operativos tipo UNIX y en Windows.
En el primer caso, lo más sencillo es instalar el paquete bzr-fastimport que instalará todas las dependencias requeridas.

Por ejemplo, con Debian y derivados, haría lo siguiente:

$ sudo apt-get install bzr-fastimport

Con RHEL, haría lo siguiente:

$ sudo yum install bzr-fastimport

Con Fedora, desde la versión 22, el nuevo administrador de paquetes es dnf:

$ sudo dnf install bzr-fastimport

Si el paquete no está disponible, puede instalarlo como complemento:

$ mkdir --parents ~/.bazaar/plugins # crea las carpetas necesarias para el


complementos $ cd ~/.bazaar/plugins
$ bzr branch lp:bzr-fastimport
fastimport # importa el complemento fastimport $ cd fastimport $ sudo python
setup.py install --record=files. txt # instala el complemento

Para que este complemento funcione, también necesitará el módulo Python fastimport . Puede verificar si está presente
o no e instalarlo con los siguientes comandos:

418
Machine Translated by Google

$ python -c "importar fastimport"


Rastreo (llamadas recientes más última):
Archivo "<cadena>", línea 1, en <módulo>
ImportError: ningún módulo llamado fastimport $
pip install fastimport

Si no está disponible, puede descargarlo en la dirección https://pypi.python.org/pypi/fastimport/.

En el segundo caso (en Windows), bzr-fastimport se instala automáticamente con la versión independiente
y la instalación predeterminada (deje todas las casillas marcadas). Así que en este caso no tienes nada que
hacer.

En este punto, la forma de importar un repositorio de Bazaar difiere según tengas una sola sucursal o estés
trabajando con un repositorio que tiene varias sucursales.

Proyecto con una sola sucursal

Ahora haga cd en el directorio que contiene su repositorio de Bazaar e inicialice el repositorio de Git:

$ cd /ruta/hacia/el/bzr/repositorio $ git init

Ahora, simplemente puede exportar su repositorio Bazaar y convertirlo en un repositorio Git usando el
siguiente comando:

$ bzr exportación rápida --normal . | Importación rápida de Git

Según el tamaño del proyecto, su repositorio de Git se crea en un lapso de unos segundos a unos minutos.

Caso de un proyecto con una rama principal y una rama de trabajo

También puede importar un repositorio de Bazaar que contenga sucursales. Supongamos que tiene dos
ramas: una representa la rama principal (myProject.trunk), la otra es la rama de trabajo (myProject.work).

$ ls
miProyecto.trunk miProyecto.trabajo

Cree el repositorio de Git y cd en él:

$ git init git-repo $ cd git-


repo

Tire de la rama maestra en git:

419
Machine Translated by Google

$ bzr fast-export --export-marks=../marks.bzr ../myProject.trunk | \ git fast-


import --export-marks=../marks.git

Tire de la rama de trabajo en Git:

$ bzr fast-export --marks=../marks.bzr --git-branch=work ../myProject.work | \ git fast-


import --import-marks=../marks.git --export-marks=../marks.git

Ahora git branch te muestra la rama maestra así como la rama de trabajo . Verifique los registros para
asegurarse de que estén completos y elimine los archivos Marks.bzr y Marks.git .

Sincronización del área de ensayo

Cualquiera que sea la cantidad de sucursales que tenía y el método de importación que utilizó, su área de
preparación no está sincronizada con HEAD, y con la importación de varias sucursales, su directorio de trabajo
tampoco está sincronizado. Esta situación se resuelve fácilmente con el siguiente comando:

$ git reset --cabeza dura

Ignorando los archivos que fueron ignorados con .bzrigore

Ahora echemos un vistazo a los archivos para ignorar. Lo primero que debe hacer es cambiar el nombre
de .bzrignore a .gitignore. Si el archivo .bzrignore contiene una o varias líneas que comienzan con "!!" o "RE:",
tendrá que modificarlo y tal vez crear varios archivos .gitignore para ignorar exactamente los mismos archivos
que Bazaar estaba ignorando.

Finalmente, deberá crear un compromiso que contenga esta modificación para la migración:

$ git mv .bzrignore .gitignore $


# modificar .gitignore si es
necesario $ git commit -am 'Migración de Bazaar a Git'

Enviando tu repositorio al servidor

¡Aquí estamos! Ahora puede enviar el repositorio a su nuevo servidor doméstico:

$ git remote add origin git@my-git-server:mygitrepository.git $ git


push origin --all $ git push origin --tags

Su repositorio Git está listo para usar.

420
Machine Translated by Google

Forzosamente

El siguiente sistema desde el que verá la importación es Perforce. Como discutimos anteriormente, hay dos formas de
permitir que Git y Perforce se comuniquen entre sí: git-p4 y Perforce Git Fusion.

Fusión forzada de Git

Git Fusion hace que este proceso sea bastante sencillo. Simplemente configure los ajustes de su proyecto, las
asignaciones de usuarios y las ramas mediante un archivo de configuración (como se explica en Git Fusion) y clone el
repositorio. Git Fusion lo deja con lo que parece ser un repositorio Git nativo, que luego está listo para enviarse a un
host Git nativo si lo desea. Incluso podría usar Perforce como su host Git si lo desea.

Git-p4

Git-p4 también puede actuar como una herramienta de importación. Como ejemplo, importaremos el proyecto Jam
desde Perforce Public Depot. Para configurar su cliente, debe exportar la variable de entorno P4PORT para que
apunte al depósito de Perforce:

$ exportar P4PORT=public.perforce.com:1666

Para seguir, necesitará un depósito de Perforce para conectarse. Usaremos el depósito público
ÿ en public.perforce.com para nuestros ejemplos, pero puede usar cualquier depósito al que tenga
acceso.

Ejecute el comando git p4 clone para importar el proyecto Jam desde el servidor de Perforce, proporcionando la ruta
del almacén y del proyecto y la ruta en la que desea importar el proyecto:

$ git-p4 clone //guest/perforce_software/jam@all p4import


Importación desde //guest/perforce_software/jam@all a p4import
Repositorio Git vacío inicializado en /private/tmp/p4import/.git/ Destino
de importación: refs/remotes/ p4/master Importando revisión 9957 (100%)

Este proyecto en particular tiene solo una rama, pero si tiene ramas que están configuradas con vistas de ramas (o
solo un conjunto de directorios), puede usar el indicador --detect-branches para clonar git p4 para importar todas las
ramas del proyecto también . Consulte Ramificación para obtener más detalles sobre esto.

En este punto ya casi has terminado. Si va al directorio p4import y ejecuta git log, puede ver su trabajo importado:

421
Machine Translated by Google

$ git log -2
confirmar e5da1c909e5db3036475419f6379f2c73710c4e6
Autor: giles <giles@giles@perforce.com> Fecha:
miércoles 8 de febrero 03:13:27 2012 -0800

Corrección a la línea 355; cambie </UL> por </OL>.

[git-p4: rutas de depósito = "//public/jam/src/": cambio = 8068]

cometer aa21359a0a135dda85c50a7f7cf249e4f7b8fd98
Autor: kwirth <kwirth@perforce.com> Fecha: martes 7 de
julio 01:35:51 2009 -0800

Corrija el error de ortografía en la página de documentos de Jam (acumulativo -> acumulativo).

[git-p4: rutas de depósito = "//public/jam/src/": cambio = 7304]

Puedes ver que git-p4 ha dejado un identificador en cada mensaje de confirmación. Está bien mantener ese identificador
allí, en caso de que necesite hacer referencia al número de cambio de Perforce más adelante. Sin embargo, si desea
eliminar el identificador, ahora es el momento de hacerlo, antes de comenzar a trabajar en el nuevo repositorio.
Puede usar git filter-branch para eliminar las cadenas de identificación en masa:

$ git filter-branch --msg-filter 'sed -e "/^\[git-p4:/d"'


Reescribir e5da1c909e5db3036475419f6379f2c73710c4e6 (125/125)
Ref 'refs/heads/master' fue reescrito

Si ejecuta git log, puede ver que todas las sumas de verificación SHA-1 para las confirmaciones han cambiado, pero
las cadenas git-p4 ya no están en los mensajes de confirmación:

$ git log -2
commit b17341801ed838d97f7800a54a6f9b95750839b7
Autor: giles <giles@giles@perforce.com> Fecha:
miércoles 8 de febrero 03:13:27 2012 -0800

Corrección a la línea 355; cambie </UL> por </OL>.

confirmar 3e68c2e26cd89cb983eb52c024ecdfba1d6b3fff
Autor: kwirth <kwirth@perforce.com> Fecha:
martes 7 de julio 01:35:51 2009 -0800

Corrija el error de ortografía en la página de documentos de Jam (acumulativo -> acumulativo).

Su importación está lista para subir a su nuevo servidor Git.

Un importador personalizado

Si su sistema no es uno de los anteriores, debe buscar un importador en línea: los importadores de calidad son

422
Machine Translated by Google

disponible para muchos otros sistemas, incluyendo CVS, Clear Case, Visual Source Safe, incluso un directorio de
archivos. Si ninguna de estas herramientas funciona para usted, tiene una herramienta más oscura o necesita un
proceso de importación más personalizado, debe usar git fast-import. Este comando lee instrucciones simples de
stdin para escribir datos Git específicos. Es mucho más fácil crear objetos de Git de esta manera que ejecutar los
comandos de Git sin formato o intentar escribir los objetos sin formato (consulte Git Internals para obtener más
información). De esta manera, puede escribir un script de importación que lea la información necesaria del sistema
desde el que está importando e imprima instrucciones sencillas en la salida estándar. Luego puede ejecutar este
programa y canalizar su salida a través de git fast-import.

Para demostrarlo rápidamente, escribirá un importador simple. Supongamos que trabaja en actual, hace una copia
de seguridad de su proyecto copiando ocasionalmente el directorio en un directorio de copia de seguridad
back_YYYY_MM_DD con marca de tiempo , y desea importarlo a Git. Su estructura de directorios se ve así:

$ ls /opt/import_from
back_2014_01_02
back_2014_01_04
back_2014_01_14
back_2014_02_03 actual

Para importar un directorio de Git, debe revisar cómo Git almacena sus datos. Como recordará, Git es
fundamentalmente una lista vinculada de objetos de confirmación que apuntan a una instantánea del contenido.
Todo lo que tiene que hacer es decirle a la importación rápida cuáles son las instantáneas de contenido, qué puntos
de datos de confirmación les asignan y el orden en que aparecen. Su estrategia será revisar las instantáneas una
a la vez y crear confirmaciones con el contenido de cada directorio, vinculando cada confirmación con la anterior.

Como hicimos en Ejemplo de política aplicada por Git, escribiremos esto en Ruby, porque es con lo que
generalmente trabajamos y tiende a ser fácil de leer. Puede escribir este ejemplo con bastante facilidad en cualquier
cosa con la que esté familiarizado: solo necesita imprimir la información adecuada en la salida estándar. Y, si está
ejecutando en Windows, esto significa que deberá tener especial cuidado de no introducir retornos de carro al final
de sus líneas: git fast-import es muy particular sobre solo querer saltos de línea (LF) no la línea de retorno de carro
feeds (CRLF) que usa Windows.

Para comenzar, cambiará al directorio de destino e identificará cada subdirectorio, cada uno de los cuales es una
instantánea que desea importar como una confirmación. Cambiará a cada subdirectorio e imprimirá los comandos
necesarios para exportarlo. Su ciclo principal básico se ve así:

423
Machine Translated by Google

última_marca = cero

# recorrer los directorios Dir.chdir(ARGV[0])


do Dir.glob("*").each do |dir| siguiente si
Archivo.archivo?(dir)

# pasar al directorio de destino Dir.chdir(dir) do


last_mark = print_export(dir, last_mark) end end
ÿ

end

Ejecuta print_export dentro de cada directorio, que toma el manifiesto y la marca de la instantánea anterior y devuelve el
manifiesto y la marca de esta; de esa manera, puede vincularlos correctamente.
“Mark” es el término de importación rápida para un identificador que le das a una confirmación; a medida que crea
compromisos, asigna a cada uno una marca que puede usar para vincularlo desde otros compromisos. Entonces, lo
primero que debe hacer en su método print_export es generar una marca del nombre del directorio:

marca = convert_dir_to_mark(dir)

Hará esto creando una matriz de directorios y usando el valor del índice como marca, porque una marca debe ser un
número entero. Su método se ve así:

$marcas = []
def convert_dir_to_mark(dir) if !
$marcas.incluir?(dir) $marcas << dir
end ($marcas.index(dir) + 1).to_s end

Ahora que tiene una representación entera de su compromiso, necesita una fecha para los metadatos del compromiso.
Debido a que la fecha se expresa en el nombre del directorio, la analizará. La siguiente línea en su archivo print_export es:

fecha = convert_dir_to_date(dir)

donde convert_dir_to_date se define como:

424
Machine Translated by Google

def convert_dir_to_date(dir) if dir


== 'actual' return Time.now().to_i
else dir = dir.gsub('back_', '') (año,
mes, día) = dir.split('_') return
Hora.local(año, mes, día).to_i end end

Eso devuelve un valor entero para la fecha de cada directorio. La última metainformación que necesita para cada
confirmación son los datos del confirmador, que codifica en una variable global:

$autor = 'John Doe <john@example.com>'

Ahora está listo para comenzar a imprimir los datos de confirmación para su importador. La información inicial
indica que está definiendo un objeto de confirmación y en qué rama está, seguida de la marca que ha generado,
la información del confirmador y el mensaje de confirmación, y luego la confirmación anterior, si corresponde.
El código se ve así:

# imprime la información de
importación puts 'commit refs/heads/
master' puts 'mark :' + mark puts
"committer #{$author} #{date} -0700"
'
export_data('imported from + dir) puts
última_marca
'from :' +
si última_marca

Codifica la zona horaria (-0700) porque hacerlo es fácil. Si está importando desde otro sistema, debe especificar la
zona horaria como un desplazamiento. El mensaje de confirmación debe expresarse en un formato especial:

datos (tamaño)\n(contenido)

El formato consta de la palabra datos, el tamaño de los datos a leer, una nueva línea y finalmente los datos.
Debido a que necesita usar el mismo formato para especificar el contenido del archivo más adelante, cree un
método auxiliar, export_data:

def exportar_datos(cadena)
imprimir "datos #{cadena.tamaño}\n#{cadena}"
end

Todo lo que queda es especificar el contenido del archivo para cada instantánea. Esto es fácil, porque tiene cada
uno en un directorio: puede imprimir el comando deleteall seguido del contenido de cada archivo en el directorio.
Luego, Git registrará cada instantánea de manera adecuada:

425
Machine Translated by Google

pone 'borrar todo'


Dir.glob("**/*").each do |archivo| next if !
File.file?(file) inline_data(file) end

Nota: Debido a que muchos sistemas piensan en sus revisiones como cambios de una confirmación a otra, la importación
rápida también puede tomar comandos con cada confirmación para especificar qué archivos se agregaron, eliminaron o
modificaron y cuáles son los nuevos contenidos. Podría calcular las diferencias entre las instantáneas y proporcionar solo
estos datos, pero hacerlo es más complejo: también puede proporcionar a Git todos los datos y dejar que lo resuelva. Si
esto se adapta mejor a sus datos, consulte la página del manual de importación rápida para obtener detalles sobre cómo
proporcionar sus datos de esta manera.

El formato para enumerar el contenido del nuevo archivo o especificar un archivo modificado con el nuevo contenido es el
siguiente:

M 644 ruta en línea/a/datos de


archivo (tamaño) (contenido del
archivo)

Aquí, 644 es el modo (si tiene archivos ejecutables, debe detectar y especificar 755 en su lugar), y en línea dice que
enumerará el contenido inmediatamente después de esta línea. Su método inline_data se ve así:

def inline_data(file, code = 'M', mode = '644') content = File.read(file)


puts "#{code} #{mode} inline #{file}" export_data(content) end

Reutiliza el método export_data que definiste anteriormente, porque es igual a la forma en que especificaste los datos de
tu mensaje de confirmación.

Lo último que debe hacer es devolver la marca actual para que pueda pasar a la siguiente iteración:

marca de retorno

Si está ejecutando en Windows, deberá asegurarse de agregar un paso adicional. Como se mencionó
anteriormente, Windows usa CRLF para caracteres de nueva línea, mientras que git fast-import solo
espera LF. Para solucionar este problema y hacer feliz la importación rápida de git , debe decirle a

ÿ Ruby que use LF en lugar de CRLF:

$salida estándar.binmode

426
Machine Translated by Google

Eso es todo. Aquí está el guión en su totalidad:

#!/usr/bin/env rubí

$stdout.binmode
$autor = "John Doe <john@example.com>"

$marcas =
[] def convertir_dir_a_marca(dir)
if !$marcas.incluir?(dir) $marcas
ÿ

<< dir
end
($marcas.index(dir)+1).to_s end

def convert_dir_to_date(dir) if dir


== 'actual'
ÿ

devuelve Time.now().to_i
else
ÿ

dir = dir.gsub('atrás_', '') (año,


ÿ

mes, día) = dir.split('_') return


ÿ

Hora.local(año, mes, día).to_i


final
final

def exportar_datos(cadena)
imprimir "datos #{cadena.tamaño}\n#{cadena}"
end

def inline_data(file, code='M', mode='644') content


= File.read(file) puts "#{code} #{mode} inline #{file}"
export_data(content) end

def print_export(dir, last_mark) date


= convert_dir_to_date(dir) mark =
convert_dir_to_mark(dir)

pone 'commit refs/heads/master' pone


"mark :#{mark}" puts "committer
#{$author} #{date} -0700" export_data("importado de
#{dir}") puts "from :#{ última_marca}" si última_marca

pone 'borrar todo'


Dir.glob("**/*").each do |archivo| next if !
ÿ

File.file?(file) inline_data(file)
ÿ

final

427
Machine Translated by Google

marca
final

# Recorre los directorios last_mark = nil


Dir.chdir(ARGV[0]) do Dir.glob("*").each do
|dir| siguiente si Archivo.archivo?(dir)

# pasar al directorio de destino Dir.chdir(dir)


ÿ

do last_mark = print_export(dir, last_mark)


ÿ

final
final final

Si ejecuta este script, obtendrá contenido similar a este:

$ ruby import.rb /opt/import_from commit refs/


heads/master mark :1 committer John Doe
<john@example.com> 1388649600 -0700
datos 29 importados de back_2014_01_02deleteall M 644 en línea
README.md

datos 28
# Hola

Este es mi Léame.
commit refs/heads/master mark :2
committer John Doe
<john@example.com> 1388822400 -0700 data 29

importado desde back_2014_01_04from :1 deleteall


M 644 inline main.rb data 34

#!/bin/env rubí

pone "Hola"
M 644 en línea LÉAME.md (...)

Para ejecutar el importador, canalice esta salida a través de git fast-import mientras se encuentra en el directorio de Git al
que desea importar. Puede crear un nuevo directorio y luego ejecutar git init en él como punto de partida, y luego ejecutar
su secuencia de comandos:

428
Machine Translated by Google

$ git inicializar
Repositorio Git vacío inicializado en /opt/import_to/.git/
$ ruby import.rb /opt/import_from | Importación rápida de Git
estadísticas de git-fast-import:
-------------------------------------------------- -------------------

Objetos asignados: 5000

Objetos totales: 13 6 duplicados 4 )


ÿ

blobs: (5( duplicados 3 deltas de 5

intentos)
ÿ

árboles : 4( 1 duplica 0 deltas de 4

intentos)
ÿ

comete: 4( 1 duplica 0 deltas de 0


intentos)
ÿ

etiquetas: 0( 0 duplicados 0 deltas de 0


intentos)
Total sucursales: 1 1 cargas )
ÿ

marcas: ( 1024 5 únicas )


ÿ

átomos: (2

Memoria total: 2344 KiB


ÿ

2110 KiB
ÿ

agrupaciones: objetos: 234 KiB


-------------------------------------------------- -------------------

=
pack_report: getpagesize() 4096
paquete_informe: core.packedGitWindowSize = 1073741824
informe_paquete: core.packedGitLimit = 8589934592
= 10
pack_report: pack_used_ctr
= 5
pack_report: pack_mmap_calls
= 2/ 2
pack_report: pack_open_windows
= 1457 / 1457
pack_report: pack_mapped
-------------------------------------------------- -------------------

Como puede ver, cuando se completa con éxito, le brinda un montón de estadísticas sobre lo que
logrado. En este caso, importó 13 objetos en total para 4 confirmaciones en 1 rama. Ahora usted puede
ejecuta git log para ver tu nuevo historial:

$ git registro -2
cometer 3caa046d4aac682a55867132ccdfbe0d3fdee498

Autor: John Doe <john@example.com>


Fecha: martes 29 de julio 19:39:04 2014 -0700

importado de actual

confirmar 4afc2b945d0d3c8cd00556fbe2e8224569dc9def

Autor: John Doe <john@example.com>


Fecha: lun 3 feb 01:00:00 2014 -0700

importado de back_2014_02_03

429
Machine Translated by Google

Ahí lo tienes: un repositorio Git agradable y limpio. Es importante tener en cuenta que nada está desprotegido:
al principio no tiene ningún archivo en su directorio de trabajo. Para obtenerlos, debe restablecer su rama a
donde está el maestro ahora:

$ ls
$ git reset --hard master
HEAD ahora está en 3caa046 importado de $ ls
actual README.md main.rb

Puede hacer mucho más con la herramienta de importación rápida : manejar diferentes modos, datos binarios,
múltiples ramas y fusiones, etiquetas, indicadores de progreso y más. Hay varios ejemplos de escenarios más
complejos disponibles en el directorio contrib/fast-import del código fuente de Git.

Resumen
Debería sentirse cómodo usando Git como cliente para otros sistemas de control de versiones, o importando
casi cualquier repositorio existente a Git sin perder datos. En el próximo capítulo, cubriremos las partes internas
sin procesar de Git para que pueda crear cada byte, si es necesario.

430
Machine Translated by Google

Internos de Git
Es posible que haya saltado a este capítulo desde un capítulo mucho más temprano, o que haya llegado hasta aquí
después de leer secuencialmente el libro completo hasta este punto; en cualquier caso, aquí es donde repasaremos el
funcionamiento interno y la implementación de Git. Descubrimos que comprender esta información era fundamentalmente
importante para apreciar cuán útil y poderoso es Git, pero otros nos han argumentado que puede ser confuso e
innecesariamente complejo para los principiantes. Por lo tanto, hemos hecho de esta discusión el último capítulo del
libro para que pueda leerlo temprano o más tarde en su proceso de aprendizaje. Te dejamos a ti decidir.

Ahora que estás aquí, comencemos. Primero, si aún no está claro, Git es fundamentalmente un sistema de archivos de
contenido direccionable con una interfaz de usuario de VCS escrita encima. Aprenderá más sobre lo que esto significa
en un momento.

En los primeros días de Git (principalmente antes de 1.5), la interfaz de usuario era mucho más compleja porque
enfatizaba este sistema de archivos en lugar de un VCS pulido. En los últimos años, la interfaz de usuario se ha refinado
hasta que es tan limpia y fácil de usar como cualquier otro sistema; sin embargo, persiste el estereotipo sobre la primera
interfaz de usuario de Git que era compleja y difícil de aprender.

La capa del sistema de archivos direccionable por contenido es asombrosamente genial, así que lo cubriremos primero
en este capítulo; luego, aprenderá sobre los mecanismos de transporte y las tareas de mantenimiento del repositorio
con las que eventualmente tendrá que lidiar.

Fontanería y Porcelanato
Este libro cubre principalmente cómo usar Git con más o menos 30 subcomandos, como pago, sucursal, control remoto,
etc. Pero debido a que Git fue inicialmente un conjunto de herramientas para un sistema de control de versiones en
lugar de un VCS completo y fácil de usar, tiene una serie de subcomandos que realizan trabajos de bajo nivel y fueron
diseñados para encadenarse al estilo UNIX o llamarse desde scripts. Estos comandos generalmente se denominan
comandos de "plomería" de Git, mientras que los comandos más fáciles de usar se denominan comandos de "porcelana".

Como ya habrá notado, los primeros nueve capítulos de este libro tratan casi exclusivamente de los comandos de
porcelana. Pero en este capítulo, se ocupará principalmente de los comandos de plomería de nivel inferior, porque le
dan acceso al funcionamiento interno de Git y ayudan a demostrar cómo y por qué Git hace lo que hace. Muchos de
estos comandos no están pensados para usarse manualmente en la línea de comandos, sino como bloques de
construcción para nuevas herramientas y scripts personalizados.

Cuando ejecuta git init en un directorio nuevo o existente, Git crea el directorio .git , que es donde se encuentra casi todo
lo que Git almacena y manipula. Si desea hacer una copia de seguridad o clonar su repositorio, copiar este único
directorio en otro lugar le brinda casi todo lo que necesita. Todo este capítulo trata básicamente de lo que puede ver en
este directorio. Este es el aspecto típico de un directorio .git recién inicializado :

431
Machine Translated by Google

$ ls -F1

descripción
de configuración

HEAD

ganchos/
info/ objetos/ refs/

Dependiendo de su versión de Git, es posible que vea algún contenido adicional allí, pero este es un repositorio nuevo de git
init : es lo que ve de manera predeterminada. El archivo de descripción solo lo usa el programa GitWeb, así que no se preocupe.
El archivo de configuración contiene las opciones de configuración específicas de su proyecto, y el directorio de información
mantiene un archivo de exclusión global para los patrones ignorados que no desea rastrear en un archivo .gitignore . El directorio
de ganchos contiene los scripts de gancho del lado del cliente o del servidor, que se analizan en detalle en Git Hooks.

Esto deja cuatro entradas importantes: el HEAD y los archivos de índice (aún por crear) , y los directorios de objetos y
referencias . Estas son las partes principales de Git. El directorio de objetos almacena todo el contenido de su base de datos, el
directorio de refs almacena punteros en objetos de confirmación en esos datos (ramas, etiquetas, controles remotos y más), el
archivo HEAD apunta a la rama que actualmente ha verificado y el archivo de índice es donde Git almacena la información del
área de ensayo. Ahora verá cada una de estas secciones en detalle para ver cómo funciona Git.

Objetos Git
Git es un sistema de archivos direccionable por contenido. Genial. ¿Que significa eso? Significa que en el núcleo de Git hay un
simple almacén de datos de clave-valor. Lo que esto significa es que puede insertar cualquier tipo de contenido en un repositorio
de Git, para lo cual Git le devolverá una clave única que puede usar más adelante para recuperar ese
contenido.

Como demostración, veamos el comando de plomería git hash-object, que toma algunos datos, los almacena en su directorio .git/
objects (la base de datos de objetos) y le devuelve la clave única que ahora se refiere a ese objeto de datos.

Primero, inicializas un nuevo repositorio de Git y verificas que (como era de esperar) no hay nada en el directorio de objetos :

$ git init test


Repositorio Git vacío inicializado en /tmp/test/.git/ $ cd test $
find .git/objects .git/objects .git/objects/info .git/objects/pack $
find .git/objects -type F

Git ha inicializado el directorio de objetos y ha creado subdirectorios de paquete e información en él, pero hay

432
Machine Translated by Google

no hay archivos regulares. Ahora, usemos git hash-object para crear un nuevo objeto de datos y almacenarlo manualmente en
su nueva base de datos de Git:

$ echo 'contenido de prueba' | git hash-objeto -w --stdin


d670460b4b4aece5915caf5c68d12f560a9fe3e4

En su forma más simple, git hash-object tomaría el contenido que le entregó y simplemente devolvería la clave única que se
usaría para almacenarlo en su base de datos de Git. La opción -w luego le dice al comando que no solo devuelva la clave, sino
que escriba ese objeto en la base de datos. Finalmente, la opción --stdin le dice a git hash-object que obtenga el contenido para
ser procesado desde stdin; de lo contrario, el comando esperaría un argumento de nombre de archivo al final del comando que
contiene el contenido que se utilizará.

El resultado del comando anterior es un hash de suma de comprobación de 40 caracteres. Este es el hash SHA-1: una suma de
verificación del contenido que está almacenando más un encabezado, del que aprenderá en un momento. Ahora puedes ver
cómo Git ha almacenado tus datos:

$ buscar .git/objects -type f .git/


objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4

Si vuelve a examinar su directorio de objetos , puede ver que ahora contiene un archivo para ese nuevo contenido. Así es como
Git almacena el contenido inicialmente: como un solo archivo por pieza de contenido, nombrado con la suma de verificación
SHA-1 del contenido y su encabezado. El subdirectorio se nombra con los primeros 2 caracteres del SHA-1 y el nombre del
archivo son los 38 caracteres restantes.

Una vez que tenga contenido en su base de datos de objetos, puede examinar ese contenido con el comando git cat-file . Este
comando es una especie de navaja suiza para inspeccionar objetos de Git. Pasar -p al archivo cat le indica al comando que
primero averigüe el tipo de contenido y luego lo muestre de manera adecuada:

$ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4


contenido de prueba

Ahora, puede agregar contenido a Git y volver a extraerlo. También puede hacer esto con el contenido de los archivos.
Por ejemplo, puede hacer un control de versión simple en un archivo. Primero, cree un nuevo archivo y guarde su contenido en
su base de datos:

$ echo 'versión 1' > prueba.txt $


git hash-object -w prueba.txt
83baae61804e65cc73a7201a7252750c76066a30

Luego, escribe algo de contenido nuevo en el archivo y guárdalo de nuevo:

$ echo 'versión 2' > prueba.txt $


git hash-object -w prueba.txt
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a

433
Machine Translated by Google

Su base de datos de objetos ahora contiene ambas versiones de este nuevo archivo (así como el primer contenido
que almacenó allí):

$ buscar .git/objects -type f .git/objects/


1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a .git/objects/83/
baae61804e65cc73a7201a7252750c76066a30 .git/objects/
d6/70460b4b4aece5c580dcaf1

En este punto, puede eliminar su copia local de ese archivo test.txt , luego usar Git para recuperar, desde la base de
datos de objetos, la primera versión que guardó:

$ git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30 > test.txt $ cat test.txt versión 1

o la segunda versión:

$ git cat-file -p 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a > test.txt $ cat test.txt versión 2

Pero recordar la clave SHA-1 para cada versión de su archivo no es práctico; además, no está almacenando el nombre
del archivo en su sistema, solo el contenido. Este tipo de objeto se denomina blob. Puede hacer que Git le diga el tipo
de objeto de cualquier objeto en Git, dada su clave SHA-1, con git cat-file -t:

$ git gato-archivo -t 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a blob

Objetos de árbol

El siguiente tipo de objeto de Git que examinaremos es el árbol, que resuelve el problema de almacenar el nombre del
archivo y también le permite almacenar un grupo de archivos juntos. Git almacena contenido de manera similar a un
sistema de archivos UNIX, pero un poco simplificado. Todo el contenido se almacena como objetos de árbol y blob,
con árboles correspondientes a entradas de directorio de UNIX y blobs correspondientes más o menos a inodos o
contenido de archivos. Un solo objeto de árbol contiene una o más entradas, cada una de las cuales es el hash SHA-1
de un blob o subárbol con su modo, tipo y nombre de archivo asociados. Por ejemplo, supongamos que tiene un
proyecto en el que el árbol más reciente se ve así:

$ git cat-file -p maestro^{árbol} 100644 blob


a906cb2a4a904a152e80877d4088654daad0c859 LÉAME
100644 gota 8f94139338f9404f26296befa88755fc2598c289 040000 árbol Biblioteca de archivos

99f1a6d12cb4b6f19c8655fca46c3ecf317074e0 de rake

La sintaxis master^{tree} especifica el objeto de árbol al que apunta la última confirmación en su

434
Machine Translated by Google

rama maestra . Tenga en cuenta que el subdirectorio lib no es un blob sino un puntero a otro árbol:

$ git cat-file -p 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0


100644 blob 47c6340d6459e05787f644c2447d2595f5d3a54b simplegit.rb

Según el shell que utilice, es posible que encuentre errores al utilizar la sintaxis master^{tree} .

En CMD en Windows, el carácter ^ se usa para escapar, por lo que debe duplicarlo para

ÿ evitar esto: git cat-file -p master^^{tree}. Al usar PowerShell, los parámetros que usan {}
caracteres deben citarse para evitar que el parámetro se analice incorrectamente: git cat-file
-p 'master^{tree}'.

Si usa ZSH, el carácter ^ se usa para englobar, por lo que debe incluir la expresión completa
entre comillas: git cat-file -p "master^{tree}".

Conceptualmente, los datos que almacena Git se ven así:

Figura 147. Versión simple del modelo de datos de Git

Puede crear fácilmente su propio árbol. Git normalmente crea un árbol tomando el estado de su área de
preparación o índice y escribiendo una serie de objetos de árbol a partir de él. Por lo tanto, para crear un objeto
de árbol, primero debe configurar un índice organizando algunos archivos. Para crear un índice con una sola
entrada, la primera versión de su archivo test.txt , puede usar el comando de plomería git update-index. usas esto

435
Machine Translated by Google

comando para agregar artificialmente la versión anterior del archivo test.txt a una nueva área de ensayo. Debe pasarle la opción --
add porque el archivo aún no existe en su área de preparación (todavía no tiene configurada un área de preparación) y --cacheinfo
porque el archivo que está agregando no está en su directorio pero está en su base de datos. Luego, especifica el modo, SHA-1 y
el nombre del archivo:

$ git update-index --add --cacheinfo 100644 \


83baae61804e65cc73a7201a7252750c76066a30 prueba.txt

En este caso, está especificando un modo de 100644, lo que significa que es un archivo normal. Otras opciones son 100755, lo
que significa que es un archivo ejecutable; y 120000, que especifica un enlace simbólico. El modo se toma de los modos UNIX
normales pero es mucho menos flexible: estos tres modos son los únicos que son válidos para archivos (blobs) en Git (aunque se
usan otros modos para directorios y submódulos).

Ahora, puede usar git write-tree para escribir el área de preparación en un objeto de árbol. No se necesita la opción -w : llamar a
este comando crea automáticamente un objeto de árbol a partir del estado del índice si ese árbol aún no existe:

$ git escribir-árbol
d8329fc1cc938780ffdd9f94e0d364e0ea74f579
$ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579 100644 blob
83baae61804e65cc73a7201a7252750c76066a30 prueba.txt

También puede verificar que este es un objeto de árbol usando el mismo comando git cat-file que vio anteriormente:

$ git gato-archivo -t d8329fc1cc938780ffdd9f94e0d364e0ea74f579 árbol

Ahora creará un nuevo árbol con la segunda versión de test.txt y también un nuevo archivo:

$ echo 'nuevo archivo' > nuevo.txt $


git update-index --add --cacheinfo 100644 \
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt $ git update-
index --add new.txt

Su área de preparación ahora tiene la nueva versión de test.txt , así como el nuevo archivo new.txt. Escriba ese árbol (registrando
el estado del área de preparación o el índice de un objeto de árbol) y vea cómo se ve:

$ git write-tree
0155eb4229851634a0f03eb265b69f5a2d56f341 $ git
cat-file -p 0155eb4229851634a0f03eb265b69f5a2d56f341 100644 blob
fa49b077972391ad58037051e4e2a3 nuevo.txt
100644 gota 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a prueba.txt

436
Machine Translated by Google

Tenga en cuenta que este árbol tiene ambas entradas de archivo y también que test.txt SHA-1 es la "versión 2" SHA-1

de antes (1f7a7a). Solo por diversión, agregará el primer árbol como un subdirectorio en este. Puede leer árboles en su área de
preparación llamando a git read-tree. En este caso, puede leer un árbol existente en su área de preparación como un subárbol
usando la opción --prefix con este comando:

$ git read-tree --prefix=bak d8329fc1cc938780ffdd9f94e0d364e0ea74f579 $ git write-tree


3c4e9cd789d88d8d89c1073707c3585e41b0e614

$ Git -p-cat archivo 3c4e9cd789d88d8d89c1073707c3585e41b0e614 040000


árbol d8329fc1cc938780ffdd9f94e0d364e0ea74f579 bak 100644 burbuja
fa49b077972391ad58037050f2a75f74e3671e92 nuevo.txt
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a 100.644 blob prueba.txt

Si creó un directorio de trabajo a partir del nuevo árbol que acaba de escribir, obtendría los dos archivos en el nivel superior del
directorio de trabajo y un subdirectorio llamado bak que contenía la primera versión del archivo test.txt . Puede pensar en los datos
que contiene Git para estas estructuras de la siguiente manera:

Figura 148. La estructura de contenido de sus datos Git actuales

Confirmar objetos

Si ha hecho todo lo anterior, ahora tiene tres árboles que representan las diferentes instantáneas de su proyecto que desea rastrear,
pero el problema anterior persiste: debe recordar los tres valores SHA-1 para recuperar las instantáneas. . Tampoco tienes
información sobre quién guardó

437
Machine Translated by Google

las instantáneas, cuándo se guardaron o por qué se guardaron. Esta es la información básica que el objeto de confirmación
almacena para usted.

Para crear un objeto de confirmación, llame a commit-tree y especifique un solo árbol SHA-1 y qué objetos de confirmación,
si los hay, lo precedieron directamente. Comienza con el primer árbol que escribiste:

$ echo 'Primera confirmación' | git commit-tree d8329f


fdf4fc3344e67ab068f836878b6c4951e3b15f3d

Obtendrá un valor hash diferente debido a la hora de creación y los datos de autor diferentes. Además,
aunque en principio cualquier objeto de confirmación puede reproducirse con precisión dados esos

ÿ datos, los detalles históricos de la construcción de este libro significan que los hashes de confirmación
impresos pueden no corresponder a las confirmaciones dadas. Reemplace los hashes de confirmación
y etiquetas con sus propias sumas de verificación más adelante en este capítulo.

Ahora puedes ver tu nuevo objeto de confirmación con git cat-file:

$ git cat-file -p fdf4fc3 árbol


d8329fc1cc938780ffdd9f94e0d364e0ea74f579 autor
Scott Chacon <schacon@gmail.com> 1243040974 -0700 autor
Scott Chacon <schacon@gmail.com> 1243040974 -0700

Primera confirmación

El formato de un objeto de confirmación es simple: especifica el árbol de nivel superior para la instantánea del proyecto en
ese punto; el padre confirma, si lo hay (el objeto de confirmación descrito anteriormente no tiene ningún padre); la información
del autor/autor (que usa su nombre de usuario y los ajustes de configuración de correo electrónico de usuario y una marca
de tiempo); una línea en blanco y luego el mensaje de confirmación.

A continuación, escribirá los otros dos objetos de confirmación, cada uno de los cuales hará referencia a la confirmación
anterior:

$ echo 'Segunda confirmación' | git commit-tree 0155eb -p fdf4fc3


cac0cab538b970a37ea1e769cbbde608743bc96d $ echo 'Tercera
confirmación' | git commit-tree 3c4e9c -p cac0cab
1a410efbd13591db07496601ebc7a059dd55cfe9

Cada uno de los tres objetos de confirmación apunta a uno de los tres árboles de instantáneas que creó. Por extraño que
parezca, ahora tiene un historial de Git real que puede ver con el comando de registro de git , si lo ejecuta en la última
confirmación SHA-1:

438
Machine Translated by Google

$ git log --stat 1a410e


compromiso 1a410efbd13591db07496601ebc7a059dd55cfe9
Autor: Scott Chacon <schacon@gmail.com> Fecha:
viernes 22 de mayo 18:15:24 2009 -0700

Tercer compromiso

bak/prueba.txt | 1 1+
archivo cambiado, 1 inserción (+)

commit cac0cab538b970a37ea1e769cbbde608743bc96d Autor:


Scott Chacon <schacon@gmail.com> Fecha: Vie 22 de mayo
18:14:29 2009 -0700

Segunda confirmación

nuevo.txt | 1 +
prueba.txt | 2 +- 2
archivos cambiados, 2 inserciones (+), 1 borrado (-)

confirmar fdf4fc3344e67ab068f836878b6c4951e3b15f3d
Autor: Scott Chacon <schacon@gmail.com> Fecha:
viernes 22 de mayo 18:09:34 2009 -0700

Primera confirmación

prueba.txt | 1 + 1
archivo cambiado, 1 inserción (+)

Asombroso. Acaba de realizar las operaciones de bajo nivel para crear un historial de Git sin usar ninguno de los
comandos de front-end. Esto es esencialmente lo que hace Git cuando ejecuta los comandos git add y git
commit : almacena blobs para los archivos que han cambiado, actualiza el índice, escribe árboles y escribe
objetos de confirmación que hacen referencia a los árboles de nivel superior y las confirmaciones que vino
inmediatamente antes que ellos. Estos tres objetos principales de Git (el blob, el árbol y la confirmación) se
almacenan inicialmente como archivos separados en su directorio .git/objects . Aquí están todos los objetos en
el directorio de ejemplo ahora, comentados con lo que almacenan:

$ Hallazgo .git / objetos de tipo


f .git / objetos / 01 / 55eb4229851634a0f03eb265b69f5a2d56f341 # 2 del árbol
de .git / objetos / 1A / 410efbd13591db07496601ebc7a059dd55cfe9 # 3 cometer .git /
objetos / 1f / 7a7a472abf3dd9643fd615f6da379c4acb3e3a # test.txt v2 .git / objetos / 3c /
4e9cd789d88d8d89c1073707c3585e41b0e614 # 3 del árbol .git / objetos / 83 /
baae61804e65cc73a7201a7252750c76066a30 # test.txt v1 .git / objetos / ca /
c0cab538b970a37ea1e769cbbde608743bc96d # 2 se comprometen .git / objetos / D6 /
70460b4b4aece5915caf5c68d12f560a9fe3e4 # 'contenido de la prueba' .git / objetos / d8 /
329fc1cc938780ffdd9f94e0d364e0ea74f579 # árbol 1 .git/objects/fa/
49b077972391ad58037050f2a75f74e3671e92 # new.txt .git/objects/fd/
f4fc3344e67ab068f836878b6c4951e3b15f3d # compromiso 1

439
Machine Translated by Google

Si sigue todos los punteros internos, obtendrá un gráfico de objetos como este:

Figura 149. Todos los objetos accesibles en su directorio Git

Almacenamiento de objetos

Mencionamos anteriormente que hay un encabezado almacenado con cada objeto que envías a tu base de datos
de objetos de Git. Tomemos un minuto para ver cómo Git almacena sus objetos. Verá cómo almacenar un objeto
blob, en este caso, la cadena "¿qué pasa, doctor?" — de forma interactiva en el lenguaje de programación Ruby.

Puede iniciar el modo Ruby interactivo con el comando irb :

$ irb
>> content = "¿qué pasa, doctor?"
=> "¿Qué pasa, doctor?"

Git primero construye un encabezado que comienza identificando el tipo de objeto, en este caso, un blob. A esa
primera parte del encabezado, Git agrega un espacio seguido del tamaño en bytes del contenido, y agrega un byte
nulo final:

>> encabezado = "blob #{contenido.bytesize}


\0" => "blob 16\u0000"

440
Machine Translated by Google

Git concatena el encabezado y el contenido original y luego calcula la suma de verificación SHA-1 de
ese nuevo contenido. Puede calcular el valor SHA-1 de una cadena en Ruby al incluir la biblioteca de
resumen SHA1 con el comando require y luego llamar a Digest::SHA1.hexdigest() con la cadena:

>> tienda = encabezado +


contenido => "blob 16\u0000¿qué pasa,
doctor?" >> requiere 'resumen/sha1'
=> cierto
>> sha1 = Resumen::SHA1.hexdigest(tienda) =>
"bd9dbf5aae1a3862dd1526723246b20206e5fc37"

Comparemos eso con la salida de git hash-object. Aquí usamos echo -n para evitar agregar una nueva
línea a la entrada.

$ echo -n "¿Qué pasa, doctor?" | git hash-objeto --stdin


bd9dbf5aae1a3862dd1526723246b20206e5fc37

Git comprime el nuevo contenido con zlib, lo que puede hacer en Ruby con la biblioteca zlib. Primero,
debe solicitar la biblioteca y luego ejecutar Zlib::Deflate.deflate() en el contenido:

>> requiere 'zlib' =>


verdadero >>
zlib_content = Zlib::Deflate.deflate(store) =>
"x\x9CK\xCA\xC9OR04c(\xCFH,Q\xC8,V(-\xD0QH\xC9O\xB6\ a\x00_\x1C\a\x9D"

Finalmente, escribirá su contenido desinflado de zlib en un objeto en el disco. Determinará la ruta del
objeto que desea escribir (los primeros dos caracteres del valor SHA-1 son el nombre del subdirectorio
y los últimos 38 caracteres son el nombre del archivo dentro de ese directorio). En Ruby, puede usar la
función FileUtils.mkdir_p() para crear el subdirectorio si no existe. Luego, abra el archivo con File.open()
y escriba el contenido previamente comprimido con zlib en el archivo con una llamada write() en el
identificador de archivo resultante:

>> ruta = '.git/objects/' + sha1[0,2] + '/' + sha1[2,38] => ".git/objects/bd/


9dbf5aae1a3862dd1526723246b20206e5fc37" >> requiere 'fileutils' =>
verdadero >> FileUtils.mkdir_p(File.dirname(ruta)) => ".git/objects/bd"

>> Archivo.abrir(ruta, 'w') { |f| f.escribir zlib_content } => 32

Verifiquemos el contenido del objeto usando git cat-file:

441
Machine Translated by Google

---

$ git cat-file -p bd9dbf5aae1a3862dd1526723246b20206e5fc37 ¿qué pasa, doctor?

---

Eso es todo: ha creado un objeto blob de Git válido.

Todos los objetos de Git se almacenan de la misma manera, solo que con diferentes tipos: en lugar del blob de cadena, el
encabezado comenzará con confirmación o árbol. Además, aunque el contenido del blob puede ser casi cualquier cosa, el
contenido del árbol y la confirmación tienen un formato muy específico.

Referencias Git
Si estuviera interesado en ver el historial de su repositorio al que se puede acceder desde la confirmación, digamos,
1a410e, podría ejecutar algo como git log 1a410e para mostrar ese historial, pero aún tendría que recordar que 1a410e es
la confirmación que desea usar como el punto de partida de esa historia. En cambio, sería más fácil si tuviera un archivo
en el que pudiera almacenar ese valor SHA-1 con un nombre simple para poder usar ese nombre simple en lugar del valor
SHA-1 sin procesar.

En Git, estos nombres simples se denominan "referencias" o "refs"; puede encontrar los archivos que contienen esos
valores SHA-1 en el directorio .git/refs . En el proyecto actual, este directorio no contiene archivos, pero contiene una
estructura simple:

$ buscar .git/refs .git/


refs .git/refs/heads .git/
refs/tags $ buscar .git/
refs -type f

Para crear una nueva referencia que te ayude a recordar dónde está tu última confirmación, técnicamente puedes hacer
algo tan simple como esto:

$ echo 1a410efbd13591db07496601ebc7a059dd55cfe9 > .git/refs/heads/master

Ahora, puede usar la referencia principal que acaba de crear en lugar del valor SHA-1 en sus comandos de Git:

$ git log --pretty=oneline master


1a410efbd13591db07496601ebc7a059dd55cfe9 Tercera confirmación
cac0cab538b970a37ea1e769cbbde608743bc96d Segunda confirmación
fdf4fc3344e67ab068f836878b6c4951e3b15f3d Primera confirmación

No se recomienda que edite directamente los archivos de referencia; en su lugar, Git proporciona el comando más seguro
git update-ref para hacer esto si desea actualizar una referencia:

442
Machine Translated by Google

$ git update-ref referencias/cabezas/maestro 1a410efbd13591db07496601ebc7a059dd55cfe9

Eso es básicamente lo que es una rama en Git: un simple puntero o referencia al encabezado de una línea de trabajo.
Para volver a crear una rama en la segunda confirmación, puede hacer esto:

$ git update-ref referencias/cabezas/prueba cac0ca

Su rama contendrá solo el trabajo de ese compromiso hacia abajo:

$ git log --pretty=oneline test


cac0cab538b970a37ea1e769cbbde608743bc96d Segunda confirmación
fdf4fc3344e67ab068f836878b6c4951e3b15f3d Primera confirmación

Ahora, su base de datos de Git conceptualmente se parece a esto:

Figura 150. Objetos de directorio de Git con referencias de cabecera de rama incluidas

Cuando ejecuta comandos como git branch <branch>, Git básicamente ejecuta ese comando update-ref para agregar el
SHA-1 de la última confirmación de la rama en la que se encuentra en cualquier referencia nueva que desee
crear.

La cabeza

La pregunta ahora es, cuando ejecutas git branch <branch>, ¿cómo sabe Git el SHA-1 de la última confirmación? La
respuesta es el archivo HEAD.

Por lo general, el archivo HEAD es una referencia simbólica a la rama en la que se encuentra actualmente. Por referencia
simbólica, queremos decir que, a diferencia de una referencia normal, contiene un puntero a otra referencia.

443
Machine Translated by Google

Sin embargo, en algunos casos excepcionales, el archivo HEAD puede contener el valor SHA-1 de un objeto git. Esto sucede cuando revisa
una etiqueta, una confirmación o una rama remota, lo que pone su repositorio en "separado".
CABEZA" estado.

Si observa el archivo, normalmente verá algo como esto:

$ gato .git/HEAD
ref: refs/heads/master

Si ejecuta la prueba de pago de git, Git actualiza el archivo para que se vea así:

$ cat .git/HEAD ref:


referencias/cabezas/prueba

Cuando ejecuta git commit, crea el objeto de confirmación, especificando que el padre de ese objeto de confirmación sea cualquier valor
SHA-1 al que apunte la referencia en HEAD.

También puede editar manualmente este archivo, pero nuevamente existe un comando más seguro para hacerlo: git simbólico-ref.
Puedes leer el valor de tu HEAD a través de este comando:

$ gitsimbolic-ref HEAD
refs/heads/master

También puede establecer el valor de HEAD usando el mismo comando:

$ git simbólico-ref HEAD refs/jefes/prueba


$ cat .git/HEAD ref: refs/jefes/prueba

No puede establecer una referencia simbólica fuera del estilo refs:

$ gitsimbolic-ref HEAD test


fatal: Negarse a apuntar HEAD fuera de refs/

Etiquetas

Acabamos de hablar sobre los tres tipos de objetos principales de Git (blobs, árboles y confirmaciones), pero hay un cuarto. El objeto de
etiqueta es muy parecido a un objeto de confirmación: contiene un etiquetador, una fecha, un mensaje y un puntero. La principal diferencia
es que un objeto de etiqueta generalmente apunta a una confirmación en lugar de a un árbol. Es como una referencia a una rama, pero
nunca se mueve; siempre apunta a la misma confirmación pero le da un nombre más amigable.

Como se discutió en Conceptos básicos de Git, hay dos tipos de etiquetas: anotadas y ligeras. Puede hacer una etiqueta ligera ejecutando
algo como esto:

444
Machine Translated by Google

$ git update-ref referencias/etiquetas/v1.0 cac0cab538b970a37ea1e769cbbde608743bc96d

Eso es todo lo que es una etiqueta ligera: una referencia que nunca se mueve. Sin embargo, una etiqueta anotada es más
compleja. Si crea una etiqueta anotada, Git crea un objeto de etiqueta y luego escribe una referencia para señalarlo en
lugar de apuntar directamente a la confirmación. Puede ver esto creando una etiqueta anotada (usando la opción -a ):

$ git tag -a v1.1 1a410efbd13591db07496601ebc7a059dd55cfe9 -m 'Etiqueta de prueba'

Aquí está el valor del objeto SHA-1 que creó:

$ gato .git/refs/tags/v1.1
9585191f37f7b0fb9444f35a9bf50de191beadc2

Ahora, ejecute git cat-file -p en ese valor SHA-1:

$ git cat-file -p 9585191f37f7b0fb9444f35a9bf50de191beadc2 object


1a410efbd13591db07496601ebc7a059dd55cfe9 type commit tag v1.1
tagger Scott Chacon <schacon@gmail.com> Sábado 23 de mayo 16:48:58
2009 -07009

Etiqueta de prueba

Observe que la entrada del objeto apunta al valor SHA-1 de confirmación que etiquetó. También tenga en cuenta que no
necesita apuntar a una confirmación; puede etiquetar cualquier objeto de Git. En el código fuente de Git, por ejemplo, el
mantenedor agregó su clave pública GPG como un objeto blob y luego lo etiquetó. Puede ver la clave pública ejecutando
esto en un clon del repositorio de Git:

$ git cat-file blob junio-gpg-pub

El repositorio del kernel de Linux también tiene un objeto de etiqueta que no apunta a confirmación: la primera etiqueta
creada apunta al árbol inicial de la importación del código fuente.

Controles remotos

El tercer tipo de referencia que verá es una referencia remota. Si agrega un control remoto y lo presiona, Git almacena el
valor que envió por última vez a ese control remoto para cada rama en el directorio refs/remotes .
Por ejemplo, puede agregar un origen remoto llamado y enviarle su rama maestra :

445
Machine Translated by Google

$ git remote add origin git@github.com:schacon/simplegit-progit.git $ git push


origin master Contando objetos: 11, listo.

Comprimir objetos: 100% (5/5), listo.


Objetos de escritura: 100% (7/7), 716 bytes, listo.
Total 7 (delta 2), reutilizados 4 (delta 1)
Para git@github.com:schacon/simplegit-progit.git
a11bef0..ca82a6d maestro -> maestro

Luego, puede ver cuál era la rama maestra en el control remoto de origen la última vez que se comunicó con el servidor,
revisando el archivo refs/remotes/origin/master :

$ gato .git/refs/remotes/origin/master
ca82a6dff817ec66f44342007202690a93763949

Las referencias remotas se diferencian de las ramas (referencias de referencias/cabeceras ) principalmente en que se
consideran de solo lectura. Puede hacer git checkout a uno, pero Git no hará referencia simbólica a HEAD a uno, por lo
que nunca lo actualizará con un comando de confirmación . Git los administra como marcadores del último estado
conocido de dónde estaban esas sucursales en esos servidores.

Paquetes de archivos

Si siguió todas las instrucciones del ejemplo de la sección anterior, ahora debería tener un repositorio Git de prueba con
11 objetos: cuatro blobs, tres árboles, tres confirmaciones y una etiqueta:

$ Hallazgo .git / objetos de tipo


f .git / objetos / 01 / 55eb4229851634a0f03eb265b69f5a2d56f341 # 2 del
árbol de .git / objetos / 1A / 410efbd13591db07496601ebc7a059dd55cfe9 #
3 cometer .git / objetos / 1f / 7a7a472abf3dd9643fd615f6da379c4acb3e3a #
test.txt v2 .git / objetos / 3c prueba / 4e9cd789d88d8d89c1073707c3585e41b0e614
# 3 del árbol .git / objetos / 83 / baae61804e65cc73a7201a7252750c76066a30
# test.txt v1 .git / objetos / 95 / 85191f37f7b0fb9444f35a9bf50de191beadc2 tag
# .git / objetos / CA / c0cab538b970a37ea1e769cbbde608743bc96d # 2
cometer .git / objetos / D6 / 70460b4b4aece5915caf5c68d12f560a9fe3e4 #'
Contenido '.git / objetos / d8 / 329fc1cc938780ffdd9f94e0d364e0ea74f579 # tree
1 .git / objetos / fa / 49b07797239ad58037050f2a75f74e3671e92 # new.txt .git /
objetos / fd / f4fc3344e67ab068f836878b6c4951e3b15f3D # commit 1

Git comprime el contenido de estos archivos con zlib, y no está almacenando mucho, por lo que todos estos archivos en
conjunto ocupan solo 925 bytes. Ahora agregará contenido más considerable al repositorio para demostrar una
característica interesante de Git. Para demostrarlo, agregaremos el archivo repo.rb de la biblioteca Grit; se trata de un
archivo de código fuente de 22K:

446
Machine Translated by Google

$ curl https://raw.githubusercontent.com/mojombo/grit/master/lib/grit/repo.rb > repo.rb $ git checkout


master $ git add repo.rb $ git commit -m 'Crear repo.rb' [maestro 484a592] Crear repo.rb 3 archivos
cambiados, 709 inserciones (+), 2 eliminaciones (-) modo de eliminación 100644 bak/test.txt modo de
creación 100644 repo.rb reescritura test.txt (100%)

Si observa el árbol resultante, puede ver el valor SHA-1 que se calculó para su nuevo objeto de blob repo.rb :

$ git cat-file -p maestro^{árbol} 100644


blob fa49b077972391ad58037050f2a75f74e3671e92 nuevo.txt
100644 blob 033b4468fa6b2a9547a70d88d1bbe8bf3f9ed0d5 100644 repo.rb
blob e3f094f522629ae358806b17daf78246c27c007b prueba.txt

Luego puede usar git cat-file para ver qué tan grande es ese objeto:

$ git gato-archivo -s 033b4468fa6b2a9547a70d88d1bbe8bf3f9ed0d5 22044

En este punto, modifique un poco ese archivo y vea qué sucede:

$ echo '# testing' >> repo.rb $ git


commit -am 'Modificar repo.rb un poco' [master
2431da6] Modificar repo.rb un poco 1 archivo
cambiado, 1 inserción (+)

Revisa el árbol creado por la última confirmación y verás algo interesante:

$ git cat-file -p master ^ {árbol} 100644


blob fa49b07772391ad58037050f2a75f74e3671e92 100644 blob nuevo.txt
b042a60ef7dff760008df33cee372b945b6e884e 100644 blob repo.rb
e3f094f522629ae358806b17daf78246c27c007b prueba.txt

El blob ahora es un blob diferente, lo que significa que aunque agregó solo una línea al final de un archivo de 400
líneas, Git almacenó ese contenido nuevo como un objeto completamente nuevo:

$ git gato-archivo -s b042a60ef7dff760008df33cee372b945b6e884e 22054

447
Machine Translated by Google

Tiene dos objetos de 22K casi idénticos en su disco (cada uno comprimido a aproximadamente 7K).
¿No sería bueno si Git pudiera almacenar uno de ellos en su totalidad, pero luego el segundo objeto solo como el delta
entre él y el primero?

Resulta que puede. El formato inicial en el que Git guarda objetos en el disco se denomina formato de objeto "suelto".
Sin embargo, ocasionalmente Git empaqueta varios de estos objetos en un solo archivo binario llamado "packfile" para
ahorrar espacio y ser más eficiente. Git hace esto si tiene demasiados objetos sueltos, si ejecuta el comando git gc
manualmente o si lo envía a un servidor remoto. Para ver qué sucede, puedes pedirle manualmente a Git que empaque
los objetos llamando al comando git gc :

$ git gc
Contando objetos: 18, listo.
Compresión delta usando hasta 8 hilos.
Comprimir objetos: 100% (14/14), hecho.
Objetos de escritura: 100% (18/18), hecho.
Total 18 (delta 3), reutilizado 0 (delta 0)

Si busca en su directorio de objetos , encontrará que la mayoría de sus objetos se han ido y ha aparecido un nuevo par
de archivos:

$ Hallazgo .git / objetos de tipo


f .git / objetos / BD / 9dbf5aae1a3862dd1526723246b20206e5fc37 .git /
objetos / D6 / 70460b4b4aece5915caf5c68d12f560a9fe3e4 .git /
objetos / info / paquetes .git / objetos / pack /
pack-978e03944f5c581011e6998cd0e9e30000905586.idx .git / objetos / paquete/
paquete-978e03944f5c581011e6998cd0e9e30000905586.paquete

Los objetos que quedan son los blobs a los que no apunta ninguna confirmación; en este caso, el "¿qué pasa, doctor?"
y los blobs de ejemplo de "contenido de prueba" que creó anteriormente. Debido a que nunca los agregó a ninguna
confirmación, se consideran colgantes y no están empaquetados en su nuevo archivo de paquete.

Los otros archivos son su nuevo archivo de paquete y un índice. El archivo de paquete es un archivo único que contiene
el contenido de todos los objetos que se eliminaron de su sistema de archivos. El índice es un archivo que contiene
compensaciones en ese archivo de paquete para que pueda buscar rápidamente un objeto específico. Lo bueno es que,
aunque los objetos en el disco antes de ejecutar el comando gc tenían un tamaño aproximado de 15 KB, el nuevo archivo
de paquete solo tiene 7 KB. Ha reducido el uso de su disco a la mitad empaquetando sus objetos.

¿Cómo hace esto Git? Cuando Git empaqueta objetos, busca archivos que tengan nombres y tamaños similares, y
almacena solo los deltas de una versión del archivo a la siguiente. Puede consultar el archivo del paquete y ver qué hizo
Git para ahorrar espacio. El comando de plomería git verificar-paquete le permite ver lo que se empaquetó:

448
Machine Translated by Google

$ git verificar-paquete -v .git/objetos/paquete/paquete


978e03944f5c581011e6998cd0e9e30000905586.idx
2431DA676938450A4D72E260DB3BF7B0F587BBC1 COMPROMET 223 155
12 69BCDAFF5328278AB1C0812CE0E07FA7D26A96D7 COMPROMET 214
152CB23ED55B226516648C7AD5D0A3DEB90 Commit 214 145 319
43168a18b7613d1281e5560855a83eb8fde3d687 compromiso 213 146 464
092917823486a802e94d727c820a9024e14a1fc2 commit 214 146 610
702470739ce72005e2edff522fde85d52a65df9b cometer 165 118 756 etiqueta
d368d0ac0678cbe6cce505be58126d3526706e54 130 122 874 árbol
fe879577cb8cffcdf25441725141e310dd7d239b 136 136 996
árbol d8329fc1cc938780ffdd9f94e0d364e0ea74f579 36 46 1132 árbol
deef2e1b793907545e50a2ea2ddb5ba6c58c4506 136 136 1178 árbol
d982c7cb2c2a972ee391a85da481fc1f9127a01d 6 17 1314 1 \
deef2e1b793907545e50a2ea2ddb5ba6c58c4506
3c4e9cd789d88d8d89c1073707c3585e41b0e614 arbol 8 19 1331 1 \
deef2e1b793907545e50a2ea2ddb5ba6c58c4506
0155eb4229851634a0f03eb265b69f5a1d56 arbol 3713 750 4
83baae61804e65cc73a7201a7252750c76066a30 gota 10 19 1426
fa49b077972391ad58037050f2a75f74e3671e92 blob 9 18 1445
b042a60ef7dff760008df33cee372b945b6e884e blob 22,054 5,799 1,463
033b4468fa6b2a9547a70d88d1bbe8bf3f9ed0d5 blob 9 20 7262 1 \
b042a60ef7dff760008df33cee372b945b6e884e 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
blob 10 19 7282 no delta: 15 objetos longitud de cadena = 1: 3 Los objetos de .git /
objetos / pack / pack-978e03944f5c581011e6998cd0e9e30000905586.pack: ok

Aquí, el blob 033b4 , que si recuerda fue la primera versión de su archivo repo.rb , hace referencia al blob b042a , que fue la segunda
versión del archivo. La tercera columna en la salida es el tamaño del objeto en el paquete, por lo que puede ver que b042a ocupa 22K
del archivo, pero que 033b4 solo ocupa 9 bytes. Lo que también es interesante es que la segunda versión del archivo es la que se
almacena intacta, mientras que la versión original se almacena como un delta; esto se debe a que es más probable que necesite un
acceso más rápido a la versión más reciente del archivo. .

Lo realmente bueno de esto es que se puede volver a empaquetar en cualquier momento. Ocasionalmente, Git volverá a empaquetar
su base de datos automáticamente, siempre tratando de ahorrar más espacio, pero también puede volver a empaquetar manualmente
en cualquier momento ejecutando git gc a mano.

La especificación de referencia

A lo largo de este libro, hemos utilizado asignaciones simples de sucursales remotas a referencias locales, pero pueden ser más
complejas. Suponga que estaba siguiendo las dos últimas secciones y había creado un pequeño repositorio Git local y ahora deseaba
agregarle un control remoto :

$ git remoto añadir origen https://github.com/schacon/simplegit-progit

Ejecutar el comando anterior agrega una sección al archivo .git/config de su repositorio , especificando el

449
Machine Translated by Google

nombre del remoto (origen), la URL del repositorio remoto y la especificación de referencia que se usará para obtener:

["origen" remoto]
url = https://github.com/schacon/simplegit-progit fetch =
+refs/heads/*:refs/remotes/origin/*

El formato de refspec es, primero, un + opcional, seguido de <src>:<dst>, donde <src> es el patrón para las referencias en el lado
remoto y <dst> es donde esas referencias se rastrearán localmente. El + le dice a Git que actualice la referencia incluso si no es
un avance rápido.

En el caso predeterminado que se escribe automáticamente mediante un comando git remote add origin , Git obtiene todas las
referencias en refs/heads/ en el servidor y las escribe en refs/remotes/origin/ localmente. Entonces, si hay una rama maestra en
el servidor, puede acceder al registro de esa rama localmente a través de cualquiera de los siguientes:

$ git log origen/maestro


$ git log remotos/origen/maestro
$ git log refs/remotos/origen/maestro

Todos son equivalentes, porque Git expande cada uno de ellos a refs/remotes/origin/master.

Si, en cambio, desea que Git despliegue solo la rama maestra cada vez, y no todas las demás ramas en el servidor remoto, puede
cambiar la línea de búsqueda para que se refiera solo a esa rama:

buscar = +refs/heads/master:refs/remotos/origen/master

Esta es solo la especificación de referencia predeterminada para git fetch para ese control remoto. Si desea realizar una
recuperación única, también puede especificar la especificación de referencia específica en la línea de comando. Para llevar la
rama maestra en el control remoto a origin/mymaster localmente, puede ejecutar:

$ git fetch origin master:refs/remotes/origin/mymaster

También puede especificar múltiples refspecs. En la línea de comando, puede desplegar varias ramas así:

$ git fetch origin master:refs/remotes/origin/mymaster \


ÿ

topic:refs/remotes/origin/topic From
git@github.com:schacon/simplegit ! [rechazado] maestro *
[nueva rama] tema -> origen/mymaster (no avance rápido)
-> origen/tema

En este caso, se rechazó la extracción de la rama maestra porque no figuraba como una referencia de avance rápido.
Puede anular eso especificando el + delante de refspec.

450
Machine Translated by Google

También puede especificar múltiples refspecs para buscar en su archivo de configuración. Si desea obtener siempre las ramas
principal y experimental del control remoto de origen , agregue dos líneas:

["origen" remoto]
url = https://github.com/schacon/simplegit-progit fetch =
+refs/heads/master:refs/remotes/origin/master fetch = +refs/
heads/experiment:refs/remotes/ origen/experimento

Desde Git 2.6.0, puede usar globos parciales en el patrón para hacer coincidir varias ramas, así que esto funciona:

buscar = +refs/heads/qa*:refs/remotes/origin/qa*

Aún mejor, puede usar espacios de nombres (o directorios) para lograr lo mismo con más estructura.
Si tiene un equipo de control de calidad que impulsa una serie de sucursales y desea obtener la sucursal maestra y cualquiera de las
sucursales del equipo de control de calidad, pero nada más, puede usar una sección de configuración como esta:

["origen" remoto]
url = https://github.com/schacon/simplegit-progit fetch =
+refs/heads/master:refs/remotes/origin/master fetch = +refs/
heads/qa/*:refs/ controles remotos/origen/qa/*

Si tiene un proceso de flujo de trabajo complejo que tiene un equipo de control de calidad que impulsa sucursales, desarrolladores
que impulsan sucursales y equipos de integración que impulsan y colaboran en sucursales remotas, puede asignarles nombres
fácilmente de esta manera.

Empujando Refspecs

Es bueno que pueda obtener referencias de espacios de nombres de esa manera, pero, en primer lugar, ¿cómo hace el equipo de
control de calidad para que sus sucursales entren en un espacio de nombres qa/ ? Lo logras usando refspecs para empujar.

Si el equipo de control de calidad quiere enviar su rama maestra a qa/master en el servidor remoto, puede ejecutar:

$ git push master de origen: refs/heads/qa/master

Si quieren que Git lo haga automáticamente cada vez que ejecuten git push origin, pueden agregar un valor push a su archivo de
configuración:

["origen" remoto]
url = https://github.com/schacon/simplegit-progit fetch =
+refs/heads/*:refs/remotes/origin/* push = refs/heads/
master:refs/heads/qa /Maestro

Nuevamente, esto hará que un origen de git push envíe la rama maestra local a la rama maestra/qa remota de forma predeterminada.

451
Machine Translated by Google

ÿ No puede usar refspec para obtener de un repositorio y enviar a otro.


Para ver un ejemplo de cómo hacerlo, consulte Mantenga su repositorio público de GitHub actualizado.

Eliminación de referencias

También puede usar refspec para eliminar referencias del servidor remoto ejecutando algo como esto:

$ git push origen :tema

Debido a que refspec es <src>:<dst>, al omitir la parte <src> , esto básicamente dice que haga que el tema se ramifique en el
nada remoto, lo que lo eliminará.

O puede usar la sintaxis más nueva (disponible desde Git v1.7.0):

$ git push origen --eliminar tema

Protocolos de transferencia

Git puede transferir datos entre dos repositorios de dos formas principales: el protocolo "tonto" y el protocolo "inteligente". Esta
sección cubrirá rápidamente cómo funcionan estos dos protocolos principales.

El protocolo tonto

Si está configurando un repositorio para que se sirva solo de lectura a través de HTTP, es probable que se use el protocolo
tonto. Este protocolo se llama "tonto" porque no requiere código específico de Git en el lado del servidor durante el proceso de
transporte; el proceso de obtención es una serie de solicitudes HTTP GET , donde el cliente puede asumir el diseño del
repositorio de Git en el servidor.

El protocolo tonto rara vez se usa en estos días. Es difícil asegurarlo o hacerlo privado, por lo que la

ÿ mayoría de los hosts de Git (tanto en la nube como en las instalaciones) se negarán a usarlo.
En general, se recomienda utilizar el protocolo inteligente, que describimos un poco más adelante.

Sigamos el proceso de obtención de http para la biblioteca simplegit:

$ clon de git http://servidor/simplegit-progit.git

Lo primero que hace este comando es desplegar el archivo info/refs . Este archivo está escrito por el comando de actualización
de información del servidor , por lo que debe habilitarlo como un enlace posterior a la recepción para que el transporte HTTP
funcione correctamente:

=> OBTENER
información/refs ca82a6dff817ec66f44342007202690a93763949árbitros/jefes/maestro

452
Machine Translated by Google

Ahora tiene una lista de las referencias remotas y SHA-1. A continuación, busque cuál es la referencia HEAD para saber qué
verificar cuando haya terminado:

=> OBTENER CABEZA

ref: refs/cabezas/maestro

Debe verificar la rama maestra cuando haya completado el proceso. En este punto, está listo para comenzar el proceso de
caminar. Debido a que su punto de partida es el objeto de confirmación ca82a6 que vio en el archivo info/refs , comienza por
obtener eso:

=> OBTENER objetos/ca/82a6dff817ec66f44342007202690a93763949 (179


bytes de datos binarios)

Obtiene un objeto de vuelta: ese objeto está en formato suelto en el servidor y lo obtuvo a través de una solicitud HTTP GET
estática. Puede descomprimirlo con zlib, quitar el encabezado y ver el contenido de la confirmación:

$ Git -p-cat archivo cfda3bf379e4f8dba8717dee55aab78aef7f4daf árbol


ca82a6dff817ec66f44342007202690a93763949 padres
085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 autor de Scott Chacon
<schacon@gmail.com> 1205815931 -0700 confirmador de Scott Chacon
<schacon@gmail.com> 1240030591 -0700

Cambiar número de versión

A continuación, tiene dos objetos más para recuperar: cfda3b, que es el árbol de contenido al que apunta la confirmación que
acabamos de recuperar; y 085bb3, que es la confirmación principal:

=> OBTENER objetos/08/5bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 (179


bytes de datos)

Eso te da tu próximo objeto de confirmación. Agarra el objeto del árbol:

=> OBTENER objetos/cf/da3bf379e4f8dba8717dee55aab78aef7f4daf


(404 No encontrado)

Vaya, parece que ese objeto de árbol no está en formato suelto en el servidor, por lo que obtiene una respuesta 404. Hay un
par de razones para esto: el objeto podría estar en un repositorio alternativo o podría estar en un archivo de paquete en este
repositorio. Git verifica primero las alternativas enumeradas:

=> OBTENER objetos/información/http-alternativos


(archivo vacío)

Si vuelve con una lista de direcciones URL alternativas, Git comprueba si hay archivos sueltos y paquetes de archivos; esto es

453
Machine Translated by Google

un buen mecanismo para proyectos que son bifurcaciones entre sí para compartir objetos en el disco. Sin embargo, debido
a que no se enumeran alternativas en este caso, su objeto debe estar en un archivo de paquete. Para ver qué archivos de
paquete están disponibles en este servidor, debe obtener el archivo de objetos/información/paquetes , que contiene una
lista de ellos (también generado por update-server-info):

=> OBTENER objetos/información/


paquetes P pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack

Solo hay un archivo de paquete en el servidor, por lo que su objeto obviamente está allí, pero verificará el archivo de índice
para asegurarse. Esto también es útil si tiene varios archivos de paquete en el servidor, para que pueda ver qué archivo
de paquete contiene el objeto que necesita:

=> OBTENER objetos/paquete/paquete-816a9b2334da9953e530f27bcac22082a9f5b835.idx


(4k de datos binarios)

Ahora que tiene el índice del archivo de paquete, puede ver si su objeto está en él, porque el índice enumera los SHA-1
de los objetos contenidos en el archivo de paquete y las compensaciones de esos objetos. Tu objeto está ahí, así que
continúa y obtén el archivo de paquete completo:

=> OBTENER objetos/paquete/paquete-816a9b2334da9953e530f27bcac22082a9f5b835.paquete


(13k de datos binarios)

Tiene su objeto de árbol, por lo que continúa recorriendo sus compromisos. También están todos dentro del archivo de
paquete que acaba de descargar, por lo que no tiene que hacer más solicitudes a su servidor. Git extrae una copia de
trabajo de la rama maestra a la que apuntaba la referencia HEAD que descargaste al principio.

El protocolo inteligente

El protocolo tonto es simple pero un poco ineficiente y no puede manejar la escritura de datos del cliente al servidor. El
protocolo inteligente es un método más común para transferir datos, pero requiere un proceso en el extremo remoto que
sea inteligente sobre Git: puede leer datos locales, descubrir qué tiene y necesita el cliente y generar un archivo de paquete
personalizado para ello. Hay dos conjuntos de procesos para transferir datos: un par para cargar datos y un par para
descargar datos.

Cargando datos

Para cargar datos en un proceso remoto, Git utiliza los procesos send-pack y receive-pack . El proceso de envío del
paquete se ejecuta en el cliente y se conecta a un proceso de recepción del paquete en el lado remoto.

SSH

Por ejemplo, supongamos que ejecuta git push origin master en su proyecto y origin se define como una URL que usa el
protocolo SSH. Git activa el proceso de paquete de envío , que inicia una conexión a través de SSH a su servidor. Intenta
ejecutar un comando en el servidor remoto a través de una llamada SSH que se parece a esto:

454
Machine Translated by Google

$ ssh -x git@server "git-receive-pack 'simplegit-progit.git'"


00a5ca82a6dff817ec66f4437202690a93763949 refs/heads/masterÿreport-status \
delete-refs side-band-64k quiet ofs-delta \ agent=git/2 :2.1.1+github-607-gfba4028
eliminar-refs 0000

El comando git-receive-pack responde inmediatamente con una línea por cada referencia que tiene actualmente; en este
caso, solo la rama principal y su SHA-1. La primera línea también tiene una lista de las capacidades del servidor (aquí, report-
status, delete-refs y algunas otras, incluido el identificador del cliente).

Los datos se transmiten en fragmentos. Cada fragmento comienza con un valor hexadecimal de 4 caracteres que especifica
la longitud del fragmento (incluidos los 4 bytes de la propia longitud). Los fragmentos suelen contener una sola línea de
datos y un avance de línea final. Su primer fragmento comienza con 00a5, que es hexadecimal para 165, lo que significa
que el fragmento tiene una longitud de 165 bytes. El siguiente fragmento es 0000, lo que significa que el servidor ha
terminado con su lista de referencias.

Ahora que conoce el estado del servidor, su proceso de paquete de envío determina qué confirmaciones tiene que el
servidor no tiene. Para cada referencia que este impulso actualizará, el proceso de envío y paquete le dice al proceso de
recepción del paquete esa información. Por ejemplo, si está actualizando la rama maestra y agregando una rama
experimental , la respuesta del paquete de envío puede verse así:

0076ca82a6dff817ec66f44342007202690a93763949 15027957951b64cf874c3557a0f3547bd83b3ff6
\ refs / heads / master-informe de estado 006c0000000000000000000000000000000000000000
cdfdb42577e2506715f8cfeacdbabc092bf63e8d \ refs / heads / experimento 0000

Git envía una línea para cada referencia que estás actualizando con la longitud de la línea, el SHA-1 antiguo, el SHA-1
nuevo y la referencia que se está actualizando. La primera línea también tiene las capacidades del cliente. El valor SHA-1
de todos los '0' significa que no había nada antes, porque está agregando la referencia del experimento. Si estuviera
eliminando una referencia, vería lo contrario: todos los '0' en el lado derecho.

A continuación, el cliente envía un archivo de paquete de todos los objetos que el servidor aún no tiene. Finalmente, el
servidor responde con una indicación de éxito (o fracaso):

000e desempaquetar ok

HTTP(S)

Este proceso es prácticamente el mismo en HTTP, aunque el protocolo de enlace es un poco diferente. La conexión se
inicia con esta petición:

455
Machine Translated by Google

=> OBTENER http://server/simplegit-progit.git/info/refs?service=git-receive-pack


001f# service=git-receive-pack 00ab6c5f0e45abd7832bf23074a333f739977c9e8188
refs/heads/masterÿreport-status \ delete-refs side -band-64k silencioso ofs-delta \
agent=git/2:2.1.1~vmg-bitmaps-bugaloo-608-g116744e 0000

Ese es el final del primer intercambio cliente-servidor. Luego, el cliente realiza otra solicitud, esta vez una POST, con
los datos que proporciona send-pack .

=> POST http://servidor/simplegit-progit.git/git-receive-pack

La solicitud POST incluye la salida del paquete de envío y el archivo del paquete como su carga útil. Luego, el servidor
indica el éxito o el fracaso con su respuesta HTTP.

Tenga en cuenta que el protocolo HTTP puede envolver aún más estos datos dentro de una codificación de transferencia fragmentada.

Descarga de datos

Cuando descarga datos, están involucrados los procesos fetch-pack y upload-pack . El cliente inicia un proceso de
paquete de búsqueda que se conecta a un proceso de paquete de carga en el lado remoto para negociar qué datos se
transferirán hacia abajo.

SSH

Si está realizando la búsqueda a través de SSH, fetch-pack ejecuta algo como esto:

$ ssh -x git@servidor "git-upload-pack 'simplegit-progit.git'"

Después de que fetch-pack se conecta, upload-pack devuelve algo como esto:

00dfca82a6dff817ec66f44342007202690a93763949 HEADÿmulti_ack thin-


pack \ side-band side-band-64k ofs-delta superficial sin progreso include-tag \
multi_ack_detailed symref=HEAD:refs/heads/master \ agent=git/2:2.1.1+github-
607-gfba4028 003fe2409a098dc3e53539a9028a94b6224db9d6a6b6 referencias/
cabezas/maestro 0000

Esto es muy similar a lo que responde el paquete de recepción , pero las capacidades son diferentes. Además,
devuelve a qué apunta HEAD (symref=HEAD:refs/heads/master) para que el cliente sepa qué verificar si se trata de un
clon.

En este punto, el proceso fetch-pack mira qué objetos tiene y responde con los objetos que necesita enviando "quiero"
y luego el SHA-1 que quiere. Envía todos los objetos que ya tiene con “have” y luego el SHA-1. Al final de esta lista,
escribe "hecho" para iniciar el proceso de carga del paquete para comenzar a enviar el paquete de datos que necesita:

456
Machine Translated by Google

003cquiero ca82a6dff817ec66f44342007202690a93763949 ofs-delta 0032tener


085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
0009hecho
0000

HTTP(S)

El protocolo de enlace para una operación de búsqueda requiere dos solicitudes HTTP. El primero es un GET al mismo
punto final utilizado en el protocolo tonto:

=> GET $GIT_URL/info/refs?service=git-upload-pack 001e#


service=git-upload-pack 00e7ca82a6dff817ec66f44342007202690a93763949
HEADÿmulti_ack thin-pack \ side-band side-band-64k ofs-delta superficial sin progreso incluir -tag \
multi_ack_detailed no-done symref=HEAD:refs/heads/master \ agent=git/2:2.1.1+github-607-gfba4028
003fca82a6dff817ec66f44342007202690a93763949 refs/heads/master 0000

Esto es muy similar a invocar git-upload-pack a través de una conexión SSH, pero el segundo intercambio se realiza como
una solicitud separada:

=> POST $GIT_URL/git-upload-pack HTTP/1.0 0032quiero


0a53e9ddeaddad63ad106860237bbf53411d11a7
0032tener 441b40d833fdfa93eb2908e52742248faf0ee993 0000

Nuevamente, este es el mismo formato que el anterior. La respuesta a esta solicitud indica éxito o fracaso e incluye el
archivo del paquete.

Resumen de protocolos

Esta sección contiene una descripción general muy básica de los protocolos de transferencia. El protocolo incluye muchas
otras características, como multi_ack o capacidades de banda lateral , pero cubrirlas está fuera del alcance de este libro.
Hemos tratado de darle una idea del vaivén general entre el cliente y el servidor; si necesita más conocimientos que estos,
probablemente querrá echar un vistazo al código fuente de Git.

Mantenimiento y Recuperación de Datos


Ocasionalmente, es posible que deba realizar alguna limpieza: hacer un repositorio más compacto, limpiar un repositorio
importado o recuperar el trabajo perdido. Esta sección cubrirá algunos de estos escenarios.

Mantenimiento

De vez en cuando, Git ejecuta automáticamente un comando llamado "auto gc". La mayoría de las veces, este comando no
hace nada. Sin embargo, si hay demasiados objetos sueltos (objetos que no están en un archivo de paquete) o demasiados

457
Machine Translated by Google

packfiles, Git lanza un comando git gc completo . El "gc" significa recolección de basura, y el comando hace una serie de cosas:
reúne todos los objetos sueltos y los coloca en archivos de paquete, consolida los archivos de paquete en un archivo de paquete
grande y elimina los objetos que no son accesibles desde cualquier lugar. cometer y tienen unos meses de edad.

Puede ejecutar auto gc manualmente de la siguiente manera:

$ git gc --auto

Nuevamente, esto generalmente no hace nada. Debe tener alrededor de 7000 objetos sueltos o más de 50 archivos de paquete
para que Git inicie un comando gc real. Puede modificar estos límites con los ajustes de configuración gc.auto y gc.autopacklimit ,
respectivamente.

La otra cosa que hará gc es empaquetar sus referencias en un solo archivo. Supongamos que su repositorio contiene las
siguientes ramas y etiquetas:

$ buscar .git/refs -type f .git/


refs/heads/experiment .git/refs/
heads/master .git/refs/tags/
v1.0 .git/refs/tags/v1.1

Si ejecuta git gc, ya no tendrá estos archivos en el directorio refs . Git los moverá por el bien de la eficiencia a un archivo
llamado .git/packed-refs que se ve así:

$ .Git cat / REF-envasados


# paquetes-refs con: pelado totalmente pelada
árbitros cac0cab538b970a37ea1e769cbbde608743bc96d / jefes / experimento
ab1afef80fac8e34258ff41fc1b867c702daa24b refs / heads / master
cac0cab538b970a37ea1e769cbbde608743bc96d árbitros / etiquetas / v1.0
9585191f37f7b0fb9444f35a9bf50de191beadc2 árbitros / etiquetas / v1.1 ^
1a410efbd13591db07496601ebc7a059dd55cfe9

Si actualiza una referencia, Git no edita este archivo, sino que escribe un nuevo archivo en refs/heads. Para obtener el SHA-1
apropiado para una referencia dada, Git busca esa referencia en el directorio de referencias y luego verifica el archivo de
referencias empaquetadas como respaldo. Entonces, si no puede encontrar una referencia en el directorio de referencias ,
probablemente esté en su archivo de referencias empaquetadas .

Observe la última línea del archivo, que comienza con ^. Esto significa que la etiqueta directamente arriba es una etiqueta anotada
y esa línea es la confirmación a la que apunta la etiqueta anotada.

Recuperación de datos

En algún momento de su viaje Git, puede perder accidentalmente una confirmación. En general, esto sucede porque fuerza la
eliminación de una rama que tenía trabajo y resulta que, después de todo, quería la rama; o restablece una rama, abandonando
así las confirmaciones de las que quería algo.
Suponiendo que esto suceda, ¿cómo puede recuperar sus confirmaciones?

458
Machine Translated by Google

Aquí hay un ejemplo que restablece por completo la rama maestra en su repositorio de prueba a una confirmación anterior
y luego recupera las confirmaciones perdidas. Primero, revisemos dónde está su repositorio en este punto:

$ git log --pretty=oneline


ab1afef80fac8e34258ff41fc1b867c702daa24b Modificar un poco repo.rb
484a59275031909e19aadb7c92262719cfcdf19a Crear repo.rb
1a410efbd13591db07496609edd57cfe059edb57 Tercera confirmación
cac0cab538b970a37ea1e769cbbde608743bc96d Segunda confirmación
fdf4fc3344e67ab068f836878b6c4951e3b15f3d Primera confirmación

Ahora, mueva la rama maestra de regreso a la confirmación del medio:

$ git reset --duro 1a410efbd13591db07496601ebc7a059dd55cfe9


HEAD ahora está en 1a410ef Tercer compromiso
$ git log --pretty=oneline
1a410efbd13591db07496601ebc7a059dd55cfe9 Tercer compromiso
cac0cab538b970a37ea1e769cbbde608743bc96d Segundo compromiso
fdf4fc3344e67ab068f836878b6c4951e3b15f3d Primera confirmación

Efectivamente, ha perdido las dos confirmaciones principales: no tiene una rama desde la que se pueda acceder a esas
confirmaciones. Debe encontrar la última confirmación SHA-1 y luego agregar una rama que apunte a ella. El truco es
encontrar la última confirmación SHA-1: no es como si la hubieras memorizado, ¿verdad?

A menudo, la forma más rápida es usar una herramienta llamada git reflog. Mientras trabaja, Git registra en silencio cuál es
su HEAD cada vez que lo cambia. Cada vez que confirmas o cambias de rama, el reflog se actualiza. El registro de
referencia también se actualiza mediante el comando git update-ref , que es otra razón para usarlo en lugar de simplemente
escribir el valor SHA-1 en sus archivos de referencia, como explicamos en Referencias de Git. Puedes ver dónde has estado
en cualquier momento ejecutando git reflog:

$ git reflog
1a410ef HEAD@{0}: restablecer: pasar a 1a410ef ab1afef
HEAD@{1}: confirmar: modificar un poco repo.rb 484a592
HEAD@{2}: confirmar: crear repo.rb

Aquí podemos ver los dos compromisos que hemos revisado, sin embargo, no hay mucha información aquí. Para ver la
misma información de una manera mucho más útil, podemos ejecutar git log -g, que le dará una salida de registro normal
para su reflog.

459
Machine Translated by Google

$ git log -g
commit 1a410efbd13591db07496601ebc7a059dd55cfe9
Reflog: HEAD@{0} (Scott Chacon <schacon@gmail.com>)
Reflog mensaje: actualización HEAD
Autor: Scott Chacon <schacon@gmail.com> Fecha:
viernes 22 de mayo 18:22:37 2009 -0700

Tercer compromiso

confirmar ab1afef80fac8e34258ff41fc1b867c702daa24b
Reflog: HEAD@{1} (Scott Chacon <schacon@gmail.com>)
Reflog mensaje: actualización HEAD
Autor: Scott Chacon <schacon@gmail.com> Fecha:
viernes 22 de mayo 18:15:24 2009 -0700

Modificar repo.rb un poco

Parece que la confirmación inferior es la que perdiste, por lo que puedes recuperarla creando una nueva rama en esa
confirmación. Por ejemplo, puede iniciar una rama llamada recovery -branch en ese compromiso (ab1afef):

$ Git branch recuperar rama ab1afef $ git


log = --pretty oneline recuperarse rama
ab1afef80fac8e34258ff41fc1b867c702daa24b Modificar repo.rb un poco
484a59275031909e19aadb7c92262719cfcdf19a Crear
1a410efbd13591db07496601ebc7a059dd55cfe9 repo.rb Tercer commit
commit cac0cab538b970a37ea1e769cbbde608743bc96d Segunda
fdf4fc3344e67ab068f836878b6c4951e3b15f3d Primera comprometerse

Genial: ahora tienes una rama llamada recovery-branch que es donde solía estar tu rama maestra , lo que hace que las dos
primeras confirmaciones sean accesibles nuevamente. A continuación, suponga que su pérdida no estaba en el registro de
referencia por alguna razón; puede simular eso eliminando la rama de recuperación y eliminando el registro de referencia.
Ahora los dos primeros compromisos no son accesibles por nada:

$ rama git -D recuperar-rama $ rm


-Rf .git/logs/

Debido a que los datos de reflog se guardan en el directorio .git/logs/ , efectivamente no tiene reflog. ¿Cómo puedes recuperar
ese compromiso en este punto? Una forma es usar la utilidad git fsck , que verifica la integridad de su base de datos. Si lo
ejecuta con la opción --full , le muestra todos los objetos que no están señalados por otro objeto:

460
Machine Translated by Google

$ git fsck --full Verificando


directorios de objetos: 100% (256/256), listo.
Comprobación de objetos: 100% (18/18), hecho.
Caballing Blob D670460B4B4AECE5915CAF5C68D12F560A9FE3E4
COMPROMISTIENTE COMPROMISO
AB1AFEF80FAC8E34258FF41FC1B867C702DAA24B ÁRBOL DE COLORGLING
AEA790B9A58F6CF6F2804EEAC9F0ABBE9631E4C9 COLGULAR BLOB 7108F7ECB345EEE9D0084193F147CDAD4D2998293

En este caso, puede ver su compromiso faltante después de la cadena "compromiso pendiente". Puede recuperarlo
de la misma manera, agregando una rama que apunte a ese SHA-1.

Eliminación de objetos

Hay muchas cosas geniales sobre Git, pero una característica que puede causar problemas es el hecho de que un
clon de git descarga el historial completo del proyecto, incluidas todas las versiones de cada archivo. Esto está bien si
todo es código fuente, porque Git está altamente optimizado para comprimir esos datos de manera eficiente.
Sin embargo, si alguien en algún momento de la historia de su proyecto agregó un solo archivo enorme, todos los
clones de todos los tiempos se verán obligados a descargar ese archivo grande, incluso si se eliminó del proyecto en
la próxima confirmación. Debido a que es accesible desde la historia, siempre estará allí.

Esto puede ser un gran problema cuando está convirtiendo repositorios de Subversion o Perforce en Git.
Debido a que no descargas todo el historial en esos sistemas, este tipo de adición tiene pocas consecuencias. Si
realizó una importación desde otro sistema o encuentra que su repositorio es mucho más grande de lo que debería
ser, así es como puede encontrar y eliminar objetos grandes.

Tenga cuidado: esta técnica es destructiva para su historial de confirmaciones. Reescribe cada objeto de
confirmación desde el árbol más antiguo que debe modificar para eliminar una referencia de archivo grande. Si hace
esto inmediatamente después de una importación, antes de que alguien haya comenzado a basar su trabajo en la
confirmación, está bien; de lo contrario, debe notificar a todos los colaboradores que deben volver a basar su trabajo
en sus nuevas confirmaciones.

Para demostrarlo, agregará un archivo grande a su repositorio de prueba, lo eliminará en la próxima confirmación, lo
encontrará y lo eliminará permanentemente del repositorio. Primero, agregue un objeto grande a su historial:

$ curl -L https://www.kernel.org/pub/software/scm/git/git-2.1.0.tar.gz > git.tgz $ git add git.tgz $ git commit -m 'Agregar
git tarball ' [master 7b30847] Agregar git tarball 1 archivo cambiado, 0 inserciones (+), 0 eliminaciones (-) modo de
creación 100644 git.tgz

Ups, no querías agregar un tarball enorme a tu proyecto. Mejor deshacerse de él:

461
Machine Translated by Google

$ git rm git.tgz rm
'git.tgz' $ git commit
-m 'Oops - remove large tarball' [master dadf725] Ups -
remove large tarball 1 archivo cambiado, 0 inserciones
(+), 0 eliminaciones (-) modo de eliminación 100644 git.tgz

Ahora, evalúe su base de datos y vea cuánto espacio está usando:

$ git gc
Contando objetos: 17, listo.
Compresión delta usando hasta 8 hilos.
Comprimir objetos: 100% (13/13), listo.
Objetos de escritura: 100% (17/17), hecho.
Total 17 (delta 1), reutilizado 10 (delta 0)

Puede ejecutar el comando contar objetos para ver rápidamente cuánto espacio está utilizando:

$ git cuenta-objetos -v
cuenta: 7 tamaño: 32

en paquete:
17 paquetes:
1 tamaño-paquete:
4868 ciruelas pasas:
0 basura: 0 tamaño-
basura: 0

La entrada del paquete de tamaño es el tamaño de sus archivos de paquete en kilobytes, por lo que está usando casi
5 MB. Antes de la última confirmación, estabas usando cerca de 2K; claramente, eliminar el archivo de la confirmación
anterior no lo eliminó de tu historial. Cada vez que alguien clone este repositorio, tendrá que clonar los 5 MB solo para
obtener este pequeño proyecto, porque accidentalmente agregó un archivo grande. Deshagámonos de eso.

Primero tienes que encontrarlo. En este caso, ya sabes qué archivo es. Pero supongamos que no lo hizo; ¿Cómo
identificaría qué archivo o archivos ocupaban tanto espacio? Si ejecuta git gc, todos los objetos están en un archivo
de paquete; puede identificar los objetos grandes ejecutando otro comando de plomería llamado gitcheck-pack y
ordenando el tercer campo en la salida, que es el tamaño del archivo. También puede canalizarlo a través del
comando tail porque solo le interesan los últimos archivos más grandes:

$ git verificar-paquete -v .git/objetos/paquete/paquete-29…69.idx \ |


ordenar -k 3 -n \ | tail -3 dadf7258d699da2c8d89b09ef6670edb7d5f91b4
commit 229 159 12 033b4468fa6b2a9547a70d88d1bbe8bf3f9ed0d5 blob
22044 5792 4977696 82c99a3e86bb1267b236a4b6eff7868d97489af1 blob
4975916 4976258 1438

462
Machine Translated by Google

El objeto grande está en la parte inferior: 5 MB. Para averiguar qué archivo es, utilizará el comando rev-list , que
utilizó brevemente en Forzar un formato de mensaje de compromiso específico. Si pasa --objects a rev-list, enumera
todos los SHA-1 confirmados y también los SHA-1 de blob con las rutas de archivo asociadas a ellos. Puedes usar
esto para encontrar el nombre de tu blob:

$ git rev-list --objetos --todos | grep 82c99a3


82c99a3e86bb1267b236a4b6eff7868d97489af1 git.tgz

Ahora, debe eliminar este archivo de todos los árboles en su pasado. Puede ver fácilmente qué confirmaciones
modificaron este archivo:

$ git log --oneline --branches -- git.tgz dadf725


¡Vaya! Eliminar tarball grande 7b30847 Añadir
git tarball

Debe volver a escribir todas las confirmaciones posteriores a 7b30847 para eliminar por completo este archivo de
su historial de Git. Para hacerlo, usa filter-branch, que usó en Rewrite History:

$ git filter-branch --index-filter \ 'git rm --


ignore-unmatch --cached git.tgz' -- 7b30847^..
Reescribir 7b30847d080183a1ab7d18fb202473b3096e9f34 (1/2)rm 'git.tgz'
Reescribir dadf7258d699da2c8d89b09ef6670edb7d5f91b4 (2/2)
Ref 'refs/heads/master' fue reescrito

La opción --index-filter es similar a la opción --tree-filter utilizada en Reescribir el historial, excepto que en lugar de
pasar un comando que modifica los archivos desprotegidos en el disco, está modificando su área de ensayo o índice
cada vez.

En lugar de eliminar un archivo específico con algo como rm file, debe eliminarlo con git rm --cached ; debe eliminarlo
del índice, no del disco. La razón para hacerlo de esta manera es la velocidad, ya que Git no tiene que verificar cada
revisión en el disco antes de ejecutar el filtro, el proceso puede ser mucho, mucho más rápido. Puede realizar la
misma tarea con --tree-filter si lo desea. La opción --ignore-unmatch de git rm le indica que no se produzca un error
si el patrón que está tratando de eliminar no está allí. Finalmente, le pide a filter-branch que reescriba su historial
solo desde la confirmación 7b30847 , porque sabe que ahí es donde comenzó este problema. De lo contrario,
comenzará desde el principio y tardará más tiempo innecesariamente.

Su historial ya no contiene una referencia a ese archivo. Sin embargo, su reflog y un nuevo conjunto de referencias
que Git agregó cuando hizo la rama de filtro en .git/refs/original todavía lo hacen, por lo que debe eliminarlos y luego
volver a empaquetar la base de datos. Debes deshacerte de cualquier cosa que apunte a esas confirmaciones
antiguas antes de volver a empaquetar:

463
Machine Translated by Google

$ rm -Rf .git/refs/original $ rm
-Rf .git/logs/ $ git gc Contar
objetos: 15, listo.

Compresión delta usando hasta 8 hilos.


Comprimir objetos: 100% (11/11), listo.
Objetos de escritura: 100% (15/15), hecho.
Total 15 (delta 1), reutilizado 12 (delta 0)

Veamos cuánto espacio ahorraste.

$ git count-objects -v
count: 11 size: 4904 in-
pack: 15 packs: 1 size-
pack: 8 prune-packable:
0 basura: 0 size-garbage:
0

El tamaño del repositorio empaquetado se ha reducido a 8K, que es mucho mejor que 5 MB. Puede ver en
el valor de tamaño que el objeto grande todavía está en sus objetos sueltos, por lo que no se ha ido; pero
no se transferirá en un push o clon posterior, que es lo importante. Si realmente quisiera, podría eliminar el
objeto por completo ejecutando git prune con la opción --expire :

$ git prune --expire now $ git


count-objects -v count: 0

tamaño: 0

en paquete: 15
paquetes: 1
tamaño-paquete:
8 ciruela-empaquetable:
0 basura: 0 tamaño-
basura: 0

Variables de entorno
Git siempre se ejecuta dentro de un shell bash y utiliza una serie de variables de entorno del shell para
determinar cómo se comporta. Ocasionalmente, resulta útil saber cuáles son y cómo se pueden usar para
hacer que Git se comporte de la manera que deseas. Esta no es una lista exhaustiva de todas las variables
de entorno a las que presta atención Git, pero cubriremos las más útiles.

464
Machine Translated by Google

Comportamiento global

Parte del comportamiento general de Git como programa de computadora depende de las variables de entorno.

GIT_EXEC_PATH determina dónde busca Git sus subprogramas (como git-commit, git-diff y otros). Puede verificar la
configuración actual ejecutando git --exec-path.

HOME generalmente no se considera personalizable (muchas otras cosas dependen de él), pero es donde Git busca el
archivo de configuración global. Si desea una instalación de Git verdaderamente portátil, completa con una configuración
global, puede anular HOME en el perfil de shell de Git portátil.

PREFIX es similar, pero para la configuración de todo el sistema. Git busca este archivo en $PREFIX/etc/gitconfig.

GIT_CONFIG_NOSYSTEM, si está configurado, deshabilita el uso del archivo de configuración de todo el sistema. Esto es útil
si la configuración de su sistema está interfiriendo con sus comandos, pero no tiene acceso para cambiarla o eliminarla.

GIT_PAGER controla el programa utilizado para mostrar la salida de varias páginas en la línea de comandos. Si esto no está
configurado, PAGER se utilizará como respaldo.

GIT_EDITOR es el editor que Git lanzará cuando el usuario necesite editar algún texto (un mensaje de confirmación, por
ejemplo). Si no está configurado, se utilizará EDITOR .

Ubicaciones de repositorio

Git usa varias variables de entorno para determinar cómo interactúa con el repositorio actual.

GIT_DIR es la ubicación de la carpeta .git . Si esto no se especifica, Git sube por el árbol de directorios hasta llegar a ~ o /,
buscando un directorio .git en cada paso.

GIT_CEILING_DIRECTORIES controla el comportamiento de búsqueda de un directorio .git . Si accede a directorios que


tardan en cargarse (como los de una unidad de cinta o a través de una conexión de red lenta), es posible que desee que Git
deje de intentarlo antes de lo que lo haría, especialmente si se invoca a Git al crear su indicador de shell .

GIT_WORK_TREE es la ubicación de la raíz del directorio de trabajo para un repositorio no vacío. Si se especifica --git -dir o
GIT_DIR pero no se especifica --work-tree, GIT_WORK_TREE o core.worktree , el directorio de trabajo actual se considera el
nivel superior de su árbol de trabajo.

GIT_INDEX_FILE es la ruta al archivo de índice (solo repositorios no vacíos).

GIT_OBJECT_DIRECTORY se puede usar para especificar la ubicación del directorio que normalmente reside en .git/objects.

GIT_ALTERNATE_OBJECT_DIRECTORIES es una lista separada por dos puntos (formateada como /dir/one:/dir/two:…)
que le dice a Git dónde buscar objetos si no están en GIT_OBJECT_DIRECTORY. Si tiene muchos proyectos con archivos
grandes que tienen exactamente el mismo contenido, esto puede usarse para evitar almacenar demasiadas copias de ellos.

465
Machine Translated by Google

especificaciones de ruta

Una "especificación de ruta" se refiere a cómo especifica las rutas a las cosas en Git, incluido el uso de comodines. Estos se usan en el
archivo .gitignore , pero también en la línea de comandos (git add *.c).

GIT_GLOB_PATHSPECS y GIT_NOGLOB_PATHSPECS controlan el comportamiento predeterminado de los comodines en pathspecs. Si

GIT_GLOB_PATHSPECS se establece en 1, los caracteres comodín actúan como comodines (que es el valor predeterminado); si
GIT_NOGLOB_PATHSPECS se establece en 1, los caracteres comodín solo coinciden entre sí, lo que significa que algo como *.c solo
coincidiría con un archivo llamado "\*.c", en lugar de cualquier archivo cuyo nombre termine con .c. Puede anular esto en casos individuales
iniciando pathspec con :(glob) o :(literal), como en :(glob)\*.c.

GIT_LITERAL_PATHSPECS deshabilita los dos comportamientos anteriores; no funcionará ningún carácter comodín, y los prefijos de

anulación también están deshabilitados.

GIT_ICASE_PATHSPECS establece que todas las especificaciones de ruta funcionen sin distinguir entre mayúsculas y minúsculas.

cometer
La creación final de un objeto de confirmación de Git generalmente la realiza git-commit-tree, que usa estas variables de entorno como su
principal fuente de información, recurriendo a los valores de configuración solo si estos no están presentes.

GIT_AUTHOR_NAME es el nombre legible por humanos en el campo "autor".

GIT_AUTHOR_EMAIL es el correo electrónico para el campo "autor".

GIT_AUTHOR_DATE es la marca de tiempo utilizada para el campo "autor".

GIT_COMMITTER_NAME establece el nombre humano para el campo "committer".

GIT_COMMITTER_EMAIL es la dirección de correo electrónico para el campo "committer".

GIT_COMMITTER_DATE se usa para la marca de tiempo en el campo "committer".

EMAIL es la dirección de correo electrónico alternativa en caso de que el valor de configuración de user.email no esté establecido. Si esto no
está configurado, Git recurre al usuario del sistema y los nombres de host.

Redes
Git usa la biblioteca curl para realizar operaciones de red a través de HTTP, por lo que GIT_CURL_VERBOSE le dice a Git que emita todos
los mensajes generados por esa biblioteca. Esto es similar a hacer curl -v en la línea de comandos.

GIT_SSL_NO_VERIFY le dice a Git que no verifique los certificados SSL. Esto a veces puede ser necesario si está utilizando un certificado
autofirmado para servir repositorios Git a través de HTTPS, o si está configurando un servidor Git pero aún no ha instalado un certificado
completo.

Si la velocidad de datos de una operación HTTP es inferior a GIT_HTTP_LOW_SPEED_LIMIT bytes por segundo durante más de
GIT_HTTP_LOW_SPEED_TIME segundos, Git cancelará esa operación. Estos valores anulan los valores de configuración de
http.lowSpeedLimit y http.lowSpeedTime .

466
Machine Translated by Google

GIT_HTTP_USER_AGENT establece la cadena de agente de usuario utilizada por Git cuando se comunica a través de HTTP. El valor
predeterminado es un valor como git/2.0.0.

Diferenciar y fusionar
GIT_DIFF_OPTS es un nombre poco apropiado. Los únicos valores válidos son -u<n> o --unified=<n>, que controla la cantidad de
líneas de contexto que se muestran en un comando git diff .

GIT_EXTERNAL_DIFF se utiliza como anulación del valor de configuración diff.external . Si está configurado, Git invocará este
programa cuando se invoque git diff .

GIT_DIFF_PATH_COUNTER y GIT_DIFF_PATH_TOTAL son útiles desde dentro del programa especificado por GIT_EXTERNAL_DIFF
o diff.external. El primero representa qué archivo de una serie se está diferenciando (empezando por 1), y el último es el número total
de archivos en el lote.

GIT_MERGE_VERBOSITY controla la salida de la estrategia de combinación recursiva. Los valores permitidos son los siguientes:

• 0 no genera nada, excepto posiblemente un único mensaje de error.

• 1 muestra solo conflictos.

• 2 también muestra los cambios de archivo.

• 3 muestra cuando los archivos se omiten porque no han cambiado.

• 4 muestra todas las rutas a medida que se procesan.

• 5 y superiores muestran información detallada de depuración.

El valor predeterminado es 2.

depuración
¿Quieres saber realmente qué está haciendo Git? Git tiene un conjunto bastante completo de seguimientos incrustados, y todo lo que
necesita hacer es activarlos. Los posibles valores de estas variables son los siguientes:

• “verdadero”, “1” o “2”: la categoría de seguimiento se escribe en stderr.

• Una ruta absoluta que comience con / : la salida de seguimiento se escribirá en ese archivo.

GIT_TRACE controla las trazas generales, que no encajan en ninguna categoría específica. Esto incluye la expansión de alias y la
delegación a otros subprogramas.

$ GIT_TRACE=true git lga


20:12:49.877982 git.c:554 rastreo: exec: 'git-lga' rastreo:
20:12:49.878369 run-command.c:341 run_command: 'git-lga' rastreo: alias
20:12:49.879529 git.c:282 '--pretty=oneline' '-- expansión: lga => 'log' '--graph'
abbrev- commit' '--decorate' '--all' 20:12:49.879885 git.c:349 trace: incorporado:
git 'log' '--graph' '-- pretty=oneline' '--abbrev-commit ' '--decorar'
20:12:49.899217
'--todo' run-command.c:341 trace:
run_command: 'less' trace: exec: 'less'

20:12:49.899675 comando de ejecución.c:192

467
Machine Translated by Google

GIT_TRACE_PACK_ACCESS controla el seguimiento del acceso al archivo de paquete. El primer campo es el archivo de paquete que se está
accedido, el segundo es el desplazamiento dentro de ese archivo:

$ GIT_TRACE_PACK_ACCESS=verdadero estado de Git


20:10:12.081397 sha1_file.c:2088 .git/objetos/paquete/paquete-c3fa...291e.paquete 12
20:10:12.081886 sha1_file.c:2088 .git/objetos/paquete/paquete-c3fa...291e.paquete 34662
20:10:12.082115 sha1_file.c:2088 # […] .git/objetos/paquete/paquete-c3fa...291e.paquete 35175

20:10:12.087398 sha1_file.c:2088 .git/objetos/paquete/paquete-e80e...e3d2.paquete


56914983
20:10:12.087419 sha1_file.c:2088 .git/objetos/paquete/paquete-e80e...e3d2.paquete
14303666
En maestro de rama
Su rama está actualizada con 'origen/maestro'.
nada que cometer, directorio de trabajo limpio

GIT_TRACE_PACKET habilita el rastreo a nivel de paquete para operaciones de red.

$ GIT_TRACE_PACKET=verdadero origen git ls-remote


20:15:14.867043 pkt-line.c:46 paquete: paquete git< # servicio=git-upload

20:15:14.867071 pkt-line.c:46 paquete: git< 0000


20:15:14.867079 pkt-line.c:46 paquete: git<
97b8860c071898d9e162678ea1035a8ced2f8b1f HEAD\0multi_ack paquete delgado banda lateral banda
lateral-64k ofs-delta superficial sin progreso etiqueta de inclusión multi_ack_detailed no hecho
symref=HEAD:refs/heads/agente maestro=git/2.0.4
20:15:14.867088 pkt-line.c:46 paquete: git<
0f20ae29889d61f2e93ae00fd34f1cdb53285702 refs/heads/ab/add-interactive-show-diff-func
nombre

20:15:14.867094 pkt-line.c:46 paquete: git<


36dc827bc9d17f80ed4f326de21247a5d1341fbc refs/heads/ah/doc-gitk-config
# […]

GIT_TRACE_PERFORMANCE controla el registro de datos de rendimiento. La salida muestra cuánto tiempo cada

toma particular de invocación de git .

468
Machine Translated by Google

$ GIT_TRACE_PERFORMANCE=verdadero git gc
20:18:19.499676 trace.c:414 'pack- rendimiento: 0.374835000 s: comando git: 'git'
refs' '--all' '--prune'
20:18:19.845585 trace.c:414 'reflog' rendimiento: 0.343020000 s: comando git: 'git'
'expire' '--all'
Contando objetos: 170994, hecho.
Compresión delta usando hasta 8 hilos.
Comprimir objetos: 100 % (43413/43413), listo.
Objetos de escritura: 100% (170994/170994), hecho.
Total 170994 (delta 126176), reutilizado 170524 (delta 125706)
20:18:23.567927 trace.c:414 'pack- rendimiento: 3.715349000 s: comando git: 'git'
objects' '--keep-true-parents' '--honor-pack-keep' '--non-empty' '--all' '--
reflog' '--unpack-unreachable=2.weeks.ago' '--local' '--delta-base-offset'
'.git/objetos/paquete/.tmp-49190-paquete'
20:18:23.584728 trace.c:414 rendimiento: 0.000910000 s: comando git: 'git'
'empaquetado con ciruela pasa'
20:18:23.605218 trace.c:414 rendimiento: 0.017972000 s: comando git: 'git'
'actualizar información del servidor'
20:18:23.606342 trace.c:414 rendimiento: 3.756312000 s: comando git: 'git'
'reempacar' '-d' '-l' '-A' '--unpack-unreachable=2.weeks.ago'
Comprobando conectividad: 170994, hecho.
20:18:25.225424 trace.c:414 'prune' rendimiento: 1.616423000 s: comando git: 'git'
'--expire' '2.weeks.ago'
20:18:25.232403 trace.c:414 'rerere' rendimiento: 0.001051000 s: comando git: 'git'
'gc'
20:18:25.233159 rastreo.c:414 'gc' rendimiento: 6.112217000 s: comando git: 'git'

GIT_TRACE_SETUP muestra información sobre lo que Git está descubriendo sobre el repositorio y
entorno con el que está interactuando.

$ GIT_TRACE_SETUP=verdadero estado de Git


20:19:47.086765 seguimiento.c:315 configuración: git_dir: .git
20:19:47.087184 seguimiento.c:316 configuración: árbol de trabajo: /Users/ben/src/git
20:19:47.087191 seguimiento.c:317 configuración: cwd: /Users/ben/src/git
20:19:47.087194 trace.c:318 En configuración: prefijo: (nulo)
maestro de rama
Su rama está actualizada con 'origen/maestro'.
nada que cometer, directorio de trabajo limpio

Diverso
GIT_SSH, si se especifica, es un programa que se invoca en lugar de ssh cuando Git intenta conectarse a un SSH
anfitrión. Se invoca como $GIT_SSH [nombre de usuario@]host [-p <puerto>] <comando>. Tenga en cuenta que este no es el
forma más fácil de personalizar cómo se invoca ssh ; no admitirá parámetros de línea de comandos adicionales, por lo que
tendría que escribir un script de contenedor y configurar GIT_SSH para señalarlo. Probablemente sea más fácil simplemente usar el
~/.ssh/config archivo para eso.

469
Machine Translated by Google

GIT_ASKPASS es una anulación del valor de configuración de core.askpass . Este es el programa que se invoca cada vez que Git
necesita solicitar las credenciales al usuario, que puede esperar un mensaje de texto como argumento de la línea de comando y
debe devolver la respuesta en la salida estándar (consulte Almacenamiento de credenciales para obtener más información sobre
este subsistema).

GIT_NAMESPACE controla el acceso a las referencias de espacios de nombres y es equivalente a la marca --namespace . Esto
es principalmente útil en el lado del servidor, donde es posible que desee almacenar varias bifurcaciones de un solo repositorio en
un repositorio, manteniendo solo las referencias separadas.

GIT_FLUSH se puede usar para obligar a Git a usar E/S sin búfer al escribir de forma incremental en la salida estándar. Un valor
de 1 hace que Git se vacíe con más frecuencia, un valor de 0 hace que toda la salida se almacene en búfer. El valor predeterminado
(si esta variable no está configurada) es elegir un esquema de almacenamiento en búfer adecuado según la actividad y el modo de
salida.

GIT_REFLOG_ACTION le permite especificar el texto descriptivo escrito en el reflog. Aquí hay un ejemplo:

$ GIT_REFLOG_ACTION="mi acción" git commit --allow-empty -m 'Mi


mensaje' [master 9e3d55a] Mi mensaje $ git reflog -1 9e3d55a HEAD@{0}: mi
acción: Mi mensaje

Resumen
En este punto, debería tener una comprensión bastante buena de lo que hace Git en segundo plano y, hasta cierto punto, cómo se
implementa. Este capítulo ha cubierto varios comandos de plomería, comandos que son de menor nivel y más simples que los
comandos de porcelana que aprendió en el resto del libro. Comprender cómo funciona Git en un nivel inferior debería facilitar la
comprensión de por qué está haciendo lo que está haciendo y también escribir sus propias herramientas y scripts de ayuda para
que su flujo de trabajo específico funcione para usted.

Git como un sistema de archivos direccionable por contenido es una herramienta muy poderosa que puede usar fácilmente como
algo más que un VCS. Esperamos que pueda usar su nuevo conocimiento de las funciones internas de Git para implementar su
propia aplicación genial de esta tecnología y sentirse más cómodo usando Git de formas más avanzadas.

470
Machine Translated by Google

Apéndice A: Git en otros entornos


Si leyó todo el libro, habrá aprendido mucho sobre cómo usar Git en la línea de comandos.
Puede trabajar con archivos locales, conectar su repositorio a otros a través de una red y trabajar de manera efectiva con
otros. Pero la historia no termina ahí; Git generalmente se usa como parte de un ecosistema más grande, y la terminal no
siempre es la mejor manera de trabajar con él. Ahora veremos algunos de los otros tipos de entornos en los que Git puede
ser útil y cómo funcionan otras aplicaciones (incluida la suya) junto con Git.

Interfaces gráficas
El entorno nativo de Git está en la terminal. Las nuevas funciones aparecen allí primero, y solo en la línea de comando está
completamente a su disposición todo el poder de Git. Pero el texto sin formato no es la mejor opción para todas las tareas; a
veces lo que necesita es una representación visual, y algunos usuarios se sienten mucho más cómodos con una interfaz de
apuntar y hacer clic.

Es importante tener en cuenta que las diferentes interfaces se adaptan a los diferentes flujos de trabajo. Algunos clientes
exponen solo un subconjunto cuidadosamente seleccionado de la funcionalidad de Git, para admitir una forma específica de
trabajo que el autor considera efectiva. Visto desde este punto de vista, ninguna de estas herramientas puede llamarse "mejor"
que las demás, simplemente son más adecuadas para su propósito previsto. También tenga en cuenta que no hay nada que
estos clientes gráficos puedan hacer que el cliente de línea de comandos no pueda hacer; la línea de comandos sigue siendo
donde tendrá más poder y control cuando trabaje con sus repositorios.

gitk y git-gui
Cuando instalas Git, también obtienes sus herramientas visuales, gitk y git-gui.

gitk es un visor de historial gráfico. Piense en ello como un poderoso shell de GUI sobre git log y git grep.
Esta es la herramienta que debe usar cuando intenta encontrar algo que sucedió en el pasado o visualizar el historial de su
proyecto.

Gitk es más fácil de invocar desde la línea de comandos. Simplemente cd en un repositorio de Git y escriba:

$ gitk [opciones de registro de git]

Gitk acepta muchas opciones de línea de comandos, la mayoría de las cuales se pasan a la acción de registro de git
subyacente . Probablemente uno de los más útiles es el indicador --all , que le dice a gitk que muestre confirmaciones
accesibles desde cualquier referencia, no solo HEAD. La interfaz de Gitk se ve así:

471
Machine Translated by Google

Figura 151. El visor de historial de gitk

En la parte superior hay algo que se parece un poco a la salida de git log --graph; cada punto representa una
confirmación, las líneas representan las relaciones principales y las referencias se muestran como cuadros de colores.
El punto amarillo representa HEAD y el punto rojo representa los cambios que aún no se han confirmado. En la parte
inferior hay una vista de la confirmación seleccionada; los comentarios y el parche a la izquierda, y una vista de resumen
a la derecha. En el medio hay una colección de controles utilizados para buscar en el historial.

git-gui, por otro lado, es principalmente una herramienta para crear compromisos. También es más fácil invocarlo desde
la línea de comandos:

$ git interfaz gráfica de usuario

Y se ve algo como esto:

472
Machine Translated by Google

Figura 152. La herramienta de confirmación git-gui

A la izquierda está el índice; los cambios no organizados están en la parte superior, los cambios organizados en la parte inferior. Puede
mover archivos completos entre los dos estados haciendo clic en sus iconos, o puede seleccionar un archivo para verlo haciendo clic en su
nombre.

En la parte superior derecha está la vista de diferencias, que muestra los cambios del archivo seleccionado actualmente. Puede organizar
fragmentos individuales (o líneas individuales) haciendo clic con el botón derecho en esta área.

En la parte inferior derecha está el área de mensajes y acciones. Escriba su mensaje en el cuadro de texto y haga clic en "Confirmar" para
hacer algo similar a git commit. También puede optar por modificar la última confirmación eligiendo el botón de opción "Modificar", que
actualizará el área "Cambios por etapas" con el contenido de la última confirmación. Luego, simplemente puede realizar o cancelar algunos
cambios, modificar el mensaje de confirmación y hacer clic en "Confirmar" nuevamente para reemplazar la confirmación anterior por una
nueva.

gitk y git-gui son ejemplos de herramientas orientadas a tareas. Cada uno de ellos está diseñado para un propósito específico (ver el historial
y crear confirmaciones, respectivamente) y omite las funciones que no son necesarias para esa tarea.

GitHub para macOS y Windows

GitHub ha creado dos clientes Git orientados al flujo de trabajo: uno para Windows y otro para macOS. Estos clientes son un buen ejemplo

de herramientas orientadas al flujo de trabajo: en lugar de exponer toda la funcionalidad de Git, se centran en un conjunto seleccionado de
funciones de uso común que funcionan bien juntas. Se ven así:

473
Machine Translated by Google

Figura 153. GitHub para macOS

Figura 154. GitHub para Windows

Están diseñados para verse y funcionar de manera muy similar, por lo que los trataremos como un solo producto en este
capítulo. No vamos a hacer un resumen detallado de estas herramientas (tienen su propia documentación), pero es
necesario un recorrido rápido por la vista de "cambios" (que es donde pasará la mayor parte de su tiempo).

• A la izquierda está la lista de repositorios que el cliente está rastreando; puede agregar un repositorio (ya sea por
clonar o adjuntar localmente) haciendo clic en el ícono "+" en la parte superior de esta área.

• En el centro hay un área de entrada de confirmación, que le permite ingresar un mensaje de confirmación y seleccionar
qué archivos deben incluirse. En Windows, el historial de confirmaciones se muestra directamente debajo de esto; en

474
Machine Translated by Google

macOS, está en una pestaña separada.

• A la derecha hay una vista de diferencias, que muestra lo que ha cambiado en su directorio de trabajo, o qué cambios se incluyeron
en la confirmación seleccionada.

• Lo último que debe notar es el botón "Sincronizar" en la parte superior derecha, que es la forma principal en que
interactuar a través de la red.

No necesita una cuenta de GitHub para usar estas herramientas. Si bien están diseñados para resaltar el servicio

ÿ de GitHub y el flujo de trabajo recomendado, trabajarán felizmente con cualquier repositorio y realizarán
operaciones de red con cualquier host de Git.

Instalación

GitHub para Windows se puede descargar desde https://windows.github.com, y GitHub para macOS desde https://mac.github.com.
Cuando las aplicaciones se ejecutan por primera vez, lo guían a través de toda la configuración de Git por primera vez, como configurar
su nombre y dirección de correo electrónico, y ambos configuran valores predeterminados para muchas opciones de configuración
comunes, como cachés de credenciales y comportamiento CRLF.

Ambos son "siempre verdes": las actualizaciones se descargan e instalan en segundo plano mientras las aplicaciones están abiertas.
Esto incluye una versión integrada de Git, lo que significa que probablemente no tendrá que preocuparse por actualizarlo manualmente
nuevamente. En Windows, el cliente incluye un acceso directo para iniciar PowerShell con Posh-git, del que hablaremos más adelante
en este capítulo.

El siguiente paso es darle a la herramienta algunos repositorios para trabajar. El cliente le muestra una lista de los repositorios a los que
tiene acceso en GitHub y puede clonarlos en un solo paso. Si ya tiene un repositorio local, simplemente arrastre su directorio desde el
Finder o el Explorador de Windows a la ventana del cliente de GitHub y se incluirá en la lista de repositorios de la izquierda.

Flujo de trabajo recomendado

Una vez que esté instalado y configurado, puede usar el cliente de GitHub para muchas tareas comunes de Git. El flujo de trabajo
previsto para esta herramienta a veces se denomina "Flujo de GitHub". Cubrimos esto con más detalle.

en The GitHub Flow, pero la esencia general es que (a) se comprometerá con una rama, y (b) se sincronizará con un repositorio remoto
con bastante regularidad.

La gestión de sucursales es una de las áreas donde las dos herramientas divergen. En macOS, hay un botón en la parte superior de la
ventana para crear una nueva rama:

Figura 155. Botón “Crear sucursal” en macOS

En Windows, esto se hace escribiendo el nombre de la nueva sucursal en el widget de cambio de sucursal:

475
Machine Translated by Google

Figura 156. Creando una rama en Windows

Una vez que se crea su rama, hacer nuevas confirmaciones es bastante sencillo. Realice algunos cambios en su directorio
de trabajo, y cuando cambie a la ventana del cliente de GitHub, le mostrará qué archivos cambiaron. Ingrese un mensaje
de confirmación, seleccione los archivos que desea incluir y haga clic en el botón "Confirmar" (ctrl-enter o ÿ-enter).

La forma principal de interactuar con otros repositorios a través de la red es a través de la función "Sincronizar".
Git internamente tiene operaciones separadas para empujar, buscar, fusionar y reorganizar, pero los clientes de GitHub
las agrupan todas en una función de varios pasos. Esto es lo que sucede cuando haces clic en el botón Sincronizar:

1. git pull --rebase. Si esto falla debido a un conflicto de fusión, recurra a git pull --no-rebase.

2. empujar git.

Esta es la secuencia más común de comandos de red cuando se trabaja con este estilo, por lo que agruparlos en un solo
comando ahorra mucho tiempo.

Resumen

Estas herramientas son muy adecuadas para el flujo de trabajo para el que están diseñadas. Tanto los desarrolladores
como los que no son desarrolladores pueden colaborar en un proyecto en cuestión de minutos, y muchas de las mejores
prácticas para este tipo de flujo de trabajo están integradas en las herramientas. Sin embargo, si su flujo de trabajo es
diferente o desea tener más control sobre cómo y cuándo se realizan las operaciones de red, le recomendamos que utilice
otro cliente o la línea de comandos.

Otras GUI
Hay una serie de otros clientes gráficos de Git, y van desde herramientas especializadas de un solo propósito hasta
aplicaciones que intentan exponer todo lo que Git puede hacer. El sitio web oficial de Git tiene una lista seleccionada de
los clientes más populares en https://git-scm.com/downloads/guis. Una lista más completa está disponible en el sitio wiki
de Git, en https://git.wiki.kernel.org/index.php/Interfaces,_frontends,_and_tools#Graphical_Interfaces .

Git en Visual Studio


Visual Studio tiene herramientas de Git integradas directamente en el IDE, comenzando con Visual Studio 2019 versión
16.8.

La herramienta admite la siguiente funcionalidad de Git:

476
Machine Translated by Google

• Crear o clonar un repositorio.

• Abrir y examinar el historial de un repositorio.

• Crear y pagar sucursales y etiquetas.

• Guardar, preparar y confirmar cambios.

• Obtener, extraer, enviar o sincronizar confirmaciones.

• Fusionar y reorganizar ramas.

• Resolver conflictos de fusión.

• Ver diferencias.

• … ¡y más!

Lea la documentación oficial aprender más.

Git en el código de Visual Studio

Visual Studio Code tiene soporte para git incorporado. Deberá tener instalada la versión 2.0.0 (o posterior) de git.

Las características principales son:

• Vea la diferencia del archivo que está editando en el canalón.

• La barra de estado de Git (abajo a la izquierda) muestra la rama actual, los indicadores sucios, entrantes y salientes.
confirmaciones salientes.

• Puede realizar las operaciones de git más comunes desde el editor:

ÿ Inicializar un repositorio.

ÿ Clonar un repositorio.

ÿ Crear ramas y etiquetas.

ÿ Preparar y confirmar cambios.

ÿ Empujar/tirar/sincronizar con una sucursal remota.

ÿ Resolver conflictos de fusión.

ÿ Ver diferencias.

• Con una extensión, puede además manejar GitHub Pull Solicitudes:


https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github.

La documentación oficial se puede encontrar aquí: https://code.visualstudio.com/Docs/editor/versioncontrol .

Git en IntelliJ / PyCharm / WebStorm / PhpStorm /


RubyMine
Los IDE de JetBrains (como IntelliJ IDEA, PyCharm, WebStorm, PhpStorm, RubyMine y otros) se envían con un complemento de
integración de Git. Proporciona una vista dedicada en el IDE para trabajar con Git y GitHub

477
Machine Translated by Google

Solicitudes de extracción.

Figura 157. Ventana de herramientas de control de versiones en los IDE de JetBrains

La integración se basa en el cliente git de la línea de comandos y requiere que se instale uno. La documentación oficial está disponible en
https://www.jetbrains.com/help/idea/using-git-integration.html.

Git en texto sublime


Desde la versión 3.2 en adelante, Sublime Text tiene integración de git en el editor.

Las características son:

• La barra lateral mostrará el estado de git de los archivos y carpetas con una insignia/icono.

• Los archivos y carpetas que están en su archivo .gitignore se desvanecerán en la barra lateral.

• En la barra de estado, puede ver la rama actual de git y cuántas modificaciones tiene
hecha.

• Todos los cambios en un archivo ahora son visibles a través de marcadores en el medianil.

• Puede usar parte de la funcionalidad del cliente git de Sublime Merge desde Sublime Text. Esta

requiere que Sublime Merge esté instalado. Ver: https://www.sublimemerge.com/.

La documentación oficial de Sublime Text se puede encontrar aquí: https://www.sublimetext.com/docs/ 3/git_integration.html.

Git en bash
Si es un usuario de Bash, puede aprovechar algunas de las funciones de su shell para que su experiencia con Git sea mucho más
amigable. En realidad, Git incluye complementos para varios shells, pero no está activado de forma predeterminada.

Primero, debe obtener una copia del archivo de finalización del código fuente de la versión de Git que está utilizando. Verifique su versión
escribiendo git version, luego use git checkout tags/vX.YZ, donde vX.YZ corresponde a la versión de Git que está usando. Copie el archivo
contrib/completion/git-completion.bash en algún lugar a mano, como su directorio de inicio, y agréguelo a su .bashrc:

. ~/git-completion.bash

Una vez hecho esto, cambie su directorio a un repositorio de Git y escriba:

478
Machine Translated by Google

$ git cheque<tab>

… y Bash se autocompletará para finalizar la compra. Esto funciona con todos los subcomandos de Git,
parámetros de la línea de comandos y controles remotos y nombres de referencia cuando corresponda.

También es útil personalizar su indicador para mostrar información sobre el Git del directorio actual
repositorio. Esto puede ser tan simple o complejo como desee, pero generalmente hay algunas piezas clave
de la información que la mayoría de la gente quiere, como la sucursal actual y el estado del trabajo
directorio. Para agregarlos a su aviso, simplemente copie el archivo contrib/completion/git-prompt.sh de
Repositorio fuente de Git a su directorio de inicio, agregue algo como esto a su .bashrc:

. ~/git-prompt.sh
exportar GIT_PS1_SHOWDIRTYSTATE=1
exportar PS1='\w$(__git_ps1 " (%s)")\$ '

\ w significa imprimir el directorio de trabajo actual, \$ imprime la parte $ del mensaje y


__git_ps1 " (%s)" llama a la función proporcionada por git-prompt.sh con un argumento de formato. Ahora
su indicador de bash se verá así cuando esté en cualquier lugar dentro de un proyecto controlado por Git:

Figura 158. Mensaje bash personalizado

Ambos scripts vienen con documentación útil; eche un vistazo a los contenidos de git finish.bash y git-prompt.sh para obtener más
información.

Git en Zsh
Zsh también incluye una biblioteca de finalización de pestañas para Git. Para usarlo, simplemente ejecute autoload -Uz compinit &&
compinit en su .zshrc. La interfaz de Zsh es un poco más poderosa que la de Bash:

$ git che<tab>
verificar-atributo -- mostrar información de gitattributes
check-ref-format: asegúrese de que un nombre de referencia esté bien formado
índice de -- rama de pago o caminos al árbol de trabajo
pago -- copiar archivos del índice al directorio de trabajo
pico de -- encontrar confirmaciones no fusionadas aguas arriba
cereza -- aplicar cambios introducidos por algunas confirmaciones existentes

479
Machine Translated by Google

Las fichas completadas ambiguas no solo se enumeran; tienen descripciones útiles, y puede navegar gráficamente
por la lista presionando repetidamente la pestaña. Esto funciona con los comandos de Git, sus argumentos y los
nombres de las cosas dentro del repositorio (como referencias y controles remotos), así como los nombres de archivo
y todas las demás cosas que Zsh sabe cómo completar con tabulación.

Zsh viene con un marco para obtener información de los sistemas de control de versiones, llamado vcs_info.
Para incluir el nombre de la rama en el indicador del lado derecho, agregue estas líneas a su archivo ~/.zshrc :

carga automática -Uz


vcs_info precmd_vcs_info()
{ vcs_info }
precmd_functions+=( precmd_vcs_info ) setopt prompt_subst
RPMT='${vcs_info_msg_0_}'
# PROMPT='${vcs_info_msg_0_}%#
' zstyle ':vcs_info:git:*' formatea '%b'

Esto da como resultado una visualización de la rama actual en el lado derecho de la ventana del terminal, siempre
que su shell esté dentro de un repositorio de Git. El lado izquierdo también es compatible, por supuesto; simplemente
descomente la asignación a PROMPT. Se parece un poco a esto:

Figura 159. Indicador zsh personalizado

Para obtener más información sobre vcs_info, consulte su documentación en la página del manual de zshcontrib(1) ,
o en línea en http://zsh.sourceforge.net/Doc/Release/User-Contributions.html#Version-Control Information.

En lugar de vcs_info, es posible que prefiera el script de personalización de solicitud que se incluye con Git, llamado
git-prompt.sh; consulte https://github.com/git/git/blob/master/contrib/completion/git-prompt.sh por

480
Machine Translated by Google

detalles. git-prompt.sh es compatible tanto con Bash como con Zsh.

Zsh es lo suficientemente poderoso como para que haya marcos completos dedicados a mejorarlo. Uno de ellos
se llama "oh-my-zsh" y se puede encontrar en https://github.com/robbyrussell/oh-my-zsh. El sistema de
complementos de oh-my-zsh viene con un poderoso git tab-completion, y tiene una variedad de "temas" de
solicitud, muchos de los cuales muestran datos de control de versiones. Un ejemplo de un tema oh-my-zsh es
solo un ejemplo de lo que se puede hacer con este sistema.

Figura 160. Un ejemplo de un tema oh-my-zsh

Git en PowerShell
El terminal de línea de comandos heredado en Windows (cmd.exe) no es realmente capaz de una experiencia
Git personalizada, pero si está usando PowerShell, está de suerte. Esto también funciona si está ejecutando
PowerShell Core en Linux o macOS. Un paquete llamado posh-git (https://github.com/dahlbyk/posh git)
proporciona poderosas funciones de finalización de pestañas, así como un aviso mejorado para ayudarlo a
mantenerse al tanto del estado de su repositorio. Se parece a esto:

Figura 161. PowerShell con Posh-git

Instalación

Requisitos previos (solo Windows)

Antes de que pueda ejecutar secuencias de comandos de PowerShell en su máquina, debe configurar su
ExecutionPolicy local en RemoteSigned (básicamente, cualquier cosa excepto Indefinido y Restringido). Si tu escoges

481
Machine Translated by Google

AllSigned en lugar de RemoteSigned, también los scripts locales (los suyos) deben estar firmados digitalmente para
poder ejecutarse. Con RemoteSigned, solo los scripts que tienen el ZoneIdentifier establecido en Internet (fueron
descargados de la web) deben firmarse, otros no. Si es administrador y desea configurarlo para todos los usuarios
en esa máquina, use -Scope LocalMachine. Si es un usuario normal, sin derechos administrativos, puede usar -Scope
CurrentUser para configurarlo solo para usted.

Más sobre Ámbitos de PowerShell: https://docs.microsoft.com/en-us/powershell/module/


microsoft.powershell.core/about/about_scopes.

Más información sobre la política de ejecución de PowerShell: https://docs.microsoft.com/en-us/powershell/module/


microsoft.powershell.security/set-executionpolicy .

Para establecer el valor de ExecutionPolicy en RemoteSigned para todos los usuarios, use el siguiente comando:

> Establecer-ExecutionPolicy-Scope LocalMachine-ExecutionPolicy RemoteSigned-Force

Galería de PowerShell

Si tiene al menos PowerShell 5 o PowerShell 4 con PackageManagement instalado, puede usar el administrador de
paquetes para instalar posh-git por usted.

Más información sobre la galería de PowerShell: https://docs.microsoft.com/en-us/powershell/scripting/gallery/


overview .

> Install-Module posh-git -Scope CurrentUser -Force


> Install-Module posh-git -Scope CurrentUser -AllowPrerelease -Force # Versión beta más
reciente con compatibilidad con PowerShell Core

Si desea instalar posh-git para todos los usuarios, use -Scope AllUsers en su lugar y ejecute el comando desde una
consola de PowerShell con privilegios elevados. Si el segundo comando falla con un error como Módulo
'PowerShellGet' no se instaló mediante Install-Module, primero deberá ejecutar otro comando:

> Instalar-Módulo PowerShellGet -Force -SkipPublisherCheck

A continuación, puede volver atrás e intentarlo de nuevo. Esto sucede porque los módulos que se envían con
Windows PowerShell están firmados con un certificado de publicación diferente.

Actualizar solicitud de PowerShell

Para incluir información de git en su solicitud, se debe importar el módulo posh-git. Para que se importe Posh Git
cada vez que se inicia PowerShell, ejecute el comando Add-PoshGitToProfile que agregará la declaración de
importación en su secuencia de comandos $profile . Este script se ejecuta cada vez que abre una nueva consola de
PowerShell. Tenga en cuenta que hay varios scripts de $profile . P.ej. uno para la consola y otro separado para el
ISE.

482
Machine Translated by Google

> Módulo de importación posh-git


> Agregar-PoshGitToProfile-AllHosts

De la fuente

Simplemente descargue una versión de posh-git de https://github.com/dahlbyk/posh-git/releases, y descomprimirlo.


Luego importe el módulo usando la ruta completa al archivo posh-git.psd1 :

> Módulo de importación <ruta-para-descomprimir-carpeta>\src\posh-git.psd1 > Add-


PoshGitToProfile -AllHosts

Esto agregará la línea adecuada a su archivo profile.ps1 y posh-git estará activo la próxima vez que abra
PowerShell.

Para obtener una descripción de la información de resumen del estado de Git que se muestra en el aviso,
consulte: https://github.com/dahlbyk/posh-git/blob/master/README.md#git-status-summary-information Para
obtener más detalles sobre cómo personalizar su solicitud de posh-git, consulte: https://github.com/dahlbyk/posh-
git/blob/master/README.md#customization-variables .

Resumen
Aprendió cómo aprovechar el poder de Git desde el interior de las herramientas que usa durante su trabajo diario
y también cómo acceder a los repositorios de Git desde sus propios programas.

483
Machine Translated by Google

Apéndice B: Incrustar Git en su


Aplicaciones
Si su aplicación es para desarrolladores, es muy probable que se beneficie de la integración con el control de código
fuente. Incluso las aplicaciones que no son de desarrollador, como los editores de documentos, podrían beneficiarse
potencialmente de las funciones de control de versiones, y el modelo de Git funciona muy bien para muchos escenarios
diferentes.

Si necesita integrar Git con su aplicación, básicamente tiene dos opciones: generar un shell y llamar al programa de línea
de comandos de git , o incrustar una biblioteca de Git en su aplicación. Aquí cubriremos la integración de la línea de
comandos y varias de las bibliotecas Git integrables más populares.

Git de línea de comandos

Una opción es generar un proceso de shell y usar la herramienta de línea de comandos de Git para hacer el trabajo. Esto
tiene la ventaja de ser canónico y todas las características de Git son compatibles. Esto también resulta bastante fácil,
ya que la mayoría de los entornos de tiempo de ejecución tienen una función relativamente simple para invocar un
proceso con argumentos de línea de comandos. Sin embargo, este enfoque tiene algunas desventajas.

Una es que toda la salida está en texto sin formato. Esto significa que tendrá que analizar el formato de salida que
cambia ocasionalmente de Git para leer el progreso y la información de resultados, lo que puede ser ineficiente y
propenso a errores.

Otro es la falta de recuperación de errores. Si un repositorio está dañado de alguna manera, o si el usuario tiene un valor
de configuración incorrecto, Git simplemente se negará a realizar muchas operaciones.

Otro más es la gestión de procesos. Git requiere que mantengas un entorno de shell en un proceso separado, lo que
puede agregar una complejidad no deseada. Tratar de coordinar muchos de estos procesos (especialmente cuando se
accede potencialmente al mismo repositorio desde varios procesos) puede ser todo un desafío.

libgit2
Otra opción a tu disposición es usar Libgit2. Libgit2 es una implementación de Git sin dependencias, con un enfoque en
tener una buena API para usar dentro de otros programas. Puede encontrarlo en https://libgit2.org.

Primero, echemos un vistazo a cómo se ve la API de C. Aquí hay un recorrido relámpago:

484
Machine Translated by Google

// Abrir un repositorio
git_repository *repo; int
error = git_repository_open(&repo, "/ruta/al/repositorio");

// Eliminar la referencia de HEAD a


una confirmación git_object
*head_commit; error = git_revparse_single(&head_commit, repositorio,
"HEAD^{commit}"); git_commit *commit = (git_commit*)head_commit;

// Imprime algunas de las propiedades de la


confirmación printf("%s",
git_commit_message(commit)); const git_signature *autor =
git_commit_author(commit); printf("%s <%s>\n", autor->nombre,
autor->email); const git_oid *tree_id = git_commit_tree_id(confirmar);

// Limpiar
git_commit_free(commit);
git_repository_free(repositorio);

El primer par de líneas abre un repositorio de Git. El tipo git_repository representa un identificador de un repositorio con un
caché en la memoria. Este es el método más simple, para cuando conoce la ruta exacta al directorio de trabajo o carpeta .git
de un repositorio. También está el git_repository_open_ext que incluye opciones para buscar, git_clone y amigos para hacer
un clon local de un repositorio remoto y git_repository_init para crear un repositorio completamente nuevo.

El segundo fragmento de código usa la sintaxis rev-parse (consulte Referencias de sucursales para obtener más información
sobre esto) para obtener la confirmación a la que finalmente apunta HEAD. El tipo devuelto es un puntero git_object , que
representa algo que existe en la base de datos de objetos de Git para un repositorio. git_object es en realidad un tipo
"principal" para varios tipos diferentes de objetos; el diseño de la memoria para cada uno de los tipos "secundarios" es el
mismo que para git_object, por lo que puede enviar con seguridad al correcto. En este caso, git_object_type(commit)
devolvería GIT_OBJ_COMMIT, por lo que es seguro convertirlo en un puntero de git_commit .

El siguiente fragmento muestra cómo acceder a las propiedades de la confirmación. La última línea aquí usa un tipo git_oid ;
esta es la representación de Libgit2 para un hash SHA-1.

A partir de esta muestra, han comenzado a surgir un par de patrones:

• Si declara un puntero y pasa una referencia a él en una llamada de Libgit2, esa llamada probablemente
devolver un código de error entero. Un valor 0 indica éxito; cualquier cosa menos es un error.

• Si Libgit2 llena un puntero por usted, usted es responsable de liberarlo.

• Si Libgit2 devuelve un puntero const de una llamada, no tiene que liberarlo, pero dejará de ser válido
cuando el objeto al que pertenece es liberado.

• Escribir C es un poco doloroso.

Eso último significa que no es muy probable que esté escribiendo C cuando use Libgit2. Afortunadamente, hay varios
enlaces específicos del idioma disponibles que facilitan bastante el trabajo con los repositorios de Git desde su idioma y
entorno específicos. Echemos un vistazo al ejemplo anterior escrito con los enlaces de Ruby para Libgit2, que se denominan
Rugged y se pueden encontrar en

485
Machine Translated by Google

https://github.com/libgit2/rugged.

repo = Rugged::Repository.new('ruta/al/repositorio')
commit = repo.head.target puts commit.message puts
"#{commit.author[:name]} <#{commit.author[:email] }>"
árbol = cometer.árbol

Como puede ver, el código está mucho menos desordenado. En primer lugar, Rugged usa excepciones; puede generar
cosas como ConfigError u ObjectError para indicar condiciones de error. En segundo lugar, no hay una liberación explícita de
recursos, ya que Ruby se recolecta como basura. Echemos un vistazo a un ejemplo un poco más complicado: crear un
compromiso desde cero

blob_id = repo.write("Contenido del blob", :blob) ÿ

index = repo.index
index.read_tree(repo.head.target.tree)
index.add(:path => 'newfile.txt', :oid => blob_id) ÿ

sig =
{ :email => "bob@example.com", :name
=> "Usuario Bob", :time =>
Time.now, }

commit_id = Rugged::Commit.create(repo, :tree


=> index.write_tree(repo), ÿ :author =>
sig, :committer => sig, ÿ :message => "Add
newfile.txt", ÿ :parents => repo.empty? ?
[] : [ repo.head.target ].compact,
ÿ :update_ref => 'HEAD', ÿ ) commit = repo.lookup(commit_id) ÿ

ÿ Cree un nuevo blob, que contenga el contenido de un nuevo archivo.

ÿ Rellene el índice con el árbol de confirmación principal y agregue el nuevo archivo en la ruta newfile.txt.

ÿ Esto crea un nuevo árbol en el ODB y lo usa para la nueva confirmación.

ÿ Utilizamos la misma firma para los campos de autor y autor.

ÿ El mensaje de confirmación.

ÿ Al crear una confirmación, debe especificar los padres de la nueva confirmación. Esto usa la punta de
HEAD para el padre soltero.

ÿ Rugged (y Libgit2) pueden actualizar opcionalmente una referencia al realizar una confirmación.

ÿ El valor devuelto es el hash SHA-1 de un nuevo objeto de confirmación, que luego puede usar para obtener un
Objeto de compromiso .

486
Machine Translated by Google

El código de Ruby es agradable y limpio, pero dado que Libgit2 está haciendo el trabajo pesado, este código también se ejecutará bastante

rápido. Si no eres un rubyista, mencionamos algunas otras encuadernaciones en Otras encuadernaciones.

Funcionalidad avanzada
Libgit2 tiene un par de capacidades que están fuera del alcance del núcleo de Git. Un ejemplo es la capacidad de conexión: Libgit2 le permite

proporcionar "backends" personalizados para varios tipos de operaciones, por lo que puede almacenar cosas de una manera diferente a como

lo hace Git estándar. Libgit2 permite backends personalizados para la configuración, el almacenamiento de referencias y la base de datos de

objetos, entre otras cosas.

Echemos un vistazo a cómo funciona esto. El siguiente código se tomó prestado del conjunto de ejemplos de back-end proporcionados por el

equipo de Libgit2 (que se puede encontrar en https://github.com/libgit2/libgit2-backends).

Así es como se configura un backend personalizado para la base de datos de objetos:

git_odb *odb;
int error = git_odb_new(&odb); ÿ

git_odb_backend *mi_backend;
error = git_odb_backend_mine(&my_backend, /*…*/); ÿ

error = git_odb_add_backend(odb, my_backend, 1); ÿ

git_repository *
repositorio; error = git_repository_open(&repo, "alguna
ruta"); error = git_repository_set_odb(repositorio, odb); ÿ

Tenga en cuenta que los errores se capturan, pero no se controlan. Esperamos que su código sea mejor que el nuestro.

ÿ Inicialice una "frontend" de base de datos de objetos vacía (ODB), que actuará como un contenedor para el

"backends", que son los que hacen el trabajo real.

ÿ Inicialice un backend ODB personalizado.

ÿ Agregue el backend al frontend.

ÿ Abra un repositorio y configúrelo para usar nuestro ODB para buscar objetos.

Pero, ¿qué es esto de git_odb_backend_mine ? Bueno, ese es el constructor para su propia implementación de ODB, y puede hacer lo que

quiera allí, siempre que complete la estructura git_odb_backend correctamente. Así es como podría verse:

487
Machine Translated by Google

estructura typedef
{ git_odb_backend padre;

// Algunas otras cosas void


*custom_context; }
my_backend_struct;

int git_odb_backend_mine(git_odb_backend **backend_out, /*…*/) { my_backend_struct


*backend;

backend = calloc(1, sizeof (my_backend_struct));

backend->contexto_personalizado = …;

backend->parent.read = &my_backend__read; backend-


>parent.read_prefix = &my_backend__read_prefix; backend->parent.read_header
= &my_backend__read_header; // …

*backend_out = (git_odb_backend *) backend;

devolver GIT_ÉXITO; }

La restricción más sutil aquí es que el primer miembro de my_backend_struct debe ser una estructura
`git_odb_backend; esto asegura que el diseño de la memoria sea lo que el código Libgit2 espera que sea. El
resto es arbitrario; esta estructura puede ser tan grande o pequeña como necesites que sea.

La función de inicialización asigna algo de memoria para la estructura, configura el contexto personalizado y
luego completa los miembros de la estructura principal que admite. Eche un vistazo al archivo include/git2/sys/
odb_backend.h en el código fuente de Libgit2 para obtener un conjunto completo de firmas de llamada; su caso
de uso particular ayudará a determinar cuál de estos querrá admitir.

Otras encuadernaciones

Libgit2 tiene enlaces para muchos idiomas. Aquí mostramos un pequeño ejemplo utilizando algunos de los
paquetes de enlaces más completos a partir de este escrito; existen bibliotecas para muchos otros lenguajes,
incluidos C++, Go, Node.js, Erlang y JVM, todos en diversas etapas de madurez. La colección oficial de enlaces
se puede encontrar navegando por los repositorios en https://github.com/libgit2. El código que escribiremos
devolverá el mensaje de confirmación de la confirmación señalada eventualmente por HEAD (algo así como git
log -1).

LibGit2Sharp

Si está escribiendo una aplicación .NET o Mono, LibGit2Sharp (https://github.com/libgit2/libgit2sharp) es lo que


buscas Los enlaces están escritos en C#, y se ha tenido mucho cuidado para envolver las llamadas Libgit2 sin
formato con API CLR de sensación nativa. Así es como se ve nuestro programa de ejemplo:

488
Machine Translated by Google

new Repository(@"C:\path\to\repo").Head.Tip.Message;

Para las aplicaciones de escritorio de Windows, incluso hay un paquete NuGet que lo ayudará a comenzar rápidamente.

objetivo-git

Si su aplicación se ejecuta en una plataforma Apple, es probable que esté utilizando Objective-C como lenguaje de
implementación. Objetivo-Git (https://github.com/libgit2/objective-git) es el nombre de los enlaces Libgit2 para ese entorno.
El programa de ejemplo se ve así:

GTRepositorio *repo =
[[GTRepository alloc] initWithURL:[NSURL fileURLWithPath: @"/path/to/repo"] error:NULL];

NSString *msg = [[[repo headReferenceWithError:NULL] resolveTarget] mensaje];

Objective-git es totalmente interoperable con Swift, así que no temas si te has olvidado de Objective-C.

pygit2

Los enlaces para Libgit2 en Python se denominan Pygit2 y se pueden encontrar en https://www.pygit2.org.
Nuestro programa de ejemplo:

pygit2.Repository("/ruta/al/repositorio") # abrir repositorio .head #


obtener la rama actual .peel(pygit2.Commit) # caminar hasta la
confirmación # leer el mensaje

.mensaje

Otras lecturas
Por supuesto, un tratamiento completo de las capacidades de Libgit2 está fuera del alcance de este libro. Si desea
obtener más información sobre Libgit2, hay documentación de API en https://libgit2.github.com/libgit2, y un conjunto de
guías en https://libgit2.github.com/docs. Para los otros enlaces, consulte el LÉAME y las pruebas incluidas; a menudo hay
pequeños tutoriales y consejos para leer más allí.

JGit
Si desea utilizar Git desde un programa Java, hay una biblioteca de Git con todas las funciones llamada JGit.
JGit es una implementación relativamente completa de Git escrita de forma nativa en Java y se usa ampliamente en la
comunidad de Java. El proyecto JGit está bajo el paraguas de Eclipse, y su hogar se puede encontrar en https://
www.eclipse.org/jgit/.

Preparándose
Hay varias formas de conectar su proyecto con JGit y comenzar a escribir código en él.
Probablemente lo más fácil sea usar Maven: la integración se logra agregando lo siguiente

489
Machine Translated by Google

fragmento a la etiqueta <dependencies> en su archivo pom.xml:

<dependencia>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>3.5.0.201409260305-r</version> </
dependency>

Lo más probable es que la versión haya avanzado para cuando lea esto; consulte https://mvnrepository.com/artifact/org.eclipse.jgit/
por automáticamente
org.eclipse.jgit información del repositorio. Una vez realizado este paso, Maven adquirirá y utilizará actualizado las bibliotecas
que necesitará.
JGit

Si prefiere administrar las dependencias binarias usted mismo, los binarios JGit preconstruidos están disponibles en https://www.eclipse.org/
jgit/download. Puede incorporarlos a su proyecto ejecutando un comando como este:

javac -cp .:org.eclipse.jgit-3.5.0.201409260305-r.jar Aplicación.java


java -cp .:org.eclipse.jgit-3.5.0.201409260305-r.jar Aplicación

Plomería
JGit tiene dos niveles básicos de API: plomería y porcelana. La terminología para estos proviene de Git mismo, y JGit se divide
aproximadamente en los mismos tipos de áreas: las API de porcelana son una interfaz fácil de usar para acciones comunes a nivel de
usuario (el tipo de cosas que un usuario normal usaría la herramienta de línea de comandos de Git para ), mientras que las API de
plomería son para interactuar directamente con objetos de repositorio de bajo nivel.

El punto de partida para la mayoría de las sesiones de JGit es la clase Repository , y lo primero que querrá hacer es crear una instancia
de ella. Para un repositorio basado en un sistema de archivos (sí, JGit permite otros modelos de almacenamiento), esto se logra usando
FileRepositoryBuilder:

// Crear un nuevo repositorio


Repositorio newCreatedRepo = FileRepositoryBuilder.create( new
File("/tmp/new_repo/.git")); ReciénCreadoRepo.create();

// Abrir un repositorio existente


Repositorio existenteRepo = new
FileRepositoryBuilder() .setGitDir(new
File("my_repo/.git")) .build();

El constructor tiene una API fluida para proporcionar todo lo que necesita para encontrar un repositorio de Git, ya sea que su programa
sepa exactamente dónde se encuentra o no. Puede usar variables de entorno (.readEnvironment()), comenzar desde un lugar en el
directorio de trabajo y buscar (.setWorkTree(…).findGitDir()), o simplemente abrir un directorio .git conocido como se indicó anteriormente.

Una vez que tenga una instancia de repositorio , puede hacer todo tipo de cosas con ella. Aquí hay una muestra rápida:

490
Machine Translated by Google

// Obtener una
referencia Ref master = repo.getRef("master");

// Obtener el objeto de los puntos de referencia para


ObjectId masterTip = master.getObjectId();

// Rev-parse
ObjectId obj = repo.resolve("HEAD^{tree}");

// Cargar contenido de objetos sin


procesar ObjectLoader loader = repo.open(masterTip);
loader.copyTo(Sistema.fuera);

// Crear una rama


RefUpdate createBranch1 = repo.updateRef("refs/heads/branch1");
createBranch1.setNewObjectId(masterTip); crearSucursal1.actualizar();

// Eliminar una rama


RefUpdate deleteBranch1 = repo.updateRef("refs/heads/branch1");
deleteBranch1.setForceUpdate(verdadero); eliminarSucursal1.eliminar();

//
Configuración Configuración cfg =
repo.getConfig(); Nombre de cadena = cfg.getString("usuario", nulo, "nombre");

Están sucediendo muchas cosas aquí, así que repasemos una sección a la vez.

La primera línea obtiene un puntero a la referencia maestra . JGit toma automáticamente la referencia maestra real ,
que se encuentra en refs/heads/master, y devuelve un objeto que le permite obtener información sobre la referencia.
Puede obtener el nombre (.getName()) y el objeto de destino de una referencia directa (.getObjectId()) o la referencia a
la que apunta una referencia simbólica (.getTarget()). Los objetos de referencia también se utilizan para representar
objetos y referencias de etiqueta, por lo que puede preguntar si la etiqueta está "pelada", lo que significa que apunta al
destino final de una cadena (potencialmente larga) de objetos de etiqueta.

La segunda línea obtiene el destino de la referencia maestra , que se devuelve como una instancia de ObjectId.
ObjectId representa el hash SHA-1 de un objeto, que puede o no existir en la base de datos de objetos de Git. La tercera
línea es similar, pero muestra cómo JGit maneja la sintaxis rev-parse (para obtener más información, consulte
Referencias de sucursales); puede pasar cualquier especificador de objeto que Git entienda, y JGit devolverá un ObjectId
válido para ese objeto o nulo.

Las siguientes dos líneas muestran cómo cargar el contenido sin procesar de un objeto. En este ejemplo, llamamos a
ObjectLoader.copyTo() para transmitir el contenido del objeto directamente a la salida estándar, pero ObjectLoader
también tiene métodos para leer el tipo y el tamaño de un objeto, así como devolverlo como una matriz de bytes. Para
objetos grandes (donde .isLarge() devuelve verdadero), puede llamar a .openStream() para obtener un objeto similar a
InputStream que pueda leer los datos del objeto sin procesar sin extraerlo todo a la memoria a la vez.

Las siguientes líneas muestran lo que se necesita para crear una nueva rama. Creamos una instancia RefUpdate,

491
Machine Translated by Google

configure algunos parámetros y llame a .update() para activar el cambio. Inmediatamente después de esto está el
código para eliminar esa misma rama. Tenga en cuenta que se requiere .setForceUpdate(true) para que esto
funcione; de lo contrario, la llamada .delete() devolverá REJECTED y no pasará nada.

El último ejemplo muestra cómo obtener el valor de nombre de usuario de los archivos de configuración de Git. Esta
instancia de Config usa el repositorio que abrimos anteriormente para la configuración local, pero detectará
automáticamente los archivos de configuración global y del sistema y también leerá los valores de ellos.

Esta es solo una pequeña muestra de la API de plomería completa; hay muchos más métodos y clases disponibles.
Tampoco se muestra aquí la forma en que JGit maneja los errores, que es mediante el uso de excepciones. Las API
de JGit a veces arrojan excepciones estándar de Java (como IOException), pero también se proporcionan una gran
cantidad de tipos de excepción específicos de JGit (como NoRemoteRepositoryException, CorruptObjectException y
NoMergeBaseException).

Porcelana
Las API de plomería son bastante completas, pero puede ser engorroso unirlas para lograr objetivos comunes, como
agregar un archivo al índice o realizar una nueva confirmación. JGit proporciona un conjunto de API de nivel superior
para ayudar con esto, y el punto de entrada a estas API es la clase Git :

repositorio de
repositorio; // construir repositorio...
Git git = nuevo Git (repositorio);

La clase Git tiene un buen conjunto de métodos de estilo constructor de alto nivel que se pueden usar para construir
un comportamiento bastante complejo. Echemos un vistazo a un ejemplo: hacer algo como git ls-remote:

CredentialsProvider cp = new UsernamePasswordCredentialsProvider("username", "p4ssw0rd");


Collection<Ref> remoteRefs =
git.lsRemote() .setCredentialsProvider(cp) .setRemote("origen") .setTags(true) .setHeads(false) .call();
for (Ref ref : remoteRefs) { System.out.println(ref.getName() + }

" "
-> + ref.getObjectId().nombre());

Este es un patrón común con la clase Git; los métodos devuelven un objeto de comando que le permite encadenar
llamadas a métodos para establecer parámetros, que se ejecutan cuando llama a .call(). En este caso, le estamos
pidiendo etiquetas al control remoto de origen , pero no cabezas. Observe también el uso de un objeto
CredentialsProvider para la autenticación.

Muchos otros comandos están disponibles a través de la clase Git, incluidos, entre otros , agregar, culpar, confirmar,
limpiar, empujar, reorganizar, revertir y restablecer.

492
Machine Translated by Google

Otras lecturas
Esta es solo una pequeña muestra de las capacidades completas de JGit. Si está interesado y desea obtener más información,
aquí es donde puede buscar información e inspiración:

• La documentación oficial de la API de JGit se puede encontrar en https://www.eclipse.org/jgit/documentation.


Estos son Javadoc estándar, por lo que su JVM IDE favorito también podrá instalarlos localmente.

• El libro de cocina JGit en https://github.com/centic9/jgit-cookbook tiene muchos ejemplos de cómo hacer tareas específicas
con JGit.

go-git
En caso de que desee integrar Git en un servicio escrito en Golang, también existe una implementación de biblioteca Go pura.
Esta implementación no tiene dependencias nativas y, por lo tanto, no es propensa a errores de administración de memoria
manual. También es transparente para las herramientas de análisis de rendimiento estándar de Golang, como CPU, perfiladores
de memoria, detectores de carrera, etc.

go-git se centra en la extensibilidad, la compatibilidad y es compatible con la mayoría de las API de fontanería, que se
documenta en https://github.com/go-git/go-git/blob/master/COMPATIBILITY.md.

Este es un ejemplo básico del uso de las API de Go:

importar "github.com/go-git/go-git/v5"

r, err := git.PlainClone("/tmp/foo", false, &git.CloneOptions{ URL: "https://


github.com/go-git/go-git", Progreso: os.Stdout, })

Tan pronto como tenga una instancia de Repositorio , puede acceder a la información y realizar mutaciones en ella:

// recupera la rama apuntada por HEAD ref,


err := r.Head()

// obtener el objeto de confirmación, señalado


por ref commit, err := r.CommitObject(ref.Hash())

// recupera el historial de
confirmación, err := commit.History()

// itera sobre las confirmaciones e imprime cada


una para
_, c := range history { fmt.Println(c) }

493
Machine Translated by Google

Funcionalidad avanzada
go-git tiene algunas características avanzadas notables, una de las cuales es un sistema de almacenamiento conectable, que es
similar a los backends de Libgit2. La implementación predeterminada es el almacenamiento en memoria, que es muy rápido.

r, err := git.Clone(memoria.NuevoAlmacenamiento(), nil,


&git.CloneOptions{ URL: "https://github.com/go-git/go-git", })

El almacenamiento enchufable ofrece muchas opciones interesantes. Por ejemplo, https://github.com/go-git/go-git/tree/master/


_examples/storage le permite almacenar referencias, objetos y configuraciones en una base de datos de Aerospike.

Otra característica es una abstracción flexible del sistema de archivos. Usando https://pkg.go.dev/github.com/go-git/go billy/v5?
tab=doc#Filesystem es fácil almacenar todos los archivos de forma diferente, es decir, empaquetándolos en un solo archivo en
el disco o manteniéndolos todos en la memoria.

Otro caso de uso avanzado incluye un cliente HTTP ajustable, como el que se encuentra en https://github.com/go-git/go-git/blob/
master/_examples/custom_http/main.go.

customClient :=
&http.Client{ Transport: &http.Transport{ // acepta cualquier certificado (puede ser útil para
realizar pruebas)
ÿ

TLSClientConfig: &tls.Config{InsecureSkipVerify: verdadero},


},
Tiempo de espera: 15 * time.Second, // 15 segundos de tiempo de espera
ÿ

CheckRedirect: func(req *http.Request, via []*http.Request) error { return


ÿ

http.ErrUseLastResponse // no siga la redirección


}, }

// Anula el protocolo predeterminado de http(s) para usar nuestro cliente


personalizado client.InstallProtocol("https", githttp.NewClient(customClient))

// Clonar el repositorio usando el nuevo cliente si el protocolo es https:// r, err :=


git.Clone(memory.NewStorage(), nil, &git.CloneOptions{URL: url})

Otras lecturas
Un tratamiento completo de las capacidades de go-git está fuera del alcance de este libro. Si desea obtener más información
sobre go-git, hay documentación de API en https://pkg.go.dev/github.com/go-git/go-git/v5, y un conjunto de ejemplos de uso en
https://github.com/go-git/go-git/tree/master/_examples.

Dulwich
También hay una implementación pura de Python Git: Dulwich. El proyecto está alojado en https://www.dulwich.io/ Su objetivo
es proporcionar una interfaz para los repositorios de git (tanto locales como remotos)

494
Machine Translated by Google

eso no llama a git directamente, sino que usa Python puro. Sin embargo, tiene extensiones C opcionales que mejoran
significativamente el rendimiento.

Dulwich sigue el diseño de git y separa dos niveles básicos de API: plomería y porcelana.

Aquí hay un ejemplo del uso de la API de nivel inferior para acceder al mensaje de confirmación de la última confirmación:

desde dulwich.repo import Repo r =


Repo('.') r.head() #

'57fbe010446356833a6ad1600059d80b1e731e15'

c = r[r.cabeza()]
C

# <Compromiso 015fc1267258458901a94d228e39f0a378370466>

c.message
# 'Agregar nota sobre la codificación.\n'

Para imprimir un registro de compromiso usando la API de porcelana de alto nivel, se puede usar:

de dulwich importar porcelana


porcelana.log('.', max_entries=1)

#commit: 57fbe010446356833a6ad1600059d80b1e731e15 #Autor:


Jelmer Vernooÿ <jelmer@jelmer.uk> #Fecha: sábado 29 de abril
de 2017 23:57:34 +0000

Otras lecturas
La documentación de la API, el tutorial y muchos ejemplos de cómo realizar tareas específicas con Dulwich están disponibles
en el sitio web oficial https://www.dulwich.io.

495
Machine Translated by Google

Apéndice C: Comandos Git


A lo largo del libro, presentamos docenas de comandos de Git y nos esforzamos por presentarlos dentro de una especie de narrativa,
agregando más comandos a la historia lentamente.
Sin embargo, esto nos deja con ejemplos del uso de los comandos algo dispersos a lo largo de todo el libro.

En este apéndice, repasaremos todos los comandos de Git que abordamos a lo largo del libro, agrupados aproximadamente según
su uso. Hablaremos sobre lo que hace cada comando en general y luego indicaremos en qué parte del libro puede encontrarnos
usándolo.

Puede abreviar opciones largas. Por ejemplo, puede escribir git commit --a, que actúa como si hubiera escrito

ÿ git commit --amend. Esto solo funciona cuando las letras después de -- son únicas para una opción. Utilice la
opción completa al escribir guiones.

Instalación y configuración

Hay dos comandos que se usan bastante, desde las primeras invocaciones de Git hasta los ajustes y referencias comunes todos los
días, los comandos config y help .

configuración de git

Git tiene una forma predeterminada de hacer cientos de cosas. Para muchas de estas cosas, puede decirle a Git que las haga de
forma predeterminada de una manera diferente, o establecer sus preferencias. Esto implica todo, desde decirle a Git cuál es su
nombre hasta preferencias específicas de color de terminal o qué editor usa. Hay varios archivos de los que este comando leerá y
escribirá para que pueda establecer valores globalmente o en repositorios específicos.

El comando git config se ha utilizado en casi todos los capítulos del libro.

En la primera configuración de Git , lo usamos para especificar nuestro nombre, dirección de correo electrónico y preferencia de
editor incluso antes de comenzar a usar Git.

En Git Aliases , mostramos cómo puede usarlo para crear comandos abreviados que se expanden a secuencias de opciones largas
para que no tenga que escribirlos cada vez.

En Rebasing lo usamos para hacer que --rebase sea el predeterminado cuando ejecutas git pull.

En Credential Storage lo usamos para configurar un almacén predeterminado para sus contraseñas HTTP.

En Expansión de palabras clave mostramos cómo configurar filtros de limpieza y difuminado en el contenido que entra y sale de Git.

Finalmente, básicamente la totalidad de la configuración de Git está dedicada al comando.

Comandos de git config core.editor


Junto con las instrucciones de configuración en Your Editor, muchos editores se pueden configurar de la siguiente manera:

496
Machine Translated by Google

Tabla 4. Lista exhaustiva de comandos de configuración de core.editor

Editor Comando de configuración

Átomo git config --global core.editor "átomo --esperar"

BBEdit (Mac, con comando git config --global core.editor "bbedit -w"
herramientas de línea)

Emacs git config --global core.editor emacs

Gedit (Linux) git config --global core.editor "gedit --wait --nueva-ventana"

Gvim (Windows de 64 bits) git config --global core.editor "'C:\Programa


Files\Vim\vim72\gvim.exe' --nofork '%*'" (También vea la nota a continuación)

Kate (Linux) git config --global core.editor "kate"

nano git config --global core.editor "nano -w"

Bloc de notas (Windows de 64 bits) Bloc de notas git config core.editor

Bloc de notas++ (Windows de 64 bits) git config --global core.editor "'C:\Programa


Archivos\Bloc de notas\notepad.exe' -multiInst -notabbar -nosession
-noPlugin" (También vea la nota a continuación)

Rasguño (Linux) git config --global core.editor "editor de texto borrador"

Texto sublime (macOS) git config --global core.editor "/Aplicaciones/Sublime\


Text.app/Contents/SharedSupport/bin/subl --ventana nueva --espera"

Sublime Text (Windows de 64 bits) git config --global core.editor "'C:\Archivos de programa\Sublime
Texto 3\sublime_text.exe' -w" (También vea la nota a continuación)

Edición de texto (macOS) git config --global core.editor "abrir --wait-apps --new -e"

compañero de texto git config --global core.editor "compañero -w"

Panel de texto (Windows de 64 bits) git config --global core.editor "'C:\Archivos de programa\TextPad
5\TextPad.exe' -m (También vea la nota a continuación)

UltraEdit (Windows de 64 bits) git config --global core.editorUedit32

Empuje git config --global core.editor "vim --nofork"

código de estudio visual git config --global core.editor "código --esperar"

VSCodium (Gratis/Libre Abierto git config --global core.editor "codio --esperar"


Software fuente Binarios de

código VSC)

WordPad git config --global core.editor '"C:\Archivos de programa\Windows


NT\Accesorios\wordpad.exe"'"

xi git config --global core.editor "xi --esperar"

Si tiene un editor de 32 bits en un sistema Windows de 64 bits, el programa se

ÿ instalado en C:\Program Files (x86)\ en lugar de C:\Program Files\ como en la tabla


encima.

497
Machine Translated by Google

ayuda
El comando de ayuda de git se usa para mostrarle toda la documentación enviada con Git sobre cualquier comando. Si
bien brindamos una descripción general aproximada de la mayoría de los más populares en este apéndice, para obtener
una lista completa de todas las opciones e indicadores posibles para cada comando, siempre puede ejecutar git help
<comando>.

Presentamos el comando de ayuda de git en Obtención de ayuda y le mostramos cómo usarlo para encontrar más
información sobre el shell de git en Configuración del servidor.

Obtener y Crear Proyectos


Hay dos formas de obtener un repositorio de Git. Una es copiarlo desde un repositorio existente en la red o en otro lugar
y el otro es crear uno nuevo en un directorio existente.

iniciar git

Para tomar un directorio y convertirlo en un nuevo repositorio de Git para que pueda comenzar a controlarlo, simplemente
puede ejecutar git init.

Primero presentamos esto en Obtención de un repositorio Git, donde mostramos la creación de un nuevo repositorio para
comenzar a trabajar.

Hablamos brevemente sobre cómo puede cambiar el nombre de sucursal predeterminado de "maestro" en sucursales
remotas.

Usamos este comando para crear un repositorio básico vacío para un servidor en Colocación del repositorio básico en
un servidor.

Finalmente, analizamos algunos de los detalles de lo que realmente hace detrás de escena en Plomería y porcelana.

clonar
El comando git clone es en realidad una especie de envoltorio alrededor de varios otros comandos. Crea un nuevo
directorio, entra en él y ejecuta git init para convertirlo en un repositorio Git vacío, agrega un control remoto (git remote
add) a la URL que le pasas (de forma predeterminada con el nombre de origen), ejecuta un git fetch desde ese control
remoto repository y luego verifica la última confirmación en su directorio de trabajo con git checkout.

El comando git clone se usa en docenas de lugares a lo largo del libro, pero solo mencionaremos algunos lugares
interesantes.

Básicamente se presenta y explica en Clonación de un repositorio existente, donde analizamos algunos ejemplos.

En Obtención de Git en un servidor , analizamos el uso de la opción --bare para crear una copia de un repositorio de Git
sin un directorio de trabajo.

En Bundling lo usamos para desagregar un repositorio Git incluido.

498
Machine Translated by Google

Finalmente, en Clonación de un proyecto con submódulos aprendemos la opción --recurse-submodules para simplificar un poco la
clonación de un repositorio con submódulos.

Aunque se usa en muchos otros lugares a lo largo del libro, estos son los que son algo únicos o donde se usa de maneras un poco
diferentes.

Instantáneas básicas
Para el flujo de trabajo básico de preparar contenido y enviarlo a su historial, solo hay unos pocos comandos básicos.

añadir

El comando git add agrega contenido del directorio de trabajo al área de preparación (o "índice") para la próxima confirmación. Cuando
se ejecuta el comando git commit , de forma predeterminada solo mira esta área de preparación, por lo que git add se usa para crear
exactamente cómo le gustaría que se viera su próxima instantánea de confirmación.

Este comando es increíblemente importante en Git y se menciona o se usa docenas de veces en este libro. Cubriremos rápidamente
algunos de los usos únicos que se pueden encontrar.

Primero presentamos y explicamos git add en detalle en Seguimiento de nuevos archivos.

Mencionamos cómo usarlo para resolver conflictos de combinación en Conflictos básicos de combinación.

Repasamos su uso para organizar de forma interactiva solo partes específicas de un archivo modificado en la puesta en escena interactiva.

Finalmente, lo emulamos a un nivel bajo en Tree Objects, para que pueda tener una idea de lo que está haciendo detrás de escena.

estado de Git

El comando de estado de git le mostrará los diferentes estados de los archivos en su directorio de trabajo y área de ensayo. Qué archivos
están modificados y sin preparar y cuáles están preparados pero aún no confirmados. En su forma normal, también le mostrará algunos
consejos básicos sobre cómo mover archivos entre estas etapas.

Primero cubrimos el estado en Verificación del estado de sus archivos, tanto en su forma básica como simplificada.
Si bien lo usamos a lo largo del libro, prácticamente todo lo que puede hacer con el comando git status se cubre allí.

diferencia de git

El comando git diff se usa cuando desea ver las diferencias entre dos árboles cualesquiera. Esta podría ser la diferencia entre su entorno
de trabajo y su área de preparación (git diff por sí mismo), entre su área de preparación y su última confirmación (git diff --staged), o entre
dos confirmaciones (git diff master branchB).

Primero observamos los usos básicos de git diff en Visualización de los cambios preparados y no preparados, donde mostramos cómo
ver qué cambios están preparados y cuáles aún no.

Lo usamos para buscar posibles problemas de espacios en blanco antes de confirmar con la opción --check en

499
Machine Translated by Google

Directrices de compromiso.

Vemos cómo verificar las diferencias entre ramas de manera más efectiva con la sintaxis git diff A…B en Determinar lo que se
introduce.

Lo usamos para filtrar las diferencias de espacios en blanco con -b y cómo comparar diferentes etapas de archivos en conflicto con
--theirs, --ours y --base en Advanced Merging.

Finalmente, lo usamos para comparar efectivamente los cambios de submódulos con --submodule en Comenzar con submódulos.

herramienta de diferenciación de git

El comando git difftool simplemente inicia una herramienta externa para mostrarle la diferencia entre dos árboles en caso de que
quiera usar algo que no sea el comando git diff incorporado.

Solo mencionamos esto brevemente en Visualización de sus cambios preparados y no preparados.

git cometer
El comando git commit toma todo el contenido del archivo que se ha preparado con git add y registra una nueva instantánea
permanente en la base de datos y luego mueve el puntero de la rama actual hacia ella.

En primer lugar, cubrimos los aspectos básicos de la confirmación en Confirmar los cambios. Allí también demostramos cómo usar
el indicador -a para omitir el paso de agregar git en los flujos de trabajo diarios y cómo usar el indicador -m para pasar un mensaje
de confirmación en la línea de comando en lugar de iniciar un editor.

En Deshacer cosas , cubrimos el uso de la opción --amend para rehacer la confirmación más reciente.

En Branches in a Nutshell, entramos en muchos más detalles sobre lo que hace git commit y por qué lo hace así.

Vimos cómo firmar confirmaciones criptográficamente con el indicador -S en Firmar confirmaciones.

Finalmente, echamos un vistazo a lo que hace el comando git commit en segundo plano y cómo se implementa realmente en
Commit Objects.

reiniciar
El comando git reset se usa principalmente para deshacer cosas, como puede ver por el verbo. Se mueve alrededor del puntero
HEAD y opcionalmente cambia el índice o el área de preparación y también puede cambiar opcionalmente el directorio de trabajo si
usa --hard. Esta última opción hace posible que este comando pierda su trabajo si se usa incorrectamente, así que asegúrese de
entenderlo antes de usarlo.

Primero cubrimos de manera efectiva el uso más simple de git reset en Unstaging a Staged File, donde lo usamos para eliminar un
archivo en el que habíamos ejecutado git add on.

Luego lo cubrimos con bastante detalle en Reset Demystified, que está enteramente dedicado a explicar este comando.

500
Machine Translated by Google

Usamos git reset --hard para abortar una fusión en Aborting a Merge, donde también usamos git merge --abort, que es una
especie de envoltorio para el comando git reset .

git rm
El comando git rm se usa para eliminar archivos del área de preparación y el directorio de trabajo de Git. Es similar a git add en
el sentido de que organiza la eliminación de un archivo para la próxima confirmación.

Cubrimos el comando git rm con cierto detalle en Eliminación de archivos, incluida la eliminación recursiva de archivos y solo la
eliminación de archivos del área de ensayo, pero dejándolos en el directorio de trabajo con --cached.

El único otro uso diferente de git rm en el libro es en Eliminación de objetos , donde usamos y explicamos brevemente --ignore-
unmatch al ejecutar git filter-branch, que simplemente hace que no se produzca un error cuando el archivo que estamos tratando
de eliminar no no existe Esto puede ser útil para propósitos de secuencias de comandos.

git mv
El comando git mv es un comando práctico para mover un archivo y luego ejecutar git add en el archivo nuevo y git rm en el
archivo anterior.

Solo mencionamos brevemente este comando en Mover archivos.

limpiar
El comando git clean se usa para eliminar archivos no deseados de su directorio de trabajo. Esto podría incluir la eliminación de
artefactos de compilación temporales o la combinación de archivos en conflicto.

Cubrimos muchas de las opciones y escenarios en los que podría usar el comando de limpieza en Limpieza de su directorio de
trabajo.

Ramificación y fusión
Solo hay un puñado de comandos que implementan la mayor parte de la funcionalidad de bifurcación y fusión en Git.

rama git
El comando git branch es en realidad algo así como una herramienta de administración de sucursales. Puede enumerar las
sucursales que tiene, crear una nueva sucursal, eliminar sucursales y cambiar el nombre de las sucursales.

La mayor parte de Git Branching está dedicada al comando branch y se usa a lo largo de todo el capítulo. Primero lo presentamos
en Creación de una nueva sucursal y revisamos la mayoría de sus otras funciones (listado y eliminación) en Administración de
sucursales.

En Ramas de seguimiento , usamos la opción git branch -u para configurar una rama de seguimiento.

Finalmente, repasamos algo de lo que hace en segundo plano en Git References.

501
Machine Translated by Google

pago git
El comando git checkout se usa para cambiar de rama y ver el contenido en su directorio de trabajo.

Primero encontramos el comando en Switching Branches junto con el comando git branch .

Vemos cómo usarlo para comenzar a rastrear sucursales con el indicador --track en Seguimiento de sucursales.

Lo usamos para reintroducir conflictos de archivos con --conflict=diff3 en la comprobación de conflictos.

Entramos en detalles más detallados sobre su relación con git reset en Reset Demystified.

Finalmente, entramos en algunos detalles de implementación en The HEAD.

combinación de git

La herramienta de fusión de git se usa para fusionar una o más ramas en la rama que ha retirado. Luego avanzará la rama actual al
resultado de la fusión.

El comando git merge se introdujo por primera vez en Basic Branching. Aunque se usa en varios lugares del libro, hay muy pocas
variaciones del comando fusionar ; generalmente, simplemente git merge <branch> con el nombre de la única rama que desea fusionar.

Cubrimos cómo hacer una combinación aplastada (donde Git combina el trabajo pero finge que es solo una nueva confirmación sin
registrar el historial de la rama en la que se está fusionando) al final de Forked Public Project.

Hablamos mucho sobre el proceso y el comando de combinación, incluido el comando -Xignore-space-change y el indicador --abort
para cancelar una combinación problemática en Advanced Merging.

Aprendimos cómo verificar las firmas antes de fusionarlas si su proyecto utiliza GPG en Signing Commits.

Finalmente, aprendimos sobre la combinación de subárboles en Subtree Merging.

herramienta de combinación de git

El comando git mergetool simplemente inicia un asistente de combinación externo en caso de que tenga problemas con una
combinación en Git.

Lo mencionamos rápidamente en Conflictos básicos de combinación y detallamos cómo implementar su propia herramienta de
combinación externa en Herramientas de comparación y combinación externa.

registro de git

El comando git log se usa para mostrar el historial registrado accesible de un proyecto desde la instantánea de confirmación más
reciente hacia atrás. De forma predeterminada, solo mostrará el historial de la rama en la que se encuentra actualmente, pero se le
pueden asignar cabezas o ramas diferentes o incluso múltiples desde las que atravesar.
También se usa a menudo para mostrar las diferencias entre dos o más ramas en el nivel de confirmación.

502
Machine Translated by Google

Este comando se usa en casi todos los capítulos del libro para demostrar la historia de un proyecto.

Presentamos el comando y lo cubrimos con cierta profundidad en Visualización del historial de confirmaciones. Allí miramos la
opción -p y --stat para tener una idea de lo que se introdujo en cada confirmación y las opciones --pretty y --oneline para ver el
historial de manera más concisa, junto con algunas opciones simples de filtrado de fecha y autor.

En la creación de una nueva rama , lo usamos con la opción --decorate para visualizar fácilmente dónde se encuentran nuestros
punteros de rama y también usamos la opción --graph para ver cómo se ven las historias divergentes.

En Private Small Team y Commit Ranges cubrimos la sintaxis branchA..branchB para usar el comando git log para ver qué
confirmaciones son únicas para una rama en relación con otra rama. En Commit Ranges analizamos esto de manera bastante
extensa.

En Merge Log y Triple Dot cubrimos el uso del formato branchA…branchB y la sintaxis --left-right para ver qué hay en una rama o
en la otra pero no en ambas. En Merge Log también vemos cómo usar la opción --merge para ayudar con la depuración de conflictos
de fusión, así como el uso de la opción --cc para ver los conflictos de confirmación de fusión en su historial.

En RefLog Shortnames , usamos la opción -g para ver el registro de referencia de Git a través de esta herramienta en lugar de
realizar un cruce de ramas.

En Búsqueda , analizamos el uso de las opciones -S y -L para realizar búsquedas bastante sofisticadas de algo que sucedió
históricamente en el código, como ver el historial de una función.

En Firma de confirmaciones , vemos cómo usar --show-signature para agregar una cadena de validación a cada confirmación en la
salida del registro de git en función de si se firmó de forma válida o no.

alijo de git

El comando git stash se usa para almacenar temporalmente el trabajo no confirmado para limpiar su directorio de trabajo sin tener
que confirmar el trabajo no terminado en una rama.

Básicamente, esto se cubre por completo en Almacenamiento y limpieza.

etiqueta git

El comando git tag se usa para dar un marcador permanente a un punto específico en el historial del código.
Generalmente esto se usa para cosas como lanzamientos.

Este comando se presenta y cubre en detalle en Etiquetado y lo usamos en la práctica en Etiquetado de sus lanzamientos.

También cubrimos cómo crear una etiqueta firmada por GPG con el indicador -s y cómo verificar una con el indicador -v en Cómo
firmar su trabajo.

Compartir y actualizar proyectos


No hay muchos comandos en Git que accedan a la red, casi todos los comandos operan en la base de datos local. Cuando esté
listo para compartir su trabajo o extraer cambios de

503
Machine Translated by Google

en otros lugares, hay un puñado de comandos que se ocupan de los repositorios remotos.

buscar

El comando git fetch se comunica con un repositorio remoto y recupera toda la información que está en ese repositorio que
no está en el actual y la almacena en su base de datos local.

Primero observamos este comando en Obtención y extracción de sus controles remotos y continuamos viendo ejemplos
de su uso en sucursales remotas.

También lo usamos en varios de los ejemplos de Contribuir a un proyecto.

Lo usamos para obtener una sola referencia específica que está fuera del espacio predeterminado en Refs de solicitud de
extracción y vemos cómo obtener de un paquete en Agrupación.

Configuramos refspecs altamente personalizadas para hacer que git fetch haga algo un poco diferente al predeterminado
en The Refspec.

tirar de git

El comando git pull es básicamente una combinación de los comandos git fetch y git merge , donde Git buscará desde el
control remoto que especifique e inmediatamente intentará fusionarlo en la rama en la que se encuentra.

Lo presentamos rápidamente en Obtención y extracción de sus controles remotos y le mostramos cómo ver lo que se
fusionará si lo ejecuta en Inspección de un control remoto.

También vemos cómo usarlo para ayudar con las dificultades de rebase en Rebase When You Rebase.

Mostramos cómo usarlo con una URL para obtener cambios de una sola vez en Verificación de sucursales remotas.

Finalmente, mencionamos rápidamente que puede usar la opción --verify-signatures para verificar que las confirmaciones
que está extrayendo han sido firmadas con GPG en Confirmaciones de firma.

empujar git

El comando git push se usa para comunicarse con otro repositorio, calcular qué tiene su base de datos local que no tiene
la remota y luego empuja la diferencia al otro repositorio.
Requiere acceso de escritura al otro repositorio y, por lo tanto, normalmente se autentica de alguna manera.

En primer lugar, analizamos el comando git push en Pushing to Your Remotes. Aquí cubrimos los conceptos básicos de
enviar una rama a un repositorio remoto. En Empujar profundizamos un poco más en empujar ramas específicas y en
Rastrear ramas vemos cómo configurar ramas de seguimiento para empujar automáticamente. En Eliminación de sucursales
remotas , usamos el indicador --delete para eliminar una sucursal en el servidor con git push.

A lo largo de Contributing to a Project , vemos varios ejemplos del uso de git push para compartir el trabajo en sucursales
a través de múltiples controles remotos.

504
Machine Translated by Google

Vemos cómo usarlo para compartir etiquetas que haya creado con la opción --tags en Compartir etiquetas.

En Publicación de cambios de submódulos , usamos la opción --recurse-submodules para verificar que todo el trabajo de nuestros submódulos se

haya publicado antes de enviar el superproyecto, lo que puede ser realmente útil cuando se usan submódulos.

En Otros ganchos de clientes , hablamos brevemente sobre el gancho previo a la inserción , que es un script que podemos configurar para que

se ejecute antes de que se complete una inserción para verificar que se le debe permitir la inserción.

Finalmente, en Pushing Refspecs , analizamos la inserción con una refspec completa en lugar de los atajos generales que se usan normalmente.

Esto puede ayudarlo a ser muy específico sobre qué trabajo desea compartir.

git remoto
El comando git remote es una herramienta de administración para su registro de repositorios remotos. Le permite guardar direcciones URL largas

como identificadores cortos, como "origen", para que no tenga que escribirlos todo el tiempo. Puede tener varios de estos y el comando remoto git

se usa para agregarlos, cambiarlos y eliminarlos.

Este comando se cubre en detalle en Trabajar con controles remotos, incluidos enumerarlos, agregarlos, eliminarlos y cambiarles el nombre.

También se usa en casi todos los capítulos posteriores del libro, pero siempre en el formato estándar de git remote add <name> <url> .

archivo git
El comando git archive se usa para crear un archivo de almacenamiento de una instantánea específica del proyecto.

Usamos git archive para crear un tarball de un proyecto para compartir en Preparación de un lanzamiento.

submódulo git
El comando git submodule se usa para administrar repositorios externos dentro de repositorios normales.

Esto podría ser para bibliotecas u otros tipos de recursos compartidos. El comando del submódulo tiene varios subcomandos (agregar, actualizar,

sincronizar, etc.) para administrar estos recursos.

Este comando solo se menciona y se cubre por completo en Submódulos.

Inspección y Comparación
mostrar
El comando git show puede mostrar un objeto Git de una manera simple y legible por humanos. Normalmente usaría esto para mostrar la

información sobre una etiqueta o una confirmación.

Primero lo usamos para mostrar información de etiquetas anotadas en Etiquetas anotadas .

Más tarde lo usamos un poco en Selección de revisión para mostrar las confirmaciones a las que se resuelven nuestras diversas selecciones de
revisión.

505
Machine Translated by Google

Una de las cosas más interesantes que hacemos con git show es en la fusión manual de archivos para extraer contenidos de archivos
específicos de varias etapas durante un conflicto de fusión.

bitácora de git
El comando git shortlog se usa para resumir la salida de git log. Tomará muchas de las mismas opciones que el comando git log , pero en
lugar de enumerar todas las confirmaciones, presentará un resumen de las confirmaciones agrupadas por autor.

Mostramos cómo usarlo para crear un buen registro de cambios en The Shortlog.

describir
El comando git describe se usa para tomar cualquier cosa que se resuelva en una confirmación y produce una cadena que es algo legible
por humanos y no cambiará. Es una forma de obtener una descripción de una confirmación que es tan inequívoca como una confirmación
SHA-1 pero más comprensible.

Usamos git describe en Generar un número de compilación y preparar un lanzamiento para obtener una cadena para nombrar nuestro
archivo de lanzamiento después.

depuración
Git tiene un par de comandos que se usan para ayudar a depurar un problema en su código. Esto va desde averiguar dónde se introdujo
algo hasta descubrir quién lo introdujo.

git bisecar
La herramienta git bisect es una herramienta de depuración increíblemente útil que se usa para encontrar qué compromiso específico fue
el primero en introducir un error o problema al realizar una búsqueda binaria automática.

Está completamente cubierto en Binary Search y solo se menciona en esa sección.

culpa
El comando git culpe anota las líneas de cualquier archivo con el que la confirmación fue la última en introducir un cambio en cada línea
del archivo y qué persona fue la autora de esa confirmación. Esto es útil para encontrar a la persona a la que pedir más información sobre
una sección específica de su código.

Está cubierto en Anotación de archivo y solo se menciona en esa sección.

git grep
El comando git grep puede ayudarlo a encontrar cualquier cadena o expresión regular en cualquiera de los archivos en su código fuente,
incluso en versiones anteriores de su proyecto.

Está cubierto en Git Grep y solo se menciona en esa sección.

506
Machine Translated by Google

Parchar
Algunos comandos en Git se centran en el concepto de pensar en las confirmaciones en términos de los cambios que introducen, como
si la serie de confirmaciones fuera una serie de parches. Estos comandos lo ayudan a administrar sus sucursales de esta manera.

selección de cereza git

El comando git cherry-pick se usa para tomar el cambio introducido en una sola confirmación de Git e intentar volver a introducirlo como
una nueva confirmación en la rama en la que se encuentra actualmente. Esto puede ser útil para tomar solo una o dos confirmaciones de
una rama individualmente en lugar de fusionarse en la rama que toma todos los cambios.

La selección selectiva se describe y demuestra en Flujos de trabajo de cambio de base y selección selectiva.

git rebase
El comando git rebase es básicamente una selección automatizada. Determina una serie de compromisos y luego los selecciona uno por
uno en el mismo orden en otro lugar.

El cambio de base se cubre en detalle en Rebase, incluida la cobertura de los problemas de colaboración relacionados con el cambio de
base de sucursales que ya son públicas.

Lo usamos en la práctica durante un ejemplo de dividir su historial en dos repositorios separados en Reemplazar, usando también el
indicador --onto .

Pasamos por un conflicto de fusión durante la reorganización en Rerere.

También lo usamos en un modo de secuencias de comandos interactivas con la opción -i en Cambio de varios mensajes de confirmación.

git revertir
El comando git revert es esencialmente una selección de git inversa. Crea una nueva confirmación que aplica exactamente lo contrario
del cambio introducido en la confirmación a la que se dirige, esencialmente deshaciéndola o revirtiéndola.

Usamos esto en Invertir la confirmación para deshacer una confirmación de fusión.

Correo electrónico

Muchos proyectos de Git, incluido el propio Git, se mantienen completamente en listas de correo. Git tiene una serie de herramientas
integradas que ayudan a facilitar este proceso, desde generar parches que puede enviar fácilmente por correo electrónico hasta aplicar
esos parches desde un buzón de correo electrónico.

Aplicar
El comando git apply aplica un parche creado con el comando git diff o incluso GNU diff. Es similar a lo que podría hacer el comando
patch con algunas pequeñas diferencias.

507
Machine Translated by Google

Demostramos su uso y las circunstancias en las que puede hacerlo en Aplicación de parches desde el correo electrónico.

git soy

El comando git am se usa para aplicar parches desde una bandeja de entrada de correo electrónico, específicamente una que
tiene formato mbox. Esto es útil para recibir parches por correo electrónico y aplicarlos fácilmente a su proyecto.

Cubrimos el uso y el flujo de trabajo en torno a git am en Aplicar un parche con am , incluido el uso de las opciones --resolved,
-i y -3 .

También hay una serie de ganchos que puede usar para ayudar con el flujo de trabajo en torno a git am y todos están cubiertos
en Enlaces de flujo de trabajo de correo electrónico.

También lo usamos para aplicar cambios en las solicitudes de extracción de GitHub con formato de parche en las notificaciones por correo electrónico.

parche de formato git

El comando git format-patch se usa para generar una serie de parches en formato mbox que puedes usar para enviar a una
lista de correo correctamente formateada.

Pasamos por un ejemplo de contribución a un proyecto utilizando la herramienta de parche de formato git en Proyecto público
por correo electrónico.

git imap-enviar

El comando git imap-send carga un buzón generado con git format-patch en una carpeta de borradores IMAP.

Pasamos por un ejemplo de contribución a un proyecto mediante el envío de parches con la herramienta git imap-send en
Public Project over Email.

Git enviar correo electrónico

El comando git send-email se usa para enviar parches generados con git format-patch por correo electrónico.

Pasamos por un ejemplo de contribución a un proyecto mediante el envío de parches con la herramienta git send-email en
Public Project over Email.

solicitud de extracción de git

El comando git request-pull simplemente se usa para generar un cuerpo de mensaje de ejemplo para enviar por correo
electrónico a alguien. Si tiene una sucursal en un servidor público y quiere que alguien sepa cómo integrar esos cambios sin
enviar los parches por correo electrónico, puede ejecutar este comando y enviar el resultado a la persona que desea que
introduzca los cambios.

Demostramos cómo usar git request-pull para generar un mensaje de extracción en Forked Public Project.

508
Machine Translated by Google

Sistemas Externos
Git viene con algunos comandos para integrarse con otros sistemas de control de versiones.

git svn
El comando git svn se utiliza para comunicarse con el sistema de control de versiones de Subversion como cliente. Esto
significa que puede usar Git para pagar y comprometerse con un servidor de Subversion.

Este comando se cubre en profundidad en Git y Subversion.

Importación rápida de Git

Para otros sistemas de control de versiones o importar desde casi cualquier formato, puede usar git fast import para asignar
rápidamente el otro formato a algo que Git pueda registrar fácilmente.

Este comando se cubre en profundidad en Un importador personalizado.

Administración
Si está administrando un repositorio de Git o necesita arreglar algo a lo grande, Git proporciona una serie de comandos
administrativos para ayudarlo.

git gc
El comando git gc ejecuta la "recolección de basura" en su repositorio, elimina archivos innecesarios en su base de datos y
empaqueta los archivos restantes en un formato más eficiente.

Este comando normalmente se ejecuta en segundo plano para usted, aunque puede ejecutarlo manualmente si lo desea.
Repasamos algunos ejemplos de esto en Mantenimiento.

git fsck
El comando git fsck se usa para verificar la base de datos interna en busca de problemas o inconsistencias.

Solo usamos esto rápidamente una vez en Recuperación de datos para buscar objetos colgantes.

git reflog
El comando git reflog revisa un registro de dónde han estado todos los jefes de sus sucursales mientras trabaja para encontrar
confirmaciones que puede haber perdido al reescribir los historiales.

Cubrimos este comando principalmente en RefLog Shortnames, donde mostramos el uso normal y cómo usar git log -g para
ver la misma información con la salida de git log .

También pasamos por un ejemplo práctico de recuperación de una rama perdida en Recuperación de datos.

509
Machine Translated by Google

rama de filtro de git

El comando git filter-branch se usa para reescribir muchas confirmaciones de acuerdo con ciertos patrones, como
eliminar un archivo de todas partes o filtrar todo el repositorio a un solo subdirectorio para extraer un proyecto.

En Eliminar un archivo de cada confirmación , explicamos el comando y exploramos varias opciones diferentes,
como --commit-filter, --subdirectory-filter y --tree-filter.

En Git-p4 lo usamos para reparar repositorios externos importados.

Comandos de plomería
También hubo una gran cantidad de comandos de plomería de nivel inferior que encontramos en el libro.

El primero que encontramos es ls-remote en Pull Request Refs , que usamos para ver las referencias sin procesar
en el servidor.

Usamos ls-files en Manual File Re-merging, Rerere y The Index para tener una visión más cruda de cómo se ve
su área de preparación.

También mencionamos rev-parse en Branch References para tomar casi cualquier cadena y convertirla en un
objeto SHA-1.

Sin embargo, la mayoría de los comandos de plomería de bajo nivel que cubrimos están en Git Internals, que es
más o menos en lo que se enfoca el capítulo. Tratamos de evitar su uso durante la mayor parte del resto del libro.

510
Machine Translated by Google

Índice
@ pequeño equipo privado, 131

$EDITOR, 344 gran proyecto público, 148

$VISUALES pequeño proyecto público, 144

ver $EDITOR, credenciales, 337 crlf, 350

344 .gitignore, 345 .NET,


488 @{upstream}, 93 @{u}, CVS, 9

93
D

difftool, 347 git


A distribuido, 126

alias, 61 Dulwich, 494

apache, 118
mi
Apple, 489
editor
archivo, 359
atributos, 353 cambiar predeterminado, 34
correo electrónico, 150 aplicar
autocorrección, 346
parches desde, 152 excluye, 345, 432
B

fiesta, 478
F
Bazar, 418
archivos
archivos binarios, 353
moviendo, 37
BitKeeper, 12
sucursales, 63 quitando, 36

flujo de trabajo básico, 70 bifurcando, 128, 172

creación, 65 eliminación
GRAMO

remota, 94 diferenciación,
Git como cliente, 375
156 de ejecución
comandos de git agregar,
prolongada, 83
27, 27, 28 am, 153
administración, 79 fusión,
aplicar, 152 archivar,
75 remota, 155, 86
165 sucursal, 65, 79
conmutación, 66 tema,
pagar, 66 seleccionar,
152, 84 seguimiento, 92
161 clonar, 25
upstream, 92 números de
construcción, 164

desnudo,
C 110 confirmar, 34,

C, 484 63 config, 150, 19, 21, 34, 343, 61


credencial, 337 demonio, 116 describir,
Do#, 488
164 diferencia, 31 comprobar, 130
Cocoa, 489
importación rápida, 422
color, 346

plantillas de compromiso, 344


contribuyendo, 129 equipo
privado administrado, 138

511
Machine Translated by Google

fetch, 52 H
fetch-pack, 456 ganchos, 361
filter-branch, 422 post-actualización, 107
format-patch, 148
gitk, 471 gui, 471 I
help, 116, 22 http- ignorar archivos, 30
backend, 118 init, Importación
25, 27 bare, 111, 114 de Bazaar, 418 de
Mercurial, 414 de otros,
422 de Perforce, 421
instaweb, 120 de Subversion, 412
log, 38 merge,
73 squash, 147 trabajo integrador, 157
mergetool, Interoperación con otros VCS
78 p4, 404, 421 mercurial, 386
pull, 52 push, 52, Forzosamente, 396
58, 91 rebase, Subversión, 375
96 receive-pack, CRI, 22
454 remote, 50,
51, 53, 54 request- j
pull , 145 rerere, 163 Java, 489
paquete de envío, 454 jgit, 489
bitácora, 165 show, 57
show-ref, 378 estado, k

26, 34 svn, 375 etiqueta, expansión de palabras clave, 356


55, 56, 58 paquete de
carga, 456 git-svn, 375 L

libgit2, 484
finales de línea, 350
Linus Torvalds, 12
Linux, 12
instalación, 16
GitHub, 167 filtrado de registros,
API, 214 45 formateo de registros, 41
Flujo, 173
METRO

organizaciones, 207

solicitudes de incorporación Mac OS


de cambios, 176 cuentas de usuario, 167 instalar, 17
GitHub para macOS, 473 mantener un proyecto, 151
GitHub para Windows, 473 maestro, 64 Mercurial, 386,
gitk, 471 414 mergetool, 347 fusionar,
GitLab, 121 75 conflictos, 76 estrategias,
Git Web, 119 361 frente a rebase, 103 Migrar
Ir, 493 a Git, 412 Mono, 488
ir-git, 493
GGP, 345
Herramientas gráficas, 471
GUI, 471

512
Machine Translated by Google

O T

Objetivo-C, 489 fiesta de finalización de

origen, 86 pestañas, 478

PowerShell, 481
PAGS

zsh, 479 etiquetas,


buscapersonas, 345 163, 55 anotado, 56
Forzosamente, 12, 396, 421, 9 ligero, 57 firma, 163
Git Fusion, 396
ejemplo de política, 364
posh-git, 481
V
PowerShell, 481
protocolos HTTP control de versiones,
tonto, 107 git, 109 8 centralizado, 9
local, 105 HTTP distribuido, 10
inteligente, 107 local, 8
estudio visual, 476
SSH, 108
W
tirando, 94
empujando, 91 espacio en blanco, 350

Pitón, 489, 494 ventanas

instalación, 17
R
flujos de trabajo, 126
reorganización, centralizado, 126
95 peligros de, dictador y lugartenientes, 128
100 frente a fusión, administrador de integración, 127
103 referencias fusión, 158 fusión (grande), 160
remoto, 86 reorganización y selección selectiva,
liberación, 165 161
rerere, 163
X
Rubí, 485
código x, 17
S
Z
servir repositorios, 105 protocolo
git, 116 zsh, 479
GitLab, 121
Git Web, 119
HTTP, 117
SSH, 111
SHA-1, 14
avisos de shell
bash, 478
PowerShell, 481
zsh, 479
Claves SSH, 112
con GitHub, 168
omisión del área de
ensayo, 35
Subversión, 12, 127, 375, 412, 9

513

Das könnte Ihnen auch gefallen