Цифровой садик - приветственная

Цифровой садик - приветственная | Полный список всего, что тут есть | RSS | Подписаться через follow.it

13.02.2026

git

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

(вторая используемая иногда - fossil)

Странички

Мелкие памятки себе

  • git worktree — возможность параллельно работать в нескольких рабочих каталогах (разных ветках, например), прикрепленных к одному репозиторию. (Интересно, насколько это понимает magit и vc-git).
    • Нельзя "зачекаутить" одну и ту же ветку в двух worktree.
    • Нельзя удалить ветку, если на нее смотрит какой-то из worktree. Гит об этом скажет, и тут просто надо удалить этот worktree (git worktree remove <path>).

В консоли кириллица отображается как \цифры

git config core.quotepath false

Только один файл из другой ветки

git checkout master -- path/name.ext

или

git restore --source master -- path/name.ext

https://stackoverflow.com/questions/2364147/how-to-get-just-one-file-from-another-branch - там есть ещё варианты.

Коммит не в ту ветку, что делать?!

git reset HEAD~ --soft # вернулись к предыдущему коммиту, но не удаляем изменения
git add .  # изменения застейджили
git stash # сохранили застейдженное в stash
git checkout имя-верной-ветки # переключились куда надо
git stash pop # извлекли сохранённое из stash

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

git add . # застейджили уже здесь
git commit -m "описание коммита" # закоммитили на нужное место

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

git reset --hard HEAD

В отдельном файле:

git checkout путь/имяфайла

И если это новые файлы, которые не были даже добавлены:

git clean -i -d
  • -d - чтоб заглядывал в неотслеживаемые каталоги.
  • как вариант, можно использовать -n для того, чтоб посмотреть, что будет вычищено, и -f - чтоб таки сделать это.)
  • в –help можно прочитать ещё потенциально полезного. Например, -X позволяет убрать файлы, которые git игнорирует.

Откатить запушенный коммит

git revert <хеш_коммита>

Создаёт новый коммит, который делает изменения, обратные изменениям в нежелательном коммите.

Срочно подправить ошибку, замеченную до пуша

git commit -a --amend --no-edit

Довнести все изменения, сообщение коммита не менять

Или

git reset --soft HEAD~

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

detached head

If you want to delete your changes associated with the detached HEAD You only need to checkout the branch you were on, e.g.

git checkout master

Next time you have changed a file and want to restore it to the state it is in the index, don't delete the file first, just do

git checkout -- path/to/foo

This will restore the file foo to the state it is in the index.

If you want to keep your changes associated with the detached HEAD

git branch tmp # this will save your changes in a new branch called tmp. Очевидимо, вместо tmp можно любое другое название
git checkout master

If you would like to incorporate the changes you made into master, run git merge tmp from the master branch. You should be on the master branch after running git checkout master.

Про удалённые (deleted) файлы

https://stackoverflow.com/questions/6017987/how-can-i-list-all-the-deleted-files-in-a-git-repository

git log --diff-filter=D --summary

Краше

git log --all --pretty=format: --name-only --diff-filter=D | sort -u

This will get you a list of all files that were deleted in all branches, sorted by their path:

git log --diff-filter=D --summary | grep "delete mode 100" | cut -c 21- | sort > deleted.txt

Локальные настройки имени и адреса

https://git-scm.com/book/ru/v2/%D0%92%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5-%D0%9F%D0%B5%D1%80%D0%B2%D0%BE%D0%BD%D0%B0%D1%87%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F-%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0-Git

Если в разных репозиториях нужно коммиты от разных Name+email.

git config --local user.name "Name"
git config --local user.email email@mysite.ru

Или залезть в .git/config репозитория и написать

[user]
        email = name@mysite.ru
        name = Name

Проверка настроек

Если хотите проверить используемую конфигурацию, можно использовать команду git config –list, чтобы показать все настройки, которые Git найдёт:

