Всегда приятно, когда удаётся сделать мир чуточку лучше.


Для одного проекта мне понадобился просмотрщик памяти в DOS. В идеале хотелось бы иметь редактор памяти, чтобы в нём был поиск по ключевым словам, перемещение к заданному адресу. Но для старта мне было бы достаточно хотя бы возможность просмотра всего мегабайта доступной «нижней» памяти.

Старожилы знают, что в комплекте с различными версиями ДОС шли дополнительные утилиты, и среди них была «замечательная» утилита debug, которая убога чуть более, чем полностью. Ещё во времена моей молодости эта утилита вызывала у меня самые противоречивые чувства, то сейчас и подавно. Пользоваться ей без успокоительных очень сложно, с другой стороны, хорошо, что она есть. Но мне возможностей и удобства этой утилиты не хватало, поэтому пришлось искать другой подходящий инструмент. После длительного гугления наткнулся на исходники утилиты RAM View.

К сожалению, исполняемого файла найти не удалось, только исходные коды под Borland C++ 3.1, и как впоследствии оказалось, сама программа содержала ошибки.

Всё это вылилось в интересный квест по поиску старого компилятора, исправления ошибок в программе 25-летней давности и создания запроса на слияние.

Компиляция


Поскольку утилита поставляется только в исходных кодах, то её необходимо собрать. Как я не пытался, найти исполняемого файла мне не удалось. Поэтому придётся пройти этот тернистый путь, благо хотя бы компиляторы все в доступе.

Для того чтобы собрать и проверить эту утилиту, мне понадобится программа DOSBOX. До того как запускать коробку с ДОС, создаю папку DOS в корне диска C, либо ~/DOS в любимом Linux.

Перехожу в эту папку и клонирую туда этот проект:

git clone https://github.com/aurelitec/ramview.git

Сборка старых программ – это определённый квест, доступный не каждому: старые компиляторы, у которых есть свои особенности работы с ними, описанные в книгах, сданных в макулатуру. Поскольку ранее с компилятором Borland C++ дел не имел, пришлось идти в ретрочатик t.me/retrocomps и просить помощи.

Согласно описанию в гите, мне нужен Borland C++ 3.1, который я скачал отсюда. Выбрал портабельную последнюю версию, чтобы не заниматься установкой, и распаковываю её в папку C:\DOS\BC\.

После чего запускаю DOSBOX и монтирую локальную папку C:\DOS, как диск C:\:



Если просто открыть проект в компиляторе BC и попробовать собрать, то вылезет ошибка, что библиотеки не найдены, и тут начинается главная магия. Идея в том, что надо понять в какую папку был установлен компилятор у автора, чтобы пути к библиотекам в его проекте совпали с реальным их расположением, но как это сделать? Всё достаточно просто, возможно вариант не самый элегантный, но рабочий.

Первое – это нужно получить Makefile из самого проекта, делается это командой PRJ2MAK (не забываем указать пути к исполняемому файлу):

PRJ2MAK RAMVIEW.PRJ



После этого у нас появится Makefile RAMVIEW.MAK, в котором уже можно посмотреть пути, где должен обитать компилятор.



Теперь осталось перенести компилятор в папку BORLANDC и должно произойти чудо.


Переименование папки

Запускаю компилятор, собираю и да, полный успех!



Казалось бы, вот оно счастье! Программа собралась, и можно пользоваться.

После всех неочевидных манипуляций запускаю программу и вижу такую картину:



Куча мусорных символов, которые разделяют шестнадцатеричные данные, и количество символов справа меньше 16 (вылезают за пределы экрана). Связанно это с тем, что отображаемый символ не один, а три, вот он и «выдавливает» текст за пределы строки.

Ну что ж, пришла пора исправлять чужие ошибки в старой программе.

Исправление ошибок в чужом коде


Проблема достаточно банальная, автор программы писал во времена DOS, в кодировке IBM CP866, а потом исходники видимо кочевали по разным дискам, вот и потерялись различные спецсимволы, которые затем были некорректно декодированы при заливке на github.

В этом можно убедиться, если открыть Notepad++ и посмотреть исходники, то достаточно быстро можно найти подобные места:




Некорректное отображение символов

Если в настройках сменить кодировку на CP866 (в моём случае это кириллица), то места станут намного более очевидными. А главное, они сразу будут узнаваемы с теми символами, что на скриншоте.




Проблемные места в кодировке CP866

Всё достаточно банально, нужно просто подобрать в ASCII-кодах подходящие символы и вставить их в нужные места. Первые две «сломанные» строки – это явно вертикальный символ, а нижний – это красивая деталь разделитель рамки.


