Situación:
Tienes que trabajar en dos proyectos: project1
y project2
.
Los dos están en el el mismo servidor (por ejemplo, GitHub
)
pero en cada uno vas a hacer commits con un usuario diferente:
user1
y user2
respectivamente.
Además, el acceso para interactuar con el servidor (clone
, pull
,
push
) también requiere usuarios diferentes.
2 problemas:
- Acceso al repositorio remoto: Tener que conectarte al mismo servidor remoto con dos identidades distintas requiere "desdoblar" de alguna manera la referencia al servidor para poder tener dos configuraciones distintas para él
- No confundir usuarios: Usar en el mismo equipo dos identidades puede provocar que sin darte cuenta hagas commit en un repositorio con el usuario equivocado
¿Cómo acceder al repositorio remoto?
Para solucionar el primer problema usaremos autenticación por SSH
y crearemos dos configuraciones distintas en .ssh/config
para
acceder al servidor.
1- Generar las claves SSH
$ ssh-keygen -t rsa -b 4096 -C "user1@example.com"
Generating public/private rsa key pair.
Enter a file in which to save the key (/home/you/.ssh/id_rsa): /home/you/.ssh/user1
Enter passphrase (empty for no passphrase): [Type a passphrase]
Enter same passphrase again: [Type passphrase again]
$ ssh-keygen -t rsa -b 4096 -C "user2@example.com"
Generating public/private rsa key pair.
Enter a file in which to save the key (/home/you/.ssh/id_rsa): /home/you/.ssh/user2
Enter passphrase (empty for no passphrase): [Type a passphrase]
Enter same passphrase again: [Type passphrase again]
Y las añadimos a GitHub
siguiendo los
pasos indicados en su manual.
2- Configurar SSH
Suponiendo que user1
es el usuario que queremos usar por defecto
editamos .ssh/config
de la siguiente manera:
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/user1
Host github-user2
HostName github.com
User git
IdentityFile ~/.ssh/user2
3- Configurar los repositorios
La clave esta en clonar cada repositorio con el host ssh
adecuado.
$ git clone git@github.com:user-random/project1.git
$ git clone git@github-user2:user-random/project2.git
De manera que en project1
tendremos un proyecto que accede al
servidor con el usuario user1
y en project2
tendremos un proyecto
que accede al servidor con el usuario user2
.
Bonus: Por comodidad, también podemos configurar git
para usar ssh
en vez de https
Como hemos dicho que user1
va a ser nuestro usuario por defecto y
en GitHub
es más habitual clonar usando https
, podemos configurar
git
para que transforme las urls directamente a ssh
de esta manera:
$ git config --global url.ssh://git@github.com/.insteadOf https://github.com/
Esto hará que hacer:
$ git clone https://github.com/user-random/project1
sea equivalente a hacer:
$ git clone git@github.com:user-random/project1.git
y como en .ssh/config
la entrada para github.com
es la de user1
,
se usara su clave para el acceso.
¿Cómo hacer commit con distinto usuario?
Manualmente podemos ir a cada repositorio y definir una configuración local:
$ cd project1
project2/ $ git config --local user.name user1
project2/ $ git config --local user.email user1@example.com
$ cd ..
$ cd project2
project2/ $ git config --local user.name user2
project2/ $ git config --local user.email user2@example.com
pero si con el tiempo vamos a ir teniendo más y más proyectos para cada uno de los usuarios debemos automatizar esto para evitar errores.
Hay varias alternativas:
Obligar a usar la configuración local
Para evitar que en un proyecto de user2
usemos la identidad
de user1
sin querer por no haber definido la configuración
local, podemos obligar a que sea un requisito haciendo:
git config --global --add user.useConfigOnly true
git config --global --unset-all user.email
git config --global --unset-all user.name
Una carpeta por usuario
Si tenemos todos los proyectos de user1
en ~/wks1
y
todos los proyectos de user2
en ~/wks2
podemos añadir
en ~/.gitconfig
las lineas:
[includeIf "gitdir:~/wks1/"]
path = ~/wks1/.gitconfig
[includeIf "gitdir:~/wks/"]
path = ~/wks2/.gitconfig
y escribir ~/wks1/.gitconfig
con el contenido:
[user]
name = user1
email = user1@example.com
y ~/wks1/.gitconfig
con el contenido:
[user]
name = user2
email = user2@example.com
Crear un hook para git clone
En realidad no existe hook
para clone
, pero podemos usar el hook
post-checkout
que se ejecuta tras un checkout
, lo cual siempre
sucede cuando se hace clone
(a no ser que se use el argumento --no-checkout
).
Para ello hacemos:
$ mkdir -p ~/.git/hooks
$ git config --global core.hooksPath ~/.git/hooks
$ touch ~/.git/hooks/post-checkout
y en ~/.git/hooks/post-checkout
ponemos este script:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import git
import ConfigParser
import os
import sys
repo = git.Repo(os.getcwd())
# Don't do anything if an identity is already configured in this
# repo's .git/config
config = repo.config_reader(config_level = 'repository')
try:
# The value of user.email is non-empty, stop here
if config.get_value('user', 'email'):
sys.exit(0)
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
# Section or option does not exist, continue
pass
origin = repo.remote('origin')
if not origin:
print('** Failed to detect remote origin, identity not updated! **')
sys.exit(0)
email = None
user = None
# This is where you adjust the code to fit your needs
if '/user1/' in origin.url:
user = "user2"
email = 'user2@example.com'
elif '/user2/' in origin.url:
user = "user1"
email = 'user1@example.com'
if email:
# Write the option to .git/config
config = repo.config_writer()
config.set_value('user', user)
config.set_value('email', email)
config.release()
print('User identity for this repository set to %s <%s>' % (user, email))
¿Y si aún así metes la pata?
Si a pesar de todo ya has hecho un commit con el usuario erróneo puedes reescribir el autor de los commits con el siguiente script:
#!/bin/sh
git filter-branch --env-filter '
OLD_EMAIL="user2@example.com"
CORRECT_NAME="user1"
CORRECT_EMAIL="user1@example.com"
if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_COMMITTER_NAME="$CORRECT_NAME"
export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_AUTHOR_NAME="$CORRECT_NAME"
export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags
adecuando las variables OLD_EMAIL
, CORRECT_NAME
y CORRECT_EMAIL
a tu caso.
Y luego fuerza el cambio con git push --force --tags origin HEAD:master
Pero ten en cuento que esto reescribe el histórico y puedes romper un montón de ramas.
Fuentes: medium.com/@therajanmaurya, collectiveidea.com, dvratil.cz, blog.sleeplessbeastie.eu, stackoverflow.com, stackoverflow.com