|
| 1 | +r""" |
| 2 | + EJERCICIO: |
| 3 | + ¡Me voy de viaje al GitHub Universe 2024 de San Francisco! |
| 4 | +
|
| 5 | + Desarrolla un CLI (Command Line Interface) que permita |
| 6 | + interactuar con Git y GitHub de manera real desde terminal. |
| 7 | + |
| 8 | + El programa debe permitir las siguientes opciones: |
| 9 | + 1. Establecer el directorio de trabajo |
| 10 | + 2. Crear un nuevo repositorio |
| 11 | + 3. Crear una nueva rama |
| 12 | + 4. Cambiar de rama |
| 13 | + 5. Mostrar ficheros pendientes de hacer commit |
| 14 | + 6. Hacer commit (junto con un add de todos los ficheros) |
| 15 | + 7. Mostrar el historial de commits |
| 16 | + 8. Eliminar rama |
| 17 | + 9. Establecer repositorio remoto |
| 18 | + 10. Hacer pull |
| 19 | + 11. Hacer push |
| 20 | + 12. Salir |
| 21 | +
|
| 22 | + Puedes intentar controlar los diferentes errores. |
| 23 | +""" |
| 24 | +import git |
| 25 | +import os |
| 26 | + |
| 27 | + |
| 28 | +def check_path(repo_path: str) -> bool: |
| 29 | + return os.path.isdir(repo_path) |
| 30 | + |
| 31 | + |
| 32 | +def is_repo(repo_path: str) -> bool: |
| 33 | + return os.path.isdir(repo_path + "/.git") |
| 34 | + |
| 35 | + |
| 36 | +def menu() -> int: |
| 37 | + print("\nIngresar número de opción") |
| 38 | + print("\t1 - Ver ramas") |
| 39 | + print("\t2 - Cambiar rama") |
| 40 | + print("\t3 - Nueva rama") |
| 41 | + print("\t4 - Eliminar rama") |
| 42 | + print("\t5 - Ver status") |
| 43 | + print("\t6 - Commit") |
| 44 | + print("\t7 - Pull") |
| 45 | + print("\t8 - Push") |
| 46 | + print("\t9 - Log") |
| 47 | + print("\t0 - Salir\n") |
| 48 | + while True: |
| 49 | + opcion = input("\t\t==> ") |
| 50 | + if opcion.isnumeric() and int(opcion) in range(0, 10): |
| 51 | + return int(opcion) |
| 52 | + |
| 53 | + |
| 54 | +def branch_index() -> int: |
| 55 | + for ind, r in enumerate(repo.heads): |
| 56 | + if r == repo.active_branch: |
| 57 | + print(f"\t{ind} - * {r}") |
| 58 | + else: |
| 59 | + print(f"\t{ind} - {r}") |
| 60 | + while True: |
| 61 | + repo_ind = input("\nIngrese el número de rama: ") |
| 62 | + if repo_ind.isnumeric() and int(repo_ind) in range(0, repo.heads.__len__()): |
| 63 | + return int(repo_ind) |
| 64 | + |
| 65 | + |
| 66 | +def new_branch(): |
| 67 | + while True: |
| 68 | + branch_name = input("\nIngrese el nombre de la nueva rama (Enter para cancelar): ") |
| 69 | + if branch_name.lower() in [str(x).lower() for x in repo.heads]: |
| 70 | + print(f"La rama {branch_name} YA existe") |
| 71 | + else: |
| 72 | + break |
| 73 | + if branch_name: |
| 74 | + repo.create_head(branch_name) |
| 75 | + print(f"Rama {branch_name} creada") |
| 76 | + |
| 77 | + |
| 78 | +def delete_branch(): |
| 79 | + deleting_branch = repo.heads[branch_index()] |
| 80 | + if yes_no(f"Eliminar {deleting_branch}"): |
| 81 | + repo.delete_head(deleting_branch) |
| 82 | + print(f"Rama {deleting_branch} eliminada.") |
| 83 | + |
| 84 | + |
| 85 | +def change_branch() -> None: |
| 86 | + global repo |
| 87 | + |
| 88 | + branch_num = branch_index() |
| 89 | + if repo.heads[branch_num] != repo.active_branch: |
| 90 | + repo.heads[branch_num].checkout() |
| 91 | + print(f"Rama actual {repo.active_branch}") |
| 92 | + |
| 93 | + |
| 94 | +def show_branches() -> None: |
| 95 | + for r in repo.branches: |
| 96 | + if r == repo.active_branch: |
| 97 | + print("* " + str(r)) |
| 98 | + else: |
| 99 | + print(r) |
| 100 | + |
| 101 | + |
| 102 | +def show_status() -> None: |
| 103 | + untracked = repo.untracked_files |
| 104 | + news_ = repo.index.diff(repo.head.commit) |
| 105 | + modified = repo.index.diff(None) |
| 106 | + sep_ = "\n" |
| 107 | + |
| 108 | + if untracked: |
| 109 | + print(f"\nFicheros 'untracked'") |
| 110 | + for r in untracked: |
| 111 | + print(f"\t{str(r).split(sep_)[0]}") |
| 112 | + |
| 113 | + if news_: |
| 114 | + print(f"\nFicheros nuevos") |
| 115 | + for r in news_: |
| 116 | + print(f"\t{str(r).split(sep_)[0]}") |
| 117 | + |
| 118 | + if modified: |
| 119 | + print(f"\nFicheros modificados") |
| 120 | + for r in modified: |
| 121 | + print(f"\t{str(r).split(sep_)[0]}") |
| 122 | + |
| 123 | + |
| 124 | +def show_log() -> None: |
| 125 | + current_branch = repo.active_branch |
| 126 | + for x in list(repo.iter_commits(current_branch, max_count=20)): |
| 127 | + print(f"{x.committed_date} - {x.message} - {x}") |
| 128 | + |
| 129 | + |
| 130 | +def yes_no(question: str) -> bool: |
| 131 | + yes_no = input(question + " (Y/N) => ") |
| 132 | + return True if yes_no.lower().startswith("y") else False |
| 133 | + |
| 134 | + |
| 135 | +def new_directory(repo_path: str) -> bool: |
| 136 | + try: |
| 137 | + os.mkdir(repo_path) |
| 138 | + return new_repo(repo_path) |
| 139 | + except Exception as e: |
| 140 | + print(str(e)) |
| 141 | + return False |
| 142 | + |
| 143 | + |
| 144 | +def new_repo(repo_path: str) -> bool: |
| 145 | + try: |
| 146 | + _ = git.Repo.init(repo_path, bare=False) |
| 147 | + return True |
| 148 | + except Exception as e: |
| 149 | + print(str(e)) |
| 150 | + return False |
| 151 | + |
| 152 | + |
| 153 | +def set_repo() -> git.Repo: |
| 154 | + |
| 155 | + repo_path = input("Ingresar directorio: ") |
| 156 | + if check_path(repo_path): |
| 157 | + if not is_repo(repo_path): |
| 158 | + if yes_no("Crear nuevo repo?"): |
| 159 | + if new_repo(repo_path): |
| 160 | + print(f"Repositorio creado en {repo_path}") |
| 161 | + else: |
| 162 | + quit(3) |
| 163 | + else: |
| 164 | + quit(2) |
| 165 | + else: |
| 166 | + if yes_no("Crear Directorio y generar repo?"): |
| 167 | + new_directory(repo_path) |
| 168 | + else: |
| 169 | + quit(1) |
| 170 | + return git.Repo(repo_path) |
| 171 | + |
| 172 | + |
| 173 | +def add_commit() -> None: |
| 174 | + message = input("Ingrese detalle del commit: ") |
| 175 | + message = "Commit manual" if not message else message |
| 176 | + if repo.is_dirty(): |
| 177 | + untracked = repo.untracked_files |
| 178 | + news_ = repo.index.diff(repo.head.commit) |
| 179 | + modified = repo.index.diff(None) |
| 180 | + sep_ = "\n" |
| 181 | + |
| 182 | + files = [str(r).split(sep_)[0] for r in untracked] |
| 183 | + if files: |
| 184 | + repo.index.add(files) |
| 185 | + |
| 186 | + files = [str(r).split(sep_)[0] for r in news_] |
| 187 | + if files: |
| 188 | + repo.index.add(files) |
| 189 | + |
| 190 | + files = [str(r).split(sep_)[0] for r in modified] |
| 191 | + if files: |
| 192 | + repo.index.add(files) |
| 193 | + |
| 194 | + repo.index.commit(message) |
| 195 | + |
| 196 | + |
| 197 | +def pull() -> None: |
| 198 | + origin = repo.remote(name="origin") |
| 199 | + origin.pull() |
| 200 | + |
| 201 | + |
| 202 | +def push() -> None: |
| 203 | + origin = repo.remote(name="origin") |
| 204 | + origin.push() |
| 205 | + |
| 206 | + |
| 207 | +repo = set_repo() |
| 208 | +while True: |
| 209 | + option = menu() |
| 210 | + match option: |
| 211 | + case 0: |
| 212 | + # Salir |
| 213 | + break |
| 214 | + case 1: |
| 215 | + # Mostrar ramas |
| 216 | + show_branches() |
| 217 | + case 2: |
| 218 | + # Cambiar de rama |
| 219 | + change_branch() |
| 220 | + case 3: |
| 221 | + # Crear rama |
| 222 | + new_branch() |
| 223 | + case 4: |
| 224 | + delete_branch() |
| 225 | + case 5: |
| 226 | + # Ver status |
| 227 | + show_status() |
| 228 | + case 6: |
| 229 | + # Commit |
| 230 | + add_commit() |
| 231 | + case 7: |
| 232 | + # Pull |
| 233 | + pull() |
| 234 | + case 8: |
| 235 | + # Push |
| 236 | + push() |
| 237 | + case 9: |
| 238 | + # Ver commits history |
| 239 | + show_log() |
0 commit comments