Таблица ASCII – кириллица

Результатом стал выбор символа 0xB3 для вертикальной черты, и 0xBA для детали рамки.

cputs(" Version 0.1 \xb3 Copyright (c) 1997 Aurelitec");
…
cputs("\xb3 F10 = Exit");
…
strcat(buffer, " \xba");

Очень удобно редактировать код в Notepad++, а затем собирать в DOSBOX, новейшие технологии стучатся в наши двери (сарказм). После перекомпиляции получаем программу, которая выглядит и работает, так как надо:


Область памяти с ROM BIOS

Вот, теперь всё на своих местах. Есть разделители, четыре блока по четыре байта, итого 16 символов. Справа ровно 16 символов для текста, и ничего не теряется. Программа работает именно так, как нужно.

В целом можно добавить и другой функционал, например, поиск в памяти, который есть у того же debug, но мне пока лень.

Поскольку автор предоставляет нам как лицензионную, так и просто человеческую возможность вносить изменения в свою программу:
Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Этой возможностью мы и воспользуемся.

Оформление изменений в чужой проект


Это самая главная задача, из-за которой и была написана эта статья. Мне бы хотелось, чтобы читатели не боялись создавать запросы на слияния в чужие репозитории и исправляли чужие ошибки или писали дополнения. Плюс это весьма полезный опыт для новичков.

Если попробовать закоммитить полученные изменения в репозиторий, из которого мы склонировали, то ничего не выйдет. Для этого в github есть другой механизм, по-русски называемый запросом на слияние. В gitlab он называется merge request, в github именуется pull request. Так как мне довелось достаточно долго поработать с github и gitlab, то я больше люблю за удобство gitlab, он сделан людьми для людей. Но человек ко всему привыкает и даже к таким ужасным вещам, как неочевидный интерфейс github.

Первое, что потребуется сделать – это создать ответвление от родительского проекта (fork). Для этого нужно клацнуть по соответствующей кнопке в родительском репозитории.


Кнопка — создание форка репозитория


Клонирование репозитория к себе, в моём случае форк уже создан

После чего, у вас в профиле, появится такой же репозиторий с тем же именем и содержанием.
После этого необходимо в вашем локальном репозитории, который на вашем компьютере, переключить origin с адреса родительского репозитория, на ваш удалённый. Для этого нужно скопировать путь до вашего репозитория.



В локальном репозитории выполняем:

git remote set-url origin https://github.com/dlinyj/ramview.git

Далее можно добавить все изменённые файлы, сделать коммит и затолкать изменения в наш форкнутый репозиторий.

git add <files>
git commit -m "Fixed minor bugs with the display of symbols"
git push

И тут начинается самое интересное. Вообще, как я понимаю, можно сделать и локально запрос на слияние, но мне удобнее через web-интерфейс.

В веб-интерфейсе вашего удалённого репозитория нужно зайти «Pull requests» и там нажать кнопку «New pull request».



Там будет предложено выбрать удалённый репозиторий, в который нам нужно произвести слияние, по умолчанию там будет уже стоять родительский, и описание вашего запроса на слияние. В результате всех манипуляций в родительском репозитории появится запрос на слияние следующего вида:


Как выглядит запрос на слияние

К сожалению для меня, и к счастью для вас, владелец репозитория проигнорировал мой запрос, и вы можете его посмотреть по следующей ссылке.

На этом ваши приключения не заканчиваются, а только начинаются. Это самая интересная, может кому-то неприятная, часть заливки кода в чужеродный репозиторий.

После того как вы создали запрос на слияние, может быть несколько вариантов развития событий: игнорирование (как в этом случае), автор может отклонить ваш запрос, добавить его в основную ветку, или самый распространённый случай, написать вам замечания, которые вы должны будете исправлять.

Самое неприятное и распространённое – это исправление замечаний от владельца репозитория. Вы вносите эти изменения точно так же локально, но когда вы делаете коммит, то не создаёте новый, а добавляете изменения в старый коммит, так чтобы его hash не менялся.

Делается это следующей командой:

git commit --amend

Но git push у вас сделать не получится, потому что это вы будете вносить исправления в уже существующие коммиты на удалённом сервере. Поэтому надо его запихать туда принудительно, без предварительных ласк и грубой силой, используя опцию --force. Крайне не рекомендуется её использовать, но других вариантов я не знаю.

git push --force

Это обновит содержимое вашего репозитория, а также исправит содержание запроса на слияние у владельца.

