Using rugged how do you perform the following operations: fetch, pull and rebase?
I am using the development branch and after reviewing its documentation found here as a guide to the Remote class.
EDIT: Since git pull is just a shorthand for git fetch and git merge FETCH_HEAD the better question is how to perform git fetch, git merge and git rebase.
git fetch:
remote = Rugged::Remote.lookup(repo, "origin")
remote.connect(:fetch) do |r|
r.download
r.update_tips!
end
git merge:
merge_index = repo.merge_commits(
Rugged::Branches.lookup(repo, "master").tip,
Rugged::Branches.lookup(repo, "origin/master").tip
)
raise "Conflict detected!" if merge_index.conflicts?
merge_commit = Rugged::Commit.create(repo, {
parents: [
Rugged::Branches.lookup(repo, "master").tip,
Rugged::Branches.lookup(repo, "origin/master").tip
],
tree: merge_index.write_tree(repo),
message: 'Merged `origin/master` into `master`',
author: { name: "User", email: "example#test.com" },
committer: { name: "User", email: "example#test.com" },
update_ref: 'master'
})
git rebase:
Rebasing was not implemented yet in libgit2, and thus is not available in Rugged.
In general, your use case sounds very high level, while the rugged API is currently a bit more focused on low-level git repository access and modification. Eventually, we'll also have many higher-level helpers (like a more simple/correct pull) in the future, but we're not there yet.
The answer above seems to be outdated. The syntax has changed. I need to implement a pull action which i am trying to do by a fetch and then a merge and commit. For fetching i use the fetch method like this
repo.fetch('origin', [repo.head.name], credentials: credits)
And it seems to actually get something since the returned hash is full with information about what has been fetched. However, it is not written to disk. I would expect the branch to be behind several commits when i do git status in the command line but it is not. If i fetch a second time with the same command above then nothing is fetched. This is probably because it has already been fetched the first time but then i dont see where the fetch is.
Now if i go ahead and do the fetch manually in the command line and then try to merge the local copy of the remote branch and the local branch (local changes are already committed) using the following code
ref_name = repo.head.name # refs/heads/branchname
branch_name = ref_name.sub(/^refs\/heads\//, '') # branchname
remote_name = "#{remote}/#{branch_name}" # origin/branchname
remote_ref = "refs/heads/#{remote_name}" # refs/heads/origin/branchname
local_branch = repository.branches[branch_name]
remote_branch = repository.branches[remote_name]
index = repo.merge_commits(local_branch.target, remote_branch.target)
options = {
author: { time: Time.now }.merge(author),
committer: { time: Time.now }.merge(committer),
message: 'merged',
parents: [
local_branch.target,
remote_branch.target
],
tree: index.write_tree(repository),
update_ref: 'HEAD'
}
Rugged::Commit.create repo, options
It creates the commit as expected. The commit is also written to disk and is visible in the fistory. But for some reason the branch has now uncommitted changes. The local file contents have not changed. I would expect them to have the contents of the fetched commit.
Can anyone please provide a working example for a fetch, merge, commit? The version of rugged at time of writing this is 0.22.0b3
Update 1
This will bring my working tree to the wanted state
repo.checkout ref_name, strategy: :force
Update2
I found out how to fetch and save the state to disk
r = repo.remotes[remote]
r.fetch(credentials: git_credentials)
r.save
Related
I'm using ruby-git to operate my Git repo. I can get the local branch that checkout from remote branch, how can I get it upstream remote branch? This's the code:
require 'Git'
repo = Git.open("xxxpath.git")
localbranch = repo.branches["localbranchnamexxx"]
The same way you would do it in normal git
remote_branch = repo.branches["origin/localbranchnamexxx"]
Counter-intuitively (to me, at least), the branch tracking information is stored in the git config, not in any branch or ref structures.
require 'git'
repo = Git.open("xxxpath.git")
localbranch = repo.current_branch
upstream_remote = repo.config["branch.#{localbranch}.remote"]
upstream_ref = repo.config["branch.#{localbranch}.merge"]
upstream_branch = upstream_ref.split('/').last
upstream = "#{upstream_remote}/#{upstream_branch}"
I'm using "checkout build script from scm" option, paired with lightweight checkout.
I would like to add repository polling to that.
This is Jenkinsfile that I use:
pipeline {
agent any
triggers {
pollSCM('H/1 * * * *')
}
stages {
stage('Checkout') {
steps {
checkout([
$class : 'GitSCM',
branches : [[name: 'master']],
userRemoteConfigs : [[url: 'file:///home/my-secret-home/workspace/pipeline-test']]])
}
}
stage('Echo!') {
steps {
sh 'echo TEST'
}
}
}
}
Although job is running, git polling log tries to convince me that 'Polling has not run yet.'
Is configuring such behavior possible at all?
No, it doesn't work.
With lightweight checkout, the mapping to the remote branches are lost, so git doesn't know where to look for further updates.
You can also confirm this by running git pull on the local repository. It returns:
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.
git pull <remote> <branch>
If you wish to set tracking information for this branch you can do so with:
git branch --set-upstream-to=origin/<branch> master
I've created the following rake task below to generate our release notes for each sprint.
I'm pulling in all commits to master older than 2 weeks.
The problem is when a branch has been developed on for more than 2-week sprints, the older commits won't be included.
Can anyone suggest a way I can get these commits in?
task :new_release_note do
puts "Creating new release note"
puts "..."
git_log = `git log --since="two weeks ago" --no-merges --format=%B`
git_log.gsub!(/^$\n/, '')
git_log.gsub!(/^/, "* ")
current_time = DateTime.now
current_date = current_time.strftime "%Y-%m-%d"
current_date_UK = current_time.strftime "%d-%m-%Y"
template = "__Release Notes__
=======================
#{current_date_UK}
__New Features__
----------------
* -
__Improvements__
----------------
* -
__Fixes__
---------
* -
__Change Log__
----------------
Detailed release notes below, listing all commit messages for this release.
#{git_log}
"
out_file = File.new("./doc/release_notes/release-notes-#{current_date}.md", "w")
out_file.puts(template)
if File.exist?(out_file)
puts "New release note generated successfully at /doc/release-notes/release-notes-#{current_date}.md"
else
puts "Error - file not generated."
end
end
Can anyone suggest a way I can get these commits in?
Few options:
git tag
git notes
git whatchanged
git tag
Read this answer on what is git tag and how to use it: What is git tag, How to create tags & How to checkout git remote tag(s)
In short: git tag allows you to mark commit which can be later on to perform your merge. As you know
git pull = git fetch + git merge
So once you have marked your last merge with the tag you can pull out all the changes form the last merge
# "Merge" the last X commits based upon your previous tag
git cherry-pick <tag_name>..master
git notes
git notes allow us to add content to commit without updating the SHA-1 of the commit, meaning we can attach content to the commit while leaving the SHA-1 unmodified.
Now once you have your notes you can find out the last commit which you "merged" previously and grab the changes from this point on using the above cherry-pick.
You can search and find your notes with git log --grep
git whatchanged
Once you what is your referenced commit you can see the list of files which were updated during this time period with the git whatchanged command
# Print out a list of files which was updated/added between the 2 commits
git whatchanged <TAG_NAME>...HEAD
Consider using git tag and tag your releases with version numbers. What my team does is to create a release branch with a version number for each release i.e. release-2.5.8 and when the release is ready, it gets merged into master. Then we tag that merge commit with a version number i.e. v2.5.8 If you do this, along with squash merges then to see all the related commits it's as easy as doing:
git log v2.5.8...v2.5.9
Which will show you all the commits within those 2 releases.
The reason I recommend squash merging your feature branch is for exactly your use case. You want to know what was done during the dev of that feature, but how can you just by date? You really can't. So when your feature is ready to be merged into your release, if you squash merge, you can keep all the notes in a single commit for the merge of that feature. The idea here is you keep what is relevant and discard what is no longer needed during development.
You might also want to check out Gitflow
So, I wanted to perform a git pull with rugged, so I make a fetch and merge like this:
require 'rugged'
certificat = Rugged::Credentials::SshKey.new({username: 'git', privatekey: 'path/to/privatekey', publickey: 'path/to/publickey' })
repo = Rugged::Repository.new("/tmp/git")
repo.checkout('master')
# git fetch
remote = repo.remotes['origin']
remote.fetch(credentials: certificat)
remote.save # save new data
# git merge
commit = repo.branches['origin/master'].target
repo.references.update(repo.head, commit.oid)
But I have this error with the save method:
undefined method `save' for #<Rugged::Remote:0x0000000135d6e8> (NoMethodError)
I don't understand why especially that the save method is in the Rugged doc (here)
Somebody know why ?
EDIT: Ok, so this documentation is outdated, the method save doesn't exist anymore. I think my merge is imcomplete, somebody know ?
EDIT2: I just add this line at the end of this code and it's work !
repo.checkout_head({strategy: :force})
Using Rugged, I create a new branch from master (let's call it new_branch), modify a file and create a commit for that. Now I want to merge this branch into master, push master to remote and delete new_branch.
At the point of running the code below there are no modified files and no staged files on either branch as the modified is committed into new_branch.
This is the code I use:
from_branch = #repo.head.name
their_commit = #repo.branches[into_branch].target_id
our_commit = #repo.branches[from_branch].target_id
index = #repo.merge_commits(our_commit, their_commit)
if index.conflicts?
# conflicts. deal with them
else
# no conflicts
commit_tree = index.write_tree(#repo)
#repo.checkout(into_branch)
commit_author = { email: GIT_EMAIL, name: GIT_NAME, time: Time.now }
Rugged::Commit.create(#repo,
committer: commit_author,
message: "Merge #{from_branch} into #{into_branch}",
parents: [#repo.head.target, our_commit],
tree: commit_tree,
update_ref: #repo.branches[into_branch].canonical_name)
#repo.push('origin', [#repo.head.name], { credentials: #cred })
#repo.branches.delete(from_branch)
end
This works as expected (modified file is merged into master, it is pushed to remote and the new branch is delete), however once it's done, I am left with the modified file showing under master as modified and staged, ready to be committed while there shouldn't be any modified files in the working directory and nothing staged. Everything should be up-to-date.
In your code you are updating an arbitrary branch, so the index and worktree should not be of concern in the general case.
If you are merging into the current branch, then (and only then) you are missing the steps to update both the index and the worktree with the results of the merge. You can use
#repo.checkout_tree(commit_tree)
to checkout the files as they are on the resulting commit. This will also update the repository's index file to have the contents of that tree. You can then update the current branch to point to the merge commit.
You have a call to #repo.checkout(into_branch) but since you already seem to be in into_branch in your case, that will at best do nothing. You need to checkout the result of the merge, the branch, index or workdir are not touched by the #repo.merge_commits() call.