git kéo trong khi không có trong một thư mục git


308

Giả sử tôi có một thư mục /X/Y, đó là kho git. Có thể bằng cách nào đó gọi một lệnh như git pulltừ bên trong /X, nhưng nhắm mục tiêu /X/Ythư mục?

EDIT: Tôi đoán tôi đã tự hỏi cụ thể: có thể làm điều này bằng cách sử dụng lệnh git, nhưng không phải thay đổi thư mục?

LƯU Ý: Tôi đã chấp nhận câu trả lời của VonC vì nó thanh lịch hơn nhiều so với các tùy chọn trước đó. Đối với những người chạy Git cũ hơn 1.8.5, vui lòng xem câu trả lời của bstpierre bên dưới .


2
Tôi muốn thêm rằng khi sử dụng git-pull trong hook sẽ không hoạt động trừ khi bạn bỏ đặt GIT_DIR. Liên quan, thích hợp.
zpmorgan

Bắt đầu từ git 1.8.5 (Q4 2013), bạn sẽ có thể "sử dụng lệnh git, nhưng không phải thay đổi thư mục". Xem câu trả lời của tôi dưới đây
VonC

Câu trả lời:


453

Bắt đầu từ git 1.8.5 (Q4 2013) , bạn sẽ có thể "sử dụng lệnh Git, nhưng không phải thay đổi thư mục".

Giống như " make -C <directory>", " git -C <directory> ..." bảo Git đến đó trước khi làm bất cứ điều gì khác .

Xem cam kết 44e1e4 của Nazri Ramliy :

Phải mất nhiều lần nhấn để gọi lệnh Git trong một thư mục khác mà không cần rời khỏi thư mục hiện tại:

  1. (cd ~/foo && git status)
    git --git-dir=~/foo/.git --work-tree=~/foo status
    GIT_DIR=~/foo/.git GIT_WORK_TREE=~/foo git status
  2. (cd ../..; git grep foo)
  3. for d in d1 d2 d3; do (cd $d && git svn rebase); done

Các phương thức hiển thị ở trên có thể chấp nhận được đối với kịch bản nhưng quá cồng kềnh đối với các yêu cầu dòng lệnh nhanh.

Với tùy chọn mới này, những điều trên có thể được thực hiện với ít lần nhấn phím hơn:

  1. git -C ~/foo status
  2. git -C ../.. grep foo
  3. for d in d1 d2 d3; do git -C $d svn rebase; done

Kể từ Git 2.3.4 (tháng 3 năm 2015) và cam kết 6a536e2 của Karthik Nayak ( KarthikNayak) , gitsẽ coi " git -C '<path>'" là một no-op khi <path>trống.

' git -C ""' chết một cách vô ích với lỗi " Cannot change to ''", trong khi đó vỏ xử lý cd "" 'là không có.
Lấy hành vi của cái vỏ làm tiền lệ, dạy gitcách coi -C "" là không có gì cả.


4 năm sau, tài liệu Git 2.23 (quý 3 năm 2019) git -C ""hoạt động và không thay đổi thư mục

Nó đã hoạt động như vậy kể từ 6a536e2 ( git: coi " git -C '<path>'" là không hoạt động khi <path>trống, 2015 / 03-06, Git v2.3.4).

Điều đó có nghĩa là tài liệu bây giờ (cuối cùng) bao gồm:

Nếu ' <path>' có mặt nhưng trống, ví dụ -C "", thì thư mục làm việc hiện tại không thay đổi.


Bạn có thể thấy git -Cđược sử dụng với Git 2.26 (Q1 2020), làm ví dụ.

Xem cam kết b441717 , cam kết 9291e63 , cam kết 5236fce , cam kết 10812c2 , cam kết 62d58cd , cam kết b87b02c , cam kết 9b92070 , cam kết 3595d10 , cam kết f511bc0 , cam kết f6041ab , cam kết f46c243 , cam kết 99c049b , cam 3.738.439 , cam 7.717.242 , cam kết b8afb90 (ngày 20 tháng 12 năm 2019) bởi Denton Liu ( Denton-L) .
(Được hợp nhất bởi Junio ​​C Hamano - gitster- trong cam kết 381e8e9 , ngày 5 tháng 2 năm 2020)

t1507: nội tuyến full_name()

Đã ký tắt: Denton Liu

Trước đây, chúng tôi đã chạy test_must_fail full_name. Tuy nhiên, test_must_failchỉ nên được sử dụng trên các lệnh git.
Nội tuyến full_name()để chúng ta có thể sử dụng trực tiếp test_must_failtrên gitlệnh.

