16 октября 2015 в 15:43

Достаточно Git-а, чтобы быть (менее) опасным из песочницы

imageТы просто-напросто ненавидишь Git? Ты абсолютно счастлив с Mercurial (или, фу, с Subversion), но раз в месяц тебе приходится отважно сталкиваться с Git, потому что каждый, даже его чертова собака, теперь использует GitHub? Тебя терзают смутные подозрения, что половина всех команд Git на самом деле удалят всю твою работу навсегда, но ты не знаешь какие именно и не хочешь проводить три недели, углубляясь в документацию?

Хорошие новости! Я написал тебе этот изумительный Интернет-пост. Я надеюсь, что смогу размазать достаточно Git-а по твоему лицу, чтобы понизить вероятность сделать что-то непоправимое, а так же уменьшить твой страх что-то сломать. Этого должно быть также достаточно, чтобы сделать документацию Git немного более понятной; она крайне тщательно и глубоко проработана и очень глупо, если ты все еще не прочитал половину.

Я постараюсь излагать коротко, но также, чтобы это было потенциально полезно тем людям, кто вообще никогда не сталкивался с контролем версий, поэтому повсюду будет разбросан 101 совет. Не бойся! Я не думаю, что пользователи Mercurial понятия не имеют, что такое патч.

Что здесь вообще происходит?


Для начала будет полезно понять дизайн Git, или по крайней мере о чем думал его автор, создавая его.

Git — это DVCS, или «распределенная система контроля версий», где «система контроля версий» означает «запоминает историю твоих файлов», а «распределенная» означает «ты можешь делать это оффлайн». (Раньше фиксирование изменений вызывало немедленную загрузку твоих изменений на центральный сервер. Ты не мог сохранить код, если не мог подключиться к серверу. Верно?)

Git был изобретен Линусом Торвальдсом, злобным мужиком, который также принес нам ядро Linux. Linux — это огромный проект с очень длинной историей и он перерос VCS, которую использовал, поэтому Линус решил написать новую, т.к. он программист, а именно этим мы и занимаемся.

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

Итак, у Линуса есть «каноничная» копия кодовой базы, которую ты можешь называть «репозиторий», потому что это место для хранения разных вещей. Время от времени ты можешь скачивать свежую копию и приступать к написанию каких-нибудь сломанных Wi-Fi-драйверов или еще чего-нибудь, и это уже будет отличаться от того, с чего ты начал. Поэтому ты генерируешь патч с изменениями и отправляешь его в почтовую рассылку, а кто-то говорит «по-моему неплохо» и Линус применяет этот патч к его копии кодовой базы. Теперь каждый, кто собирается работать с этим кодом, увидит там и твою работу тоже.

Великий секрет к пониманию Git, который, я надеюсь, заставит широко раскрыться твои глаза и прозвучать «ааа» из твоего рта, заключается в следующем:

Git — это просто набор инструментов для рассылки патчей по почте.

Нет, серьезно. Есть всего где-то пять команд внутри поставки Git для этих определенных целей. Есть даже подразделы в документации: am, apply, format-patch, send-email, request-pull. Ты можешь прямо сейчас пойти в почтовую рассылку ядра Linux и увидеть, что до сих пор все так и делается, Git просто делает большую часть скучной работы. Даже man-страница Git описывает Git, как «тупой трекер содержимого».

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

Давай будем рассматривать модель Git, держа это в голове.

Коммиты


Коммит — это патч. Все. Он перечисляет некоторые изменения в некоторых файлах в формате «единого diff-а».

В нем также есть некоторые заголовки, которые подозрительно напоминают почтовые. Там есть автор, отметка времени и прочее.

Здесь и происходит вся магия. Запомни, патч выражает различия между двумя наборами файлов. (Давай назовем их «деревьями» — по аналогии с деревьями каталогов.) Итак, если ты отправишь мне патч по почте, я не многое смогу с ним сделать, пока мы не согласуем к чему я должен применить патч. Может будет полезно указать, скажем, «примени этот патч к ядру Linux». Может будет даже более полезно указать «примени этот патч к релизу 3.0.5 ядра Linux».

Git-коммит кодирует это в заголовке «parent», указывая, поверх какого коммита его нужно применить.

Эй, подожди-ка, ведь коммит — это всего лишь патч. Как применить патч к другому патчу? Ты можешь применить патч только к полному набору файлов (дереву). Но после этого ты получаешь новый набор файлов (дерево). Поэтому «коммит» также означает «состояние репозитория после применения этого патча».

Но небольшая рекурсивная проблема все еще остается. Если у тебя есть коммит C и он говорит, что его родитель — B… что ж, ты не знаешь как выглядит состояние репозитория в B, пока не применишь его, и поэтому тебе нужно смотреть на его родителя, верно?

Верно. История Git — это очень длинная цепочка инструкций для пересоздания кодовой базы с нуля, шаг за шагом. Представь это как стопку патчей, как ящик с входящими на твоем письменном столе. Некая простая история может выглядеть так:

  • Коммит C: Мой родитель — B. Добавь «three» в конец файла «numbers.txt».
  • Коммит B: Мой родитель — A. Добавь «two» в конец файла «numbers.txt».
  • Коммит A: Создай файл «numbers.txt», содержащий «one».

Коммит A здесь особенный, у него нет родителя. Это означает, что его патч может только создавать новые файлы — нет никаких существующих файлов для изменения! В остальном, это такой же коммит, как и любой другой.

Итак, ты начинаешь с чистого листа. Затем ты применяешь патч A, который дает тебе one. Затем ты можешь применить патч B, который дает тебе one two. И наконец, ты можешь применить патч C, который дает тебе one two three — состояние кодовой базы для коммита C. (Git не проделывает это буквально каждый раз, конечно; там достаточно хитрое кэширование и всякое-такое. Но модель действует достаточно схожим образом.)

Документация Git стремится изображать историю слева направо, поэтому описанное выше выглядело бы так:

A---B---C

Та же идея, только написанная по-другому. Имеет немного больше смысла, если ты представишь стрелки: A → B → C.

В реальности коммиты обозначаются не буквами, а хэшами, которые выглядят как 8edc525cd8dc9e81b5fbeb297c44dd513ba5518e, но обычно сокращаются до 8edc52. Ты можешь подумать, что они называются «хэшами», потому что это длинные шестнадцатеричные строки, которые выглядят как хэши SHA-1. В общем, да, но также они буквально являются SHA-1-хэшами патча, включая заголовки. (И т.к. родитель — это один из заголовков, то хэш включает хэш родителя, который включает хэш его родителя и т.д. Это длинная цепочка хэшей до самого начала. Прямо как в Bitcoin!)

Замечательное свойство такого хэширования в том, что отдельный коммит не может быть изменен. Ты не можешь просто вернуться назад и по тихому вбить строчку в патч A, потому что это изменит его хэш и B не будет указывать на измененный A. Если ты захочешь обновить родителя B, то это изменит его хэш и он потеряется. Как только у тебя будет хэш коммита, можешь быть абсолютно уверен, что его история неизменна.

Деревья


Я протащил этот термин в секцию выше, потому что это фактически самая важная вещь в Git: дерево директорий, содержащее некоторый ассортимент файлов. Каждый коммит имеет ассоциированное дерево, которое отражает состояние репозитория для этого коммита. Деревья тоже обозначаются хэшами.

Тебе очень редко придется беспокоиться о деревьях — после многих лет использования Git я думал о деревьях примерно дважды. Это всего лишь деталь реализации. Я заметил их только потому, что документация обратила на них мое внимание, и это приятно, когда ты понимаешь о чем, черт возьми, тебе рассказывает документация. Они появляются в двух (практических) местах документации.

  1. Некоторые команды описаны, как принимающие «древовидный» аргумент, например, использование git checkout для работы с отдельными файлами. Это всего лишь означает «что-то, из чего Git может извлечь дерево». Т.к. у каждого коммита есть дерево, ты можешь просто использовать коммит в качестве аргумента.
  2. Существует множество ссылок на «рабочее дерево». Это просто дерево, в котором ты работаешь, т.е. актуальная копия кодовой базы, которая расположена у тебя на винте.

И это все, что тебе нужно знать о деревьях!

Ветки


Если ты использовал Mercurial, забудь о ветках Mercurial. Я не знаю как они работают, но пользователи Mercurial рассказывали мне, что это такая боль в заднице, что никто на самом деле их больше не использует.

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

Что ж, нет проблем. Когда они впервые скачивают код, они могут привязать его к директории под названием master (потому что это мастер-копия). Затем, когда они приступают к работе над своим драйвером, они могут все это полностью скопировать в директорию под названием ужасный-драйвер-broadcom. Чтобы сгенерировать патч, им нужно просто получить разницу между этими двумя директориями.

Это и есть ветки Git в двух словах.

Заметь, что при таком подходе, никто не знает и никого не волнуют имена твоих веток. В конце концов, ты не отправляешь кому-то другому целую директорию; ты просто отправляешь им некоторые патчи. Патчи не содержат имена веток; они только знают своих родителей.

Более технически, ветка — это только имя, которое указывает на какой-то коммит. (Буквально, ничего более. Ветка foo — это 41-байтный текстовый файл, содержащий хэш коммита.) Однако, ветка имеет особое свойство, при котором, если ты делаешь новый коммит, пока находишься в данной ветке, имя ветки начнет указывать на этот новый коммит. Еще раз, это работает как учебный пример: если ты делаешь какую-то работу или применяешь патч в своей директории ужасный-драйвер-broadcom, очевидно, что новое содержимое директории будет отражать новые изменения.

Вот почему о Git говорят, что у него «дешевое локальное ветвление». Оно дешевое, потому что ветка — это не более, чем имя; оно локальное, потому что тебя не заставляют синхронизировать имена твоих веток с кем-то еще.

Ветки добавляют новую возможность в нашу модель: теперь истории не обязательно быть линейной. Два разных патча могут иметь одного и того же родителя! Разумеется, тебе в действительности не нужны для этого ветки — если два человека работают с ядром Linux и оба делают изменения, они оба производят патчи с одинаковым родителем. Говоря о котором…

Удаленные репозитории


«Удаленный» означает «где-то еще», поэтому естественно, что удаленный репозиторий Git — это просто репозиторий, который находится где-то еще. Обычно это центральный сервер, но не обязательно; можно вообще работать без центрального сервера, когда разные разработчики просто перечисляют друг-друга в качестве удаленных репозиториев.

Когда ты впервые клонируешь репозиторий, место, из которого ты склонировал, обозначается как удаленный репозиторий под названием «origin», потому что это оригинал твоего кода. Ничоси.

Ты также получишь все ветки оригинала. Ну. Вроде того. Имена веток — локальны, запомни. Если у твоего оригинала есть ветка с именем foo, Git создаст для тебя ветку с именем origin/foo (называемую «удаленно-отслеживаемой» веткой). А т.к. ты ее не создавал, то по-умолчанию она не отображается в git branch.

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

Слияние


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

Так что, теперь у тебя: A → B → C

А у Линуса: A → B → D → E → F

Или, если рисовать в духе документации Git, где время течет слева направо:

      C            ужасный-драйвер-broadcom
     /
A---B---D---E---F  origin/master

Но, эй, нет проблем. Ядро Linux — это огромный проект, поэтому есть шансы, что ни один из этих патчей не касался тех же файлов, что и ты.

Если бы мы рассылали патчи по почте, мы бы могли просто сказать: плевать, просто примени C поверх F, даже если он говорит, что находится поверх B. Но в модели Git коммит обозначается своим хэшем, который включает его родителя. Изменение родителя потребует создание нового, отличающегося коммита, с другим хэшем.

Вместо этого, Git может просто слить эти два разных отображения истории вместе, создав новый коммит с двумя родителями: C и F.

      C-----------.    ужасный-драйвер-broadcom
     /             \
A---B---D---E---F---G  origin/master

Если никакие изменения с любой стороны не противоречат друг-другу, то это «простое» слияние. Т.к. ничего нового на самом деле не изменилось, то патч в G — пустой; он присутствует только для склейки C и F вместе.

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

Тэги


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

Также, тэги (чаще всего) глобальны, не ограничены пространством имен, как ветки. Наконец, они не меняются, поэтому обычно подразумевается, что каждому следует согласиться с тем, на что они указывают.



Понятненько. Это круто. Но как мне сделать хоть что-то?


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

Добудь немного кода


git clone github.com/funny_guy/just_for_lulz_code вывалит смешной код этого весельчака в новую директорию just_for_lulz_code.

Когда ты захочешь обновить его, ты можешь вызвать команду git pull origin master, которая получит все изменения и попытается слить их в твою текущую ветку. Если ты ничего не менял, то твое рабочее состояние просто перейдет в актуальное.

Если у тебя устаревшее рабочее состояние репозитория и ты не помнишь делал ли ты что-либо, ты можешь выполнить команду git pull --ff-only origin master, которая сделает что-либо только в том случае, если обновление будет «прямой перемоткой». Это всего лишь означает, что твоя сторона не делала никаких коммитов и никаких слияний не требуется. Другими словами, если у тебя состояние репозитория A, а у оригинала A → B → C, то это будет прямой перемоткой, потому что Git необходимо просто нарастить еще больше коммитов прямо поверх тех, что у тебя уже есть.

Посмотри содержимое


git log покажет тебе лог. Формат немного многословен и не очень подходит для беглого просмотра истории.

git log --oneline --graph --decorate намного приятнее для просмотра. Ты также можешь установить tig, который делает в основном то же самое, но ты сможешь использовать Enter на коммите, чтобы увидеть различия на месте.

git log --follow показывает тебе лог изменений, которые коснулись только конкретного файла (или директории). --follow означает - отслеживать историю файла, включая переименования, но это работает только для одного файла.

git show показывает тебе патч, внесенный коммитом. git show : показывает тебе состояние файла для конкретного коммита.


Просто используй эту чертову штуку, чтобы сделать этот чертов патч для этого чертова проекта


git status
рассказывает тебе о текущем состоянии твоей кодовой базы: в какой ветке ты находишься, какие изменения ты сделал и т.д.

git branch создает новую ветку, основанную на коммите, в котором ты работаешь, но не переключается на нее. Вместо этого тебе может понадобиться команда наподобие git checkout -b origin/master, которая создает новую ветку, основанную на origin/master, а также переключается на нее.

git checkout устанавливает текущую ветку и переключается в соответствующее состояние кодовой базы. Ты также можешь перейти в удаленную ветку, в тэг или в конкретный коммит, но текущая ветка будет покинута и ты будешь получать предупреждения о наличии "оторванной HEAD". Это буквально означает, что HEAD (специальное имя, которое всегда указывает на то, с чем ты работаешь) не указывает на ветку, и если ты делаешь новые коммиты, у них не будет ничего, указывающее на них и они могут леко потеряться.

git add говорит Git о новых файлах, созданных тобой, которые нужны тебе в следующем коммите.

git rm говорит Git, что ты собираешься удалить файл, а так же удаляет его физически. (Это всегда обратимо. Git отклонит операцию, если файл был изменен. Также ты можешь просто удалить файл командой rm, а git commit -a зафиксирует это.)

git mv говорит Git, что ты переименовываешь файл. (Заметь, что Git в действительности не хранит переименования; он догадывается на лету, был ли файл переименован.)

git commit -a откроет текстовый редактор, для запроса описания коммита, затем создаст коммит из всех сделанных изменений всех файлов, известных Git.

Кое-что в модели Git я еще не затронул: там есть одна вещь, называемая "index", или "staging area", или иногда "cache". (Я не знаю зачем ей нужно столько имен.) Это те изменения, которые ты собираешься зафиксировать. Когда ты используешь git add и компанию, любые изменения файла (или все содержимое целиком, если это новый файл) формируются и отображаются в своих собственных секциях в git status. Несформированные изменения перечисляются под ними. Если ты используешь простой git commit без -a, то только сформированные изменения станут частью коммита. Иногда это бывает довольно полезно, потому что позволяет тебе проводить кучу исследовательской работы, а затем упаковывать ее в различные коммиты для будущих археологов. (Если у тебя разыгралось воображение, то рассмотри git add -p.) Но ты можешь просто использовать git commit -a, когда захочешь. Черт, да тебе даже не нужно git add; ты можешь просто передавать список файлов в git commit.

Понятненько. Теперь, как мне работать где-угодно?


Через отправку изменений, что лишь означает выталкивание одной или более веток на конкретный удаленный репозиторий. Git позволит тебе сделать отправку изменений только с перемоткой вперед - ты даже не можешь произвести автоматическое слияние вместе с отправкой изменений. Если ты пытаешься отправить изменения и получаешь жалобу о "не перематывающей вперед" отправке изменений, тебе сначала необходимо просто вытянуть изменения, а потом попытаться опять. (Но если ты используешь GitHub и пулл реквесты, когда отправляешь изменения в личную ветку, то есть шансы, что GitHub произведет для тебя простые слияния.)

git push отправит твою ветку в ветку с тем же именем в удаленном репозитории. Если ты используешь форк с GitHub, тогда у тебя вероятно есть единственный удаленный репозиторий под названием "origin", который и является твоим форком, и ты, вероятно, просто работаешь в master ветке. Тогда ты можешь сделать git push origin master и все будет в порядке.

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

Конфликты слияния


Если ты делаешь слияние, или отправку изменений, или (упаси Господи) перемещение, возможно твои изменения будут конфликтовать с чьими-то чужими. Git остановит слияние с сообщением "Автоматическое слияние не удалось; ошибка бла-бла-бла". Если ты посмотришь в git status, ты увидишь новую секцию для конфликтующих фалов. Тебе необходимо это исправить для завершения слияния или выполнения множества других реальных задач.

Открой конфликтующий файл и ты увидишь что-то вроде этого:

<<<<<<< HEAD
что-то, что ты изменил
=======
что-то, что изменил кто-то другой
>>>>>>> origin/master

(Стиль отображения конфликтов diff3 может немного улучшить ситуацию; смотри секцию настроек ниже.)

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

Если тебе повезло, то "конфликт" - это просто исправление каким-то засранцем ошибок расстановки пробельных символов, или вы оба добавляете секцию импорта в том же самом месте, или какие-то другие простые вещи. Отредактируй файл как тебе нужно и выполни git add, чтобы сообщить Git, что он готов к отправке. Как только все конфликты исправлены и все файлы добавлены через git add, сделай простой git commit, чтобы завершить слияние.

Если тебе не повезло, то кто-то провел большой рефакторинг, пока ты исправлял маленький баг, и теперь конфликтует весь файл, а ты окончательно попал. Ты можешь выполнить git merge --abort, чтобы отменить слияние, создать новую ветку, основанную на текущей ветке master, и повторить свои изменения вручную.

