Giữ lại quyền đối với tệp với Git


109

Tôi muốn kiểm soát phiên bản máy chủ web của mình như được mô tả trong Kiểm soát phiên bản cho máy chủ web của tôi , bằng cách tạo một repo git từ của tôi /var/www directory. Hy vọng của tôi là sau đó tôi có thể đẩy nội dung web từ máy chủ nhà phát triển của chúng tôi sang github, kéo nó đến máy chủ sản xuất của chúng tôi và dành thời gian còn lại trong ngày tại nhóm.

Rõ ràng có một điểm khác biệt trong kế hoạch của tôi là Git sẽ không tôn trọng quyền đối với tệp (tôi chưa thử, chỉ đang đọc về nó bây giờ.) Tôi đoán điều này có lý khi các hộp khác nhau có trách nhiệm thiết lập người dùng / nhóm khác nhau. Nhưng nếu tôi muốn buộc quyền truyền bá, biết rằng máy chủ của tôi được định cấu hình giống nhau, tôi có bất kỳ tùy chọn nào không? Hay có cách nào dễ dàng hơn để tiếp cận những gì tôi đang cố gắng làm không?



1
Vâng, đoán vậy, mặc dù giải pháp mà họ chỉ ra tôi thành thật không biết phải làm gì. Tôi đã hy vọng một cách tiếp cận đơn giản hơn.
Yarin

Còn về trường hợp mã nguồn đến từ môi trường Dev (ví dụ: Windows - XAMPP, v.v.) không có thông tin về quyền sở hữu tệp thì sao? Các tệp ở cuối quy trình git cần khớp với quyền sở hữu và quyền đối với vị trí đích. Git-cache-meta có thể giải quyết vấn đề này không? Đồng ý với Yarin ... chắc chắn đây là một trường hợp sử dụng khá phổ biến, cần có một giải pháp khá đơn giản?
user3600150

Câu trả lời:


43

Câu hỏi git-cache-metađược đề cập trong câu hỏi SO " git - làm thế nào để khôi phục quyền đối với tệp . Git cho rằng tệp phải là? " (Và Câu hỏi thường gặp về git ) là cách tiếp cận đơn giản hơn.

Ý tưởng là lưu trữ trong một .git_cache_metatệp các quyền của tệp và thư mục.
Đây là một tệp riêng biệt không được tạo phiên bản trực tiếp trong kho Git.

Đó là lý do tại sao cách sử dụng nó là:

$ git bundle create mybundle.bdl master; git-cache-meta --store
$ scp mybundle.bdl .git_cache_meta machine2: 
#then on machine2:
$ git init; git pull mybundle.bdl master; git-cache-meta --apply

Vì vậy, bạn:

  • gói repo của bạn và lưu các quyền tệp liên quan.
  • sao chép hai tệp đó trên máy chủ từ xa
  • khôi phục repo ở đó và áp dụng quyền

2
VonC- Cảm ơn vì điều này, tôi sẽ dùng thử- nhưng việc đóng gói có cần thiết không? Tôi không thể giữ lại quy trình làm việc của mình (dev -> github -> production) và chỉ kiểm tra / kiểm tra metafile?
Yarin

@Yarin: không, bó không bắt buộc. Đó là một cách gọn gàng để chuyển repo khi không có giao thức truyền nào khác.
VonC

3
Việc sử dụng gói ở đây là một sự phân tâm lớn đối với tôi. Nó khiến tôi hoàn toàn không có câu trả lời. (Tôi không gặp khó khăn khi kéo repo từ máy chủ.) Câu trả lời của @ omid-ariyan bên dưới với các hook cam kết trước / sau dễ hiểu hơn nhiều. Sau đó, tôi nhận ra rằng các tập lệnh hook đó đang hoạt động giống hệt như git-cache-meta. Hãy xem ý tôi là gì: gist.github.com/andris9/1978266 . Họ đang phân tích cú pháp và lưu trữ lợi nhuận từ git ls-files.
pauljohn32

Liên kết đến git-cache-meta đã chết - ai đó biết về điều này có thể định vị nó và chỉnh sửa bài đăng không?
rosuav

@rosuav Chắc chắn: Tôi đã chỉnh sửa câu trả lời và khôi phục liên kết. Cảm ơn bạn đã cho tôi biết về liên kết chết này.
VonC

63