$ git config --list
user.name=John Doe
user.email=johndoe@example.com
color.status=auto
color.branch=auto
color.interactive=auto
color.diff=auto

Некоторые ключи (названия) настроек могут отображаться несколько раз, потому что Git читает настройки из разных файлов (например, из /etc/gitconfig и ~/.gitconfig). В таком случае Git использует последнее значение для каждого ключа.

Также вы можете проверить значение конкретного ключа, выполнив git config <key>:

$ git config user.name
John Doe

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

$ git config --show-origin rerere.autoUpdate
file:/home/johndoe/.gitconfig	false

Посмотреть историю изменений одного файла

git log -p -- path/filename.txt

Найти коммиты с появлением и удалением строки

В кавычках, разумеется, строка

git log -S '7b28ff06-ab13-4070-8dc2-fcbc9fe2f0b0'
  • this looks for differences that introduce or remove an instance of <string>. It usually means "revisions where you added or removed line with 'Foo'".
  • the –pickaxe-regex option allows you to use extended POSIX regex instead of searching for a string. Example (from git log): git log -S"frotz\(nitfol" –pickaxe-regex

    С регэкспом

git log -G<regexp> --branches --al

https://stackoverflow.com/questions/2928584/how-to-grep-search-committed-code-in-the-git-history

И без учёта регистра

git log -S foobar -i --oneline

or

git log --regexp-ignore-case -Sfoobar

or

git log -i -Sfoobar

Note that with 1.x git versions this option will not work with a regexps, only with a fixed string. It works with regexps only since git 2.0 and commit 218c45a, git 2.0, May 2014.

https://stackoverflow.com/questions/25384842/case-insensitive-git-pickaxe-search

Достать файл из коммита

git checkout b9d55fe7e7e48b106464dfad0285190ced2c78ea /path/to/filename
  • после слова checkout и перед путём - хэш подходящего коммита. вместо хэша можно указывать по истории, типа там сколько назад от HEAD и типа того.
  • путь к файлу - от корня репозитория

"git stripspace" - может убирать комментарии и ненужные пробелы

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

Дата последнего изменения файла

git log -1 --pretty="format:%ci" /path/to/repo/anyfile.any

-1 restricts it to the very last time the file changed

%ci is just one of the date formats you can choose from others here at https://git-scm.com/docs/pretty-formats

https://stackoverflow.com/questions/22497597/get-the-last-modification-date-of-a-file-in-git-repo

Пуш не пушится

error: src refspec <branchname> соответствует более чем одному
error: не удалось отправить некоторые ссылки в «<репозиторий>»

или

error: src refspec <branchname> matches more than one
error: failed to push some refs to '<repository>'

Гит не может понять, что конкретно я от него хочу. Что именно отправить-то надо. Вероятнее всего, почему-то образовался тег, совпадающий с именем ветки. Можно подробнее указать ветку (refs/heads/<branchname>), можно удалить тег.

Удалить тег:

git tag -d <tag>

Переименовать тег не судьба вообще. :)

Если хочется полно обратиться к тегу, это refs/tags/<tag>.

Полностью команда push - это

git push [options] <repository> <refspec>

, где refspec - это

<source>:<destination>

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

Более безопасный push –-force

Вместо git push –force лучше делать git push –force-with-lease.

В чем разница? При совместной работе git push –force может просто выкинет чужой коммит из истории на сервере, потому что ваш репозиторий его не видит. Тогда как git push –force-with-lease запрещает переписывание ветки, если там есть новые чужие коммиты.

–[no-]force-with-lease, –force-with-lease=<refname>, –force-with-lease=<refname>:<expect>

Usually, "git push" refuses to update a remote ref that is not an ancestor of the local ref used to overwrite it.

This option overrides this restriction if the current value of the remote ref is the expected value. "git push" fails otherwise.

Imagine that you have to rebase what you have already published. You will have to bypass the "must fast-forward" rule in order to replace the history you originally published with the rebased history. If somebody else built on top of your original history while you are rebasing, the tip of the branch at the remote may advance with their commit, and blindly pushing with –force will lose their work.

