Cómo borrar una rama local y remota en Git

logo de git

A veces no te interesa una rama de git porque has hecho experimentos con ella y no quieres conservarla. O bien has terminado con ella, has hecho merge a la rama principal y quieres borrarla. Vemos como borrar esas ramas tanto en local como en remoto.

Borrar rama local de Git

Para borrar una rama local de Git tienes el comando

git branch -d <nombre-rama>

Siendo nombre-rama el nombre de la rama que quieres borrar. No puedes borrar la rama en la que estás actualmente. Y este comando te dará un aviso si intentas borrar una rama cuyos cambios no has mergeado en otra rama, avisándote que perderas esos cambios definitivamente, puesto que no hay otra rama que los tenga.

Si quieres borrar sin este aviso, puedes usar la opción -D mayúscula

git branch -D <nombre-rama>

Esto fuerza el borrado de la rama, aunque no tengas esos cambios en otro sitio. Los perderás derfinitivamente.

Borrar rama remota de Git

Para borrar una rama remota el comando es

git push origin -d <nombre-rama>

origin, por supuesto, es el nombre habitual del repositorio remoto. En tu caso podría ser otro si lo has configurado así. En versiones más antiguas de Git (anteriores a 1.7.0) el comando es algo menos intuitivo

git push origin :<nombre-rama>

Un : (dos puntos) delante del nombre de la rama en vez de una opción -d

Actualizar repositorio local

Si hemos borrado una rama remota pero todavía la tenemos en local, bien porque no la hemos borrado, bien porque la ha borrado otro programador de nuestro equipo, debemos reflejar esos cambios en local. El comando es

git fetch --all --prune

Esto borrará todas las referencias locales (ramas, tags, etc) que hayan sido borradas del remoto. Si solo tienes un remoto, normalmente origin, no es necesaria la opción –all. La opción –prune es la que se encarga de limpiar.

Publicado en git | Etiquetado , , , , | Deja un comentario

Deshacer el cambio más reciente en GIT

logo de git

A veces subimos cosas a git que luego no queremos que estén subidas. Es relativamente habitual hacer un commit y darnos cuenta luego que no hemos subido exactamente lo que queríamos o que se ha subido algún fichero de más. Nos gustaría deshacer el cambio. ¿Cómo lo deshacemos?

Si no has subido con push el cambio al servidor remoto, el mecanismo es fácil.

El comando git reset hace que tu copia de trabajo apunte a un commit concreto, borrando de tu repositorio local todos los commit posteriores. Tu copia local de trabajo la deja como esté, por lo que no pierdes los cambios que has metido por error.

Tienes que tener ojo con esto, pierdes toda la historia de git posterior al commit al que hayas decidido ir. Si tu copia local no tiene las cosas que te interese mantener, tendrá que volver a hacerlas.

Si quieres deshacer justo el último commit, el comando es

git reset HEAD~

HEAD es el commit de tu repositorio local al que apunta tu directorio de trabajo. Habitualmente, HEAD suele ser el último commit, es decir, el erróneo que quieres deshacer. Puedes indicar commits anteriores con HEAD~1, HEAD~2, etc, según quieras el commit anterior, o dos anteriores, etc. Como abreviatura, HEAD~1 se puede poner simplemente como HEAD~. También es posible poner HEAD~<hash-del-commit> para ir a uno concreto sin necesidad de contar commits.

Una vez ejecutado el comando, el último commit se deshará de nuestro repositorio local, pero nosotros tendremos la copia de trabajo tal cual la teníamos, sin cambios. Ya solo nos queda corregir lo que fuera que hubieramos hecho erróneo y volver a hacer el commit.

Resumen: Para deshacer el cambio más reciente en un commit de git, si no lo has subido al servidor con un push

  • git reset HEAD~
  • // Corregir en local lo que necesitemos
  • git add … // si queremos añadir ficheros nuevos.
  • git commit …. // meter los cambios correctos.

Algunos detalles a tener en cuenta

HEAD~ funciona bien si la historia hacia atrás es más o menos lineal. Si tu commit es de hacer un merge entre dos ramas, HEAD~ elegirá una de ellas, quizás no la que quieras. En ese caso, quizás es mejor usar HEAD~<hash-del-commit>