Git là Hệ thống kiểm soát phiên bản, được tạo ra để phát triển phần mềm, vì vậy từ toàn bộ tập hợp các chế độ và quyền, nó chỉ lưu trữ bit thực thi (đối với các tệp thông thường) và bit liên kết biểu tượng. Nếu bạn muốn lưu trữ các quyền đầy đủ, bạn cần công cụ của bên thứ ba, như git-cache-meta( được VonC đề cập ) hoặc Metastore (được sử dụng bởi etckeeper ). Hoặc bạn có thể sử dụng IsiSetup , IIRC sử dụng git làm phụ trợ.

Xem trang Giao diện, giao diện người dùng và công cụ trên Git Wiki.


2
Cảm ơn Jakub- bạn có thể giải thích cho tôi lý do tại sao Git quan tâm đến bit thực thi và chỉ điều đó không?
Yarin

5
@Yarin: chỉ bit thực thi? Khi bạn sao chép tất cả các tập hợp tệp từ hệ thống này sang hệ thống khác, khái niệm "chỉ đọc" hoặc "đọc-ghi" không hoàn toàn phù hợp (như bạn đã nói trong câu hỏi của mình: người dùng / nhóm khác nhau). Nhưng khái niệm "có thể thực thi" không phụ thuộc vào người dùng và nhóm và có thể được sử dụng lại từ hệ thống này sang hệ thống (từ xa).
VonC

1
Jakub, trong trường hợp đó, nó không nên thay đổi các quyền. Ý tôi là, nó nên để các đặc quyền một mình hoặc quản lý chúng, nhưng không gây rối với chúng nếu nó không quản lý chúng.
CommaToast

3
Ngoài ra, tôi đã tìm thấy /usr/share/git-core/contrib/hooks/setgitperms.perltrong git-contribgói của mình - một tập lệnh cho mục đích tương tự. ("Tập lệnh này có thể được sử dụng để lưu / khôi phục toàn bộ quyền và dữ liệu quyền sở hữu trong cây làm việc git.")
imz - Ivan Zakharyaschev

Điều này vẫn chính xác hay github bằng cách nào đó làm điều gì đó trên git? Tôi vừa thay đổi một tệp thành tệp thực thi và cam kết nó, và bảng thay đổi cho cam kết hiển thị dưới dạng 0 dòng được thay đổi cho tệp, nhưng có 100644 → 100755 bên cạnh tên tệp. Điều này thực sự giống như các quyền đầy đủ được lưu trữ cùng với tệp.
Cruncher

23

Điều này là khá muộn nhưng có thể giúp một số người khác. Tôi làm những gì bạn muốn bằng cách thêm hai git hook vào kho lưu trữ của tôi.

.git / hooks / pre-commit:

#!/bin/bash
#
# A hook script called by "git commit" with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message if it wants
# to stop the commit.

SELF_DIR=`git rev-parse --show-toplevel`
DATABASE=$SELF_DIR/.permissions

# Clear the permissions database file
> $DATABASE

echo -n "Backing-up permissions..."

IFS_OLD=$IFS; IFS=$'\n'
for FILE in `git ls-files --full-name`
do
   # Save the permissions of all the files in the index
   echo $FILE";"`stat -c "%a;%U;%G" $FILE` >> $DATABASE
done

for DIRECTORY in `git ls-files --full-name | xargs -n 1 dirname | uniq`
do
   # Save the permissions of all the directories in the index
   echo $DIRECTORY";"`stat -c "%a;%U;%G" $DIRECTORY` >> $DATABASE
done
IFS=$IFS_OLD

# Add the permissions database file to the index
git add $DATABASE -f

echo "OK"

.git / hooks / post-checkout:

#!/bin/bash

SELF_DIR=`git rev-parse --show-toplevel`
DATABASE=$SELF_DIR/.permissions

echo -n "Restoring permissions..."

IFS_OLD=$IFS; IFS=$'\n'
while read -r LINE || [[ -n "$LINE" ]];
do
   ITEM=`echo $LINE | cut -d ";" -f 1`
   PERMISSIONS=`echo $LINE | cut -d ";" -f 2`
   USER=`echo $LINE | cut -d ";" -f 3`
   GROUP=`echo $LINE | cut -d ";" -f 4`

   # Set the file/directory permissions
   chmod $PERMISSIONS $ITEM

   # Set the file/directory owner and groups
   chown $USER:$GROUP $ITEM

done < $DATABASE
IFS=$IFS_OLD