This option allows you to say that you expect the history you are updating is what you rebased and want to replace. If the remote ref still points at the commit you specified, you can be sure that no other people did anything to the ref. It is like taking a "lease" on the ref without explicitly locking it, and the remote ref is updated only if the "lease" is still valid.

  • –force-with-lease alone, without specifying the details, will protect all remote refs that are going to be updated by requiring their current value to be the same as the remote-tracking branch we have for them.
  • –force-with-lease=<refname>, without specifying the expected value, will protect the named ref (alone), if it is going to be updated, by requiring its current value to be the same as the remote-tracking branch we have for it.
  • –force-with-lease=<refname>:<expect> will protect the named ref (alone), if it is going to be updated, by requiring its current value to be the same as the specified value <expect> (which is allowed to be different from the remote-tracking branch we have for the refname, or we do not even have to have such a remote-tracking branch when this form is used). If <expect> is the empty string, then the named ref must not already exist.

Note that all forms other than –force-with-lease=<refname>:<expect> that specifies the expected current value of the ref explicitly are still experimental and their semantics may change as we gain experience with this feature.

  • "–no-force-with-lease" will cancel all the previous –force-with-lease on the command line.

A general note on safety: supplying this option without an expected value, i.e. as –force-with-lease or –force-with-lease=<refname> interacts very badly with anything that implicitly runs git fetch on the remote to be pushed to in the background, e.g. git fetch origin on your repository in a cronjob.

The protection it offers over –force is ensuring that subsequent changes your work wasn’t based on aren’t clobbered, but this is trivially defeated if some background process is updating refs in the background. We don’t have anything except the remote tracking info to go by as a heuristic for refs you’re expected to have seen & are willing to clobber.

If your editor or some other system is running git fetch in the background for you a way to mitigate this is to simply set up another remote:

git remote add origin-push $(git config remote.origin.url) git fetch origin-push

Now when the background process runs git fetch origin the references on origin-push won’t be updated, and thus commands like:

git push –force-with-lease origin-push

Will fail unless you manually run git fetch origin-push. This method is of course entirely defeated by something that runs git fetch –all, in that case you’d need to either disable it or do something more tedious like:

git fetch # update 'master' from remote git tag base master # mark our base point git rebase -i master # rewrite some commits git push –force-with-lease=master:base master:master

I.e. create a base tag for versions of the upstream code that you’ve seen and are willing to overwrite, then rewrite history, and finally force push changes to master if the remote version is still at base, regardless of what your local remotes/origin/master has been updated to in the background.

Alternatively, specifying –force-if-includes as an ancillary option along with –force-with-lease[=<refname>] (i.e., without saying what exact commit the ref on the remote side must be pointing at, or which refs on the remote side are being protected) at the time of "push" will verify if updates from the remote-tracking refs that may have been implicitly updated in the background are integrated locally before allowing a forced update.

про remote

remote – то, что заводится автоматически

После клонирования хранилища команды git push или git pull автоматически отправляют и получают его по первоначальному адресу. Потому что настройки, заданные при создании клона.

git config --list

Опция remote.origin.url задает исходный адрес; origin — имя первоначального хранилища. Как и имя ветки master, это соглашение. Мы можем изменить или удалить это сокращённое имя, но как правило, нет причин для этого.

Если оригинальное хранилище переехало, можно обновить его адрес командой

git config remote.origin.url git://новый.url/proj.git

Опция branch.master.merge задает удаленную ветку по умолчанию для git pull. В ходе первоначального клонирования она устанавливается на текущую ветку исходного хранилища, так что даже если HEAD исходного хранилища впоследствии переместится на другую ветку, pull будет верно следовать изначальной ветке.

Этот параметр обращается только к хранилищу, которое мы изначально клонировали и которое записано в параметре branch.master.remote. При выполнении pull из других хранилищ мы должны указать нужную ветку:

git pull git://пример.com/other.git master