Otra opción para deshacer cambios es git revert. La diferencia es que git revert realiza un nuevo commit para deshacer los cambios. Es decir, en los commits tendrás el commit erróneo más un nuevo commit que deshace los cambios del commit erróneo. Puede ser interesante si no estás seguro de querer perder el commit erróneo.

Publicado en git | Etiquetado , , | Deja un comentario

Servicios Angular: providedIn y providers

logo de angular

Estoy jugando con los Módulos en Angular. Y como siempre, me gusta hacer un ejemplo lo más sencillo posible para asentar los conceptos. Mi ejemplo: un módulo principal «tonto» que importe un módulo «Greeting» y lo use.

Como los módulos teóricamente sirven para agrupar componentes y exponer solo algunas de ellas, pues a «Greeting» le pongo un servicio GreetingService que devuelva un «Hola» y un componente GreetingComponent que lo muestre en pantalla. La idea es exportar el componente ocultando el servicio al módulo principal.

Sin problemas por el lado del componente. En el fichero greeting.module.ts basta poner en declarations y en exports el componente GreetingComponent.

@NgModule({
  declarations: [GreetingComponent],
  exports: [GreetingComponent],
  ...
})
export class GreetingModule { }

Pero con los servicios… ¡Menudo lío!. Entenderlo me ha dado bastante guerra. Estaba empeñado en ocultar el servicio salvo que el módulo lo exportara, pero no es posible. Los servicios no tienen nada que ver con los módulos. Más allá de que un módulo diga que necesita un servicio.

Cuando declaramos un servicio, podemos ponerle providedIn y decirle dónde se registra. Hay cinco valores posibles: Un nombre de módulo o componente. Uno de los textos ‘root’, ‘platform’ o ‘any’. O bien null o dejarlo vacío, que es lo mismo. Pero tienen comportamientos muy similares y dos de esas opciones están obsoletas. ‘platform’ es un caso especial que sólo tiene sentido si varias aplicaciones Angular corren en el mismo navegador. Así que quedan

  • root‘: Se instancia el servicio una sola vez cuando alguien lo necesite y está diponible para todo el que lo quiera, sin necesidad de que lo declare en su apartado ‘providers’
  • null: Se instancia el servicio una vez cuando alguien lo necesite, pero tienen que ponerlo en su apartado ‘providers’

Así que providedIn sirve básicamente para decir si el servicio está diponible por defecto para todo el mundo o sólo para el que lo indique explícitamente en ‘providers’.

Pero hay más. Había varias opciones que están obsoletas y todas hablan de que pueda haber varias instancias del mismo servicio. ¿Es esto posible? ¿Y cómo funciona?

Con la opción ‘root’ no hay nada de esto. Instancia única para todos.

Pero la opción null sí permite esto. El mecanismo es el siguiente. Cuando se carga la aplicación para ponerla en marcha, habrá una serie de módulos angular de nuestra aplicación (los decorados con ‘@NgModule’) que se cargarán en el arranque porque la página que se muestra inicialmente los necesita.

Pero podemos crear módulos que no se necesiten en el arraque y luego, por alguna acción del usuario como pinchar un enlace o un botón, provocar la carga del módulo para mostrarle un determinado contenido nuevo. Estos modulos los llamaremos ‘lazy loaded modules’.

Y una vez soltado este rollo, ¿qué hace la opción null de providedIn?. Todos los módulos que se cargan en el arranque compartirán una misma instancia del servicio. Pero los módulos ‘lazy loaded’ obtendrán instancias nuevas de este servicio.

Resumen:

Aparte de ‘platform’, sólo hay dos opciones no obsoletas para providedIn.

‘root’ : Una instancia única del servicio para todo el que la necesite y el que la necesite no tiene que ponerla en ‘providers’. Es la forma aconsejada.

null: Una instancia única para todos los módulos que se carguen en el arranque de la aplicación. Una instancia nueva para los módulos ‘lazy loaded’ que se carguen después. El que necesite este servicio tiene que ponerlo en ‘providers’. Debería usarse sólo en caso de que el servicio tenga un estado interno propio para cada posible cliente. Y en este caso, todos los módulos, salvo quizás uno, deberían ser ‘lazy loadaded’ para asegurar que cada módulo tiene su propia instancia.