echo "OK"

exit 0

Hook đầu tiên được gọi khi bạn "commit" và sẽ đọc quyền sở hữu và quyền đối với tất cả các tệp trong kho lưu trữ và lưu trữ chúng trong một tệp trong thư mục gốc của kho lưu trữ được gọi là .permissions và sau đó thêm tệp .permissions vào cam kết.

Móc thứ hai được gọi khi bạn "kiểm tra" và sẽ xem qua danh sách các tệp trong tệp .permissions và khôi phục quyền sở hữu và quyền của các tệp đó.

  • Bạn có thể cần thực hiện cam kết và thanh toán bằng sudo.
  • Đảm bảo rằng các tập lệnh cam kết trước và sau khi thanh toán có quyền thực thi.

Omid ... cảm ơn bạn! Tôi thấy mã của bạn là một giải pháp hoàn hảo cho tôi.
Ricalsin

@Ricalsin Bạn rất được hoan nghênh! Tôi rất vui khi đã giúp :)
Omid Ariyan

1
$SELF_DIR/../../không nhất thiết phải là gốc của kho lưu trữ ... nhưng git rev-parse --show-toplevellà. (Không chắc tại sao bạn không chỉ sử dụng pwdcho thư mục hiện tại, nhưng đó là cuộc tranh luận.)
PJSCopeland

Như hiện tại, phần trên sẽ chia nhỏ các tên tệp với khoảng trống trong đó. Theo câu trả lời này , bạn có thể đặt IFS=$'\n'trước forvòng lặp để dừng điều đó (và unset IFSsau đó để an toàn).
PJSCopeland

Điều này không cho phép bạn dễ dàng thực hiện các quyền đối với một hệ thống khác, với một hệ điều hành khác, nơi bạn có một tên người dùng khác. Tôi tự hỏi mình "tôi thực sự cần gì?" và cắt toàn bộ xuống giải pháp cho chmod 0600 .pgpasstrong post-checkout. Có, tôi sẽ phải cập nhật nó theo cách thủ công bất cứ khi nào tôi có một tệp cần các quyền cụ thể, nhưng chúng bị gián đoạn.
PJSCopeland

2

Trong trường hợp bạn đang đi vào vấn đề này ngay bây giờ, tôi vừa mới xem qua hôm nay và có thể tóm tắt điều này đứng ở đâu. Nếu bạn chưa thử cách này, một số chi tiết ở đây có thể hữu ích.

Tôi nghĩ cách tiếp cận của @Omid Ariyan là cách tốt nhất. Thêm kịch bản cam kết trước và sau khi thanh toán. ĐỪNG quên đặt tên chính xác cho chúng theo cách Omid làm và ĐỪNG quên làm cho chúng có thể thực thi được. Nếu bạn quên một trong hai điều đó, chúng không có tác dụng gì và bạn chạy "git commit" lặp đi lặp lại và tự hỏi tại sao không có gì xảy ra :) Ngoài ra, nếu bạn cắt và dán ra khỏi trình duyệt web, hãy cẩn thận rằng dấu ngoặc kép và dấu tích không bị thay đổi.

Nếu bạn chạy tập lệnh cam kết trước một lần (bằng cách chạy git cam kết), thì tệp .permissions sẽ được tạo. Bạn có thể thêm nó vào kho lưu trữ và tôi nghĩ rằng không cần thiết phải thêm nó nhiều lần vào cuối tập lệnh pre-commit. Nhưng nó không đau, tôi nghĩ (hy vọng).

Có một số vấn đề nhỏ về tên thư mục và sự tồn tại của khoảng trắng trong tên tệp trong các tập lệnh của Omid. Khoảng trống là một vấn đề ở đây và tôi đã gặp một số rắc rối với bản sửa lỗi IFS. Đối với bản ghi, tập lệnh cam kết trước này đã hoạt động chính xác đối với tôi:

#!/bin/bash  

SELF_DIR=`git rev-parse --show-toplevel`
DATABASE=$SELF_DIR/.permissions

# Clear the permissions database file
> $DATABASE

echo -n "Backing-up file permissions..."

IFSold=$IFS
IFS=$'\n'
for FILE  in `git ls-files`
do
   # Save the permissions of all the files in the index
   echo $FILE";"`stat -c "%a;%U;%G" $FILE` >> $DATABASE
done
IFS=${IFSold}
# Add the permissions database file to the index
git add $DATABASE