Несколько примечаний:

  • Дважды проверяй, что ты действительно исправил все конфликты. Git НЕ БУДЕТ препятствовать тебе фиксировать отметки о конфликте!
  • Иногда, конфликт - это когда одна сторона отредактировала файл, а другая сторона удалила этот файл. Когда это происходит, Git расскажет тебе кто произвел удаление. Я чаще всего сталкиваюсь с этим, когда использую автоматическое форматирование, или рефакторинг, или еще что-то, в этом случае мне на самом деле плевать на файл, который был удален; если это тот случай, ты можешь просто удалить его через git rm.
  • Есть полу-интерактивная команда git mergetool, которую ты можешь использовать в ходе конфликта, и которая откроет твою программу разрешения слияний для каждого конфликтующего файла. В моем случае это vimdiff, использование которой у меня никогда не входило в привычку, поэтому я не использую ее слишком часто. В твоем случае это может отличаться.


Боже-божечки мои! Что я наделал?!


Ты скопипастил вызов git какого-то придурка на Stack Overflow и теперь все сломано. Не паникуй! И тем более не копипасть проверенное решение от какого-то другого придурка.

Если твоя рабочая копия или индекс окончательно навернулись, ты можешь использовать команду git reset --hard, чтобы отменить все свои незафиксированные изменения. Но не используй ее необдуманно, поскольку это, естественно, деструктивная операция.

Если ты делал какую-то интерактивную многоступенчатую вещь, вроде git rebase или git cherry-pick и все пошло ужасно неверно, git status укажет тебе на это, а, например, git rebase --abort гарантированно вернет тебя туда, откуда ты начал.

Если ты думаешь, что каким-то образом потерял коммиты, ты можешь найти их в git reflog.

В самом худшем случае ты можешь вытащить свои наработки в виде патчей с помощью git show и начать заново со свежим клоном.

И еще, немного синтаксического барахла


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

HEAD - это что-то вроде специального имени ветки, которое просто ссылается на то, с чем ты работаешь прямо сейчас.

Есть целая куча синтаксисов для указания коммитов и диапазонов коммитов. Ты можешь просмотреть man gitrevisions на досуге. Наиболее полезные это:

  • foo^ - это (первый) родитель foo. Чаще всего используется как HEAD^. Заметь, что ^ - это специальный символ во многих оболочках и может понадобиться экранирование.
  • foo..bar - это диапазон и обозначает все, что после foo, вплоть до bar включительно.

Есть еще больше в man gitrevisions, но 80% из этого я никогда не использовал, если честно.

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


Полезные настройки


У меня немного в моем .gitconfig, но там есть несколько моих любимых вещей, может тебе они тоже понравятся. Если ты очень активно используешь Git, то может быть полезным пролистать man git-config, какой-нибудь из множества представленных вариантов его настройки может относиться к твоей проблеме.

Ты можешь запросить свою конфигурацию Git с помощью git config foo.bar.baz. Ты также можешь редактировать ее с помощью git config --global foo.bar.baz value, где параметр --global изменит твой ~/.gitconfig файл (который применяется к любому репозиторию, с которым ты работаешь), а его пропуск изменит .git/config (который применяется только к текущему репозиторию).

Или ты можешь крякнуть ~/.gitconfig, открыв его в текстовом редакторе, потому что это чертов INI-файл, в общем, не бином Ньютона. Давай представим, что делаем это вместо команд.

Прежде, чем ты сделаешь ЧТО-ЛИБО, настрой свои имя и почту


Как мы знаем, каждый коммит Git содержит имя и почту, прикрепленные к нему, потому что Git был разработан людьми, которые буквально не могут представить себе никакой рабочий процесс, не сосредоточенный на почте. (Да, это означает, что адрес твоей почты на GitHub фактически публичен, даже если он явно не показан на веб-сайте.)

Если ты не укажешь Git свое имя, то ему придется гадать, а гадает он плохо. Он возьмет твое имя из поля "настоящее имя" в /etc/passwd (что может быть верным), а твою почту он возьмет из твоего логина плюс имени хоста твоего компьютера (что, конечно, полная бессмыслица, если только ты не на университетском сервере и это не 1983 год). И ты не сможешь исправить их задним числом, потому что они являются частью коммитов, а коммиты - неизменны.

Поэтому первые три строчки твоего .gitconfig должны исправить эту проблему:

[user]
    name = Eevee (Alex Munroe)
    email = eevee.git@veekun.com

Легкотня.

Стандартные цвета - это мусор, вселяющий ужас


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

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

Что меня встревожило и показалось ужасающим. Пожалуйста, просто доверься мне, когда я говорю, что тебе абсолютно необходимо тупо вставить этот блок определения цветов в свой .gitconfig.

[color "branch"]
    current = yellow reverse
    local = yellow
    remote = green
[color "diff"]
    meta = yellow bold
    frag = magenta bold
    old = red bold
    new = green bold
[color "status"]
    added = yellow
    changed = green
    untracked = cyan

Стиль отображения конфликтов


Единственная действительно стоящая вещь в моем .gitconfig вот эта:

[merge]
    conflictstyle = diff3

Обычно, конфликт слияния выглядит так:

<<<<<<< HEAD
то, на что ты поменял
=======
то, на что они поменяли
>>>>>>> master

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

Введи diff3, который меняет отображение конфликтов слияний так:

<<<<<<< HEAD
то, на что ты поменял
|||||||
то, что было изначально
=======
то, на что они поменяли
>>>>>>> master

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


Некоторые допущения, которые ты можешь, но не должен допускать


Git - это не дружелюбный инструмент управления проектом. Git - это тупой трекер содержимого.

Скорее, Git - это странная файловая система и у нее есть набор инструментов, типа rm и ls. Чем на более низкий уровень ты спускаешься, тем меньше Git будет предполагать о том, что ты пытаешься сделать, и тем меньше будет пытаться тебя остановить от проделывания чего-то странного. Если ты почерпнул только одну вещь из этой статьи, пусть это будет следующее: Git был спроектирован для тех людей, которые уже поняли его на 100% - для людей, которые его написали. В этом плане сейчас становится лучше, но это причина множества его острых углов.

Ради наставления тебя на путь истинный, вот несколько допущений, которые ты уже мог, но не должен был делать:

  • Коммит не обязан иметь одного родителя. У него их может быть двое (если это слияние). Или трое, или больше (если это "осьминожное" слияние). Или ноль (если это первоначальный коммит).
  • У тебя может быть удаленный репозиторий, у которого ноль общих коммитов с твоим репозиторием. Нет ничего строго предписывающего двум репозиториям содержать "одинаковую" кодовую базу или заставляющего их никогда не взаимодействовать. Просто это обычно не так полезно. (Один возможный способ использования: я слил два проекта в один репозиторий без потери какой-либо истории, через добавление одного, как удаленного репозитория другого и просто слияния их историй вместе.)
  • Похожим образом у тебя может быть две ветки в том же самом репозитории, у которых ноль общих коммитов. (Что означает, что у тебя может быть более одного первоначального коммита!) Это то, как GitHub хранит "страницы GitHub": они находятся на отдельной ветке gh-pages внутри твоего репозитория, ведя совершенно независимую историю.
  • Коммиты не знают на какой ветке они были созданы. Ветка указывает на отдельный коммит; коммит никогда не указывает на ветку. Хотя, в большинстве практических случаев ты можешь достаточно верно это предположить.
  • Git отслеживает файлы, а не директории. Ты не можешь хранить пустую директорию в Git. Обычной практикой является хранения файла нулевого размера с имененм .keep или что-то еще в директории и фиксирование этого файла.
  • Документация не обязательно перечисляет опции, или формы команд, или огромное множество всего остального в порядке полезности. Например, наиболее фундаментальная команда это, вероятно, git commit, а третья опция в документации - это -C, выполняющая некую странную форму слияния, которую я сомневаюсь, что когда-либо использовал. Опция -m, которая позволяет тебе создавать описание коммита, появляется лишь на шестнадцатом месте.




Револьвер в сапоге, на всякий случай


Git - это, в основном, странная файловая система, а команды Git, по сути, странные команды файловой системы. Прямо как ls и rm одинаково непрозрачны, если ты еще не знаешь, что они делают, так и команды Git не обладают очевидными признаками того, что они опасны или нет.

Поэтому, вот несколько опасных вещей и то, как их безопасно использовать, или, по крайней мере, как их использовать с наименьшим риском.

git rm


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

git checkout


git checkout переключает ветки, но на более фундаментальном уровне то, что она делает - это вытаскивает файлы. Ты можешь использовать ее как git checkout [commit] -- <files...>, чтобы вытащить некоторые файлы конкретного коммита. По-умолчанию это относится к твоей текущей ветке, поэтому способом отменить изменения, которые ты сделал в файле (но еще не зафиксировал), является git checkout -- .

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

Ты можешь захотеть передать опцию -p
, которая интерактивно покажет тебе откат каждой отдельной части каждого файла. (Различные команды принимают опцию -p, включая git add, которая дает возможность делать различные изменения в отдельный файл и фиксировать только некоторые из них. Довольно удобно.)

git reset


"Reset" - это странная команда. Обычно она регулирует состояние твоей текущей ветки, индекса и твоего рабочего дерева.

Опасная часть это git reset --hard <files...>, которая отменит твою работу без предупреждений, прямо как git checkout. Здесь нет какой-либо "проверочной" опции. Будь очень осторожен с этим и трижды проверь, что у тебя нет ничего, что ты хотел бы сначала сохранить.

Более безопасный вариант - это git stash, которая запихнет все твои незафиксированные изменения в некий временный псевдо-коммит, не привязанный к твоей ветке. Ты можешь увидеть их, используя git stash list, и если ты поймешь, что хочешь оставить что-то из этой работы, ты можешь заново применить спрятанный патч с помощью git stash apply.

git rebase


Мне плевать, что говорят другие. Не используй ничего, что содержит "rebase", пока ты не понимаешь, что именно ты делаешь.

"Rebase" - для редактирования истории. Но ты не можешь редактировать историю, по причине ее полного хэширования. Вместо этого git rebase создает новую историю.

Скажем, у тебя есть A → B → C, где C - это твой собственный коммит, а B - это самый последний коммит в origin/master. Ты отправляешь изменения и... О, нет! Там уже есть новый коммит D на сервере. Поэтому ты получаешь следующее:

      .---C  master
     /
A---B---D    origin/master

Ты бы мог сделать слияние здесь... или ты бы мог сделать перемещение. Фундаментально, "перемещение" означает пересоздание коммита с другим родителем. Git возьмет патч в C, применит его поверх D, исправит все номера строк и попросит тебя разрешить все конфликты (прямо как в слиянии), а потом создаст новый коммит из результата. Это не может быть до сих пор коммит C, потому что родитель является частью хэша коммита, а родитель изменился. Вместо этого ты получишь коммит C'. (Новый хэш не обязательно похож как-либо на старый; апостроф, произносимый как "штрих", это соглашение, заимствованное из математики.)

Поэтому теперь у тебя:

      .---C
     /
A---B---D         origin/master
         \
          .---C'  master

Твой коммит должен был быть основан на B, но ты переписал его, чтобы он был основан на D. Следовательно, он перемещен. Я полагаю.

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

Заметь, что C до сих пор присутствует, но у него больше нет имени. Git сохраняет висящие коммиты вроде этого около 30 дней (видно в git reflog), просто на случай, если ты совершил ошибку, и удаляет их в ходе сборки мусора.

Перемещение может быть очень разрушительным, и не должно легко выполняться. Определенно, никогда не перемещай коммиты, которые ты уже так или иначе опубликовал - если у кого-то еще работа основана на твоем оригинальном коммите C, то обновление их работы так, чтобы она основывалась вместо этого на C', становится огромной болью в заднице. А если они этого не делают, то ты можешь остаться с обоими C и C' в своей истории, или они могут конфликтовать друг с другом, или кто его знает что еще. Я повидал злоупотребление git rebase, превратившее линейную ветку с четырьмя коммитами в запутанное месиво из порядка пятнадцати коммитов, все слитые вперемешку с копиями друг друга.

А также существуют дальнейшие осложнения, наподобие: C может быть более чем один коммит, C может включать коммиты слияния, ты можешь редактировать C, пока ты в нем находишься и т.д. Это может доставить неудобства и довольно скоро, и не обязательно очевидно, как разрешить дурацкую проблему, если ты не абсолютно уверен в том, что делает Git.

Если ты решил поэкспериментировать с перемещением, одно последнее предупреждение. В слиянии твоя ветка "наша", а чужая ветка - "их". Но в перемещении все наоборот - ты начинаешь с чужой ветки и заново добавляешь свои собственные коммиты поверх нее, даже если ты думал, что перемещал свою текущую ветку. Поэтому с точки зрения Git, твоя ветка "их", а чужая ветка - "наша"! Это влияет на принудительное разрешение с помощью команды git checkout --ours, она обходит все патчи в отметках о конфликте и инвертирует "их" в "нас", когда описывает конфликты в git status. Еще одна причина не производить перемещение до тех пор, пока ты абсолютно не уверен, что ты понимаешь, что происходит!

Если ты делаешь лажу во время перемещения, ты всегда можешь выполнить git rebase --abort. Или, если перемещение уже закончено, ты можешь сослаться на старую версию ветки с помощью специального синтаксиса имяветки@{1}, который означает "куда указывала имяветки, перед тем, как была изменена в последний раз". Тебе следует использовать git reset --hard чтобы заставить ветку вернуться, хотя, ой, фу.

--force


Обычно появляется как аргумет для git push после перемещения. Будь супер-пупер осторожен, т.к. он вслепую переписывает что бы то ни было на удаленном репозитории. Если ты читаешь эту статью, у тебя, вероятно, нет хорошего повода для принудительной отправки изменений. А если ты думаешь, что есть, то, вероятно, все еще нет, потому что в Git 2.0 есть аргумент --force-with-lease, который, по крайней мере, защищает от ситуации гонки.

Сохранение паролей, больших файлов и т.д.


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

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

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

Забывая, что ты в середине чего-то


Git - это инструмент командной строки, не интерактивная программа, поэтому можно находиться в середине многоступенчатого процесса и потом... забыть об этом.

Вероятно, ты пытался выполнить перемещение, но произошел конфликт. Тебе стало скучно, пока разрешал конфликты и ты ушел домой на ночь. Вернулся утром, все готово к работе! Делал все утро какие-то коммиты, а затем понял, что Git все еще думает, что ты в середине перемещения, потому что создание коммитов в ходе перемещения - это, на самом деле, абсолютно разумная вещь.

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


Вот и все, что у меня есть


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

Несмотря на это, я надеюсь, что что-то из этого будет полезным или, по крайней мере, сделает другие ресурсы по Git более понятными!

Если ты просто умираешь от нетерпения узнать больше о Git, интернет переполнен другими людьми, пытающимися рассказать о нем. Отсылаю тебя к списку статей от GitHub.


P.S.


Автор оригинальной статьи - Алекс Манро (Alex Munroe aka Eevee).
Автор перевода - Indexator.

Материал распространяется под лицензией CC-BY.

