Mail.ru Group corporate blog
Website development
Programming
Git
Comments 62
+3
Вы можете запускать многие команды (такие, как git diff, git blame и т.д.) с флагом "-w" и Git будет просто игнорировать все изменения, связанные с пробельными символами (пробел, табуляция и другие).
Чёрт, для языка программирования Whitespace такая опция не подойдёт :-(
0
Ой, опечатка. Там «команды», конечно же. Спасибо! Да, для whitespace эту опцию лучше не использовать :)
0
У меня есть давний вопрос по гиту, но никто на него не может толком ответить. Предположим, у нас есть какой-то коммит. И стопочка тегов. Хочется узнать, между какими двумя тегами находится коммит. Как?

Тривиальная задача: понять, в какую версию попало изменение в первый раз. Но каждый раз сталкиваюсь с задачей — и каждый раз в ход идут какие-то ужасные хаки с перебором и т.д.
+1
А если как-нибудь вот так вот попробовать?

git log {{commit-hash}}.. --reverse --oneline --decorate | grep 'tag: ' | head -1
+6
Ну почему сразу «хаки», вполне себе рабочий вариант :) Много есть случаев, когда именно такой подход работает, а делать из гита монстра, который умет прям всё-всё-всё глупо и нет смысла. Linux-way, все дела.
+6
О, спасибо! (Не могу голосовать за карму, потому что у вас ни одного поста нету -_-).
+2
4. Просмотр ещё не влитых в родительскую ветку изменений

Я обычно смотрю какие ветки не были влиты в master:
git fetch origin
git branch -a --no-merged origin/master
+1
Нет, это не старые ветки. Это ветки над которыми ведётся работа, но которые ещё почему-то не были влиты в master. Например, их отложили до следующего релиза или разработчика отвлекли другой, более срочной задачей.
0
Оу оу оу, точно! Я что-то не так понял сходу. Конечно же вы правы!
+2
По пункту 4: вместо git log --no-merges удобно использовать команду git cherry — она сравнивает коммиты по изменениям и, следовательно, «прокуривает» ситуацию, когда был сделан cherry-pick из одной ветки в другую.

По пункту 11: Meld восхитителен — он гораздо лучше самого git'а понимает различные конфликтные ситуации и простые разруливает самостоятельно. В более сложных ситуациях он упрощает понимание того, что было сделано, так как показывает сразу три варианта — как было до того, как ветки разошлись, как стало в ветке А и как стало в ветке Б. Ну и всякие команды, которые быстро переносят результаты из веток в нужном порядке (типа «здесь берём оба варианта, но из ветки А идёт первым, а из Б — следом за ним»). Одна беда — на маке Meld неюзабелен.
0
Использую diffuse вместо meld. Он намного шустрее, но не умеет сравнивать директории. На маке вполне юзабелен, только надо что-то сделать с сочетаниями клавиш ctrl+стрелки, которые на маке привязаны на какие-то действия (можно перенастроить).

Для мёржа настроил diffuse в четыре колонки:
[merge]
    tool = diffuse_
[mergetool "diffuse_"]
    cmd = diffuse \"$LOCAL\" \"$BASE\" \"$REMOTE\" \"$MERGED\"
+1
Под винду мне очень понравился Beyond Compare, правда я давно им пользовался, не знаю, что там в последних версиях.
0
В последних версиях всё хорошо, включая работу под Linux и Mac. Но при наличии бесплатных альтернатив выкладывать минимум $30 за персональную лицензию слишком обломно :)
0
Согласен. Однако, зачем, например, покупать PyCharm при наличии бесплатных альтернатив (vim, например)? :)
0
Ну что ж сразу вим, всё зависит от задач и потребностей :)

По мне так эклипсовского диффа вполне хватает. Одно время развлекался прикручиванием внешних утилит, перебирал всё, что описано здесь. Однозначно заменил meld на p4merge. Потом уже порывался заплатить за SmartSynchronize. А потом понял, что и половины всех возможностей всё равно не использую…
0
То есть было бы меньше возможностей у тулзы — заплатили бы? :)
0
Странный подход. Зачем платить за то, что и так есть бесплатно? Вот если бы там было что-то, что позарез нужно, и чего нет в бесплатных аналогах — тогда другой вопрос.
0
Вопрос в том, как скоро то, чего еще нет в бесплатных аналогах, но есть в платных, появится в этих бесплатных аналогах? :) Поэтому лично я, в этом контексте, не вижу смысла платить за это.
+3
Tig очень хорош, да, но это всё-таки не совсем то, это GUI (ncurses).

