ลบไฟล์ที่มีความละเอียดอ่อนและความมุ่งมั่นของพวกเขาจากประวัติ Git


353

ฉันต้องการวางโครงการ Git บน GitHub แต่มีไฟล์บางอย่างที่มีข้อมูลที่ละเอียดอ่อน (ชื่อผู้ใช้และรหัสผ่านเช่น /config/deploy.rb สำหรับ capistrano)

ฉันรู้ว่าฉันสามารถเพิ่มชื่อไฟล์เหล่านี้ใน. gitignoreแต่นี่จะไม่ลบประวัติของพวกเขาภายใน Git

ฉันไม่ต้องการเริ่มต้นใหม่อีกครั้งโดยการลบไดเรกทอรี /.git

มีวิธีที่จะลบทุกร่องรอยของไฟล์โดยเฉพาะอย่างยิ่งในประวัติศาสตร์ Git ของคุณหรือไม่



คำตอบ:


448

สำหรับการใช้งานจริงสิ่งแรกที่คุณควรกังวลคือเปลี่ยนรหัสผ่านของคุณ! ยังไม่ชัดเจนจากคำถามของคุณว่าที่เก็บ git ของคุณอยู่ในพื้นที่ทั้งหมดหรือว่าคุณมีที่เก็บระยะไกลที่อื่น หากอยู่ห่างไกลและไม่ปลอดภัยจากผู้อื่นคุณมีปัญหา หากมีใครโคลนที่เก็บข้อมูลนั้นก่อนที่คุณจะแก้ไขปัญหานี้พวกเขาจะมีสำเนารหัสผ่านของคุณในเครื่องของพวกเขาและไม่มีวิธีใดที่คุณสามารถบังคับให้พวกเขาอัปเดตเป็นรุ่น "คงที่" โดยหายไปจากประวัติ สิ่งเดียวที่ปลอดภัยที่คุณทำได้คือเปลี่ยนรหัสผ่านเป็นอย่างอื่นทุกที่ที่คุณใช้


ด้วยวิธีดังกล่าวนี่คือวิธีแก้ไข GitHub ตอบคำถามนั้นอย่างแน่นอนว่าเป็นคำถามที่พบบ่อย :

