Там царь Кащей над златом чахнет, или как сохранить все старые репозитории в один включая историю git

15 ИЮНЯ 2022

------------------------------------------------------------------------

Наш телеграм-канал: t.me/@mailsgun_online

Youtube: youtube.com/@mailsgun_online

------------------------------------------------------------------------

Обновить

Когда-то пару лет назад на работе ко мне обратился коллега (привет!), который знает мою любовь к автоматизации с довольно нетривиальной просьбой. Нужно было почистить старые репозитории в корпоративной орге Github, но не совсем удалить, а сохранить на «всякий случай». Да и не просто сохранить, а сохранить с git историей. Мы с ним довольно быстро набросали скрипт на баше, который принимал аргументом orgName/projectName. После окончания работы скрипта он пушил код в отдельную ветку, и потом можно было это померджить в основную «ветку-хранилище». Скрипт был написан быстро, задачу решал, но все равно оставалось пара действий, которые надо было сделать «руками». Но в том случае это было нормально, так как требовалось подтверждение на архивирование старого проекта. Еще тогда у меня появилась идея сделать Github Actions workflow, с которым я только познакомился. Но свободного времени на это не было. И вот спустя год-полтора я наконец-то сделал задуманное — так появился на свет Bygone project, который доступен на github.

В 2017-2018 я учился в филиале французской школы 42 и мне приходилось много писать на Си. Было много учебных проектов, начиная от реверс-инжениринга стандартной библиотеки Си, printf, sha/md5/des алгоритмов, заканчивая своими собственными проектами, которые не имели отношения к Школе 42. Был простенький DNS proxy, сниффер сетевых пакетов, реверс-инжениринг игры Сапер. Сейчас я понимаю насколько ужасен тот код, но хочется его сохранить как память о бессонных ночах над сегфолтами, бас ероррами, потерянными указателями, утечках памяти. Два десятка репозиториев воспоминаний.

Мой github давно хотел чистки, и я все откладывал идею сделать полностью автоматический сборщик до более свободных дней. И вот у меня четвертый день кашель и второй день температура 37,5 — спать не получается — настало время доделать «хотелку». Репозиторий для проекта я создал несколько месяцев назад, но руки так и не доходили сделать.

Сбор требований

Скриншот моего архива в Github
Скриншот моего архива в Github
  1. Скрипт должен складывать каждую ветку проекта в отдельную папку

  2. Скрипт должен сохранять историю коммитов

  3. Скрипт должен быть устойчив к мердж конфликтам типа (rename/rename)

  4. Скрипт должен работать через Gihub Actions и запускать вручную

  5. Скрипт должен принимать переменными окружения репозиторий архива, архивируемый репозиторий, основную ветку архива в которую сливать все, а так же токен и имя пользователя github, чтобы запушить новое

  6. Скрипт должен работать без вмешательства через консоль (например с браузера телефона можно добавить в архив новый проект)

  7. Скрипт должен быть максимально простым, чтобы побудить людей поучаствовать в его доработке (<3 open source)

Реализация

Согласно списку требований я решил написать сам скрипт на баше, хотя я в нем и не силен. Логика довольно простая: получаем список веток, копируем содержимое проекта и переносим все в подпапку, коммитимся, повторяем для всех веток, потом добавляем новый удаленный репозиторий, и мерджим все ветки в архив. Но есть один нюанс, если мы переносим все в подпапку, а потом тоже самое делаем с соседней веткой — мы можем получить мердж конфликт типа (rename/rename). Поэтому пришлось добавить коммит в одном месте, который не сработает, если нет мердж конфликта (гит проигнорирует команду, так как нечего коммитить), но если был мердж конфликт (rename/rename) — проблему решит просто git add . && git commit.

По комментарию коммитов видно, что ветка master слилась без проблем, а на ветке fps был конфликт (rename/rename), который разрешился обычным  git add . && git commit.
По комментарию коммитов видно, что ветка master слилась без проблем, а на ветке fps был конфликт (rename/rename), который разрешился обычным git add . && git commit.