Так что git часто можно использовать почти без аргументов. Но не всегда

Добавить удалённый (remote) репозиторий уже существующему локальному

Например

git remote add origin git@gitlab.com:agnessa/emacs-config.git

«git@gitlab.com:agnessa/emacs-config.git» - очевидимо, конкретный удалённый реп.

pushurl – или пушить в несколько репозиториев разом

You can choose to provide the name of a remote which you had previously configured using git-remote, git-config or even by a manual edit to the $GIT_DIR/config file. The URL of this remote will be used to access the repository. The refspec of this remote will be used by default when you do not provide a refspec on the command line. The entry in the config file would appear like this:

[remote "<name>"]
        url = <URL>
        pushurl = <pushurl>
        push = <refspec>
        fetch = <refspec>

The <pushurl> is used for pushes only. It is optional and defaults to <URL>. Pushing to a remote affects all defined pushurls or all defined urls if no pushurls are defined. Fetch, however, will only fetch from the first defined url if multiple urls are defined.

Так вот, если этих пушурлов будет несколько в одном таком remote, то git push remote будет пушить по всем. Если remote – origin, так просто git push будет пушить по всем.

Источник – man git-push. Упоминание, что так бывает – в емаксочатике. :)

git archive – скачать себе архивчик файлов

Команда git archive --format=zip --output git-archive-result-gitlab.zip --remote=git@gitlab.com:agnessa/emacs-config.git HEAD README.org org/emacs.org, запущенная вне локальной копии (и без знания о ней), но с ssh-ключом, позволяющим доступ к этому приватному репозиторию, создала мне архив git-archive-result-gitlab.zip с указанными файлами с учетом путей в самой их актуальной версии из мастер-ветки, и никуда не тянула остальное.

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

Если тянуть из удалённого репа (ключ --remote), то так можно с гитлабом и можно с репозиторием на vps-ке, где просто bare-репозиторий. Гитхаб такое не хочет позволять. Про всякое другое не зна, не пробовала.

Ну, и можно ещё shallow-репозитории делать.

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

git clone --depth 1 --filter=blob:none --no-checkout git@thatgitlab.com/group/project.git
cd project
git sparse-checkout init --cone
git sparse-checkout set путь/к/каталогу
git checkout HEAD
git archive --format=zip --output=каталог.zip путь/к/каталогу/

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

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

Куда писать инфу - почему не только и не столько в описание коммита

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

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

https://www.nikialeksey.com/2019/10/26/long-commit-message.html

Special GitLab References

GFM [gitlab flavored markdown] recognized special references.

You can easily reference e.g. an issue, a commit, a team member or even the whole team within a project.

GFM will turn that reference into a link so you can navigate between them easily.

GFM will recognize the following:

@foo : for specific team members or groups @all : for the whole team #123 : for issues !123 : for merge requests $123 : for snippets 1234567 : for commits [file](path/to/file) : for file references

GFM also recognizes references to commits, issues, and merge requests in other projects:

namespace/project#123 : for issues namespace/project!123 : for merge requests namespace/project@1234567 : for commits

[2015-03-31 Вт 10:19]

Stash

Если кто-то еще не пользуется git stash, советую обратить на эту команду пристальное внимание. Более чем удобно, занимаясь одним делом, «отложить» текущую работу в сторону и отвлечься, скажем, на срочное исправление бага, даже если он находится в другом бранче. После исправления и коммита можно преспокойно вернуться к начатому.

i. hack-hack-hack ii. git stash iii. fix-fix-fix iv. git commit -a -m 'bugfix #31337' v. git stash pop

Те же, кто знает про git stash, посмотрите на последнюю строку — её отличие от apply в том, что откладываемые результаты не остаются во временном хранилище (посмотрите git stash list после нескольких примений stash!)

Если изменение было фактически законченным, можно оформить коммит не отходят от кассы — git stash save 'commit msg' [2015-03-04 Ср 07:31] %

