Getting git metadata from git_repository rule - bazel

Prior to this commit (first included in 0.17.0), I was able to use a genrule to gather git metadata from external git repositories.
It effectively looked like this:
genrule(
name = "git-describe-foo",
# We can't dep all of #foo, so we pick a file
srcs = ["#foo//:SOME_FILE"],
outs = ["my_version"],
# Do a git describe and strip off the leading "v"
cmd = "git -C $$(dirname $(location #foo//:SOME_FILE)) describe --tags | cut -c 2- > $#",
# I don't know if this is strictly necessary
stamp = True,
# This is required or bazel will sandbox us with just SOME_FILE
local = True,
output_to_bindir = True,
)
However, this no longer works because the .git/ directory has now been removed. I understand that this was done to improve reproducibility, but the git SHA (and theoretically the git history) should actually not affect the reproducibility of builds.
My original approach was to try to pass in the git SHA and git metadata that I needed via --workspace_status_command somehow, but then i'd also have to use that git SHA to clone the git_repository, which I don't think is possible.
Is there another way to gather this information?

First of all, your use of the genrule is generally broken, as it depends on more than the declared inputs. As you noticed yourself, sandboxing detects these undeclared (and hence untracked by bazel) inputs.
The reason why the .git subdirectory is deleted as part of the git_repository rule is to have reproducible content of the external repository in a machine-verifiable form. However, all parts of the repository rule, including the patch_cmds are executed before the .git subdirectory is removed. So you can create meta data as part of the repository itself, e.g., as follows.
load("#bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
remote = "...",
...
patch_cmds = [
"git log -n 1 --format=%H > VERSION",
],
)
There are two things to keep in mind.
To be reproducible, the meta data should be fully determined by the commit itself.
Make sure that the that the added meta-data file is exported, e.g., by patching exports_files(["VERSION"]) into the BUILD file of the external repository.

Related

Is there a Git config setting to auto-signed-off-by my commits? [duplicate]

I'm looking for a way to write the Signed-off-by: tag automatically when I commit.
I tried configuring it through the .git/config file (Reference). I put these lines of code:
[alias]
commit = commit -s
This did not work. As commented below, you can not edit git's own alias (like commit).(Reference)
I also tried using the command (Reference):
git config --global format.signoff true
Also had no effect. This explains why.
I'm looking for any solution that automatically places the tag and allows me to edit the commit message directly on git, without having to use a system alias.
[Edit made after last comment]
I think if I am guessing correctly then, you cannot alias using words which are 'reserved' words for a git command.
However if you do something like this
[alias]
ci = commit -s
Then it will do what you want it to do.
Use the commits hooks to achieve this
https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#_committing_workflow_hooks
prepare-commit-msg
The prepare-commit-msg hook is run before the commit message editor is fired up but after the default message is created.
It lets you edit the default message before the commit author sees it.
This hook takes a few parameters: the path to the file that holds the commit message so far, the type of commit, and the commit SHA-1 if this is an amended commit.
This hook generally isn’t useful for normal commits; rather, it’s good for commits where the default message is auto-generated, such as templated commit messages, merge commits, squashed commits, and amended commits.
You may use it in conjunction with a commit template to programmatically insert information.
You can use commit.gpgSign option
you can add it per repository by issuing the command below in the repo folder:
$ git config commit.gpgSign true
or for all git repository on your machine:
$ git config --global commit.gpgSign true

How can I force "git commit -s" using "git commit" command?

I'm looking for a way to write the Signed-off-by: tag automatically when I commit.
I tried configuring it through the .git/config file (Reference). I put these lines of code:
[alias]
commit = commit -s
This did not work. As commented below, you can not edit git's own alias (like commit).(Reference)
I also tried using the command (Reference):
git config --global format.signoff true
Also had no effect. This explains why.
I'm looking for any solution that automatically places the tag and allows me to edit the commit message directly on git, without having to use a system alias.
[Edit made after last comment]
I think if I am guessing correctly then, you cannot alias using words which are 'reserved' words for a git command.
However if you do something like this
[alias]
ci = commit -s
Then it will do what you want it to do.
Use the commits hooks to achieve this
https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#_committing_workflow_hooks
prepare-commit-msg
The prepare-commit-msg hook is run before the commit message editor is fired up but after the default message is created.
It lets you edit the default message before the commit author sees it.
This hook takes a few parameters: the path to the file that holds the commit message so far, the type of commit, and the commit SHA-1 if this is an amended commit.
This hook generally isn’t useful for normal commits; rather, it’s good for commits where the default message is auto-generated, such as templated commit messages, merge commits, squashed commits, and amended commits.
You may use it in conjunction with a commit template to programmatically insert information.
You can use commit.gpgSign option
you can add it per repository by issuing the command below in the repo folder:
$ git config commit.gpgSign true
or for all git repository on your machine:
$ git config --global commit.gpgSign true

