A perfect example of the sort of trouble I'm having with git just happened again. I have a public bare repo on my machine that I have cloned to make a private repo. I just want to sync my branches on my public and private repos. I do not want to merge across branches, I just want to "sync". So, here's what I did. In my private repo: % cat .git/remotes/origin URL: /repos/git/project Pull: refs/heads/master:refs/heads/origin Pull: refs/heads/topic:refs/heads/topic And this is the sequence of unfortunate events: Starting on topic branch: % git commit -a -m "Fix spacing rules" % git checkout master % git pull [Won't pull non-fast-forward on my topic, so I try to get that synced.] % git checkout topic % git push [ok, fine, seems good.] [Now, instead of remembering to move back to master, I do this:] % git pull Trying really trivial in-index merge... fatal: Merge requires file-level merging Nope. [AAAAGH!] Merging HEAD with 37e229835103a11365b1e081f9b9987a88437e62 Merging: e298e7f Skip rails in user nets 37e2298 Typofixen. [NO NO NO! This is not what I want!] found 1 common ancestor(s): a2ba736 Try #2: Fixed (mostly harmless) bugs in handling of time variable. Auto-merging src/ast/tstD.cc Auto-merging src/meth/XMLImporter.cc Auto-merging src/meth/XMLImporter.hh Auto-merging src/meth/tstXMLI.cc merge: warning: conflicts during merge CONFLICT (content): Merge conflict in src/meth/tstXMLF.cc Auto-merging src/nat/MacroFanLoader.cc Auto-merging src/nat/VPE.cc Auto-merging src/nat/PnDef.cc Auto-merging src/nat/VLExporter.hh Auto-merging src/nat/tstMod.cc Automatic merge failed; fix conflicts and then commit the result. Ok, now I'm hosed. Putting aside WHY git would do this to me (yes, I know the answer is that I asked for it), on my topic branch I now have tons of files listed when I do git status. git diff shows tons of stuff I don't want in my branch. So, I edit the file and "fix" the merge conflict, then realize that this is probably not what I ...
Because you told it to.
% cat .git/remotes/origin
URL: /repos/git/project
Pull: refs/heads/master:refs/heads/origin
Pull: refs/heads/topic:refs/heads/topic
It tells "git pull" to drive "git fetch" to copy their master to
your origin and overwrite your topic with their topic, and then
merge their master to whatever branch you are currently on.
The sane/safe thing to do in the traditional layout (I'll talk
about non-traditional one in a second) is:
- do your 'pull' only and always while on your 'master' and not
anywhere else.
- never build on a branch that appears on the RHS of ':'.
This layout is convenient when you always do fetches and pulls
while on 'master', but has burned enough people. So what people
on the list seem to recommend is to use a separate remote layout
in the repository.
The principle is:
* The branches you work on in the repository are kept in refs/heads/
* Copies of branches from other repositories (it does not
matter who is in control of them -- some of them may be your
repository) are kept in refs/remotes/<symbolic name>.
So the current "git clone" (if you are using 1.4.4 series, you
can say "git clone --use-separate-remote") creates something
like this instead:
URL: /repos/git/project
Pull: refs/heads/master:refs/remotes/origin/master
Pull: refs/heads/topic:refs/remotes/origin/topic
(git-clone from 1.5.0 does not actually make remotes/origin file
in .git/ that has the above -- it creates the moral equivalent
in .git/config).
So whatever you do the first step of "git pull", which is "git
fetch", will _not_ overwrite the current branch.
In order to prevent merging their 'master' into your 'topic'
when you are on 'topic', git-fetch/git-pull from 1.5.0 uses
further safety which is left by 'git clone'. The real
configuration created by 'git clone' looks like this:
[remote "origin"]
url = /repos/git/project
fetch = ...This I understand, and can follow. Sorry, but there my comprehension
stops. Lots of confusion and befuddlement follow. Thank you in
This I don't quite understand. So, if it is on the LHS, it is ok?
But, if it is ALSO on the RHS it is not?
So, this:
Pull: refs/heads/topic:refs/heads/topic
really means don't don't work on a branch named topic in this
repository?
I assume by "build on" you mean "work, compile, check stuff in,
I don't currently have any 'refs/remotes' of any sort, so I guess you
mean that the new principle, using git clone --use-separate-remote
So, using 1.4.4 series, or 1.5, the "sane" way to work in git
I assume by this you mean that if I do the separate remote trick, I
will not shoot myself by doing a 'git pull' while on my topic branch,
Ok, so if I am on master, I do this:
[master] % git pull
and this will fetch the remote master and merge it to my master, and
fetch the remote topic and merge it to my local topic.
While, if I am on my topic branch, if I do this:
[topic] % git pull
it sill fetches from the remote master and the remote topic, but will
not merge at all.
Could you verify if I have stated your position correctly?
If I am, this still seems bizarre. I really just want a way to sync
two repos that works consistently, and is invoked consistently, no
matter what branch I am currently on. And, again, by "sync", I just
mean no cross-branch merging --- no "crossing of the streams". Even
if it were limited to syncing the current branch only, that would be
ok, but this variable behavior seems rather odd and confusing. In
other words, I just want to type the equivalent of 'git sync' and have
it work, and not have to give a branch name, or be in the "right
place" for it to work as I expect.
Thus, I don't want to have to think "oh, I'm on my topic branch, and
if I really want to sync from my remote repo, I need to get on my
master branch". It seems that the only difference in the "insane" way
I was doing ...It means to fetch the remote's branch "refs/heads/topic" and store the current head in _your_ "refs/heads/topic" as a tracking branch, but only if it's a fast-forward. Yes, I know it says "Pull:", but it's really about fetching. You can then merge the result of that fetch into your current branch. So what you want is something like: Pull: refs/heads/topics:refs/heads/remote-topic which will use 'remote-topic' as a tracking branch, always updating it at fetch time to reflect the remote's version of topic. You can then merge remote-topic into your local topic branch by fetching+merging, or I believe he means 'store commits in'. That is, the RHS of the refspec should be used solely for tracking fetches from the remote. If you make a commit on top of it (either directly, or by doing a merge), then the fetch must either throw away your commits, or fail to fast-forward to Yes. It will basically give you a RHS of "refs/remotes/$REMOTE/$BRANCH" to track a branch $BRANCH coming from $REMOTE (generally "origin"); it's a more organized way of doing what I mentioned above (RHS of Most people think so (though I think Junio actually still uses the traditional layout, because he finds it more convenient). Note that in 1.5, --use-separate-remote is the default (the option is still No, you will not shoot yourself because the fetch part of the pull will store the remote's position of 'refs/heads/topic' at 'refs/remotes/origin/topic' instead of trying to overwrite the branch you've been working on. As an additional bonus, you can put this in your .git/config: [branch "topic"] remote = origin merge = refs/heads/topic which means "When I'm on my refs/heads/topic branch and I issue a git-pull without any arguments, do a git-fetch on origin. Then, merge what the remote end calls refs/heads/topic into my current branch." Without this, git-pull in v1.4.* will attempt to merge the remote's That is correct. If you add the config I mentioned above, you can get the "automatically ...
A very good point, and an obvious one in retrospect. I guess I will be entirely satisfied if I am on branch X I can just say 'git pull' and it will NOT pull from any other branch. You have added to my understanding on this, and thank you for taking the time. Bill -
I think this should go to git-pull/git-clone man pages. Personaly for me this post dispels dark magic about git-pull's merging logic. I always did git-fetch and then git-pull . <some-branch> to control what and where should be merged. -
Fair enough. But I am known to be very bad at writing, so I would ask the list to proofread this to see if it makes sense, and prefereably rewrite it to make it easier to understand. I think it is technically accurate -- I just do not know if I am not writing enough, leaving certain necessary things unsaid, because I assumed (wrongly) too much knowledge on the reader's side. diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt index a81d68c..94478ed 100644 --- a/Documentation/git-pull.txt +++ b/Documentation/git-pull.txt @@ -33,6 +33,60 @@ include::urls.txt[] include::merge-strategies.txt[] +DEFAULT BEHAVIOUR +----------------- + +Often people use `git pull` without giving any parameter. +Traditionally, this has been equivalent to saying `git pull +origin`. However, when configuration `branch.<name>.remote` is +present while on branch `<name>`, that value is used instead of +`origin`. + +In order to determine what URL to use to fetch from, the value +of the configuration `remote.<origin>.url` is consulted +and if there is not any such variable, the value on `URL: ` line +in `$GIT_DIR/remotes/<origin>` file is used. + +In order to determine what remote branches to fetch (and +optionally store in the tracking branches) when the command is +run without any refspec parameters on the command line, values +of the configuration variable `remote.<origin>.fetch` are +consulted, and if there aren't any, `$GIT_DIR/remotes/<origin>` +file is consulted and its `Pull: ` lines are used. +In addition to the refspec formats described in the OPTIONS +section, you can have a globbing refspec that looks like this: + +------------ +refs/heads/*:refs/remotes/origin/* +------------ + +A globbing refspec must have a non-empty RHS (i.e. must store +what were fetched in tracking branches), and its LHS and RHS +must end with `/*`. The above specifies that all remote +branches are tracked using tracking branches in +`refs/remotes/origin/` hierarchy under the ...
Hi, git reset --hard It's probably explained in the new user manual (I did not check). Ciao, Dscho -
Hmm ... from my topic branch: % git reset -hard Usage: /usr/bin/git-reset [--mixed | --soft | --hard] [<commit-ish>] Bill -
Hi, Please use two dashes: "--" instead of "-". Ciao, Dscho -
Ok, others already replied, but here's a few rules to ease your mind in general: - First off: you can always _trivially_ get back to whatever state you had before, as long as you committed it, and didn't have any dirty state (uncommitted patches) in your working tree. This is something that it's worth repeating, and even perhaps experimenting with to get really comfortable with. Why? Because once you learn to get back to any random state you had before, and once you are comfortable with that, you suddenly lose the fear of experimenting. Whatever you screw up, if you're confident you can get back, who cares? So the "get back from a mistake" should probably be at the very front of our manuals and tutorials. I don't think it currently is, but it's actually not that hard. There's just one command to remember: "git reset", and the only issue you might ever have with it is: - make sure you're on the right branch first! If the problem you had is that you're on the wrong branch, switch branches first! Don't try to make the wrong branch "look right". But once you know you're on the right branch, you know that "git reset" is your friend to getting it anywhere you want. - do you want to throw away all your working tree changes (and if you screwed up some git operation, the answer is usually "Yes!"). If so, add the "--hard" flag. It's not on by default, exactly because it *will* throw away all state in your tracked working tree, and reset it to whatever you want to go back to. - exactly *what* do you want to go back to? The default is to go back to the state at the last successful commit, but sometimes what you screwed up was exactly the last commit (eg an unintentional "pull" that actually succeeded), and then you need to tell it *what* to reset to. That's really all there is to it. It's really simple, although especially the second point can take some practice to get right. So NORMALLY, if you did ...
Well, I have read all of the very welcome advice and am comfortable with all of it. However, I still have a few open issues with the other branch of this discussion, i.e., why can we not have an update operation that respects branches in the first place, as 'git pull' seems to do, when run from the master branch? I do realize that the branch 'foo' in my repo is different from the branch 'foo' in your repo. However, I want to track things, and "track" here is a very appropriate word. Tracks don't cross, and I don't want to cross my "logically equivalent" branches (at least yet), even though, as Linus pointed out in great detail, this is easy to undo (though, do see below for a qualification of "easy"). So, why should I care? Because, an ounce of prevention is worth a pound of cure. So, if a pound of undo is so very easy, then, in my mind, an ounce of preventing the problem in the first place is at least 1/16th that. When working in a peer-to-peer relationship, I often push and pull with my peers, perhaps on a daily basis, perhaps weekly. Just the other day, my peer was the one who goofed up his branches and I pulled them into my public repo, all tangled up, and did not realize it. Thence pulled into my private repo, did lots of work, pushed back to my public repo, and after more time intervened, realized something was wrong. It took a LOT of work (for me, I'm sure for others here it would have been much, much less) for me to 1) figure out the genesis of the problem, and 2) figure out how to undo it all without destroying my subsequent work. When we both do this, and merge unexpectedly, at different points on one branch from a different point on another, and then pollute each others' repos, it does become rather ... well, annoying is the best way to put it. In CVS, if I am on branch topic and say 'cvs update', it updates my branch topic. If I am on branch master and say 'cvs update', it updates my branch master. Etc., etc. It doesn't matter that you ...
Bill Lear wrote: [cut] With git 1.5.0-rc4 cloned repository, with globbing refspecs for origin you don't have the problem. When you are on branch 'master', "git pull" fetches and merges 'origin/master' into 'master'. When on any other branch, "git pull" would fetch only (unless configured otherwise). Note: you cannot pull into 'master' if you are not on 'master' because In CVS branches are totally f**ked up. And enforced update before commit So do fetch, and do pull only when changes are ready... RTFM. Take a look at http://git.or.cz/gitwiki/GitLinks namely section "Seminars and presentations", read new Git User's Manual also at http://www.fieldses.org/~bfields/git-user-manual.html, browse GitWiki. By the way, the workflow looks slightly different if you pull directly from one another (A pulls or fetches from B, B pulls or fetches from A), and if you have one central public bare repository (A pulls or fetches from 'public' and pushes her changes to 'public', B pulls or fetches from 'public' and pushes his changes to 'public'). In the latter git asks you to pull (fetch) before pushing if you are not up to date. Notice that it is on push, not on commit! We should really update http://git.or.cz/gitwiki/GitWorkflows ... but how to make diagrams: ASCII art is hard because it needs monospace, upload of images attachements is not possible... -- Jakub Narebski Poland -
Actually, git does that all correctly for other branches too, but only in git-1.5. It's one of the bigger UI warts that got fixed since the last release (although it got fixed by better config management, and as such you'll only *see* the fixes if you end up doing the initial clone with the new git version - if you use a new git version with an old repo, many - but not all - bad semantics will remain). Considering how stable the -rc kernels are (and actually, git "master" in general), there's really very little reason to wait for the real release. Junio has been very careful, and I think a lot of the delay in 1.5 has been about trying to get all the new stuff that changes semantics subtly in before the release, so that Junio will not have to do any real user- visible changes later. So it might be worth while trying out git-1.5.0-rc4, and seeing if that solves some of the UI issues for you guys. It changes things like where the default remote branches are, and makes the distinction between "my local copy of branch X" and "the remote branch X" much clearer, which has clearly been a UI problem. Linus -
Ok, very reasonable. I've been our corporate guinea pig, so I'll give this a whirl. One thing I'm very, very happy about in git is the ability to quickly experiment. Believe it or not, I have solved actual git problems in our company by doing just that. Of necessity, it's largely my complaints and problems that are aired here, though. Bill -
With regard to the new version and old repos, am I correct in assuming that we can upgrade our old repo (a bare one) to the new git by first installing the new git, and then doing this: % cd /repos/git % mv project project.old_git % git --bare clone project.old_git project or is there something else we must do? Bill -
So, I assume I need to tell our developers that once we have installed the new git, they will need to set aside their old repos and just clone again from our company repo? Bill -
Right. Otherwise they need to do the config changes by hand in their existing repository, which may be annoying/tedious/painful/difficult, depending on your knowledge level with git. You can actually use the old developer repositories with the newer Git without doing anything specific to upgrade them. Its just that 1.5.0 sets up the initial config of the repository differently, and that's exactly the change in functionality you are looking for. They can save their old topic branches (if they are important) by doing something like: mv proj old_proj git clone git://server/proj proj cd proj git fetch ../old_proj topicA:topicA [topicB:topicB ...] at which point ../old_proj can be tossed. -- Shawn. -
Nope.
1. New git works with old repositories, and would continue to work.
Nevertheless you need new layout and new configuration to make use
of some new features.
2. They need to clone _their own_ repositories. It's the simplest
way, but
3. You can simply
a) convert remotes configuration from .git/remotes/origin file
to .git/config using remotes2config.sh script in contrib area
of git, or http://repo.or.cz/w/git.git?a=blob_plain;f=contrib/remotes2config.sh
b) hand edit remotes configuration to use globbing for refspec,
and per branch configuration
If old repository was _not_ cloned with --use-separate-remote (using
separate remote layout), you would also have to:
c) move branches from old layout to new layout using "git branch -m"
command: 'refs/heads/origin' branch to 'refs/remotes/origin/master',
all branches except 'master' (refs/heads/master) from
'refs/heads/<branch>' to 'refs/remotes/origin/<branch>'.
That's all. You have new layout and new configuration without re-cloning.
--
Jakub Narebski
Poland
-
Hint: If a developer has some relevant data in his old private repository, he can always pull from old to new repository there after he clones from the public repository. This way you won't lose any data in the conversion. -- MST -
Not unless they want to take advantage of *all* the new features. The new version of git will work fine with old repositories, both on the "server" side and the "user" side. And people can use a lot of the new features even if they do nothing at all. But for the _specific_ case of having a clearly separated "local branch" vs "remote branch" case, you do need to make that distinction clear when you create the repository (unless you want to get really down and dirty with the repo and just modify it yourself: certainly possible but generally just not worth the effort since it's just easier to clone a new one instead). So it's really a matter of how you use it. Switching to a new version of git on the "server side" (ie the shared repository operations) won't really affect anything at all. Linus -
I would actually suggest against that. Why? Because it will set a new "origin" (pointing to your old repo), and if you had something else before, that's probably not what you want. Anyway, for the *shared* repositories, the git-1.5 changes really don't tend to make any difference anyway (since they don't even tend to really _care_ about things like origin branches - they are just used to push and pull from). It's much more noticeable for the actual *development* repositories, because they are the ones that have "origin" pointing to something else. And yes, for those development repositories, it's usually a good idea to just do mv project old-project git clone /repos/git/project cd project .. work work work .. and be happy. You can also set up the new configurations by hand in an old repository, but there really doesn't tend to be a lot of reason to do that. Just as an example: the above was _literally_ what I did myself, just because I was too lazy to start editing .git/config files and setting things up in other ways (renaming origin branches etc). In fact, I just did it the other day for my "sparse" repository (which is another project I started, but that is maintained by others these days). So here's a snippet from my bash history: ... 837 mv sparse old-sparse 838 cat old-sparse/.git/config 839 git clone master.kernel.org:/pub/scm/linux/kernel/git/josh/sparse ... (that "cat old-sparse/.git/config" was just because I had forgotten exactly where the origin of that repo was, so I did that cat just to do a cut-and-paste for the subsequent "git clone" ;^). And yes, I did that just to get the nicer branch layout, something that my old sparse git repo didn't have, because I had set it up with an old version of git (and done some minimal manual maintenance). Linus -
Heh, I do not work on kernels ;-) Seriously, I think you are giving me a bit too much credit, but I do agree that the tip of "master" tends to be very stable most of the time. This is especially true since some tagged releases were followed up with immediate corrections for "oops, brown paper bag" bugs in the past X-<. But the tip of "master" contains dubious change from time to time (for example, I still haven't sorted out your "log -z" stuff, which is already in my tree). -
I usually undo a pull by throwing away just the merge commit by
git reset --hard HEAD^
This seems to always get me back to the head commit I had previously, but I'm
wondering would git in some circumstances leave me with the commits I just pulled
and throw away my own work instead. Or is it guaranteed that I always reset
to the parent commit I had before the pull (i.e. ORIG_HEAD)?
Of course HEAD^ doesn't work the same with fast-forward merges, so it would
probably make more sense to just use ORIG_HEAD all the time.
-
Don't do this. If the merge just fast-forwarded, you'll do the wrong thing. So yes, it _works_, but it only works if you actually ended up having a real merge. In contrast, the ORIG_HEAD thing always works. ORIG_HEAD is also particularly useful for doing things like "ok, what did I get from that pull?" especially when you track somebody elses work (in which case it will basically _always_ be a fast-forward). So I do gitk ORIG_HEAD.. in git almost every time I pull from Junio's git thing, just because it's a wonderful way to see what has changed, if you're interested in that kind of detail. Linus -
Yes, I know. But when looking at the history with gitk, it feels
quite intuitive to just get rid of the one new commit that appeared
on top of the "good" history. Without that kind of visualisation
I would surely always just use ORIG_HEAD as a reference.
Perhaps gitk could (optionally) also show ORIG_HEAD. That way we could
just do
gitk --all
after a pull and see what got pulled, and everything else was already
there, too, if needed.
-
git config alias.new "gitk --all --not ORIG_HEAD" Would give you a new git subcommand: git new which shows all of the new stuff, on all branches, but doesn't show your prior commit history. -- Shawn. -
Aliases don't seem to be working for me; I'm using git 1.5.0-rc4. Am
I doing something wrong?
<tytso@candygram> {/usr/projects/linux/linux-2.6} [master]
37% git version
git version 1.5.0.rc4
<tytso@candygram> {/usr/projects/linux/linux-2.6} [master]
38% git config alias.new "gitk --all --not ORIG_HEAD"
<tytso@candygram> {/usr/projects/linux/linux-2.6} [master]
39% git new
git: 'new' is not a git-command
The most commonly used git commands are:
add Add file contents to the changeset to be committed next
apply Apply a patch on a git index file and a working tree
archive Creates an archive of files from a named tree
...
<tytso@candygram> {/usr/projects/linux/linux-2.6} [master]
40% tail .git/config
[user]
name = Theodore Ts'o
email = tytso@mit.edu
[remote "iwlwifi"]
url = http://bughost.org/repos/iwlwifi.git/
fetch = +refs/heads/*:refs/remotes/iwlwifi/*
[alias]
new = gitk --all --not ORIG_HEAD
-
Here are some patches to fix the horrible error message and to allow
aliases to expand to external shell commands.
- Ted
-
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
---
git.c | 9 ++++++++-
1 files changed, 8 insertions(+), 1 deletions(-)
diff --git a/git.c b/git.c
index 82a8357..c43d4ff 100644
--- a/git.c
+++ b/git.c
@@ -387,8 +387,15 @@ int main(int argc, const char **argv, char **envp)
done_alias = 1;
}
- if (errno == ENOENT)
+ if (errno == ENOENT) {
+ if (done_alias) {
+ fprintf(stderr, "Expansion of alias '%s' failed; "
+ "'%s' is not a git-command\n",
+ cmd, argv[0]);
+ exit(1);
+ }
help_unknown_cmd(cmd);
+ }
fprintf(stderr, "Failed to run command '%s': %s\n",
cmd, strerror(errno));
--
1.5.0.rc4
-
If the alias expansion is prefixed with an exclamation point, treat
it as a shell command which is run using system(3).
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
---
Documentation/config.txt | 6 ++++++
git.c | 10 ++++++++++
2 files changed, 16 insertions(+), 0 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 4e650af..de185d8 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -222,6 +222,12 @@ alias.*::
spaces, the usual shell quoting and escaping is supported.
quote pair and a backslash can be used to quote them.
+ If the alias expansion is prefixed with an exclamation point,
+ it will be treated as a shell command. For example, defining
+ "alias.new = !gitk --all --not ORIG_HEAD", the invocation
+ "git new" is eqvuialent to running the shell command
+ "gitk --all --not ORIG_HEAD".
+
apply.whitespace::
Tells `git-apply` how to handle whitespaces, in the same way
as the '--whitespace' option. See gitlink:git-apply[1].
diff --git a/git.c b/git.c
index c43d4ff..fc08396 100644
--- a/git.c
+++ b/git.c
@@ -159,6 +159,16 @@ static int handle_alias(int *argcp, const char ***argv)
alias_command = (*argv)[0];
git_config(git_alias_config);
if (alias_string) {
+ if (alias_string[0] == '!') {
+ trace_printf("trace: alias to shell cmd: %s => %s\n",
+ alias_command, alias_string+1);
+ ret = system(alias_string+1);
+ if (ret >= 0 && WIFEXITED(ret) &&
+ WEXITSTATUS(ret) != 127)
+ exit(WEXITSTATUS(ret));
+ die("Failed to run '%s' when expanding alias '%s'\n",
+ alias_string, alias_command);
+ }
count = split_cmdline(alias_string, &new_argv);
option_count = handle_options(&new_argv, &count);
memmove(new_argv - option_count, new_argv,
--
1.5.0.rc4
-
ACK. This should also make it possible to do pipelines etc as aliases, although to be *really* useful we would probably have to have some way to specify where the arguments to the alias would go. The more generic solution is obviously to just do it as external shell scripts (which can be named "git-xyzzy" so that you don't even need this kind of thing), but for the simple cases like gitk/qgit/xmerge/whatever, this approach by Ted seems to be a good way to get easy access to stuff that doesn't need anything fancier.. Linus -
Here's a revised patch which fixes a stupid spelling typo in the
documentation. ("eqvuialent" --> "equivalent")
From c16544aa786b0fb244fd974a22831a1210286ec5 Mon Sep 17 00:00:00 2001
From: Theodore Ts'o <tytso@mit.edu>
Date: Sat, 10 Feb 2007 10:50:58 -0500
Subject: [PATCH] Allow aliases to expand to shell commands
If the alias expansion is prefixed with an exclamation point, treat
it as a shell command which is run using system(3).
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
---
Documentation/config.txt | 6 ++++++
git.c | 10 ++++++++++
2 files changed, 16 insertions(+), 0 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 4e650af..e6e9409 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -222,6 +222,12 @@ alias.*::
spaces, the usual shell quoting and escaping is supported.
quote pair and a backslash can be used to quote them.
+ If the alias expansion is prefixed with an exclamation point,
+ it will be treated as a shell command. For example, defining
+ "alias.new = !gitk --all --not ORIG_HEAD", the invocation
+ "git new" is equivalent to running the shell command
+ "gitk --all --not ORIG_HEAD".
+
apply.whitespace::
Tells `git-apply` how to handle whitespaces, in the same way
as the '--whitespace' option. See gitlink:git-apply[1].
diff --git a/git.c b/git.c
index c43d4ff..fc08396 100644
--- a/git.c
+++ b/git.c
@@ -159,6 +159,16 @@ static int handle_alias(int *argcp, const char ***argv)
alias_command = (*argv)[0];
git_config(git_alias_config);
if (alias_string) {
+ if (alias_string[0] == '!') {
+ trace_printf("trace: alias to shell cmd: %s => %s\n",
+ alias_command, alias_string+1);
+ ret = system(alias_string+1);
+ if (ret >= 0 && WIFEXITED(ret) &&
+ WEXITSTATUS(ret) != 127)
+ exit(WEXITSTATUS(ret));
+ die("Failed to run '%s' when expanding alias '%s'\n",
+ alias_string, alias_command);
+ }
count = ...Hi, Here, you add 1 to alias string (though I would put spaces around the So, shouldn't you here, too? It made me feel a little uneasy that we can execute _any_ command now, but I can only find one way to exploit this, when an attacker does not have shell access anyway: git-shell. Ciao, Dscho -
That's not how I code but it does seem to be the prevailing git coding Yes, that makes the error message look a bit nicer. I'll respin the ... and git-shell only allows git-receive-pack and git-upload-pack to be called, with a single argument, and aliases aren't allowed to override commands. So we're safe here, I think. - Ted -
Hi, Yes, sorry. I have a modified git-shell, which allows the git wrapper, too, to allow setting the config. I'll just fix it here. Ciao, Dscho -
If all you've enabled is the ability to set the config, I think we're still safe, since aliases can't override commands. Still there are enough config options that might be scary, either now (the http.ssl* options) or in the future (someone might think that it makes sense to set the post-commit, post-push, et. al hooks in the config), that I wouldn't be particularly comfortable letting git-shell have unrestricted access to set the config without having some restriction about which config parameters were allowed to be set from the restricted shell. Why did you add that ability, out of curiosity? - Ted -
Hi, It seemed a good idea to make the description for gitweb a config variable, and I wanted the users to change that themselves. It no longer seems a good idea, so I will probably just undo my changes. Ciao, Dscho -
I feel a bit uneasy to hear safety argument based on that current restriction, since we might want to loosen it later. -
Hi, After seeing that it was a personal breakage only, I think we only have to keep the safety in mind, _iff_ we are to loosen it later, not before that. For the moment, there are no safety issues, but real advantages, IMHO. Ciao, Dscho -
Loosen which restriction?
1) The ability for aliases to shadow existing git commands?
2) The ability for untrusted users to make arbitrary changes to the
config file?
3) The ability for untrusted users to execute arbitrary git commands via
git-shell?
You hjave to loosen at least 2 of the 3 current restrictions before
the ability to execute shell commands out of aliases becomes a problem
--- and I would argue that either (2) or (3) are things that we would
be insane to loosen at least to the point of allowing untrusted users
to make arbitrary changes to the config or execute arbitrary git
commands, since even today, they could do a huge amount of damage
already.
- Ted
-
I agree, 2 and 3 are the real issue here, not 1. 1 is only an issue for scripts which expect the plumbing to behave a certain way, but doesn't, as the user has aliased the plumbing command. -- Shawn. -
Thanks for your alias and diff patches. I'll be away from the keyboard for most of today, so if the list can do distributed QA (and debugging if necessary) before I return that would be very appreciated ;-). -
Its not you. The problem is 'gitk' is not an internal command, nor is there a 'git-gitk'. So we cannot execute it. Instead we are giving back a horrible error message. Symlink git-gitk to gitk and it works. Sorry about giving false hopes. :-) -- Shawn. -
Yes, but what I meant was that gitk wouldn't stop at ORIG_HEAD,
but just display it as another branch head with a nice green tag.
Normally, displaying ORIG_HEAD would probably not be interesting,
but it might make sense with
gitk --all
It would give more context to the pull than just
gitk ORIG_HEAD..
-