หมายเหตุสำหรับผู้ใช้ Windows : ใช้เครื่องหมายคำพูดคู่ (") แทนคำเดี่ยวในคำสั่งนี้

git filter-branch --index-filter \
'git update-index --remove PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA' <introduction-revision-sha1>..HEAD
git push --force --verbose --dry-run
git push --force

อัปเดต 2019:

นี่คือรหัสปัจจุบันจากคำถามที่พบบ่อย:

  git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA" \
  --prune-empty --tag-name-filter cat -- --all
  git push --force --verbose --dry-run
  git push --force

โปรดทราบว่าเมื่อคุณส่งรหัสนี้ไปที่ที่เก็บระยะไกลเช่น GitHub และคนอื่น ๆ ได้ทำการโคลนที่เก็บระยะไกลนั้นตอนนี้คุณอยู่ในสถานการณ์ที่คุณเขียนประวัติใหม่ เมื่อคนอื่นลองดึงการเปลี่ยนแปลงล่าสุดของคุณหลังจากนี้พวกเขาจะได้รับข้อความแจ้งว่าไม่สามารถใช้การเปลี่ยนแปลงได้เพราะไม่ใช่การส่งต่อที่รวดเร็ว

เพื่อแก้ไขปัญหานี้พวกเขาจะต้องลบพื้นที่เก็บข้อมูลที่มีอยู่และพวกเขาอีกครั้งโคลนมันหรือทำตามคำแนะนำภายใต้หัวข้อ "การกู้คืนจากต้นน้ำ Rebase" ในmanpage-git rebase

เคล็ดลับ : ดำเนินการgit rebase --interactive


ในอนาคตหากคุณยอมรับการเปลี่ยนแปลงบางอย่างด้วยข้อมูลที่ละเอียดอ่อน แต่คุณสังเกตเห็นก่อนที่จะผลักดันไปยังที่เก็บข้อมูลระยะไกลจะมีการแก้ไขที่ง่ายขึ้น หากคุณยอมรับเป็นคนสุดท้ายที่จะเพิ่มข้อมูลที่ละเอียดอ่อนคุณสามารถลบข้อมูลที่ละเอียดอ่อนจากนั้นเรียกใช้:

git commit -a --amend

ที่จะแก้ไขก่อนหน้านี้กระทำกับการเปลี่ยนแปลงใหม่ ๆ git rmที่คุณได้ทำรวมทั้งการลบไฟล์ทั้งหมดทำได้ด้วย หากการเปลี่ยนแปลงนั้นย้อนกลับไปในประวัติศาสตร์ แต่ยังไม่ได้ถูกผลักไปยังที่เก็บระยะไกลคุณสามารถทำการรีบูตแบบโต้ตอบ:

git rebase -i origin/master

ที่เปิดตัวแก้ไขด้วยการกระทำที่คุณทำไว้ตั้งแต่บรรพบุรุษร่วมกันครั้งสุดท้ายของคุณกับที่เก็บระยะไกล เปลี่ยน "เลือก" เป็น "แก้ไข" บนบรรทัดใด ๆ ที่แสดงถึงการส่งข้อมูลที่ละเอียดอ่อนและบันทึกและออก Git จะดำเนินการตามการเปลี่ยนแปลงต่างๆและวางคุณไว้ในจุดที่คุณสามารถ:

$EDITOR file-to-fix
git commit -a --amend
git rebase --continue

สำหรับการเปลี่ยนแปลงแต่ละครั้งด้วยข้อมูลที่ละเอียดอ่อน ในที่สุดคุณจะกลับมาที่สาขาของคุณและคุณสามารถผลักดันการเปลี่ยนแปลงใหม่ได้อย่างปลอดภัย


5
เพื่อนที่สมบูรณ์แบบนั่นเป็นคำตอบที่ดี คุณบันทึกวันของฉัน
zzeroo

18
เพียงเพื่อเพิ่มหนึ่งบิต - บน Windows คุณควรใช้เครื่องหมายคำพูดคู่ (") แทนซิงเกิ้ล
ripper234

4
เตรียมพร้อมที่จะใช้งานได้ ฉันหลงทางในการแปล ฉันใช้ลิงก์แทนคำสั่งที่นี่ นอกจากนี้คำสั่งของ Windows ก็ต้องการการอ้างอิงสองครั้งในฐานะ ripper234 กล่าวถึงเส้นทางแบบเต็มตามที่ MigDus แนะนำและไม่รวมอักขระ "\" ที่ลิงก์วางเป็นตัวบ่งชี้การตัดบรรทัดใหม่ คำสั่งสุดท้ายดูเหมือน: git filter-branch --force --index-filter "git rm --cached --ignore-unmatch src [โครงการ] [ไฟล์] [ext]" --prune-empty - แท็ก - ชื่อตัวกรองแมว -
Eric Swanson

3
ดูเหมือนว่ามีความแตกต่างที่สำคัญระหว่างfilter-branchรหัสของคุณและในหน้า GitHub ที่คุณเชื่อมโยง --prune-empty --tag-name-filter cat -- --allเช่นสายที่ 3 ของพวกเขา วิธีแก้ปัญหาเปลี่ยนไปหรือฉันขาดอะไรไป?
geotheory

2
วิธีการแก้ปัญหานี้ดูค่อนข้างดี แต่ถ้าฉันได้แนะนำไฟล์ที่จะลบในการกระทำครั้งแรก<introduction-revision-sha1>..HEADไม่ทำงาน มันจะลบไฟล์จากการกระทำที่สองเป็นต้นไป (ฉันจะรวมการคอมมิชชันเริ่มต้นไว้ในช่วงของการคอมมิชชันได้อย่างไร) วิธีการบันทึกนั้นชี้ให้เห็นที่นี่: help.github.com/articles/… git filter-branch --force --index-filter \ 'git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA' \ --prune-empty --tag-name-filter cat -- --all
white_gecko

91

การเปลี่ยนรหัสผ่านของคุณเป็นความคิดที่ดี แต่สำหรับขั้นตอนการลบรหัสผ่านออกจากประวัติของ repo ของคุณฉันขอแนะนำBFG Repo-Cleanerทางเลือกที่เร็วและง่ายกว่าเพื่อการgit-filter-branchออกแบบที่ชัดเจนสำหรับการลบข้อมูลส่วนตัวออกจาก repos Git

สร้างprivate.txtไฟล์ที่แสดงรหัสผ่าน ฯลฯ ที่คุณต้องการลบ (หนึ่งรายการต่อบรรทัด) จากนั้นเรียกใช้คำสั่งนี้:

$ java -jar bfg.jar  --replace-text private.txt  my-repo.git

ไฟล์ทั้งหมดที่อยู่ภายใต้ขีด จำกัด ขนาด (1MB โดยค่าเริ่มต้น) ในประวัติ repo ของคุณจะถูกสแกนและสตริงที่ตรงกัน (ที่ไม่ได้อยู่ในการส่งล่าสุดของคุณ) จะถูกแทนที่ด้วยสตริง "*** REMOVED ***" จากนั้นคุณสามารถใช้git gcเพื่อล้างข้อมูลที่ตายแล้ว:

$ git gc --prune=now --aggressive

โดยทั่วไปแล้ว BFG นั้นเร็วกว่าการรัน 10-50 เท่าgit-filter-branchและตัวเลือกนั้นได้รับการปรับให้เรียบง่ายขึ้นและปรับให้เหมาะสมกับทั้งสองกรณีการใช้งานทั่วไป:

  • การลบไฟล์ Crazy Big
  • การลบรหัสผ่านข้อมูลรับรองและข้อมูลส่วนตัวอื่น ๆ

การเปิดเผยอย่างสมบูรณ์: ฉันเป็นผู้แต่ง BFG Repo-Cleaner


นี่คือตัวเลือก แต่อาจทำให้แอปพลิเคชันของคุณพังเมื่อใช้รหัสผ่านเช่นการตั้งค่าการเชื่อมต่อฐานข้อมูล ฉันต้องการคำตอบที่ได้รับการยอมรับในปัจจุบันเนื่องจากเป็นไปได้ที่จะยังคงเก็บรหัสผ่านไว้ในสำเนาการทำงานของคุณและละเว้นไฟล์ที่มี. gitignore
Henridv

6
นี่เป็นชัยชนะครั้งใหญ่ที่นี่ หลังจากพยายามสองสามครั้งฉันสามารถใช้สิ่งนี้เพื่อตัดการคอมมิทที่มีข้อมูลที่ละเอียดอ่อนจาก repo ส่วนตัวอย่างละเอียดและอัปเดต repo ระยะไกลอย่างมีประสิทธิภาพด้วยประวัติการแก้ไข หมายเหตุด้านหนึ่งคือคุณต้องทำให้แน่ใจว่า repo (HEAD) ของคุณสะอาดหมดจดโดยไม่มีข้อมูลที่ละเอียดอ่อนเนื่องจากการกระทำนี้ถือเป็นการ "ป้องกัน" และจะไม่ถูกแก้ไขโดยเครื่องมือนี้ ถ้ามันไม่ได้เป็นเพียงแค่ทำความสะอาด / git commitแทนที่ด้วยตนเองและ มิฉะนั้น +1 สำหรับเครื่องมือใหม่ในกล่องเครื่องมือของนักพัฒนา :)
แมตต์บอร์

1
@Henridv ต่อความคิดเห็นล่าสุดของฉันมันไม่ควรทำให้แอปพลิเคชันของคุณเสียหายอย่างที่คุณคาดไว้โดยสมมติว่าแอปพลิเคชันของคุณตั้งอยู่ที่ปลายหรือหัวสาขาของคุณ (เช่นการคอมมิชชันล่าสุด) เครื่องมือนี้จะรายงานการกระทำครั้งสุดท้ายของคุณอย่างชัดเจนThese are your protected commits, and so their contents will NOT be alteredในขณะที่สำรวจและแก้ไขประวัติการกระทำที่เหลือของคุณ อย่างไรก็ตามหากคุณต้องการย้อนกลับใช่แล้วคุณจะต้องค้นหา***REMOVED***ในสิ่งที่คุณเพิ่งย้อนกลับไป
Matt Borja

1
+1 สำหรับ BFG (หากคุณติดตั้ง Java หรือไม่สนใจติดตั้ง) หนึ่งจับคือ BFG ปฏิเสธที่จะลบไฟล์ถ้ามันมีอยู่ในหัว ดังนั้นจึงเป็นการดีกว่าถ้าคุณจะยอมรับว่าไฟล์ที่ต้องการจะถูกลบก่อนแล้วจึงรัน BFG หลังจากนั้นคุณสามารถคืนค่าการส่งครั้งล่าสุดได้ตอนนี้ก็ไม่เปลี่ยนอะไรเลย
Fr0sT

1
สิ่งนี้ควรได้รับการยอมรับว่าเป็นคำตอบที่ถูกต้อง ทำตามที่พูดไว้ในกล่องหรือไม่!
gjoris

21

ถ้าคุณผลักไปที่ GitHub การบังคับให้กดไม่เพียงพอให้ลบที่เก็บหรือฝ่ายสนับสนุนผู้ติดต่อ

แม้ว่าคุณจะกดหนึ่งวินาทีหลังจากนั้นมันก็ยังไม่เพียงพอดังที่อธิบายไว้ด้านล่าง

หลักสูตรการปฏิบัติที่ถูกต้องเพียงอย่างเดียวคือ:

  • สิ่งที่รั่วออกหนังสือรับรองเปลี่ยนแปลงเช่นรหัสผ่าน?

    • ใช่: แก้ไขรหัสผ่านของคุณทันทีและพิจารณาใช้ OAuth และ API คีย์เพิ่มเติม!
    • ไม่ (ภาพเปล่า):

      • คุณสนใจไหมว่าปัญหาทั้งหมดในที่เก็บได้รับการอบ

        • ไม่ใช่: ลบที่เก็บ
        • ใช่:

          • ติดต่อฝ่ายสนับสนุน
          • หากการรั่วไหลมีความสำคัญอย่างยิ่งต่อคุณจนถึงจุดที่คุณยินดีที่จะได้รับการหยุดทำงานของที่เก็บข้อมูลเพื่อให้โอกาสในการรั่วไหลน้อยลงให้เป็นแบบส่วนตัวในขณะที่คุณรอการสนับสนุนจาก GitHub

การบังคับให้กดหนึ่งวินาทีในภายหลังนั้นไม่เพียงพอเพราะ:

  • GitHub ยังคงมุ่งมั่นอย่างต่อเนื่องเป็นเวลานาน

    พนักงานของ GitHub มีอำนาจในการลบการผูกมัดดังกล่าวหากคุณติดต่อกับพวกเขา

    ผมมีประสบการณ์นี้มือแรกเมื่อฉันทั้งหมดที่อัปโหลด GitHub กระทำอีเมลไปยัง repogcพวกเขาถามฉันจะเอามันลงดังนั้นผมและพวกเขาได้ คำขอดึงที่มีข้อมูลจะต้องถูกลบ แต่ : ข้อมูล repo นั้นยังคงสามารถเข้าถึงได้มากถึงหนึ่งปีหลังจากการลบข้อมูลเริ่มต้นเนื่องจากสิ่งนี้

    การผูกมัดที่สามารถมองเห็นได้ผ่าน:

    วิธีที่สะดวกวิธีหนึ่งในการรับแหล่งข้อมูลที่กระทำนั้นคือการใช้วิธีการดาวน์โหลด zip ซึ่งสามารถรับการอ้างอิงใด ๆ เช่น: https://github.com/cirosantilli/myrepo/archive/SHA.zip

  • เป็นไปได้ที่จะดึง SHAs ที่ขาดหายไปโดย:

  • มี scrappers เช่นhttp://ghtorrent.org/และhttps://www.githubarchive.org/ซึ่งจะรวบรวมข้อมูล GitHub และเก็บไว้ที่อื่นเป็นประจำ

    ฉันไม่สามารถหาได้ว่าพวกเขาคัดลอกการกระทำที่เกิดขึ้นจริงและไม่น่าเป็นไปได้เพราะจะมีข้อมูลมากเกินไป แต่เป็นไปได้ในทางเทคนิคและ NSA และเพื่อน ๆ มีแนวโน้มที่จะใช้ตัวกรองเพื่อเก็บถาวรเฉพาะเนื้อหาที่เชื่อมโยงกับผู้คน

หากคุณลบที่เก็บแทนเพียงแรงผลักดัน แต่กระทำไม่หายไปแม้จะมาจาก API ทันทีและให้ 404 เช่นhttps://api.github.com/repos/cirosantilli/test-dangling-delete/commits/8c08448b5fbf0f891696819f3b2b2d653f7a3824งานนี้ แม้ว่าคุณจะสร้างที่เก็บอื่นขึ้นใหม่ด้วยชื่อเดียวกัน

เพื่อทดสอบสิ่งนี้ฉันได้สร้าง repo: https://github.com/cirosantilli/test-danglingและทำ:

git init
git remote add origin git@github.com:cirosantilli/test-dangling.git

touch a
git add .
git commit -m 0
git push

touch b
git add .
git commit -m 1
git push

touch c
git rm b
git add .
git commit --amend --no-edit
git push -f

ดูเพิ่มเติม: วิธีการลบการผูกห้อยออกจาก GitHub ได้อย่างไร


20

ฉันแนะนำสคริปต์นี้โดย David Underhill ทำงานเหมือนมีเสน่ห์สำหรับฉัน

มันเพิ่มคำสั่งเหล่านี้นอกเหนือจากตัวกรองของ natacado เพื่อทำความสะอาดสิ่งที่ทิ้งไว้:

rm -rf .git/refs/original/
git reflog expire --all
git gc --aggressive --prune

สคริปต์แบบเต็ม (ให้เครดิตกับ David Underhill ทั้งหมด)

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

1
โปรดทราบว่าการใช้งานของคุณหมดอายุและลูกพรุนไม่ถูกต้องหากคุณไม่ได้ระบุวันที่มันจะมีค่าเริ่มต้นสำหรับการกระทำทั้งหมดที่มีอายุมากกว่า 2 สัปดาห์สำหรับลูกพรุน สิ่งที่คุณต้องการคือการกระทำทั้งหมด:git gc --aggressive --prune=now
Adam Parkin

@Adam Parkin ฉันจะปล่อยให้รหัสในคำตอบเดียวกันเพราะมันมาจากสคริปต์ในเว็บไซต์ของเดวิดอันเดอร์ฮิลคุณสามารถแสดงความคิดเห็นที่นั่นและถ้าเขาเปลี่ยนมันฉันจะเปลี่ยนคำตอบนี้เพราะฉันไม่รู้ว่าจริง ๆ ดี. คำสั่งหมดอายุก่อนลูกพรุนไม่ส่งผลกระทบต่อสิ่งนั้นหรือไม่
Jason Goemaat

1
@MarkusUnterwaditzer: อันนั้นใช้ไม่ได้กับการส่งข้อความ
Max Beikirch

บางทีคุณควรใส่คำสั่งทั้งหมดลงในคำตอบของคุณ มันจะสอดคล้องกันมากขึ้นและไม่จำเป็นต้องมีการรวมจิตใจของการโพสต์แยก :)
แอนดรูเหมา

