git - .PO (gettext) 文件的 3 向 Git merge 驱动程序在哪里?

标签 git localization merge conflict gettext

我已经关注了

[attr]POFILE merge=merge-po-files

locale/*.po POFILE

.gitattributes 中,当相同的本地化文件(例如 locale/en.po)被修改时,我希望 merge 分支以正常工作在平行分支中。我目前正在使用以下 merge 驱动程序:

#!/bin/bash
# git merge driver for .PO files (gettext localizations)
# Install:
# git config merge.merge-po-files.driver "./bin/merge-po-files %A %O %B"

LOCAL="${1}._LOCAL_"
BASE="${2}._BASE_"
REMOTE="${3}._REMOTE_"

# rename to bit more meaningful filenames to get better conflict results
cp "${1}" "$LOCAL"
cp "${2}" "$BASE"
cp "${3}" "$REMOTE"

# merge files and overwrite local file with the result
msgcat "$LOCAL" "$BASE" "$REMOTE" -o "${1}" || exit 1

# cleanup
rm -f "$LOCAL" "$BASE" "$REMOTE"

# check if merge has conflicts
fgrep -q '#-#-#-#-#' "${1}" && exit 1

# if we get here, merge is successful
exit 0

但是,msgcat 太笨了,这不是真正的三向 merge 。 例如,如果我有

  1. 基本版本

    msgid "foo"
    msgstr "foo"
    
  2. 本地版本

    msgid "foo"
    msgstr "bar"
    
  3. 远程版本

    msgid "foo"
    msgstr "foo"
    

我会以冲突告终。 但是,真正的三向 merge 驱动程序会输出正确的 merge :

msgid "foo"
msgstr "bar"

请注意,我不能简单地将 --use-first 添加到 msgcat,因为 REMOTE 可能包含更新的翻译。此外,如果 BASE、LOCAL 和 REMOTE 都是唯一的,我仍然希望发生冲突,因为那真的会发生冲突。

我需要更改什么才能使其正常工作?如果可能的话,比“#-#-#-#-#”更少疯狂的冲突标记的奖励积分。

最佳答案

[这是一个历史版本,请参阅我对 2021 年版本的 merge 驱动程序的另一个更新的回答。]

这是一个有点复杂的示例驱动程序,它似乎输出正确的 merge ,其中可能包含一些本应被本地或远程版本删除的翻译。
什么都不应该遗漏,所以这个驱动程序在某些情况下只会增加一些额外的困惑。

此版本使用 gettext native 冲突标记,看起来像 #-#-#-#-#结合 fuzzy标记而不是普通的 git 冲突标记。
msgcat 中解决错误(或功能)的驱动程序有点难看和 msguniq :

#!/bin/bash
# git merge driver for .PO files
# Copyright (c) Mikko Rantalainen <mikko.rantalainen@peda.net>, 2013
# License: MIT

ORIG_HASH=$(git hash-object "${1}")
WORKFILE=$(git ls-tree -r HEAD | fgrep "$ORIG_HASH" | cut -b54-)
echo "Using custom merge driver for $WORKFILE..."

LOCAL="${1}._LOCAL_"
BASE="${2}._BASE_"
REMOTE="${3}._REMOTE_"

LOCAL_ONELINE="$LOCAL""ONELINE_"
BASE_ONELINE="$BASE""ONELINE_"
REMOTE_ONELINE="$REMOTE""ONELINE_"

OUTPUT="$LOCAL""OUTPUT_"
MERGED="$LOCAL""MERGED_"
MERGED2="$LOCAL""MERGED2_"

TEMPLATE1="$LOCAL""TEMPLATE1_"
TEMPLATE2="$LOCAL""TEMPLATE2_"
FALLBACK_OBSOLETE="$LOCAL""FALLBACK_OBSOLETE_"

# standardize the input files for regexping
# default to UTF-8 in case charset is still the placeholder "CHARSET"
cat "${1}" | perl -npe 's!(^"Content-Type: text/plain; charset=)(CHARSET)(\\n"$)!$1UTF-8$3!' | msgcat --no-wrap --sort-output - > "$LOCAL"
cat "${2}" | perl -npe 's!(^"Content-Type: text/plain; charset=)(CHARSET)(\\n"$)!$1UTF-8$3!' | msgcat --no-wrap --sort-output - > "$BASE"
cat "${3}" | perl -npe 's!(^"Content-Type: text/plain; charset=)(CHARSET)(\\n"$)!$1UTF-8$3!' | msgcat --no-wrap --sort-output - > "$REMOTE"

# convert each definition to single line presentation
# extra fill is required to make sure that git separates each conflict 
perl -npe 'BEGIN {$/ = "\n\n"}; s/#\n$/\n/s; s/#/##/sg; s/\n/#n/sg; s/#n$/\n/sg; s/#n$/\n/sg; $_.="#fill#\n" x 4' "$LOCAL" > "$LOCAL_ONELINE"
perl -npe 'BEGIN {$/ = "\n\n"}; s/#\n$/\n/s; s/#/##/sg; s/\n/#n/sg; s/#n$/\n/sg; s/#n$/\n/sg; $_.="#fill#\n" x 4' "$BASE"  > "$BASE_ONELINE"
perl -npe 'BEGIN {$/ = "\n\n"}; s/#\n$/\n/s; s/#/##/sg; s/\n/#n/sg; s/#n$/\n/sg; s/#n$/\n/sg; $_.="#fill#\n" x 4' "$REMOTE"  > "$REMOTE_ONELINE"

# merge files using normal git merge machinery
git merge-file -p --union -L "Current (working directory)" -L "Base (common ancestor)" -L "Incoming (applied changeset)" "$LOCAL_ONELINE" "$BASE_ONELINE" "$REMOTE_ONELINE" > "$MERGED"
MERGESTATUS=$?

# remove possibly duplicated headers (workaround msguniq bug http://comments.gmane.org/gmane.comp.gnu.gettext.bugs/96)
cat "$MERGED" | perl -npe 'BEGIN {$/ = "\n\n"}; s/^([^\n]+#nmsgid ""#nmsgstr ""#n.*?\n)([^\n]+#nmsgid ""#nmsgstr ""#n.*?\n)+/$1/gs' > "$MERGED2"

# remove lines that have totally empty msgstr
# and convert back to normal PO file representation
cat "$MERGED2" | grep -v '#nmsgstr ""$' | grep -v '^#fill#$' | perl -npe 's/#n/\n/g; s/##/#/g' > "$MERGED"

# run the output through msguniq to merge conflicts gettext style
# msguniq seems to have a bug that causes empty output if zero msgids
# are found after the header. Expected output would be the header...
# Workaround the bug by adding an empty obsolete fallback msgid
# that will be automatically removed by msguniq

cat > "$FALLBACK_OBSOLETE" << 'EOF'

#~ msgid "obsolete fallback"
#~ msgstr ""

EOF
cat "$MERGED" "$FALLBACK_OBSOLETE" | msguniq --no-wrap --sort-output > "$MERGED2"


# create a hacked template from default merge between 3 versions
# we do this to try to preserve original file ordering
msgcat --use-first "$LOCAL" "$REMOTE" "$BASE" > "$TEMPLATE1"
msghack --empty "$TEMPLATE1" > "$TEMPLATE2"
msgmerge --silent --no-wrap --no-fuzzy-matching "$MERGED2" "$TEMPLATE2" > "$OUTPUT"

# show some results to stdout
if grep -q '#-#-#-#-#' "$OUTPUT"
then
    FUZZY=$(cat "$OUTPUT" | msgattrib --only-fuzzy --no-obsolete --color | perl -npe 'BEGIN{ undef $/; }; s/^.*?msgid "".*?\n\n//s')
    if test -n "$FUZZY"
    then
        echo "-------------------------------"
        echo "Fuzzy translations after merge:"
        echo "-------------------------------"
        echo "$FUZZY"
        echo "-------------------------------"
    fi
fi

# git merge driver must overwrite the first parameter with output
mv "$OUTPUT" "${1}"

# cleanup
rm -f "$LOCAL" "$BASE" "$REMOTE" "$LOCAL_ONELINE" "$BASE_ONELINE" "$REMOTE_ONELINE" "$MERGED" "$MERGED2" "$TEMPLATE1" "$TEMPLATE2" "$FALLBACK_OBSOLETE"

# return conflict if merge has conflicts according to msgcat/msguniq
grep -q '#-#-#-#-#' "${1}" && exit 1

# otherwise, return git merge status
exit $MERGESTATUS

# Steps to install this driver:
# (1) Edit ".git/config" in your repository directory
# (2) Add following section:
#
# [merge "merge-po-files"]
#   name = merge po-files driver
#   driver = ./bin/merge-po-files %A %O %B
#   recursive = binary
#
# or
#
# git config merge.merge-po-files.driver "./bin/merge-po-files %A %O %B"
#
# The file ".gitattributes" will point git to use this merge driver.

关于此驱动程序的简短说明:

  • 它将常规 PO 文件格式转换为单行格式,其中每行都是一个翻译条目。
  • 然后它使用常规的 git merge-file --union进行 merge , merge 后生成的单行格式将转换回常规 PO 文件格式。
    实际的冲突解决是在此之后使用 msguniq 完成的,
  • 然后最终将生成的文件与常规 msgcat 生成的模板 merge merge 原始输入文件以恢复可能丢失的元数据。

警告:此驱动程序将使用 msgcat --no-wrap.PO 上文件并将强制UTF-8如果未指定实际编码,则编码。
如果您想使用此 merge 驱动程序但始终检查结果,请更改最终的 exit $MERGESTATUS看起来像exit 1 .

从该驱动程序获取 merge 冲突后,解决冲突的最佳方法是使用virtaal 打开冲突文件。并选择 Navigation: Incomplete .
我发现此 UI 是解决冲突的非常好的工具。

关于git - .PO (gettext) 文件的 3 向 Git merge 驱动程序在哪里?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16214067/

相关文章:

git - "git add -p"是否有任何 GUI 工具?

Mysql从2个不同的表中选择并合并结果

python - Pandas 合并 : combining column values & merging new column values to the same row

javascript - 如何将批处理代码与vbscript代码合并

eclipse - 在 Eclipse 中克隆后立即取消暂存更改

git - 如何使用 heroku CLI 连接到选定的应用程序

windows - 为什么 Windows 上的 git diff 会警告 "terminal is not fully functional"?

jsp - 在带有 Spring 的 JSP 中使用什么进行本地化?

objective-c - 首选语言 iOS 9

cocoa - 如何在 Cocoa 中获取 NSLocale 的显示名称?