По поводу утилит есть ещё чумовая git up (и её же вариант на питоне, который лично я предпочитаю).
+2
Спасибо! Про reflog можно ещё немного написать бы, чтобы знали, что и такое бывает и иногда выручает.
+4
По поводу текстов коммитов:
Автор оригинала предлагает заканчивать фразу When applied this commit will... В статье, на которую он сам ссылается, предлагается писать императивом chris.beams.io/posts/git-commit/#imperative
Для английского противоречий нет, так как форма When applied this commit will...Fix some bugs идентична императиву Fix some bugs

В русском же эти формы разные. Не думаю, правда, что многие люди пишут коммит-месседжи на русском
+2
Мы пишем на русском. У нас принята форма ответа на вопрос «Что было сделано?» (например: «Реализовано то-то» или «Исправлена ошибка такая-то»). Это удобно, когда прибегает менеджер и, бешено вращая глазами, требует срочно предоставить список, того, что мы сделали за неделю/месяц/в этом релизе. git log, минимальные правки, копипаст в письмо и можно программировать дальше.
+6
Добавлю про --amend
Бывает, что нужно исправить опечатку в комментарии к последнему коммиту, не затрагивая файлов. В этом случае удобно воспользоваться флагом allow-empty, чтобы гит не ругался на отсутствие изменений для коммита:

git commit --amend --allow-empty -m "New commit message"


+2
Еще там удобно использовать "--no-edit", тогда используется коммент от коммита, и его не нужно писать заново.
+3
У меня, кажется, без --allow-empty такая конструкция прокатывает.
Просто вызываю стрелочкой вверх последний коммит из истории, правлю сообщение и в конец дописываю --amend.
Пример:
nickolaus@nicknout:~/CHAS-CORRECT/correct$ git add chrome/dictionary.js
nickolaus@nicknout:~/CHAS-CORRECT/correct$ git commit -m "Bred"
[master b145062] Bred
 1 file changed, 1 insertion(+), 1 deletion(-)
nickolaus@nicknout:~/CHAS-CORRECT/correct$ git commit -m "В словарь" --amend
[master b80d697] В словарь
 1 file changed, 1 insertion(+), 1 deletion(-)
nickolaus@nicknout:~/CHAS-CORRECT/correct$ 

0
«Хорошие примечания к коммиту»
Я пишу комментарии в порядке добавления файлов, например, следующим образом:
1. common/modules/delivery/components/classes/HTTPRequestAdapter: инициализация параметров curl перенесена в конструктор + отлажен механизм отправки
2. common/modules/delivery/components/classes/cdek/CDEKAdapter: убран неиспользуемый метод форматирования даты

Думаю такие комменты читать команде проекта, да мне самому удобнее
+8
Не совсем понял, обе строчки идут в один коммит? Если да, то это странно, суда по комментариям, это должны быть разные коммиты. А писать в коммите, какие файлы были затронуты — тоже странно, всегда можно посмотреть лог коммитов для конкретного файла с помощью git log, если хочется понять кто и зачем трогал файл.
+1
«Если я хочу отменить все внесённые изменения и начать работу с чистого листа, я использую команду git reset --hard HEAD (самый частый случай).»

Достаточно `git reset --hard`

«Если я просто хочу взять три последних коммита и слить их в один большой коммит, я использую команду git reset --soft {{some-start-point-hash}}.»

Лучше использовать `git rebase -i HEAD~3`. И про `reflog` можно добавить — обязательно пригодится, когда произойдёт неправильное использование `git rebase`
+6
Недавно узнал способ именовать stash, чтобы потом можно было по его описанию понять, что там такое:

git stash save something something
0
Не делайте ребейз коммитов, находящиеся вне вашего репозитория.

Вот давно хотел спросить, что это значит? не делать rebase c удаленной ветки не своего репозитория?

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

  1. Всегда делайте только интерактивный rebase
  2. Внимательно изучайте список коммитов, который предлагается к применению
  3. Пропускайте (skip) те коммиты, которые делали не вы

В этом случае проблем не будет.

Предположим в мастере у вас 3 коммита:

master:
A---B---C

Разработчик бранчуется от C и добавляет свои 2 коммита:

feature:
A---B---C
         \
          D---E

Потом мы делаем rebase master HEAD~2. Страшно даже подумать, у-у-у-у… Что получаем?

A---B'---C'
\
 B---C---D---E

С точки зрения git коммиты B и B' разные, так как у них разные хэши. При попытке сделать git rebase master (без -i) git попытатеся применить коммит B на B' и C', что, естественно, вызовет конфликт. Однако разработчик должен четко понимать, что он с коммитами B и C не работал и они его вообще никак не волнуют. Потому при git rebase -i master он должен пропустить B и C.

В результате после rebase разработчик получит:

A---B'---C'
          \
           D---E
