1.I'm new to git and would like to know what happen if I've a file which was modified and already staged in the past(but now modified again), and I want to commit the file using a command such as :
git commit -m "yada yada" ~/home/Dan/project/file_to_commit.py
Is this equivalent to doing:
a.git add ~/home/dan/project/file_to_commit.py
b.git commit -m "yada yada"
If not please explain.
2.How can I see the remotes branch commits logs?(pushes) without doing git pull?
Thanks
This might be better as two separate questions, and the second question is already answered correctly, but I'll take a stab at answering both. Before I start, though, I want to say that the path
~/home/Dan/project/file_to_commit.py
is pretty suspect: it suggests that your git directory is/.git
, which is not a good idea. I'm going to assume, below, that the.git
directory is much further down and you're just addingproject/file
orfile
(and I'll trim the paths in the question).Note that the TL;DR version of the first answer is that they're almost the same, and you only need to know about the difference in some edge cases. (Hence the existing answer from eleventhend is probably good enough for most purposes.)
Q1: Add and commit vs
git commit path
No, it's not exactly equivalent. This is a little bit tricky and it will help a lot if we define some terms and get a few things pinned down a bit more.
Also, you say "already staged in the past (but now modified again)", which leaves a bit of ambiguity: did you do a
git commit
in between these operations? I'll address both the "yes" and "no" cases by describing the full, general case.The index
First, we need to define git's index or staging area (it has even a few more names, e.g., cache as in
git diff --cached
, but "index" and "staging area" are the two most common terms). Git has a (one, single) standard index—let's call this "the" index, and when we want to refer to another, different index, we'll spell out which other one we mean. When you run most normal git commands, includinggit add
, git updates this index.1Next, we need to take some notes on what's actually in the index. Aside from some interesting but not relevant here cases like merges, and one thing I'm leaving out on purpose, in essence, the index has one entry per file that will be in the next commit.2 That is, suppose you've made a commit, or checked out some branch, so that your current commit, which is now in your work tree, has 100 files in it. (Your work tree may have additional untracked and/or ignored files, as long as it also has those 100 files.)
Using
git add
When you run
git add
, git stores a new copy of each of the files being added into the repository, which it calls blob objects. It calculates a hash value for each blob as it adds it to the repository, then puts the new hash into the index.When you run
git commit
—at least, without either paths or-a
—git reads the index and uses that to form the new commit. If the new commit would have the same tree as the previous commit,3 git requires that you add the--allow-empty
flag. (This doesn't mean that the index is empty, but rather that the index matches the index you'd get by re-checking-out the current commit. So--allow-empty
might be the wrong name for this flag; it maybe should have been called--allow-same
orallow-unchanged
or some such.)Hence, if you do
git add path
and thengit commit -m message
, you'll get a commit that uses the index as updated by thegit add
, which may have additional updates from before thatgit add
. Since there's just the one entry per path, though, if you do:there will only be one update to
foo.py
in the new commit.So what's the difference?
I claimed above that
git commit path
(andgit commit -a
) is not exactly the same as doing thegit add
and thengit commit
. Let's look at the precise difference.When you give path names (or
-a
) togit commit
, git uses a new, different, temporary index. It starts by copying something—exactly what gets a bit complicated—to the temporary index, then it adds each file that is to be committed, then it makes a commit from the temporary index. Once the commit is done, git goes back to using the normal, ordinary index, and it updates the index. That is, after adding stuff to the temporary index and committing, it also adds to the regular index.To see how this really works we need some examples. Suppose we have two files and we
git add
a change to one of them:At this point,
git status
will tell us that we have changes tofile1
that are staged to be committed, and changes tofile2
that are not staged to be committed.If we run
git add file2; git commit
, we'll get both updates in the new commit. Once the commit is done,git status
will show there is nothing to commit. But if, instead, we do:and then run
git status
, we'll see thatfile1
is still staged and ready to commit. The commit we just made has only the change tofile2
.This is because the
git commit file2
command started by making a temporary index using theHEAD
commit, addingfile2
, making the commit, and then updating the normal index with the newfile2
. This last bit is important: if git didn't copy the change back into the (regular) index, the index would still have the old version offile2
, and the next commit would undo the change we just committed.This copy-back step has an important side effect. Suppose that we have a complicated change to
foo.py
. For instance, suppose we went to fix a bug, and along the way we refactored a few functions. We've tested the fix and it works, so we didgit add foo.py
and were about to commit it:At this point we realized that we shouldn't commit both changes together: we should commit the refactored code first, and then commit the bug fix.4
Well, we've already
git add
-ed the refactored and fixed code, so it's safely stashed away in the index, right? (No, WRONG! Danger Will Robinson! But let's proceed, since this is an example.) So we can just undo the fix part, leaving only the refactoring, and commit that first:Once that commit is done, we can commit the staged version:
Alas, git now tells us that there's nothing to commit. What happened to the carefully staged full-fix version of
foo.py
?The answer is,
git commit foo.py
wiped it out. Git first added the refactor-onlyfoo.py
to a temporary index and committed that; but then it copied the refactor-onlyfoo.py
back to the regular index, losing our carefully staged full-fix version. We can either re-create it, or fish around in the repository for the "dangling blob" that is left behind because of this.(This should probably be considered a bug in git, although it's not clear what to do with the staged but uncommitted version.)
git commit -a
and/or paths:--only
vs--include
Here I need to quote myself from just a moment ago. When using
-a
or paths,git commit
starts by copying something—exactly what gets a bit complicated. If you look closely at thegit commit
documentation, you will find the-i
or--include
option (and a corresponding, but default,-o
/--only
option). These control what goes into the temporary index.When using
--include
,git commit
creates its temporary index from the current index. When using the default--only
mode,git commit
creates its temporary index from theHEAD
commit.(Because of the copy-back step at the end, the only way to see that both commands are in fact using a temporary index is to view the index in the middle of the commit operation. If we use
--include
and check after the commit is done, the regular index will match the newHEAD
commit, as ifgit commit
were adding to the regular index rather than to the temporary index. Fortunately it's very easy to view the regular index "in the middle", by not supplying the-m
flag, so thatgit commit
fires up the editor. While that's going on, rungit status
in another window, or using job control. Here's an example:This shows that the (regular) index is still set up the way we had it. Once we write the message out and exit the editor, the commit process will update the regular index for the new
mxgroup.py
entry, and the now-committeda.py
change is also in the newHEAD
commit, sogit status
will show neither file.)Q2: Viewing logs
Git itself operates almost entirely locally. You may be able to do this directly with web viewers, but it's pretty convenient to just do locally, by first running
git fetch
.The
git pull
command is actually meant as a convenience. There are two steps needed to incorporate other people's commits:These two steps are handled by different git commands:
git fetch
does step 1, andgit merge
andgit rebase
—you must pick one of the two—does whichever version of step 2 you want.The
git pull
command simply does step 1, then does step 2. It choosesmerge
unless you instruct it otherwise. For historical reasons, it has multiple ways of choosing which operation to run in step 2.My recommendation is that as a newbie to git, you avoid
git pull
entirely. There are two reasons for this, only one of which is valid today (unless you're stuck with very old versions of git):Traditionally,
git pull
has had various bugs, some of which can even lose work. (As far as I know these are all fixed since git 2.0.)While it is convenient,
git pull
obscures what's happening and makes you choose merge-vs-rebase too early. It is true that rebase is almost always the right answer, butgit pull
defaults to doing merge, which means that its default is wrong for new users. Plus, of course, there's that "obscures what's happening" issue. If you knew about fetch and then rebase as separate steps, this question probably would not even have come up.1The index is just a file, and you can find it in
.git/index
. You can make git use a different index by settingGIT_INDEX_FILE
in the environment, but this is mainly meant for use by scripts likegit stash
.2The cases I'm leaving out are:
Conflicted merges, which record up to three entries per path, using non-zero stage numbers. Once your resolve the conflict and
git add
the result, the nonzero stages are cleaned out and the normal stage-0 result is stored instead, and we're back to the normal, ready-to-commit case for that index intry.Removing a file that is in the current commit (
git rm
, with or without--cached
). This writes a special stage-0 entry marking the file as to-be-omitted, rather than simply removing the entry.3If you're committing a merge, git allows the tree to match those of any or all parents, since the merge commit needs to record the multiple parents. The "empty" test is thus applied only to non-merge, single-parent commits. This is documented much better in modern git than it was in old versions of git, but it still has the wrong name.
4This has nothing to do with git itself. The idea here is to commit small, readable, understandable, and most importantly testable changes. Any time you find yourself writing up a commit as "do A and B, and fix C, and add D and E" it's an indication that you should probably split this into one commit per thing—in this case, about 5 separate commits.
It is actually equivalent. When you commit a file directly, using
git commit <filepath>
, it stages the current file before committing. You do have to stage the file the first time the file is added before committing it (tell the repository to start tracking the file) usinggit add <file>
.Sample workflow:
git add <file>
git commit -m "yada yada" <file>
git commit -m "blah blah" <file>
2.
To see the commit logs of a remote git repository, first use
git fetch
on the repository, then rungit log <path/branch>
to view the log.See here: https://github.com/abhikp/git-test/wiki/View-the-commit-log-of-a-remote-branch