Khi full_name()được giới thiệu trong 28fb84382b ("Giới thiệu <branch>@{upstream}ký hiệu", 2009-09-10, Git v1.7.0-rc0 - merge ), git -Ctùy chọn này chưa khả dụng (vì nó được giới thiệu trong 44e1e4d67d (" git: chạy trong một thư mục đã cho với tùy chọn -C ", 2013-09-09, Git v1.8.5-rc0 - hợp nhất được liệt kê trong lô số 5 )).
Do đó, chức năng của trình trợ giúp đã loại bỏ nhu cầu thủ công cdmỗi lần. Tuy nhiên, vì git -Chiện đã có sẵn, chúng tôi chỉ có thể sử dụng thay thế và nội tuyến full_name().


6
Ồ, thật tuyệt! Đây là cách thanh lịch hơn, vì vậy tôi sẽ đánh dấu nó được chấp nhận cho người xem trong tương lai.
Gavin Anderegg

1
Không hoạt động với tôi: # git --version && git -C ~ / .m2 / checkout master git phiên bản 1.8.3.4 (Apple Git-47) Tùy chọn không xác định: -C sử dụng: git [--version] [--help ] [-c name = value] [--exec-path [= <path>]] [--html-path] [--man-path] [--info-path] [-p | --paginate | --no-pager] [--no-thay-object] [--bare] [--git-dir = <path>] [--work-tree = <path>] [--namespace = <name> ] <lệnh> [<args>]
Jan Galinski

7
@JanGalinski Nhưng tôi đã đề cập đến "Bắt đầu git 1.8.5". Vì vậy, git 1.8.3.x chưa biết về tùy chọn đó.
VonC

2
Trên Ubuntu 12.04 tôi đã phải cài đặt phiên bản git mới hơn. Tôi đã làm nó như thế này: apt-get install software-properties-common python-software-propertiessau đó thêm repo git add-apt-repository ppa:git-core/ppa. Bước cuối cùng là cập nhật git : apt-get update && apt-get upgrade.
ph3nx


54

Chỉnh sửa :

Có một lỗi với git pullhoặc bạn không thể làm những gì bạn đang cố gắng thực hiện với lệnh đó. Tuy nhiên, bạn có thể làm điều đó với tìm nạp và hợp nhất:

cd /X
git --git-dir=/X/Y/.git fetch
git --git-dir=/X/Y/.git --work-tree=/X/Y merge origin/master

Câu trả lời gốc :

Giả sử bạn đang chạy bash hoặc tương tự, bạn có thể làm (cd /X/Y; git pull).

Trang git man chỉ định một số biến (xem "Kho lưu trữ git") có vẻ như chúng sẽ giúp ích, nhưng tôi không thể làm cho chúng hoạt động đúng (với kho lưu trữ của tôi trong / tmp / ggg2):

GIT_WORK_TREE=/tmp/ggg2 GIT_DIR=/tmp/ggg2/.git git pull
fatal: /usr/lib/git-core/git-pull cannot be used without a working tree.

Chạy lệnh bên dưới trong khi cwd của tôi là / tmp cập nhật repo đó, nhưng tệp cập nhật xuất hiện trong / tmp thay vì cây làm việc / tmp / ggg2:

GIT_DIR=/tmp/ggg2/.git git pull

Xem thêm câu trả lời này cho một câu hỏi tương tự , trong đó thể hiện --git-dir--work-treecờ.


Bạn đã thử chỉ sử dụng GIT_WORK_TREE?
Arrowmaster

@Arrowmaster: có, nếu bạn làm điều đó, git không thể tìm thấy .gitthư mục.
bstpierre

À đúng rồi, trang man nói rằng GIT_WORK_TREE không được sử dụng nếu GIT_DIR không được đặt. Có vẻ lạ là nó không hoạt động khi cả hai được sử dụng.
Arrowmaster

@Arrowmaster: Tôi phải tự hỏi nếu có một lỗi ở đây ở đâu đó. Nếu tôi làm git --git-dir=/tmp/ggg2/.git --work-tree=/tmp/ggg2 pull, tôi nhận được một thông báo lỗi. Nhưng nếu tôi làm git --git-dir=/tmp/ggg2/.git --work-tree=. pulltrong khi tôi ở / tmp, nó sẽ đặt các tệp được cập nhật vào / tmp như bình thường.
bstpierre