echo "OK"

Bây giờ, chúng ta làm được gì từ điều này?

Tệp .permissions nằm ở cấp cao nhất của git repo. Nó có một dòng cho mỗi tệp, đây là ví dụ trên cùng của tôi:

$ cat .permissions
.gitignore;660;pauljohn;pauljohn
05.WhatToReport/05.WhatToReport.doc;664;pauljohn;pauljohn
05.WhatToReport/05.WhatToReport.pdf;664;pauljohn;pauljohn

Như bạn thấy, chúng tôi có

filepath;perms;owner;group

Trong các bình luận về cách tiếp cận này, một trong những người đăng bài phàn nàn rằng nó chỉ hoạt động với cùng một tên người dùng và điều đó đúng về mặt kỹ thuật, nhưng rất dễ sửa chữa. Lưu ý rằng kịch bản sau thanh toán có 2 phần hành động,

# Set the file permissions
chmod $PERMISSIONS $FILE
# Set the file owner and groups
chown $USER:$GROUP $FILE

Vì vậy, tôi chỉ giữ cái đầu tiên, đó là tất cả những gì tôi cần. Tên người dùng của tôi trên máy chủ Web thực sự khác nhau, nhưng quan trọng hơn là bạn không thể chạy chown trừ khi bạn root. Tuy nhiên, có thể chạy "chgrp". Nó là đủ rõ ràng làm thế nào để sử dụng nó.

Trong câu trả lời đầu tiên của bài đăng này, câu trả lời được chấp nhận rộng rãi nhất, đề xuất là nên sử dụng git-cache-meta, một tập lệnh đang thực hiện công việc giống như các tập lệnh hook trước / sau (phân tích cú pháp đầu ra từ git ls-files) . Đối với tôi, các tập lệnh này dễ hiểu hơn, mã git-cache-meta khá phức tạp hơn. Có thể giữ git-cache-meta trong đường dẫn và viết các tập lệnh pre-commit và post-checkout sẽ sử dụng nó.

Khoảng trắng trong tên tệp là một vấn đề với cả hai tập lệnh của Omid. Trong tập lệnh sau thanh toán, bạn sẽ biết mình có khoảng trắng trong tên tệp nếu bạn gặp lỗi như thế này

$ git checkout -- upload.sh
Restoring file permissions...chmod: cannot access  '04.StartingValuesInLISREL/Open': No such file or directory
chmod: cannot access 'Notebook.onetoc2': No such file or directory
chown: cannot access '04.StartingValuesInLISREL/Open': No such file or directory
chown: cannot access 'Notebook.onetoc2': No such file or directory

Tôi đang kiểm tra các giải pháp cho điều đó. Đây là một cái gì đó có vẻ hoạt động, nhưng tôi chỉ thử nghiệm trong một trường hợp

#!/bin/bash

SELF_DIR=`git rev-parse --show-toplevel`
DATABASE=$SELF_DIR/.permissions

echo -n "Restoring file permissions..."
IFSold=${IFS}
IFS=$
while read -r LINE || [[ -n "$LINE" ]];
do
   FILE=`echo $LINE | cut -d ";" -f 1`
   PERMISSIONS=`echo $LINE | cut -d ";" -f 2`
   USER=`echo $LINE | cut -d ";" -f 3`
   GROUP=`echo $LINE | cut -d ";" -f 4`

   # Set the file permissions
   chmod $PERMISSIONS $FILE
   # Set the file owner and groups
   chown $USER:$GROUP $FILE
done < $DATABASE
IFS=${IFSold}
echo "OK"

exit 0

Vì thông tin về quyền là một dòng tại một thời điểm, tôi đặt IFS thành $, vì vậy chỉ những dấu ngắt dòng được xem là những thứ mới.

Tôi đọc rằng điều RẤT QUAN TRỌNG là đặt biến môi trường IFS trở lại như cũ! Bạn có thể thấy lý do tại sao một phiên shell có thể trở nên tồi tệ nếu bạn để $ làm dấu phân tách duy nhất.


2

Chúng tôi có thể cải thiện các câu trả lời khác bằng cách thay đổi định dạng của .permissionstệp thành các chmodcâu lệnh thực thi và sử dụng -printftham số để find. Đây là .git/hooks/pre-committệp đơn giản hơn :

#!/usr/bin/env bash

echo -n "Backing-up file permissions... "

cd "$(git rev-parse --show-toplevel)"

