¿Quiénes somos "nosotros" y quiénes son "ellos" según Git?

Resuelto danza asked hace 10 años • 2 respuestas

Después de una rebase de Git, y en otras circunstancias, puede encontrar algunos archivos marcados como eliminados por nosotros en el git statusinforme. ¿ Quiénes somos según Git y por qué?

¿Se refiere a que estoy sentado en esta rama y funciona para mí? ¿O se refiere a sí mismo y a las personas que trabajan en la rama contra la que estoy rebasando?

danza avatar Jan 09 '14 23:01 danza
Aceptado

Cuando fusionas , usse refiere a la rama en la que te estás fusionando, a diferencia de them, la rama que se fusionará.

Cuando rebase , usse refiere a la rama ascendente y themes la rama por la que se está moviendo. Es un poco contrario a la intuición en caso de una rebase.

La razón es que Git usa el mismo motor de fusión para rebase, y en realidad está seleccionando tus cosas en la rama ascendente. us= hacia, them= desde.

SzG avatar Jan 09 '2014 16:01 SzG

¿Quiénes somos "nosotros"/"nuestros" y "ellos"/"de ellos" según Git?

(Esto también responde a la pregunta: "¿Cómo funciona un rebase de git y qué está sucediendo exactamente con él?")

Cuando tienes conflictos en medio de un git merge, cherry-pick, rebaseo revert, es posible que tengas que elegir un lado para elegir mantener el contenido conflictivo solo del lado --ourso --theirs(y de lo contrario, mantener el contenido no conflictivo de ambos lados), lo que lleva a la Pregunta: ¿quiénes somos "nosotros" frente a "ellos" en cada uno de esos casos?

La resolución de conflictos podría implicar lo siguiente, por ejemplo:

git checkout master
git merge feature_branch
git checkout --theirs -- path/to/some/dir
git add path/to/some/dir 
git status 
git merge --continue 

Pero, ¿qué hace la git checkout --theirslínea y qué otras opciones tenemos? Aquí hay algunas opciones:

# ------------------------------------------------------------------------------
# Do this:
# - Generally, one of these might be useful during conflict resolution:
# ------------------------------------------------------------------------------

# Integrate changes from both sides, but in the lines which conflict, keep the
# `--theirs` changes for all conflicts within files inside this directory.
git checkout --theirs -- path/to/some/dir

# OR: Integrate changes from both sides, but in the lines which conflict, keep
# the `--ours` changes for all conflicts within files inside this directory.
git checkout --ours -- path/to/some/dir

# ------------------------------------------------------------------------------
# Do *not* do this:
# - Generally, do *not* do this during conflict resolution:
# ------------------------------------------------------------------------------

# Discard all changes from both sides by hard resetting all the contents inside of
# this directory back to their state at the `some_branch` commit. 
# - But, oddly enough, this does *not* do deletions of files inside this directory
#   which don't exist at this `some_branch` commit but are there now. So, this
#   isn't even a proper hard reset of this directory. It's a mishmash. 
git checkout some_branch -- path/to/some/dir

Consulte el "mejor ejemplo de resolución de conflictos" a continuación, así como todos los ejemplos de la sección "Casos de uso de ejemplo" , para obtener más detalles. Consulte también "¡ADVERTENCIA, ADVERTENCIA, ADVERTENCIA!" sección sin embargo. En la mayoría de los casos, no querrás hacerlo git checkout some_branch -- path/to/some/diren un intento de resolver conflictos, ya que eso descarta todo merge, cherry-pick, rebaseo revert, deshaciendo todos los cambios de un lado en lugar de mantener los cambios de ambos lados pero favoreciendo a un lado en las líneas en conflicto. Entonces, usar --ourso --theirs, como se muestra arriba, generalmente es el curso de acción correcto en lugar de usar some_branch.

Estudie esas secciones al final de mi respuesta para obtener más detalles.

Pasemos ahora a la respuesta principal:

En todos los casos:

En términos sencillos ( gracias, @user20358 ):

"nosotros" es el código que ya está en el repositorio y "ellos" es el código que [estás] intentando fusionar

En términos más completos:

  1. "nosotros" (o "nuestro"HEAD ) = el commit ( ) actualmente desprotegido en el momento en que git realiza la acción que causa el conflicto (más sobre esto más adelante), y:
  2. "ellos" (o "de ellos" ) = el otro compromiso, NO verificado por git en el momento en que git realiza la acción que causa el conflicto (más sobre esto más adelante).