Why do I get an Large File Warning from Github for a file that i have listed in gitignore?

Without really thinking about it, I've been committing and then pushing to my Github repository the images I am using in development.
After discovering that this was causing issues that prevented me from pushing my project to my branch, I searched for a solution to remove those images from my repository then add those images to my gitignore file.
I found several solutions: StackOverflow, this blog, git and a few others. They all seemed to be pushing me the same way:
git rm --cached -r /public/uploads/image/file/**
I've run a few variations of this code, like dropping **, file/**, --cached, and image/file/**, but it doesn't change the fact that I can still see the files on my GitHub branch.
Also I've added this to my gitignore file: /public/uploads/image/file/**
But when I push to the repository branch I get this info telling me why I can't push to Github:
I started from git add . for context.
ruby 2.3.3-p222
╳ project_name categories ◆ git add .
ruby 2.3.3-p222
╳ project_name categories ◆ git commit -m "trying to get a commit in after purging development environment image data"
[categories 8c13b0a] trying to get a commit in after purging development environment image data
1 file changed, 1 insertion(+), 3 deletions(-)
ruby 2.3.3-p222
╳ project_name categories git push origin categories
Counting objects: 3840, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3664/3664), done.
Writing objects: 100% (3672/3672), 163.83 MiB | 3.98 MiB/s, done.
Total 3672 (delta 1242), reused 0 (delta 0)
remote: Resolving deltas: 100% (1242/1242), completed with 57 local objects.
remote: error: GH001: Large files detected. You may want to try Git Large File Storage - https://git-lfs.github.com.
remote: error: Trace: 85ba931580b369a222fcf5903416f84e
remote: error: See http://git.io/iEPt8g for more information.
remote: error: File public/uploads/image/file/30/show_55MiEk4_-_Imgur.gif is 119.49 MB; this exceeds GitHub's file size limit of 100.00 MB
To git#github.com:Lenocam/project_name.git
! [remote rejected] categories -> categories (pre-receive hook declined)
error: failed to push some refs to 'git#github.com:Lenocam/project_name.git'
So, now I'm confused because doesn't adding /public/uploads/image/file/** to my gitignore file tell git to ignore the folder and the files inside of it? Why does the file continue to be pushed to my repository?
It seems to me I've asked git/Github to get rid of those old files(through the terminal command) and completely forget they ever existed so they will stop asking me about them(through gitignore).
I assume I've done something out of order or otherwise incorrectly. Any assistance you're able to give me will be appreciated.
.gitignore doesn't really ignore files
In Git, a file is tracked if and only if it is in the index.1
If a file is in the index and you make a new commit, that file goes into the commit. This happens regardless of whether the file name is in .gitignore.
Once a file is in a commit, it is in that commit forever. The only way to avoid it is to stop using that commit entirely.
What .gitignore does is to make Git stop whining. For each file you have in the work-tree,2 but not in the index, Git complains: "hey, this file is in the work-tree but not in the index! Maybe you should add it!" But some files that do belong in the work-tree do not belong in any commit, and hence should never go into the index.
Putting the file—or a matching glob pattern, e.g., anything using * or **—into .gitignore tells Git: "Don't complain, and also, if it's not already in the index, don't automatically add it either with git add -A etc." But it doesn't take the file out of the index, and it literally can't take the file out of any existing commits that have it.
To remove a file from the index, without removing it from the work-tree, use git rm --cached.3
You not only have (or had) the file in the index—which means that git add -A updates it in the index—you also have it in some commit you have not yet pushed. So removing it from the index is not sufficient. You must abandon each commit that contains the large file.
To do so, you probably want to use git rebase -i to copy that commit (or those commits) to a new and improved version, where the improvement is simply "do not include the file in the commit".
See also Can't push to GitHub because of large file which I already deleted.
1The index is where you build the next commit. It is not a commit itself, but when you run git commit, Git packages up the index contents to make the new commit.
2The work-tree is simply the place where you work on your files, since the form of files inside Git's index and Git's commits is unusable for normal work.
3Note that you should not let the shell expand any glob patterns you are using in your .gitignore files, for two reasons. First, the shell expansion may not match that done by Git. Specifically, not all shells expand ** at all, and those that do, do not always do it the same way. Second, the work-tree contents may differ in significant ways from the index contents: for instance, if you have public/uploads/image/file/1 in the work-tree but not in the index, the shell, which looks at the work-tree, may include that in its glob expansion, while Git, which looks only at Git's index when doing git rm, would not put that in the list of files to remove—and as soon as Git finds one file it can't remove from the index, it stops removing other files.
git rm --cached -r /public/uploads/image/file/**
You have added the file to .gitignore after it was already added to git.
Look like your ignore pattern doesnot match the file pattern
public/uploads/image/file/30/show_55MiEk4_-_Imgur.gif
Add the following pattern to the .gitignore
/public/uploads/image/file/**/**
You first have to remove it and than push it again.
git rm --cached <file>
git commit -m "Message"
git push ....