Интерактивное добавление файла в индекс с выбором отдельных чанков (частный случай -i)

git add -p # [2015-03-04 Ср 07:34]

Проверка «здоровья» репозитория и удаление из него мусора.

  • git fsck. Даже без –full неплохая проверка.
  • git count-objects. Проверка, сколько объектов будет потеряно и объём освобождаемого места при перепаковке репозитория.
  • git gc. Переупаковка локальных репозиториев и другие виды повседневных задач.

[2015-03-04 Ср 07:35]

(git fsck –full — Все объекты, на которые никто не указывает.)

Настройки при клонировании

ключ

Если нужно задать конкретный ключ, это в ~/.ssh/config, потому что этим рулит ssh (об этом же тут: ключ для конкретного сервиса). Или core.sshCommand в .gitconfig.

И дальше как обычно:

git clone ssh:user@host:port/path

bare

Опция --bare делает репозиторий, в котором некуда делать checkout. Поэтому то, что в обычном случае живет в подкаталоге .git, оказывается сразу в основном каталоге. У меня в таком виде живут большинство репозиториев на vps. Потому что они мне нужны для синхронизации с разных устройств, а не для непосредственной работы с файлами.

не на всю глубину

Это получаются shallow репозитории, типа, «поверхностные, пустые, мелководные, неглубокие». Опций по выбиранию, что надо и не надо, вообще много. Но мне пригождалось только depth.

–depth <depth>
Глубина задана числом коммитов. Мне иногда надо копию актуального состояния + возможность обновлять при изменении, так это оно.

Create a shallow clone with a history truncated to the specified number of commits. Implies –single-branch unless –no-single-branch is given to fetch the histories near the tips of all branches. If you want to clone submodules shallowly, also pass –shallow-submodules.

–shallow-since=<date>
Глубина задана указанием момента. Всё, что после. Именно это мне не было надо, но так-то симпатичное.

Create a shallow clone with a history after the specified time.

Если нужно сколько-то конкретных файлов, можно посмотреть на git archive.

.git/config для i2p

You have local tunnels set up as per the guide for pull.git.repo.i2p on port 9450 and push.git.repo.i2p on port 9451

You are linking the local Git repository to the hosted repository test.git. If the hosted repository is a fork, replace "test.git" with "base/fork.git" etc.

[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
[remote "origin"]
    url = git://127.0.0.1:9450/test.git
    pushurl = ssh://username@127.0.0.1:9451/srv/git/test.git
    fetch = +refs/heads/*:refs/remotes/origin/*

Связанные штуки

vcsh (для хранения конфигов)

http://vcs-home.branchable.com/ - vcsh - способ хранить в git конфиги домашней папки так, что можно два соседних файла держать в разных репозиториях.

While it may appear that there's an overwhelming amount of documentation and while the explanation of the concepts behind vcsh needs to touch a few gory details of git internals, getting started with vcsh is extremely simple.

Let's say you want to version control your vim configuration:

vcsh init vim
vcsh vim add ~/.vimrc ~/.vim
vcsh vim commit -m 'Initial commit of my Vim configuration'
# optionally push your files to a remote
vcsh vim remote add origin <remote>
vcsh vim push -u origin master
# from now on you can push additional commits like this
vcsh vim push

If all that looks a lot like standard git, that's no coincidence; it's a design feature. [2015-03-08 Вс 20:57]

bup - backup system based on the git packfile format

Backup system based on the git packfile format, providing fast incremental saves and global deduplication (among and within files, including virtual machine images). Current release is 0.26, and the development branch is master.

git-annex – иногда смотрела в ту сторону

В целом поняла, что это тема для управления файлами через git и в том числе, когда сами файлы недоступны. Но пока моя голова не есть это мочь :)


Если у вас есть мысли, комментарии, предложения или отклики по поводу этой страницы или этого цифрового сада в целом, напишите мне сообщение на me (at) ladykosha.ru. Мне ооочень интересно!

Задонатить.


An IndieWeb Webring 🕸💍