352

Let's say I have a directory, /X/Y, which is a git repository. Is it possible to somehow call a command like git pull from inside /X, but targeting the /X/Y directory?

EDIT: I guess I was wondering specifically: is it possible to do this using the a git command, but without having to change directories?

NOTE: I've accepted VonC's answer as it's much more elegant than previous options. For people running Git older than 1.8.5, please see bstpierre's answer below.

Community
  • 1
  • 1
Gavin Anderegg
  • 6,281
  • 2
  • 25
  • 35
  • 2
    I'd like to add that when using git-pull within a hook will not work unless you unset GIT_DIR. [Relevant.](http://stackoverflow.com/questions/4043609/getting-fatal-not-a-git-repository-when-using-post-update-hook-to-execut) – zpmorgan Feb 28 '12 at 07:08
  • Starting git 1.8.5 (Q4 2013), you will be able to "use a git command, but without having to change directories". See [my answer below](http://stackoverflow.com/a/20115526/6309) – VonC Nov 21 '13 at 08:09
  • Just use following example command > git -C "" pull – Dharmendrasinh Chudasama Feb 07 '22 at 06:49

9 Answers9

516

Starting git 1.8.5 (Q4 2013), you will be able to "use a Git command, but without having to change directories".

Just like "make -C <directory>", "git -C <directory> ..." tells Git to go there before doing anything else.

See commit 44e1e4 by Nazri Ramliy:

It takes more keypresses to invoke Git command in a different directory without leaving the current directory:

  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

The methods shown above are acceptable for scripting but are too cumbersome for quick command line invocations.

With this new option, the above can be done with fewer keystrokes:

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

Since Git 2.3.4 (March 2015), and commit 6a536e2 by Karthik Nayak (KarthikNayak), git will treat "git -C '<path>'" as a no-op when <path> is empty.

'git -C ""' unhelpfully dies with error "Cannot change to ''", whereas the shell treats cd ""' as a no-op.
Taking the shell's behavior as a precedent, teach git to treat -C ""' as a no-op, as well.


4 years later, Git 2.23 (Q3 2019) documents that 'git -C ""' works and doesn't change directory

It's been behaving so since 6a536e2 (git: treat "git -C '<path>'" as a no-op when <path> is empty, 2015-03-06, Git v2.3.4).

That means the documentation now (finally) includes:

If '<path>' is present but empty, e.g. -C "", then the current working directory is left unchanged.


You can see git -C used with Git 2.26 (Q1 2020), as an example.

See commit b441717, commit 9291e63, commit 5236fce, commit 10812c2, commit 62d58cd, commit b87b02c, commit 9b92070, commit 3595d10, commit f511bc0, commit f6041ab, commit f46c243, commit 99c049b, commit 3738439, commit 7717242, commit b8afb90 (20 Dec 2019) by Denton Liu (Denton-L).
(Merged by Junio C Hamano -- gitster -- in commit 381e8e9, 05 Feb 2020)

t1507: inline full_name()

Signed-off-by: Denton Liu

Before, we were running test_must_fail full_name. However, test_must_fail should only be used on git commands.
Inline full_name() so that we can use test_must_fail on the git command directly.

When full_name() was introduced in 28fb84382b ("Introduce <branch>@{upstream} notation", 2009-09-10, Git v1.7.0-rc0 -- merge), the git -C option wasn't available yet (since it was introduced in 44e1e4d67d ("git: run in a directory given with -C option", 2013-09-09, Git v1.8.5-rc0 -- merge listed in batch #5)).
As a result, the helper function removed the need to manually cd each time. However, since git -C is available now, we can just use that instead and inline full_name().

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • 1
    Does not work for me: # git --version && git -C ~/.m2/ checkout master git version 1.8.3.4 (Apple Git-47) Unknown option: -C usage: git [--version] [--help] [-c name=value] [--exec-path[=]] [--html-path] [--man-path] [--info-path] [-p|--paginate|--no-pager] [--no-replace-objects] [--bare] [--git-dir=] [--work-tree=] [--namespace=] [] – Jan Galinski Jan 30 '14 at 19:48
  • 7
    @JanGalinski But I did mention "Starting git 1.8.5". So git 1.8.3.x wouldn't know yet about that option. – VonC Jan 30 '14 at 19:49
  • 2
    On Ubuntu 12.04 I had to install a newer git version. I did it like this: `apt-get install software-properties-common python-software-properties` then add the git repo `add-apt-repository ppa:git-core/ppa`. The last step is to update git: `apt-get update && apt-get upgrade`. – ph3nx May 30 '14 at 12:22
  • Argh crap CentOS 6.6. – Brian Cannard Jun 24 '15 at 07:53
  • 1
    @avesus no problem: http://stackoverflow.com/q/21820715/6309 or http://serverfault.com/a/604124/783 or https://www.howtoforge.com/how-to-install-the-latest-git-version-on-centos – VonC Jun 24 '15 at 07:56
  • Doing this on many folders with wildcards does not work... `git -C */default pull` :-( Anyone? – Jonny May 30 '16 at 08:41
  • @Jonny I agree, it won't work (even in bash shell). You would need to wrap it with for: http://stackoverflow.com/a/13337546/6309 – VonC May 30 '16 at 08:43
  • After various failed attempts e.g `--git-dir=foo/.git`, `--work-tree=foo`, the `-C` option seems to cover it, thanks. – tresf Sep 18 '17 at 16:39
  • cannot use the `~` in the path, use the `$HOME` or full path or relative path will be ok. – Xin Meng Feb 02 '18 at 11:30
  • @XinMeng Strange: what OS and git version are you using?: `~` works just fine when used with `-C`. – VonC Feb 02 '18 at 12:16
  • @VonC I am using Fedora 26 – Xin Meng Feb 02 '18 at 12:28
  • @XinMeng And which version of Git? – VonC Feb 02 '18 at 12:45
  • @VonC git version 2.13.6 – Xin Meng Feb 02 '18 at 12:53
  • @XinMeng Seems recent enough. A `git -C ~/path/to/other/git/repo remote -v` should work. – VonC Feb 02 '18 at 12:54
59

Edit:

There's either a bug with git pull, or you can't do what you're trying to do with that command. You can however, do it with fetch and merge:

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

Original answer:

Assuming you're running bash or similar, you can do (cd /X/Y; git pull).

The git man page specifies some variables (see "The git Repository") that seem like they should help, but I can't make them work right (with my repository in /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.

Running the command below while my cwd is /tmp updates that repo, but the updated file appears in /tmp instead of the working tree /tmp/ggg2:

GIT_DIR=/tmp/ggg2/.git git pull

See also this answer to a similar question, which demonstrates the --git-dir and --work-tree flags.

Community
  • 1
  • 1
bstpierre
  • 30,042
  • 15
  • 70
  • 103
  • Did you try using just GIT_WORK_TREE? – Arrowmaster Feb 22 '11 at 20:22
  • @Arrowmaster: yes, if you do that, git can't find the `.git` directory. – bstpierre Feb 22 '11 at 20:25
  • Ah right, the man page says that GIT_WORK_TREE is not used if GIT_DIR is not set. It seems strange that its not working then when both are used. – Arrowmaster Feb 22 '11 at 20:32
  • @Arrowmaster: I have to wonder if there's a bug here somewhere. If I do `git --git-dir=/tmp/ggg2/.git --work-tree=/tmp/ggg2 pull`, I get an error message. But if I do `git --git-dir=/tmp/ggg2/.git --work-tree=. pull` while I'm in /tmp, it puts the updated files in /tmp as it should. – bstpierre Feb 22 '11 at 20:36
  • @bstpierre: I don't have access to a system with git installed right now but if I did I would be trying other alternatives with `--work-tree` right now like `--work-tree=/tmp/ggg2/` and `--work-tree=/tmp/ggg2/.` since it could be an issue with how its parsing the path. – Arrowmaster Feb 22 '11 at 20:40
  • @bstpierre: Also what version of git are you trying this on? – Arrowmaster Feb 22 '11 at 20:46
  • Thanks, your edited answers helped. Spent an hour playing with git pull and various options and `cd`s until I tried your solution and it worked – German Rumm Apr 14 '12 at 15:58
  • I'm using git v1.7.9.5 and `git --git-dir=${HOME}/repo/.git pull` works fine. – wting Dec 20 '12 at 18:31
  • Thank you for the parenthesized `cd`. – Olathe Jun 05 '13 at 14:00
34

You may wrap it in a bash script or git alias:

cd /X/Y && git pull && cd -
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
takeshin
  • 49,108
  • 32
  • 120
  • 164
  • 1
    This definitely does the trick, but I'm wondering if there's some way to use the git command without changing directories. I'm starting to think that there isn't. – Gavin Anderegg Feb 22 '11 at 20:15
  • 1
    and use the `pushd/popd` pair instead of `cd/cd-` – axd Jul 31 '17 at 08:49
  • 1
    Or you could use a subshell to prevent having to cd back: `(cd xyz && git pull)` – jarmod Sep 18 '19 at 14:39
32

This post is a bit old so could be there was a bug andit was fixed, but I just did this:

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

And it worked. Took me a minute to figure out that it wanted the dotfile and the parent directory (in a standard setup those are always parent/child but not in ALL setups, so they need to be specified explicitly.

Electric Coffee
  • 11,733
  • 9
  • 70
  • 131
samtresler
  • 663
  • 1
  • 7
  • 8
7

As some of my servers are on an old Ubuntu LTS versions, I can't easily upgrade git to the latest version (which supports the -C option as described in some answers).

This trick works well for me, especially because it does not have the side effect of some other answers that leave you in a different directory from where you started.

pushd /X/Y
git pull
popd

Or, doing it as a one-liner:

pushd /X/Y; git pull; popd

Both Linux and Windows have pushd and popd commands.

IvanD
  • 7,971
  • 4
  • 35
  • 33
5

Using combination pushd, git pull and popd, we can achieve this functionality:

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

For example:

pushd "E:\Fake Directory\gitrepo" && git pull && popd
Raman Sahasi
  • 30,180
  • 9
  • 58
  • 71
4

You can write a script like this:

cd /X/Y
git pull

You can name it something like gitpull.
If you'd rather have it do arbitrary directories instead of /X/Y:

cd $1
git pull

Then you can call it with gitpull /X/Z
Lastly, you can try finding repositories. I have a ~/git folder which contains repositories, and you can use this to do a pull on all of them.

g=`find /X -name .git`
for repo in ${g[@]}
do
    cd ${repo}
    cd ..
    git pull
done
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
jonescb
  • 22,013
  • 7
  • 46
  • 42
2

For anyone like me that was trying to do this via a drush (Drupal shell) command on a remote server, you will not be able to use the solution that requires you to CD into the working directory:

Instead you need to use the solution that breaks up the pull into a fetch & merge:

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
dkinzer
  • 32,179
  • 12
  • 66
  • 85
1

This might be a similar problem, but you can also simply chain you commands. eg

On one line

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

Or via SSH.

ssh username@atyourserver.com -t "cd ~/Sites/thedir/web;git pull origin master"
John Ballinger
  • 7,380
  • 5
  • 41
  • 51