Ошибки компоновщика могут произойти, если заголовочный файл и связанная с ним общая библиотека (файл .lib) не синхронизируются. Позволь мне объяснить.
Как работают линкеры? Линкер соответствует объявлению функции (объявленному в заголовке) с его определением (в общей библиотеке) путем сравнения их подписи. Вы можете получить ошибку компоновщика, если компоновщик не найдет определение функции, которое идеально подходит.
Возможно ли получить ошибку компоновщика, даже если объявление и определение, похоже, совпадают? Да! Они могут выглядеть одинаково в исходном коде, но это действительно зависит от того, что видит компилятор. По сути, вы можете столкнуться с такой ситуацией:
// header1.h
typedef int Number;
void foo(Number);
// header2.h
typedef float Number;
void foo(Number); // this only looks the same lexically
Обратите внимание, что хотя обе декларации функций выглядят одинаково в исходном коде, но они действительно различаются в зависимости от компилятора.
Вы можете спросить, как это получается в такой ситуации? Включите пути, конечно! Если при компиляции разделяемой библиотеки путь include приводит к header1.h
, и вы в конечном итоге используете header2.h
в своей собственной программе, вы оставите царапины на своем заголовке, задаваясь вопросом, что произошло (каламбур).
Пример того, как это может произойти в реальном мире, объясняется ниже.
У меня есть два проекта: graphics.lib
и main.exe
. Оба проекта зависят от common_math.h
. Предположим, что библиотека экспортирует следующую функцию:
// graphics.lib
#include "common_math.h"
void draw(vec3 p) { ... } // vec3 comes from common_math.h
И затем вы идете вперед и включаете библиотеку в свой собственный проект.
// main.exe
#include "other/common_math.h"
#include "graphics.h"
int main() {
draw(...);
}
Boom! Вы получаете ошибку компоновщика, и вы понятия не имеете, почему она терпит неудачу. Причина в том, что общая библиотека использует разные версии одного и того же include common_math.h
(я сделал это очевидным здесь в этом примере, включив другой путь, но это может быть не всегда так очевидно. Возможно, путь include отличается в настройки компилятора).
Обратите внимание, что в этом примере компоновщик сказал бы вам, что не смог найти draw()
, когда на самом деле вы знаете, что он явно экспортируется библиотекой. Вы могли часами царапать себе голову, думая, что пошло не так. Дело в том, что компоновщик видит другую подпись, потому что типы параметров немного отличаются. В этом примере vec3
является другим типом в обоих проектах в отношении компилятора. Это может произойти из-за того, что они происходят из двух немного разных файлов include (возможно, включенные файлы поступают из двух разных версий библиотеки).
DUMPBIN - ваш друг, если вы используете Visual Studio. Я уверен, что другие компиляторы имеют другие подобные инструменты.
Процесс выглядит следующим образом:
[1] По проекту я имею в виду набор исходных файлов, которые связаны друг с другом для создания либо библиотеки, либо исполняемого файла .
РЕДАКТИРОВАТЬ 1: Переписать первый раздел, который будет легче понять. Пожалуйста, прокомментируйте ниже, чтобы сообщить мне, нужно ли что-то еще исправлять. Спасибо!
Для всех практических целей, первое вещь, о которой вы должны беспокоиться, - ИЗМЕНЕНИЕ ВАШИХ ПАРОЛЕЙ! Из вашего вопроса неясно, является ли ваш репозиторий git полностью локальным или у вас еще есть удаленный репозиторий; если он удален и не защищен от других, у вас есть проблема. Если кто-то клонировал этот репозиторий, прежде чем исправить это, у вас будет копия ваших паролей на их локальной машине, и вы не сможете заставить их обновить свою «фиксированную» версию, если она уйдет из истории. Единственная безопасная вещь, которую вы можете сделать, - это изменить свой пароль на что-то еще, где бы вы его ни использовали.
С этим в сторону, вот как это исправить. GitHub ответил именно на этот вопрос в качестве FAQ :
Примечание для пользователей Windows: вместо синглов в этой команде используйте двойные кавычки (")
git filter-branch --index-filter \
'git update-index --remove filename' <introduction-revision-sha1>..HEAD
git push --force --verbose --dry-run
git push --force
Имейте в виду, что после того, как вы переместили этот код в удаленный репозиторий, такой как GitHub, а другие клонировали этот удаленный репозиторий, теперь вы находитесь в ситуации, когда вы переписываете историю. Когда другие пытаются снести ваши последние изменения после этого, они получат сообщение о том, что изменения не могут быть применены, потому что это не ускоренная перемотка вперед.
Чтобы исправить это, им придется либо удалить существующий репозиторий, либо повторно клонировать его , или следуйте инструкциям в разделе «ВОССТАНОВЛЕНИЕ ОТ РЕЖИМА UPSTREAM» в git-rebase manpage .
В будущем, если вы случайно совершите некоторые изменения с конфиденциальной информацией, но вы замечаете, прежде чем нажимать на удаленный репозиторий, есть несколько простых исправлений. Если вы последний коммит является тем, кто добавляет конфиденциальную информацию, вы можете просто удалить конфиденциальную информацию n, а затем запустите:
git commit -a --amend
Это изменит предыдущую фиксацию с внесенными вами новыми изменениями, включая полное удаление файлов, выполненное с помощью git rm
. Если изменения вернулись в историю, но все же не были перенесены в удаленный репозиторий, вы можете сделать интерактивную перезагрузку:
git rebase -i origin/master
Это открывает редактор с фиксациями, которые вы сделали со своего последнего общего предка с удаленным репозиторием. Измените «pick» на «редактировать» на любых строках, представляющих фиксацию с конфиденциальной информацией, и сохраните и закройте. Git проведет изменения и оставит вас в месте, где вы можете:
$EDITOR file-to-fix
git commit -a --amend
git rebase --continue
Для каждого изменения с конфиденциальной информацией. В конце концов, вы вернетесь в свою ветку, и вы можете спокойно нажать новые изменения.
Я рекомендую этот скрипт Дэвида Андерхилла, работавший как прелесть для меня.
Он добавляет эти команды в дополнение к фильтру-ветке natacado, чтобы очистить беспорядок, который он оставляет:
rm -rf .git/refs/original/
git reflog expire --all
git gc --aggressive --prune
Полный скрипт (весь кредит Дэвиду Андерхиллу)
#!/bin/bash
set -o errexit
# Author: David Underhill
# Script to permanently delete files/folders from your git repository. To use
# it, cd to your repository's root and then run the script with a list of paths
# you want to delete, e.g., git-delete-history path1 path2
if [ $# -eq 0 ]; then
exit 0
fi
# make sure we're at the root of git repo
if [ ! -d .git ]; then
echo "Error: must run this script from the root of a git repository"
exit 1
fi
# remove all paths passed as arguments from the history of the repo
files=$@
git filter-branch --index-filter \
"git rm -rf --cached --ignore-unmatch $files" HEAD
# remove the temporary history git-filter-branch
# otherwise leaves behind for a long time
rm -rf .git/refs/original/ && \
git reflog expire --all && \
git gc --aggressive --prune
Последние две команды могут работать лучше, если они изменены на следующие:
git reflog expire --expire=now --all && \
git gc --aggressive --prune=now
Чтобы быть ясным: принятый ответ правильный. Сначала попробуйте. Тем не менее, это может быть излишне сложным для некоторых случаев использования, особенно если вы столкнулись с такими неприятными ошибками, как «фатальная: плохая ревизия - пустая» или действительно не заботятся об истории вашего репо.
Альтернативой будет:
Это, конечно, удалит все фиксации ветвей истории, а также проблем как с вашим реестром github, так и с местным реестром git. Если это неприемлемо, вам придется использовать альтернативный подход.
Назовите это ядерным вариантом.
Вы можете использовать git forget-blob
.
Использование довольно просто git forget-blob file-to-forget
. Вы можете получить дополнительную информацию здесь
Он исчезнет из всех коммитов вашей истории, reflog, тегов и т. д.
Я сталкиваюсь с той же проблемой время от времени, и каждый раз, когда я должен возвращаться к этому сообщению и другим, вот почему я автоматизировал процесс.
Кредиты вкладчикам из Stack Overflow, которые позволили мне собрать это вместе
Итак, это выглядит примерно так:
git rm --cached /config/deploy.rb
echo /config/deploy.rb >> .gitignore
Удалить кеш для отслеживаемого файла из git и добавить этот файл в список
blockquote>.gitignore
Изменение паролей - хорошая идея, но для процесса удаления паролей из истории вашего репо я рекомендую BFG Repo-Cleaner , более быструю и простую альтернативу git-filter-branch
, явно предназначенную для удаление личных данных из Git repos.
Создайте файл private.txt
, в котором перечислены пароли и т. д., которые вы хотите удалить (одна запись в строке), а затем выполните следующую команду:
$ java -jar bfg.jar --replace-text private.txt my-repo.git
Все файлы под пороговым размером (по умолчанию 1 МБ) в истории вашего репо будут отсканированы, и любая соответствующая строка (которая не входит в ваш последний commit] будет заменена на строку " *** REMOVED ***». Затем вы можете использовать git gc
для удаления мертвых данных:
$ git gc --prune=now --aggressive
BFG обычно на 10-50 раз быстрее, чем запуск git-filter-branch
, и опции упрощены и адаптированы к этим двум распространенным операциям, случаи:
Полное раскрытие: я являюсь автором BFG Repo-Cleaner.
Использовать ветвь фильтра:
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch *file_path_relative_to_git_repo*' --prune-empty --tag-name-filter cat -- --all
git push origin *branch_name* -f
Вот мое решение в windows
git filter-branch --tree-filter "rm -f 'filedir / filename'" HEAD
git push - force
blockquote>убедитесь, что путь верен, иначе он не будет работать
Надеюсь, это поможет