Force git to ignore a directory and all present and future files within

So, I'm a newbie at git, but I'm using it because I have my rails app deployed through heroku. My app generates a bookmarklet (which is just a js file) for each user upon sign-up. Unfortunately, when I deploy, all of the bookmarklets for the users on the live site get overwritten with the bookmarklets for the users on my dev environment. I've read some other questions about this kind of thing, and I know I'll have to add the bookmarklet folder to the .gitignore file, and something about rm --cache (but I'm not sure exactly what I'll have to do). I tried doing these things, but I'm wondering if the problem is that git is ignoring all of the files that are there now, but isn't ignoring the ones that are generated after doing the whole gitignore process. Either that or I'm just doing it wrong (this is very, very likely).
Any help is welcome. And sorry that this covers the same ground as a lot of other similar questions. I did as much research as I could.
Thanks.
Here some simple steps:
Create a file .gitignore in the root of your repository, with the following simple content:
/path/to_your/folder
Add the file to your repository:
git add .gitignore
Remove the folder from your repository (this won’t physically delete the folder):
git rm --cached /path/to_your/folder
Commit
git commit
After that, the folder should be removed from your repository and subsequent changes in it will be ignored by git.
Sounds like Heroku is cleaning out every file not checked in to your Git repository when you deploy. Modify your app to save the bookmarklets to a directory outside of your Git repository.
#poke's answer is mostly correct, but the leading slash in the path name is problematic so I'm posting revised instructions.
The following steps assume the subdirectory inside your git repository is named foo.
Make sure you're at the top level of your Git working directory:
cd "$(git rev-parse --show-toplevel)"
Add foo to your top-level .gitignore file:
echo /foo/ >>.gitignore
The leading slash says to ignore foo in the top level but not */foo or */*/foo, etc. The trailing slash says to ignore foo if it is a directory, but not if it is a file or symbolic link.
Stage the newly modified .gitignore:
git add .gitignore
Commit:
git commit -m "Add foo to .gitignore"
Stop tracking the contents of the foo directory in the Git repository:
git rm -r --cached foo
The --cached option tells Git to not delete the foo folder from your working directory.
Commit:
git commit -m "Remove the foo directory"
Add the following to your .gitignore:
path/to/ignore/**/*
If there are already tracked files on that path, they won't be ignored.
You'll have to run
git rm -r --cached path/to/ignore/

Exclude specific files when pushing to a specific Git repository

Is it possible to exclude specific files (*.ai, *.psd) when pushing to certain repositories with Git?
My need comes from trying to use Git for both version control and deployment to Heroku. If I include my graphic assets in the deploy, the slug size is larger than desired. However, I do need to include all project files in my main github repository.
The easy way to solve your actual problem is to create a .slugignore file in the root of the repository that lists files that shouldn't be packaged in the slug.
Heroku documentation on Slugignore
You can maintain a second branch for deployment to Heroku, which contains none of those files, but still merges from master. (Of course, you'll have to work out a system for resolving the merge conflicts you get when you modify the .ai and .psd files in master).
The specific thing you ask is impossible, for the simple reason that when you push, you transfer the exact commits from one repository to another, and two commits which don't have the same tree are by definition different commits.
Tip: The most recent versions of git have a --porcelain option for git status which will give easy to parse information like "M file1" "DU file2" (modified and unmerged/deleted by us, respectively). You could write a git-merge wrapper for your deployment branch which attempts the merge, and automatically cleans up the expected conflicts:
git checkout deploy
if ! git merge master; then
git rm $(git status --porcelain | awk '/^DU/ {print $NF}')
fi
(The reason I printed $NF instead of $2 is that if the file's renamed, it'll look like "DU original_name -> new_name", and the copy placed in the work tree will be new_name, not original_name.)
Of course, the script could get more complex if your situation is - you could look for only certain extensions (add them to the limiting awk pattern), or even capture the whole output in a perl script so you can easily do some more fancy logic...
There isn't a direct easy way to do that. It's certainly manageable, but with a lot of pain (git wasn't designed to do this).
It would be easier probably if you ask Heroku to provide a way to exclude some files from the deploy.

Resources