@Indexator
карма
48,7
рейтинг 0,0
/devel
Самое читаемое Разработка

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

  • +22
    Коммит — это патч. Все. Он перечисляет некоторые изменения в некоторых файлах в формате «единого diff-а». [...] История Git — это очень длинная цепочка инструкций для пересоздания кодовой базы с нуля, шаг за шагом. Представь это как стопку патчей, [...] В общем, да, но также они буквально являются SHA-1-хэшами патча, включая заголовки.

    Эм, но… нет.

    The major difference between Git and any other VCS (Subversion and friends included) is the way Git thinks about its data. Conceptually, most other systems store information as a list of file-based changes. [...] Git doesn’t think of or store its data this way. Instead, Git thinks of its data more like a set of snapshots of a miniature filesystem. Every time you commit, or save the state of your project in Git, it basically takes a picture of what all your files look like at that moment and stores a reference to that snapshot.


    (http://git-scm.com/book/en/v2/Getting-Started-Git-Basics)

    Иными словами, каждый коммит в гите — это дерево блобов с полным содержимым файлов, а не изменений.
    • –6
      Это только в «представлении». В паках гита хранятся диффы.
      • +11
        Вот только это не исторические диффы, а просто дельта-компрессия блобов.

        stackoverflow.com/a/8198276/1105881
        • +5
          Причём что интересно, дельта-компрессия может быть даже не от конкретной предыдущей версии данного файла, а от чего-угодно, что покажется гиту похожим на новую версию файла. К примеру в первой ревизии у нас есть файл 1.txt с содержимым «Вася» и файл 2.txt с содержимым «Петя». Теперь мы меняем содержимое файла 1.txt на что-то типа «Пета» — так вот в гите это может сохраниться как «файл 1.txt второй ревизии — это файл 2.txt из первой ревизии с 4-ым байтом 'а' вместо 'я' ».
          • 0
            Да, я именно это и имел в виду. Только файлы адресуются не по ревизиям, а напрямую по хэшам.
          • 0
            Дополнение: именно так git и определяет, что в комите файл был скопирован или переименовался и даже показывает процент совпадения, что невероятно удобно.
            • +3
              И невероятно НЕудобно, когда промахивается, а явно, в отличие от мерка, переименованный/перемещенный файл указать нельзя.
      • +4
        Увы. вы, похоже, совершенно неправильно понимаете гит и как он работает. Каждый коммит — это определенное состояние рабочей директории. Все дифы (патчи) которые возникают — всегда результат сравнения разных состояний рабочих директорий будь то merge, rebase или просто diff.. И они даже не кешируются, а каждый раз высчитываются. Именно поэтому в гите не рекомендуется хранить больших файлов — даже при малом изменении будет храниться новая полная копия файла. Другое дело, что хранение рабочих директорий устроено таким образом, что дифы считаются феерически быстро.

        Вы уверены, что хотите этому учить читателя?
    • 0
      Позвольте не согласиться с вашим высказыванием, как с валидной критикой данного поста.

      С технической точки зрения, вы, скорее всего, правы насчёт природы коммитов — я не влезал в тонкости реализации.

      Однако, семантика использования (за исключением rebase) больше всего напоминает набор патчей.

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

      Может, конечно, у дураков мысли сходятся, однако такая абстракция, на деле помогает очень быстро ввести нового человека в цикл разработки с использованием git.

      Особенно наглядно это работает, когда, например, кроме почты, действительно нет общедоступного канала для обмена изменениями. Тут вы действительно оперируете всегда патчами (коммиты) и патч сиквенсами (ветки). При этом, тот же gitflow, вполне себе спокойно при таком канале работается, практически без лишних телодвижений, если юниксовые мэилбоксы использовать особенно.
      • 0
        С технической точки зрения, вы, скорее всего, правы насчёт природы коммитов

        Я опираюсь на утверждения из Git Book, и это не техническая точка зрения, а концептуальная.

        Однако, семантика использования (за исключением rebase) больше всего напоминает набор патчей.

        Вот это «за исключением» все и портит.
        • 0
          Ну тут можно спорить, но как модель — оно работает хорошо. А когда человек достаточно освоился, он может отбросить эту модель, и держать в голове больше деталей реализации (если оно ему надо).
          • +2
            Я не очень люблю сначала учить неправильной модели, а потом, когда возникают проблемы, переучивать.
            • +2
              Вы очень категоричны :)
              Никогда не занимались рефакторингом? Или вы всегда и сразу строите безупречную и полную модель бизнесс процессов? Является ли предыдущая модель неправильной?

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

              когда возникают проблемы, переучивать

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

              Опять же, если почитать man gittutorial — вы не встретите там ни слова даже про хэши — там да и в самих cli утилитах они, в основном, упоминаются как «commit», что как бы намекает на то, где предметная область, с которой предлагается оперерировать, а где детали исполнения. Пользователю не нужно знать про тонкости реализации до тех пор, пока ему действительно не нужно про них знать :)

              Могу я вас попросить поделиться практикой введения людей в git (или введения вас в git :))? Вы отправляете читать документацию? На какой срок? Какие контрольные вопросы потом задаёте или тесты проводите? Через какой промежуток времени, вы понимаете, что человек готов работать с кодовой базой без вашего надзора?
              • 0
                Никогда не занимались рефакторингом? Или вы всегда и сразу строите безупречную и полную модель бизнесс процессов?

                Занимался. Но обычно модель бизнес-процесса развивается эволюционно, а не революционно.

                или введения вас в git

                git-scm.com/book/en

                И две недели на гитхабе со своим проектом.

  • +19
    размазать достаточно Git-а по твоему лицу
    нет, спасибо :)
    • +16
      пушнуть на лицо
  • +2
    … Когда они впервые скачивают код, они могут привязать его к директории под названием master (потому что это мастер-копия). Затем, когда они приступают к работе над своим драйвером, они могут все это полностью скопировать в директорию под названием ужасный-драйвер-broadcom. Чтобы сгенерировать патч, им нужно просто получить разницу между этими двумя директориями.

    ах, видимо, поэтому один из вопросов от меркуриальщиков: «как узнать в какой ветке были такие-то изменения?»
  • +1
    раз в месяц тебе приходится отважно сталкиваться с Git, потому что каждый, даже его чертова собака, теперь использует GitHub?
    для гитхаба раз в месяц есть hg-git и help.github.com/articles/support-for-subversion-clients
    • +3
  • +16
    Господи, когда же в мейнстриме появится что-то более простое и понятное чем Git. Пока все статьи которые я вижу из серии «Git это просто» больше напоминают сессии аутотренинга в стиле «все хорошо, все нормально».

    Я через силу заставил себя базовый основы Git выучить, но в целом Git вызывает примерно такое же отвращение, как vi после Sublime.
    • +29
      Git настолько восхитителен, когда его понимаешь, что не представляю, что может быть лучше.
      Если вы не умеете его готовить, то это не означает, что он не вкусный.
      • +1
        Мне в начале его освоения как раз помогло описание его внутреннего устройства. После чего стало понятно, что не может быть ничего лучше VCS с такой прекрасной архитектурой и идеями, которые в неё заложены.
      • +12
        то, что вы не представляете жизни за пределами Git, не значит, что её там нет
        • +3
          Знаю, что есть, но не возникает желания или необходимости мигрировать. Зачем, если все отлично работает?
          И то, что именно git стал на сегодняшний день мэйнстримом, только подтверждает, что большинство придерживается того же мнения.
          • +10
            Ну так и wordpress стал мэйнстримом, Миллоны мух не могут ошибаться.
            • 0
              То что исходный код вордпресса ужасен не отменяет того факта, что с точки зрения пользователя, это превосходный очень удобный движок. Именно поэтому он и стал популярным. Так что аналогия с мухами неверная.
            • 0
              Я что-то не припомню, чтобы мухи ошибались.
            • 0
              Wordpress на момент появления был лучшим движком. Чему удивляться.
              Хотя, если честно, я его всегда недолюбливал.
              Все претензии к нему от того, что новостной движок пытаются использовать для разработки сайтов и магазинов. Просто не надо этого делать и он будет вполне сносен.
              А какие опенсорсные движки сейчас лучше? Просто интересно.
              • +1
                Лучшим по каким критериям? Возможности PHP4 в нём если и использовались, то весьма ограниченно, а ему тогда года 3 уже было.
                • 0
                  Лучшим по тем критериям, что ничего лучше в опенсорсе на тот момент не было.
                  «Миллионы мух» не интересуются, насколько там все прекрасно архитектурно или какие фичи PHP там используются. Раз движок стал так популярен, то разумно предположить, что были для этого причины? Раз считаете, что все так было на тот момент плохо в движке — сделали бы в то время что-то лучше и выложили бы в опенсорс, делов то.
                  • 0
                    Лучшим по тем критериям, что ничего лучше в опенсорсе на тот момент не было.

                    Лучше по каким критериям? :)

                    Есть множество критериев лучшести и Вордпресс оказался лучшим по совокупности этих критериев для «миллионов мух». Но это не значит, что он был абсолютно лучшим, просто какие-то его плюсы оказались более значимыми для «миллионов мух» чем все его минусы.
                  • 0
                    А потому, что совершенство технологии — это далеко на самый важный фактор в распространении. Просто написать крутую штуку и выложить на гитхаб — этого мало.

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

                    Wordpress зашёл в 2003, это был расцвет эпохи блогов и тогда, он действительно был приемлем в технологическом плане.
                    Он предложил простую установку и расширяемость.
                    Wordpress стали выбирать из-за обилия плагинов и тем.
                    Появился wordpress.com…
                    Теперь уже есть армия wordpress-разработчиков тем и плагинов. Так что wordpress — это в какой-то мере технология-стандарт.

                    Та же ситуация с GIT: удачный заход из мира разработки Linux (где ей было самое место) через авторитет Линуса (вспоминаем эксперимент Милгрема), потом github, фактически самая крупная социалочка для OpenSource-разработчиков. Потом codeplex отказывается от HG, потом git-используется по дефолту в кучи мест.
                    Теперь даже если кто-то напишет что-то лучше и удобнее, чем git, оно не станет популярным без очень сильного PR.
                    • 0
                      Совершенно верно, о чем, собственно и разговор.
                      Пока не возникнут новые задачи, которые не решает Git и пока кто-то не предложит новое решение, Git будет оставаться мэйнстримом.
                      Должно быть не просто то же самое, но немного удобнее (что вообще спорная категория), а на порядок лучшее. Или лучше в чем-то конкретном (ниша). Тогда сообщество это быстро раскрутит.
      • 0
        приготовьте мне гит так, чтобы оно записывало переименование/копирование
        • +1
          Сколько лет пользуюсь и не знаю, что есть какие-то проблемы с переименованием или копированием. С чем именно у вас тут сложности?
        • +5
          Если у вас такие вопросы, вы не понимаете гит. Гит отслеживает не файлы, а изменения в них. И это круто, потому что когда ты спрашиваешь «откуда-куда скопирован файл», то скорее всего инетерсен не сам файл, а «откуда взялся здесь этот кусок кода?», и гит даёт ответ на этот вопрос (см. man git-log, флаг -S).

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

          Да, а «переименование/копирование» — это состояние файлов, и это не так важно, как кажется. Впрочем, гит вычисляет эти категории изменений на лету по «похожести» файлов.

          См. githowto.com/changes_not_files
          • +6
            Гит отслеживает не файлы, а изменения в них.
            гит не отслеживает изменения, гит делает снапшоты, а изменения между ними вычисляет по требованию. соответственно, вычислить, когда файл просто переименовали/скопировали (не меняя содержимого), труда не составляет — достигается банальным сравнением хэшей содержимого. Однако в реальном мире контроля версий исходных текстов ПО, гораздо чаще встречается переименование/копирование одновременно с изменением содержимого файла (например, так). И вот в этом месте начинается «угадайка»: что в куда переименовано/скопировано. Естественно, при наличии нескольких похожих изменений в коммите, угадывалка может угадать не то. Что намного веселее, по достижении некоторого порога правок на файл (50% по умолчанию), Git решит, что вы переписали файл заново. Ну и да, в достаточно больших коммитах угадывалка отключается вообще.

            Разумеется, ни mercurial, ни даже всячески гонимый линусом и присными subversion данной проблеме не подвержены.
            • +2
              Я кстати, так понял, что Линус имел слабое представление о subversion, в его понимании subversion = CVS, но это очень сильно не так. CVS действительно была ужасной системой.
              • 0
                Кстати, кто-то может всё-таки понятно объяснить, чем это так плох subversion, что на него столько гонева?

                p.s я абсолютно точно уверен, что мне нужна именно нераспределённая vsc. В моём случае, распределённость — явный минус
                • +2
                  Лично я для себя вижу только одно: в Subversion нет локальных коммитов. Может, их уже добавили, но раньше их не было.
                  У SVN нет поддержки на github и bitbucket, так что для open source проектов он не очень походит. Там как раз нужно форкать репозиторий.
                  А в остальном svn очень годная штука с низким порогом вхождения.

                • 0
                  На мой взгляд интересная заметка автора библиотеки SObjectizer о SVN, нелюбви к нему и степени владения инструментом тех, кто его не любит: http://eao197.blogspot.ru/2015/09/progflame-svn.html
                • 0
                  Неудобная (или сложно найти мануал по удобной) работа с ветками — это основное для меня.
                • +2
                  Распределенность же есть не просит.
                  Можете просто ее не использовать. По опыту работы 90% пользуются одним центральным репозиторием.
                  Но на самом деле, это удобно — у вас один репозиторий, у клиента — свой и вы можете обмениваться правками работая каждый со своей логикой и своим процессом. Мне лично очень нравится эта гибкость.
              • 0
                SVN была ужасной системой. Медленной, с плохой работой веток,… (tree-конфликты как вспомню, так вздрогну).
                Пока не переехали на Git было очень много сложностей с командной работой над большим проектом с длинными параллельными разработками.
                • +1
                  Как я понимаю, ветки в SVN изначально совсем для другого — не для параллельной разработки. А скорее, чтобы хранить несколько версий одного продукта для разных пользователей, скажем.
                  А разработка в SVN хорошо идет, если все всё время работают с одним репозиторием (в одной ветке). Это требует вначале некоторой организации кода, чтобы реально можно было пробовать разные вещи не мешая друг другу, и не мешая основному проекту — на уровне кода, не через SVN. Но зато потом это работает железно, 0 проблем.

                  Видимо Git пытается взять на себя поддержку параллельной разработки, и от этого на него такая нагрузка. Ведь параллельная разработка — это никогда не бывает слишком просто. В принципе это и не может быть просто, вряд ли здесь есть универсальное решение.
                  • 0
                    Не в организации кода дело, просто при разработке в одной ветке (независимо от системы) эта ветка становится продакшен веткой, коммиты в неё могут попадать только после приемочного тестирования.
                  • 0
                    Насчет SVN — очень похоже на то.
                    Пока мы работали с одной веткой — все было довольно неплохо. Но когда стали вести параллельный процесс (несколько пользователей пилят сразу несколько фич), то сведение превратилось в сущий ад.
                    Процесс в git у нас сейчас такой же — несколько параллельных веток, которые регулярно сводятся. Но благодаря ребэйсу (ежедневному и обязательному) и правильной организации задач все годаздо проще.
            • 0
              Вы меня не слушали. Да, снепшоты он делает, и делает он это для отслеживания *изменений*, хотя лучше здесь сказать *содержимого*? Ведь данные он хранит в блобах (не связанных напрямую с именем оригинальных файлов, если быть педантом — имена хранятся рядом, в деревьях). И сжимает блобы, как было уже указано выше, дельта-компрессией. И всё это сделано, чтобы следить за изменением содержимого. Блин, ну не было у меня никогда проблем с переименованием файлов в гите, не было. И если файл при переименовании изменился больше, чем на 50%, то действительно ли это старый файл? Ну какая мне разница, что файл был переименован, если он настолько изменён, что по сути это другой файл? Ещё раз: мне не важно где какой файл как называется, мне важно, оттуда какой кусок купола взялся. И гит решает эту проблему идеологическую. А блобы там и деревья — это деталь реализации, и она меня редко волнует. А учитывая популярность гита, я не один такой. Так что может пора просто изменить точку зрения на нашу программисткую реальность, и понять, что мы создаём не файлы, а текст, код, содержимое?
              • +6
                Ну какая мне разница, что файл был переименован, если он настолько изменён, что по сути это другой файл?
                Это не так сложно как может показаться: переименовали пару тесно связанных классов вместе с файлами, поправили типы полей, имена переменных, ругательства в ассертах — и вуаля, сущность по сути та же, а гит думает, что мы её с нуля написали. Впрочем, с гита взятки гладки, гит — тупотрекалка содержимого, настоящая же проблема в том, что это 1) затрудняет археологические раскопки 2) запутывает коллег при code review
                Так что может пора просто изменить точку зрения на нашу программисткую реальность
                Интересно, это чего ради же? Чтобы вписаться в ограничения второстепенной утилиты, неспособной тупо записывать за мной? Много чести, проще взять аналог, который это умеет.
                • –3
                  Вы сейчас какие-то небылицы рассказываете. Чтоб были понятно — у меня 15 лет стажа после универа, лет 5 на гите, писал профессионально на перле, пхп, питоне, теперь на скале пишу. Команды были от 2-3, до 15 человек, сейчас пишем большой командой суровый энтерпрайз: скала, хадуп, бигдата, все дела. С описанными проблемами не сталкивался никогда. Или просто за проблемы их не считал.
                  • +5
                    ну начинается :) у меня 10 опыта и 5 гита, и что с того? это каким-то образом отменяет объективную реальность?
                    С описанными проблемами не сталкивался никогда.
                    1) это только 1 (одна) проблема, 2) ах ну раз не сталкивались, значит всё в порядке. carry on :)
                    Или просто за проблемы их не считал.
                    philosoraptor.png
      • +3
        Что в нем восхитительного?
        Отсутствие веток (только именованные головы) и такая милая штука как отсоединенные коммиты?
        Или только автоматическое определение переименованных/перемещенных файлов?
        Или merge, который сливает два снапшота вместо двух графов диффов?
        • 0
          Почему же нет веток? У меня есть.
          У меня по процессу 4 постоянных ветки и я в любой момент могу делать сколько угодно новых — под конкретные фичи. И все это элементарно и беспроблемно таскается из ветки в ветку, меняется местами и сводится. Я в любой момент могу решить, что войдет в апдейт, что отложить, что выложить частично, например. Не представляю, что может быть гибче.
          Мерджи я по процессу совсем исключил. Они слишком мусорят историю. Все делаю ребэйсом. Даже пулл — с опцией ребэйса. Так же удобно собирать коммиты (если случилось, что по одной задаче их получилось несколько) в один интерактивным ребэйсом.
          А что из какой ветки пришло сохраняется при слиянии функционала после ребэйса уже мерджем с --no-ff.
          История всегда идеальна и прозрачна.
          • +3
            Я вас разочарую — у вас нет веток. У вас есть именованные головы и связи между коммитами. «Ветка» в гите — раскраска от коммита в прошлое по связям.
            Ветки, привычные по живой природе и другим системам контроля версий, совершенно другие: растут не от крайнего листочка в прошлое, а от родительской ветки в будущее.

            > И все это элементарно и беспроблемно таскается из ветки в ветку, меняется местами и сводится. Я в любой момент могу решить, что войдет в апдейт, что отложить, что выложить частично, например. Не представляю, что может быть гибче.
            Я вас снова разочарую: в мерке все это делается еще проще и надежнее ;) Потому что с нормальными именованными ветками, без дурацких отсоединенных коммитов, куда более интеллектуальным слиянием, возможностью руками указать на перемещение-переименование

            > Мерджи я по процессу совсем исключил. Они слишком мусорят историю. Все делаю ребэйсом.
            То бишь предпочитаете фальсифицировать историю. Это вполне возможно и в других системах контроля версий. Проблема только в том, что при ребейзе мерж никуда не пропадает по факту, но из истории вычеркивается. Это делает расследование и исправление проблем особенно увлекательным. Также очень приятно что без специальных ключей гит элегантно сотрет лишнюю по его мнению информацию о принадлежности коммита к ветке (я в курсе, что такой информации у гита на самом деле нет, я про то как это выглядит для пользователя).
      • +2
        Я умею его готовить и даже есть.
        Но гит именно что невкусный.
        • 0
          А что именно вам в нем не нравится? Конкретные примеры?
          • +2
            Коммит отсоединенный.
            Слияние без учета истории сливаемых веток.
            Переименование-перемещение только если как сам гит соизволит принять вердикт… который может меняться на лету вместе с настройками.
            Отсутствие именованных веток.
    • +2
      www.fossil-scm.org куда еще проще?
      • +4
        Ну а что? По сути это мини-github в кармане. Кроме распределенного репозитария получаем распределенный трекер ошибок, базу знаний и т.д. и т.п. Fossil — это лично мой выбор, как человека до сих пор сидящего на git. Он чуток проще git, можно таскать на флешке без надобности настройки окружения. Один минус — большие бинарные файлы не для него, но я пишу только код. Для маленькой комманды нет ничего лучше.
        Git оставим для больших проектов с кучей народа. А свои личные проекты я буду держать на этом маленьком гиганте.
    • +5
      Попробуйте Mercurial с помощью TortoiseHg. Он интуитивно кажется гораздо понятнее.
      • +3
        а чем это будет отличаться от tortoisegit или tortoisesvn?
        • 0
          если пользоваться черепахой, то конечно, гит покажется недружелюбным. Git идеален а консоли — только так понимаешь, как именно он работает, и почему это правильно.
          • 0
            Родной GitGui тоже очень хорош.
            • +5
              По моему, все недовольство git'ом вызвано как раз использованием GUI-оболочек.
              Оболочки еще больше запутывают и без того «странноватый» синтаксис git'а. А понимание логики работы приходит только когда работаешь с git'ом напрямую через консоль.
              • +1
                Ну в винде (если я не в шелле msys2) мне удобнее использовать GitGui. Ну а нравится он или нет — дело вкуса. Мне и так хорошо и так.
              • +1
                Лично мое первое впечатление от git было: «version destroy system»
                А все потому что впервые столкнулся с отсоединенным коммитом.
                Коммит, конечно, нашелся, но осадочек неприятный остался.
                Логику работы понял так: «и чего только люди ни придумают лишь бы именованных веток не делать».
                • 0
                  В первый раз отсоединенный коммит (особенно, когда его не ожидаешь) — это страшно )
                  Но это от непонимания что к чему.
                  На самом деле, ничего страшного в нем нет.
                  • +1
                    Страшного ничего нет — жить без веток и с отсоединенными коммитами можно.
                    Вот только как часть дизайна системы контроля версий и то, и другое — фейл, эпик фейл.
                    Не должна система контроля версий автоматически утрачивать такую важную часть метаданных, как ветка, в которую сделан коммит.
                    И уж тем более ничего подобного не должно автоматически происходить с самими коммитами.
                    • 0
                      Не должна система контроля версий автоматически утрачивать такую важную часть метаданных, как ветка, в которую сделан коммит.

                      а что есть такое важное в тех метаданных (кроме самого названия) ветки меркуриала? (без иронии и сарказма)
                      я так и не понимаю пока, что это даёт…

                      взять тот же репозиторий самого Git'а:
                      развесист он до ужаса, однако, найти кто, когда в ходе какого набора коммитов сделано изменение — вообще не проблема…
                      и самое главное — узнать почему: это описано в КАЖДОМ коммите
                      и хотя это мера административная… весьма нужная… но в произвольном проекте всё равно зависит от разработчика
                      и уж если разраб не пишет ПОЧЕМУ он сделал эти изменения, а ты потом вынужден догадываться (по имени той ветки, в которой он это делал), то это не столько проблема ЛЮБОЙ SCM (даже SVN), сколько «разрабу по башке надавать», чтобы не писал коммиты вида «Исправлена ошибка»
                      или есть ещё что-то, что я не вижу?
          • +3
            И граф коммитов тоже в консоли смотрите?
            У меня вот очень большой проект, чтобы проследить от кого пришло изменение мне порой приходится стреляться даже с SourceTree. Как вы это всё в консольке делаете?
            • +1
              git log с дополнительными опциями
              stackoverflow.com/a/9074343/272927
              • +4
                Да, я знаю, что у git есть log с кучей параметров.
                Мне интересные конкретные действия, которые делает человек, который отрицает GUI для Git для того, чтобы найти от кого пришло изменение. Ну там, открывает 10 терминалов, долго уныло скроллит, пишет grep или как?
                • +1
                  Ну да. git log --grep, git blame и т.д.
                  10 терминалов не нужно. Достаточно одного.
                  • 0
                    соответственно, с десяток тыкнутых клавиш вместо пары кликов мышкой) тут действительно нечего спорить, кому как удобнее. На небольших проектах с небольшим числом разработчиков gui вполне достаточно (а зачастую в силу более низкой планки входа — и удобнее). Консоль представляет больше возможностей, там где они действительно нужны. Этот вечный спор консольщиков и визуалов напоминает ситуацию с АКПП. Первое время автоматы считались моветоном, 'не по пацански' ;) а ведь ничего, сейчас половина машин, если не больше, уже без педали сцепления. И ничего, 'пацаны' нормально ездят.
                • +1
                  tig? — https://github.com/jonas/tig
            • 0
              Граф, каюсь, смотрю в gitk — запускаю из консоли.
              В нем мне действительно удобнее смотреть историю.
              Хотя проблем с git log тоже нет. Особенно, когда работаешь не на своей машине, а по ssh.
              Из доп.средств еще пользуюсь kdiff3 для разбора конфликтов, тоже как-то привык к нему.
          • –1
            Не знаю, уверен что профессионалы Git, конечно, правы. Но так как по сути и Git, и SVN, и все такое — делают ОЧЕНЬ ПРОСТЫЕ ВЕЩИ, то совершенно ожидаемо, что я, как пользователь, ожидаю от них, что бы они делали это ПРОСТО.

            Ну как бы система выполняет 3-5 команд всего. Неужели так было трудно сделать, чтобы эти 3-5 команд выполнялись легко, без того, что нужно год работать под Git чтобы во всем разобраться? Ясно, что в конце концов человек во всем способен разобраться.

            Merge под Git — это просто катастрофа. Всегда приходится использовать посторонние программы, но дело не в этом. Не дай бог случайно сделать что-нибудь не то, какую-то ошибку. Потом откатить назад все очень сложно.
            Как-то в SVN я этого не замечал. Так что я жалею, что весь мир перешел на Git.

            Недавно мы начали работать с суб-репозиториями. Это вообще ужасно. Собрать головной проект по всем референтным ссылкам на суб-репозитории, которые независимо обновляются… Ну правда, это как использовать vi вместо Microsoft Word.
    • +1
      Ну. vim настолько же лучше Sublime, насколько vim лучше блокнота.
    • +2
      Git сложный и вся его прелесть внутри, в этих tree-hash, в этих индексах, в этих immutable цепочках комитов. Он настолько сложен и прекрасен внутри, что когда он выходит наружу через console, а еще хуже через UI, он становится совсем непонятным. Почему надо сначала делать git add, а потом git commit. Почему есть git push и есть git commit, эти многие вопросы, которые появляются когда люди переходят с svn или других систем. На самом git гораздо сложнее внутри, чем svn, зато эта сложность оправдана. Он позволяет делать такие алгоритмы, которые невозможны в других vcs. В git действительно, очень сложно потерять, его действительно очень сложно сломать без возможности восстановить (чего не скажешь о svn), git делает потрясающие merge. Сравнивать его с mercurial не стану, долго им не пользовался, но git действительно очень интересно устроен. Мне кажется, каждый программист должен изучить сначала его изнутри, тогда многое на верхнем уровне станет понятнее. На самом деле git команды изначально были не самым лучшим образом устроены, поэтому появилось множество утилит и «упрощений» типа github client, но они меняются со временем и становятся только лучше.
      • +2
        Так может, он бы и жил своей внутренней жизнью, вообще не выходя наружу? Мы бы радовались его красоте, а пользовались для жизни чем-нибудь другим?

        Вот например, прекрасная ситуация: сделал git push, а потом, перед git commit проверил обновления и увидел, что код существенно изменен. Казалось бы, что проще? Возьми последнюю версию, сделай merge.
        Но после обновления выходит, что уже есть 3 версии: локальная, та которая push и обновленный репозиторий. Сидишь, думаешь, с чем сливать. Ладно, проще иногда сделать commit с конфликтами, просто чтобы не связываться с этим, а потом еще раз вычистить все конфликты и снова сделать commit. Но тогда кто-то другой может успеть взять версию с конфликтами из репозитория…
        • +2
          Но после обновления выходит, что уже есть 3 версии: локальная, та которая push и обновленный репозиторий. Сидишь, думаешь, с чем сливать.

          эээ… ТРИ версии? откуда?
          в простейшем случае в одной «ветке» проекта есть две версии кода:
          ваша локальная и та, что на удалённом «центральном» сервере, и всё.

          и зачем перед git commit делать обновления? ну, хорошо, git fetch можно делать в любое время, но каким образом это мешает коммитить? или вы следуете парадигмам SVN: коммитите только в последнюю версию с сервера?

          может порядок сначала навести в головах или workflow?

          если готовы конструктивно обсуждать, я тоже готов, а отвечать на нытьё «я там что-то сделал, не понял что, а оно сломалось, вот оно говно» не хочется :)))
          • +1
            эээ… ТРИ версии? откуда?

            1. локальная ветка
            2. удаленная ветка
            3. рабочая директория с результатом мержа
            • +3
              ну, давайте упростим себе задачу:
              1. начнём с того, что мёрджить стОит только закоммиченные изменения (хотя можно и с «грязной» WC, если изменённые файлы не трогаются мёрджем; но зачем себе яму рыть?)
              2. таким образом мы имеем две версии: локальная и удалённая… мёрджить их — не проблема. даже в случае ошибок, всегда можно повторить мёрдж…
              3. пушить на сервер можно только закоммиченные изменения, поэтому вопрос «у меня тут незакоммиченный результат мёрджа, не знаю что пушить» — как-то абсурден, мне кажется? а закоммиченный мёрдж превращается в локальную версию… и опять у нас ДВЕ версии: локальная и удалённая…

              поэтому я не понимаю тут проблемы ТРЁХ версий…
              • +1
                1. Иногда можно и забыть — человеческий фактор и всё такое, но не суть.
                2. В момент мержа или сразу после него у нам три версии по любому — локальная в репозитории до мержа, удаленная в репозитории до мержа и результат мержа (закомиченный или нет тут не особо важно), что хорошо видно по всяким *diff3
          • –1
            Сделать commit не получается просто так. Если удаленная версия изменилась, то, как я понимаю, Git заставляет её взять начала. Дальше возможны два варианта. Если его автоматический merge сработал, то в Tortoise возникает такая смешная картинка, которая меня приводит в некий ступор: одна стрелочка вверх, одна вниз. Ну то есть можно после этого сделать push, не вполне понятно в какую версию. Но через несколько pulls все восстанавливается, кажется.
            А вот если не повезло, и возникли конфликты в слиянии… Тут все совсем плохо. Локально версия становится с этими безумными <<<<<<, >>>>>>>>; версия staged без них. При этом откатить авто-commit, кажется, невозможно (может, я не знаю как просто?).
            Надо дальше читать про git?
            • 0
              У вас что‐то с workflow. Если бы вы делали коммит находясь исключительно на своей закладке‐ветке, то таких проблем бы не возникло на стадии фиксации. Конфликты, конечно, никуда не делись бы, но они возникли бы позже: при merge или rebase.

              Ну и если вам не нравятся <<</>>>, то используйте другой mergetool (см. git help mergetool и git help config, во втором искать mergetool).

              PS: вернуть репозиторий из абсолютно любого состояния возможно всегда: я, к примеру, иногда вынужден делать git branch -f branch origin/branch. Но не всегда это легко. git reflog вам поможет.
              • 0
                Извините, я неправильно написал. Надо было «сделать push не получается просто так»…
                Просто я работаю с несколькими системами одновременно, и все эти понятия слегка смешались у меня в голове.
                Но это была моя ошибка, извините.

                По сути я описал проблему верно, только ошибся в словах.
            • +1
              Коммит можно делать всегда и из любых положений, он не требует обновлений с сервера, потому что это сугубо локальная операция. Только git push может потребовать git pull если удалённая ветка обновилась.
              • +1
                Это была моя ошибка, извините. Я выше (и ниже) объяснил.
            • +3
              Если удаленная версия изменилась, то, как я понимаю, Git заставляет её взять начала.

              в этом и проблема, что Вы неверно понимаете
              в отличие от SVN, используя Git, вы можете вообще не обращать внимание на то, как обновляется «центральный» сервер (боюсь Вам говорить, что удалённых репозиториев может быть не один), коммитите себе и коммитите в свою ветку…
              а брать pull'ом удалённые репозитории — не самый лучший способ

              судя по всему, вы коммитите сразу в master, и ещё делаете git pull ПЕРЕД коммитом…
              в общем, вы трогаетесь сразу с третьей передачи, и удивляетесь, что она глохнет… иногда только газу когда сильнее даёте, до отсечки, тогда иногда трогается, но сцепление горит…

              сходите в автошколу, что ли… на пару занятий…
              • 0
                Да нет, я кажется плохо объяснил. Во-первых я обычный пользователь, и уж конечно никто мне в master работать не даст. Моя область профессионализма, ну извините, лежит не в IT; в своей области я хороший профессионал. А с Git мне просто приходится иметь дело.

                Commit, разумеется, сделать легко. И никакой pull перед commit я не делаю (хотя, возможно, стоило бы). Проблемы начинаются, когда делаешь push и понимаешь, что версия уже изменилась. И тут начинается автоматический merge + все последующие проблемы.

                Скажем, конкретный вопрос: а) можно ли откатить авто-merge? б) можно ли откатить commit? Просто легче делать merge между локальной копией, и удаленной. А не между stage-версией (которая неизвестно где находится), и удаленной.
                Под stage-версией я имею ввиду ту, которой уже сделали commit но еще не смогли сделать push.
                • +2
                  Откатить автослияние должно быть можно обычным git reset. Но я предпочитаю просто работать с ветками‐закладками, которые трогаю только я, потому с такой ситуацией практически не сталкиваюсь. Но, насколько я помню, автослияние только при pull, а с push такого быть не должно. Если после push идёт автослияние, то вам поможет только push -f {remote} {old-commit-hash}:{branch-name}, если я правильно понимаю (точнее, вам точно нужен push -f, но не уверен, что он примет {old-commit-hash}: если нет, то придётся использовать ещё и branch -f {branch-name} {old-commit-hash}, предварительно слиняв с ветки или тот же reset (что проще: линять с ветки не нужно)).

                  Фиксацию обычно откатывают через reset. Помните, что под «откатом» в git имеется ввиду исключительно перемещение закладок: само изменение никуда не денется, просто на него после отката перестанут ссылаться, а через некоторое время оно будет удалено сборщиком мусора. Поэтому в особо сложных случаях можно «откатывать» через branch -f {branch-name} {commit-hash}, восстанавливая изменения в рабочем каталоге через checkout {removed-commit-hash} -- '*'. Предварительно сделав checkout --detach и в конце не забыв checkout {branch-name}.

                  И ещё одно: stage версия — это незафиксированное состояние, с которым до фиксации (commit) можно работать только локально, ни в коем случае не состояние, имеющиеся в любом из изменений.
                  • 0
                    точнее, вам точно нужен push -f

                    Предварительно сделав checkout --detach

                    оу! оу! оу! полегче!
                    *и после этого ещё ругаются на Git...*
                    не лечите по фотографии, а то ща насоветуете…
                    • 0
                      Это единственный способ откатить слияние, если оно с какой‐то радости произошло именно при push, а не локально, как должно. Я недостаточно знаю git, чтобы сказать, что слияние на сервере при push в принципе невозможно.
                    • 0
                      Лечите, лечите. Может я наконец разберусь. Поверьте, я уже прочитал книжку по Git, и работаю с ним пару лет, и то до сих пор не разобрался в некоторых базовых вопросах. Это уже даже не смешно.

                      На работе, если у меня что-то не получается, то просто приходит человек и исправляет. Но я все равно не понимаю до конца. Причем, мне кажется, даже он не всегда понимает как именно надо исправить, просто делает несколько разных попыток и в конце все работает.
                      У нас последняя история — начали работать с submodules, это вообще финиш. Приходится каждый раз ВРУЧНУЮ проверять все ссылки на submodules каждый раз, прежде чем делать pull главной версии. А этих submodules уже больше десятка. Но про это я даже не спрашиваю, мне бы с вещами попроще разобраться.
                      • +1
                        В mercurial свой аналог (subrepos) пометили как «feature of last resort» («использовать в крайнем случае»). Наверное, не зря.
                        • 0
                          Наверное. К сожалению, это не мне решать. Возможно это облегчает координацию между группами в чем-то, когда на проекте более тысячи человек.
                        • 0
                          В git'е их тоже не стоит использовать, пока не прижмёт. Всё-таки фича тяжелая для повседневного использования и требует хорошо продуманного workflow.
                  • 0
                    Я что-то понял, но мне хотелось бы прояснить. Сначала определимся с терминологией.

                    Версия А — моя локальная версия. НЕКОТОРЫЕ файлы из которой я хочу отправить в репозиторий, а с другими продолжаю работать.
                    Будем считать, что когда я начал работать с локальной версией, она была синхронизована с репозиторием в день 1.

                    Я делаю commit этим файлам, и у меня появляется:
                    Версия Б — это удаленная версия день 1 + мои правки, все это все еще у меня на компьютере.
                    При этом версия А тоже продолжает оставаться на моем компьютере, так как я сделал commit не всем файлам.

                    Следующий этап — я делаю push, и понимаю, что удаленная версия изменилась. То есть сразу push не проходит. Назовем удаленная версия день 2, это версия В.
                    Значит я делаю pull этой версии В, и этот pull портит мне версию А. Но я могу зато сделать merge. При этом версия Б остается неизменной, мой merge на нее не влияет, к сожалению.

                    Ок, я делаю merge В с А (хотя хотел бы Б с А), потом делаю еще один commit и надеюсь, что теперь Б соединилась с В.
                    После этого я делаю push и вроде все должно быть нормально.

                    Однако, поскольку эта процедура портит мне локальную версию А, то и merge A c В может не вполне адекватно отражать то, что я хочу сделать merge Б c В.

                    Где физически находится версия Б? Могу ли я работать с ней напрямую, после commit но до push? Могу ли я работать с Б так, чтобы моя локальная версия А оставалась нетронутой?

                    Извините, если запутано получилось.
                    • 0
                      да уж… весьма запутано
                      Вы визуализируете себе дерево коммитов?
                      gitk --all ?
                      git log --all --oneline --graph --decorate на «худой конец»?
                    • 0
                      Ну здесь хотя бы нету слияния при push, так что про push -f лучше забыть. Версия Б находится локально в виде изменения и изменить её нельзя никак, но можно создать версию Г, что‐то сделав (rebase, к примеру) из Б и обычно этот вариант считают изменённой версией Б (знание о том, что версия Б не изменилась и никуда не делась пригодиться, если вы где‐то накосячили — Б можно добыть из reflog). Я предлагаю следующее:
                      1. git stash — фиксирую версию А в специальном изменении, которое никуда не push’ится. Достаётся с помощью git stash apply, при этом происходит аналог rebase. Это лучше вообще делать до pull, если вы хотите нормально работать с Б.
                      2. Теперь делаете pull, указывая что делать с Б (слияние, rebase).
                      3. И теперь достаёте сохранённый А, происходит rebase A поверх слияния Б+В (которое я ранее назвал Г для случая «rebase»).
                      Делать push можно в любой момент после 2.
                      • 0
                        Звучит очень хорошо. ОК, про А мы можем забыть. Делаем ей stash, действительно, и все. Вместо pull сначала fetch, чтобы осмотреться.

                        По поводу 2: вы не знаете, как в Tortoise выбрать слияние или rebase? И в чем разница?
                        И другой вопрос — как в Tortoise откатить слияние, если оно слишком запуталось (то есть вернуть чистую версию Б?)

                        Я как-то уже привык работать через Tortoise, не хочется учить terminal git только для пары вещей.

                        Спасибо!
                        • +1
                          Про tortoise меня не спрашивайте, я использую либо терминал, либо Vim с моим дополнением aurum (которое попутно стирает часть различий между git и mercurial). Но слияние/rebase только через терминал.
        • 0
          #some changes in working
          git add.
          git commit
          git fetch
          git merge --no-commit origin/…
          #control merge
          git push
    • +4
      Думаю, что очень нескоро. Github, bower и прочие тулы, где git идёт по-умолчанию, фактически, монополизировали git.
    • +2
      Да-да. У меня у прабабки, когда была жива компьютеры вызывали отвращение, поэтому она до упора пользовалась печатной машинкой. Это про vim против sublime. Про git Вам и так много ответили.
      • +1
        Некоторые и сейчас предпочитают печатные машинки ) Да и качество первых экранов и самих компьютеров могло вызывать.
        Заголовок спойлера
        image
        Californication
      • 0
        Всё бы хорошо было в sublime, если бы там текстовый редактор доработали. А так — когда мне нужен блокнот на стероидах — я предпочитаю IDE.
    • +4
      «git gets easier once you get the basic idea that branches are homeomorphic endofunctors mapping submanifolds of a Hilbert space.» twitter.com/tabqwerty/status/45611899953491968
    • +1
      Вроде как Google собирается раскрыть свою систему контроля версий, основанную на mercurial.
  • –8
    Да, есть много хейтеров гита, даже я таким был, и боготворил меркуриал… Но до тех пор, пока не начал работать в команде из более чем 3х человек.
    Меркуриал прекрасен, быстр, прост и удобен.
    Но в гите понимаешь все прелести cherry-pick, rebase, ветвления, множество апстримов (они есть в меркуриале, но либо скрыты в недрах, либо реализованы костылями)… Около полутора лет назад плотно подсел на git, но до сих пор не понимаю как он работает. Очень часто ломаю ветки пытаясь сделать rebase на master, иногда возникают вопросы как сделать инвертированный cherry-pick (особенно для отката более чем одной ревизии), не всегда ясно почему это дерьмо не хочет «пушить» в мастер с одной машины, но успешно «пушит» с другой, но я его люблю, потому что… Да не знаю почему… Наверное потому что он прекраснее меркуриала, с ним можно поиграться, поискать проблем на пятую точку и сломать все нахрен =)
    • +4
      Если с каждой возникающей проблемой один раз разобраться и запомнить, то Вы начнёте боготворить гит.
      • 0
        Если разобраться предварительно, то и проблем не будет возникать.
        • +16
          Если использовать меркуриал, то и разбираться не придется — оно просто сразу работает так, как надо.
          • –2
            Не работает оно как надо, основной костыль mercurial — это бранчи, в git они работают просто и понятно, в mercurial это что-то другое. Еще mercurial не давал никаких инструментов по созданию «чистой» историю, а не сложнейшего графа комитов — не было rebase стратегии (ситуация может поменялась за 3 года, так что буду рад услышать что-то новое)
            • +9
              Про бранчи — с точностью до наоборот.
              Главный костыль гита — обозвать именованную голову веткой, а отсоединенный коммит — нормой. «Свобода — это рабство, незнание — сила».
              И меркуриал вы не знаете — 3 года назад все перечисленное вами уже было.
            • 0
              А может мне кто нибудь пояснить необходимость rebase? Чисто для красоты дерева коммитов что ли?!
              Под hg есть плагин, видимо, повторяющий функционал из git, но не могу понять, зачем бы мне захотелось переносить изменения вместо слияния?
              • +3
                Есть замечательная презентация от Mark Jason Dominus: perl.plover.com/classes/git-rebase-wtf/samples/slide001.html
                Она многое объясняет.

                Применений много. Например, вы накоммитили в локальную ветку кучу коммитов, включая коммиты виды «сделал фичу а», «сделал фичу б», «ой, забыл запятую в фиче a», «починил фичу б», «фича б поломала фичу а, починил её», «отрефакторил фичу б». Никому не интересно в истории видеть все ваши метания с запятыми, фиксами и правкой пробелов. Ребейз позволяет такую историю ужать до «сделал фичу а», «сделал фичу б» и отдать ветку в мир с двумя аккурантыми коммитами.

                Другой пример: у вас ветка следит за мастером. Если постоянно вытягивать и мержить мастер в вашу фичеветку, в истории получается «колосок» из мержей, что 1) некрасиво, 2) после пуша только путает историю, т.к. никому неинтересно, когда и как вы подтягивали мастер, 3) добавляет лишний хаос, 4) эти мержи не несут смысловой нагрузки к вашей разработке, итд. Альтернатива: вытягивать master отдельно, а потом фичеветку ребейзить на него. Тогда, когда вы отдадите свою ветку в мир, он будет базироваться на актуальном мастере, история будет ровная, гладкая и шековистая^W^W.

                Есть ещё несколько примеров, когда ребейз полезен, в том числе для исравления всяких ошибок и факапов. Я привёл самые частые случаю использования. Рекомендую всё же посмотреть презентацию Mark-а.
                • +2
                  Всё это, само собой, имеет для вас смысл, если вы заботитесь о чистоте истории так же, как о чистоте кода. Если вам всё равно, и вы считаете, что чистая история никому не нужно, то ребейз не для вас.
                  • 0
                    Да, точно. Поймал себя на мысли, что не вижу ценности в «чистой истории».
                  • +1
                    Очищенная история — это не история, а попытка выдать желаемое за действительное.
                • +1
                  Чисто для красоты дерева коммитов что ли?!


                  Применений много.


                  Для красоты.
                • 0
                  Подписываюсь под каждым словом.
                  Вытягивать ветку за которой следит текущая можно через pull --rebase, еще проще это прописать в конфиг и делать автоматом при каждом пуле.
              • 0
                для того, чтобы в релиз пошла красивая история без творческих метаний и пингпонга с reviewerом
                • 0
                  В релиз пойдёт снэпшот.
              • +2
                У меня длительные проекты и я через какое-то время уперся в то, что история стала просто нереально мусорной — мерджи копились и все это стало напоминать бесконечную пирамиду.
                Ну и сам вопрос мерджей — когда сливаются две правки в одну — не очень приятен при больших изменениях.
                Гораздо лучше из ситуации «делали две большие фичи параллельно и вот они сливаются в одну кучу» сделать ситуацию «вот сделана одна большая фича, вот за ней сделана другая большая фича» — так история получается линейной и фактически двухуровневой (вместо разрастающейся годами пирамиды). Если при этом ребэйсить правки друг-друга ежедневно, то о проблеме с конфликтами можно забыть окончательно. Гораздо легче разрешить конфликты как только они появились, чем делать это при финальном сведении правок. Поэтому у нас прописан в конфиге pull с опцией rebase — разработчик что-то делает в ветке, если при этом кто-то уже залил в эту ветку какой-то другой функционал, то этот разработчик должен свои правки локально перенести на актуальное состояние ветки — если при этом возникнут конфликты, то он их тут же, по свежей памяти разрешит, а когда он закончит работу над фичей и соберется запушить ее в основную ветку, то она идеально встанет в историю — без конфликтов, поскольку он постоянно актуализировал (ребэйсил) свою работу.
                Еще раз повторюсь — для меня это наиболее приемлемый вариант ведения истории по большим долгосрочным проектам с командной разработкой.

                Так же у ребэйса есть интерактивный режим — с возможностью объединять и переставлять коммиты — это очень удобно для финального рефакторинга правок.
                • 0
                  Ок, понял. На всякий случай уточню: в git ведь не обязательно для актуализации с мастером (при длительной разработке) делать ребэйсы? Просто можно время от времени смерживать актуальные изменения?
                  • +1
                    На всякий случай уточню: в git ведь не обязательно для актуализации с мастером (при длительной разработке) делать ребэйсы?

                    не обязательно
                    Просто можно время от времени смерживать актуальные изменения?

                    можно

                    вы вольны выбирать ту схему работы с коммитами, которая вам удобна )))
                    хочешь кучу merge'ей и видеть реально, кто, что, откуда и когда? — пажалте
                    не нравится — делайте rebase, и получи «полусинтетическую» «красивую» историю, в том виде, в котором с ней потом удобней работать
                    дело хозяйское, и SCM Git тому не препятствие ))
          • –2
            Сколько не пытался юзать меркуриал, долго плевался и возвращался на гит. Зачем там дублирование хешей номерами коммитов, для застарелых свнщиков? А ветки там тоже такую боль приносят, чтобы привычнее после свна было? Не знаю, может мой мозг съеден гитом, но мекруриал мне кажется каким-то поломанным и недоделанным подобием гита.
            • +8
              Еще как съеден ;)
              В мерке нет отсоединенных коммитов.
              В мерке мержатся не два снапшота, а две истории коммитов.
              В мерке после мержа двух веток коммиты сохраняют свою изначальную привязку к веткам.
              Так что ветки мерка никакой боли не приносят, если воспринимать их именно как ветки, а не именованные головы с предысторией.
              • +2
                Вот чувствую там что-то прекрасное, но не могу никак понять, что именно и зачем?
                Что дает отсутствие отсоединенных коммитов?
                Чем хороша привязка коммита к ветке? У меня в git программист запушил в release то, что должно было быть в hotfix (или поменялись приоритеты и что-то из релиза нужно выложить срочно) — я просто перекинул коммит из ветки в ветку. Это равнозначно тому, что задача изначально и была бы сделана в нужной ветке. Зачем мне чтобы коммиты были привязаны навсегда к своей ветке? Или я неправильно понимаю и это работает как-то по другому?
                Если работать только с мерджами разве не будет история похожа на карту метро? Или всегда все в мерке делается идеально? Или тоже ребэйсится для улучшения истории?
                Чем мержить две истории лучше, чем мерждить два снапшота? Чем мердж двух историй отличается от ребэйса?
                Мердж он и есть мердж — две ветки сводятся в одну. Мне кажется это вообще в истории неправильное действие и я пользуюсь ребэйсом.
                Ветки у меня и в git есть — но я их контролирую самостоятельно, а не на уровне движка. Но не пойму, в чем именно профит?
                Отсоединенные коммиты — чем плохи? Если я выкачиваю из gerrit что-то чего у меня в истории нет, конечно будет отсоединенный коммит. Ну и пусть будет (это ведь так — не к чему ему привязываться), в чем проблема? А если мне он нужен будет в истории, я его перекину cherri-pick'ом и запушу в одну из веток. Это ведь так и должно быть. Что здесь страшного? Как это решается в мерке? Видимо за счет физических веток выкачивается всегда вся история?
                А как быть с тем, что мои ветки — это мои ветки. И у каждого программиста так. В git я сам решаю какую локальную ветку в какую удаленную закинуть, сколько веток иметь в какой момент времени и это очень удобно и очень гибко. Это главное в git.
                Git мне чем-то напоминает регулярные выражения. Когда понимаешь, как он работает, ты можешь делать все, что угодно очень легко и изящно.

                Вопросы искренние, затронули интересную тему — хочется понять. Есть ощущение, что мерк навязывает тот процесс, который я выстроил в git, но только уже на уровне системы. Это должно быть здорово, но это ведь должно и ограничивать? Не будет той гибкости за которую я так люблю git. Или я неправ?
                Про мерк слышал много хорошего, но ведь у меня в git все то же самое, разве нет?
                • +1
                  Что дает отсутствие отсоединенных коммитов?
                  Дело в том, что сама постановка вопроса неверна. Это не в Mercurial «отсоединённые коммиты» отсутствуют, совсем наоборот: единственная DVCS, в которой они есть — Git. Причина существования понятия detached HEAD — в том, что в Гите считаются значимыми только и исключительно те ветви DAG версий репозитория, на которые указывает какой-нибудь ref (branch или tag). В противном случае ветвь считается мусором и будет удалена git-gc. В Mercurial, bzr и прочих негитах все ветки DAG версий равноправны. И это нормально, т.к. проще.
                • 0
                  Git мне чем-то напоминает регулярные выражения. Когда понимаешь, как он работает, ты можешь делать все, что угодно очень легко и изящно.

                  хорошо сказано! ))
                  • +1
                    Только хрен кто поймёт, что и как ты сделал. Результат есть, а процесс, как говорится, write-only.
                    • 0
                      что означает «вы не понимаете регулярок» :))))))))
                      мдааа… вот уж не предполагал, что и среди DVCS могут быть холивары!
                      • +4
                        Если вы пытаетесь решить проблему регулярками, то проблем у вас уже две ;)
                        Комбинаторные парсеры не пробовали?
                        • 0
                          не, не пробовал
                          честно говоря, от Вас первый раз про такое слышу ))) почитаю

                          но у меня и нет проблем ни с регулярками, ни с Git'ом ))))
                  • +1
                    А мне Git напоминает стеклянную банку кетчупа с узким горлом, к которой прилагается специальная ложечка и книжка в 200 страниц о том, как правильно его выковыривать.

                    В то время как можно его просто выдавливать. Ок, процесс выдавливания, возможно, не столь эстетичен, и что-то даже теряется по дороге. Но как-то зато быстро и вкусно.
                    • 0
                      хм… вообще-то в Git как раз в полный рост TMTOWTDI
                      даже закоммитить нет единственного способа:
                      git add --update && git commit vs git commit --all

                      З.Ы. а вот «потерять по дороге» в Git непросто, да )) и в этом-то и смак
                      • +1
                        Способ по‐прежнему единственный — git commit. Просто как и с git checkout -b налицо прямое нарушение принципа разделения обязанностей: git commit --all с какой‐то радости что‐то делает с индексом (для чего есть тот же git add), git checkout -b с какой‐то радости создаёт новую закладку (для чего есть git branch).

                        Вы ещё можете вспомнить про команды более низкого уровня (вроде git commit-tree), но «спуск на уровень ниже» ≠ «нахождению другого способа что‐то сделать». В некоторой степени пару git add && git commit можно рассматривать как что‐то на уровень ниже git commit --all. Или, скорее, второе как alias первого.
                        • +1
                          я (честно!) не знаю, что ответить на такие претензии (как по мне, так формальные) к SCM, суть которой всё же несколько в другом, нежели синтаксис её команд
                          да, я заметил, что Git — не для всех, а так судя по таким холиварам — вообще для гикнутых гиков…
                          очевидно — непрост. вероятно — сложен. но бесконечно мощен и прекрасен )))
                          и тут меня не переубедить ))
                          • +1
                            бесконечно мощен и прекрасен )))
                            то-то его никак в виде shared library оформить не могут. у libgit2, правда, почему-то получается. ненастоящие гики там, видать.
                            • 0
                              это проблема функционала? или всё же архитектуры?
                              каким образом меня, как пользователя, касается внутренняя красота кода Git (хотя я отправлял пару патчей)?

                              *а зачем Вам Git в shared library?

                              • 0
                                архитектуры. в GUI не встроить нормально (только exec), сложить репозиторий не на диск вообще принципиально нельзя и так далее.

                                ну как зачем. чтобы не изобретать велосипед, если в моей, к примеру, CAD потребуется полноценное версионирование документа проекта и в то же время не быть принуждённым к хранению списков вершин в plain text.
                                • 0
                                  ну, то есть «очень хочется Git, но невозможно»?
                                  может, посмотреть на другие DVCS? вот пресловутый Hg можно так использовать?
                                  • 0
                                    У mercurial довольно стабильный внутренний API, но официально, если вам нужно куда‐то встроить mercurial, поднимайте command server и общайтесь с ним, передавая команды типа commit (т.е. то же, что и через командную строку). hg … — тоже официальный внешний API, как и command server (преимущества command server — нужен один fork+exec и одна инициализация Python). А внутренний только для дополнений, остальное на свой страх и риск.

                                    А сложение репозитория не на диск к наличию shared library вообще никаким боком не относится. В libgit2 такое сделали, но это не потому, что они shared library, а потому что у них архитектура не такая кривая. С обычным git такое тоже можно было бы учудить, создав механизм а‐ля модули в zsh (т.е. подгружаемые библиотеки, при том API позволяет сделать множество интересных вещей: я, к примеру, делал возможность использовать Python непосредственно в процессе оболочки, а кто‐то добавил через модули переменные, ограниченные одной функцией (обычно локальные переменные доступны для вызываемых функций, zsh/param/private добавляет локальные переменные, недоступные для вызываемых функций)).
                          • +2
                            Про «мощен» я уже не раз говорил: mercurial позволяет запихать в себя всё, что угодно, только пишите дополнения, архитектура позволяет, а хорошие идеи авторы примут. В git вам что дали, с тем вы и сидите. Все дополнения — мелкая косметика. Зато есть куча адептов, кричащих о его мощи.

                            У mercurial есть мощь: evolve, ветки, закладки, фазы, возможности в протоколе, позволяющие, к примеру, потребовать от пользователя включить largefiles до клонирования (git — аналог работает на gitattributes, отказ происходит после clone) или полностью проигнорировать фазы (для старых клиентов). И всё это развивается со временем. Бесконечностью пахнет только здесь, где вы вставляете в ядро такой сложный концепт, как obscolescence markers (этот тот самый evolve)… с помощью дополнения. Дополнения, устанавливаемого любым пользователем, а не отдельной веткой с новой функциональностью и с необходимостью пользователю иметь квалификацию и время на сборку и установку с решением сопутствующих проблем.

                            У git есть своя мощь, но нет ни инфраструктуры для её наращивания, ни развития. Фактически, единственное, что мне совершенно не хватает в mercurial — удобное редактирование истории — станет ущербным в сравнении с mercurial после evolve в релизе с поддержкой его bitbucket’ом. Может libgit2 здесь что‐то когда‐нибудь исправит (насколько я помню, там, как минимум, есть возможность полностью заменить принцип хранения данных (т.е., к примеру, поместить всё в базу данных)). Но я бы не надеялся на libgit2 в upstream.
                            • 0
                              ну, кому что ))
                              у Hg, как видим, тоже есть куча адептов ))) и каждый по-своему прав )) и слава Б )))
                      • 0
                        > а вот «потерять по дороге» в Git непросто, да
                        Правда? Отсоединенный коммит получить не просто, а очень просто. Да, с помощью командной строки, рефлога и какой-то матери все можно вернуть до сборки мусора, но осадочек остается.

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

                        • 0
                          Отсоединенный коммит получить не просто, а очень просто.

                          ну и что? Вы его ПОТЕРЯЛИ при этом? совсем? навсегда?

                          Да, с помощью командной строки, рефлога и какой-то матери все можно вернуть до сборки мусора

                          ну, видите же,. — можно! ))) для меня это — ключевое слово.
                          удалённый файл в NTFS/FAT32 восстановить же можно? можно! только спецтулзами… только в FAT32 они могут похериться «сами просто так», а отличие от NTFS…

                          а как часто у вас сборщик мусора вызывается?
                          а то я вот активно делаю рибейсы, а вот сборщик раз в 3 месяца, дай Б, вижу

                          как по мне, так в итоге все «недостатки» Git'а сводятся к его непониманию ))) а я его понимаю — и тут, извините, я на коне )))

                          З.Ы. мне это напоминает чем-то жён, которые не хотят ездить на машине с «ручкой», а только на автомате:
                          «там всё так сложно, надо одновременно сцепление и газ… чуть не так — заглохнет»… и т.п. по мне — так «дело вкуса»… благо — альтернативы есть )))
                          • +1
                            ну и что? Вы его ПОТЕРЯЛИ при этом? совсем? навсегда?
                            в логе такого коммита не видно, поэтому если не знать (забыть), где именно искать, то вполне можно считать потерянным. а после gc — ещё и навсегда.
                            а как часто у вас сборщик мусора вызывается?
                            а он автоматически вызывается при merge и fetch. проявляет себя словами «Auto packing the repository…»
                            а я его понимаю — и тут, извините, я на коне )))
                            это пройдёт, когда поймёте на самом деле :)
                            • 0
                              Напоминаю, что сборщик мусора всегда ограничивается только старыми изменениями. Насколько старыми задаётся в настройках, но по‐умолчанию это две недели (см. gc.pruneExpire в git help config).
                            • –2
                              :)
                            • +1

                              Напоминаю, что по умолчанию рефлоги хранятся и не трогаются сборщиком мусора аж 90 дней.


                              С трудом представляю себе ситуацию, когда программист пишет код, коммитит его в виде отсоединенного коммита — и забывает про него на 3 месяца, после чего жалуется что гит плохой :)

                        • +1
                          Ну и как часто вы получали отсоединённый коммит? Лично я — чуть чаще чем никогда.
                          • 0
                            Новичку такое сделать просто. Прогнали git bisect и начали исправлять проблему, не сделав git bisect reset. Решили поработать в «ветке» origin/master вместо master. Решили посмотреть «как оно там в изменении deadbeef» и забыли сделать checkout обратно в ветку. Решили поработать в «ветке» pulls/42/head. Я такого уже почти никогда не допускаю, но «прострелить себе ногу» вполне можно.

                            (Кстати, в powerline я вполне себе делаю отсоединённое изменение намеренно. Сливать их с develop вполне можно, ссылаясь как HEAD@{1}, а создавать имена для веток не всегда охота: получается что‐то вроде временных веток для простых (т.е. не требующих прогона тестов) изменений вроде изменений в документации.)
                            • 0
                              Новичок станет делать git bisect? Если человек уже узнал про git bisect и начал его использовать, то вряд ли он «новичок». А за git checkout origin/master без понимания процесса нужно бить по рукам. Ну и в случае git checkout который приводит к detached head гит выдаёт здоровенный текстовый баннер с подробным описанием, что вы сейчас в «detached HEAD», что нужно сделать «git checkout -b branch» чтобы сохранить изменения и т.д., и т.п. Так что тут ещё и нужно не уметь читать, чтобы так лопухнуться.
                              • +4
                                Новичок в git и новичок в DVCS — две большие разницы.
                                А помимо консоли есть еще и GUI.
                                Вдобавок в том же мерке анонимная ветка сама по себе абсолютно нормальна, и совершенно непонятно, с какого перепугу гит считает ее мусором.
                              • +2
                                А почему нет? Помимо того, что есть mercurial, в котором тоже есть bisect, ещё существует вариант, когда человек приходит в issue tracker, говорит о своей проблеме, а его просят сделать bisect. Чтобы его сделать не нужно читать умных книг про git, нужны просто минимальные навыки поиска в Google или прочтение части git help bisect (кстати, слово «detached» здесь нигде не упоминается). Кстати, git checkout -b branch во многих случаях не является правильным способом решить проблему: в случае того же bisect вы вполне можете хотеть зафиксировать изменение в какой‐то существующей ветке‐закладке. И почти наверняка хотите иметь какую‐то закладку в качестве родителя, а не изменение где‐то посередине. И проще всего первое делать через stash→checkout→stash apply (второе, впрочем, через checkout -b→rebase). Впрочем, это неважно, т.к. вместо большого предупреждения при commit вы получите просто [detached HEAD {hash}] msg… на первой строке из трёх.

                                И да, хороший интерфейс не тот, который выплёвывает вам в лицо длинные предупреждения. А тот, где предупреждения не требуются. А про предупреждение, кстати, можно отвлечься на что‐то и забыть.
                          • +3
                            Не хвататься за оголенные провода несложно.
                            Понять, почему на обычном рабочем месте изоляция на проводах отсутствует, гораздо сложнее.
                            Еще сложнее понять тех, кто продает все это как достоинства инструмента.
                • 0
                  Поскольку никто не объяснил, то мне кажется я понял сам. Не уверен, что правильно, и конечно, слегка провакационно (может хоть так спровоцирую на разъяснение), но по крайней мере в моей вселенной все с таким объяснением встает на свои места:
                  Мерк — попытка не разобравшись с логикой Git сделать все так, как было в Svn — чтобы ветки были железобетонные, чтобы add добавлял в репозиторий, а не в коммит,… Я через это проходил при миграции, и мне кажется это неправильно. Git лучше, чем Svn, а динамические ветки лучше предопределенных — гораздо продуктивнее научиться пользоваться новым инструментом вместо того, чтобы продолжать мыслить старой парадигмой.
                  • +1
                    Чем ветки мерка «старее» веток гита? Чем они хуже? Я начал использовать svn гораздо позже гита и мерка, его идеология надо мной не довлела, я изучал обе современные системы параллельно, ничего не зная про устаревшую. Подход мерка мне кажется разумнее: лучше хранить больше информации о коммите, чем меньше — игнорировать проще чем, строить догадки.
                  • 0
                    Если вы не знали, при старте обоих проектов (Git и mercurial) исходные данные были для обоих одинаковыми. Для исходников Linux был выбран Git по вполне понятным причинам. Соответственно и для других подобный авторитет оказался решающим.
                  • +2
                    Мерк — попытка не разобравшись с логикой Git сделать все так, как было в Svn
                    mercurial, как и Git, изначально создавался для того, чтобы хранить в нем исходники Linux. разработка обоих началась в 2005 с разрывом в месяц, что ли.
                    Изучите матчасть для начала.
                    • –2
                      Спасибо за информацию.
                      Ну значит был неправ, но только в плане «не разобравшись с логикой Git». Тем более верно мое ощущение и предположение что логика Svn в мерке присутствует (поскольку на момент разработки обоих версий Svn был мэйнстримом), в отличие от Git, который реализует совсем другую концепцию.
                      А то, что Git при этом стал более популярным только говорит в его пользу.
                      У меня ощущение дежа-вю с ситуацией mootools + jQuery. Mootools был очень приятным внутри, с классами и архитектурой, но jQuery был проще, гибче и имел очень удобный механизм поиска элементов на странице (такая же удобная система веток и в Git — работа с ветками очень легкая, гибкая и простая) и за счет этого стал популярнее. До сих пор с ужасом вспоминаю процесс миграции проектов в mootols на jQuery. Лучше сразу делать правильный выбор.
                      Да, скорее всего, мерк внутри красивее — об этом много слышу, но по складывающейся ситуации этого явно недостаточно и вряд ли ситуация уже изменится.
                      Тем не менее хорошо, что он есть, поскольку конкуренция в любой среде — это хорошо.
                      • +3
                        А то, что Git при этом стал более популярным только говорит в его пользу.

                        Если бы святой Торвальс поддержал HG, то ситуация была бы иной.
                        Git — работа с ветками очень легкая, гибкая и простая

                        О лёгкости работы в git написано уже 100500 статей.
                        А вот Джобс считал, что хорошему интерфейсу мануалы не нужны. В моей практике, чтобы научить человека hg, достаточно поставить UI и показать как оно работает. Чтобы научить человека git-у, нужно «понять» гит в консоли.
                        • +2
                          Поймите, если бы Git был столь ужасен, как вы пишите, никакой святой Торвальдс не смог бы сделать его популярным, тем более, при наличии такого великолепного конкурента.
                          • +4
                            А я и не говорю, что git ужасный. Ему просто не повезло из тула для написания линуксов стать индустриальным стандартом.
                      • 0
                        Тем более верно мое ощущение и предположение что логика Svn в мерке присутствует (поскольку на момент разработки обоих версий Svn был мэйнстримом), в отличие от Git, который реализует совсем другую концепцию.
                        1. svn может и был mainstream, да только bitkeeper (на замену которому разрабатывались git и hg) был как раз DVCS.
                        2. что Git, что Mercurial оперируют DAG версий репозитория. Другой вопрос, что в Git присутствуют дополнительные ограничения вроде «головы, на которые никто не ссылается — мусор» или «не будем записывать изменения, будем хранить снапшоты». «Логика svn» в hg присутствует разве что в мере «стараться не мешать пользователю работать работу». Единственная претендующая на уникальность «концепция», реализованная в git — «оно здесь так само выросло»
                        Да, скорее всего, мерк внутри красивее
                        забавно, что одним только гитоводам не всё равно что у контроля версий внутри.
                        • +1
                          Я бы согласился с тем, что «логика svn присутствует», потому как после svn начать использовать мерк очень просто. То есть, базовые действия не вызывают затруднений, и даже распределённая структура не вызывает.

                          Один адепт мерка буквально говорил, что svn — говно. Я просил объяснить, чем же он отличается от мерка, в его понимании, кроме того, что кроме перед update в мерке он вводит pull, а после commit — push. Не смог он ничего объяснить — воркфлоу был идентичный, распределённая структура не использовалась, ветки в мерке не использовались (до них в то время ещё было как до луны); да если бы они и использовались, это было бы сделано по модели svn. Наезды были в духе «потому что». А, ещё он однажды заблочил базу коллизий svnа, создав в рабочей копии файлы от другой учётной записи, так что у svnа не было прав с ними что-то делать, и он от греха заблокировался. Я вот утверждаю, что в этой ситуации «сам дурак», а svn всё правильно сделал.

                          Чего нельзя сказать про гит. Гит кажется невменяемым после любой другой vcs. Пока не почитаешь документацию (и не поломаешь голову) — не осилишь.
      • –1
        Так я его и так почти боготворю!
  • +7
    > он перерос VCS, которую использовал, поэтому Линус решил написать новую, т.к. он программист, а именно этим мы и занимаемся.
    А я думал, что это потому, что их VCS отказала линуксоидам в когда-то выданной бесплатной лицензии за то, что ребята попытались её хакнуть…
    • –3
      пруф будет?
      • +7
      • 0
        Пруф дали, пока шёл до планшета
        Историю надо знать ;)
        • 0
          теперь знаю
  • +1
    не могу понять как это работает
    git-scm.com/docs/git-request-pull
    знает кто? я всегда думал что нельзя делать пул-реквесты напрямую из дефолтной консоли
    • 0
      эта команда — неполный аналог hg incoming
  • –1
    git clone github.com/funny_guy/just_for_lulz_code
    Cloning into 'just_for_lulz_code'…
    remote: Repository not found.
    fatal: repository 'https://github.com/funny_guy/just_for_lulz_code/' not found
    • +2
      Попробуйте другой пример, должно сработать:
      git clone https://github.com/torvalds/linux
      
      :)
  • 0
    Статья интересна но написана сложно, в комментах много споров на тему 2 + 2 = 4? Вы уверены?
    Все что нужно для GIT это прочитать книгу -> Pro Git book или скачать PDF вариант.
  • 0
    Спасибо за перевод.
  • 0
    Для тех, кто хочет разобраться, как работает git, но лучше соображает с картинками, есть замечательная «Наглядная справка по git»: marklodato.github.io/visual-git-guide/index-en.html
  • +12
    Я не знаю как они работают, но пользователи Mercurial рассказывали мне, что это такая боль в заднице, что никто на самом деле их больше не использует.

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

    image

    Даже джуниоров без проблем обучаем работе с ветками. Никто в команде ни разу не сталкивался ни с какими проблемами, кроме конфликтов. Быть может, всё дело в волшебном TortoiseHG, который работает так как ты этого ожидаешь с первого раза даже без чтения документации. Я уж не говорю про статьи на хабре на 20 (!!!) страниц.

    К нам в команду часто приходят фанаты и адепты ГИТа и очень неохотно переучиваются на мерк. Я вижу как людей это злит и бесит. Но никто ещё, ни один человек не смог объяснить мне чем гит лучше? Какие функции есть в гите, которых нет в мерке? В мерке есть всё, но реализовано гораздо проще. ГИТ — это первая изобретённая Dcvs. Но не идеальная, как первый блин. Мерк — это, можно сказать, форк от гита в котором реализовали всё тоже самое, но значительно проще.

    К моему сожалению, эта не удобная система стала более популярна но явно не благодаря своей простоте. Возможно дело в эко-системе? Популярный Линус и ещё более популярный GitHub сделали своё дело, но не сам гит.

    Понимаю, что сейчас минусов нахватаю за не популярную точку зрения :(
    • 0
      Аналогично сложно объяснить чем Мерк принципиально лучше Гита.
      Привычка.
      • +1
        Если не считать эко-систему и GitHub, то сам по себе git ничем не лучше мерка, потому что в мерке есть весь тот же самый функционал, но проще.
      • 0
      • +1
        Есть информация в каждом коммите, из какой ветки он пришел. В команде из 10+ человек бывает очень полезно.
        • 0
          Так и в гите есть. И не надо говорить про rebase и cherry-pick как будто статью не читали.
          • +2
            В гите таки нет. Коммит хранит ссылку на родительские коммиты и все. «Ветка» гита — ссылка на коммит и не более того.
            • 0
              но коммит-то + все его родители — это и есть сама «ветка изменений» (а нам ведь онА важна), и она видна, и доступна
              а как она называлась — до этого дело, видимо, только меркуриаловцам…
              • +3
                Нет, коммит+все его родители — это не ветка изменений. Пример: я только что-то сделал ветку от другой ветки — сейчас у меня в ветке ноль коммитов, гит же считает, что моя ветка все коммиты до первого. Гит не знает ответвлялся я от коммита в ветке дев или мастер — для него дев и мастер когда указывают на один коммит абсолютно равнозначны.
                • 0
                  это так, да…
                  только в чём тут проблема?
                  • +1
                    Гит не хранит истории работы с ветками. Для кого-то это проблема, которую надо решать другими способами (например, заставлять разработчиков писать название ветки в коммите и бить по рукам за то не указывают, или указывают неверно), для кого-то неудобство, кому-то всё равно, а кому-то может даже эта информация мешать будет.
                    • 0
                      полагаю, все претензии рода, подобного обсуждаемому, к SCM — сублимация претензий к разрабам, не следующим гайдам «Как писать хорошие сообщения коммитов»

                      за сообщения коммитов ваще драть надо ))))
                      • 0
                        Это скорее претензии разрабов: у SCM есть информация о том, в какой ветке я делаю коммит, но она в коммит не пишет, потому меня ПМ заставляет руками писать.
                        • 0
                          ну, раз уж, используя Git, нужно имя ветки в сообщении коммита, чтобы не писать руками, можно/нужно использовать prepare-commit-msg hook

                          а как выглядят сообщения коммитов у вас? *интересно
                          • 0
                            Хуки — штука хорошая, но для данного случая, пожалуй, оверкилл.
                            • 0
                              ну, лучше, конечно, сетовать на SCM, что она не хранит имена веток и ручками каждый раз писать, да )))
                              • 0
                                Зачем сетовать? Можно взять SCM, которая для такой простейшей вещи хуков не требует.
                      • +1
                        В случае гита гайд толще, а сообщения многословнее, потому что приходится дублировать в сообщении коммита информацию, которая уже есть в имени ветки.
                        • 0
                          видимо, вам хватает ли вам одной лишь имени ветки, чтобы узнать что, зачем да почему…
                          я же предпочитаю (как рекомендуют, а в разработке самого Git — требуют), знать это из сообщения коммита
                          и когда надо по быстрому глянуть, мне такой лог
                          Заголовок спойлера
                          git log --graph --oneline
                          *   c43413e Merged: fixed: #644: Ошибка при рассылке e-mail.
                          |\  
                          | * e25152f fixed: #644: Ошибка при рассылке e-mail.
                          | * edbbd74 issue #644: improve: tests: add TMailSender.InitService[Params] tests
                          | * 2befdc3 fixed: tests: fake SMTP-server: possible "stream is closed" error
                          | * 266c11c refactoring: tests: fake SMTP-server: add a space
                          | * 8d1b795 refactoring: tests: fake SMTP-server: log incoming request BEFORE its processing
                          | * ebcb002 issue #644: refactoring: tests: fake SMTP-server: add tests
                          | * 30157bf issue #644: refactoring: tests: fake SMTP-server: rename authenticator classes
                          | * 6dbd039 issue #644: refactoring: tests: fake SMTP-server: lib/auth: introduce SMTP module
                          |/  
                          * c096916 fixed: #472: Ошибка при установке АС.
                          *   c5d7dee Merged: issue #644: improve: tests: добавить тестовыe SMTP-серверы с LOGIN-аутентификацией
                          |\  
                          | * b2b96c7 issue #644: improve: tests: добавить тестовыe SMTP-серверы с LOGIN-аутентификацией
                          | * 6909136 issue #644: fixed: tests: тестовый SMTP-server PLAIN не работает, если параметр задавать позже
                          | * cb12e39 issue #644: refactoring: tests: fake SMTP-server
                          | * 4cafc56 issue #644: fixed: tests: тестовый SMTP-сервер с PLAIN аутентификацией "пускает" с пустыми логином и паролем
                          | * 905d7d6 issue #644: improve: tests: fake SMTP-servers: добавить ключ запуска --extend-with-shutdown
                          | * d96eef1 issue #644: improve: tests: fake SMTP-servers: добавить ключ запуска --debug
                          | * c96652f issue #644: improve: introduce EUnknownMessageSenderException
                          | * fdc3693 issue #644: tests: fake SMTP-server: уточнить текст ошибки при неверном методе аутентификации
                          | * af2375e issue #644: refactoring: tests: fake SMTP-server: rename classes (remove "T" prefix)
                          | * 577ed4b issue #644: improve: tests: avoid processor load
                          | * a45a153 issue #644: refactoring: tests: fake SMTP-server: add module SMTP
                          |/  
                          * 8baa7d6 refactoring: tests: avoid compiler warning
                          * 87913f6 issues #624, #625, #637: rake: add "prepare:accompanies:mail:..." tasks
                          *   4a44395 Merged: fixed: #643: Пропала компенсация из списка.
                          |\  
                          | * 02065f2 fixed: #643: Пропала компенсация из списка.
                          | * 7be70bd issues #607, #643: improve: добавить скрипт для анонимизации БД
                          |/  
                          *   206e9d3 Merged: refactoring: update lib/CF, lib/Common
                          |\  
                          | * aa2035b refactoring: update lib/CF, lib/Common
                          | * 7c90634 fixed: TUpdater зависает, если при вызове .Start происходит исключение
                          | * b55d639 refactoring: убрать директивы условной компиляции Indep и AladdinSupport
                          | * ba54807 refactoring: убрать директиву условной компиляции ASK_GROUP_SAVE
                          |/  
                          * 188a580 refactoring: tests: use const keyword for string parameters
                          * bb51653 fixed: tests: `TTestTimeoutsSetup` is shown as `imeouts`
                          

                          оно же, но полное:
                          git log --graph --decorate
                          Скрытый текст
                          *   commit c43413eb9199a3797157d821a366f051e8703758 (HEAD -> devel, tag: v3.0.157, origin/branch-v3.0)
                          |\  Merge: c096916 e25152f
                          | | Author: ***Developer***
                          | | Date:   Thu Oct 22 11:19:45 2015 +0300
                          | | 
                          | |     Merged: fixed: #644: Ошибка при рассылке e-mail.
                          | |   
                          | * commit e25152f57a18d25dff44d06858bc46ceb0c77ac0
                          | | Author: ***Developer***
                          | | Date:   Thu Oct 22 11:19:23 2015 +0300
                          | | 
                          | |     fixed: #644: Ошибка при рассылке e-mail.
                          | |     
                          | |     При рассылке e-mail используется тип авторизации AutoSelect, что значит,
                          | |     что авторизация БУДЕТ выполняться, если сервер её поддерживает, даже
                          | |     если логин/пароль не заданы.
                          | |     До сих пор это работало. Пока не появился клиент с сервером, который
                          | |     поддерживает и авторизацию, и анонимную рассылку, и этот сервер постоянно
                          | |     запрашивает имя пользователя (которое не задано, т.к. пользователь
                          | |     знает, что сервер может рассылать и анонимно)
                          | |     (см. 6909136 (issue #644: fixed: tests: тестовый SMTP-server PLAIN не
                          | |     работает, если параметр задавать позже, 2015-10-19)).
                          | |     
                          | |     Доработаем АС так, что он при будет пытаться авторизоваться на
                          | |     SMTP-сервере (с типом AutoSelect), только если задан логин.
                          | |     
                          | |     З.Ы. А в тестах нужно доработать SMTP-серверы так, чтобы они
                          | |     обрабатывали аутентификацию одной сессии за раз, и могли поддерживать и
                          | |     анонимную рассылку тоже (по флагу).
                          | |   
                          | * commit edbbd74f03550a6a47560ecc59a1c848cab817f5
                          | | Author: ***Developer***
                          | | Date:   Wed Oct 21 18:46:59 2015 +0300
                          | | 
                          | |     issue #644: improve: tests: add TMailSender.InitService[Params] tests
                          | |   
                          | * commit 2befdc3a7325bc71dfd10b3760ce58952bdd3b3b
                          | | Author: ***Developer***
                          | | Date:   Wed Oct 21 17:51:34 2015 +0300
                          | | 
                          | |     fixed: tests: fake SMTP-server: possible "stream is closed" error
                          | |     
                          | |     Обычно выход из цикла обработки входящих команд происходит по команде,
                          | |     но может и по закрытию потока. Однако, в этом случае запись в поток
                          | |     вызовет ошибку, чего нам не надо.
                          | |     
                          | |     Будем проверять поток на закрытие перед попыткой в него писать.
                          | |   
                          | * commit 266c11c41563022386bd2a78a3ef8d5c48196604
                          | | Author: ***Developer***
                          | | Date:   Wed Oct 21 17:50:13 2015 +0300
                          | | 
                          | |     refactoring: tests: fake SMTP-server: add a space
                          | |   
                          | * commit 8d1b795c5aece2d463e97c26963d801a1a4420d7
                          | | Author: ***Developer***
                          | | Date:   Wed Oct 21 17:49:12 2015 +0300
                          | | 
                          | |     refactoring: tests: fake SMTP-server: log incoming request BEFORE its processing
                          | |   
                          | * commit ebcb00245337614cca4ad2a38b88102c42a4f7d5
                          | | Author: ***Developer***
                          | | Date:   Tue Oct 20 14:49:47 2015 +0300
                          | | 
                          | |     issue #644: refactoring: tests: fake SMTP-server: add tests
                          | |     
                          | |     Add missing tests.
                          | |   
                          | * commit 30157bf5a02e4da8a3939353e5de225ee77171dd
                          | | Author: ***Developer***
                          | | Date:   Tue Oct 20 14:37:42 2015 +0300
                          | | 
                          | |     issue #644: refactoring: tests: fake SMTP-server: rename authenticator classes
                          | |     
                          | |     Remove "T"-prefix.
                          | |   
                          | * commit 6dbd039f033f80d7dda5e8b14a77affb5b973802
                          |/  Author: ***Developer***
                          |   Date:   Tue Oct 20 14:33:00 2015 +0300
                          |   
                          |       issue #644: refactoring: tests: fake SMTP-server: lib/auth: introduce SMTP module
                          |  
                          * commit c096916c2fa92ac02a883f6f60e4393d1abaaee3 (tag: v3.0.156)
                          | Author: ***Developer***
                          | Date:   Wed Oct 21 14:13:32 2015 +0300
                          | 
                          |     fixed: #472: Ошибка при установке ...
                          |     
                          |     Решили усложнить пароль для пользователя "..." при установке MS
                          |     SQL-сервера, т.к. на серверах часто включены политики сложного пароля.
                          |     
                          |     Пароль "...." вполне удовлетворяет самым строгим политикам.
                          |    
                          *   commit c5d7dee3bbba6538b9d844bb47709889e2ca3021
                          |\  Merge: 8baa7d6 b2b96c7
                          | | Author: ***Developer***
                          | | Date:   Mon Oct 19 15:37:14 2015 +0300
                          | | 
                          | |     Merged: issue #644: improve: tests: добавить тестовыe SMTP-серверы с LOGIN-аутентификацией
                          | |   
                          | * commit b2b96c7a3ac662fe50ad9787fa6d85fdfc04e110
                          | | Author: ***Developer***
                          | | Date:   Mon Oct 19 15:35:59 2015 +0300
                          | | 
                          | |     issue #644: improve: tests: добавить тестовыe SMTP-серверы с LOGIN-аутентификацией
                          | |     
                          | |     Один из них симулирует поведение сервера, который в ответ на MAIL FROM
                          | |     выдаёт 334 Password: при попытке анонимной отправки.
                          | |   
                          | * commit 69091362a1bba12c8f2f06e18a6a1a2e8b11e270
                          | | Author: ***Developer***
                          | | Date:   Mon Oct 19 13:14:06 2015 +0300
                          | | 
                          | |     issue #644: fixed: tests: тестовый SMTP-server PLAIN не работает, если параметр задавать позже
                          | |     
                          | |     Сервер с PLAIN аутентификацией работает, только если использовать
                          | |     параметр во время команды
                          | |       AUTH PLAIN <...>
                          | |       235 Authentication successful
                          | |     
                          | |     но НЕ если захотим использовать схему
                          | |       AUTH PLAIN
                          | |       334
                          | |       <...>
                          | |       235 Authentication successful
                          | |     
                          | |     И хотя наш SMTP-клиент использует первый вариант, исправим это, т.к.
                          | |     дальше мы добавим LOGIN-аутентификацию, которая работает по похожей
                          | |     схеме.
                          | |     Заодно уберём текст "Username:" в ответе 334.
                          | |   
                          | * commit cb12e395078cbeee22d5c492d98a5bf90b0fa4eb
                          | | Author: ***Developer***
                          | | Date:   Fri Oct 16 17:32:24 2015 +0300
                          | | 
                          | |     issue #644: refactoring: tests: fake SMTP-server
                          | |     
                          | |     Rename @pass to @password. Initialize @login and @password.
                          | |   
                          | * commit 4cafc56b15cdb287e3c6c6869ed0dccdff2caf9e
                          | | Author: ***Developer***
                          | | Date:   Fri Oct 16 15:23:40 2015 +0300
                          | | 
                          | |     issue #644: fixed: tests: тестовый SMTP-сервер с PLAIN аутентификацией "пускает" с пустыми логином и паролем
                          | |     
                          | |     Хотя не должен, должен пускать с непустым логином (и, как и раньше,
                          | |     паролем, равным логину).
                          | |   
                          | * commit 905d7d6103bde4f37ec446f8f5c1f95b49103c5f
                          | | Author: ***Developer***
                          | | Date:   Mon Oct 19 12:20:50 2015 +0300
                          | | 
                          | |     issue #644: improve: tests: fake SMTP-servers: добавить ключ запуска
                          | |     --extend-with-shutdown
                          | |     
                          | |     В тестах используется команда SHUTDOWN, которая завершает работу сервера
                          | |     (чтобы не приходилось "прибивать" процессы). Клиент посылает, а сервер
                          | |     корректно обрабатывает эту команду.
                          | |     Однако, иногда приходится запускать скрипт smtp-server.rb "вручную", и
                          | |     тогда не очень нужно, чтобы серверы завершали работу после "прогона"
                          | |     тестов.
                          | |     Так что сделаем опцией команду SHUTDOWN.
                          | |   
                          | * commit d96eef1a70633e28e249a1011ee686b94b368f79
                          | | Author: ***Developer***
                          | | Date:   Fri Oct 16 15:24:59 2015 +0300
                          | | 
                          | |     issue #644: improve: tests: fake SMTP-servers: добавить ключ запуска --debug
                          | |   
                          | * commit c96652fb687c17744e50ba5d1f60d987717fdc5d
                          | | Author: ***Developer***
                          | | Date:   Mon Oct 19 13:56:26 2015 +0300
                          | | 
                          | |     issue #644: improve: introduce EUnknownMessageSenderException
                          | |     
                          | |     В тестах нужно определять, что ошибка неизвестная, а та же
                          | |     EAuthSenderException, например, - наследник от EMessageSenderException,
                          | |     что не даёт уверенности, что возникла не она. Поэтому нужно ввести
                          | |     специальный тип исключения - EUnknownMessageSenderException.
                          | |   
                          | * commit fdc3693263f1768ef62d037850f50f304ded71a6
                          | | Author: ***Developer***
                          | | Date:   Fri Oct 16 14:44:44 2015 +0300
                          | | 
                          | |     issue #644: tests: fake SMTP-server: уточнить текст ошибки при неверном методе аутентификации
                          | |     
                          | |     При неверном методе аутентификации выдаётся ошибка с текстом "Unknown
                          | |     command", хотя лучше выдавать "Unknown authentication type".
                          | |   
                          | * commit af2375ea04e4d51494bb352a40ef77a60e0fe2bf
                          | | Author: ***Developer***
                          | | Date:   Mon Oct 19 12:00:18 2015 +0300
                          | | 
                          | |     issue #644: refactoring: tests: fake SMTP-server: rename classes (remove "T" prefix)
                          | |   
                          | * commit 577ed4b4b173a30ff13e2a97ebc0b3d37f12311e
                          | | Author: ***Developer***
                          | | Date:   Mon Oct 19 12:58:57 2015 +0300
                          | | 
                          | |     issue #644: improve: tests: avoid processor load
                          | |     
                          | |     Sleep in a loop
                          | |   
                          | * commit a45a1536176b30801ac2820b2e9403bb3fd8b1af
                          |/  Author: ***Developer***
                          |   Date:   Mon Oct 19 11:54:55 2015 +0300
                          |   
                          |       issue #644: refactoring: tests: fake SMTP-server: add module SMTP
                          |  
                          * commit 8baa7d698231cbb880a2ab5ed54e721ad270268f
                          | Author: ***Developer***
                          | Date:   Mon Oct 19 15:30:44 2015 +0300
                          | 
                          |     refactoring: tests: avoid compiler warning
                          |  
                          * commit 87913f6f4263d02bb3456d6ebeba39d6b6fa1d1f
                          | Author: ***Developer***
                          | Date:   Tue Oct 6 12:53:30 2015 +0300
                          | 
                          |     issues #624, #625, #637: rake: add "prepare:accompanies:mail:..." tasks
                          |     
                          |     Google Mail не позволяет отсылать исполняемые и командные файлы даже в
                          |     .zip-архивах. Поэтому их нужно переименовывать, добавляя, например, знак
                          |     подчёркивания суффиксом. Автоматизируем это.
                          |    
                          *   commit 4a443957ef3b946a76876335d4fe4ef3de961d69 (tag: v3.0.155)
                          |\  Merge: 206e9d3 02065f2
                          | | Author: ***Developer***
                          | | Date:   Tue Oct 13 15:19:01 2015 +0300
                          | | 
                          | |     Merged: fixed: #643: Пропала компенсация из списка.
                          | |   
                          | * commit 02065f2756b834507961a57cc68e8d1951e4a9e0
                          | | Author: ***Developer***
                          | | Date:   Tue Oct 13 12:52:40 2015 +0300
                          | | 
                          | |     fixed: #643: Пропала компенсация из списка.
                          | |     
                          | |     Если при добавлении/редактировании компенсации её название совпадёт с
                          | |     наименованием НАЧИСЛЕНИЯ, и после сообщения об ошибке об этом исправить
                          | |     название компенсации и сохранить её, то она "пропадёт" из дерева
                          | |     компенсаций.
                          | |     Происходит это потому, что при проверке на дубликат названия, получается
                          | |     и тип этого дубликата (если он есть), и тип компенсации ошибочно
                          | |     изменяется на этот тип, что сказывается при её сохранении.
                          | |     
                          | |     Исправим это, не изменяя тип компенсации при проверке на дубликат.
                          | |   
                          | * commit 7be70bd51a2d35280fb64519e3c79f6f62a64497
                          |/  Author: ***Developer***
                          |   Date:   Wed Jun 3 11:05:16 2015 +0200
                          |   
                          |       issues #607, #643: improve: добавить скрипт для анонимизации БД
                          |       
                          |       Некоторые абоненты не хотят давать БД по соображениям конфиденциальности
                          |       Таким мы можем предложить следующий скрипт, который преобразовывает
                          |       имена абонетов и групп в имя вида Contractor/Group-<ID>-<номер_телефона>
                          |       
                          |       UPDATE c
                          |       SET ....
                          |       FROM dbo.b_Co....
                          |       LEFT JOIN dbo.Vi....
                          |          ON  t.ID_....
                          |    
                          *   commit 206e9d33703cd997d277a5f10e37f122757b976e
                          |\  Merge: 188a580 aa2035b
                          | | Author: ***Developer***
                          | | Date:   Tue Oct 13 15:17:53 2015 +0300
                          | | 
                          | |     Merged: refactoring: update lib/CF, lib/Common
                          | |   
                          | * commit aa2035b9dadaa86e16c7b687382f0ad629f5c624
                          | | Author: ***Developer***
                          | | Date:   Thu Oct 8 15:20:02 2015 +0300
                          | | 
                          | |     refactoring: update lib/CF, lib/Common
                          | |     
                          | |     Модули CF, Common обновлены: добавлены директивый условной компиляции
                          | |     URLREADER_USE_PROXY и URLREADER_USE_BASEUNIT.
                          | |     
                          | |     И т.к. мы всегда используем XMLHTTPRequest, то флаг не нужен.
                          | |     
                          | |     Submodule lib/CF 038eab1..c80a287:
                          | |       > refactoring: avoid BaseUnit dependency
                          | |     Submodule lib/Common 580a371..895856f:
                          | |       > improve: introduce TInternetOpenClient class
                          | |       > refactoring: removed commented code
                          | |   
                          | * commit 7c9063457fc9c5f4d27e490fabcee183359c92cf
                          | | Author: ***Developer***
                          | | Date:   Tue Oct 13 13:46:39 2015 +0300
                          | | 
                          | |     fixed: TUpdater зависает, если при вызове .Start происходит исключение
                          | |     
                          | |     В деструкторе ожидается обнуление FCurrent, однако, если при Start
                          | |     вызовется исключение, отчего не вызовется FreeOnTerminatе, который
                          | |     вызывает FreeCurrent, то деструктор "повиснет" в бесконечном цикле.
                          | |   
                          ...
                          ---cut---
                          




                          кажется более юзабельным, нежели вот у товарища Joshua
                          • +3
                            Полагаю, что настолько полный дубликат таск-трекера в репе нужен далеко не всем.
                            С другой стороны, лог, нечитаемый без ныряния в таск-трекер, также малополезен.
                            Предпочитаю информацию о таске брать из имени ветки, а назначение конкретного коммита — из его сообщения.
                            Это просто, надежно и требует от человека меньше механической работы.
                            • +3
                              SCM разные, а методы — одни ))) и то хорошо ))

                              просто заявления «Git в сommit message не вставляет сам ID задачи» (ведь в итоге, я прАвильно понял, весь сыр-бор про имя веток сводится к этому?) симулирует утверждение «Git в commit message не умеет автоматически вставлять ID задачи, приходится писать руками, а мне лень, поэтому Mercurial удобнее»

                              а «не умеет» — это уже неверно ))) благодаря prepare-commit-msg hook'у, как, собственно, у меня и происходит (Вы же не думаете, что я каждый раз при закрытии задачи её ID и summary пишу руками?)

                              к слову, бывает ли необходимость просмотра истории через полгода, например, и не приходится ли вспоминать по ID задачи (из summary) её суть?
                              • 0
                                Для меня prepare-commit-msg hook оказался бы совершенно бесполезен: commit message я предпочитаю писать в том же редакторе, что и код, а не запускать git commit (редактор потом запустит git commit -m или git commit -f). Если нужно что‐то вставлять туда автоматически и всегда, то нужно использовать не prepare-, а просто commit-msg.

                                Но в любом случае даже этот вариант, во‐первых, оставляет простор для ошибок: commit с ненастроенной машины и кто‐то будет ругаться: либо разработчик, которому сервер провалил push, либо тот, кому эти метаданные нужны, а их нет. И дополнительно придётся так или иначе делать запрет «неправильных» сообщений серверными hook’ами или административно. В mercurial, как обычно, всё сразу работает.

                                И ещё: summary можно вполне запихнуть и в название ветки. В большинстве случаев это название писать всё равно не придётся, для остального есть автодополнение.
                                • 0
                                  ну, это уже детали…
                                  главное, что я, наконец, понял из обсуждения этого поста в чём претензии меркуриаловцев к «неполноценным веткам» Git'а… спасибо
                                  • +2
                                    > ну, это уже детали…
                                    Это — самая суть. То, что в мерке структурированно, хранится в одном месте (имя ветки), всегда существует для коммита, в гите — произвольное текстовое дополнение, раскопированное по всем коммитам ветки с помощью гайдов, хуков и ручной работы.
                                    Мерковский transplant (аналог гитового cherry pick) оставляет в новом коммите настоящую ссылку на старый вместо опционального упоминания об оригинале в сообщении коммита.
                                    Претензий к «веткам» гита нет нет никаких, есть претензии к отсутствию настоящих веток в гите, наличию отсоединенных коммитов, отсутствию данных о публичности коммита и т.п.
                                    Все это не означает, что гитом нельзя пользоваться или что он вообще плохой и неправильный. Но свои недостатки у гита есть (я бы сравнил текущее положение гита с положением C++ в 90-е) и бывает очень забавно наблюдать за фанатами, которые пытаются назвать даже их достоинствами.
                              • 0
                                Нет-нет-нет, речь вообще не про автоматизацию.
                                Просто если в мерке использовать стратегию «ветка на задачу», то id и название задачи можно отобразить в имени ветки, а в строке описания коммита оставить только то что относится к самому коммиту. В гите так сделать не выйдет, потому что его ветки эфемерны и придется информацию о задаче дублировать в каждом относящемся к ней коммите. А ветка мерка и через год никуда не исчезнет
                                • 0
                                  а можете показать как это выглядит? я что-то плохо себе это представляю (мой мозг тоже съеден Git'ом ;) )…
                                  в смысле как выглядит информация о ветке в Hg + сообщения коммитов
                          • 0
                            Отличия, видимо, лишь в описании коммита в дополнении к номеру задачи? Если так, то это Вам лишь кажется ) Одно из достоинств нашей команды (она же головная боль) — это отсутствие авторитетов в построении процесса разработки.

                            Каждое действие должно нести пользу и в этом еще нужно УБЕДИТЬ. Вот например, выше я интересовался преимуществом GitWorkFlow по сравнению с нашим именно потому, что когда то первоначально ввел именно его, а потом не смог найти его преимущества перед схемой ветка = версия.

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

                            Переход на задачу — в один клик по номеру задачи. А комменты вроде:
                            «refactoring: убрать директивы условной компиляции Indep и AladdinSupport»

                            и так очевидны из диффа.
                            • 0
                              а GitFlow, разве, не «ветка = версия», просто в нём есть ветки и долгоживущие, и короткие?

                              Переход на задачу — в один клик по номеру задачи.

                              переход по клику не всегда возможен *тут столкнулись апологеты GUI и консоли )))*
                              кроме того, задачи в issue tracker'е могут быть долгоживущими, и искать в её комментах когда именно было сделано это изменение (а уж тем более в связи с чем) — тоже ручная работа (кстати, у вас к комментам задачи добавляется связь с коммитом репозитория?)

                              А комменты вроде:
                              «refactoring: убрать директивы условной компиляции Indep и AladdinSupport»


                              и так очевидны из диффа.


                              а тут вы взяли уж очень простой и очевидный пример. согласитесь, не все изменения настолько очевидны? или Вам привести неочевидный пример? ;)
                              • 0
                                задачи в issue tracker'е могут быть долгоживущими

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

                                кстати, у вас к комментам задачи добавляется связь с коммитом репозитория?

                                В жире есть вкладка — коммиты по задаче. Про это? Коммит связываем просто с задачей, не с комментарием к ней. Может и было бы иногда удобно но в 99% случаев хватает номера задачи.

                                По условной компиляции: у нас бы завели задачу и сделали бы один коммит по одной задаче. Тогда у другого разработчика не возникло бы вопросов о том, зачем убрали одни атрибуты и добавили другие. Сейчас для этого требуется Ваше пояснение.

                                Впрочем, действительно, у нас тоже есть ИНОГДА коммиты с задачей и поясняющим текстом. И некоторые коммиты без задач: мердж, stylecop, мелкий рефакторинг для fxcop. Вопрос размера рефакторинга, который можно сделать без задачи — на усмотрение разработчика, до тех пор, пока не будет рецедива )
                                • 0
                                  ага, понятно ))
                                  у нас бы завели задачу и сделали бы один коммит по одной задаче. Тогда у другого разработчика не возникло бы вопросов о том, зачем убрали одни атрибуты и добавили другие. Сейчас для этого требуется Ваше пояснение.

                                  ну, тут, я так понимаю, ещё и следование регламенту, с возможностью отследить «чем занимался», ибо «завести задачу» вряд ли даёт выигрыш в эффективности в сравнении с «написать комментарий в сообщении коммита» и дальнейшим «одним кликом» при просмотре истории…
                            • 0
                              А комменты вроде:
                              «refactoring: убрать директивы условной компиляции Indep и AladdinSupport»


                              и так очевидны из диффа.

                              а, кстати, не так уж и очевидны ))
                              само «убирание» директив — да, возможно )) но зачем? )) если и так всё работало…
                              а затем, что в следующем коммите добавятся новые директивы, которые вызовут ошибку… ибо у компилятора, оказывается, есть ограничение на суммарную длину директив, заданных в командной строке
                              -DFirstConditionalDefine;SecondConditionalDefine;...
                              

                              ну, а поскольку я люблю атомарные коммиты, то сначала удаляем ненужные, а затем — добавляем нужные
                              и объяснить зачем удалять — надо… писать это в багтрекере? сомнительная польза
                            • 0
                              На мой взгляд это уж очень радикально. Если имя и id задачи включены в имя ветки, а описание коммита в его строку, то до этой информации будет не один, а ноль кликов, что во многих случаях позволяет получить одновременно выразительную и детальную картину изменений, переходы же к другим ресурсам (таск-трекер) оставить для случаев, когда краткой информации недостаточно.
                              Плюс комментарий к коммиту создает полезную избыточность, позволяя выявлять случаи, когда разработчик заведомо не заливает все необходимое или (что чаще) включает в коммит не относящиеся к его цели правки.
              • +2
                До этого дело есть всем, просто в гите приходится для коммита писать как то, ради чего сделан этот конкретный коммит, так и то, для чего сделана ветка, которая этот коммит содержит. Допустим, коммит содержит некоторый рефакторинг, о чем в сообщении и написано (удалены ненужные методы X и Y, выделен интерфейс Z ради T). Но он является подготовительным для решения бизнес задачи. В результате в гите я вынужден писать для коммита еще и id таски в жире и название самой таски. А в мерке эта информация есть в имени ветки и никуда не исчезнет.
                Причем в мерке у тебя есть выбор (можно использовать как букмарки, аналогичные гит-веткам, так и собственно ветки), а гите — нет. Вообще git-way как-то очень уж скуден на метаданные.
                • +1
                  Он не просто скуден на метаданные: я как‐то не могу припомнить, чтобы метаданные туда добавляли. У mercurial изначально не было ни закладок, ни фаз, ни evolve. Но их добавили. При чём закладки изначально существовали в виде дополнения (как evolve сейчас).

                  Это я к тому, что в mercurial метаданные добавляют, причём добавляют их, в том числе, через расширения — архитектура позволяет. В git не добавляют ничего. Это ясно говорит либо о том, что в протоколе взаимодействия и/или формате хранения данных (скорее первое: второе уже меняли и даже, вроде бы, не раз) просто не предусмотрена возможность расширения. Либо о том, что архитектура проекта не позволяет добавить что‐то вроде фаз или веток без тяжелого рефакторинга. Либо о том, что разработчиков нельзя убедить в полезности дополнительных метаданных, а добавить их самому через расширение как в mercurial не позволяет либо протокол, либо архитектура.
                  • +1
                    Так и представляю себе, как хочет мейнтейнер что-то такое добавить в гит, а Линус ему — да ты парень полон дерьма, и так далее в своём стиле :)
                    • 0
                      там уже давно не линус. Junio Hamano просто не отвечает :)
                  • +1
                    Мне кажется, что тут скорее идеологические причины — в гит не добавили даже метку публичности коммита
                • +1
                  в гите я вынужден писать для коммита еще и id таски в жире и название самой таски

                  аа, ну вот же и ответ на мой вопрос

                  prepare-commit-msg is overkill, говорите?

                  нее, ну не вопрос, когда под свои нужды можно сменить SCM… )
    • +1
      Понимаю, что сейчас минусов нахватаю за не популярную точку зрения :(
      Я предпочитаю не ставить минусы за непопулярную точку зрения.

      А вот за написание глупостей — вполне. Git не был первой dvcs (а не dcvs, как вы написали): были sun teamware, bitkeeper, monotone, arch, arx, darcs, dvcs (да, такое творение тоже существовало и расшифровывается, как distributed concurrent versions system).
    • 0
      Но никто ещё, ни один человек не смог объяснить мне чем гит лучше?
      просто они гит и не знают толком, вот и не могут объяснить. так-то сразу бы вспомнили octopus merge и распиливание hunkов в интерактивном add
      • +2
        распиливание ханков есть. Называется «Отложить в долгий ящик». Очень удобная штука. Возможно это только в TortoiseHG, а не сам hg — не разбирался.

        Octopus merge — есть arrenbrecht.ch/mercurial/pbranch/octopus.htm (сам я такими извращениями никогда не страдал, но этот функционал есть).
        • 0
          Называется «Отложить в долгий ящик». Очень удобная штука. Возможно это только в TortoiseHG, а не сам hg — не разбирался.
          нет, не оно. сначала было только в thg, последний год — плагин shelve. но hunkи распиливать оно не умеет. А вот crecord, как оказалось, умеет отмечать не только hunkи, но и отдельные строки в них, что ещё лучше распиливания.
          • +1
            нет, не оно.

            А это что? (никаких плагинов я не ставил, всё из коробки):
            image
            • 0
              это не оно, тут из одного hunkа два и больше не сделать (где применимо).
    • +1
      > Но никто ещё, ни один человек не смог объяснить мне чем гит лучше?
      У меня большое подозрение, что дело банально в возможности менять историю. Делать вид, будто ты никогда не ошибался, не косячил, вместо пути показывать только результат.

      Исторически, насколько я читал статьи, гит был сделан для того, чтобы человек мог долго пилить у себя фичу для линукса, а потом спрессовать это в один коммит и отправить ревьюеру. Которого, действительно, твои ошибки и не должны интересовать.

      Но для хранения истории проекта… это очень болезненное яблоко искушения.
      • 0
        В мерке это тоже есть и делается тремя кликами
        image
        • 0
          Что делается?
          • 0
            Есть много разных вариантов:
            1. «Экспортировать список различий» — создаётся один *.diff показывающий разницу между 1917 и 1926. Можно свою фичу пилить в отдельной ветке, а потом этот патч накатить на основной транк.
            2. «Экспортировать часть граффа» — создаёт 10 diff-файлов
            3. «Сжать историю» — создаёт 1 коммит выходящий из 1917 (второй башкой).
            • 0
              А зачем вы это написали в ответ на мой комментарий об изменении истории? :)
              • 0
                Потому что №1 и №3 позволяет сделать: чтобы человек мог долго пилить у себя фичу для линукса, а потом спрессовать это в один коммит или diff и отправить ревьюеру. Которого, действительно, твои ошибки и не должны интересовать.

                Могу дополнительно рассказать как менять историю, но это слишком скучно и тривиально.
                • 0
                  Всё то же самое в гите, скучно и тривиально. И в чём здесь преимущество мерка?
                  • +1
                    Я ни разу не открыл документацию и ничего не читал. Просто кликнул пару раз и сразу увидел то что мне было надо.
                    • 0
                      Del
                • 0
                  Так я вроде не собирался пилить фич и их отправлять
                  Я высказал своё мнение о том, почему гит более популярен

                  Поэтому я и недоумеваю — зачем вы мне объясняете, как делать то в хг то, что я делать не планирую…
      • 0
        У меня большое подозрение, что дело банально в возможности менять историю.
        ну да, ведь это только гит такой уникальный. Правда, для Mercurial существует (хоть и не релизнут) плагин, позволяющий автоматически объединять правленую историю одной командой, но кому какое дело — это же не Git.
        • 0
          для Mercurial существует (хоть и не релизнут) плагин


          то есть то, что в гит есть из коробки, в мерке мне нужно ставить плагин, который к тому же ещё и в бете?
          • 0
            то есть то, что в гит есть из коробки
            неверно. В гите аналога EvolveExtension нет ни в коробке, ни «плагином» и, насколько мне известно, никаких шагов для реализации данной функциональности не предпринимается вообще.
            • 0
              Почитал немного про changeset evolution, выглядит интересно. Как я понял, это как бы ведение истории изменения истории? Можете рассказать по подробнее простыми словами? В двух словах.
              • +1
                любое переписывание истории теперь не удаляет старые коммиты, а добавляет их список в специальную БД, наряду со списком переписанных. т.е. в каждой записи содержится информация вида «коммиты 12345, 234235, и 123123 отменяют коммиты 123123, 234234, 639045 и 2349823». клиенты обмениваются данными из этой БД и таким образом разрешение конфликтующих правок истории становится тривиальным.

                www.youtube.com/watch?v=4OlDm3akbqg вот наглядная демонстрация. вкратце сводится к «там отредактировали историю, здесь отредактировали, забрали обновки друг у друга, кровь-кишки-ра… а нет, показалось, скомандовали hg evolve и пошли дальше работать».

                в гите пришлось бы смотреть друг другу в рефлог, чтобы понять, кому куда rebaseиться
                • 0
                  Ну в гите можно накостылять с ветками, то есть вместо того, чтобы ребейзить одну и ту же ветку, делать git checkout -b feature-v2 feature && git rebase master; git checkout -b feature-v3 feature && git rebase master итд. При желании можно это всё завернуть в красивый плагин (похожее сделали с git-stash) и счастливо пользоваться, но как-то никогда не нужна была такая функциональность. Хотя может быть, если бы была, ей бы и пользовались, хз. Как вы сами, часть пользуетесь hg evolve? Интересен реальный опыт.
                  • +1
                    костыли, именно.
                    как-то никогда не нужна была такая функциональность
                    забавная тенденция однако — почему-то ненужной оказывается именно функциональность, отсутствующая в Git :) вы серьёзно? править историю в хвост и в гриву без оглядки на коллег и чтобы вам за это ничего не было — и не нужно?
                    Как вы сами, часть пользуетесь hg evolve?
                    не пользуюсь: 1) bitbucket его пока не поддерживает, а искаробочной смотрелки репозитория мне недостаточно 2) недостаточно народу на проектах под mercurial, чтобы разворачивать evolution (там, где народу достаточно, VCS выбирал не я ⇒ Git)
                    • 0
                      править историю в хвост и в гриву без оглядки на коллег и чтобы вам за это ничего не было

                      Кто вам такое сказал? Я постоянно пользуюсь git rebase, каждый день раз по пять, как практически все мои коллеги. При должном опыте, практике и соблюдении элементарных правил безопасности, это совершенно нормально и безопасно. Правила просты: ребейзить только свои локальные ветки, не ребейзить общие ветки (master, develop) без согласования со всей командой, если опубликовал фичеветку ребейзить можно только если над фичей работаешь только ты, если кто-то ещё — согласовывать ребейзы с этим коллегой. Всё, всё просто, никаких проблем. Именно что, с оглядкой на коллег, чтобы ничего за это не было. Мне ли вам объяснять, что гит — это инструмент, со своими опасностями, как фрезерный станок: если сувать руки внутрь, то может оторвать, но если правильно пользоваться — можно это делать совершенно безопасно годами. Тут есть момент, что инструмент для команды коллективный, да. Но при ошибках работы с ним никто не умирает, в отличие от ошибок с коллектиным оружием (если кто-то один накосячит, например, с миномётом, разорвёт весь взвод).
                      • +3
                        При должном опыте, практике и соблюдении элементарных правил безопасности, это совершенно нормально и безопасно.
                        hg evolve делает правку истории просто безопасной. Безусловно. Никаких «если» и «но». Никаких предварительных требований. Понадобилось переписать историю — переписал. Это как если выкинуть весь раздел «recovering from upstream rebase» из man git-rebase за ненужностью. Смекаете?
                        • +1
                          Да, я уже понял, что hg evolve это суперкруто. Однако:

                          не пользуюсь: 1) bitbucket его пока не поддерживает, а искаробочной смотрелки репозитория мне недостаточно 2) недостаточно народу на проектах под mercurial, чтобы разворачивать evolution (там, где народу достаточно, VCS выбирал не я ⇒ Git)
                          • –1
                            и напомню, в гите нет подобной функциональности даже в виде планов
                      • 0
                        Советую приплюсовать gerrit — тогда все правки делаются в одном коммите и не нужно опасаться за rebase.
                        Меняет концепцию работы, но реально очень полезно, особенно, при появлении в команде джуниоров.
      • 0
        Так история и должна быть правильной.
        И не только в ошибках и правильности дело.
        Изменение истории — это как рефакторинг в программировании. Пока не видел ни одного программиста, который бы в реально боевой, ежедневной работе над проектом писал бы код не требующий рефакторинга.
        Это правильно, что когда все готово, то нужно прибраться и все причесать, чтобы история была реальной историей, а не мусорной кучей.
        • +2
          Вы хотите религиозного спора? Ночь же уже, но давайте попробуем.

          Историю должна быть историей. Разграничивать то, что было и чего не было. Если есть желание рассказывать прекрасные красивые сказки — заради бога, но то, что это называется history и log…
          Нет, ребята, извините. На самом деле этого не было. На самом деле всё было совсем не так.
          • +1
            Нет, религиозного не хочу. Хочу конструктивного.
            Насчет истории — не согласен.
            История должна отражать, кто и что сделал. А как он это делал — не должна. Мне не нужно видеть 100500 коммитов по одной задаче из-за того, что тестировщик нашел баг, а потом еще один, и еще один.
            Мне нужно, чтобы в историю попадал один коммит, в котором сделаны изменения именно по задаче. Это важно, если, например, придется откатить изменение.
            Я хочу сам (как лицо ответственное за разработку и за историю) решать, что мне нужно в истории, а что нет. Если в историю будет попадать все, что делают разработчики это будет помойка, а не история.
            • +2
              А меня мама учила не врать.
              Какой уж тут конструктив, когда меня учат обманывать, да ещё так красиво объясняют, почему обманывать — так хорошо и правильно :)
            • +1