9

เพื่อให้ชัดเจน: คำตอบที่ยอมรับนั้นถูกต้อง ลองก่อน อย่างไรก็ตามอาจมีความซับซ้อนโดยไม่จำเป็นสำหรับบางกรณีการใช้งานโดยเฉพาะอย่างยิ่งหากคุณพบข้อผิดพลาดที่น่ารังเกียจเช่น 'ร้ายแรง: การแก้ไขที่ไม่ดี - ว่างเปล่า' หรือไม่สนใจประวัติของ repo ของคุณ

ทางเลือกอื่นคือ:

  1. cd ไปยังสาขาพื้นฐานของโครงการ
  2. ลบรหัส / ไฟล์ที่ละเอียดอ่อน
  3. rm -rf .git / # ลบข้อมูล git ทั้งหมดออกจากรหัสของคุณ
  4. ไปที่ GitHub และลบที่เก็บของคุณ
  5. ทำตามคำแนะนำนี้เพื่อส่งรหัสของคุณไปยังที่เก็บใหม่ตามปกติ - https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/

แน่นอนว่าสิ่งนี้จะลบสาขาที่กระทำในประวัติศาสตร์ทั้งหมดและออกจาก repo github ของคุณและ repit git ในพื้นที่ของคุณ หากไม่สามารถยอมรับได้คุณจะต้องใช้วิธีอื่น