Publicado en angular | Etiquetado , | Deja un comentario

Librería collections de Python

python

Pues sigo con ChatGPT de ayudante y he encontrado una forma que me ha gustado. En vez de preguntarle algo concreto simplemente le digo algo como «dame un tip de programación en python». Y te cuenta «cosas». Y está bien, porque te cuenta cosas de python de forma aleatoria (o no) y a veces son cosas que no conocías. No son cosas complejas, nada que no puedas mirar por google en un minuto, si las buscas. La gracia es que te dice cosas que quizás no sabías que existían y que por tanto no ibas a buscar salvo que te surgiera una necesidad concreta. Es una forma de aprender cosas nuevas.

Una de las cosas que me ha contado es sobre algunas de las clases de librería collections de Python. Me ha contado cuatro clases de ahí: Counter, defaultdict, namedtuple y deque.

  • Counter nos ayuda a contar elementos de una lista que cumplan determinadas condiciones.
  • defaultdict es como un dict pero nos devuelve un valor por defecto si accedemos a una clave que no existe, en vez de dar un error.
  • namedtuple es una tupla a la que podemos acceder a sus elementos por un nombre en vez de por índice.
  • deque es una cola eficiente para añadir y retirar elementos de los dos extremos.

Y el artículo con los detalles, escrito por ChatGPT pero con algunos arreglos míos está en Uso de la librería collections en python.

Los retoques míos son más que nada por dar un poco de claridad. He revisado los ejemplos y visto que funcionan, pero leyendo tal cual lo que me contaba ChatGPT no me quedaba lo suficientemente claro, así que decidír reescribir o ampliar algunas definiciones que daba.

Publicado en python | Etiquetado , , | Deja un comentario

Decoradores de TypeScript y ChatGPT

logo de angular

Con esto de Angular, me he puesto a escribir cosas de TypeScript. En concreto, sobre Decoradores de TypeScript. Y ya puestos, pues con ChatGPT le voy pidiendo ejemplos de código y explicaciones, para llevarlos a mis pruebas TypeScript en github.

Pues bien, todo más o menos «sobre ruedas», por decir algo. ChatGPT ayuda bastante y soy más rápido haciendo mis ejemplos y luego redactando en la wiki.

Pero no es oro todo lo que reluce. Hay varios tipos de decoradores, de clase, de atributo de clase, de método y de parámetro de método.

Quise hacer a modo de prueba un decorador de forma que cuando alguien haga un new de la clase decorada, saque un log de que se ha hecho el new. Buscando por google y el ejemplo de ChatGPT te lo ponen siempre más o menos igual: una función decoradora que saca el log, una clase decorada y se hace un new para ver que sale.

Pero ni la mayoría de los ejemplos/tutoriales de google ni ChatGPT te dicen dos cositas:

  • Al decorador se le llama una sola vez y es cuando se declara la clase. No cuando se instancia. En los ejemplos hacen un solo new y sale el log del decorador, todo aparentemente «guay». Pero si no haces ningún new también sale. El log del decorador sale independientemente de si haces new o no haces new y si haces un new o quince news. Sale una sola vez. Siempre.
  • La sintaxis que te ponen es experimental, así que ni siquiera compila. Tienes que habilitar la opción experimentalDecorators.

Así que se sigue investigando y se descubre el truco. Para nuestro ejemplo, el decorador debe devolver una clase hija de la original, con un constructor nuevo que saque el log que queremos por pantalla y llame al constructor de la clase padre original.

Y algo parecido pasa con los decoradores de método. Debes devolver una función alternativa que haga lo que tú quieres (el log en nuestro ejemplo) y llamar a la función original.

Y con los atributos, un poco más raro. Debes definir un método set() y get() para el atributo que haga lo que tu quieres y asignarlo al atributo en un PropertyDescriptor. Debes definir los dos, aunque sólo quieras hacer algo en el set() o en el get()

Y la liamos con los decoradores de parámetro de método. Le pido a ChatGPT el código para hacer log cada vez que llamen a un método que tenga un parámetro decorado, me lo hace. Y no funciona. Miro ejemplos por internet y todos se quedan en lo básico, sacar un log cuando llaman al decorador la primera y única vez que lo llaman. Media hora de charla con ChatGPT para intentar arreglar el código, no hay manera.

