Reputation: 745
Searching in StackOverflow and on Google, I can find lots of items about how to sync, and do everything with git, except I can't find a reliable way using a bash script to tell if my local repository is in sync with its remote. I've tried:
1.
git branch -v |
perl -wlne'
print "$ENV{reponame} [$1] --> $3 $2"
if /^..(\S+)\s+([a-f0-9]+)\s+(\[(?:ahead|behind)\s+\d+\])/
' |
while IFS= read -r MOD; do
printf ' %s\n' "$MOD" # Replace with code that uses $MOD
done
and,
2.
isInSync=`git remote update>/dev/null; git status -uno | grep -o "is up to date"`
if [ $? -ne 0 ]; then
echo " $reponame [$br] --> Out of sync with remote at $d"
ok=false
fi
and have been trying to find a way to use:
git log --oneline | head -n1 | sed # this I don't know.
to try to get the first word in the log file, which is the commit hash of the last commit, and compare it with:
rev=$(git rev-parse --short HEAD)
which is the commit hash of the local repository branch you are in.
The problem with #1 is that it doesn't seem to pick up when the local is out of sync with the remote.
The problem with # 2, is that it causes the local .git/config to get involved and produces odd attempts to access different remote repositories like heroic.
The problem with #3 is that I can't figure out how to get the hash code from the git log and then compare it to the $rev above. It would seem to be the best bet as when I check it on different computers in different states, it seems to convey the right information.
I am writing a bash script that checks a group of git projects and tells me their states, i.e. up to date, untracked files, uncommitted files, and out of sync with the remote.
Help would be appreciated in either suggesting a better way or how to do the extraction of the commit-hash from the log and compare it to the current commit-hash of the local last commit.
Upvotes: 0
Views: 838
Reputation: 875
OK. I'm up to date locally, but out of sync with remote. So "git status" says
On branch main
Your branch is up to date with 'origin/main'
even though changes to "main" have been made from another computer.
In this situation
git remote show origin
provides useful info. The documentation for that command just says "Gives some information about the remote ." What I get looks like this:
$ git remote show origin
* remote origin
Fetch URL: https://github.com/user/project.git
Push URL: https://github.com/user/project.git
HEAD branch: main
Remote branches:
damaged-mesh new (next fetch will store in remotes/origin)
main tracked
rend3-update-1 new (next fetch will store in remotes/origin)
Local branch configured for 'git pull':
main merges with remote main
Local ref configured for 'git push':
main pushes to main (local out of date)
That's correct, and it tells me that local is out of date.
Another project, which is in sync, returns
Local refs configured for 'git push':
main pushes to main (up to date)
stats pushes to stats (up to date)
What I want to do, as part of a script that packages releases, is to make sure that local is up to date with remote, and abort the release packager if it isn't. But I don't want to change any local state with the check, so I don't want to fetch.
Is checking git status
for
Your branch is up to date with 'origin/main'
and git remote show origin
for
main pushes to main (up to date)
sufficient to make sure that this is a build from a clean "main"? Or is there anything else I should check?
Upvotes: 0
Reputation: 745
In the above code for git_check, I replace the section: 2.
isInSync=`git remote update>/dev/null; git status -uno | grep -o "is up to date"`
if [ $? -ne 0 ]; then
echo " $reponame [$br] --> Out of sync with remote at $d"
ok=false
fi
with:
last_commit=`git log --oneline | head -n1 | grep -o "^\w*\b"`
rev=$(git rev-parse --short HEAD)
if [ "$last_commit" != "$rev" ]; then
echo " $reponame [$br] --> Out of sync with remote at $d"
ok=false
fi
Here is the Console output with some comments
Modify a file that has been committed.
[:~/bin] develop(+4/-2) 2s ± git status
On branch develop
Your branch is up to date with 'origin/develop'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: git_check
no changes added to commit (use "git add" and/or "git commit -a")
[:~/bin] develop(+4/-2) ± git_check
bin [develop] --> Changes to be staged/committed at /Users/userid/bin
[:~/bin] develop(+4/-2) 128 ± git add -A
[:~/bin] develop(+0/-0) ± git diff --cached --shortstat
1 file changed, 4 insertions(+), 2 deletions(-)
[:~/bin] develop(+0/-0) ± git_check
bin [develop] --> Changes to be committed at /Users/userid/bin
[:~/bin] develop(+0/-0) 2s ± git commit -m "Better way to check for remote sync"[develop fab4f1d] Better way to check for remote sync
1 file changed, 4 insertions(+), 2 deletions(-)
[:~/bin] develop(1) ± git_check
OK --> bin [develop] fab4f1d
bin [develop] --> [ahead 1] fab4f1d
[:~/bin] develop(1) 2s ± git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 4 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 416 bytes | 416.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
To https://github.com/userid/repo.git
ee0f0ca..fab4f1d develop -> develop
[:~/bin] develop 4s ± git_check
OK --> bin [develop] fab4f1d
For me, at least, this appears to solve my problem. As the console output shows, it gives me information about what is happening at each stage of the workflow. Especially, it tells me that my local repository is ahead of the remote repository by 1, telling me I need to do a push. On the other machines, I will see that they are behind by 1, telling me I need to do a pull to sync up.
Thanks to torek for the helpful information.
Upvotes: 0
Reputation: 488123
As you are seeing, you have to define what you mean by in sync.
A Git repository first and foremost is a way to hold commits. The set of commits in the repository, as found by branch and tag names and other such names, is what is in the repository. The commits are the history.
Most Git repositories that users work with, however, are not "bare". They have a work-tree or working tree into which files can be extracted from any commit, and/or additional files created and/or various files modified and so on. This working area can be "clean" (matches a commit, more or less) or "dirty".
Is a "dirty" repository, with lots of work going on inside it, "in sync" with some other repository, even if both repositories have exactly the same set of commits? Or are these "not in sync"? What if the other repository has a work-tree and it's "dirty" in exactly the same way? That's something you need to define.
(Besides the work-tree, all repositories—even bare ones—have an index as well, which can also be "clean" or "dirty", and perhaps you should factor that in as well.)
A repository can have one or more remotes defined as well. Given a remote name, such as origin
, Git can be told: connect to some network URL and obtain new commits from a Git over there, and put them into this repository. That's what your git remote update
is doing. How many remotes that contacts—it could get some of them, or all of them, or maybe some are unreachable at the moment—is difficult to answer, as this is all quite configurable.
... [get] the commit hash of the last commit
Each branch name automatically holds the hash ID of the last commit in that branch. There can be more than one "last commit", in other words. Using HEAD
is the right way to find the hash ID of the current commit, but this may not be the tip commit of any branch:
rev=$(git rev-parse HEAD)
If HEAD
contains the name of an unborn branch, this step will fail. That state is the case in any totally-empty repository (because there are no commits, hence there can be no branch names; branch names are required to name some existing commit). It's also the state after a git checkout --orphan
operation, however.
I am writing a bash script that checks a group of git projects and tells me their states, i.e. up to date, untracked files, uncommitted files, and out of sync with the remote.
So, you get to choose how to define each of these things.
In general, I would:
git remote update
does; consider here whether it's good or bad to allow --prune
(see also the fetch.prune
setting).git symbolic-ref HEAD
: if this command fails, there is no current branch, i.e., we're on a detached HEAD.git status --porcelain=v2
or similar, to look at the state of the index and work-tree. Consider checking submodule status here as well; git status
may do this for you, or not, depending on settings.git rev-list --left-right --count branch...branch@{u}
to count the number of commits ahead and/or behind, perhaps after the git remote update
with or without --prune
.dev
might not be related to origin/dev
, for instance. If my dev
goes with origin/develop
I probably set the upstream of dev
to origin/develop
, so consider checking each branch's upstream. Note that git branch -vv
does this (and also counts ahead/behind values, unless told not to, and also has extra support for added work-trees now).Except for current branch and dirtiness (which git status
already reports), most of the work is just git remote update -p
and git branch -vv
, really.
Upvotes: 1