У такого подхода есть недостаток, что стираются замечания, которые владелец вам оставил (это особенность github, у нормального gitlab таких проблем нет), так что рекомендую сохранять все замечания до финального коммита.

Выводы



Реальное применение исправленной программы

Достаточно часто встречаю от начинающих программистов вопрос: как мне набраться опыта в программировании? И всем им даю один и тот же ответ: идите на гитхаб и принимайте участие в каком-либо проекте. Правка чужого кода – это лучший способ научиться программировать. Поэтому настоятельно рекомендую не стесняться и делать такие правки, в худшем случае отклонят ваш запрос на слияние. В любом случае вы получите полезный опыт.

В моём случае это представляло скорее спортивный интерес, плюс мне нужна была рабочая версия программы, а сделать запрос на слияние для меня дело пяти минут.

Для тех, кому нужен исполняемый файл, вы можете его скачать у меня с диска.

Полезные ссылки:


  1. Оригинальный репозиторий.
  2. Моя исправления копия.
  3. Оформленный запрос на слияние.
  4. Готовая собранная программа.

P.S. Попробуем правила 6 рукопожатий, быть может, кто-то из вас знает владельца репозитория и попросит его посмотреть мой запрос на слияние?

Комментарии (40)


  1. sfrolov
    07.11.2022 12:57
    +4

    Да, рекомендую тот ретрочатик


    1. dlinyj Автор
      07.11.2022 13:59
      +3

      Продублирую ссылку, чтобы в тексте не искать. Тоже рекомендую, тепло и лампово.


  1. AlexanderS
    07.11.2022 13:51
    +7

    Отличная работа. Отдельное спасибо за раздел «Оформление изменений в чужой проект» — теперь эту статью можно как мануал другим показывать!


    1. dlinyj Автор
      07.11.2022 13:58
      +4

      Спасибо. В общем-то так и старался написать, чтобы была готовая мурзилка-мануал.


      1. 0xd34df00d
        07.11.2022 18:40
        +6

        Только у вас там что-то странное написано:


        но когда вы делаете коммит, то не создаёте новый, а добавляете изменения в старый коммит, так чтобы его hash не менялся.

        У вас это не получится сделать (с точностью до коллизий хэш-функции), потому что хэш зависит от содержания.


        И, если честно, мне как принимающему изменения не хотелось бы видеть amend'нутые коммиты после ревью — сложнее увидеть, что еще изменилось. Если мейнтейнер репозитория так уж не хочет иметь несколько коммитов после одного PR, то он может вместо мерджа squash'нуть.


        Короче, в этом случае аменд лучше не делать.


        1. dlinyj Автор
          07.11.2022 19:26
          +1

          Можете рассказать подробную инструкцию, как лучше делать. Не считаю себя знатоком гита, и поэтому мог что-то упустить.


          1. zloddey
            07.11.2022 22:27
            +7

            Сделать ещё один коммит с исправлениями поверх первого. А потом обычный пуш (без `--force`). Pull request привязывается к ветке, а не к коммиту. После этого в нём появится оба коммита, и будет возможно увидеть как суммарные изменения по обоим коммитам, так и сам отдельный фикс.


  1. screwer
    07.11.2022 14:06
    +2

    Ещё один вариант - автор не принимает ваш пуллоеквест, не просит сделать изменения, а по "образу и подобию" делает коммит от своего имени. Вот от такого поведения желание делать пуллреквесты быстро угасает.


    1. dlinyj Автор
      07.11.2022 14:22
      +9

      Да ладно, главное конечный результат — код правильный. А ЧСВ почесать это мы в другом месте успеем.


    1. motoroller95
      07.11.2022 15:39
      +2

      у меня был подобный случай. уже не помню в какой проект контрибьютил, но самое забавное что автор сам закомитил мои изменение, так еще и в коменты мне написал мол чел, у тебя там конфликты, пофиксай.


  1. BubaVV
    07.11.2022 14:24
    +5

    Лучше git push --force-with-lease .Это тоже форс, но он не позволит случайно переписать чужой коммит


  1. fk0
    07.11.2022 14:47
    +6

    Есть же qview (https://www.enlight.ru/qview/about.htm)... Зачем что-то собирать самому, кроме как за ради интереса?


    1. dlinyj Автор
      07.11.2022 14:51
      +2

      Видимо потому, что я гуглил, гуглил и не нашёл эту чудесную утилиту. Ну и чтобы был хороший пример :).

      Спасибо, гляну при случае.


  1. Pochemuk
    07.11.2022 14:49
    +2

    Хех! А ведь я когда-то тоже писал просмотрщик дампа памяти для DOS. На Ассемблере. Похожий, но красивше. И висел он в резидентах и его окно вызывалось по горячим клавишам. Даже из графического режима (при закрытии окна графический режим восстанавливался (разрешение, палитра, смещение окна отображения экрана). Было удобно для анализа игрушек - где какой счетчик хранится.

    Я даже нашел сорс и скомпилированный исполнимый коммандник. Но, к сожалению, запустить даже из под DOSBox не получилось - не реагирует на горячие клавиши. Видимо, в DOSBox нельзя перехватить клавиатурное прерывание 16H. Да и вообще нельзя подменять/перехватывать вызов обработчиков.


    1. dlinyj Автор
      07.11.2022 14:52

      Ну у меня задача именно на реальном железе. Тем более, что ДОС можно накатить на виртуальную машину.


      1. Pochemuk
        07.11.2022 16:21
        +1

        Могу выслать командник и сорс. Правда, не уверен, что именно это та версия, которая с графикой работает корректно. Вернее, работает только с EGA - VGA, когда это писалось, еще не были широко распространены.


        1. dlinyj Автор
          07.11.2022 16:28

          Не, спасибо, мне в сути для моих задачек хватит этой утилиты. Тут вот ещё одну посоветовал fk0.


    1. Mishootk
      07.11.2022 15:28
      +2

      В детстве баловались набором читеров: резидентом скидывали память в файлы, утилиткой и ручками искали счетчики, прописывали в конфиг, еще одним резидентом эти счетчики принудительно восстанавливались в нужное нам значение. Бесконечные патроны для игрушек - замечательно.


      1. dlinyj Автор
        07.11.2022 15:37

        Была же готовая программа для этих целей, не помню название.


        1. dimwap
          07.11.2022 16:11
          +3

          Artmoney?


          1. dlinyj Автор
            07.11.2022 16:15

            Да, именно она.


        1. Mishootk
          07.11.2022 16:49
          +1

          Видимо я про нее и вспомнил. Или про одну из семейства ей подобных. Там главное было быстро не умирать, тогда успевала обновить счетчик жизней.


  1. gwer
    07.11.2022 15:56
    +5

    Вы вносите эти изменения точно так же локально, но когда вы делаете коммит, то не создаёте новый, а добавляете изменения в старый коммит, так чтобы его hash не менялся.

    Это утверждение не совсем корректно. git commit --amend создаст новый коммит, и хэш у него будет новый.

    Упрощённо это можно называть изменением коммита, но если мы дошли до таких подробностей, как хэш, то уже нет — коммиты атомарны, менять их содержимое нельзя.

    В общем-то можно и без --amend, засквошить коммиты можно и непосредственно перед слиянием.

    Ну и пушить лучше через git push --force-with-lease — это безопаснее. Всё ещё не слишком хорошо, но в свою личную ветку можно.


    1. dlinyj Автор
      07.11.2022 15:59

      Да, вы совершенно правы. И я читателям статьи рекомендую обратить внимание на ваше замечание.


  1. misha98857
    07.11.2022 15:57
    +1

    Под рукой pull request-а на GitHub не оказалось, чтобы проверить. Разве у GitHub нет возможности squash-нуть все коммиты в MR в один merge коммит и не использовать хак с force push?


    1. dlinyj Автор
      07.11.2022 15:58

      Немного в другом проблема. Вы сделали мердж, уже всё готово. Все коммиты сквасились, но вдруг вам замечания прилетают. И вот чтобы исправленные замечания попали снова в этот же мердж, нужно сделать --force.


      1. SuperFly
        07.11.2022 17:38
        +9

        Пока ПР не смержен, просто продолжаете коммитить в свою ветку, без всяких амендов и форс-пуш. Когда владелец посчитает, что все замечания учтены и код готов к слиянию, он сможет выбрать как имено это сделать - сквошить ли все в один коммит или оставить все ваши коммиты как есть.
        по моему так


        1. dlinyj Автор
          07.11.2022 17:43

          Если ветка в едином репозитории, то да. Тут залив происходит из одной репы в другую.


          1. SuperFly
            07.11.2022 18:11
            +2

            Не возьмусь утверждать что это сейчас именно так, но насколько я помню, там что с форка, что с другой ветки - работает одинаково.
            последние несколько лет, работал с единым репозиторием. до этого год через форк.
            (А несколько месяцев назад сменил работу и тут гитлаб. Так вот у меня перманентная фрустрация: какого хера и для кого это сделано вообще? это как комент к вашему "gitlab, он сделан людьми для людей", так что дело привычки, надеюсь и я в какой-то момент привыкну к гитлабу)


      1. Mingun
        07.11.2022 18:02
        +4

        А зачем вы сами делаете мердж? Это обязанность того, кто принимать будет, а не ваша


    1. Mingun
      07.11.2022 18:00
      -1

      Есть конечно


      • простой мердж, со своим сообщением
      • squash всего, объединение всех сообщений коммитов в список
      • ребейз поверх цели и встраивание коммитов без мерджа (fast-forward)
      • обязательный мердж (даже когда можно сделать fast-forward)


    1. slonopotamus
      07.11.2022 20:47

      не использовать хак с force push?

      Почему вы называете это хаком?


  1. Mingun
    07.11.2022 17:53
    +2

    Если в настройках сменить кодировку на CP866 (в моём случае это кириллица),

    Эм… а что, есть другие варианты?


    У такого подхода есть недостаток, что стираются замечания, которые владелец вам оставил (это особенность github, у нормального gitlab таких проблем нет), так что рекомендую сохранять все замечания до финального коммита.

    Вы из какого века пишете? Ничего не стирается, на GitHub-е уже лет 5 как все ваши force-push-и ничего не забывают и даже кнопочка рядом с каждым есть, чтобы посмотреть различия force-push-нутого от того, что было раньше (любуйтесь: https://github.com/pegjs/pegjs/pull/399). Может только если вы зайдете в репозиторий и принудительно запустите в нем git gc, ну так кто ж в этом виноват...


    И что сложного в создании PR? После пуша там висит уведомление о том, что вы только что ветку протолкнули, хотите сделать PR? Не заметить просто нереально. Не говоря уж о том, что при пуше новой ветки git возвращает сообщение с url-ом для создания PR, только кликай и все (может в консольном и нет такого, никогда не пробовал, но что-то я сомневаюсь, что это самодеятельность TortoiseGit)
    Так что нечего дурить молодежи голову


    Правка чужого кода – это лучший способ научиться программировать.

    А вот это действительно очень и очень ценная мысль.


  1. m_shamhalov
    08.11.2022 11:39
    +1

    Скачал и запустил ваш исправленный бинарник и запустил во FreeDOS.


    1. dlinyj Автор
      08.11.2022 11:52
      +1

      Спасибо за замечание, лучше было в личном сообщении. Пожалуйста проверьте ещё раз, должно всё работать. Заменил на рабочий.


  1. BubaVV
    08.11.2022 19:27
    +1

    А вот вопрос из практики, пока нерешенный. Есть проект А, и его форк Б. Владелец Б делает пулл-реквест в А, который фиксит багу, которая лично мне очень мешает. Автор А этот пулл-реквест не принимает, а автор Б уходит в закат и не ребейзит его на свежий мастер А. Ребейз там несложный, но автоматом не поребейзит - А развивается довольно динамично. Я сдуру форкнул Б и поправил ПР, но теперь могу его отправить только в Б, а там никакой активности. Форкнуть А теперь не дает гитхаб, говорит уже есть форк.

    Внимание, вопрос: как мне нормально закоммитить рабочий ПР, желательно не снося свой форк репы? Ну и чтобы не обидеть автора Б, который собственно основной автор патча?


    1. dlinyj Автор
      09.11.2022 12:10

      Весь вечер думал над вашим вопросом, элегантного решения нет. Я бы сделал так, сделал форк от действющего A, сделал бы ребейз вашего форка от Б и смерджил его туда, а потом бы уже этот форк добавил бы в А. Но если у вас будет скваш коммитов, то работа того товарища потеряется.


      1. Mingun
        09.11.2022 17:42

        Я бы сделал так, сделал форк от действющего A

        Сказано же, что GitHub не даёт сделать форк, потому что уже есть форк от форка:


        Форкнуть А теперь не дает гитхаб, говорит уже есть форк.


        1. dlinyj Автор
          09.11.2022 19:17

          Ну в локальной репе-то можно делать всё что душе угодно, а там отребейзить всё локально и сделать всё по уму.


    1. Mingun
      09.11.2022 12:52
      +1

      Я вижу 2 варианта:


      • Можете написать в поддержку GitHub-а, чтобы ваш форк сделали самостоятельным репозиторием. Затем форкните заново
      • Можете слить себе все нужные ветки локально, удалить свой форк на гитхабе, сделать новый форк от правильного репозитория

      В конце обоих вариантов проталкиваете изменения из вашей локальной копии в новый форк. Авторство коммитов сохранится.