Así que me fuí a la documentación oficial: Parameter Decorators y veo un ejemplo rarísimo, además mezclado con un decorador de método. Y mirando con más calma, veo este aviso

NOTE  A parameter decorator can only be used to observe that a parameter has been declared on a method.

¡Aclarado!. El decorador de parámetro básicamente no sirve para nada :). Sólo te avisa que un parámetro ha sido decorado, pero no hay forma de que te avise cada vez que se llame a un método que tenga el parámetro decorador para hacer algo como sacar log o cambiar algo. De ahí que tengas que mezclarlo con un decorador de método:

  • El código que tienes que hacer en el decorador de parámetro es guaradarse en algún sito que el parámetro ha sido decorado con tal decoración. Una tabla o mapa donde pongas nombre del parámetro, de qué clase y método y qué decoración tiene.
  • Y luego tienes que hacer un decorador del método que devuelva un método alternativo que consulte eso que te has guardado para hacerle la lógica al parámetro.

Y ChatGPT, conmigo, emperrado en arreglar un código que no funciona. Y básicamente porque los montones de ejemplos que hay por internet tampoco han profundizado un poco en el tema, se quedan en que me avisa (una sola vez) cuando decoro el parámetro, independientemente de que lo use o no.

Publicado en TypeScript | Etiquetado , , , | Deja un comentario

EditorConfig

EditorConfig

Me he puesto a juguetear con Angular, una de mis asignaturas pendientes.

El primer paso, ¿cómo no?, crear un «Hola Mundo». Pero el comando ng new my-app crea demasiados ficheros y demasiadas cosas. Incluso con las opciones para hacer el «Hola Mundo», lo más reducido posible, como –minimal

Así que me pongo a revisar ficheros que crea para ver de cuáles puedo prescindir, que no sean estrictamente obligatorios para un «Hola Mundo». Y me encuentro con el fichero .editorconfig

Aparentemente es un fichero en el que se define el estilo que el editor que uses debe poner en tu código fuente. Pero ¿qué editor? ¿uno concreto? ¿todos?. Así que indago…. y resulta que la mayoría de los IDES lo soportan

En algunos viene incorporado «de fábrica», desde editores «simple» como vim o IDEs completos, como IntelliJ. Para otros hay que intalar un plugin como eclipse, netbeans o notepad++

El fichero tiene una sitaxis relativamente sencilla. Para cada extensión de fichero podemos decir el tipo de retorno de carro a usar, como hacer los sangrados (espacios, tabuladores, de qué tamaño), la codificación de caracteres a utilizar y poco más.

Puedes ver más detalles en EditorConfig.

Publicado en eclipse, IntelliJ | Etiquetado , , , , , | Deja un comentario

Map.computeIfAbsent()

Logo de Java

Aunque está ya desde Java 8, acabo de descubrir el método computeIfAbsent() de Map

Hay veces que en mi código tengo un Map cuya clave es cualquier cosa, un String por ejemplo, y que el valor es a su vez otra Collection, por ejemplo, una List

Map<String, List<Integer>> myMap = new HashMap<>();

Cuando llegaba el momento de meter un nuevo Integer en la lista valor, siempre había que comprobar primero si había o no lista para crearla en caso de que no. Luego ya se podía añadir el Integer

if (null == myMap.get("key1")) {
   myMap.put("key1", new ArrayList());
}
myMap.get("key1").put(anInteger);

Pues bien, el método computeIfAbsent() nos facilita esta tarea. El código quedaría

myMap.computeIfAbsent("key1", k->new ArrayList()).add(anInteger);

Básicamente, si el valor de «key1» existe, devuelve el valor List que corresponda. Si no existe, llama a lo de new ArrayList(), lo mete en el Map y lo devuelve

En cualquiera de los dos casos, devuelve la List. Ya solo nos queda llamar al método add() del List devuelto para meter el entero.

Una de las ventajas de tener en el IDE un analizador estático de código, estilo SonarLint y fijarse en las sugerencias que te hace.

Publicado en java | Etiquetado | Deja un comentario

Proguard con Maven y con Gradle