+2
Вообще‐то git при rebase автоматически пропускает уже применённые изменения, даже если они были применены в других (или как часть других) commit’ов. Конфликты иногда будут, если там ещё что‐то рядом меняли, но rebase --interactive вам здесь поможет, только если вы прочитали все сделанные другими разработчиками изменения.

Я с rebase работаю всегда в одном из двух режимов: histedit — меняю историю, но ничего никуда не переношу, имею для этого специальный alias: ri = !sh -c 'git rebase --interactive $(git merge-base --is-ancestor ${1:-master} ${2:-HEAD} && git rev-parse ${1:-master} || git merge-base ${1:-master} ${2:-HEAD})' -; или только перебазирование без каких‐либо изменений истории. Первое используется чаще, особенно если соглашения, принятые в проекте, не требуют обязательный rebase на master. Именно histedit используется, чтобы не терять контекста, в котором были сделаны изменения.
0
Не очень понятная формулировка. В прицнипе, это можно делать в общем репозитории в исключительных случаях.
Например, если это будет делаться в ветке, которую ведёте только вы и это не затронет коммиты до момента ответвления. Правда, в данном случае надо не забывать пушить обновления только в эту ветку (git push -f origin your_branch), так как git может быть настроен на пуш всех веток, если не указано иного и вы перезапишете изменения коллег во всех остальных ветках.
-1
Добавлю от себя: всегда делайте squash перед слиянием ветки с master — это позволит избежать мусора в мастере. Как мы делаем у себя на проекте?

  1. Разработчик создает pull-request
  2. После получения достаточного количества голосов «за» разработчик делает git rebase -i HEAD~n, где n — это количество коммитов, которые он сделал в данной ветке (можно сказать, что это количество коммитов, на которое его ветка опережает master)
  3. Первый коммит отмечается pick, остальные — squash
  4. Все сообщения коммитов удаляются, первому выставляется подробное описание, что за фича, что сделал, что исправил
  5. git push -f
  6. merge
  7. ...
  8. PROFIT!

В результате вместо такого
image

Получаем что-то такое
image


PS: И всегда делайте интерактивный rebase. No exceptions
0
А если использовать arcanist + phabricator, то все это можно получить из коробки. Очень удобно, не говоря уж о удобстве phabricator-а для code review.
0
Вопрос на засыпку. Чем принципиально отличаются unstaged и untracked? :)
0
untracked — ни разу не был добавлен в git. git о таком файле ничего не знает, у него нет никакой истории изменений. Это, например, могут быть какие-либо автоматически сгенерированные файлы, логи и т.п.

unstaged — файл уже добавлялся в git, сейчас он изменен, но не добавлен в будущий commit. Например, вы поменяли 5 файлов, но хотите сейчас закоммитить только 3, а оставшиеся 2 добавить в следующий commit. Вот те 2 и будут unstaged.
0
Тут еще не хватает той картинки со staging area из мануала. Имхо, этот вопрос в статье стоило бы чуть подробнее пояснить, штука достаточно важная.
0
Да, эта нагляднее. Я видел аналогичную, но чуть попроще, где-то в первой главе. Там было именно staging area.
+3
Еще полезный совет:
Если вы используете русскоязычные или любые другие не англоязычные имена файла при работе с Гит, для Вас будет полезным использование ключа -z
Например, для просмотра новых файлов можно использовать команду
git status --porcelain

Для русскоязычного имени в этом случае будет показан результат
?? "\320\234\320\276\320\264\321\203\320\273\321\214.txt"

а вот «правильная» команда
git status --porcelain -z
покажет намного более юзабельный вариант
?? Модуль.txt
0
Новый вариант совершенно не юзабельный, если изменения в нескольких файлах: например, вместо
A  "\302\253\302\273"
??  .git /
?? a.img
?? ab/
?? abc.c
?? foo/
показывается
A  «»??  .git /?? a.img?? ab/?? abc.c?? foo/%
(% — это от zsh, указывает на то, что вывод предыдущей команды не был завершён новой строкой). Может, ваш терминал воспринимает нулевой байт, как новую строку, но ни один из моих нет (и не должен). Если у вас есть такие файлы, то лучше иметь что‐то вроде alias’а st = "!sh -c \"git status -z \\\"\\$@\\\" | tr '\\\\0' '\\\\n'\" -".
+2
Как, пожалуй, с любой технологией, к Git применимо правило 80:20. 20% знаний дают 80% профита, остальные 80% знаний дают оставшиеся 20%. Эти 20% — тоже много и нужны и, как мне кажется, статья дает именно эти 20% которых многим не хватает. Спасибо!
Only those users with full accounts are able to leave comments., please.