เรียกสิ่งนี้ว่าตัวเลือกนิวเคลียร์


9

git forget-blobคุณสามารถใช้

git forget-blob file-to-forgetการใช้งานสวยเรียบง่าย คุณสามารถรับข้อมูลเพิ่มเติมได้ที่นี่

https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/

มันจะหายไปจากความมุ่งมั่นทั้งหมดในประวัติศาสตร์ reflog แท็กและอื่น ๆ

ฉันพบปัญหาเดียวกันทุกครั้งแล้วและทุกครั้งที่ฉันต้องกลับมาที่โพสต์นี้และคนอื่น ๆ นั่นเป็นสาเหตุที่ฉันทำกระบวนการอัตโนมัติ

เครดิตให้กับผู้มีส่วนร่วมจาก Stack Overflow ที่อนุญาตให้ฉันนำสิ่งนี้มารวมกัน


8

นี่คือทางออกของฉันใน windows

git filter-branch --tree-filter "rm -f 'filedame / filename'" HEAD

git push --force

ตรวจสอบให้แน่ใจว่าเส้นทางนั้นถูกต้องมิฉะนั้นจะไม่ทำงาน

ฉันหวังว่ามันจะช่วย


8

ใช้ตัวกรองสาขา :

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

3

ฉันต้องทำวันนี้สองสามครั้ง โปรดทราบว่าวิธีนี้ใช้ได้กับไฟล์ 1 ไฟล์ต่อครั้งเท่านั้น

  1. รับรายการข้อผูกพันทั้งหมดที่แก้ไขไฟล์ คนที่อยู่ด้านล่างจะเป็นคนแรกที่กระทำ:

    git log --pretty=oneline --branches -- pathToFile

  2. หากต้องการลบไฟล์ออกจากประวัติให้ใช้คำสั่ง sha1 และพา ธ ไปยังไฟล์จากคำสั่งก่อนหน้าและป้อนลงในคำสั่งนี้:

    git filter-branch --index-filter 'git rm --cached --ignore-unmatch <path-to-file>' -- <sha1-where-the-file-was-first-added>..


3

ดังนั้นมันมีลักษณะดังนี้:

git rm --cached /config/deploy.rb
echo /config/deploy.rb >> .gitignore

ลบแคชสำหรับไฟล์ที่ถูกติดตามจากคอมไพล์และเพิ่มไฟล์นั้นลงใน.gitignoreรายการ


2

ในโครงการ Android ของฉันฉันมีadmob_keys.xmlแยกเป็นไฟล์ xml ในapp / src / main / res / values ​​/ folder เพื่อลบไฟล์ที่ละเอียดอ่อนนี้ฉันใช้สคริปต์ด้านล่างและทำงานได้อย่างสมบูรณ์

git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch  app/src/main/res/values/admob_keys.xml' \
--prune-empty --tag-name-filter cat -- --all
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.