Una asignatura pendiente que tengo es ofuscar el código con Proguard. Dos «requisitos»

  • Que se haga automáticamente durante el proceso de desarrollo. Cada vez que compile y genere jars, que se generen los ofuscados.
  • Y con el requisito anterior, que mi entorno de desarrollo use los jar ofuscados directamente. De esta forma, sobre mi entorno de desarrollo, según desarrollo y pruebo, veo si el ofuscado es correcto y todo funciona sin problemas.

Así que me decido a hacer el experimiento tanto con Gradle como con Maven en mi IntelliJ IDEA.

Proguard con Maven

Tienes el resultado en Proguard con Maven. Me llevó un rato, pero sin demasiados problemas.

  • Poner el Plugin proguard-maven-plugin
  • Decir que se ejecute durante la fase package de Maven
  • Poner el Plugin build-helper-maven-plugin para indicar que el jar ofuscado generado, con apellido classifier small debe ser tratado como un segundo artifact del proyecto

Cree un proyecto maven padre con dos hijos, el de la librería que quiero ofuscar y del que he contado todo esto y luego un segundo hijo con un proyecto main que usa la librería ofuscada. Ahi puse la dependencia del jar ofuscado, básicamente, añadiendo <classifier>

IntelliJ me costó un poquito más configuarlo. Básicamente en la configuración de Run tuve que poner que ejecutara la tarea maven verify antes de la tarea build propia de IntelliJ. Eso me aseguraba que se construía el jar ofuscado cada vez.