@bstpierre: Tôi không có quyền truy cập vào một hệ thống có cài đặt git ngay bây giờ nhưng nếu có, tôi sẽ thử các lựa chọn thay thế khác --work-treengay bây giờ --work-tree=/tmp/ggg2/--work-tree=/tmp/ggg2/.vì nó có thể là một vấn đề với cách phân tích đường dẫn của nó.
Arrowmaster

32

Bạn có thể gói nó trong một tập lệnh bash hoặc bí danh git:

cd /X/Y && git pull && cd -

1
Đây chắc chắn là một mẹo nhỏ, nhưng tôi tự hỏi liệu có cách nào để sử dụng lệnh git mà không thay đổi thư mục. Tôi bắt đầu nghĩ rằng không có.
Gavin Anderegg

1
và sử dụng pushd/popdcặp thay vìcd/cd-
axd

Hoặc bạn có thể sử dụng một mạng con để tránh phải quay lại:(cd xyz && git pull)
jarmod

30

Bài đăng này hơi cũ nên có thể có một lỗi và nó đã được sửa, nhưng tôi chỉ làm điều này:

git --work-tree=/X/Y --git-dir=/X/Y/.git pull origin branch

Va no đa hoạt động. Mất một phút để tôi nhận ra rằng nó muốn dotfile và thư mục cha (trong một thiết lập tiêu chuẩn luôn luôn là cha / con nhưng không phải trong TẤT CẢ các thiết lập, vì vậy chúng cần được chỉ định rõ ràng.


Tôi thích giải pháp này vì nó hoạt động với các thư mục như \ remotemachine \ C $ \ thư mục \ etc
twasbrillig

7

Vì một số máy chủ của tôi đang sử dụng phiên bản Ubuntu LTS cũ, tôi không thể dễ dàng nâng cấp git lên phiên bản mới nhất (hỗ trợ tùy chọn -C như được mô tả trong một số câu trả lời).

Thủ thuật này hoạt động tốt với tôi, đặc biệt vì nó không có tác dụng phụ của một số câu trả lời khác khiến bạn rơi vào một thư mục khác với nơi bạn bắt đầu.

pushd /X/Y
git pull
popd

Hoặc, thực hiện dưới dạng một lớp lót:

pushd /X/Y; git pull; popd

Cả Linux và Windows đều có lệnh đẩy và popd.


5

Sử dụng kết hợp pushd, git pullpopdchúng ta có thể đạt được chức năng này:

pushd <path-to-git-repo> && git pull && popd

Ví dụ:

pushd "E:\Fake Directory\gitrepo" && git pull && popd

Thật đáng kinh ngạc. Hoạt động hoàn hảo
Stretch0

4

Bạn có thể viết một kịch bản như thế này:

cd /X/Y
git pull

Bạn có thể đặt tên cho nó một cái gì đó như gitpull.
Nếu bạn muốn có nó, hãy làm các thư mục tùy ý thay vì /X/Y:

cd $1
git pull

Sau đó, bạn có thể gọi nó với gitpull /X/Z
Lastly, bạn có thể thử tìm kho lưu trữ. Tôi có một ~/gitthư mục chứa các kho lưu trữ và bạn có thể sử dụng thư mục này để thực hiện tất cả chúng.

g=`find /X -name .git`
for repo in ${g[@]}
do
    cd ${repo}
    cd ..
    git pull
done

2
Nếu bạn muốn làm điều đó từ dòng lệnh, chỉ cần thực hiện trong một khung con : (cd /X/Y && git pull).
Cascabel

2

Đối với bất kỳ ai như tôi đang cố gắng thực hiện điều này thông qua lệnh drush (Drupal shell) trên máy chủ từ xa, bạn sẽ không thể sử dụng giải pháp yêu cầu bạn đưa CD vào thư mục làm việc:

Thay vào đó, bạn cần sử dụng giải pháp phá vỡ sự lôi kéo thành tìm nạp & hợp nhất:

drush @remote exec git --git-dir=/REPO/PATH --work-tree=/REPO/WORKDIR-PATH fetch origin
drush @remote exec git --git-dir=/REPO/PATH --work-tree=/REPO/WORKDIR-PATH merge origin/branch

1

Đây có thể là một vấn đề tương tự, nhưng bạn cũng có thể đơn giản xâu chuỗi các lệnh của bạn. ví dụ

Trên một dòng

cd ~/Sites/yourdir/web;git pull origin master

Hoặc thông qua SSH.

ssh username@atyourserver.com -t "cd ~/Sites/thedir/web;git pull origin master"
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.