Код конфига для Gihub Actions:

name: archivist
on:
  workflow_dispatch:
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Archived Repository
        uses: actions/checkout@v2
        with:
          repository: ${{ secrets.ARCHIVED_REPOSITORY }}
          token: ${{ secrets.WRITE_GITHUB_TOKEN }}
          path: archived
          fetch-depth: 0
      - run: |
          cd ./archived
          git config user.name github-actions
          git config user.email github-actions@github.com
          existBranches=($(git branch -a | grep remotes | awk -F '/' '{ print $3  }'));
          for i in "${existBranches[@]}"; do
               git checkout -- .
               git checkout -b ${i}-move origin/${i} || true
               git reset --hard HEAD
               mkdir -p ../tmp/${{ secrets.ARCHIVED_REPOSITORY }}
               mv ./.git ../tmp/.git
               cd ..
               mv ./archived/ tmp/${{ secrets.ARCHIVED_REPOSITORY }}/${i}/
               cd ./tmp/
               git add .
               git commit -m "Preparing ${{ secrets.ARCHIVED_REPOSITORY }}/${i} for move"
               cd ../
               mv ./tmp/ ./archived/
               cd ./archived
               done;
          git config -l | grep 'http..*.extraheader' | cut -d= -f1 | xargs -L1 git config --unset-all
          git remote add archive "https://${{ secrets.GIT_USERNAME }}:${{ secrets.WRITE_GITHUB_TOKEN }}@github.com/${{ secrets.ARCHIVE_REPOSITORY }}"
          git fetch archive
          git checkout -b tmp_${{ secrets.ARCHIVE_REPOSITORY_DEFAULT_BRANCH }} archive/${{ secrets.ARCHIVE_REPOSITORY_DEFAULT_BRANCH }} || true
          for i in "${existBranches[@]}"; do
               git merge ${i}-move --allow-unrelated-histories --no-edit --no-ff > /dev/null 2>&1 || true
               git add .
               git commit -m "resolve conflicts for ${{ secrets.ARCHIVE_REPOSITORY }}/$i" || true
               git push --force --quiet "https://${{ secrets.GIT_USERNAME }}:${{ secrets.WRITE_GITHUB_TOKEN }}@github.com/${{ secrets.ARCHIVE_REPOSITORY }}" tmp_${{ secrets.ARCHIVE_REPOSITORY_DEFAULT_BRANCH }}:${{ secrets.ARCHIVE_REPOSITORY_DEFAULT_BRANCH }}
          done;
          >&2 echo "All done" ;

Итоги

Скрипт я написал за пару часов, и потом еще минут 40 потратил чтобы слить туда более 20 репозиториев (и наконец-то их удалить). Конечно хотелось бы сразу добавить список, но мне подошло и текущее решение. В любой момент можно добавить новый проект в архив.

Ссылка на проект

Ссылка на архив

Всего голосов 6: ↑6 и ↓0+6

Просмотры2.8K

  • 28 июля в 13:12
    Всего голосов 21: ↑19 и ↓2+17

    Просмотры3.3K

  • 5 марта 2020 в 11:00
    Всего голосов 20: ↑16 и ↓4+12

    Просмотры8.5K

  • 14 марта 2011 в 13:24
    Всего голосов 119: ↑109 и ↓10+99

    Просмотры6.1K

  • вчера в 10:02
    Всего голосов 146: ↑146 и ↓0+146

    Просмотры20K

  • вчера в 12:01
    Всего голосов 21: ↑21 и ↓0+21

    Просмотры2.6K

  • вчера в 18:51
    Всего голосов 18: ↑18 и ↓0+18

    Просмотры449

  • вчера в 07:19
    Всего голосов 18: ↑18 и ↓0+18

    Просмотры3K

  • вчера в 16:01
    Всего голосов 16: ↑16 и ↓0+16

    Просмотры2.1K

Source: habr1

Ну и напоследок напоминаю вам о нашем новом проекте vkserfing bot.