Y todo bien. Toco los fuentes de mi código que se va a ofuscar, ejecuto desde IntelliJ el main y todo va bien. Se ejecuta Proguard y el main corre con el código ofuscado (lo veo en el log, por el nombre de las clases y métodos

09:32:41.606 [main] INFO com.chuidiang.examples.proguard_library.a.a – Toy sumando numeros

Fíjate que saca el log con el paquete »a», clase »a»

Proguard con Gradle

Esto si fue una odisea. Son las cosas de Gradle. Muy versátil, tanto, que cuesta hacerlo funcionar. Tienes el resultado en Gradle con Proguard.

Lo primero, que no me pasó con Maven, es que tras poner el plugin proguard-gradle y configurarlo, es que daba un error sin ninguna explicación. Poniendo la opción -i de gradle y verbose en la configuración de Proguard, ya si sale el error:

java.io.IOException: Please correct the above warnings first

y no hay más errores. La opción de gradle –stacktrace muestra la excepción pero tampoco da pista. Por un IOException esperaba que me diera el nombre de un fichero o qué IO le estaba dando el error.

Google y resulta que hay que poner en un libraryjars de Proguard las dependencias que hayas puesto en tu fichero build.gradle. Como no queremos ponerlas una a una, más google y encuentras un «gradle’s truco»

libraryjars configurations.findByName(‘runtimeClasspath’).getFiles()

Vale, ya soy capaz de crear el jar ofuscado con el comando gradle y lanzando la tarea de ofuscación que he creado. Ahora me queda hacer que se ejecute automáticamente al generar el jar. Sin mucho problema.

Ahora viene la parte de IntelliJ. El primer problema es la dependencia. Si en mi subproyecto de main pongo la depencia de esta forma

implementation project(‘:library’)

No soy capaz de decirle que use el jar ofuscado en vez de el normal. O al menos, no he encontrado la forma. Y si lo pongo como jar de terceros

implementation «com.chuidiang.proguard:library:${project.version}:small»

Pues gradle no lo encuentra. Para Gradle es neceario que este jar esté un un repositorio accesible (maven central, maven local, ….). Así que la solución pasa por poner el plugin maven-pulish , configurar los publishing y hacer que se publique ese jar en maven local al compilar.

Así que toca el lío de que la tareas proguard y publishMavenPublicationToMavenLocal se ejecuten en el orden adecuado de forma automática.

Con todo esto, al final quedó conseguido. En IntelliJ, en la configuración de Run, tuve que añadir la tarea previa a la ejecución de gradle jar. Esta tarea es estándar de gradle y tal cual hemos configurado en build.gradle es la que lanza todo lo demás (proguard y publish).

Resumiendo

Resumiendo, lo habitual. Tanto con Gradle como con Maven conseguí hacerlo. Con Maven me llevó una horita tenerlo todo montado y funcionando. Con Gradle un día entero.

Publicado en gradle, maven, proguard | Etiquetado , , | Deja un comentario

Java switch e instanceof Pattern Matching

Logo de Java

LLevo programando en Java lo menos desde el 2000. Recuerdo empezar con Java 1.2, que pasó a llamarse Java 2 y que tenía Swing como gran novedad, «reemplazando» AWT. Desde entonces y de forma más o menos contínua, en el curro programo en Java.

Todo esto hace que tenga ya mis formas de hacer las cosas, con las que me encuentro cómodo, y que no ande muy pendiente de las nuevas novedades que van saliendo en las nuevas versiones. Pero me voy tropezando con código que hace gente más joven o que encuentro por internet. Aunque algunas cosas me parecen alternativas dudosas, como el tema de lambdas y fluent apis, otras veo que claramente son útiles.

Y aunque son «antiguas» (Java 17), dejo aquí un par de mejoras en instanceof y en switch

Java Instanceof Pattern Matching

Algo frecuente de tener que hacer es comprobar el tipo de una objeto que te llega para convertirlo al tipo adecuado. En mi forma tradicional, hacía cosas como esta

if (value instanceof String) {
   String aString = (String)value;
   // Usamos aString como String en lo que necesitemos
}

Desde Java 17, ahora es posible hacerlo de una forma más directa

if (value instanceof String aString) {
   // Nos ahorramos el cast, ya tenemos aString como String
}

Java Switch Pattern Matching

Quizás no tan frecuente, pero relacionado con lo anterior. Si tenemos posibilidades de que el objeto que nos pasan sea de varios tipos posibles, antes había que hacer un if para casa caso. Ahora switch nos permite hacerlo más fácilmente.

switch (value) {
    case Integer anInteger :
        System.out.println("Es entero "+anInteger);
        break;
    case String aString:
        System.out.println("Es string "+aString);
        break;
    case Double aDouble :
        System.out.println("Es double "+aDouble);
        break;
    default:
        System.out.println("Ninguno de los anteriores");
}

Así que ya sé alguna nueva cosa útile que ahorra código y, lo más importante, el firme propósito de revisar las novedadees tanto de las versiones de java que no he revisado como de las nuevas.

Publicado en java | Etiquetado , , , | Deja un comentario

Record en Java 14

Logo de Java

No suelo mirar en cada versión de Java qué novedades hay. Más bien me suelo tropezar con ellas. Y la última con la que me he tropezado son los record, de java 14 en adelante

Qué es un record en Java. Un ejemplo de record

public record Registro(int a, String b) {
}

Esto crea una clase Java, de nombre Registro, con dos campos int a y String b. Pero esa clase tiene una serie de particularidades

Tiene un constructor con dos parámetros, a y b

Los campos a y b son inmutables, es decir, una vez construida la clase, no podemos cambiar su valor

Tiene getter, aunque no cumplen la nomenclatura estándar. Son registro.a() y registro.b(). Como son inmutables, no hay setter

Tiene una implementación por defecto de hashcode(), equals() y toString() que contemplan todos los campos (a y b)

Para qué sirve un record en Java

Pues tiene una utilidad muy relativa. Es una forma sencilla de construir una estructura de datos que no se pueden modificar

Así que sirve principalmente para pasar datos de un lado a otro, pero que no necesiten ser modificados, ya que es inmutable. Y para esos casos, es una forma sencilla de definir esa estructura de datos.

Constructores y métodos en un record en Java

Podemos dar lógica al constructor por defecto de record, que lleva dos parámetros o definir otros constructores

public record Registro(int a, String b) {
    public Registro(int a, String b){
        if (null==b){
            throw new IllegalArgumentException();
        }
        this.a=a;
        this.b=b;
    }
    public Registro (int a) {
        this(a,"");
    }
    public Registro(){
        this(0,"");
    }
}

Hemos añadido lógica al constructor con dos parámetros para que no admite un null como parámetro b. Hemos añadido un constructor con un solo parámetro y sin parámetros, que llaman al constructor con dos parámetros dando valores por defecto.

Podemos también añadir otros métodos. Lo único es que en esos métodos no podemos modificar los valores de a y b, ya que internamente están definidos con final.

Publicado en java | Etiquetado , , | Deja un comentario