find . -printf 'chmod %m "%p"\n' > .permissions

git add .permissions

echo done.

... và đây là .git/hooks/post-checkouttệp đơn giản :

#!/usr/bin/env bash

echo -n "Restoring file permissions... "

cd "$(git rev-parse --show-toplevel)"

. .permissions

echo "done."

Hãy nhớ rằng các công cụ khác có thể đã định cấu hình các tập lệnh này, vì vậy bạn có thể cần hợp nhất chúng lại với nhau. Ví dụ: đây là một post-checkouttập lệnh cũng bao gồm các git-lfslệnh:

#!/usr/bin/env bash

echo -n "Restoring file permissions... "

cd "$(git rev-parse --show-toplevel)"

. .permissions

echo "done."

command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on you
r path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/post-checkout.\n"; exit 2; }
git lfs post-checkout "$@"

1

Trong pre-commit / post-checkout, một tùy chọn sẽ là sử dụng tiện ích "mtree" (FreeBSD) hoặc "fmtree" (Ubuntu) "so sánh hệ thống phân cấp tệp với một đặc điểm kỹ thuật, tạo một đặc điểm kỹ thuật cho hệ thống phân cấp tệp hoặc sửa đổi sự chỉ rõ."

Bộ mặc định là cờ, gid, liên kết, chế độ, nlink, kích thước, thời gian, loại và uid. Điều này có thể được trang bị cho mục đích cụ thể với công tắc -k.


1

Tôi đang chạy trên FreeBSD 11.1, khái niệm ảo hóa freebsd jail giúp hệ điều hành tối ưu. Phiên bản Git hiện tại tôi đang sử dụng là 2.15.1, tôi cũng thích chạy mọi thứ trên shell script hơn. Với ý nghĩ đó, tôi đã sửa đổi các đề xuất ở trên như sau:

git push: .git / hooks / pre-commit

#! /bin/sh -
#
# A hook script called by "git commit" with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message if it wants
# to stop the commit.

SELF_DIR=$(git rev-parse --show-toplevel);
DATABASE=$SELF_DIR/.permissions;

# Clear the permissions database file
> $DATABASE;

printf "Backing-up file permissions...\n";

OLDIFS=$IFS;
IFS=$'\n';
for FILE in $(git ls-files);
do
   # Save the permissions of all the files in the index
    printf "%s;%s\n" $FILE $(stat -f "%Lp;%u;%g" $FILE) >> $DATABASE;
done
IFS=$OLDIFS;

# Add the permissions database file to the index
git add $DATABASE;

printf "OK\n";

git pull: .git / hooks / post-merge

#! /bin/sh -

SELF_DIR=$(git rev-parse --show-toplevel);
DATABASE=$SELF_DIR/.permissions;

printf "Restoring file permissions...\n";

OLDIFS=$IFS;
IFS=$'\n';
while read -r LINE || [ -n "$LINE" ];
do
   FILE=$(printf "%s" $LINE | cut -d ";" -f 1);
   PERMISSIONS=$(printf "%s" $LINE | cut -d ";" -f 2);
   USER=$(printf "%s" $LINE | cut -d ";" -f 3);
   GROUP=$(printf "%s" $LINE | cut -d ";" -f 4);

   # Set the file permissions
   chmod $PERMISSIONS $FILE;

   # Set the file owner and groups
   chown $USER:$GROUP $FILE;

done < $DATABASE
IFS=$OLDIFS

pritnf "OK\n";

exit 0;

Nếu vì lý do nào đó bạn cần tạo lại tập lệnh, đầu ra tệp .permissions phải có định dạng sau:

.gitignore;644;0;0

Đối với tệp .gitignore với 644 quyền được cấp cho root: wheel

Lưu ý rằng tôi đã phải thực hiện một vài thay đổi đối với các tùy chọn thống kê.

Thưởng thức,


1

Một bổ sung cho câu trả lời của @Omid Ariyan là quyền trên các thư mục. Thêm điều này sau forvòng lặp donetrong pre-committập lệnh của anh ấy .

for DIR in $(find ./ -mindepth 1 -type d -not -path "./.git" -not -path "./.git/*" | sed 's@^\./@@')
do
    # Save the permissions of all the files in the index
    echo $DIR";"`stat -c "%a;%U;%G" $DIR` >> $DATABASE
done

Điều này cũng sẽ tiết kiệm các quyền của thư mục.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.