Я решил свою проблему после чтения комментария от @Patrick
Поведение, которое Вы описываете, является emacs режимом.
Который заставил меня понять, что я не был в vi режиме. Я также поместил
set -o vi
Строка в моем .bash_profile и теперь все хорошо работает. То, которое является странным, потому что от то, что я считал .bash_profile, используется для оболочек входа в систему и .bashrc для интерактивных оболочек невхода в систему, которым я верил бы, является моей ситуацией.
Я написал следующую реализацию на языке TXR . Сначала я использовал ваш алгоритм определения количества перемещений. Однако я заметил, что он дает бесполезные результаты :, например, он идентифицировал положительные значения для «перемещенных строк» в изменениях, которые не содержали ничего, кроме строк +
, просто потому, что некоторые из строк +
были дубликатами друг с другом. Новый алгоритм обсуждается в примечаниях в конце.
Полная программа:
#!/usr/bin/env txr
@(bind option-spec
@(list (opt nil "since" :str
"Specifies the starting date (passed \
\ through to git); it is mandatory.")
(opt nil "help" :bool
"Prints this help text")))
@(bind parsed-opts @(getopts option-spec *args*))
@(if (or [parsed-opts "help"] (not [parsed-opts "since"])))
@ (output)
usage: @{self-path} --since=<date> -- git arguments
@ (end)
@ (do (opthelp option-spec)
(exit 0))
@(end)
@(do
(defun histogram (strings)
[group-reduce (hash :equal-based) identity (op succ @1) strings 0])
(defun moved (a b)
(let* ((hist-a (histogram a))
(hist-b (histogram b))
(isec [hash-isec hist-a hist-b min]))
[reduce-left + (hash-values isec) 0])))
@(next (open-command `git log --since=@[parsed-opts "since"] \
\ --date=short --pretty=format:"%H:%ad%x09" \
\ --numstat @{parsed-opts.out-args}`))
@(repeat)
@sha:@date@\t
@ (collect :gap 0)
@added@\t@removed@\t@rawpath
@ (next :string rawpath)
@ (cases)
@pro/{@before => @after}/@epi
@ (bind path `@pro/@after/@epi`)
@ (or)
@before => @after
@ (bind path after)
@ (or)
@ (bind path rawpath)
@ (end)
@ (next (open-command `git show -p @sha -- @path`))
@ (collect :vars ((+line nil) (-line nil)))
@ (cases)
+@{+line}
@ (or)
-@{-line}
@ (end)
@ (end)
@ (flatten -line +line)
@ (bind moved @(moved +line -line))
@ (end)
@ (output)
@date@\t
@ (repeat)
@added@\t@removed@\t@moved@\t@rawpath
@ (end)
@ (end)
@(end)
У меня есть это в помеченном исполняемом файле с именем movedlines.txr
и пример использования:
$./movedlines.txr --since=2017-01-01 path/to
Опция --since
является обязательной; path/to
— необязательный аргумент, передаваемый в git
. Если вы не укажете обязательный параметр или укажете --help
, программа распечатает сводку справки и завершит работу.
Примечания:
Я немного изменил формат вывода вашего примера команды git
, чтобы включить SHA слева от даты, разделенной двоеточием :см. %H
. Программа анализирует это, а затем может использовать SHA для выполнения git show -p
для каждого файла в каждом наборе.SHA опускается, когда имитация вывода повторяется программой с дополнительным перемещенным столбцом.
Сложность заключается в том, что вывод git показывает переименования. Синтаксис распадается на три случая, которые четко и легко разбираются по случаям с использованием конструкции @(cases)
. Если переименовывается весь путь, переименовывается from => to
. Если переименовываются только некоторые компоненты, это be/fore/{from => to}/after
. Я не знаю, встречается ли в одном пути синтаксис с несколькими фигурными скобками; Я этого не видел. Мы должны изменить этот синтаксис и преобразовать его в простой путь, потому что git его не понимает. т.е. мы должны преобразовать be/fore/{from => to}/after
в be/fore/to/after
. Возможно, есть опция git, чтобы пути выводились таким образом без обозначений; Я не стала искать.
Сценарий не устойчив к пробелам в именах файлов из-за использования open-command
. Для этого нам нужен open-process
, который принимает список аргументов.
Алгоритм вычисляет отдельные частотные гистограммы diff -
и +
строк (минус их ведущие -
или+
). Затем он вычисляет пересечение этих наборов, при этом сохраняются только те элементы гистограммы, которые встречаются на обеих гистограммах. Функция соединения для пересечения — min
. Например, предположим, что строка abc
была добавлена 5 раз и удалена 3 раза. (min 3 5)
равно 3
и это количество перемещенных abc
строк. Это коммутирует. Если удалить 3 вхождения abc
и добавить 5, это означает 3 хода. Это ни в коем случае не позиционируется как какой-то идеальный алгоритм обнаружения ходов. У него есть очевидные подводные камни, такие как неопределение строк, которые де-факто перемещаются, но также претерпевают тривиальные изменения пробелов, такие как отступы.