IMPORTANTE: HEADen el momento en que realiza la acción que causa el conflicto NO es necesariamente la HEADen el momento en que escribe el comando git. Esto es esencial de entender. Git puede realizar algunas comprobaciones y cambiar HEAD(qué confirmación se verifica) antes de ejecutar la acción que causa el conflicto, lo que hace que "nosotros" y "ellos" aparezcan intercambiados o al revés para el ojo inexperto.

Los 4 casos, en detalle: fusionar, seleccionar, rebase, revertir:

  1. git merge(intuitivo):
    1. Comando de muestra:
      git checkout master
      git merge feature_branch  # merge feature_branch into master
      
    2. "us"/"ours" = HEAD, es decir master, porque estabas en la sucursal masteren el momento en que ejecutaste git merge feature_branch.
    3. "them"/"theirs" = feature_branch, que es la rama en la que te estás fusionando master.
  2. git cherry-pick(intuitivo):
    1. Comando de muestra:
      git checkout feature_branch
      git cherry-pick some_commit  # apply some_commit to feature_branch
      
    2. "us"/"ours" = HEAD, es decir feature_branch, porque estabas en la sucursal feature_branchen el momento en que ejecutaste git cherry-pick some_commit.
    3. "them"/"theirs" = some_commit, que es el compromiso que estás seleccionando feature_branch.
  3. git rebase(contraintuitivo, pero tiene mucho sentido una vez que comprendes la mecánica de cómo funciona):
    1. Comando de muestra:
      git checkout feature_branch
      git rebase master  # rebase feature_branch onto latest master
      
    2. Diagrama de esto (dibujado en https://asciiflow.com ), con las confirmaciones más recientes o más recientes en la parte superior y/o a la derecha:
      #             Prior to rebase: feature_branch
      #             received new commits while
      #             master did too
      # 
      #                           master
      #                           x
      #                           |          feature_branch
      #                           x          y
      #                           |          |
      #                           x          y
      #                           |         /
      # git merge-base      ────► x--y--y--y
      # master feature_branch     |
      #                           x
      # 
      # 
      #             After rebase: feature_branch has
      #             been completely cherry-picked onto
      #             the end of master
      # 
      #                                   feature_branch
      #                                   y'
      #                                   |
      #                                   y'
      #                                  /
      #                         y'--y'--y'
      #                         |
      #                  master x
      #                         |
      #                         x
      #                         |
      #                         x
      #                         |
      #                         x
      #                         |
      #                         x
      
    3. "us"/"ours" = HEAD, que es la rama ascendente: inicialmente la última xconfirmación en master, y luego, alguna NUEVA confirmación, y'además cherry-pickedde eso (¡ésta es complicada!). Esto se debe a que cuando escribiste git rebase master, git primero comprueba mastercomo el punto de partida para comenzar a seleccionar tus feature_branchconfirmaciones , luego determina cuáles confirmaciones feature_branchseleccionarás (es decir, cuáles de tus feature_branchconfirmaciones aún no están activadas master). Lo hace encontrando merge-base(el compromiso que es común a ambos feature_branchy masterque se puede encontrar con git merge-base master feature_branch), y ENTONCES comienza a seleccionar los compromisos desde el primero después de este merge-basey en adelante, trabajando uno a la vez, hacia la última confirmación en feature_branch, en la punta de master, "rebasando" todas ylas confirmaciones "nuevas" que agregó feature_brancha la última master, como nuevas y'confirmaciones. Por lo tanto, "nosotros"/"nuestros" = HEAD, pero dado que git realizó una nueva verificación detrás de escena para realizar esta rebase, HEADNO es la rama en la que estaba cuando escribió git rebase master. En cambio, nosotros , o HEAD, es la última xconfirmación mastersi el conflicto ocurre durante la primera cherry-pick, o es cualquier NUEVA confirmación, que y'fue seleccionada con éxito por última vez mastersi el conflicto ocurre durante una selección posterior. Por lo tanto, es el otro compromiso, que es un ycompromiso feature_branchque se aplica a este nuevo HEADmediante una selección, en orden, DESDE el primer ycompromiso feature_branchque es inmediatamente después git merge-base master feature_branch HASTA el último ycompromiso feature_branch. Vea también la explicación para "ellos", justo debajo.
    4. "ellos"/"sus" = alguna yconfirmación feature_branchque se está aplicando a un recién retirado HEAD, donde HEADestá la última xconfirmación masterpara la primera operación de selección durante la rebase, O una de estas y'confirmaciones recién creadas en la parte superior de masteras feature_branchestá "rebasada", o se selecciona una confirmación a la vez (a lo largo de su cadena de nuevas confirmaciones desde git merge-base master feature_branchla última confirmación en feature_branch) en master. Vea también la explicación para "nosotros", justo arriba.
  4. git revert(algo intuitivo):
    1. Comando de muestra:
      git checkout feature_branch
      # create a new commit to undo the changes from some_previous_commit
      # within feature_branch
      git revert some_previous_commit  
      
    2. Para conocer algunas de las mecánicas detalladas de bajo nivel de esta, consulte mi sección "Resultados y conclusiones" al final de mi otra respuesta aquí , así como esta respuesta muy larga y detallada de @torek aquí .
    3. "us"/"ours" = HEAD, es decir feature_branch, porque estabas en la sucursal feature_branchen el momento en que ejecutaste git revert some_previous_commit. Más específicamente, "nosotros"/"nuestro" contiene los cambios expresados ​​por git diff some_previous_commit..HEAD, que son los cambios desde el punto en el que se confirmó la confirmación revertida ( some_previous_commit) hasta la confirmación en la que nos encontramos ahora.
    4. "them"/"theirs" = (lo inverso u opuesto a) some_previous_commit, que es la confirmación cuyos cambios estás revirtiendo (deshacer, creando una nueva confirmación encima feature_branch). En otras palabras, "ellos"/"de ellos" contiene los cambios expresados ​​por git diff some_previous_commit..some_previous_commit~, donde some_previous_commit~está el compromiso principal de some_previous_commit. Esto significa que "ellos"/"de ellos" es lo opuesto a some_previous_commit, ya que contiene lo opuesto a sus cambios, para deshacer some_previous_commitlos cambios de .

Casos de uso de ejemplo:

Fusionar ejemplos de resolución de conflictos:

# 1. Merge `feature_branch` into `master`, accepting ALL of 
# `master`'s (`ours`) changes in the event of 
# any merge conflicts! 
git checkout master
git merge -X ours feature_branch

# 2. Merge `feature_branch` into `master`, accepting ALL of 
# `feature_branch`'s (`theirs`) changes in the event of 
# any merge conflicts! 
git checkout master
git merge -X theirs feature_branch

Aquí hay algunos más. Estas son mis técnicas más utilizadas , en lugar de los dos ejemplos anteriores.

# 3. Assuming this merge attempt results in merge conflicts in
# these 3 files, but we know all of the changes on the `master`
# branch side are the ones we wish to keep, check out these 3 
# files from `master` (`--ours`) to overwrite the conflicted
# files. Then, add these now-fixed files to stage them for 
# committing, and continue (finish) the merge. 
git checkout master
git merge feature_branch
git checkout --ours -- path/to/somefile1.c path/to/somefile2.c path/to/somefile3.c
git add path/to/somefile1.c path/to/somefile2.c path/to/somefile3.c
git status 
git merge --continue

# 4. Assuming this merge attempt results in merge conflicts in
# these 3 files, but we know all of the changes on the `feature_branch`
# side are the ones we wish to keep, check out these 3 
# files from `feature_branch` (`--theirs`) to overwrite the conflicted
# files. Then, add these now-fixed files to stage them for 
# committing, and continue (finish) the merge. 
git checkout master
git merge feature_branch
git checkout --theirs -- path/to/somefile1.c path/to/somefile2.c path/to/somefile3.c
git add path/to/somefile1.c path/to/somefile2.c path/to/somefile3.c
git status 
git merge --continue

MUY ÚTIL: Si existe una carpeta completa de conflictos, también puede especificar aceptar todos los cambios conflictivos de la rama --ourso para toda la carpeta a la vez , ¡así!:--theirs

**MEJOR EJEMPLO DE RESOLUCIÓN DE CONFLICTOS DE FUSIÓN:**

# 5. [BEST EXAMPLE] Assuming this merge attempt results in merge conflicts in
# a bunch of files, some of which are inside `path/to/some/dir`, I can
# choose to accept the changes from one side or the other **for 
# all conflicts within files inside this directory**, like this!:
git checkout master
git merge feature_branch

# Keep `--theirs` for all conflicts within files inside this dir
git checkout --theirs -- path/to/some/dir
# OR: keep `--ours` for all conflicts within files inside this dir
git checkout --ours -- path/to/some/dir

# Add (stage for committing) all changes within files inside this dir 
# all at once
git add path/to/some/dir 
git status 
git merge --continue 

TRATAMIENTO path does not have our versionDE path does not have their versionERRORES:

Si alguna vez ejecutas algo como esto:

git checkout --ours -- path/to/some/dir

...y no funcionó! No hizo nada. En cambio, genera estos errores:

error: path 'path/to/some/dir/file1.cpp' does not have our version
error: path 'path/to/some/dir/file2.cpp' does not have our version
error: path 'path/to/some/dir/file3.cpp' does not have our version

El problema es que estos archivos con errores son archivos eliminados en el ourlateral, por lo que debemos git rmcada uno de ellos manualmente ANTES de ejecutar git checkout --ours -- path/to/some/dir.

git rm path/to/some/dir/file1.cpp path/to/some/dir/file2.cpp \
path/to/some/dir/file3.cpp

# then try again
git checkout --ours -- path/to/some/dir

También puedes hacer esto para automatizar el proceso:

git checkout --ours -- path/to/some/dir \
|& gawk '{ print $3 }' | xargs git rm

git checkout --ours -- path/to/some/dir

Vea mi respuesta aquí para obtener una explicación detallada de los comandos anteriores: git checkout --ours cuando la especificación del archivo incluye el archivo eliminado .

¡ADVERTENCIA ADVERTENCIA ADVERTENCIA!

Para obtener un ejemplo más detallado del problema que se describe a continuación, consulte mi otra respuesta aquí .

NO lo haga git checkout -- path/to/some/dir, ni git checkout some_branch -- path/to/some/diren medio de la resolución de un conflicto (como durante un mergeconflicto como en los ejemplos anteriores), A MENOS QUE INTENTE VERIFICAR TODOS LOS ARCHIVOS DE HEAD, o de some_branch, respectivamente, en el directorio path/to/some/dir, Y SOBRESCRIBIR LOS ARCHIVOS LOCALES CON AQUELLOS ARCHIVOS, por lo que no se limita a aceptar los cambios conflictivos de un lado o del otro.

En otras palabras, en medio de la resolución de un conflicto , esto:

BIEN:

# GOOD :)
# Accept all conflicts from one side or the other (while still 
# merging changes from both sides into one, in the event of being
# in the middle of a `git merge`).

git checkout --ours -- path/to/some/dir
# OR
git checkout --ours -- path/to/some/file

aceptará solo los cambios del lado --ours, lo cual siempre es bueno y seguro si eso es lo que queremos, mientras que esto:

MALO:

# BAD :(
# OVERWRITE all files with these files from `some_branch` instead,
# thereby _losing_ any changes and/or files contained in the other
# side but which are not in `some_branch`.

git checkout some_branch -- path/to/some/dir 
# OR
git checkout some_branch -- path/to/some/file

will fully check out and overwrite the ENTIRE DIRECTORY or ENTIRE FILE, as specified, rather than only the conflicting changes themselves. This means you may be inadvertently deleting changes from one side or the other by doing a full checkout with git checkout some_branch rather than a conflict resolution with git checkout --ours or git checkout --theirs. FOR THIS REASON, IT IS RECOMMENDED TO USE git checkout --ours -- file_or_dir_paths or git checkout --theirs -- file_or_dir_paths, NOT git checkout some_branch -- file_or_dir_paths whenever you are in the middle of a conflict resolution such as for git merge, git cherry-pick, git rebase, or git revert.

HOWEVER, if you DO run git checkout some_branch -- file_or_dir_paths because you understand this behavior and that's what you want, then you need to be aware of this too: checking out an entire directory like that does NOT delete local files in that dir which do not exist at some_branch, like you'd expect it would. Instead, it leaves them alone in your local file system if they exist locally but not at some_branch. So, you must MANUALLY remove all of those files instead. Keep that in mind or else it will leave you very very confused in the end. Read more about this in my other answers here (this answer has a good solution to that too) and here.

Going further / additional notes and tips:

  1. The above examples of file conflict resolution can apply in the event of conflicts in any of the various types of operations (git merge, git cherry-pick, git rebase, git revert, etc.), except you need to keep in mind what theirs and ours means for each type of conflict resolution, as explained above.
  2. You can also just use branch names such as master or feature_branch in place of -X ours/-X theirs and --ours and --theirs. WARNING: Passing branch names may seem easier and clearer, but CAN BE DANGEROUS to your changes, as doing it this way does a FULL FILE OR DIRECTORY REPLACEMENT, RATHER THAN A CONFLICT RESOLUTION FOR JUST THE CONFLICTS WITHIN THE FILES. See the "WARNING WARNING WARNING" section above. If you DO want to do a full file replacement, however, rather than just accepting conflicting changes from one side or the other, here's how:
    # See "WARNING WARNING WARNING" section above.
    git checkout feature_branch -- path/to/somefile1.c path/to/somefile2.c path/to/somefile3.c
    
  3. You can also check out entire directories rather than specifying files individually! See this answer here and my answer here and my other answer here. I just tested it. This works too. Again, however, pay attention to the "WARNING WARNING WARNING" section above. This does a full directory replacement, rather than just accepting conflicting changes from one side or the other for an entire folder:
    # Check out ALL files from feature_branch which are in
    # directory "path/to/dir". See "WARNING WARNING WARNING"
    # section above.
    git checkout feature_branch -- path/to/dir
    
  4. Remember, if you do intentionally use git checkout feature_branch -- path/to/dir, expecting/hoping it will delete local files in directory path/to/dir which do NOT exist at feature_branch, it will NOT. You must remove those files manually in your local file system before or after running the checkout command. Read more in my answers here:
    1. All about checking out files or directories in git
    2. How to do a --soft or --hard git reset by path

References:

  1. [my answer, which I reference all the time!] "All about checking out files or directories in git": How to get just one file from another branch
  2. [my answer] Why git can't do hard/soft resets by path? --> see especially the git terminology and definitions in the "Background knowledge" section at the end!
  3. [my own answer on what "them" and "us" mean during git revert] Who is `them` and `us` in a `git revert`?
  4. [@LeGEC's answer which points out that "ours/us" is always HEAD, and "them/theirs" is always the other branch or commit] Who is `them` and `us` in a `git revert`?
  5. [the pre-existing answer which I cross-checked against regarding my understandings of git merge and git rebase] Who is "us" and who is "them" according to Git?
  6. From man git checkout: "Note that during git rebase and git pull --rebase, ours and theirs may appear swapped":
    --ours, --theirs
        When checking out paths from the index, check out stage #2 (ours) or #3 (theirs) for
        unmerged paths.
    
        Note that during git rebase and git pull --rebase, ours and theirs may appear swapped;
        --ours gives the version from the branch the changes are rebased onto, while --theirs
        gives the version from the branch that holds your work that is being rebased.
    
        This is because rebase is used in a workflow that treats the history at the remote as
        the shared canonical one, and treats the work done on the branch you are rebasing as the
        third-party work to be integrated, and you are temporarily assuming the role of the
        keeper of the canonical history during the rebase. As the keeper of the canonical
        history, you need to view the history from the remote as ours (i.e. "our shared
        canonical history"), while what you did on your side branch as theirs (i.e. "one
        contributor’s work on top of it").
    
  7. [answer to my question]: you can pass directory paths to git too to check out all files from entire directories, rather than having to specify files individually: How do I accept git merge conflicts from "their" branch for only a certain directory?
  8. [my answer] git checkout --ours when file spec includes deleted file
  9. For drawing pretty ASCII pictures or diagrams to place in code: https://asciiflow.com/.

Additional Study:

  1. [I NEED TO STUDY THIS ANSWER MYSELF STILL!--done; I've figured it out and updated my answer here now as of 7 Jan. 2020] Who is `them` and `us` in a `git revert`?
  2. [I NEED TO STUDY AND READ THIS STILL] git checkout --ours does not remove files from unmerged files list

See also:

  1. Quick links to my answers I reference frequently and consider to be "git fundamentals":
    1. Various ways to create a branch in git from another branch
    2. All about checking out files or directories in git
    3. Who is "us" and who is "them" according to Git?
Gabriel Staples avatar Sep 16 '2020 00:09 Gabriel Staples