2013-04-25 42 views
13

Zaten .gitattributes içinde.PO (gettext) dosyaları için 3 yollu Git birleştirme sürücüsü nerede?

[attr]POFILE merge=merge-po-files 

locale/*.po POFILE 

aşağıdaki var ve aynı yerelleştirme dosyası (örneğin locale/en.po) ile paralel branşlarda modifiye edildiğinde düzgün çalışabilmesi için şube birleştirme almak istiyorum. Şu anda aşağıdaki birleştirme sürücüsünü kullanıyorum:

#!/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 

Ancak msgcat çok aptal ve bu gerçek bir üç yollu birleştirme değil. Örneğin , ben

  1. TABAN versiyonu

    msgid "foo" 
    msgstr "foo" 
    
  2. YEREL sürüm

    msgid "foo" 
    msgstr "bar" 
    
  3. UZAKTAN sürüm

    msgid "foo" 
    msgstr "foo" 
    
varsa

Bir çakışma ile sonuçlanacağım. Ancak , gerçek üç yollu birleştirme sürücü olur çıktısı doğru birleştirme: UZAKTAN güncellenmiş çevirisini içerebilir çünkü sadece msgcat için --use-first ekleyemezsiniz

msgid "foo" 
msgstr "bar" 

Not. Ek olarak, eğer BASE, LOCAL ve REMOTE hepsi benzersiz ise, hala bir çatışma istiyorum, çünkü bu gerçekten bir çatışma olacaktır.

Bu işi yapmak için neyi değiştirmem gerekiyor? Mümkünse '# - # - # - # - #' daha az çakışma belirteci için bonus puanları.

+0

şansı var mı? – VonC

+0

Kdiff3 ile çakışan .PO dosyasını birleştirmeyi denediniz mi? Bende ve güzel değil. .PO dosyaları ile ilgili sorun, gerçekte, metin dosyaları gibi görünen ikili veritabanı dosyalarıdır. Metin dosyalarını birleştirmek için tasarlanmış herhangi bir araç başarısız olur. –

cevap

1

Mikko'nun cevabından biraz ilham alarak, git-whistles Ruby gemine tam teşekküllü 3 yönlü birleşme ekledik.

git-merge veya Perl ile dizeyi yeniden yazma ve yalnızca Gettext araçlarıyla PO dosyalarını işleme.

İşte (MIT lisanslı) kod: Eğer KDiff3 gibi başka bir birleştirme aracı, (3-yollu) de kullanabilir

#!/bin/sh 
# 
# Three-way merge driver for PO files 
# 
set -e 

# failure handler 
on_error() { 
    local parent_lineno="$1" 
    local message="$2" 
    local code="${3:-1}" 
    if [[ -n "$message" ]] ; then 
    echo "Error on or near line ${parent_lineno}: ${message}; exiting with status ${code}" 
    else 
    echo "Error on or near line ${parent_lineno}; exiting with status ${code}" 
    fi 
    exit 255 
} 
trap 'on_error ${LINENO}' ERR 

# given a file, find the path that matches its contents 
show_file() { 
    hash=`git hash-object "${1}"` 
    git ls-tree -r HEAD | fgrep "$hash" | cut -b54- 
} 

# wraps msgmerge with default options 
function m_msgmerge() { 
    msgmerge --force-po --quiet --no-fuzzy-matching [email protected] 
} 

# wraps msgcat with default options 
function m_msgcat() { 
    msgcat --force-po [email protected] 
} 


# removes the "graveyard strings" from the input 
function strip_graveyard() { 
    sed -e '/^#~/d' 
} 

# select messages with a conflict marker 
# pass -v to inverse selection 
function grep_conflicts() { 
    msggrep [email protected] --msgstr -F -e '#-#-#' - 
} 

# select messages from $1 that are also in $2 but whose contents have changed 
function extract_changes() { 
    msgcat -o - $1 $2 \ 
    | grep_conflicts \ 
    | m_msgmerge -o - $1 - \ 
    | strip_graveyard 
} 


BASE=$1 
LOCAL=$2 
REMOTE=$3 
OUTPUT=$LOCAL 
TEMP=`mktemp /tmp/merge-po.XXXX` 

echo "Using custom PO merge driver (`show_file ${LOCAL}`; $TEMP)" 

# Extract the PO header from the current branch (top of file until first empty line) 
sed -e '/^$/q' < $LOCAL > ${TEMP}.header 

# clean input files 
msguniq --force-po -o ${TEMP}.base --unique ${BASE} 
msguniq --force-po -o ${TEMP}.local --unique ${LOCAL} 
msguniq --force-po -o ${TEMP}.remote --unique ${REMOTE} 

# messages changed on local 
extract_changes ${TEMP}.local ${TEMP}.base > ${TEMP}.local-changes 

# messages changed on remote 
extract_changes ${TEMP}.remote ${TEMP}.base > ${TEMP}.remote-changes 

# unchanged messages 
m_msgcat -o - ${TEMP}.base ${TEMP}.local ${TEMP}.remote \ 
    | grep_conflicts -v \ 
    > ${TEMP}.unchanged 

# messages changed on both local and remote (conflicts) 
m_msgcat -o - ${TEMP}.remote-changes ${TEMP}.local-changes \ 
    | grep_conflicts \ 
    > ${TEMP}.conflicts 

# messages changed on local, not on remote; and vice-versa 
m_msgcat -o ${TEMP}.local-only --unique ${TEMP}.local-changes ${TEMP}.conflicts 
m_msgcat -o ${TEMP}.remote-only --unique ${TEMP}.remote-changes ${TEMP}.conflicts 

# the big merge 
m_msgcat -o ${TEMP}.merge1 ${TEMP}.unchanged ${TEMP}.conflicts ${TEMP}.local-only ${TEMP}.remote-only 

# create a template to filter messages actually needed (those on local and remote) 
m_msgcat -o - ${TEMP}.local ${TEMP}.remote \ 
    | m_msgmerge -o ${TEMP}.merge2 ${TEMP}.merge1 - 

# final merge, adds saved header 
m_msgcat -o ${TEMP}.merge3 --use-first ${TEMP}.header ${TEMP}.merge2 

# produce output file (overwrites input LOCAL file) 
cat ${TEMP}.merge3 > $OUTPUT 

# check for conflicts 
if grep '#-#' $OUTPUT > /dev/null ; then 
    echo "Conflict(s) detected" 
    echo " between ${TEMP}.local and ${TEMP}.remote" 
    exit 1 
fi 
rm -f ${TEMP}* 
exit 0 
+0

Bu, kullanımım için yeterince kararlı değildi. Bunun doğru yön olduğunu kabul ediyorum, ancak bazı durumlarda birleşme başarısız oluyor. Örnek davayı paylaşamıyorum ve şu anda minimum test vakası oluşturmak için zamanım yok. Yeterli zamanım olduğunda sorunu ayıklamaya çalışacağım. Aşağıdaki karmaşık sürücüm başarıyla birleştirilebilir, ancak bu sürücü çirkin bir hack. –

3

Doğru metinlerde çakışma işaretçileriyle doğru metin tabanlı farklılık gösteren örnek bir sürücü. Ancak, çatışması durumunda, git mergetool sonuçlara bulaşmayacağından emin olabilirsiniz, bu gerçekten iyi değildir. Sadece bir metin düzenleyicisi kullanarak birleştirmeleri çelişkili düzeltmek istiyorsanız, o zaman bu ince olmalıdır: Bu sürücü hakkında

#!/bin/bash 
# git merge driver for .PO files 
# Copyright (c) Mikko Rantalainen <[email protected]>, 2013 
# License: MIT 

LOCAL="${1}._LOCAL_" 
BASE="${2}._BASE_" 
REMOTE="${3}._REMOTE_" 
MERGED="${1}._MERGED_" 
OUTPUT="$LOCAL""OUTPUT_" 

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

# standardize the input files for regexping 
msgcat --no-wrap --strict --sort-output "${1}" > "$LOCAL" 
msgcat --no-wrap --strict --sort-output "${2}" > "$BASE" 
msgcat --no-wrap --strict --sort-output "${3}" > "$REMOTE" 

# convert each definition to single line presentation 
# extra fill is required to make sure that git separates each conflict 
perl -npe 'BEGIN {$/ = "#\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"}; 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"}; 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 -L "Current (working directory)" -L "Base (common ancestor)" -L "Incoming (another change)" "$LOCAL_ONELINE" "$BASE_ONELINE" "$REMOTE_ONELINE" > "$MERGED" 
MERGESTATUS=$? 

# convert back to normal PO file representation 
cat "$MERGED" | grep -v '^#fill#$' | perl -npe 's/#n/\n/g; s/##/#/g' > "$OUTPUT" 

# 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" 

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. 

Kısa açıklama: her satırı bir çeviri giriştir tek satırlık biçimde düzenli PO dosya biçimini dönüştürür. Sonra birleştirme yapmak için düzenli git merge-file kullanır ve birleştirme sonra oluşan tek satır biçimi normal PO dosya biçimine geri dönüştürülür. Uyarı: bu sürücü .PO dosyasında msgcat --sort-output kullanır, böylece PO dosyalarınızı belirli bir sırada istiyorsanız, bu sizin için bir araç olmayabilir.

5

Yerel veya uzak sürüm tarafından silinmesi gereken bazı çevirileri içerebilecek doğru birleştirme çıktı gibi görünüyor, biraz karmaşık bir örnek sürücü.
Hiçbir şey eksik olmamalıdır, bu nedenle bu sürücü bazı durumlarda ek bir kargaşa ekler.

Bu sürüm, yerine normal git çakışması işaretçileri olan fuzzy bayrağıyla eşleşen gettext yerel çakışma belirtecini kullanır.

  • Tek hattına düzenli PO dosya biçimini dönüştürür: Bu sürücü hakkında

    #!/bin/bash 
    # git merge driver for .PO files 
    # Copyright (c) Mikko Rantalainen <[email protected]>, 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. 
    

    Kısa açıklama:
    sürücü çirkin biraz böcek (veya özellikleri) msgcat ve msguniq geçici çözüm için ise Her satırın bir çeviri girişi olduğu biçim.

  • Daha sonra, birleştirme işlemini yapmak için düzenli olarak git merge-file --union kullanır ve birleştirildikten sonra, sonuçta oluşan tek satır biçimi, normal PO dosya biçimine dönüştürülür.
    msguniq kullanarak bundan sonra gerçek çakışma çözümleme yapılır,
  • ve sonra nihayet muhtemelen kayıp meta geri orijinal giriş dosyalarını birleştirerek düzenli msgcat tarafından oluşturulan şablonu ile sonuçlanan dosya birleştirir.

Uyarı: bu sürücü .PO dosya üzerinde msgcat --no-wrap kullanacak ve gerçek kodlama belirtilmemişse UTF-8 kodlamayı zorlar.
Bu birleştirme sürücüsünü kullanmak, ancak sonuçları her zaman incelemek isterseniz, exit 1 gibi görünmek için son exit $MERGESTATUS'u değiştirin.

bu sürücüsünden çatışmayı birleştirme aldıktan sonra, çatışmayı sabitleme için en iyi yöntem virtaal ile çelişen dosyasını açmak ve Navigation: Incomplete seçmektir.
Bu kullanıcı arabirimini çakışma gidermek için oldukça güzel bir araç buluyorum.